Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aee981d9c0 | |||
| 3bede84a2d | |||
| e14d411150 | |||
| f91f000df1 | |||
| 18f8443a9f | |||
| 746d842dd4 | |||
| 185efb1c2a | |||
| a31f9b5e04 | |||
| 94aec87b5d | |||
| 854a9bb22e | |||
| 477a883885 | |||
| b3a63042cb | |||
| d53a683285 | |||
| 27a99e2973 | |||
| 53c64010e8 | |||
| 7f704bcd61 | |||
| 1e08adb26b | |||
| 75bbb17881 | |||
| 58c8556058 | |||
| 1c56de8698 | |||
| 0eb0a6e8fd | |||
| 9b5e0109a7 | |||
| a1b811aada | |||
| 1b424bd663 | |||
| 938ba53a38 | |||
| 1e609f4550 | |||
| e33f591a1f | |||
| 456af0f1ef | |||
| b6d25da7c2 | |||
| 288d2247da | |||
| e5eb9f0fd3 | |||
| 908aa20036 | |||
| 60f1ca1408 | |||
| 5ef687a722 | |||
| 86ed9a7a01 | |||
| 4fee2fad01 | |||
| 35ba8ad81c | |||
| 0985724b19 | |||
| 7aff7977fc | |||
| 55bb263a12 | |||
| 32b85e75db | |||
| 3935375d1b | |||
| 97dd2b1096 | |||
| de7211d1a5 | |||
| 26380ab987 | |||
| f898b75b9d | |||
| e513cb9b54 | |||
| 6fd7ab7405 | |||
| 15833323ae | |||
| 653462721b | |||
| 2889e50507 | |||
| 6b15f69e00 | |||
| 231982d7f7 | |||
| 3bea256ca5 | |||
| 4c0f23c5c1 | |||
| 27d54fb38a | |||
| 3b6999f6ef | |||
| de43d67638 | |||
| 2fca71aa53 | |||
| 7870791fef | |||
| 83f9b580ef | |||
| 54f9e61b6a | |||
| 94b7ba1ccc | |||
| e7d30d933f | |||
| 150fb3cf6a | |||
| 169f33c67a |
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -2,7 +2,7 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.2.0"
|
version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -54,7 +54,7 @@ dependencies = [
|
|||||||
name = "rs"
|
name = "rs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -142,7 +142,7 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
"checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2"
|
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
|
||||||
"checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e"
|
"checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e"
|
||||||
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
|
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
|
||||||
"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
|
"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
|
||||||
|
|||||||
10
Cargo.toml
10
Cargo.toml
@ -3,11 +3,11 @@ name = "rs"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "1.0"
|
bitflags = "1.0.*"
|
||||||
maplit = "1.0"
|
maplit = "1.0.*"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0.*", features = ["derive"] }
|
||||||
serde_yaml = "0.8"
|
serde_yaml = "0.8.*"
|
||||||
xkbcommon = { version = "0.4", features = ["wayland"] }
|
xkbcommon = { version = "0.4.*", features = ["wayland"] }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "rs"
|
name = "rs"
|
||||||
|
|||||||
@ -42,7 +42,7 @@ Use the `cargo.sh` script for maintaining the Cargo part of the build. The scrip
|
|||||||
|
|
||||||
```
|
```
|
||||||
cd build_dir
|
cd build_dir
|
||||||
sh /source_path/cargo.sh /source_path '' test
|
sh /source_path/cargo.sh '' test
|
||||||
```
|
```
|
||||||
|
|
||||||
### Cargo dependencies
|
### Cargo dependencies
|
||||||
@ -53,6 +53,6 @@ Dependencies must be specified in `Cargo.toml` with 2 numbers: "major.minor". Si
|
|||||||
|
|
||||||
```
|
```
|
||||||
cd build_dir
|
cd build_dir
|
||||||
sh /source_path/cargo.sh /source_path '' update
|
sh /source_path/cargo.sh '' update
|
||||||
ninja test
|
ninja test
|
||||||
```
|
```
|
||||||
|
|||||||
19
cargo.sh
Normal file → Executable file
19
cargo.sh
Normal file → Executable file
@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
|
|
||||||
# This script manages Cargo operations
|
# This script manages Cargo operations
|
||||||
# while keeping the artifact directory within the build tree
|
# while keeping the artifact directory within the build tree
|
||||||
@ -6,18 +6,19 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
SOURCE_DIR="$1"
|
SCRIPT_PATH="$(realpath "$0")"
|
||||||
|
SOURCE_DIR="$(dirname "$SCRIPT_PATH")"
|
||||||
|
|
||||||
export CARGO_TARGET_DIR=`pwd`
|
CARGO_TARGET_DIR="$(pwd)"
|
||||||
if [ ! -z ${2} ]; then
|
export CARGO_TARGET_DIR
|
||||||
OUT_PATH=`realpath "${2}"`
|
if [ -n "${1}" ]; then
|
||||||
|
OUT_PATH="$(realpath "$1")"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd $SOURCE_DIR
|
cd "$SOURCE_DIR"
|
||||||
shift
|
shift
|
||||||
shift
|
cargo "$@"
|
||||||
cargo $BUILD_ARG $@
|
|
||||||
|
|
||||||
if [ ! -z ${OUT_PATH} ]; then
|
if [ -n "${OUT_PATH}" ]; then
|
||||||
cp "${CARGO_TARGET_DIR}"/debug/librs.a "${OUT_PATH}"
|
cp "${CARGO_TARGET_DIR}"/debug/librs.a "${OUT_PATH}"
|
||||||
fi
|
fi
|
||||||
|
|||||||
197
data/keyboards/el.yaml
Normal file
197
data/keyboards/el.yaml
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
# Greek layout created by Antonis Tsolomitis
|
||||||
|
# University of the Aegean, Department of Mathematics, atsol@aegean.gr
|
||||||
|
# Sep 2019
|
||||||
|
---
|
||||||
|
row_spacing: 11.33
|
||||||
|
button_spacing: 4.67
|
||||||
|
|
||||||
|
bounds: { x: 0, y: 6.33, width: 426, height: 250 }
|
||||||
|
|
||||||
|
outlines:
|
||||||
|
default:
|
||||||
|
bounds: { x: 0, y: 0, width: 32, height: 52 }
|
||||||
|
altline:
|
||||||
|
bounds: { x: 0, y: 0, width: 48.39024, height: 52 }
|
||||||
|
outline7:
|
||||||
|
bounds: { x: 0, y: 0, width: 88.97561, height: 52 }
|
||||||
|
spaceline:
|
||||||
|
bounds: { x: 0, y: 0, width: 150.5853, height: 52 }
|
||||||
|
|
||||||
|
views:
|
||||||
|
base:
|
||||||
|
- "; ς ε ρ τ υ θ ι ο π !"
|
||||||
|
- "α σ δ φ γ η ξ κ λ show_accented"
|
||||||
|
- "Shift_L ζ χ ψ ω β ν μ , BackSpace"
|
||||||
|
- "show_numbers preferences space . Return"
|
||||||
|
upper:
|
||||||
|
- ": EuroSign Ε Ρ Τ Υ Θ Ι Ο Π"
|
||||||
|
- "Α Σ Δ Φ Γ Η Ξ Κ Λ show_accented"
|
||||||
|
- "Shift_L Ζ Χ Ψ Ω Β Ν Μ · BackSpace"
|
||||||
|
- "show_numbers preferences space « » Return"
|
||||||
|
accented:
|
||||||
|
- "ά έ ή ί ό ύ ώ ϊ ϋ ΐ"
|
||||||
|
- "ΰ Ά Έ Ή Ί Ό Ύ Ώ Ϊ show_base"
|
||||||
|
- "Ϋ Ϗ ϐ ϑ ϕ ϖ ϗ – — BackSpace"
|
||||||
|
- "show_numbers preferences space quoteleft quoteright 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_letters preferences space period 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"
|
||||||
|
- "show_letters preferences space period Return"
|
||||||
|
buttons:
|
||||||
|
Shift_L:
|
||||||
|
action:
|
||||||
|
locking:
|
||||||
|
lock_view: "upper"
|
||||||
|
unlock_view: "base"
|
||||||
|
outline: "altline"
|
||||||
|
icon: "key-shift"
|
||||||
|
BackSpace:
|
||||||
|
outline: "altline"
|
||||||
|
icon: "edit-clear-symbolic"
|
||||||
|
preferences:
|
||||||
|
action: "show_prefs"
|
||||||
|
outline: "altline"
|
||||||
|
icon: "keyboard-mode-symbolic"
|
||||||
|
show_numbers:
|
||||||
|
action:
|
||||||
|
set_view: "numbers"
|
||||||
|
outline: "altline"
|
||||||
|
label: "123"
|
||||||
|
show_letters:
|
||||||
|
action:
|
||||||
|
set_view: "base"
|
||||||
|
outline: "altline"
|
||||||
|
label: "ΑΒΓ"
|
||||||
|
show_symbols:
|
||||||
|
action:
|
||||||
|
set_view: "symbols"
|
||||||
|
outline: "altline"
|
||||||
|
label: "*/="
|
||||||
|
show_accented:
|
||||||
|
action:
|
||||||
|
locking:
|
||||||
|
lock_view: "accented"
|
||||||
|
unlock_view: "base"
|
||||||
|
outline: "altline"
|
||||||
|
label: "άΐ"
|
||||||
|
show_base:
|
||||||
|
action:
|
||||||
|
set_view: "base"
|
||||||
|
outline: "altline"
|
||||||
|
label: "αι"
|
||||||
|
period:
|
||||||
|
outline: altline
|
||||||
|
label: "."
|
||||||
|
space:
|
||||||
|
outline: spaceline
|
||||||
|
label: " "
|
||||||
|
Return:
|
||||||
|
outline: outline7
|
||||||
|
icon: "key-enter"
|
||||||
|
aring:
|
||||||
|
label: "å"
|
||||||
|
Aring:
|
||||||
|
label: "Å"
|
||||||
|
oslash:
|
||||||
|
label: "ø"
|
||||||
|
Oslash:
|
||||||
|
label: "Ø"
|
||||||
|
ae:
|
||||||
|
label: "æ"
|
||||||
|
AE:
|
||||||
|
label: "Æ"
|
||||||
|
asterisk:
|
||||||
|
label: "*"
|
||||||
|
asciitilde:
|
||||||
|
label: "~"
|
||||||
|
quoteleft:
|
||||||
|
label: "`"
|
||||||
|
bar:
|
||||||
|
label: "|"
|
||||||
|
U00B7:
|
||||||
|
label: "·"
|
||||||
|
squareroot:
|
||||||
|
label: "√"
|
||||||
|
Greek_pi:
|
||||||
|
label: "π"
|
||||||
|
division:
|
||||||
|
label: "÷"
|
||||||
|
multiply:
|
||||||
|
label: "×"
|
||||||
|
paragraph:
|
||||||
|
label: "¶"
|
||||||
|
Greek_tau:
|
||||||
|
label: "τ"
|
||||||
|
copyright:
|
||||||
|
label: "©"
|
||||||
|
numbersign:
|
||||||
|
label: "#"
|
||||||
|
U00AE:
|
||||||
|
label: "®"
|
||||||
|
at:
|
||||||
|
label: "@"
|
||||||
|
dollar:
|
||||||
|
label: "$"
|
||||||
|
U00A3:
|
||||||
|
label: "£"
|
||||||
|
percent:
|
||||||
|
label: "%"
|
||||||
|
EuroSign:
|
||||||
|
label: "€"
|
||||||
|
ampersand:
|
||||||
|
label: "&"
|
||||||
|
U00A5:
|
||||||
|
label: "¥"
|
||||||
|
minus:
|
||||||
|
label: "-"
|
||||||
|
asciicircum:
|
||||||
|
label: "^"
|
||||||
|
underscore:
|
||||||
|
label: "_"
|
||||||
|
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: "]"
|
||||||
|
|
||||||
96
data/keyboards/es.yaml
Normal file
96
data/keyboards/es.yaml
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
---
|
||||||
|
row_spacing: 11.33
|
||||||
|
button_spacing: 4.67
|
||||||
|
|
||||||
|
bounds: { x: 0, y: 1, width: 360, height: 198 }
|
||||||
|
|
||||||
|
outlines:
|
||||||
|
default:
|
||||||
|
bounds: { x: 0, y: 0, width: 30.67, height: 40.67 }
|
||||||
|
altline:
|
||||||
|
bounds: { x: 0, y: 0, width: 48, height: 40.67 }
|
||||||
|
wide:
|
||||||
|
bounds: { x: 0, y: 0, width: 57.33, height: 40.67 }
|
||||||
|
spaceline:
|
||||||
|
bounds: { x: 0, y: 0, width: 95.00, height: 40.67 }
|
||||||
|
special:
|
||||||
|
bounds: { x: 0, y: 0, width: 39.33, height: 40.67 }
|
||||||
|
|
||||||
|
views:
|
||||||
|
base:
|
||||||
|
- "q w e r t y u i o p"
|
||||||
|
- "a s d f g h j k l ñ"
|
||||||
|
- "Shift_L z x c v b n m BackSpace"
|
||||||
|
- "show_numbers show_eschars preferences space ? period 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 show_eschars preferences space ¿ period Return"
|
||||||
|
numbers:
|
||||||
|
- "1 2 3 4 5 6 7 8 9 0"
|
||||||
|
- "@ # € % & - _ + ( )"
|
||||||
|
- "show_symbols , \" ' colon ; ! = BackSpace"
|
||||||
|
- "show_letters show_eschars preferences space ? period Return"
|
||||||
|
symbols:
|
||||||
|
- "~ ` | · √ π τ ÷ × ¶"
|
||||||
|
- "© ® £ $ ¥ ^ ° * { }"
|
||||||
|
- "show_numbers \\ / < > = [ ] BackSpace"
|
||||||
|
- "show_letters show_eschars preferences space ? period Return"
|
||||||
|
eschars:
|
||||||
|
- "á é í ó ú Á É Í Ó Ú"
|
||||||
|
- "à è ì ò ù À È Ì Ò Ù"
|
||||||
|
- "show_numbers ü ç ï Ü Ç Ï ¡ BackSpace"
|
||||||
|
- "show_letters show_eschars preferences space « » Return"
|
||||||
|
|
||||||
|
buttons:
|
||||||
|
Shift_L:
|
||||||
|
action:
|
||||||
|
locking:
|
||||||
|
lock_view: "upper"
|
||||||
|
unlock_view: "base"
|
||||||
|
outline: "altline"
|
||||||
|
icon: "key-shift"
|
||||||
|
BackSpace:
|
||||||
|
outline: "altline"
|
||||||
|
icon: "edit-clear-symbolic"
|
||||||
|
preferences:
|
||||||
|
action: "show_prefs"
|
||||||
|
outline: "default"
|
||||||
|
icon: "keyboard-mode-symbolic"
|
||||||
|
show_numbers:
|
||||||
|
action:
|
||||||
|
set_view: "numbers"
|
||||||
|
outline: "altline"
|
||||||
|
label: "123"
|
||||||
|
show_letters:
|
||||||
|
action:
|
||||||
|
set_view: "base"
|
||||||
|
outline: "altline"
|
||||||
|
label: "abc"
|
||||||
|
show_symbols:
|
||||||
|
action:
|
||||||
|
set_view: "symbols"
|
||||||
|
outline: "altline"
|
||||||
|
label: "*/="
|
||||||
|
show_eschars:
|
||||||
|
action:
|
||||||
|
locking:
|
||||||
|
lock_view: "eschars"
|
||||||
|
unlock_view: "base"
|
||||||
|
outline: "altline"
|
||||||
|
label: "áÁ"
|
||||||
|
|
||||||
|
period:
|
||||||
|
outline: "default"
|
||||||
|
label: "."
|
||||||
|
space:
|
||||||
|
outline: "spaceline"
|
||||||
|
label: " "
|
||||||
|
Return:
|
||||||
|
outline: "altline"
|
||||||
|
icon: "key-enter"
|
||||||
|
colon:
|
||||||
|
label: ":"
|
||||||
|
"\"":
|
||||||
|
keysym: "quotedbl"
|
||||||
98
data/keyboards/it.yaml
Normal file
98
data/keyboards/it.yaml
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
---
|
||||||
|
row_spacing: 11.33
|
||||||
|
button_spacing: 4.67
|
||||||
|
|
||||||
|
bounds: { x: 0, y: 1, width: 360, height: 198 }
|
||||||
|
|
||||||
|
outlines:
|
||||||
|
default:
|
||||||
|
bounds: { x: 0, y: 0, width: 30.67, height: 40.67 }
|
||||||
|
altline:
|
||||||
|
bounds: { x: 0, y: 0, width: 48, height: 40.67 }
|
||||||
|
wide:
|
||||||
|
bounds: { x: 0, y: 0, width: 57.33, height: 40.67 }
|
||||||
|
spaceline:
|
||||||
|
bounds: { x: 0, y: 0, width: 95.00, height: 40.67 }
|
||||||
|
special:
|
||||||
|
bounds: { x: 0, y: 0, width: 39.33, height: 40.67 }
|
||||||
|
|
||||||
|
views:
|
||||||
|
base:
|
||||||
|
- "q w e r t y u i o p"
|
||||||
|
- "a s d f g h j k l n"
|
||||||
|
- "Shift_L z x c v b n m BackSpace"
|
||||||
|
- "show_numbers show_eschar preferences space ? period 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 show_eschar preferences space ? period Return"
|
||||||
|
numbers:
|
||||||
|
- "1 2 3 4 5 6 7 8 9 0"
|
||||||
|
- "@ # € % & - _ + ( )"
|
||||||
|
- "show_symbols , \" ' colon ; ! ? BackSpace"
|
||||||
|
- "show_letters show_eschar preferences space ? period Return"
|
||||||
|
symbols:
|
||||||
|
- "~ ` | · √ π τ ÷ × ¶"
|
||||||
|
- "© ® £ $ ¥ ^ ° * { }"
|
||||||
|
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
|
||||||
|
- "show_letters show_eschar preferences space ? period Return"
|
||||||
|
eschar:
|
||||||
|
- "á é í ó ú Á É Í Ó Ú"
|
||||||
|
- "à è ì ò « » ù ! { }"
|
||||||
|
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
|
||||||
|
- "show_letters show_symbols preferences space ? period Return"
|
||||||
|
|
||||||
|
buttons:
|
||||||
|
Shift_L:
|
||||||
|
action:
|
||||||
|
locking:
|
||||||
|
lock_view: "upper"
|
||||||
|
unlock_view: "base"
|
||||||
|
outline: "altline"
|
||||||
|
icon: "key-shift"
|
||||||
|
BackSpace:
|
||||||
|
outline: "altline"
|
||||||
|
icon: "edit-clear-symbolic"
|
||||||
|
preferences:
|
||||||
|
action: "show_prefs"
|
||||||
|
outline: "default"
|
||||||
|
icon: "keyboard-mode-symbolic"
|
||||||
|
show_numbers:
|
||||||
|
action:
|
||||||
|
set_view: "numbers"
|
||||||
|
outline: "altline"
|
||||||
|
label: "123"
|
||||||
|
show_numbers_from_symbols:
|
||||||
|
action:
|
||||||
|
set_view: "numbers"
|
||||||
|
outline: "altline"
|
||||||
|
label: "123"
|
||||||
|
show_letters:
|
||||||
|
action:
|
||||||
|
set_view: "base"
|
||||||
|
outline: "altline"
|
||||||
|
label: "ABC"
|
||||||
|
show_symbols:
|
||||||
|
action:
|
||||||
|
set_view: "symbols"
|
||||||
|
outline: "altline"
|
||||||
|
label: "*/="
|
||||||
|
show_eschar:
|
||||||
|
action:
|
||||||
|
set_view: "eschar"
|
||||||
|
outline: "altline"
|
||||||
|
label: "àè"
|
||||||
|
period:
|
||||||
|
outline: "default"
|
||||||
|
label: "."
|
||||||
|
space:
|
||||||
|
outline: "spaceline"
|
||||||
|
label: " "
|
||||||
|
Return:
|
||||||
|
outline: "altline"
|
||||||
|
icon: "key-enter"
|
||||||
|
colon:
|
||||||
|
label: ":"
|
||||||
|
"\"":
|
||||||
|
keysym: "quotedbl"
|
||||||
@ -1,18 +1,17 @@
|
|||||||
---
|
---
|
||||||
bounds: { x: 0, y: 10, width: 426, height: 229 }
|
row_spacing: 11.33
|
||||||
|
button_spacing: 4.67
|
||||||
|
|
||||||
|
bounds: { x: 0, y: 6.33, width: 426, height: 250 }
|
||||||
|
|
||||||
outlines:
|
outlines:
|
||||||
default:
|
default:
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 32, height: 52 }
|
bounds: { x: 0, y: 0, width: 32, height: 52 }
|
||||||
altline:
|
altline:
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 48.39024, height: 52 }
|
bounds: { x: 0, y: 0, width: 48.39024, height: 52 }
|
||||||
outline7:
|
outline7:
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 88.97561, height: 52 }
|
bounds: { x: 0, y: 0, width: 88.97561, height: 52 }
|
||||||
spaceline:
|
spaceline:
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 150.5853, height: 52 }
|
bounds: { x: 0, y: 0, width: 150.5853, height: 52 }
|
||||||
|
|
||||||
views:
|
views:
|
||||||
|
|||||||
@ -1,18 +1,17 @@
|
|||||||
---
|
---
|
||||||
bounds: { x: 0, y: 10, width: 410, height: 229 }
|
row_spacing: 11.33
|
||||||
|
button_spacing: 4.67
|
||||||
|
|
||||||
|
bounds: { x: 0, y: 6.33, width: 410, height: 250 }
|
||||||
|
|
||||||
outlines:
|
outlines:
|
||||||
default:
|
default:
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 37.46341, height: 52 }
|
bounds: { x: 0, y: 0, width: 37.46341, height: 52 }
|
||||||
altline:
|
altline:
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 48.39024, height: 52 }
|
bounds: { x: 0, y: 0, width: 48.39024, height: 52 }
|
||||||
outline7:
|
outline7:
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 88.97561, height: 52 }
|
bounds: { x: 0, y: 0, width: 88.97561, height: 52 }
|
||||||
spaceline:
|
spaceline:
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 120.5853, height: 52 }
|
bounds: { x: 0, y: 0, width: 120.5853, height: 52 }
|
||||||
|
|
||||||
views:
|
views:
|
||||||
|
|||||||
@ -1,41 +1,42 @@
|
|||||||
---
|
---
|
||||||
bounds: { x: 10, y: 10, width: 410, height: 229 }
|
row_spacing: 11.33
|
||||||
|
button_spacing: 4.67
|
||||||
|
|
||||||
|
bounds: { x: 0, y: 1, width: 360, height: 198 }
|
||||||
|
|
||||||
outlines:
|
outlines:
|
||||||
default:
|
default:
|
||||||
corner_radius: 1
|
bounds: { x: 0, y: 0, width: 30.67, height: 40.67 }
|
||||||
bounds: { x: 0, y: 0, width: 37.46341, height: 52 }
|
|
||||||
altline:
|
altline:
|
||||||
corner_radius: 1
|
bounds: { x: 0, y: 0, width: 48, height: 40.67 }
|
||||||
bounds: { x: 0, y: 0, width: 48.39024, height: 52 }
|
wide:
|
||||||
outline7:
|
bounds: { x: 0, y: 0, width: 57.33, height: 40.67 }
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 88.97561, height: 52 }
|
|
||||||
spaceline:
|
spaceline:
|
||||||
corner_radius: 1
|
bounds: { x: 0, y: 0, width: 137.33, height: 40.67 }
|
||||||
bounds: { x: 0, y: 0, width: 150.5853, height: 52 }
|
special:
|
||||||
|
bounds: { x: 0, y: 0, width: 39.33, height: 40.67 }
|
||||||
|
|
||||||
views:
|
views:
|
||||||
base:
|
base:
|
||||||
- "q w e r t y u i o p"
|
- "q w e r t y u i o p"
|
||||||
- "a s d f g h j k l"
|
- "a s d f g h j k l"
|
||||||
- "Shift_L z x c v b n m BackSpace"
|
- "Shift_L z x c v b n m BackSpace"
|
||||||
- "show_numbers preferences space . Return"
|
- "show_numbers preferences space period Return"
|
||||||
upper:
|
upper:
|
||||||
- "Q W E R T Y U I O P"
|
- "Q W E R T Y U I O P"
|
||||||
- "A S D F G H J K L"
|
- "A S D F G H J K L"
|
||||||
- "Shift_L Z X C V B N M BackSpace"
|
- "Shift_L Z X C V B N M BackSpace"
|
||||||
- "show_numbers preferences space . Return"
|
- "show_numbers preferences space period Return"
|
||||||
numbers:
|
numbers:
|
||||||
- "1 2 3 4 5 6 7 8 9 0"
|
- "1 2 3 4 5 6 7 8 9 0"
|
||||||
- "@ # $ % & - _ + ( )"
|
- "@ # $ % & - _ + ( )"
|
||||||
- "show_symbols , \" ' colon ; ! ? BackSpace"
|
- "show_symbols , \" ' colon ; ! ? BackSpace"
|
||||||
- "show_letters preferences space . Return"
|
- "show_letters preferences space period Return"
|
||||||
symbols:
|
symbols:
|
||||||
- "~ ` | · √ π τ ÷ × ¶"
|
- "~ ` | · √ π τ ÷ × ¶"
|
||||||
- "© ® £ € ¥ ^ ° * { }"
|
- "© ® £ € ¥ ^ ° * { }"
|
||||||
- "show_numbers \\ / < > = [ ] BackSpace"
|
- "show_numbers_from_symbols \\ / < > = [ ] BackSpace"
|
||||||
- "show_letters preferences space . Return"
|
- "show_letters preferences space period Return"
|
||||||
|
|
||||||
buttons:
|
buttons:
|
||||||
Shift_L:
|
Shift_L:
|
||||||
@ -50,9 +51,14 @@ buttons:
|
|||||||
icon: "edit-clear-symbolic"
|
icon: "edit-clear-symbolic"
|
||||||
preferences:
|
preferences:
|
||||||
action: "show_prefs"
|
action: "show_prefs"
|
||||||
outline: "altline"
|
outline: "special"
|
||||||
icon: "keyboard-mode-symbolic"
|
icon: "keyboard-mode-symbolic"
|
||||||
show_numbers:
|
show_numbers:
|
||||||
|
action:
|
||||||
|
set_view: "numbers"
|
||||||
|
outline: "wide"
|
||||||
|
label: "123"
|
||||||
|
show_numbers_from_symbols:
|
||||||
action:
|
action:
|
||||||
set_view: "numbers"
|
set_view: "numbers"
|
||||||
outline: "altline"
|
outline: "altline"
|
||||||
@ -60,20 +66,21 @@ buttons:
|
|||||||
show_letters:
|
show_letters:
|
||||||
action:
|
action:
|
||||||
set_view: "base"
|
set_view: "base"
|
||||||
outline: "altline"
|
outline: "wide"
|
||||||
label: "ABC"
|
label: "ABC"
|
||||||
show_symbols:
|
show_symbols:
|
||||||
action:
|
action:
|
||||||
set_view: "symbols"
|
set_view: "symbols"
|
||||||
outline: "altline"
|
outline: "altline"
|
||||||
label: "*/="
|
label: "*/="
|
||||||
".":
|
period:
|
||||||
outline: altline
|
outline: "special"
|
||||||
|
label: "."
|
||||||
space:
|
space:
|
||||||
outline: spaceline
|
outline: "spaceline"
|
||||||
label: " "
|
label: " "
|
||||||
Return:
|
Return:
|
||||||
outline: outline7
|
outline: "wide"
|
||||||
icon: "key-enter"
|
icon: "key-enter"
|
||||||
colon:
|
colon:
|
||||||
label: ":"
|
label: ":"
|
||||||
|
|||||||
@ -1,21 +1,33 @@
|
|||||||
.keyboard {
|
sq_view {
|
||||||
background-color: rgba(0, 0, 0, 255);
|
background-color: rgba(0, 0, 0, 255);
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-family: cantarell, sans-serif;
|
font-family: cantarell, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.key {
|
sq_button {
|
||||||
color: #deddda;
|
color: #deddda;
|
||||||
background: #464448;
|
background: #464448;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
border-color: #5e5c64;
|
border-color: #5e5c64;
|
||||||
border-radius: 2px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.key:active {
|
sq_button:active {
|
||||||
background: #1c71d8;
|
background: #545256;
|
||||||
border-color: #3584e4;
|
border-color: #716e78;
|
||||||
|
}
|
||||||
|
|
||||||
|
sq_button.altline,
|
||||||
|
sq_button.special,
|
||||||
|
sq_button.wide {
|
||||||
|
background: #2b292f;
|
||||||
|
border-color: #3e3a44
|
||||||
|
}
|
||||||
|
|
||||||
|
sq_button.locked {
|
||||||
|
background: #ffffff;
|
||||||
|
color: #2b292f;
|
||||||
}
|
}
|
||||||
|
|
||||||
#Return {
|
#Return {
|
||||||
@ -27,13 +39,3 @@
|
|||||||
background: #1c71d8;
|
background: #1c71d8;
|
||||||
border-color: #3584e4;
|
border-color: #3584e4;
|
||||||
}
|
}
|
||||||
|
|
||||||
#Shift_L {
|
|
||||||
background: #2b292f;
|
|
||||||
border-color: #3e3a44
|
|
||||||
}
|
|
||||||
|
|
||||||
#Shift_L:active {
|
|
||||||
background: #1c71d8;
|
|
||||||
border-color: #3584e4;
|
|
||||||
}
|
|
||||||
|
|||||||
6
debian/changelog
vendored
6
debian/changelog
vendored
@ -1,3 +1,9 @@
|
|||||||
|
squeekboard (1.2.1) amber-phone; urgency=medium
|
||||||
|
|
||||||
|
* Use different distribution
|
||||||
|
|
||||||
|
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Tue, 08 Oct 2019 10:56:10 +0000
|
||||||
|
|
||||||
squeekboard (1.2.0) unstable; urgency=medium
|
squeekboard (1.2.0) unstable; urgency=medium
|
||||||
|
|
||||||
* Use Cargo-based dependencies
|
* Use Cargo-based dependencies
|
||||||
|
|||||||
2
debian/rules
vendored
2
debian/rules
vendored
@ -8,7 +8,7 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
|||||||
|
|
||||||
# The Debian version of linked-hash-map doesn't provide any hash,
|
# The Debian version of linked-hash-map doesn't provide any hash,
|
||||||
# causing Cargo to refuse to build with a crates.io copy
|
# causing Cargo to refuse to build with a crates.io copy
|
||||||
build:
|
build-arch:
|
||||||
rm Cargo.lock
|
rm Cargo.lock
|
||||||
dh $@ --builddirectory=_build --buildsystem=meson
|
dh $@ --builddirectory=_build --buildsystem=meson
|
||||||
|
|
||||||
|
|||||||
@ -52,8 +52,6 @@ typedef struct _EekGtkKeyboardPrivate
|
|||||||
{
|
{
|
||||||
EekRenderer *renderer;
|
EekRenderer *renderer;
|
||||||
LevelKeyboard *keyboard;
|
LevelKeyboard *keyboard;
|
||||||
GtkCssProvider *css_provider;
|
|
||||||
GtkStyleContext *scontext;
|
|
||||||
|
|
||||||
GdkEventSequence *sequence; // unowned reference
|
GdkEventSequence *sequence; // unowned reference
|
||||||
} EekGtkKeyboardPrivate;
|
} EekGtkKeyboardPrivate;
|
||||||
@ -98,7 +96,7 @@ eek_gtk_keyboard_real_draw (GtkWidget *self,
|
|||||||
if (!priv->renderer) {
|
if (!priv->renderer) {
|
||||||
PangoContext *pcontext = gtk_widget_get_pango_context (self);
|
PangoContext *pcontext = gtk_widget_get_pango_context (self);
|
||||||
|
|
||||||
priv->renderer = eek_renderer_new (priv->keyboard, pcontext, priv->scontext);
|
priv->renderer = eek_renderer_new (priv->keyboard, pcontext);
|
||||||
|
|
||||||
eek_renderer_set_allocation_size (priv->renderer,
|
eek_renderer_set_allocation_size (priv->renderer,
|
||||||
allocation.width,
|
allocation.width,
|
||||||
@ -383,22 +381,7 @@ eek_gtk_keyboard_class_init (EekGtkKeyboardClass *klass)
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
eek_gtk_keyboard_init (EekGtkKeyboard *self)
|
eek_gtk_keyboard_init (EekGtkKeyboard *self)
|
||||||
{
|
{}
|
||||||
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
|
|
||||||
|
|
||||||
/* 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");
|
|
||||||
|
|
||||||
/* Apply the style to the widget */
|
|
||||||
priv->scontext = gtk_widget_get_style_context (GTK_WIDGET(self));
|
|
||||||
gtk_style_context_add_class (priv->scontext, "keyboard");
|
|
||||||
gtk_style_context_add_provider (priv->scontext,
|
|
||||||
GTK_STYLE_PROVIDER(priv->css_provider),
|
|
||||||
GTK_STYLE_PROVIDER_PRIORITY_USER);
|
|
||||||
gtk_style_context_set_state (priv->scontext, GTK_STATE_FLAG_NORMAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* eek_gtk_keyboard_new:
|
* eek_gtk_keyboard_new:
|
||||||
|
|||||||
@ -1,217 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2006 Sergey V. Udaltsov <svu@gnome.org>
|
|
||||||
*
|
|
||||||
* 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, write to the
|
|
||||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
||||||
* Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
#include <pango/pangocairo.h>
|
|
||||||
|
|
||||||
#include "eek-types.h"
|
|
||||||
|
|
||||||
static gdouble
|
|
||||||
length (gdouble x, gdouble y)
|
|
||||||
{
|
|
||||||
return sqrt (x * x + y * y);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gdouble
|
|
||||||
point_line_distance (gdouble ax, gdouble ay, gdouble nx, gdouble ny)
|
|
||||||
{
|
|
||||||
return ax * nx + ay * ny;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
normal_form (gdouble ax, gdouble ay,
|
|
||||||
gdouble bx, gdouble by,
|
|
||||||
gdouble * nx, gdouble * ny, gdouble * d)
|
|
||||||
{
|
|
||||||
gdouble l;
|
|
||||||
|
|
||||||
*nx = by - ay;
|
|
||||||
*ny = ax - bx;
|
|
||||||
|
|
||||||
l = length (*nx, *ny);
|
|
||||||
|
|
||||||
*nx /= l;
|
|
||||||
*ny /= l;
|
|
||||||
|
|
||||||
*d = point_line_distance (ax, ay, *nx, *ny);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
inverse (gdouble a, gdouble b, gdouble c, gdouble d,
|
|
||||||
gdouble * e, gdouble * f, gdouble * g, gdouble * h)
|
|
||||||
{
|
|
||||||
gdouble det;
|
|
||||||
|
|
||||||
det = a * d - b * c;
|
|
||||||
|
|
||||||
*e = d / det;
|
|
||||||
*f = -b / det;
|
|
||||||
*g = -c / det;
|
|
||||||
*h = a / det;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
multiply (gdouble a, gdouble b, gdouble c, gdouble d,
|
|
||||||
gdouble e, gdouble f, gdouble * x, gdouble * y)
|
|
||||||
{
|
|
||||||
*x = a * e + b * f;
|
|
||||||
*y = c * e + d * f;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
intersect (gdouble n1x, gdouble n1y, gdouble d1,
|
|
||||||
gdouble n2x, gdouble n2y, gdouble d2, gdouble * x, gdouble * y)
|
|
||||||
{
|
|
||||||
gdouble e, f, g, h;
|
|
||||||
|
|
||||||
inverse (n1x, n1y, n2x, n2y, &e, &f, &g, &h);
|
|
||||||
multiply (e, f, g, h, d1, d2, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* draw an angle from the current point to b and then to c,
|
|
||||||
* with a rounded corner of the given radius.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
rounded_corner (cairo_t * cr,
|
|
||||||
gdouble bx, gdouble by,
|
|
||||||
gdouble cx, gdouble cy, gdouble radius)
|
|
||||||
{
|
|
||||||
gdouble ax, ay;
|
|
||||||
gdouble n1x, n1y, d1;
|
|
||||||
gdouble n2x, n2y, d2;
|
|
||||||
gdouble pd1, pd2;
|
|
||||||
gdouble ix, iy;
|
|
||||||
gdouble dist1, dist2;
|
|
||||||
gdouble nx, ny, d;
|
|
||||||
gdouble a1x, a1y, c1x, c1y;
|
|
||||||
gdouble phi1, phi2;
|
|
||||||
|
|
||||||
cairo_get_current_point (cr, &ax, &ay);
|
|
||||||
#ifdef KBDRAW_DEBUG
|
|
||||||
printf (" current point: (%f, %f), radius %f:\n", ax, ay,
|
|
||||||
radius);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* make sure radius is not too large */
|
|
||||||
dist1 = length (bx - ax, by - ay);
|
|
||||||
dist2 = length (cx - bx, cy - by);
|
|
||||||
|
|
||||||
radius = MIN (radius, MIN (dist1, dist2));
|
|
||||||
|
|
||||||
/* construct normal forms of the lines */
|
|
||||||
normal_form (ax, ay, bx, by, &n1x, &n1y, &d1);
|
|
||||||
normal_form (bx, by, cx, cy, &n2x, &n2y, &d2);
|
|
||||||
|
|
||||||
/* find which side of the line a,b the point c is on */
|
|
||||||
if (point_line_distance (cx, cy, n1x, n1y) < d1)
|
|
||||||
pd1 = d1 - radius;
|
|
||||||
else
|
|
||||||
pd1 = d1 + radius;
|
|
||||||
|
|
||||||
/* find which side of the line b,c the point a is on */
|
|
||||||
if (point_line_distance (ax, ay, n2x, n2y) < d2)
|
|
||||||
pd2 = d2 - radius;
|
|
||||||
else
|
|
||||||
pd2 = d2 + radius;
|
|
||||||
|
|
||||||
/* intersect the parallels to find the center of the arc */
|
|
||||||
intersect (n1x, n1y, pd1, n2x, n2y, pd2, &ix, &iy);
|
|
||||||
|
|
||||||
nx = (bx - ax) / dist1;
|
|
||||||
ny = (by - ay) / dist1;
|
|
||||||
d = point_line_distance (ix, iy, nx, ny);
|
|
||||||
|
|
||||||
/* a1 is the point on the line a-b where the arc starts */
|
|
||||||
intersect (n1x, n1y, d1, nx, ny, d, &a1x, &a1y);
|
|
||||||
|
|
||||||
nx = (cx - bx) / dist2;
|
|
||||||
ny = (cy - by) / dist2;
|
|
||||||
d = point_line_distance (ix, iy, nx, ny);
|
|
||||||
|
|
||||||
/* c1 is the point on the line b-c where the arc ends */
|
|
||||||
intersect (n2x, n2y, d2, nx, ny, d, &c1x, &c1y);
|
|
||||||
|
|
||||||
/* determine the first angle */
|
|
||||||
if (a1x - ix == 0)
|
|
||||||
phi1 = (a1y - iy > 0) ? M_PI_2 : 3 * M_PI_2;
|
|
||||||
else if (a1x - ix > 0)
|
|
||||||
phi1 = atan ((a1y - iy) / (a1x - ix));
|
|
||||||
else
|
|
||||||
phi1 = M_PI + atan ((a1y - iy) / (a1x - ix));
|
|
||||||
|
|
||||||
/* determine the second angle */
|
|
||||||
if (c1x - ix == 0)
|
|
||||||
phi2 = (c1y - iy > 0) ? M_PI_2 : 3 * M_PI_2;
|
|
||||||
else if (c1x - ix > 0)
|
|
||||||
phi2 = atan ((c1y - iy) / (c1x - ix));
|
|
||||||
else
|
|
||||||
phi2 = M_PI + atan ((c1y - iy) / (c1x - ix));
|
|
||||||
|
|
||||||
/* compute the difference between phi2 and phi1 mod 2pi */
|
|
||||||
d = phi2 - phi1;
|
|
||||||
while (d < 0)
|
|
||||||
d += 2 * M_PI;
|
|
||||||
while (d > 2 * M_PI)
|
|
||||||
d -= 2 * M_PI;
|
|
||||||
|
|
||||||
#ifdef KBDRAW_DEBUG
|
|
||||||
printf (" line 1 to: (%f, %f):\n", a1x, a1y);
|
|
||||||
#endif
|
|
||||||
if (!(isnan (a1x) || isnan (a1y)))
|
|
||||||
cairo_line_to (cr, a1x, a1y);
|
|
||||||
|
|
||||||
/* pick the short arc from phi1 to phi2 */
|
|
||||||
if (d < M_PI)
|
|
||||||
cairo_arc (cr, ix, iy, radius, phi1, phi2);
|
|
||||||
else
|
|
||||||
cairo_arc_negative (cr, ix, iy, radius, phi1, phi2);
|
|
||||||
|
|
||||||
#ifdef KBDRAW_DEBUG
|
|
||||||
printf (" line 2 to: (%f, %f):\n", cx, cy);
|
|
||||||
#endif
|
|
||||||
cairo_line_to (cr, cx, cy);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* renamed from rounded_polygon, use EekPoint instead of GdkPoint not
|
|
||||||
to depend on GTK+, and exported */
|
|
||||||
void
|
|
||||||
_eek_rounded_polygon (cairo_t *cr,
|
|
||||||
gdouble radius,
|
|
||||||
EekPoint *points,
|
|
||||||
guint num_points)
|
|
||||||
{
|
|
||||||
cairo_move_to (cr,
|
|
||||||
(gdouble) (points[num_points - 1].x +
|
|
||||||
points[0].x) / 2,
|
|
||||||
(gdouble) (points[num_points - 1].y +
|
|
||||||
points[0].y) / 2);
|
|
||||||
|
|
||||||
for (guint i = 0; i < num_points; i++) {
|
|
||||||
guint j = (i + 1) % num_points;
|
|
||||||
rounded_corner (cr, (gdouble) points[i].x,
|
|
||||||
(gdouble) points[i].y,
|
|
||||||
(gdouble) (points[i].x + points[j].x) / 2,
|
|
||||||
(gdouble) (points[i].y + points[j].y) / 2,
|
|
||||||
radius);
|
|
||||||
}
|
|
||||||
cairo_close_path (cr);
|
|
||||||
}
|
|
||||||
@ -29,7 +29,6 @@
|
|||||||
enum {
|
enum {
|
||||||
PROP_0,
|
PROP_0,
|
||||||
PROP_PCONTEXT,
|
PROP_PCONTEXT,
|
||||||
PROP_STYLE_CONTEXT,
|
|
||||||
PROP_LAST
|
PROP_LAST
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -38,11 +37,9 @@ typedef struct _EekRendererPrivate
|
|||||||
LevelKeyboard *keyboard;
|
LevelKeyboard *keyboard;
|
||||||
PangoContext *pcontext;
|
PangoContext *pcontext;
|
||||||
GtkCssProvider *css_provider;
|
GtkCssProvider *css_provider;
|
||||||
GtkStyleContext *scontext;
|
GtkStyleContext *layout_context;
|
||||||
GtkStyleContext *key_context;
|
GtkStyleContext *button_context; // TODO: maybe move a copy to each button
|
||||||
|
|
||||||
EekColor default_foreground_color;
|
|
||||||
EekColor default_background_color;
|
|
||||||
gdouble border_width;
|
gdouble border_width;
|
||||||
|
|
||||||
gdouble allocation_width;
|
gdouble allocation_width;
|
||||||
@ -54,6 +51,7 @@ typedef struct _EekRendererPrivate
|
|||||||
|
|
||||||
PangoFontDescription *ascii_font;
|
PangoFontDescription *ascii_font;
|
||||||
PangoFontDescription *font;
|
PangoFontDescription *font;
|
||||||
|
// TODO: Drop those or transform into general button surface caches
|
||||||
GHashTable *outline_surface_cache;
|
GHashTable *outline_surface_cache;
|
||||||
GHashTable *active_outline_surface_cache;
|
GHashTable *active_outline_surface_cache;
|
||||||
GHashTable *icons;
|
GHashTable *icons;
|
||||||
@ -63,15 +61,7 @@ typedef struct _EekRendererPrivate
|
|||||||
|
|
||||||
G_DEFINE_TYPE_WITH_PRIVATE (EekRenderer, eek_renderer, G_TYPE_OBJECT)
|
G_DEFINE_TYPE_WITH_PRIVATE (EekRenderer, eek_renderer, G_TYPE_OBJECT)
|
||||||
|
|
||||||
static const EekColor DEFAULT_FOREGROUND_COLOR = {0.3, 0.3, 0.3, 1.0};
|
|
||||||
static const EekColor DEFAULT_BACKGROUND_COLOR = {1.0, 1.0, 1.0, 1.0};
|
|
||||||
|
|
||||||
/* eek-keyboard-drawing.c */
|
/* eek-keyboard-drawing.c */
|
||||||
extern void _eek_rounded_polygon (cairo_t *cr,
|
|
||||||
gdouble radius,
|
|
||||||
EekPoint *points,
|
|
||||||
guint num_points);
|
|
||||||
|
|
||||||
static void eek_renderer_real_render_button_label (EekRenderer *self,
|
static void eek_renderer_real_render_button_label (EekRenderer *self,
|
||||||
PangoLayout *layout,
|
PangoLayout *layout,
|
||||||
const struct squeek_button *button);
|
const struct squeek_button *button);
|
||||||
@ -79,7 +69,7 @@ static void eek_renderer_real_render_button_label (EekRenderer *self,
|
|||||||
static void invalidate (EekRenderer *renderer);
|
static void invalidate (EekRenderer *renderer);
|
||||||
static void render_button (EekRenderer *self,
|
static void render_button (EekRenderer *self,
|
||||||
cairo_t *cr, struct button_place *place,
|
cairo_t *cr, struct button_place *place,
|
||||||
gboolean active);
|
gboolean pressed, gboolean locked);
|
||||||
|
|
||||||
struct _CreateKeyboardSurfaceCallbackData {
|
struct _CreateKeyboardSurfaceCallbackData {
|
||||||
cairo_t *cr;
|
cairo_t *cr;
|
||||||
@ -109,7 +99,7 @@ create_keyboard_surface_button_callback (struct squeek_button *button,
|
|||||||
.row = data->row,
|
.row = data->row,
|
||||||
.button = button,
|
.button = button,
|
||||||
};
|
};
|
||||||
render_button (data->renderer, data->cr, &place, FALSE);
|
render_button (data->renderer, data->cr, &place, FALSE, FALSE);
|
||||||
|
|
||||||
cairo_restore (data->cr);
|
cairo_restore (data->cr);
|
||||||
}
|
}
|
||||||
@ -140,7 +130,7 @@ render_keyboard_surface (EekRenderer *renderer, struct squeek_view *view)
|
|||||||
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
|
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
|
||||||
EekColor foreground;
|
EekColor foreground;
|
||||||
|
|
||||||
eek_renderer_get_foreground_color (renderer, priv->scontext, &foreground);
|
eek_renderer_get_foreground_color (renderer, priv->layout_context, &foreground);
|
||||||
|
|
||||||
EekBounds bounds = squeek_view_get_bounds (level_keyboard_current(priv->keyboard));
|
EekBounds bounds = squeek_view_get_bounds (level_keyboard_current(priv->keyboard));
|
||||||
|
|
||||||
@ -151,11 +141,11 @@ render_keyboard_surface (EekRenderer *renderer, struct squeek_view *view)
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Paint the background covering the entire widget area */
|
/* Paint the background covering the entire widget area */
|
||||||
gtk_render_background (priv->scontext,
|
gtk_render_background (priv->layout_context,
|
||||||
data.cr,
|
data.cr,
|
||||||
0, 0,
|
0, 0,
|
||||||
priv->allocation_width, priv->allocation_height);
|
priv->allocation_width, priv->allocation_height);
|
||||||
gtk_render_frame (priv->scontext,
|
gtk_render_frame (priv->layout_context,
|
||||||
data.cr,
|
data.cr,
|
||||||
0, 0,
|
0, 0,
|
||||||
priv->allocation_width, priv->allocation_height);
|
priv->allocation_width, priv->allocation_height);
|
||||||
@ -180,49 +170,25 @@ render_keyboard_surface (EekRenderer *renderer, struct squeek_view *view)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
render_button_outline (EekRenderer *renderer,
|
render_outline (cairo_t *cr,
|
||||||
cairo_t *cr,
|
GtkStyleContext *ctx,
|
||||||
const struct squeek_button *button,
|
EekBounds bounds)
|
||||||
gboolean active)
|
|
||||||
{
|
{
|
||||||
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
|
gtk_render_background (ctx, cr, 0, 0, bounds.width, bounds.height);
|
||||||
EekBounds bounds = squeek_button_get_bounds(button);
|
gtk_render_frame (ctx, cr, 0, 0, bounds.width, bounds.height);
|
||||||
|
|
||||||
/* Set the name of the button on the widget path, using the name obtained
|
|
||||||
from the button's symbol. */
|
|
||||||
g_autoptr (GtkWidgetPath) path = NULL;
|
|
||||||
path = gtk_widget_path_copy (gtk_style_context_get_path (priv->key_context));
|
|
||||||
const char *name = squeek_button_get_name(button);
|
|
||||||
gtk_widget_path_iter_set_name (path, -1, name);
|
|
||||||
|
|
||||||
/* Update the style context with the updated widget path. */
|
|
||||||
gtk_style_context_set_path (priv->key_context, path);
|
|
||||||
|
|
||||||
/* Set the state to take into account whether the button is active
|
|
||||||
(pressed) or normal. */
|
|
||||||
gtk_style_context_set_state(priv->key_context,
|
|
||||||
active ? GTK_STATE_FLAG_ACTIVE : GTK_STATE_FLAG_NORMAL);
|
|
||||||
|
|
||||||
gtk_render_background (priv->key_context,
|
|
||||||
cr, 0, 0, bounds.width, bounds.height);
|
|
||||||
gtk_render_frame (priv->key_context,
|
|
||||||
cr, 0, 0, bounds.width, bounds.height);
|
|
||||||
|
|
||||||
gtk_style_context_set_state(priv->key_context, GTK_STATE_FLAG_NORMAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void render_button_in_context(EekRenderer *self,
|
||||||
render_button (EekRenderer *self,
|
|
||||||
cairo_t *cr,
|
cairo_t *cr,
|
||||||
|
GtkStyleContext *ctx,
|
||||||
struct button_place *place,
|
struct button_place *place,
|
||||||
gboolean active)
|
gboolean active) {
|
||||||
{
|
|
||||||
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
|
|
||||||
cairo_surface_t *outline_surface;
|
cairo_surface_t *outline_surface;
|
||||||
GHashTable *outline_surface_cache;
|
GHashTable *outline_surface_cache;
|
||||||
PangoLayout *layout;
|
PangoLayout *layout;
|
||||||
PangoRectangle extents = { 0, };
|
PangoRectangle extents = { 0, };
|
||||||
EekColor foreground;
|
EekColor foreground;
|
||||||
|
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
|
||||||
|
|
||||||
/* render outline */
|
/* render outline */
|
||||||
EekBounds bounds = squeek_button_get_bounds(place->button);
|
EekBounds bounds = squeek_button_get_bounds(place->button);
|
||||||
@ -233,6 +199,7 @@ render_button (EekRenderer *self,
|
|||||||
outline_surface_cache = priv->outline_surface_cache;
|
outline_surface_cache = priv->outline_surface_cache;
|
||||||
|
|
||||||
outline_surface = g_hash_table_lookup (outline_surface_cache, place->button);
|
outline_surface = g_hash_table_lookup (outline_surface_cache, place->button);
|
||||||
|
|
||||||
if (!outline_surface) {
|
if (!outline_surface) {
|
||||||
cairo_t *cr;
|
cairo_t *cr;
|
||||||
|
|
||||||
@ -250,7 +217,7 @@ render_button (EekRenderer *self,
|
|||||||
|
|
||||||
cairo_save (cr);
|
cairo_save (cr);
|
||||||
eek_renderer_apply_transformation_for_button (self, cr, place, 1.0, FALSE);
|
eek_renderer_apply_transformation_for_button (self, cr, place, 1.0, FALSE);
|
||||||
render_button_outline (self, cr, place->button, active);
|
render_outline (cr, ctx, bounds);
|
||||||
cairo_restore (cr);
|
cairo_restore (cr);
|
||||||
|
|
||||||
cairo_destroy (cr);
|
cairo_destroy (cr);
|
||||||
@ -259,11 +226,10 @@ render_button (EekRenderer *self,
|
|||||||
(gpointer)place->button,
|
(gpointer)place->button,
|
||||||
outline_surface);
|
outline_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
cairo_set_source_surface (cr, outline_surface, 0.0, 0.0);
|
cairo_set_source_surface (cr, outline_surface, 0.0, 0.0);
|
||||||
cairo_paint (cr);
|
cairo_paint (cr);
|
||||||
|
|
||||||
eek_renderer_get_foreground_color (self, priv->key_context, &foreground);
|
eek_renderer_get_foreground_color (self, ctx, &foreground);
|
||||||
/* render icon (if any) */
|
/* render icon (if any) */
|
||||||
const char *icon_name = squeek_button_get_icon_name(place->button);
|
const char *icon_name = squeek_button_get_icon_name(place->button);
|
||||||
|
|
||||||
@ -294,7 +260,6 @@ render_button (EekRenderer *self,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* render label */
|
/* render label */
|
||||||
layout = pango_cairo_create_layout (cr);
|
layout = pango_cairo_create_layout (cr);
|
||||||
eek_renderer_real_render_button_label (self, layout, place->button);
|
eek_renderer_real_render_button_label (self, layout, place->button);
|
||||||
@ -311,10 +276,49 @@ render_button (EekRenderer *self,
|
|||||||
foreground.green,
|
foreground.green,
|
||||||
foreground.blue,
|
foreground.blue,
|
||||||
foreground.alpha);
|
foreground.alpha);
|
||||||
|
|
||||||
pango_cairo_show_layout (cr, layout);
|
pango_cairo_show_layout (cr, layout);
|
||||||
cairo_restore (cr);
|
cairo_restore (cr);
|
||||||
g_object_unref (layout);
|
g_object_unref (layout);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
render_button (EekRenderer *self,
|
||||||
|
cairo_t *cr,
|
||||||
|
struct button_place *place,
|
||||||
|
gboolean pressed,
|
||||||
|
gboolean locked)
|
||||||
|
{
|
||||||
|
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
|
||||||
|
|
||||||
|
GtkStyleContext *ctx = priv->button_context;
|
||||||
|
/* Set the name of the button on the widget path, using the name obtained
|
||||||
|
from the button's symbol. */
|
||||||
|
g_autoptr (GtkWidgetPath) path = NULL;
|
||||||
|
path = gtk_widget_path_copy (gtk_style_context_get_path (ctx));
|
||||||
|
const char *name = squeek_button_get_name(place->button);
|
||||||
|
gtk_widget_path_iter_set_name (path, -1, name);
|
||||||
|
|
||||||
|
/* Update the style context with the updated widget path. */
|
||||||
|
gtk_style_context_set_path (ctx, path);
|
||||||
|
/* Set the state to take into account whether the button is active
|
||||||
|
(pressed) or normal. */
|
||||||
|
gtk_style_context_set_state(ctx,
|
||||||
|
pressed ? GTK_STATE_FLAG_ACTIVE : GTK_STATE_FLAG_NORMAL);
|
||||||
|
const char *outline_name = squeek_button_get_outline_name(place->button);
|
||||||
|
if (locked) {
|
||||||
|
gtk_style_context_add_class(ctx, "locked");
|
||||||
|
}
|
||||||
|
gtk_style_context_add_class(ctx, outline_name);
|
||||||
|
|
||||||
|
render_button_in_context(self, cr, ctx, place, pressed);
|
||||||
|
|
||||||
|
// Save and restore functions don't work if gtk_render_* was used in between
|
||||||
|
gtk_style_context_set_state(ctx, GTK_STATE_FLAG_NORMAL);
|
||||||
|
gtk_style_context_remove_class(ctx, outline_name);
|
||||||
|
if (locked) {
|
||||||
|
gtk_style_context_remove_class(ctx, "locked");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -445,7 +449,8 @@ eek_renderer_real_render_button (EekRenderer *self,
|
|||||||
struct squeek_key *key = squeek_button_get_key(place->button);
|
struct squeek_key *key = squeek_button_get_key(place->button);
|
||||||
render_button (
|
render_button (
|
||||||
self, cr, place,
|
self, cr, place,
|
||||||
squeek_key_is_pressed(key) || squeek_key_is_locked (key)
|
squeek_key_is_pressed(key),
|
||||||
|
squeek_key_is_locked (key)
|
||||||
);
|
);
|
||||||
cairo_restore (cr);
|
cairo_restore (cr);
|
||||||
}
|
}
|
||||||
@ -496,10 +501,6 @@ eek_renderer_set_property (GObject *object,
|
|||||||
priv->pcontext = g_value_get_object (value);
|
priv->pcontext = g_value_get_object (value);
|
||||||
g_object_ref (priv->pcontext);
|
g_object_ref (priv->pcontext);
|
||||||
break;
|
break;
|
||||||
case PROP_STYLE_CONTEXT:
|
|
||||||
priv->scontext = g_value_get_object (value);
|
|
||||||
g_object_ref (priv->scontext);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
@ -576,15 +577,33 @@ eek_renderer_class_init (EekRendererClass *klass)
|
|||||||
g_object_class_install_property (gobject_class,
|
g_object_class_install_property (gobject_class,
|
||||||
PROP_PCONTEXT,
|
PROP_PCONTEXT,
|
||||||
pspec);
|
pspec);
|
||||||
|
}
|
||||||
|
|
||||||
pspec = g_param_spec_object ("style-context",
|
|
||||||
"GTK Style Context",
|
static GType new_type(char *name) {
|
||||||
"GTK Style Context",
|
GTypeInfo info = {0};
|
||||||
GTK_TYPE_STYLE_CONTEXT,
|
info.class_size = sizeof(GtkWidgetClass);
|
||||||
G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE);
|
info.instance_size = sizeof(GtkWidget);
|
||||||
g_object_class_install_property (gobject_class,
|
|
||||||
PROP_STYLE_CONTEXT,
|
return g_type_register_static(GTK_TYPE_WIDGET, name, &info,
|
||||||
pspec);
|
G_TYPE_FLAG_ABSTRACT
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GType layout_type() {
|
||||||
|
static GType type = 0;
|
||||||
|
if (!type) {
|
||||||
|
type = new_type("sq_view");
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GType button_type() {
|
||||||
|
static GType type = 0;
|
||||||
|
if (!type) {
|
||||||
|
type = new_type("sq_button");
|
||||||
|
}
|
||||||
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -594,8 +613,6 @@ eek_renderer_init (EekRenderer *self)
|
|||||||
|
|
||||||
priv->keyboard = NULL;
|
priv->keyboard = NULL;
|
||||||
priv->pcontext = NULL;
|
priv->pcontext = NULL;
|
||||||
priv->default_foreground_color = DEFAULT_FOREGROUND_COLOR;
|
|
||||||
priv->default_background_color = DEFAULT_BACKGROUND_COLOR;
|
|
||||||
priv->border_width = 1.0;
|
priv->border_width = 1.0;
|
||||||
priv->allocation_width = 0.0;
|
priv->allocation_width = 0.0;
|
||||||
priv->allocation_height = 0.0;
|
priv->allocation_height = 0.0;
|
||||||
@ -627,18 +644,30 @@ eek_renderer_init (EekRenderer *self)
|
|||||||
gtk_css_provider_load_from_resource (priv->css_provider,
|
gtk_css_provider_load_from_resource (priv->css_provider,
|
||||||
"/sm/puri/squeekboard/style.css");
|
"/sm/puri/squeekboard/style.css");
|
||||||
|
|
||||||
/* Create a style context for keys */
|
/* Create a style context for the layout */
|
||||||
priv->key_context = gtk_style_context_new ();
|
GtkWidgetPath *path = gtk_widget_path_new();
|
||||||
gtk_style_context_add_class (priv->key_context, "key");
|
gtk_widget_path_append_type(path, layout_type());
|
||||||
gtk_style_context_add_provider (priv->key_context,
|
|
||||||
|
priv->layout_context = gtk_style_context_new();
|
||||||
|
gtk_style_context_set_path(priv->layout_context, path);
|
||||||
|
gtk_widget_path_unref(path);
|
||||||
|
gtk_style_context_add_provider (priv->layout_context,
|
||||||
GTK_STYLE_PROVIDER(priv->css_provider),
|
GTK_STYLE_PROVIDER(priv->css_provider),
|
||||||
GTK_STYLE_PROVIDER_PRIORITY_USER);
|
GTK_STYLE_PROVIDER_PRIORITY_USER);
|
||||||
|
|
||||||
g_autoptr (GtkWidgetPath) path = NULL;
|
/* Create a style context for the buttons */
|
||||||
path = gtk_widget_path_new();
|
path = gtk_widget_path_new();
|
||||||
gtk_widget_path_append_type (path, GTK_TYPE_BUTTON);
|
gtk_widget_path_append_type(path, layout_type());
|
||||||
gtk_style_context_set_path (priv->key_context, path);
|
gtk_widget_path_append_type(path, button_type());
|
||||||
gtk_style_context_set_state (priv->key_context, GTK_STATE_FLAG_NORMAL);
|
priv->button_context = gtk_style_context_new ();
|
||||||
|
gtk_style_context_set_path(priv->button_context, path);
|
||||||
|
gtk_widget_path_unref(path);
|
||||||
|
|
||||||
|
gtk_style_context_set_parent(priv->button_context, priv->layout_context);
|
||||||
|
gtk_style_context_set_state (priv->button_context, GTK_STATE_FLAG_NORMAL);
|
||||||
|
gtk_style_context_add_provider (priv->button_context,
|
||||||
|
GTK_STYLE_PROVIDER(priv->css_provider),
|
||||||
|
GTK_STYLE_PROVIDER_PRIORITY_USER);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -660,12 +689,10 @@ invalidate (EekRenderer *renderer)
|
|||||||
|
|
||||||
EekRenderer *
|
EekRenderer *
|
||||||
eek_renderer_new (LevelKeyboard *keyboard,
|
eek_renderer_new (LevelKeyboard *keyboard,
|
||||||
PangoContext *pcontext,
|
PangoContext *pcontext)
|
||||||
GtkStyleContext *scontext)
|
|
||||||
{
|
{
|
||||||
EekRenderer *renderer = g_object_new (EEK_TYPE_RENDERER,
|
EekRenderer *renderer = g_object_new (EEK_TYPE_RENDERER,
|
||||||
"pango-context", pcontext,
|
"pango-context", pcontext,
|
||||||
"style-context", scontext,
|
|
||||||
NULL);
|
NULL);
|
||||||
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
|
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
|
||||||
priv->keyboard = keyboard;
|
priv->keyboard = keyboard;
|
||||||
@ -759,7 +786,7 @@ eek_renderer_get_button_bounds (EekRenderer *renderer,
|
|||||||
|
|
||||||
min = points[2];
|
min = points[2];
|
||||||
max = points[0];
|
max = points[0];
|
||||||
for (uint i = 0; i < G_N_ELEMENTS(points); i++) {
|
for (unsigned i = 0; i < G_N_ELEMENTS(points); i++) {
|
||||||
eek_point_rotate (&points[i], angle);
|
eek_point_rotate (&points[i], angle);
|
||||||
if (points[i].x < min.x)
|
if (points[i].x < min.x)
|
||||||
min.x = points[i].x;
|
min.x = points[i].x;
|
||||||
@ -862,30 +889,6 @@ eek_renderer_render_keyboard (EekRenderer *renderer,
|
|||||||
EEK_RENDERER_GET_CLASS(renderer)->render_keyboard (renderer, cr);
|
EEK_RENDERER_GET_CLASS(renderer)->render_keyboard (renderer, cr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
eek_renderer_set_default_foreground_color (EekRenderer *renderer,
|
|
||||||
const EekColor *color)
|
|
||||||
{
|
|
||||||
g_return_if_fail (EEK_IS_RENDERER(renderer));
|
|
||||||
g_return_if_fail (color);
|
|
||||||
|
|
||||||
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
|
|
||||||
|
|
||||||
memcpy (&priv->default_foreground_color, color, sizeof(EekColor));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
eek_renderer_set_default_background_color (EekRenderer *renderer,
|
|
||||||
const EekColor *color)
|
|
||||||
{
|
|
||||||
g_return_if_fail (EEK_IS_RENDERER(renderer));
|
|
||||||
g_return_if_fail (color);
|
|
||||||
|
|
||||||
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
|
|
||||||
|
|
||||||
memcpy (&priv->default_background_color, color, sizeof(EekColor));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
eek_renderer_get_foreground_color (EekRenderer *renderer,
|
eek_renderer_get_foreground_color (EekRenderer *renderer,
|
||||||
GtkStyleContext *context,
|
GtkStyleContext *context,
|
||||||
@ -927,7 +930,7 @@ eek_are_bounds_inside (EekBounds bounds, EekPoint point, EekPoint origin, int32_
|
|||||||
points[3].x = points[0].x;
|
points[3].x = points[0].x;
|
||||||
points[3].y = points[2].y;
|
points[3].y = points[2].y;
|
||||||
|
|
||||||
for (uint i = 0; i < G_N_ELEMENTS(points); i++) {
|
for (unsigned i = 0; i < G_N_ELEMENTS(points); i++) {
|
||||||
eek_point_rotate (&points[i], angle);
|
eek_point_rotate (&points[i], angle);
|
||||||
points[i].x += origin.x;
|
points[i].x += origin.x;
|
||||||
points[i].y += origin.y;
|
points[i].y += origin.y;
|
||||||
|
|||||||
@ -57,8 +57,7 @@ struct _EekRendererClass
|
|||||||
|
|
||||||
GType eek_renderer_get_type (void) G_GNUC_CONST;
|
GType eek_renderer_get_type (void) G_GNUC_CONST;
|
||||||
EekRenderer *eek_renderer_new (LevelKeyboard *keyboard,
|
EekRenderer *eek_renderer_new (LevelKeyboard *keyboard,
|
||||||
PangoContext *pcontext,
|
PangoContext *pcontext);
|
||||||
GtkStyleContext *scontext);
|
|
||||||
void eek_renderer_set_allocation_size
|
void eek_renderer_set_allocation_size
|
||||||
(EekRenderer *renderer,
|
(EekRenderer *renderer,
|
||||||
gdouble width,
|
gdouble width,
|
||||||
|
|||||||
@ -51,7 +51,6 @@ enum {
|
|||||||
PROP_0, // Magic: without this, keyboard is not useable in g_object_notify
|
PROP_0, // Magic: without this, keyboard is not useable in g_object_notify
|
||||||
PROP_KEYBOARD,
|
PROP_KEYBOARD,
|
||||||
PROP_VISIBLE,
|
PROP_VISIBLE,
|
||||||
PROP_FULLSCREEN,
|
|
||||||
PROP_LAST
|
PROP_LAST
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -70,7 +69,6 @@ static guint signals[LAST_SIGNAL] = { 0, };
|
|||||||
struct _EekboardContextServicePrivate {
|
struct _EekboardContextServicePrivate {
|
||||||
gboolean enabled;
|
gboolean enabled;
|
||||||
gboolean visible;
|
gboolean visible;
|
||||||
gboolean fullscreen;
|
|
||||||
|
|
||||||
LevelKeyboard *keyboard; // currently used keyboard
|
LevelKeyboard *keyboard; // currently used keyboard
|
||||||
GHashTable *keyboard_hash; // a table of available keyboards, per layout
|
GHashTable *keyboard_hash; // a table of available keyboards, per layout
|
||||||
@ -170,9 +168,6 @@ eekboard_context_service_set_property (GObject *object,
|
|||||||
case PROP_VISIBLE:
|
case PROP_VISIBLE:
|
||||||
context->priv->visible = g_value_get_boolean (value);
|
context->priv->visible = g_value_get_boolean (value);
|
||||||
break;
|
break;
|
||||||
case PROP_FULLSCREEN:
|
|
||||||
context->priv->fullscreen = g_value_get_boolean (value);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
@ -194,9 +189,6 @@ eekboard_context_service_get_property (GObject *object,
|
|||||||
case PROP_VISIBLE:
|
case PROP_VISIBLE:
|
||||||
g_value_set_boolean (value, context->priv->visible);
|
g_value_set_boolean (value, context->priv->visible);
|
||||||
break;
|
break;
|
||||||
case PROP_FULLSCREEN:
|
|
||||||
g_value_set_boolean (value, context->priv->fullscreen);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
@ -401,20 +393,6 @@ eekboard_context_service_class_init (EekboardContextServiceClass *klass)
|
|||||||
g_object_class_install_property (gobject_class,
|
g_object_class_install_property (gobject_class,
|
||||||
PROP_VISIBLE,
|
PROP_VISIBLE,
|
||||||
pspec);
|
pspec);
|
||||||
|
|
||||||
/**
|
|
||||||
* EekboardContextService:fullscreen:
|
|
||||||
*
|
|
||||||
* Flag to indicate if keyboard is rendered in fullscreen mode.
|
|
||||||
*/
|
|
||||||
pspec = g_param_spec_boolean ("fullscreen",
|
|
||||||
"Fullscreen",
|
|
||||||
"Fullscreen",
|
|
||||||
FALSE,
|
|
||||||
G_PARAM_READWRITE);
|
|
||||||
g_object_class_install_property (gobject_class,
|
|
||||||
PROP_FULLSCREEN,
|
|
||||||
pspec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -524,19 +502,6 @@ eekboard_context_service_get_keyboard (EekboardContextService *context)
|
|||||||
return context->priv->keyboard;
|
return context->priv->keyboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* eekboard_context_service_get_fullscreen:
|
|
||||||
* @context: an #EekboardContextService
|
|
||||||
*
|
|
||||||
* Check if keyboard is rendered in fullscreen mode in @context.
|
|
||||||
* Returns: %TRUE or %FALSE
|
|
||||||
*/
|
|
||||||
gboolean
|
|
||||||
eekboard_context_service_get_fullscreen (EekboardContextService *context)
|
|
||||||
{
|
|
||||||
return context->priv->fullscreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
void eekboard_context_service_set_keymap(EekboardContextService *context,
|
void eekboard_context_service_set_keymap(EekboardContextService *context,
|
||||||
const LevelKeyboard *keyboard)
|
const LevelKeyboard *keyboard)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -98,8 +98,6 @@ void eekboard_context_service_hide_keyboard
|
|||||||
(EekboardContextService *context);
|
(EekboardContextService *context);
|
||||||
void eekboard_context_service_destroy (EekboardContextService *context);
|
void eekboard_context_service_destroy (EekboardContextService *context);
|
||||||
LevelKeyboard *eekboard_context_service_get_keyboard(EekboardContextService *context);
|
LevelKeyboard *eekboard_context_service_get_keyboard(EekboardContextService *context);
|
||||||
gboolean eekboard_context_service_get_fullscreen
|
|
||||||
(EekboardContextService *context);
|
|
||||||
|
|
||||||
void eekboard_context_service_set_keymap(EekboardContextService *context,
|
void eekboard_context_service_set_keymap(EekboardContextService *context,
|
||||||
const LevelKeyboard *keyboard);
|
const LevelKeyboard *keyboard);
|
||||||
|
|||||||
@ -1,654 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
|
|
||||||
* Copyright (C) 2010-2011 Red Hat, Inc.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SECTION:eekboard-context
|
|
||||||
* @short_description: client interface of eekboard input context service
|
|
||||||
*
|
|
||||||
* The #EekboardContext class provides a client access to remote input
|
|
||||||
* context.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "eekboard/eekboard-context.h"
|
|
||||||
//#include "eekboard/eekboard-marshalers.h"
|
|
||||||
|
|
||||||
#define I_(string) g_intern_static_string (string)
|
|
||||||
|
|
||||||
enum {
|
|
||||||
ENABLED,
|
|
||||||
DISABLED,
|
|
||||||
DESTROYED,
|
|
||||||
LAST_SIGNAL
|
|
||||||
};
|
|
||||||
|
|
||||||
static guint signals[LAST_SIGNAL] = { 0, };
|
|
||||||
|
|
||||||
enum {
|
|
||||||
PROP_0,
|
|
||||||
PROP_VISIBLE,
|
|
||||||
PROP_LAST
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct _EekboardContextPrivate
|
|
||||||
{
|
|
||||||
gboolean visible;
|
|
||||||
gboolean enabled;
|
|
||||||
gboolean fullscreen;
|
|
||||||
gint group;
|
|
||||||
} EekboardContextPrivate;
|
|
||||||
|
|
||||||
G_DEFINE_TYPE_WITH_PRIVATE (EekboardContext, eekboard_context, G_TYPE_DBUS_PROXY)
|
|
||||||
|
|
||||||
static void
|
|
||||||
eekboard_context_real_g_signal (GDBusProxy *self,
|
|
||||||
const gchar *sender_name,
|
|
||||||
const gchar *signal_name,
|
|
||||||
GVariant *parameters)
|
|
||||||
{
|
|
||||||
EekboardContext *context = EEKBOARD_CONTEXT (self);
|
|
||||||
EekboardContextPrivate *priv = eekboard_context_get_instance_private (context);
|
|
||||||
|
|
||||||
if (g_strcmp0 (signal_name, "Enabled") == 0) {
|
|
||||||
g_signal_emit (context, signals[ENABLED], 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_strcmp0 (signal_name, "Disabled") == 0) {
|
|
||||||
g_signal_emit (context, signals[DISABLED], 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_strcmp0 (signal_name, "Destroyed") == 0) {
|
|
||||||
g_signal_emit (context, signals[DESTROYED], 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_strcmp0 (signal_name, "VisibilityChanged") == 0) {
|
|
||||||
gboolean visible = FALSE;
|
|
||||||
|
|
||||||
g_variant_get (parameters, "(b)", &visible);
|
|
||||||
if (visible != priv->visible) {
|
|
||||||
priv->visible = visible;
|
|
||||||
g_object_notify (G_OBJECT(context), "visible");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_strcmp0 (signal_name, "GroupChanged") == 0) {
|
|
||||||
gint group = 0;
|
|
||||||
|
|
||||||
g_variant_get (parameters, "(i)", &group);
|
|
||||||
if (group != priv->group) {
|
|
||||||
priv->group = group;
|
|
||||||
/* g_object_notify (G_OBJECT(context), "group"); */
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_return_if_reached ();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
eekboard_context_real_enabled (EekboardContext *self)
|
|
||||||
{
|
|
||||||
EekboardContextPrivate *priv = eekboard_context_get_instance_private (self);
|
|
||||||
|
|
||||||
priv->enabled = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
eekboard_context_real_disabled (EekboardContext *self)
|
|
||||||
{
|
|
||||||
EekboardContextPrivate *priv = eekboard_context_get_instance_private (self);
|
|
||||||
|
|
||||||
priv->enabled = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
eekboard_context_real_destroyed (EekboardContext *self)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
eekboard_context_get_property (GObject *object,
|
|
||||||
guint prop_id,
|
|
||||||
GValue *value,
|
|
||||||
GParamSpec *pspec)
|
|
||||||
{
|
|
||||||
EekboardContext *context = EEKBOARD_CONTEXT(object);
|
|
||||||
EekboardContextPrivate *priv = eekboard_context_get_instance_private (context);
|
|
||||||
|
|
||||||
switch (prop_id) {
|
|
||||||
case PROP_VISIBLE:
|
|
||||||
g_value_set_boolean (value, priv->visible);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
eekboard_context_class_init (EekboardContextClass *klass)
|
|
||||||
{
|
|
||||||
GDBusProxyClass *proxy_class = G_DBUS_PROXY_CLASS (klass);
|
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
||||||
GParamSpec *pspec;
|
|
||||||
|
|
||||||
klass->enabled = eekboard_context_real_enabled;
|
|
||||||
klass->disabled = eekboard_context_real_disabled;
|
|
||||||
klass->destroyed = eekboard_context_real_destroyed;
|
|
||||||
|
|
||||||
proxy_class->g_signal = eekboard_context_real_g_signal;
|
|
||||||
|
|
||||||
gobject_class->get_property = eekboard_context_get_property;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EekboardContext:visible:
|
|
||||||
*
|
|
||||||
* Flag to indicate if keyboard is visible or not.
|
|
||||||
*/
|
|
||||||
pspec = g_param_spec_boolean ("visible",
|
|
||||||
"visible",
|
|
||||||
"Flag that indicates if keyboard is visible",
|
|
||||||
FALSE,
|
|
||||||
G_PARAM_READABLE);
|
|
||||||
g_object_class_install_property (gobject_class,
|
|
||||||
PROP_VISIBLE,
|
|
||||||
pspec);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EekboardContext::enabled:
|
|
||||||
* @context: an #EekboardContext
|
|
||||||
*
|
|
||||||
* Emitted when @context is enabled.
|
|
||||||
*/
|
|
||||||
signals[ENABLED] =
|
|
||||||
g_signal_new (I_("enabled"),
|
|
||||||
G_TYPE_FROM_CLASS(gobject_class),
|
|
||||||
G_SIGNAL_RUN_LAST,
|
|
||||||
G_STRUCT_OFFSET(EekboardContextClass, enabled),
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
g_cclosure_marshal_VOID__VOID,
|
|
||||||
G_TYPE_NONE,
|
|
||||||
0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EekboardContext::disabled:
|
|
||||||
* @context: an #EekboardContext
|
|
||||||
*
|
|
||||||
* The ::disabled signal is emitted each time @context is disabled.
|
|
||||||
*/
|
|
||||||
signals[DISABLED] =
|
|
||||||
g_signal_new (I_("disabled"),
|
|
||||||
G_TYPE_FROM_CLASS(gobject_class),
|
|
||||||
G_SIGNAL_RUN_LAST,
|
|
||||||
G_STRUCT_OFFSET(EekboardContextClass, disabled),
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
g_cclosure_marshal_VOID__VOID,
|
|
||||||
G_TYPE_NONE,
|
|
||||||
0);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EekboardContext::destroyed:
|
|
||||||
* @context: an #EekboardContext
|
|
||||||
*
|
|
||||||
* The ::destroyed signal is emitted each time the name of remote
|
|
||||||
* end is vanished.
|
|
||||||
*/
|
|
||||||
signals[DESTROYED] =
|
|
||||||
g_signal_new (I_("destroyed"),
|
|
||||||
G_TYPE_FROM_CLASS(gobject_class),
|
|
||||||
G_SIGNAL_RUN_LAST,
|
|
||||||
G_STRUCT_OFFSET(EekboardContextClass, destroyed),
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
g_cclosure_marshal_VOID__VOID,
|
|
||||||
G_TYPE_NONE,
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
eekboard_context_init (EekboardContext *self)
|
|
||||||
{
|
|
||||||
/* void */
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
context_name_vanished_callback (GDBusConnection *connection,
|
|
||||||
const gchar *name,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
EekboardContext *context = user_data;
|
|
||||||
g_signal_emit (context, signals[DESTROYED], 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* eekboard_context_new:
|
|
||||||
* @connection: a #GDBusConnection
|
|
||||||
* @object_path: object path
|
|
||||||
* @cancellable: a #GCancellable
|
|
||||||
*
|
|
||||||
* Create a D-Bus proxy of an input context maintained by
|
|
||||||
* eekboard-server. This function is seldom called from applications
|
|
||||||
* since eekboard_server_create_context() calls it implicitly.
|
|
||||||
*/
|
|
||||||
EekboardContext *
|
|
||||||
eekboard_context_new (GDBusConnection *connection,
|
|
||||||
const gchar *object_path,
|
|
||||||
GCancellable *cancellable)
|
|
||||||
{
|
|
||||||
GInitable *initable;
|
|
||||||
GError *error;
|
|
||||||
|
|
||||||
g_return_val_if_fail (object_path != NULL, NULL);
|
|
||||||
g_return_val_if_fail (G_IS_DBUS_CONNECTION(connection), NULL);
|
|
||||||
|
|
||||||
error = NULL;
|
|
||||||
initable =
|
|
||||||
g_initable_new (EEKBOARD_TYPE_CONTEXT,
|
|
||||||
cancellable,
|
|
||||||
&error,
|
|
||||||
"g-name", "org.fedorahosted.Eekboard",
|
|
||||||
"g-connection", connection,
|
|
||||||
"g-interface-name", "org.fedorahosted.Eekboard.Context",
|
|
||||||
"g-object-path", object_path,
|
|
||||||
NULL);
|
|
||||||
if (initable != NULL) {
|
|
||||||
EekboardContext *context = EEKBOARD_CONTEXT (initable);
|
|
||||||
gchar *name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY(context));
|
|
||||||
|
|
||||||
if (name_owner == NULL) {
|
|
||||||
g_object_unref (context);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* the vanished callback is called when the server is disconnected */
|
|
||||||
g_bus_watch_name_on_connection (connection,
|
|
||||||
name_owner,
|
|
||||||
G_BUS_NAME_WATCHER_FLAGS_NONE,
|
|
||||||
NULL,
|
|
||||||
context_name_vanished_callback,
|
|
||||||
context,
|
|
||||||
NULL);
|
|
||||||
g_free (name_owner);
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_warning ("can't create context client: %s", error->message);
|
|
||||||
g_error_free (error);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
context_async_ready_callback (GObject *source_object,
|
|
||||||
GAsyncResult *res,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
GError *error = NULL;
|
|
||||||
GVariant *result;
|
|
||||||
|
|
||||||
result = g_dbus_proxy_call_finish (G_DBUS_PROXY(source_object),
|
|
||||||
res,
|
|
||||||
&error);
|
|
||||||
if (result)
|
|
||||||
g_variant_unref (result);
|
|
||||||
else {
|
|
||||||
g_warning ("error in D-Bus proxy call: %s", error->message);
|
|
||||||
g_error_free (error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* eekboard_context_add_keyboard:
|
|
||||||
* @context: an #EekboardContext
|
|
||||||
* @keyboard: a string representing keyboard
|
|
||||||
* @cancellable: a #GCancellable
|
|
||||||
*
|
|
||||||
* Register @keyboard in @context.
|
|
||||||
*/
|
|
||||||
guint
|
|
||||||
eekboard_context_add_keyboard (EekboardContext *context,
|
|
||||||
const gchar *keyboard,
|
|
||||||
GCancellable *cancellable)
|
|
||||||
{
|
|
||||||
GVariant *result;
|
|
||||||
GError *error;
|
|
||||||
|
|
||||||
g_return_val_if_fail (EEKBOARD_IS_CONTEXT(context), 0);
|
|
||||||
|
|
||||||
error = NULL;
|
|
||||||
result = g_dbus_proxy_call_sync (G_DBUS_PROXY(context),
|
|
||||||
"AddKeyboard",
|
|
||||||
g_variant_new ("(s)", keyboard),
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
|
||||||
-1,
|
|
||||||
cancellable,
|
|
||||||
&error);
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
guint keyboard_id;
|
|
||||||
|
|
||||||
g_variant_get (result, "(u)", &keyboard_id);
|
|
||||||
g_variant_unref (result);
|
|
||||||
|
|
||||||
return keyboard_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_warning ("error in AddKeyboard call: %s", error->message);
|
|
||||||
g_error_free (error);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* eekboard_context_remove_keyboard:
|
|
||||||
* @context: an #EekboardContext
|
|
||||||
* @keyboard_id: keyboard ID
|
|
||||||
* @cancellable: a #GCancellable
|
|
||||||
*
|
|
||||||
* Unregister the keyboard with @keyboard_id in @context.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
eekboard_context_remove_keyboard (EekboardContext *context,
|
|
||||||
guint keyboard_id,
|
|
||||||
GCancellable *cancellable)
|
|
||||||
{
|
|
||||||
g_return_if_fail (EEKBOARD_IS_CONTEXT(context));
|
|
||||||
|
|
||||||
g_dbus_proxy_call (G_DBUS_PROXY(context),
|
|
||||||
"RemoveKeyboard",
|
|
||||||
g_variant_new ("(u)", keyboard_id),
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
|
||||||
-1,
|
|
||||||
cancellable,
|
|
||||||
context_async_ready_callback,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* eekboard_context_set_keyboard:
|
|
||||||
* @context: an #EekboardContext
|
|
||||||
* @keyboard_id: keyboard ID
|
|
||||||
* @cancellable: a #GCancellable
|
|
||||||
*
|
|
||||||
* Select a keyboard with ID @keyboard_id in @context.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
eekboard_context_set_keyboard (EekboardContext *context,
|
|
||||||
guint keyboard_id,
|
|
||||||
GCancellable *cancellable)
|
|
||||||
{
|
|
||||||
g_return_if_fail (EEKBOARD_IS_CONTEXT(context));
|
|
||||||
|
|
||||||
g_dbus_proxy_call (G_DBUS_PROXY(context),
|
|
||||||
"SetKeyboard",
|
|
||||||
g_variant_new ("(u)", keyboard_id),
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
|
||||||
-1,
|
|
||||||
cancellable,
|
|
||||||
context_async_ready_callback,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* eekboard_context_set_group:
|
|
||||||
* @context: an #EekboardContext
|
|
||||||
* @group: group number
|
|
||||||
* @cancellable: a #GCancellable
|
|
||||||
*
|
|
||||||
* Set the keyboard group of @context.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
eekboard_context_set_group (EekboardContext *context,
|
|
||||||
gint group,
|
|
||||||
GCancellable *cancellable)
|
|
||||||
{
|
|
||||||
g_return_if_fail (EEKBOARD_IS_CONTEXT(context));
|
|
||||||
|
|
||||||
EekboardContextPrivate *priv = eekboard_context_get_instance_private (context);
|
|
||||||
|
|
||||||
if (priv->group != group) {
|
|
||||||
g_dbus_proxy_call (G_DBUS_PROXY(context),
|
|
||||||
"SetGroup",
|
|
||||||
g_variant_new ("(i)", group),
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
|
||||||
-1,
|
|
||||||
cancellable,
|
|
||||||
context_async_ready_callback,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* eekboard_context_get_group:
|
|
||||||
* @context: an #EekboardContext
|
|
||||||
* @cancellable: a #GCancellable
|
|
||||||
*
|
|
||||||
* Get the keyboard group of @context.
|
|
||||||
*/
|
|
||||||
gint
|
|
||||||
eekboard_context_get_group (EekboardContext *context,
|
|
||||||
GCancellable *cancellable)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (EEKBOARD_IS_CONTEXT(context), 0);
|
|
||||||
|
|
||||||
EekboardContextPrivate *priv = eekboard_context_get_instance_private (context);
|
|
||||||
|
|
||||||
return priv->group;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* eekboard_context_show_keyboard:
|
|
||||||
* @context: an #EekboardContext
|
|
||||||
* @cancellable: a #GCancellable
|
|
||||||
*
|
|
||||||
* Request eekboard-server to show a keyboard set by
|
|
||||||
* eekboard_context_set_keyboard().
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
eekboard_context_show_keyboard (EekboardContext *context,
|
|
||||||
GCancellable *cancellable)
|
|
||||||
{
|
|
||||||
g_return_if_fail (EEKBOARD_IS_CONTEXT(context));
|
|
||||||
|
|
||||||
EekboardContextPrivate *priv = eekboard_context_get_instance_private (context);
|
|
||||||
|
|
||||||
if (priv->enabled) {
|
|
||||||
g_dbus_proxy_call (G_DBUS_PROXY(context),
|
|
||||||
"ShowKeyboard",
|
|
||||||
NULL,
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
|
||||||
-1,
|
|
||||||
cancellable,
|
|
||||||
context_async_ready_callback,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* eekboard_context_hide_keyboard:
|
|
||||||
* @context: an #EekboardContext
|
|
||||||
* @cancellable: a #GCancellable
|
|
||||||
*
|
|
||||||
* Request eekboard-server to hide a keyboard.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
eekboard_context_hide_keyboard (EekboardContext *context,
|
|
||||||
GCancellable *cancellable)
|
|
||||||
{
|
|
||||||
g_return_if_fail (EEKBOARD_IS_CONTEXT(context));
|
|
||||||
|
|
||||||
EekboardContextPrivate *priv = eekboard_context_get_instance_private (context);
|
|
||||||
|
|
||||||
if (priv->enabled) {
|
|
||||||
g_dbus_proxy_call (G_DBUS_PROXY(context),
|
|
||||||
"HideKeyboard",
|
|
||||||
NULL,
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
|
||||||
-1,
|
|
||||||
cancellable,
|
|
||||||
context_async_ready_callback,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* eekboard_context_press_keycode:
|
|
||||||
* @context: an #EekboardContext
|
|
||||||
* @keycode: keycode number
|
|
||||||
* @cancellable: a #GCancellable
|
|
||||||
*
|
|
||||||
* Tell eekboard-server that a key identified by @keycode is pressed.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
eekboard_context_press_keycode (EekboardContext *context,
|
|
||||||
guint keycode,
|
|
||||||
GCancellable *cancellable)
|
|
||||||
{
|
|
||||||
g_return_if_fail (EEKBOARD_IS_CONTEXT(context));
|
|
||||||
|
|
||||||
EekboardContextPrivate *priv = eekboard_context_get_instance_private (context);
|
|
||||||
|
|
||||||
if (priv->enabled) {
|
|
||||||
g_dbus_proxy_call (G_DBUS_PROXY(context),
|
|
||||||
"PressKeycode",
|
|
||||||
g_variant_new ("(u)", keycode),
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
|
||||||
-1,
|
|
||||||
cancellable,
|
|
||||||
context_async_ready_callback,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* eekboard_context_release_keycode:
|
|
||||||
* @context: an #EekboardContext
|
|
||||||
* @keycode: keycode number
|
|
||||||
* @cancellable: a #GCancellable
|
|
||||||
*
|
|
||||||
* Tell eekboard-server that a key identified by @keycode is released.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
eekboard_context_release_keycode (EekboardContext *context,
|
|
||||||
guint keycode,
|
|
||||||
GCancellable *cancellable)
|
|
||||||
{
|
|
||||||
g_return_if_fail (EEKBOARD_IS_CONTEXT(context));
|
|
||||||
|
|
||||||
EekboardContextPrivate *priv = eekboard_context_get_instance_private (context);
|
|
||||||
|
|
||||||
if (priv->enabled) {
|
|
||||||
g_dbus_proxy_call (G_DBUS_PROXY(context),
|
|
||||||
"ReleaseKeycode",
|
|
||||||
g_variant_new ("(u)", keycode),
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
|
||||||
-1,
|
|
||||||
cancellable,
|
|
||||||
context_async_ready_callback,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* eekboard_context_is_visible:
|
|
||||||
* @context: an #EekboardContext
|
|
||||||
*
|
|
||||||
* Check if keyboard is visible.
|
|
||||||
*/
|
|
||||||
gboolean
|
|
||||||
eekboard_context_is_visible (EekboardContext *context)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (EEKBOARD_IS_CONTEXT(context), FALSE);
|
|
||||||
|
|
||||||
EekboardContextPrivate *priv = eekboard_context_get_instance_private (context);
|
|
||||||
|
|
||||||
return priv->enabled && priv->visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* eekboard_context_set_enabled:
|
|
||||||
* @context: an #EekboardContext
|
|
||||||
* @enabled: flag to indicate if @context is enabled
|
|
||||||
*
|
|
||||||
* Set @context enabled or disabled. This function is seldom called
|
|
||||||
* since the flag is set via D-Bus signal #EekboardContext::enabled
|
|
||||||
* and #EekboardContext::disabled.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
eekboard_context_set_enabled (EekboardContext *context,
|
|
||||||
gboolean enabled)
|
|
||||||
{
|
|
||||||
g_return_if_fail (EEKBOARD_IS_CONTEXT(context));
|
|
||||||
|
|
||||||
EekboardContextPrivate *priv = eekboard_context_get_instance_private (context);
|
|
||||||
|
|
||||||
priv->enabled = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* eekboard_context_is_enabled:
|
|
||||||
* @context: an #EekboardContext
|
|
||||||
*
|
|
||||||
* Check if @context is enabled.
|
|
||||||
*/
|
|
||||||
gboolean
|
|
||||||
eekboard_context_is_enabled (EekboardContext *context)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail (EEKBOARD_IS_CONTEXT(context), FALSE);
|
|
||||||
|
|
||||||
EekboardContextPrivate *priv = eekboard_context_get_instance_private (context);
|
|
||||||
|
|
||||||
return priv->enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* eekboard_context_set_fullscreen:
|
|
||||||
* @context: an #EekboardContext
|
|
||||||
* @fullscreen: a flag to indicate fullscreen mode
|
|
||||||
* @cancellable: a #GCancellable
|
|
||||||
*
|
|
||||||
* Set the fullscreen mode of @context.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
eekboard_context_set_fullscreen (EekboardContext *context,
|
|
||||||
gboolean fullscreen,
|
|
||||||
GCancellable *cancellable)
|
|
||||||
{
|
|
||||||
g_return_if_fail (EEKBOARD_IS_CONTEXT(context));
|
|
||||||
|
|
||||||
EekboardContextPrivate *priv = eekboard_context_get_instance_private (context);
|
|
||||||
|
|
||||||
if (priv->fullscreen != fullscreen) {
|
|
||||||
g_dbus_proxy_call (G_DBUS_PROXY(context),
|
|
||||||
"SetFullscreen",
|
|
||||||
g_variant_new ("(b)", fullscreen),
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
|
||||||
-1,
|
|
||||||
cancellable,
|
|
||||||
context_async_ready_callback,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,94 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
|
|
||||||
* Copyright (C) 2010-2011 Red Hat, Inc.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#if !defined(__EEKBOARD_CLIENT_H_INSIDE__) && !defined(EEKBOARD_COMPILATION)
|
|
||||||
#error "Only <eekboard/eekboard-client.h> can be included directly."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef EEKBOARD_CONTEXT_H
|
|
||||||
#define EEKBOARD_CONTEXT_H 1
|
|
||||||
|
|
||||||
#include <gio/gio.h>
|
|
||||||
#include "eek/eek.h"
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
#define EEKBOARD_TYPE_CONTEXT (eekboard_context_get_type())
|
|
||||||
G_DECLARE_DERIVABLE_TYPE (EekboardContext, eekboard_context, EEKBOARD, CONTEXT, GDBusProxy)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EekboardContextClass:
|
|
||||||
* @enabled: class handler for #EekboardContext::enabled signal
|
|
||||||
* @disabled: class handler for #EekboardContext::disabled signal
|
|
||||||
* @key_pressed: class handler for #EekboardContext::key-pressed signal
|
|
||||||
* @destroyed: class handler for #EekboardContext::destroyed signal
|
|
||||||
*/
|
|
||||||
struct _EekboardContextClass {
|
|
||||||
/*< private >*/
|
|
||||||
GDBusProxyClass parent_class;
|
|
||||||
|
|
||||||
/*< public >*/
|
|
||||||
/* signals */
|
|
||||||
void (*enabled) (EekboardContext *self);
|
|
||||||
void (*disabled) (EekboardContext *self);
|
|
||||||
void (*destroyed) (EekboardContext *self);
|
|
||||||
|
|
||||||
/*< private >*/
|
|
||||||
/* padding */
|
|
||||||
gpointer pdummy[24];
|
|
||||||
};
|
|
||||||
|
|
||||||
GType eekboard_context_get_type (void) G_GNUC_CONST;
|
|
||||||
|
|
||||||
EekboardContext *eekboard_context_new (GDBusConnection *connection,
|
|
||||||
const gchar *object_path,
|
|
||||||
GCancellable *cancellable);
|
|
||||||
guint eekboard_context_add_keyboard (EekboardContext *context,
|
|
||||||
const gchar *keyboard,
|
|
||||||
GCancellable *cancellable);
|
|
||||||
void eekboard_context_remove_keyboard (EekboardContext *context,
|
|
||||||
guint keyboard_id,
|
|
||||||
GCancellable *cancellable);
|
|
||||||
void eekboard_context_set_keyboard (EekboardContext *context,
|
|
||||||
guint keyboard_id,
|
|
||||||
GCancellable *cancellable);
|
|
||||||
void eekboard_context_show_keyboard (EekboardContext *context,
|
|
||||||
GCancellable *cancellable);
|
|
||||||
void eekboard_context_hide_keyboard (EekboardContext *context,
|
|
||||||
GCancellable *cancellable);
|
|
||||||
void eekboard_context_set_group (EekboardContext *context,
|
|
||||||
gint group,
|
|
||||||
GCancellable *cancellable);
|
|
||||||
gint eekboard_context_get_group (EekboardContext *context,
|
|
||||||
GCancellable *cancellable);
|
|
||||||
void eekboard_context_press_keycode (EekboardContext *context,
|
|
||||||
guint keycode,
|
|
||||||
GCancellable *cancellable);
|
|
||||||
void eekboard_context_release_keycode (EekboardContext *context,
|
|
||||||
guint keycode,
|
|
||||||
GCancellable *cancellable);
|
|
||||||
gboolean eekboard_context_is_visible
|
|
||||||
(EekboardContext *context);
|
|
||||||
void eekboard_context_set_enabled (EekboardContext *context,
|
|
||||||
gboolean enabled);
|
|
||||||
gboolean eekboard_context_is_enabled (EekboardContext *context);
|
|
||||||
void eekboard_context_set_fullscreen (EekboardContext *context,
|
|
||||||
gboolean fullscreen,
|
|
||||||
GCancellable *cancellable);
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
||||||
#endif /* EEKBOARD_CONTEXT_H */
|
|
||||||
@ -3,13 +3,13 @@ extern crate xkbcommon;
|
|||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
use rs::data::{ load_layout_from_resource, LoadError };
|
use rs::data::{ Layout, LoadError };
|
||||||
|
|
||||||
use xkbcommon::xkb;
|
use xkbcommon::xkb;
|
||||||
|
|
||||||
|
|
||||||
fn check_layout(name: &str) {
|
fn check_layout(name: &str) {
|
||||||
let layout = load_layout_from_resource(name)
|
let layout = Layout::from_resource(name)
|
||||||
.and_then(|layout| layout.build().map_err(LoadError::BadKeyMap))
|
.and_then(|layout| layout.build().map_err(LoadError::BadKeyMap))
|
||||||
.expect("layout broken");
|
.expect("layout broken");
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
project(
|
project(
|
||||||
'squeekboard',
|
'squeekboard',
|
||||||
'c', 'rust',
|
'c', 'rust',
|
||||||
version: '1.2.0',
|
version: '1.2.1',
|
||||||
license: 'GPLv3',
|
license: 'GPLv3',
|
||||||
meson_version: '>=0.51.0',
|
meson_version: '>=0.51.0',
|
||||||
default_options: [
|
default_options: [
|
||||||
|
|||||||
194
src/data.rs
194
src/data.rs
@ -67,15 +67,7 @@ impl fmt::Display for LoadError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_layout_from_resource(
|
#[derive(Debug, PartialEq)]
|
||||||
name: &str
|
|
||||||
) -> Result<Layout, LoadError> {
|
|
||||||
let data = resources::get_keyboard(name)
|
|
||||||
.ok_or(LoadError::MissingResource)?;
|
|
||||||
serde_yaml::from_str(data)
|
|
||||||
.map_err(|e| LoadError::BadResource(e))
|
|
||||||
}
|
|
||||||
|
|
||||||
enum DataSource {
|
enum DataSource {
|
||||||
File(PathBuf),
|
File(PathBuf),
|
||||||
Resource(String),
|
Resource(String),
|
||||||
@ -93,63 +85,90 @@ impl fmt::Display for DataSource {
|
|||||||
/// Tries to load the layout from the first place where it's present.
|
/// Tries to load the layout from the first place where it's present.
|
||||||
/// If the layout exists, but is broken, fallback is activated.
|
/// If the layout exists, but is broken, fallback is activated.
|
||||||
fn load_layout(
|
fn load_layout(
|
||||||
name: &str
|
name: &str,
|
||||||
) -> (Result<::layout::Layout, LoadError>, DataSource) {
|
keyboards_path: Option<PathBuf>,
|
||||||
let path = env::var_os("SQUEEKBOARD_KEYBOARDSDIR")
|
) -> (
|
||||||
.map(PathBuf::from)
|
Result<::layout::Layout, LoadError>, // last attempted
|
||||||
.or_else(|| xdg::data_path("squeekboard/keyboards"))
|
DataSource, // last attempt source
|
||||||
.map(|path| path.join(name).with_extension("yaml"));
|
Option<(LoadError, DataSource)>, // first attempt source
|
||||||
|
) {
|
||||||
let (layout, source) = match path {
|
let path = keyboards_path.map(|path|
|
||||||
Some(path) => {(
|
path.join(name).with_extension("yaml")
|
||||||
Layout::from_yaml_stream(path.clone())
|
|
||||||
.map_err(|e| LoadError::BadData(e)),
|
|
||||||
DataSource::File(path)
|
|
||||||
)},
|
|
||||||
None => {(
|
|
||||||
load_layout_from_resource(name),
|
|
||||||
DataSource::Resource(name.into())
|
|
||||||
)},
|
|
||||||
};
|
|
||||||
|
|
||||||
let layout = layout.and_then(
|
|
||||||
|layout| layout.build().map_err(LoadError::BadKeyMap)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
(layout, source)
|
let layout = match path {
|
||||||
|
Some(path) => Some((
|
||||||
|
Layout::from_file(path.clone())
|
||||||
|
.map_err(LoadError::BadData)
|
||||||
|
.and_then(|layout|
|
||||||
|
layout.build().map_err(LoadError::BadKeyMap)
|
||||||
|
),
|
||||||
|
DataSource::File(path),
|
||||||
|
)),
|
||||||
|
None => None, // No env var, not an error
|
||||||
|
};
|
||||||
|
|
||||||
|
let (failed_attempt, layout) = match layout {
|
||||||
|
Some((Ok(layout), path)) => (None, Some((layout, path))),
|
||||||
|
Some((Err(e), path)) => (Some((e, path)), None),
|
||||||
|
None => (None, None),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (layout, source) = match layout {
|
||||||
|
Some((layout, path)) => (Ok(layout), path),
|
||||||
|
None => (
|
||||||
|
Layout::from_resource(name)
|
||||||
|
.and_then(|layout|
|
||||||
|
layout.build().map_err(LoadError::BadKeyMap)
|
||||||
|
),
|
||||||
|
DataSource::Resource(name.into()),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
(layout, source, failed_attempt)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log_attempt_info(attempt: Option<(LoadError, DataSource)>) {
|
||||||
|
match attempt {
|
||||||
|
Some((
|
||||||
|
LoadError::BadData(Error::Missing(_e)),
|
||||||
|
DataSource::File(_file)
|
||||||
|
)) => {
|
||||||
|
// Missing file, not to worry. TODO: print in debug logging level
|
||||||
|
}
|
||||||
|
Some((e, source)) => {
|
||||||
|
eprintln!(
|
||||||
|
"Failed to load layout from {}: {}, trying builtin",
|
||||||
|
source, e
|
||||||
|
);
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_layout_with_fallback(
|
fn load_layout_with_fallback(
|
||||||
name: &str
|
name: &str
|
||||||
) -> ::layout::Layout {
|
) -> ::layout::Layout {
|
||||||
let (layout, source) = load_layout(name);
|
let path = env::var_os("SQUEEKBOARD_KEYBOARDSDIR")
|
||||||
let (layout, source) = match (layout, source) {
|
.map(PathBuf::from)
|
||||||
|
.or_else(|| xdg::data_path("squeekboard/keyboards"));
|
||||||
|
|
||||||
|
let (layout, source, attempt) = load_layout(name, path.clone());
|
||||||
|
|
||||||
|
log_attempt_info(attempt);
|
||||||
|
|
||||||
|
let (layout, source, attempt) = match (layout, source) {
|
||||||
(Err(e), source) => {
|
(Err(e), source) => {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"Failed to load layout from {}: {}, using fallback",
|
"Failed to load layout from {}: {}, using fallback",
|
||||||
source, e
|
source, e
|
||||||
);
|
);
|
||||||
load_layout(FALLBACK_LAYOUT_NAME)
|
load_layout(FALLBACK_LAYOUT_NAME, path)
|
||||||
},
|
},
|
||||||
res => res,
|
(res, source) => (res, source, None),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (layout, source) = match (layout, source) {
|
log_attempt_info(attempt);
|
||||||
(Err(e), source) => {
|
|
||||||
eprintln!(
|
|
||||||
"Failed to load fallback layout from {}: {}, using hardcoded",
|
|
||||||
source, e
|
|
||||||
);
|
|
||||||
(
|
|
||||||
load_layout_from_resource(FALLBACK_LAYOUT_NAME)
|
|
||||||
.and_then(
|
|
||||||
|layout| layout.build().map_err(LoadError::BadKeyMap)
|
|
||||||
),
|
|
||||||
DataSource::Resource(FALLBACK_LAYOUT_NAME.into()),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
res => res,
|
|
||||||
};
|
|
||||||
|
|
||||||
match (layout, source) {
|
match (layout, source) {
|
||||||
(Err(e), source) => {
|
(Err(e), source) => {
|
||||||
@ -170,6 +189,8 @@ fn load_layout_with_fallback(
|
|||||||
#[derive(Debug, Deserialize, PartialEq)]
|
#[derive(Debug, Deserialize, PartialEq)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Layout {
|
pub struct Layout {
|
||||||
|
row_spacing: f64,
|
||||||
|
button_spacing: f64,
|
||||||
bounds: Bounds,
|
bounds: Bounds,
|
||||||
views: HashMap<String, Vec<ButtonIds>>,
|
views: HashMap<String, Vec<ButtonIds>>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@ -218,14 +239,18 @@ enum Action {
|
|||||||
#[derive(Debug, Clone, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Deserialize, PartialEq)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
struct Outline {
|
struct Outline {
|
||||||
corner_radius: f64,
|
|
||||||
bounds: Bounds,
|
bounds: Bounds,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Errors encountered loading the layout into yaml
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
Yaml(serde_yaml::Error),
|
Yaml(serde_yaml::Error),
|
||||||
Io(io::Error),
|
Io(io::Error),
|
||||||
|
/// The file was missing.
|
||||||
|
/// It's distinct from Io in order to make it matchable
|
||||||
|
/// without calling io::Error::kind()
|
||||||
|
Missing(io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
@ -233,21 +258,38 @@ impl fmt::Display for Error {
|
|||||||
match self {
|
match self {
|
||||||
Error::Yaml(e) => write!(f, "YAML: {}", e),
|
Error::Yaml(e) => write!(f, "YAML: {}", e),
|
||||||
Error::Io(e) => write!(f, "IO: {}", e),
|
Error::Io(e) => write!(f, "IO: {}", e),
|
||||||
|
Error::Missing(e) => write!(f, "Missing: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for Error {
|
||||||
|
fn from(e: io::Error) -> Self {
|
||||||
|
let kind = e.kind();
|
||||||
|
match kind {
|
||||||
|
io::ErrorKind::NotFound => Error::Missing(e),
|
||||||
|
_ => Error::Io(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Layout {
|
impl Layout {
|
||||||
fn from_yaml_stream(path: PathBuf) -> Result<Layout, Error> {
|
pub fn from_resource(name: &str) -> Result<Layout, LoadError> {
|
||||||
|
let data = resources::get_keyboard(name)
|
||||||
|
.ok_or(LoadError::MissingResource)?;
|
||||||
|
serde_yaml::from_str(data)
|
||||||
|
.map_err(LoadError::BadResource)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_file(path: PathBuf) -> Result<Layout, Error> {
|
||||||
let infile = BufReader::new(
|
let infile = BufReader::new(
|
||||||
fs::OpenOptions::new()
|
fs::OpenOptions::new()
|
||||||
.read(true)
|
.read(true)
|
||||||
.open(&path)
|
.open(&path)?
|
||||||
.map_err(Error::Io)?
|
|
||||||
);
|
);
|
||||||
serde_yaml::from_reader(infile)
|
serde_yaml::from_reader(infile).map_err(Error::Yaml)
|
||||||
.map_err(Error::Yaml)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Result<::layout::Layout, FormattingError> {
|
pub fn build(self) -> Result<::layout::Layout, FormattingError> {
|
||||||
let button_names = self.views.values()
|
let button_names = self.views.values()
|
||||||
.flat_map(|rows| {
|
.flat_map(|rows| {
|
||||||
@ -303,6 +345,10 @@ impl Layout {
|
|||||||
width: self.bounds.width,
|
width: self.bounds.width,
|
||||||
height: self.bounds.height,
|
height: self.bounds.height,
|
||||||
},
|
},
|
||||||
|
spacing: ::layout::Spacing {
|
||||||
|
row: self.row_spacing,
|
||||||
|
button: self.button_spacing,
|
||||||
|
},
|
||||||
rows: view.iter().map(|row| {
|
rows: view.iter().map(|row| {
|
||||||
Box::new(::layout::Row {
|
Box::new(::layout::Row {
|
||||||
angle: 0,
|
angle: 0,
|
||||||
@ -469,13 +515,13 @@ fn create_button(
|
|||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
eprintln!("No default outline defied Using 1x1!");
|
eprintln!("No default outline defied Using 1x1!");
|
||||||
Outline {
|
Outline {
|
||||||
corner_radius: 0f64,
|
|
||||||
bounds: Bounds { x: 0f64, y: 0f64, width: 1f64, height: 1f64 },
|
bounds: Bounds { x: 0f64, y: 0f64, width: 1f64, height: 1f64 },
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
::layout::Button {
|
::layout::Button {
|
||||||
name: cname,
|
name: cname,
|
||||||
|
outline_name: CString::new(outline_name).expect("Bad outline"),
|
||||||
// TODO: do layout before creating buttons
|
// TODO: do layout before creating buttons
|
||||||
bounds: ::layout::c::Bounds {
|
bounds: ::layout::c::Bounds {
|
||||||
x: outline.bounds.x,
|
x: outline.bounds.x,
|
||||||
@ -483,7 +529,6 @@ fn create_button(
|
|||||||
width: outline.bounds.width,
|
width: outline.bounds.width,
|
||||||
height: outline.bounds.height,
|
height: outline.bounds.height,
|
||||||
},
|
},
|
||||||
corner_radius: outline.corner_radius,
|
|
||||||
label: label,
|
label: label,
|
||||||
state: state,
|
state: state,
|
||||||
}
|
}
|
||||||
@ -498,10 +543,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_parse_path() {
|
fn test_parse_path() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Layout::from_yaml_stream(
|
Layout::from_file(PathBuf::from("tests/layout.yaml")).unwrap(),
|
||||||
PathBuf::from("tests/layout.yaml")
|
|
||||||
).unwrap(),
|
|
||||||
Layout {
|
Layout {
|
||||||
|
row_spacing: 0f64,
|
||||||
|
button_spacing: 0f64,
|
||||||
bounds: Bounds { x: 0f64, y: 0f64, width: 0f64, height: 0f64 },
|
bounds: Bounds { x: 0f64, y: 0f64, width: 0f64, height: 0f64 },
|
||||||
views: hashmap!(
|
views: hashmap!(
|
||||||
"base".into() => vec!("test".into()),
|
"base".into() => vec!("test".into()),
|
||||||
@ -517,7 +562,6 @@ mod tests {
|
|||||||
},
|
},
|
||||||
outlines: hashmap!{
|
outlines: hashmap!{
|
||||||
"default".into() => Outline {
|
"default".into() => Outline {
|
||||||
corner_radius: 1f64,
|
|
||||||
bounds: Bounds {
|
bounds: Bounds {
|
||||||
x: 0f64, y: 0f64, width: 0f64, height: 0f64
|
x: 0f64, y: 0f64, width: 0f64, height: 0f64
|
||||||
},
|
},
|
||||||
@ -530,7 +574,7 @@ mod tests {
|
|||||||
/// Check if the default protection works
|
/// Check if the default protection works
|
||||||
#[test]
|
#[test]
|
||||||
fn test_empty_views() {
|
fn test_empty_views() {
|
||||||
let out = Layout::from_yaml_stream(PathBuf::from("tests/layout2.yaml"));
|
let out = Layout::from_file(PathBuf::from("tests/layout2.yaml"));
|
||||||
match out {
|
match out {
|
||||||
Ok(_) => assert!(false, "Data mistakenly accepted"),
|
Ok(_) => assert!(false, "Data mistakenly accepted"),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -548,7 +592,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_extra_field() {
|
fn test_extra_field() {
|
||||||
let out = Layout::from_yaml_stream(PathBuf::from("tests/layout3.yaml"));
|
let out = Layout::from_file(PathBuf::from("tests/layout3.yaml"));
|
||||||
match out {
|
match out {
|
||||||
Ok(_) => assert!(false, "Data mistakenly accepted"),
|
Ok(_) => assert!(false, "Data mistakenly accepted"),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -567,7 +611,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_layout_punctuation() {
|
fn test_layout_punctuation() {
|
||||||
let out = Layout::from_yaml_stream(PathBuf::from("tests/layout_key1.yaml"))
|
let out = Layout::from_file(PathBuf::from("tests/layout_key1.yaml"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -582,7 +626,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_layout_unicode() {
|
fn test_layout_unicode() {
|
||||||
let out = Layout::from_yaml_stream(PathBuf::from("tests/layout_key2.yaml"))
|
let out = Layout::from_file(PathBuf::from("tests/layout_key2.yaml"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -597,12 +641,26 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parsing_fallback() {
|
fn parsing_fallback() {
|
||||||
assert!(load_layout_from_resource(FALLBACK_LAYOUT_NAME)
|
assert!(Layout::from_resource(FALLBACK_LAYOUT_NAME)
|
||||||
.and_then(|layout| layout.build().map_err(LoadError::BadKeyMap))
|
.and_then(|layout| layout.build().map_err(LoadError::BadKeyMap))
|
||||||
.is_ok()
|
.is_ok()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// First fallback should be to builtin, not to FALLBACK_LAYOUT_NAME
|
||||||
|
#[test]
|
||||||
|
fn fallbacks_order() {
|
||||||
|
let (layout, source, _failure) = load_layout(
|
||||||
|
"nb",
|
||||||
|
Some(PathBuf::from("tests"))
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
source,
|
||||||
|
load_layout("nb", None).1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unicode_keysym() {
|
fn unicode_keysym() {
|
||||||
let keysym = xkb::keysym_from_name(
|
let keysym = xkb::keysym_from_name(
|
||||||
|
|||||||
@ -3,7 +3,6 @@ use std::ffi::CString;
|
|||||||
use std::num::Wrapping;
|
use std::num::Wrapping;
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
|
|
||||||
use super::bitflags;
|
|
||||||
use ::util::c::into_cstring;
|
use ::util::c::into_cstring;
|
||||||
|
|
||||||
// Traits
|
// Traits
|
||||||
|
|||||||
@ -32,6 +32,7 @@ EekBounds squeek_button_get_bounds(const struct squeek_button*);
|
|||||||
const char *squeek_button_get_label(const struct squeek_button*);
|
const char *squeek_button_get_label(const struct squeek_button*);
|
||||||
const char *squeek_button_get_icon_name(const struct squeek_button*);
|
const char *squeek_button_get_icon_name(const struct squeek_button*);
|
||||||
const char *squeek_button_get_name(const struct squeek_button*);
|
const char *squeek_button_get_name(const struct squeek_button*);
|
||||||
|
const char *squeek_button_get_outline_name(const struct squeek_button*);
|
||||||
|
|
||||||
struct squeek_key *squeek_button_get_key(const struct squeek_button*);
|
struct squeek_key *squeek_button_get_key(const struct squeek_button*);
|
||||||
uint32_t *squeek_button_has_key(const struct squeek_button* button,
|
uint32_t *squeek_button_has_key(const struct squeek_button* button,
|
||||||
|
|||||||
@ -40,11 +40,6 @@ pub mod c {
|
|||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct UserData(*const c_void);
|
pub struct UserData(*const c_void);
|
||||||
|
|
||||||
/// The index in the relevant outline table
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct OutlineRef(u32);
|
|
||||||
|
|
||||||
/// Defined in eek-types.h
|
/// Defined in eek-types.h
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -195,6 +190,13 @@ pub mod c {
|
|||||||
button.name.as_ptr()
|
button.name.as_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C"
|
||||||
|
fn squeek_button_get_outline_name(button: *const Button) -> *const c_char {
|
||||||
|
let button = unsafe { &*button };
|
||||||
|
button.outline_name.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_button_has_key(
|
fn squeek_button_has_key(
|
||||||
@ -328,8 +330,8 @@ pub mod c {
|
|||||||
.map(|button| button.bounds.clone())
|
.map(|button| button.bounds.clone())
|
||||||
.collect()
|
.collect()
|
||||||
}).collect();
|
}).collect();
|
||||||
|
let spacing = view.spacing.clone();
|
||||||
view.place_buttons_with_sizes(sizes);
|
view.place_buttons_with_sizes(sizes, spacing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -440,6 +442,10 @@ pub mod c {
|
|||||||
x: 0f64, y: 0f64,
|
x: 0f64, y: 0f64,
|
||||||
width: 0f64, height: 0f64
|
width: 0f64, height: 0f64
|
||||||
},
|
},
|
||||||
|
spacing: Spacing {
|
||||||
|
button: 0f64,
|
||||||
|
row: 0f64,
|
||||||
|
},
|
||||||
rows: vec!(row),
|
rows: vec!(row),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -459,6 +465,10 @@ pub mod c {
|
|||||||
x: 0f64, y: 0f64,
|
x: 0f64, y: 0f64,
|
||||||
width: 0f64, height: 0f64
|
width: 0f64, height: 0f64
|
||||||
},
|
},
|
||||||
|
spacing: Spacing {
|
||||||
|
button: 0f64,
|
||||||
|
row: 0f64,
|
||||||
|
},
|
||||||
rows: Vec::new(),
|
rows: Vec::new(),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -498,10 +508,10 @@ pub mod c {
|
|||||||
) -> Box<Button> {
|
) -> Box<Button> {
|
||||||
Box::new(Button {
|
Box::new(Button {
|
||||||
name: CString::new(name.clone()).unwrap(),
|
name: CString::new(name.clone()).unwrap(),
|
||||||
corner_radius: 0f64,
|
|
||||||
bounds: c::Bounds {
|
bounds: c::Bounds {
|
||||||
x: 0f64, y: 0f64, width: 0f64, height: 0f64
|
x: 0f64, y: 0f64, width: 0f64, height: 0f64
|
||||||
},
|
},
|
||||||
|
outline_name: CString::new("test").unwrap(),
|
||||||
label: Label::Text(CString::new(name).unwrap()),
|
label: Label::Text(CString::new(name).unwrap()),
|
||||||
state: state,
|
state: state,
|
||||||
})
|
})
|
||||||
@ -561,18 +571,15 @@ pub struct Button {
|
|||||||
pub name: CString,
|
pub name: CString,
|
||||||
/// Label to display to the user
|
/// Label to display to the user
|
||||||
pub label: Label,
|
pub label: Label,
|
||||||
pub corner_radius: f64,
|
|
||||||
/// TODO: position the buttons before they get initial bounds
|
/// TODO: position the buttons before they get initial bounds
|
||||||
/// Position relative to some origin (i.e. parent/row)
|
/// Position relative to some origin (i.e. parent/row)
|
||||||
pub bounds: c::Bounds,
|
pub bounds: c::Bounds,
|
||||||
|
/// The name of the visual class applied
|
||||||
|
pub outline_name: CString,
|
||||||
/// current state, shared with other buttons
|
/// current state, shared with other buttons
|
||||||
pub state: Rc<RefCell<KeyState>>,
|
pub state: Rc<RefCell<KeyState>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: derive from the style/margin/padding
|
|
||||||
const BUTTON_SPACING: f64 = 4.0;
|
|
||||||
const ROW_SPACING: f64 = 7.0;
|
|
||||||
|
|
||||||
/// The graphical representation of a row of buttons
|
/// The graphical representation of a row of buttons
|
||||||
pub struct Row {
|
pub struct Row {
|
||||||
pub buttons: Vec<Box<Button>>,
|
pub buttons: Vec<Box<Button>>,
|
||||||
@ -599,7 +606,7 @@ impl Row {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_button_positions(outlines: Vec<c::Bounds>)
|
fn calculate_button_positions(outlines: Vec<c::Bounds>, button_spacing: f64)
|
||||||
-> Vec<c::Bounds>
|
-> Vec<c::Bounds>
|
||||||
{
|
{
|
||||||
let mut x_offset = 0f64;
|
let mut x_offset = 0f64;
|
||||||
@ -609,7 +616,7 @@ impl Row {
|
|||||||
x: x_offset,
|
x: x_offset,
|
||||||
..outline.clone()
|
..outline.clone()
|
||||||
};
|
};
|
||||||
x_offset += outline.width + BUTTON_SPACING;
|
x_offset += outline.width + button_spacing;
|
||||||
position
|
position
|
||||||
}).collect()
|
}).collect()
|
||||||
}
|
}
|
||||||
@ -648,9 +655,16 @@ impl Row {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Spacing {
|
||||||
|
pub row: f64,
|
||||||
|
pub button: f64,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct View {
|
pub struct View {
|
||||||
/// Position relative to keyboard origin
|
/// Position relative to keyboard origin
|
||||||
pub bounds: c::Bounds,
|
pub bounds: c::Bounds,
|
||||||
|
pub spacing: Spacing,
|
||||||
pub rows: Vec<Box<Row>>,
|
pub rows: Vec<Box<Row>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -662,7 +676,9 @@ impl View {
|
|||||||
/// and derive a scaling factor that lets contents fit into view)
|
/// and derive a scaling factor that lets contents fit into view)
|
||||||
/// (or TODO: blow up view bounds to match contents
|
/// (or TODO: blow up view bounds to match contents
|
||||||
/// and then scale the entire thing)
|
/// and then scale the entire thing)
|
||||||
fn calculate_row_positions(&self, sizes: Vec<Size>) -> Vec<c::Bounds> {
|
fn calculate_row_positions(&self, sizes: Vec<Size>, row_spacing: f64)
|
||||||
|
-> Vec<c::Bounds>
|
||||||
|
{
|
||||||
let mut y_offset = self.bounds.y;
|
let mut y_offset = self.bounds.y;
|
||||||
sizes.into_iter().map(|size| {
|
sizes.into_iter().map(|size| {
|
||||||
let position = c::Bounds {
|
let position = c::Bounds {
|
||||||
@ -671,7 +687,7 @@ impl View {
|
|||||||
width: size.width,
|
width: size.width,
|
||||||
height: size.height,
|
height: size.height,
|
||||||
};
|
};
|
||||||
y_offset += size.height + ROW_SPACING;
|
y_offset += size.height + row_spacing;
|
||||||
position
|
position
|
||||||
}).collect()
|
}).collect()
|
||||||
}
|
}
|
||||||
@ -680,19 +696,23 @@ impl View {
|
|||||||
/// The view itself will not be affected by the sizes
|
/// The view itself will not be affected by the sizes
|
||||||
fn place_buttons_with_sizes(
|
fn place_buttons_with_sizes(
|
||||||
&mut self,
|
&mut self,
|
||||||
button_outlines: Vec<Vec<c::Bounds>>
|
button_outlines: Vec<Vec<c::Bounds>>,
|
||||||
|
spacing: Spacing,
|
||||||
) {
|
) {
|
||||||
// Determine all positions
|
// Determine all positions
|
||||||
let button_positions: Vec<_>
|
let button_positions: Vec<_>
|
||||||
= button_outlines.into_iter()
|
= button_outlines.into_iter()
|
||||||
.map(Row::calculate_button_positions)
|
.map(|outlines| {
|
||||||
|
Row::calculate_button_positions(outlines, spacing.button)
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let row_sizes = button_positions.iter()
|
let row_sizes = button_positions.iter()
|
||||||
.map(Row::calculate_row_size)
|
.map(Row::calculate_row_size)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let row_positions = self.calculate_row_positions(row_sizes);
|
let row_positions
|
||||||
|
= self.calculate_row_positions(row_sizes, spacing.row);
|
||||||
|
|
||||||
// Apply all positions
|
// Apply all positions
|
||||||
for ((mut row, row_position), button_positions)
|
for ((mut row, row_position), button_positions)
|
||||||
|
|||||||
@ -19,7 +19,6 @@ sources = [
|
|||||||
'../eek/eek-element.c',
|
'../eek/eek-element.c',
|
||||||
'../eek/eek-gtk-keyboard.c',
|
'../eek/eek-gtk-keyboard.c',
|
||||||
'../eek/eek-keyboard.c',
|
'../eek/eek-keyboard.c',
|
||||||
'../eek/eek-keyboard-drawing.c',
|
|
||||||
'../eek/eek-layout.c',
|
'../eek/eek-layout.c',
|
||||||
'../eek/eek-renderer.c',
|
'../eek/eek-renderer.c',
|
||||||
'../eek/eek-types.c',
|
'../eek/eek-types.c',
|
||||||
@ -29,7 +28,6 @@ sources = [
|
|||||||
enums,
|
enums,
|
||||||
'../eekboard/key-emitter.c',
|
'../eekboard/key-emitter.c',
|
||||||
'../eekboard/eekboard-context-service.c',
|
'../eekboard/eekboard-context-service.c',
|
||||||
'../eekboard/eekboard-context.c',
|
|
||||||
'../eekboard/eekboard-service.c',
|
'../eekboard/eekboard-service.c',
|
||||||
# '../eekboard/eekboard-xklutil.c',
|
# '../eekboard/eekboard-xklutil.c',
|
||||||
squeekboard_resources,
|
squeekboard_resources,
|
||||||
@ -60,13 +58,29 @@ rslibs = custom_target(
|
|||||||
output: ['librs.a'],
|
output: ['librs.a'],
|
||||||
install: false,
|
install: false,
|
||||||
console: true,
|
console: true,
|
||||||
command: [cargo_script, '@CURRENT_SOURCE_DIR@', '@OUTPUT@', 'build']
|
command: [cargo_script, '@OUTPUT@', 'build']
|
||||||
|
)
|
||||||
|
|
||||||
|
build_rstests = custom_target(
|
||||||
|
'build_rstests',
|
||||||
|
build_by_default: false,
|
||||||
|
# HACK: this target needs to build before all the tests,
|
||||||
|
# but it doesn't produce anything stable.
|
||||||
|
# Declaring build_by_default with some random but irrelevant output
|
||||||
|
# ensures that it's always built as it should
|
||||||
|
build_always_stale: true,
|
||||||
|
output: ['src'],
|
||||||
|
install: false,
|
||||||
|
console: true,
|
||||||
|
command: [cargo_script, '', 'build', '--tests'],
|
||||||
|
depends: rslibs, # no point building tests if the code itself fails
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'rstest',
|
'rstest',
|
||||||
cargo_script,
|
cargo_script,
|
||||||
args: [meson.source_root(), '', 'test']
|
args: ['', 'test'],
|
||||||
|
depends: build_rstests,
|
||||||
)
|
)
|
||||||
|
|
||||||
libsqueekboard = static_library('libsqueekboard',
|
libsqueekboard = static_library('libsqueekboard',
|
||||||
|
|||||||
@ -5,6 +5,9 @@
|
|||||||
|
|
||||||
const KEYBOARDS: &[(*const str, *const str)] = &[
|
const KEYBOARDS: &[(*const str, *const str)] = &[
|
||||||
("us", include_str!("../data/keyboards/us.yaml")),
|
("us", include_str!("../data/keyboards/us.yaml")),
|
||||||
|
("el", include_str!("../data/keyboards/el.yaml")),
|
||||||
|
("es", include_str!("../data/keyboards/es.yaml")),
|
||||||
|
("it", include_str!("../data/keyboards/it.yaml")),
|
||||||
("nb", include_str!("../data/keyboards/nb.yaml")),
|
("nb", include_str!("../data/keyboards/nb.yaml")),
|
||||||
("number", include_str!("../data/keyboards/number.yaml")),
|
("number", include_str!("../data/keyboards/number.yaml")),
|
||||||
];
|
];
|
||||||
|
|||||||
@ -40,6 +40,7 @@ struct _ServerContextService {
|
|||||||
|
|
||||||
GtkWidget *window;
|
GtkWidget *window;
|
||||||
GtkWidget *widget;
|
GtkWidget *widget;
|
||||||
|
guint hiding;
|
||||||
|
|
||||||
gdouble size_constraint_landscape[2];
|
gdouble size_constraint_landscape[2];
|
||||||
gdouble size_constraint_portrait[2];
|
gdouble size_constraint_portrait[2];
|
||||||
@ -51,17 +52,6 @@ struct _ServerContextServiceClass {
|
|||||||
|
|
||||||
G_DEFINE_TYPE (ServerContextService, server_context_service, EEKBOARD_TYPE_CONTEXT_SERVICE);
|
G_DEFINE_TYPE (ServerContextService, server_context_service, EEKBOARD_TYPE_CONTEXT_SERVICE);
|
||||||
|
|
||||||
static void set_geometry (ServerContextService *context);
|
|
||||||
|
|
||||||
static void
|
|
||||||
on_monitors_changed (GdkScreen *screen,
|
|
||||||
ServerContextService *context)
|
|
||||||
|
|
||||||
{
|
|
||||||
if (context->window)
|
|
||||||
set_geometry (context);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_destroy (GtkWidget *widget, gpointer user_data)
|
on_destroy (GtkWidget *widget, gpointer user_data)
|
||||||
{
|
{
|
||||||
@ -107,15 +97,6 @@ on_notify_keyboard (GObject *object,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
on_notify_fullscreen (GObject *object,
|
|
||||||
GParamSpec *spec,
|
|
||||||
ServerContextService *context)
|
|
||||||
{
|
|
||||||
if (context->window)
|
|
||||||
set_geometry (context);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_notify_map (GObject *object,
|
on_notify_map (GObject *object,
|
||||||
ServerContextService *context)
|
ServerContextService *context)
|
||||||
@ -131,49 +112,6 @@ on_notify_unmap (GObject *object,
|
|||||||
g_object_set (context, "visible", FALSE, NULL);
|
g_object_set (context, "visible", FALSE, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
set_geometry (ServerContextService *context)
|
|
||||||
{
|
|
||||||
GdkScreen *screen = gdk_screen_get_default ();
|
|
||||||
GdkWindow *root = gdk_screen_get_root_window (screen);
|
|
||||||
GdkDisplay *display = gdk_display_get_default ();
|
|
||||||
GdkMonitor *monitor = gdk_display_get_monitor_at_window (display, root);
|
|
||||||
LevelKeyboard *keyboard = eekboard_context_service_get_keyboard (EEKBOARD_CONTEXT_SERVICE(context));
|
|
||||||
|
|
||||||
GdkRectangle rect;
|
|
||||||
|
|
||||||
gdk_monitor_get_geometry (monitor, &rect);
|
|
||||||
EekBounds bounds = squeek_view_get_bounds (level_keyboard_current(keyboard));
|
|
||||||
|
|
||||||
if (eekboard_context_service_get_fullscreen (EEKBOARD_CONTEXT_SERVICE(context))) {
|
|
||||||
gint width = rect.width;
|
|
||||||
gint height = rect.height;
|
|
||||||
|
|
||||||
if (width > height) {
|
|
||||||
width *= context->size_constraint_landscape[0];
|
|
||||||
height *= context->size_constraint_landscape[1];
|
|
||||||
} else {
|
|
||||||
width *= context->size_constraint_portrait[0];
|
|
||||||
height *= context->size_constraint_portrait[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (width * bounds.height > height * bounds.width)
|
|
||||||
width = (height / bounds.height) * bounds.width;
|
|
||||||
else
|
|
||||||
height = (width / bounds.width) * bounds.height;
|
|
||||||
|
|
||||||
gtk_window_resize (GTK_WINDOW(context->widget), width, height);
|
|
||||||
|
|
||||||
gtk_window_move (GTK_WINDOW(context->window),
|
|
||||||
(rect.width - width) / 2,
|
|
||||||
rect.height - height);
|
|
||||||
|
|
||||||
gtk_window_set_decorated (GTK_WINDOW(context->window), FALSE);
|
|
||||||
gtk_window_set_resizable (GTK_WINDOW(context->window), FALSE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define KEYBOARD_HEIGHT 210
|
#define KEYBOARD_HEIGHT 210
|
||||||
static void
|
static void
|
||||||
make_window (ServerContextService *context)
|
make_window (ServerContextService *context)
|
||||||
@ -237,7 +175,6 @@ make_widget (ServerContextService *context)
|
|||||||
gtk_widget_set_has_tooltip (context->widget, TRUE);
|
gtk_widget_set_has_tooltip (context->widget, TRUE);
|
||||||
gtk_container_add (GTK_CONTAINER(context->window), context->widget);
|
gtk_container_add (GTK_CONTAINER(context->window), context->widget);
|
||||||
gtk_widget_show (context->widget);
|
gtk_widget_show (context->widget);
|
||||||
set_geometry (context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -245,6 +182,11 @@ server_context_service_real_show_keyboard (EekboardContextService *_context)
|
|||||||
{
|
{
|
||||||
ServerContextService *context = SERVER_CONTEXT_SERVICE(_context);
|
ServerContextService *context = SERVER_CONTEXT_SERVICE(_context);
|
||||||
|
|
||||||
|
if (context->hiding) {
|
||||||
|
g_source_remove (context->hiding);
|
||||||
|
context->hiding = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (!context->window)
|
if (!context->window)
|
||||||
make_window (context);
|
make_window (context);
|
||||||
if (!context->widget)
|
if (!context->widget)
|
||||||
@ -255,12 +197,22 @@ server_context_service_real_show_keyboard (EekboardContextService *_context)
|
|||||||
gtk_widget_show (context->window);
|
gtk_widget_show (context->window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
on_hide (ServerContextService *context)
|
||||||
|
{
|
||||||
|
gtk_widget_hide (context->window);
|
||||||
|
context->hiding = 0;
|
||||||
|
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
server_context_service_real_hide_keyboard (EekboardContextService *_context)
|
server_context_service_real_hide_keyboard (EekboardContextService *_context)
|
||||||
{
|
{
|
||||||
ServerContextService *context = SERVER_CONTEXT_SERVICE(_context);
|
ServerContextService *context = SERVER_CONTEXT_SERVICE(_context);
|
||||||
|
|
||||||
gtk_widget_hide (context->window);
|
if (!context->hiding)
|
||||||
|
context->hiding = g_timeout_add (200, (GSourceFunc) on_hide, context);
|
||||||
|
|
||||||
EEKBOARD_CONTEXT_SERVICE_CLASS (server_context_service_parent_class)->
|
EEKBOARD_CONTEXT_SERVICE_CLASS (server_context_service_parent_class)->
|
||||||
hide_keyboard (_context);
|
hide_keyboard (_context);
|
||||||
@ -349,21 +301,10 @@ server_context_service_class_init (ServerContextServiceClass *klass)
|
|||||||
static void
|
static void
|
||||||
server_context_service_init (ServerContextService *context)
|
server_context_service_init (ServerContextService *context)
|
||||||
{
|
{
|
||||||
GdkScreen *screen = gdk_screen_get_default ();
|
|
||||||
|
|
||||||
g_signal_connect (screen,
|
|
||||||
"monitors-changed",
|
|
||||||
G_CALLBACK(on_monitors_changed),
|
|
||||||
context);
|
|
||||||
g_signal_connect (context,
|
g_signal_connect (context,
|
||||||
"notify::keyboard",
|
"notify::keyboard",
|
||||||
G_CALLBACK(on_notify_keyboard),
|
G_CALLBACK(on_notify_keyboard),
|
||||||
context);
|
context);
|
||||||
|
|
||||||
g_signal_connect (context,
|
|
||||||
"notify::fullscreen",
|
|
||||||
G_CALLBACK(on_notify_fullscreen),
|
|
||||||
context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EekboardContextService *
|
EekboardContextService *
|
||||||
|
|||||||
@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
|
|
||||||
* Copyright (C) 2010-2011 Red Hat, Inc.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program 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 General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#ifndef SERVER_CONTEXT_H
|
|
||||||
#define SERVER_CONTEXT_H 1
|
|
||||||
|
|
||||||
#include <gio/gio.h>
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
#define SERVER_CONTEXT_PATH "/org/fedorahosted/Eekboard/Context_%d"
|
|
||||||
#define SERVER_CONTEXT_INTERFACE "org.fedorahosted.Eekboard.Context"
|
|
||||||
|
|
||||||
#define SERVER_TYPE_CONTEXT (server_context_get_type())
|
|
||||||
#define SERVER_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SERVER_TYPE_CONTEXT, ServerContext))
|
|
||||||
#define SERVER_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SERVER_TYPE_CONTEXT, ServerContextClass))
|
|
||||||
#define SERVER_IS_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SERVER_TYPE_CONTEXT))
|
|
||||||
#define SERVER_IS_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SERVER_TYPE_CONTEXT))
|
|
||||||
#define SERVER_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SERVER_TYPE_CONTEXT, ServerContextClass))
|
|
||||||
|
|
||||||
typedef struct _ServerContext ServerContext;
|
|
||||||
|
|
||||||
ServerContext *server_context_new (const gchar *object_path,
|
|
||||||
GDBusConnection *connection);
|
|
||||||
void server_context_set_enabled (ServerContext *context,
|
|
||||||
gboolean enabled);
|
|
||||||
void server_context_set_client_connection
|
|
||||||
(ServerContext *context,
|
|
||||||
const gchar *client_connection);
|
|
||||||
const gchar *server_context_get_client_connection
|
|
||||||
(ServerContext *context);
|
|
||||||
void server_context_set_client_name
|
|
||||||
(ServerContext *context,
|
|
||||||
const gchar *client_name);
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
||||||
#endif /* SERVER_CONTEXT_H */
|
|
||||||
@ -1,4 +1,7 @@
|
|||||||
---
|
---
|
||||||
|
row_spacing: 0
|
||||||
|
button_spacing: 0
|
||||||
|
|
||||||
bounds:
|
bounds:
|
||||||
x: 0
|
x: 0
|
||||||
y: 0
|
y: 0
|
||||||
@ -9,7 +12,6 @@ views:
|
|||||||
- "test"
|
- "test"
|
||||||
outlines:
|
outlines:
|
||||||
default:
|
default:
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 0, height: 0 }
|
bounds: { x: 0, y: 0, width: 0, height: 0 }
|
||||||
|
|
||||||
buttons:
|
buttons:
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
---
|
---
|
||||||
# missing views
|
# missing views
|
||||||
|
row_spacing: 0
|
||||||
|
button_spacing: 0
|
||||||
|
|
||||||
bounds:
|
bounds:
|
||||||
x: 0
|
x: 0
|
||||||
y: 0
|
y: 0
|
||||||
@ -7,6 +10,5 @@ bounds:
|
|||||||
height: 0
|
height: 0
|
||||||
outlines:
|
outlines:
|
||||||
default:
|
default:
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 0, height: 0 }
|
bounds: { x: 0, y: 0, width: 0, height: 0 }
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
---
|
---
|
||||||
# extra field
|
# extra field
|
||||||
|
row_spacing: 0
|
||||||
|
button_spacing: 0
|
||||||
|
|
||||||
bounds:
|
bounds:
|
||||||
x: 0
|
x: 0
|
||||||
y: 0
|
y: 0
|
||||||
@ -10,7 +13,6 @@ views:
|
|||||||
- "test"
|
- "test"
|
||||||
outlines:
|
outlines:
|
||||||
default:
|
default:
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 0, height: 0 }
|
bounds: { x: 0, y: 0, width: 0, height: 0 }
|
||||||
|
|
||||||
bad_field: false
|
bad_field: false
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
---
|
---
|
||||||
# punctuation
|
# punctuation
|
||||||
|
row_spacing: 0
|
||||||
|
button_spacing: 0
|
||||||
|
|
||||||
bounds:
|
bounds:
|
||||||
x: 0
|
x: 0
|
||||||
y: 0
|
y: 0
|
||||||
@ -10,7 +13,6 @@ views:
|
|||||||
- "."
|
- "."
|
||||||
outlines:
|
outlines:
|
||||||
default:
|
default:
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 0, height: 0 }
|
bounds: { x: 0, y: 0, width: 0, height: 0 }
|
||||||
|
|
||||||
buttons:
|
buttons:
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
---
|
---
|
||||||
# punctuation
|
# punctuation
|
||||||
|
row_spacing: 0
|
||||||
|
button_spacing: 0
|
||||||
|
|
||||||
bounds:
|
bounds:
|
||||||
x: 0
|
x: 0
|
||||||
y: 0
|
y: 0
|
||||||
@ -10,7 +13,6 @@ views:
|
|||||||
- "å"
|
- "å"
|
||||||
outlines:
|
outlines:
|
||||||
default:
|
default:
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 0, height: 0 }
|
bounds: { x: 0, y: 0, width: 0, height: 0 }
|
||||||
|
|
||||||
buttons:
|
buttons:
|
||||||
|
|||||||
@ -47,11 +47,11 @@ endforeach
|
|||||||
# The layout test is in the examples directory
|
# The layout test is in the examples directory
|
||||||
# due to the way Cargo builds executables
|
# due to the way Cargo builds executables
|
||||||
# and the need to call it manually
|
# and the need to call it manually
|
||||||
foreach layout : ['us', 'nb', 'number']
|
foreach layout : ['us', 'el', 'es', 'it', 'nb', 'number']
|
||||||
test(
|
test(
|
||||||
'test_layout_' + layout,
|
'test_layout_' + layout,
|
||||||
cargo_script,
|
cargo_script,
|
||||||
args: [meson.source_root(), '', 'run', '--example', 'test_layout', layout]
|
args: ['', 'run', '--example', 'test_layout', layout]
|
||||||
)
|
)
|
||||||
endforeach
|
endforeach
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user