Compare commits

..

4 Commits

Author SHA1 Message Date
0e2def7069 Merge branch 'dorota.czaplejewicz/squeekboard-layouts' into 'layouts'
Check for null buttons

See merge request dorota.czaplejewicz/squeekboard!7
2019-08-28 14:14:41 +00:00
aa5e1d87dd Check for null buttons 2019-08-28 14:01:25 +00:00
0a0f7a09a4 Check for button position more in Rust
The check against fitting inside the Layout was removed: as an optimization it is unneeded, as the actual search must be optimized to be quick. In addition, the view bounds don't correspond to anything physical as long as negative offsets are allowed.
2019-08-28 10:45:04 +00:00
9ef38ecf30 Drop callback iteration for button finding 2019-08-28 10:45:00 +00:00
86 changed files with 7471 additions and 2928 deletions

View File

@ -9,10 +9,6 @@ stages:
- librem5
before_script:
- apt-get -y update
- apt-get -y install wget ca-certificates gnupg
- echo "deb http://ci.puri.sm/ scratch librem5" > /etc/apt/sources.list.d/ci.list
- wget -O- https://ci.puri.sm/ci-repo.key | apt-key add -
- apt-get -y update
- apt-get -y build-dep .
@ -26,26 +22,6 @@ build_meson:
- meson . _build/ -Ddepdatadir=/usr/share
- ninja -C _build install
build_deb:
<<: *tags
stage: build
artifacts:
paths:
- "*.deb"
script:
- apt-get -y install devscripts
- debuild -i -us -uc -b
- cp ../*.deb .
test_lintian:
<<: *tags
stage: test
dependencies:
- build_deb
script:
- apt-get -y install lintian
- lintian *.deb
test:
<<: *tags
stage: test

162
Cargo.lock generated
View File

@ -1,162 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "bitflags"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "dtoa"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "linked-hash-map"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "maplit"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memmap"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "proc-macro2"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rs"
version = "0.1.0"
dependencies = [
"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)",
"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)",
"xkbcommon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_derive"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_yaml"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
"yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "xkbcommon"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "yaml-rust"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"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 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 maplit 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
"checksum proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afdc77cc74ec70ed262262942ebb7dac3d479e9e5cfa2da1841c0806f6cdabcc"
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
"checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd"
"checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e"
"checksum serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)" = "38b08a9a90e5260fe01c6480ec7c811606df6d3a660415808c3c3fa8ed95b582"
"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum xkbcommon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fda0ea5f7ddabd51deeeda7799bee06274112f577da7dd3d954b8eda731b2fce"
"checksum yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d"

View File

@ -1,15 +0,0 @@
[package]
name = "rs"
version = "0.1.0"
[dependencies]
bitflags = "1.0.*"
maplit = "1.0.*"
serde = { version = "1.0.*", features = ["derive"] }
serde_yaml = "0.8.*"
xkbcommon = { version = "0.4.*", features = ["wayland"] }
[lib]
name = "rs"
path = "src/lib.rs"
crate-type = ["staticlib", "rlib"]

View File

@ -1,58 +0,0 @@
Hacking
=======
This document describes the standards for modifying and maintaining the *squeekboard* project.
Testing
-------
Most common testing is done in CI. Occasionally, and for each release, do perform manual tests to make sure that
- the application draws correctly
- it shows when relevant
- it changes layouts
- it changes levels
Testing with an application:
```
python3 tests/entry.py
```
Testing visibility:
```
$ busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b true
$ busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b false
```
Testing layouts:
```
$ gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('xkb', 'ua')]"
$ gsettings set org.gnome.desktop.input-sources current 1
```
Maintenance
-----------
Squeekboard uses Rust & Cargo for some of its dependencies.
Use the `cargo.sh` script for maintaining the Cargo part of the build. The script takes the usual Cargo commands, after the first 2 positionsl arguments: source directory, and output artifact. So, `cargo test` becomes:
```
cd build_dir
sh /source_path/cargo.sh '' test
```
### Cargo dependencies
Dependencies must be specified in `Cargo.toml` with 2 numbers: "major.minor". Since bugfix version number is meant to not affect the interface, this allows for safe updates.
`Cargo.lock` is used for remembering the revisions of all Rust dependencies. It should be updated often, preferably with each bugfix revision, and in a commit on its own:
```
cd build_dir
sh /source_path/cargo.sh '' update
ninja test
```

View File

@ -3,18 +3,14 @@
*Squeekboard* is a virtual keyboard supporting Wayland, built primarily for the *Librem 5* phone.
It squeaks because some Rust got inside.
Features
--------
### Present
- GTK3
- Custom yaml-defined keyboards
- Custom xml-defined keyboards
- DBus interface to show and hide
- Use Wayland input method protocol to show and hide
- Use Wayland virtual keyboard protocol
### Temporarily dropped
@ -22,6 +18,8 @@ Features
### TODO
- Use Wayland virtual keyboard protocol
- Use Wayland text input protocol
- Use Wayland input method protocol
- Pick up DBus interface files from /usr/share
@ -40,20 +38,31 @@ $ cd squeekboard
$ mkdir ../build
$ meson ../build/
$ cd ../build
$ ninja test
$ ninja install
```
For development, alter the `meson` call:
```
$ meson ../build/ --prefix=../install
```
and don't skip `ninja install` before running. The last step is necessary in order to find the keyboard definition files.
Running
-------
```
$ phoc # if no compatible Wayland compositor is running yet
$ rootston
$ cd ../build/
$ src/squeekboard
```
Developing
----------
### Testing
See `HACKING.md`
```
$ busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b true
$ busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b false
$ gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('xkb', 'ua')]"
$ gsettings set org.gnome.desktop.input-sources current 1
```

View File

@ -0,0 +1 @@
Eek cheader_filename="eek/eek.h"

View File

@ -0,0 +1 @@
EekGtk cheader_filename="eek/eek-gtk.h"

View File

@ -0,0 +1 @@
EekXkl cheader_filename="eek/eek-xkl.h"

View File

@ -0,0 +1 @@
gio-2.0

View File

@ -0,0 +1 @@
eek-0.90

View File

@ -0,0 +1,2 @@
eek-0.90
x11

View File

@ -1,24 +0,0 @@
#!/bin/sh
# This script manages Cargo operations
# while keeping the artifact directory within the build tree
# instead of the source tree
set -e
SCRIPT_PATH="$(realpath "$0")"
SOURCE_DIR="$(dirname "$SCRIPT_PATH")"
CARGO_TARGET_DIR="$(pwd)"
export CARGO_TARGET_DIR
if [ -n "${1}" ]; then
OUT_PATH="$(realpath "$1")"
fi
cd "$SOURCE_DIR"
shift
cargo "$@"
if [ -n "${OUT_PATH}" ]; then
cp "${CARGO_TARGET_DIR}"/debug/librs.a "${OUT_PATH}"
fi

View File

@ -1,197 +0,0 @@
# 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: "]"

View File

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

View File

@ -0,0 +1,64 @@
<?xml version="1.0"?>
<geometry version="0.90">
<bounds x="10" y="10" width="410.0000" height="229"/>
<outline id="default" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="37.46341" y="0.000000"/>
<point x="37.46341" y="52"/>
<point x="0.000000" y="52"/>
</outline>
<outline id="altline" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="48.39024" y="0.000000"/>
<point x="48.39024" y="52"/>
<point x="0.000000" y="52"/>
</outline>
<outline id="outline7" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="88.97561" y="0.000000"/>
<point x="88.97561" y="52"/>
<point x="0.000000" y="52"/>
</outline>
<outline id="spaceline" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="150.5853" y="0.000000"/>
<point x="150.5853" y="52"/>
<point x="0.000000" y="52"/>
</outline>
<button name="Shift_L" oref="altline" />
<button name="BackSpace" oref="altline" />
<button name="preferences" oref="altline" />
<button name="show_numbers" oref="altline" keycode="0" />
<button name="show_letters" oref="altline" keycode="0" />
<button name="show_symbols" oref="altline" keycode="0" />
<button name="period" oref="altline" />
<button name="space" oref="spaceline" />
<button name="Return" oref="outline7" />
<view>
<section angle="0">q w e r t y u i o p</section>
<section angle="0">a s d f g h j k l</section>
<section angle="0"> Shift_L z x c v b n m BackSpace </section>
<section angle="0"> show_numbers preferences space period Return </section>
</view>
<view>
<section angle="0">Q W E R T Y U I O P</section>
<section angle="0">A S D F G H J K L</section>
<section angle="0"> Shift_L Z X C V B N M BackSpace </section>
<section angle="0"> show_numbers preferences space period Return </section>
</view>
<view>
<section angle="0">1 2 3 4 5 6 7 8 9 0</section>
<section angle="0">at numbersign dollar percent ampersand minus underscore plus parenleft parenright</section>
<section angle="0"> show_symbols comma quotedbl quoteright colon semicolon exclam question BackSpace </section>
<section angle="0"> show_letters preferences space period Return </section>
</view>
<view>
<section angle="0">asciitilde quoteleft bar U00B7 squareroot Greek_pi Greek_tau division multiply paragraph</section>
<section angle="0">copyright U00AE U00A3 EuroSign U00A5 asciicircum degree asterisk braceleft braceright</section>
<section angle="0"> show_numbers backslash slash less greater equal bracketleft bracketright BackSpace </section>
<section angle="0"> show_letters preferences space period Return </section>
</view>
</geometry>

View File

@ -0,0 +1,105 @@
<?xml version="1.0"?>
<geometry version="0.90">
<bounds x="0" y="10.000000" width="426.0000" height="296.5853"/>
<outline id="outline2" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="32" y="0.000000"/>
<point x="32" y="52"/>
<point x="0.000000" y="52"/>
</outline>
<outline id="altline" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="48.39024" y="0.000000"/>
<point x="48.39024" y="52"/>
<point x="0.000000" y="52"/>
</outline>
<outline id="outline4" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="59.31707" y="0.000000"/>
<point x="59.31707" y="52"/>
<point x="0.000000" y="52"/>
</outline>
<outline id="outline5" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="59.31707" y="0.000000"/>
<point x="59.31707" y="52"/>
<point x="0.000000" y="52"/>
</outline>
<outline id="outline6" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="68.68292" y="0.000000"/>
<point x="68.68292" y="52"/>
<point x="0.000000" y="52"/>
</outline>
<outline id="outline7" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="88.97561" y="0.000000"/>
<point x="88.97561" y="52"/>
<point x="0.000000" y="52"/>
</outline>
<outline id="outline8" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="88.97561" y="0.000000"/>
<point x="88.97561" y="52"/>
<point x="0.000000" y="52"/>
</outline>
<outline id="outline9" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="109.2682" y="0.000000"/>
<point x="109.2682" y="52"/>
<point x="0.000000" y="52"/>
</outline>
<outline id="outline10" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="37.46341" y="0.000000"/>
<point x="37.46341" y="52"/>
<point x="0.000000" y="52"/>
</outline>
<outline id="outline13" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="79.60975" y="0.000000"/>
<point x="79.60975" y="52"/>
<point x="0.000000" y="52"/>
</outline>
<outline id="spaceline" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="150.5853" y="0.000000"/>
<point x="150.5853" y="52"/>
<point x="0.000000" y="52"/>
</outline>
<button name="Shift_L" oref="altline" />
<button name="BackSpace" oref="altline" />
<button name="preferences" oref="altline" />
<button name="show_numbers" oref="altline" keycode="0" />
<button name="show_letters" oref="altline" keycode="0" />
<button name="show_symbols" oref="altline" keycode="0" />
<button name="space" oref="spaceline" />
<button name="return" oref="outline7" />
<view>
<section angle="0">q w e r t y u i o p aring</section>
<section angle="0">a s d f g h j k l oslash ae</section>
<section angle="0"> Shift_L z x c v b n m BackSpace </section>
<section angle="0"> show_numbers preferences space period Return </section>
</view>
<view>
<section angle="0">Q W E R T Y U I O P Aring</section>
<section angle="0">A S D F G H J K L Oslash AE</section>
<section angle="0"> Shift_L Z X C V B N M BackSpace </section>
<section angle="0"> show_numbers preferences space period Return </section>
</view>
<view>
<section angle="0">1 2 3 4 5 6 7 8 9 0</section>
<section angle="0">at numbersign dollar percent ampersand minus underscore plus parenleft parenright</section>
<section angle="0"> show_symbols comma quotedbl quoteright colon semicolon exclam question BackSpace </section>
<section angle="0"> show_letters preferences space period Return </section>
</view>
<view>
<section angle="0">asciitilde quoteleft bar U00B7 squareroot Greek_pi Greek_tau division multiply paragraph</section>
<section angle="0">copyright U00AE U00A3 EuroSign U00A5 asciicircum degree asterisk braceleft braceright</section>
<section angle="0"> show_numbers backslash slash less greater equal bracketleft bracketright BackSpace </section>
<section angle="0"> show_letters preferences space period Return </section>
</view>
</geometry>

View File

@ -0,0 +1,40 @@
<?xml version="1.0"?>
<geometry version="0.90">
<bounds x="0" y="10.000000" width="426.0000" height="296.5853"/>
<outline id="default" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="37.46341" y="0.000000"/>
<point x="37.46341" y="52.44877"/>
<point x="0.000000" y="52.44877"/>
</outline>
<outline id="altline" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="48.39024" y="0.000000"/>
<point x="48.39024" y="52.44877"/>
<point x="0.000000" y="52.44877"/>
</outline>
<outline id="outline7" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="88.97561" y="0.000000"/>
<point x="88.97561" y="52.44877"/>
<point x="0.000000" y="52.44877"/>
</outline>
<outline id="spaceline" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="120.5853" y="0.000000"/>
<point x="120.5853" y="52.44877"/>
<point x="0.000000" y="52.44877"/>
</outline>
<button name="BackSpace" oref="altline" />
<button name="space" oref="spaceline" />
<button name="Return" oref="outline7" />
<view>
<section angle="0">1 2 3 parenleft parenright</section>
<section angle="0">4 5 6 numbersign asterisk</section>
<section angle="0">7 8 9 plus minus</section>
<section angle="0">BackSpace 0 space Return</section>
</view>
</geometry>

View File

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

View File

@ -0,0 +1,100 @@
<?xml version="1.0"?>
<keyboards version="0.90">
<keyboard id="ar" name="ar"
geometry="compact" symbols="ar"
longname="Arabic" language="ar"/>
<keyboard id="be" name="be"
geometry="compact" symbols="be"
longname="Belarusian" language="be"/>
<keyboard id="fa" name="fa"
geometry="compact" symbols="fa"
longname="Farsi (ISIRI 2901-1994)" language="fa"/>
<keyboard id="he" name="he"
geometry="compact" symbols="he"
longname="Hebrew" language="he"/>
<keyboard id="ja" name="ja"
geometry="compact" symbols="ja-kana"
longname="Japanese (Kana)" language="ja"/>
<keyboard id="kk" name="kk"
geometry="compact" symbols="kk"
longname="Kazakh" language="kk"/>
<keyboard id="ks" name="ks"
geometry="compact" symbols="ks"
longname="Kashmiri" language="ks"/>
<keyboard id="my" name="my"
geometry="compact" symbols="my"
longname="Myanmar" language="my"/>
<keyboard id="nb" name="nb"
geometry="extended" symbols="nb"
longname="Norwegian" language="nb"/>
<keyboard id="ru" name="ru"
geometry="compact" symbols="us"
longname="Russian" language="ru"/>
<keyboard id="th" name="th"
geometry="compact" symbols="th"
longname="Thai" language="th"/>
<keyboard id="ua" name="ua"
geometry="compact" symbols="ua"
longname="Ukrainian" language="ua"/>
<keyboard id="ug" name="ug"
geometry="compact" symbols="ug"
longname="Uyghur" language="ug"/>
<keyboard id="us" name="us"
geometry="compact" symbols="us"
longname="US" language="en"/>
<keyboard id="zh-bopomofo" name="zh-bopomofo"
geometry="compact" symbols="zh-bopomofo"
longname="Chinese (Bopomofo)" language="zh"/>
<!-- Indic Inscript keyboards converted from m17n-lib -->
<keyboard id="as-inscript" name="as-inscript"
geometry="compact" symbols="as-inscript"
longname="Assamese (Inscript)" language="as"/>
<keyboard id="bn-inscript" name="bn-inscript"
geometry="compact" symbols="bn-inscript"
longname="Bengali (Inscript)" language="bn"/>
<keyboard id="gu-inscript" name="gu-inscript"
geometry="compact" symbols="gu-inscript"
longname="Gujarati (Inscript)" language="gu"/>
<keyboard id="hi-inscript" name="hi-inscript"
geometry="compact" symbols="hi-inscript"
longname="Hindi (Inscript)" language="hi"/>
<keyboard id="kn-inscript" name="kn-inscript"
geometry="compact" symbols="kn-inscript"
longname="Kannada (Inscript)" language="kn"/>
<keyboard id="ks-inscript" name="ks-inscript"
geometry="compact" symbols="ks-inscript"
longname="Kashmiri Devanagari (Inscript)" language="ks"/>
<keyboard id="mai-inscript" name="mai-inscript"
geometry="compact" symbols="mai-inscript"
longname="Maithili (Inscript)" language="mai"/>
<keyboard id="ml-inscript" name="ml-inscript"
geometry="compact" symbols="ml-inscript"
longname="Malayalam (Inscript)" language="ml-inscript"/>
<keyboard id="mr-inscript" name="mr-inscript"
geometry="compact" symbols="mr-inscript"
longname="Marathi (Inscript)" language="mr"/>
<keyboard id="or-inscript" name="or-inscript"
geometry="compact" symbols="or-inscript"
longname="Oriya (Inscript)" language="or"/>
<keyboard id="pa-inscript" name="pa-inscript"
geometry="compact" symbols="pa-inscript"
longname="Punjabi (Inscript)" language="pa"/>
<keyboard id="sd-inscript" name="sd-inscript"
geometry="compact" symbols="sd-inscript"
longname="Sindhi (Inscript)" language="sd"/>
<keyboard id="ta-inscript" name="ta-inscript"
geometry="compact" symbols="ta-inscript"
longname="Tamil (Inscript)" language="ta"/>
<keyboard id="te-inscript" name="te-inscript"
geometry="compact" symbols="te-inscript"
longname="Telugu (Inscript)" language="te"/>
<!-- Common keyboards -->
<keyboard id="number" name="number"
geometry="number-keypad" symbols="special/number"
longname="Numeric keypad" language="all"/>
<keyboard id="phone" name="phone"
geometry="number-keypad" symbols="special/number"
longname="Phone keypad" language="all"/>
</keyboards>

View File

@ -1,178 +0,0 @@
---
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:
- "q w e r t y u i o p aring"
- "a s d f g h j k l oslash ae"
- "Shift_L z x c v b n m BackSpace"
- "show_numbers preferences space period Return"
upper:
- "Q W E R T Y U I O P Aring"
- "A S D F G H J K L Oslash AE"
- "Shift_L Z X C V B N M BackSpace"
- "show_numbers preferences space period 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: "ABC"
show_symbols:
action:
set_view: "symbols"
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: "]"

View File

@ -1,46 +0,0 @@
---
row_spacing: 11.33
button_spacing: 4.67
bounds: { x: 0, y: 6.33, width: 410, height: 250 }
outlines:
default:
bounds: { x: 0, y: 0, width: 37.46341, 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: 120.5853, height: 52 }
views:
base:
- "1 2 3 parenleft parenright"
- "4 5 6 numbersign asterisk"
- "7 8 9 plus minus"
- "BackSpace 0 space Return"
buttons:
BackSpace:
outline: "altline"
icon: "edit-clear-symbolic"
space:
outline: spaceline
label: " "
Return:
outline: outline7
icon: "key-enter"
asterisk:
label: "*"
numbersign:
label: "#"
minus:
label: "-"
plus:
label: "+"
parenleft:
label: "("
parenright:
label: ")"

View File

@ -0,0 +1,132 @@
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<symbols version="0.90">
<symbol label="*">asterisk</symbol>
<symbol label="+/=">show_symbols</symbol>
<symbol label="&#964;">Greek_tau</symbol>
<symbol label="å">aring</symbol>
<symbol label="ø">oslash</symbol>
<symbol label="æ">ae</symbol>
<symbol label="Å">Aring</symbol>
<symbol label="Ø">Oslash</symbol>
<symbol label="Æ">AE</symbol>
<symbol label="q">q</symbol>
<symbol label="Q">Q</symbol>
<symbol label="1">1</symbol>
<symbol label="~">asciitilde</symbol>
<symbol label="w">w</symbol>
<symbol label="W">W</symbol>
<symbol label="2">2</symbol>
<symbol label="`">quoteleft</symbol>
<symbol label="e">e</symbol>
<symbol label="E">E</symbol>
<symbol label="3">3</symbol>
<symbol label="|">bar</symbol>
<symbol label="r">r</symbol>
<symbol label="R">R</symbol>
<symbol label="4">4</symbol>
<symbol label="&#183;">U00B7</symbol>
<symbol label="t">t</symbol>
<symbol label="T">T</symbol>
<symbol label="5">5</symbol>
<symbol label="&#8730;">squareroot</symbol>
<symbol label="y">y</symbol>
<symbol label="Y">Y</symbol>
<symbol label="6">6</symbol>
<symbol label="&#960;">Greek_pi</symbol>
<symbol label="u">u</symbol>
<symbol label="U">U</symbol>
<symbol label="7">7</symbol>
<symbol label="&#247;">division</symbol>
<symbol label="i">i</symbol>
<symbol label="I">I</symbol>
<symbol label="8">8</symbol>
<symbol label="&#215;">multiply</symbol>
<symbol label="o">o</symbol>
<symbol label="O">O</symbol>
<symbol label="9">9</symbol>
<symbol label="&#182;">paragraph</symbol>
<symbol label="p">p</symbol>
<symbol label="P">P</symbol>
<symbol label="0">0</symbol>
<symbol label="&#9651;">U25B3</symbol>
<symbol keyval="229" label="&#229;">aring</symbol>
<symbol keyval="197" label="&#197;">Aring</symbol>
<symbol label="a">a</symbol>
<symbol label="A">A</symbol>
<symbol label="@">at</symbol>
<symbol label="&#169;">copyright</symbol>
<symbol label="s">s</symbol>
<symbol label="S">S</symbol>
<symbol label="#">numbersign</symbol>
<symbol label="&#174;">U00AE</symbol>
<symbol label="d">d</symbol>
<symbol label="D">D</symbol>
<symbol label="$">dollar</symbol>
<symbol label="&#163;">U00A3</symbol>
<symbol label="f">f</symbol>
<symbol label="F">F</symbol>
<symbol label="%">percent</symbol>
<symbol label="&#8364;">EuroSign</symbol>
<symbol label="g">g</symbol>
<symbol label="G">G</symbol>
<symbol label="&amp;">ampersand</symbol>
<symbol label="&#165;">U00A5</symbol>
<symbol label="h">h</symbol>
<symbol label="H">H</symbol>
<symbol label="-">minus</symbol>
<symbol label="^">asciicircum</symbol>
<symbol label="j">j</symbol>
<symbol label="J">J</symbol>
<symbol label="_">underscore</symbol>
<symbol label="&#176;">degree</symbol>
<symbol label="k">k</symbol>
<symbol label="K">K</symbol>
<symbol label="+">plus</symbol>
<symbol label="=">equal</symbol>
<symbol label="l">l</symbol>
<symbol label="L">L</symbol>
<symbol label="(">parenleft</symbol>
<symbol label="{">braceleft</symbol>
<symbol keyval="248" label="&#248;">oslash</symbol>
<symbol keyval="216" label="&#216;">Oslash</symbol>
<symbol label=")">parenright</symbol>
<symbol label="}">braceright</symbol>
<symbol keyval="230" label="&#230;">ae</symbol>
<symbol keyval="198" label="&#198;">AE</symbol>
<symbol keyval="65293" icon="key-enter">Return</symbol>
<symbol keyval="65505" icon="key-shift">Shift_L</symbol>
<symbol label="z">z</symbol>
<symbol label="Z">Z</symbol>
<symbol label=",">comma</symbol>
<symbol label="\">backslash</symbol>
<symbol label="x">x</symbol>
<symbol label="X">X</symbol>
<symbol label="&quot;">quotedbl</symbol>
<symbol label="/">slash</symbol>
<symbol label="c">c</symbol>
<symbol label="C">C</symbol>
<symbol label="'">quoteright</symbol>
<symbol label="&lt;">less</symbol>
<symbol label="v">v</symbol>
<symbol label="V">V</symbol>
<symbol label=":">colon</symbol>
<symbol label="&gt;">greater</symbol>
<symbol label="b">b</symbol>
<symbol label="B">B</symbol>
<symbol label=";">semicolon</symbol>
<symbol label="=">equal</symbol>
<symbol label="n">n</symbol>
<symbol label="N">N</symbol>
<symbol label="!">exclam</symbol>
<symbol label="[">bracketleft</symbol>
<symbol label="m">m</symbol>
<symbol label="M">M</symbol>
<symbol label="?">question</symbol>
<symbol label="]">bracketright</symbol>
<symbol label=".">period</symbol>
<symbol label="123">show_numbers</symbol>
<symbol label="ABC">show_letters</symbol>
<symbol label="&#9786;" icon="keyboard-mode-symbolic" tooltip="Setup">preferences</symbol>
<symbol label=" ">space</symbol>
<symbol keyval="65288" icon="edit-clear-symbolic">BackSpace</symbol>
</symbols>

View File

@ -0,0 +1,22 @@
<?xml version='1.0' encoding='ASCII' standalone='yes'?>
<symbols version="0.90">
<symbol label="1">1</symbol>
<symbol label="2">2</symbol>
<symbol label="3">3</symbol>
<symbol label="(">parenleft</symbol>
<symbol label=")">parenright</symbol>
<symbol label="4">4</symbol>
<symbol label="5">5</symbol>
<symbol label="6">6</symbol>
<symbol label="#">numbersign</symbol>
<symbol label="*">asterisk</symbol>
<symbol label="7">7</symbol>
<symbol label="8">8</symbol>
<symbol label="9">9</symbol>
<symbol label="+">plus</symbol>
<symbol label="-">minus</symbol>
<symbol label="0">0</symbol>
<symbol keyval="65293" icon="key-enter">Return</symbol>
<symbol label=" ">space</symbol>
<symbol keyval="65288" icon="edit-clear-symbolic">BackSpace</symbol>
</symbols>

View File

@ -0,0 +1,118 @@
<?xml version='1.0' encoding='ASCII' standalone='yes'?>
<symbols version="0.90">
<symbol label="*">asterisk</symbol>
<symbol label="+/=">show_symbols</symbol>
<symbol label="q">q</symbol>
<symbol label="Q">Q</symbol>
<symbol label="1">1</symbol>
<symbol label="~">asciitilde</symbol>
<symbol label="w">w</symbol>
<symbol label="W">W</symbol>
<symbol label="2">2</symbol>
<symbol label="`">quoteleft</symbol>
<symbol label="e">e</symbol>
<symbol label="E">E</symbol>
<symbol label="3">3</symbol>
<symbol label="|">bar</symbol>
<symbol label="r">r</symbol>
<symbol label="R">R</symbol>
<symbol label="4">4</symbol>
<symbol label="&#183;">U00B7</symbol>
<symbol label="t">t</symbol>
<symbol label="T">T</symbol>
<symbol label="5">5</symbol>
<symbol label="&#8730;">squareroot</symbol>
<symbol label="y">y</symbol>
<symbol label="Y">Y</symbol>
<symbol label="6">6</symbol>
<symbol label="&#960;">Greek_pi</symbol>
<symbol label="u">u</symbol>
<symbol label="U">U</symbol>
<symbol label="7">7</symbol>
<symbol label="&#247;">division</symbol>
<symbol label="i">i</symbol>
<symbol label="I">I</symbol>
<symbol label="8">8</symbol>
<symbol label="&#215;">multiply</symbol>
<symbol label="o">o</symbol>
<symbol label="O">O</symbol>
<symbol label="9">9</symbol>
<symbol label="&#182;">paragraph</symbol>
<symbol label="p">p</symbol>
<symbol label="P">P</symbol>
<symbol label="0">0</symbol>
<symbol label="&#964;">Greek_tau</symbol>
<symbol label="a">a</symbol>
<symbol label="A">A</symbol>
<symbol label="@">at</symbol>
<symbol label="&#169;">copyright</symbol>
<symbol label="s">s</symbol>
<symbol label="S">S</symbol>
<symbol label="#">numbersign</symbol>
<symbol label="&#174;">U00AE</symbol>
<symbol label="d">d</symbol>
<symbol label="D">D</symbol>
<symbol label="$">dollar</symbol>
<symbol label="&#163;">U00A3</symbol>
<symbol label="f">f</symbol>
<symbol label="F">F</symbol>
<symbol label="%">percent</symbol>
<symbol label="&#8364;">EuroSign</symbol>
<symbol label="g">g</symbol>
<symbol label="G">G</symbol>
<symbol label="&amp;">ampersand</symbol>
<symbol label="&#165;">U00A5</symbol>
<symbol label="h">h</symbol>
<symbol label="H">H</symbol>
<symbol label="-">minus</symbol>
<symbol label="^">asciicircum</symbol>
<symbol label="j">j</symbol>
<symbol label="J">J</symbol>
<symbol label="_">underscore</symbol>
<symbol label="&#176;">degree</symbol>
<symbol label="k">k</symbol>
<symbol label="K">K</symbol>
<symbol label="+">plus</symbol>
<symbol label="=">equal</symbol>
<symbol label="l">l</symbol>
<symbol label="L">L</symbol>
<symbol label="(">parenleft</symbol>
<symbol label="{">braceleft</symbol>
<symbol label=")">parenright</symbol>
<symbol label="}">braceright</symbol>
<symbol keyval="65293" icon="key-enter">Return</symbol>
<symbol keyval="65505" icon="key-shift">Shift_L</symbol>
<symbol label="z">z</symbol>
<symbol label="Z">Z</symbol>
<symbol label=",">comma</symbol>
<symbol label="\">backslash</symbol>
<symbol label="x">x</symbol>
<symbol label="X">X</symbol>
<symbol label="&quot;">quotedbl</symbol>
<symbol label="/">slash</symbol>
<symbol label="c">c</symbol>
<symbol label="C">C</symbol>
<symbol label="'">quoteright</symbol>
<symbol label="&lt;">less</symbol>
<symbol label="v">v</symbol>
<symbol label="V">V</symbol>
<symbol label=":">colon</symbol>
<symbol label="&gt;">greater</symbol>
<symbol label="b">b</symbol>
<symbol label="B">B</symbol>
<symbol label=";">semicolon</symbol>
<symbol label="n">n</symbol>
<symbol label="N">N</symbol>
<symbol label="!">exclam</symbol>
<symbol label="[">bracketleft</symbol>
<symbol label="m">m</symbol>
<symbol label="M">M</symbol>
<symbol label="?">question</symbol>
<symbol label="]">bracketright</symbol>
<symbol label=".">period</symbol>
<symbol label="123">show_numbers</symbol>
<symbol label="ABC">show_letters</symbol>
<symbol label="&#9786;" icon="keyboard-mode-symbolic" tooltip="Setup">preferences</symbol>
<symbol label=" ">space</symbol>
<symbol keyval="65288" icon="edit-clear-symbolic">BackSpace</symbol>
</symbols>

View File

@ -1,88 +0,0 @@
---
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: 137.33, 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 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 preferences space period Return"
numbers:
- "1 2 3 4 5 6 7 8 9 0"
- "@ # $ % & - _ + ( )"
- "show_symbols , \" ' colon ; ! ? BackSpace"
- "show_letters preferences space period Return"
symbols:
- "~ ` | · √ π τ ÷ × ¶"
- "© ® £ € ¥ ^ ° * { }"
- "show_numbers_from_symbols \\ / < > = [ ] 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: "special"
icon: "keyboard-mode-symbolic"
show_numbers:
action:
set_view: "numbers"
outline: "wide"
label: "123"
show_numbers_from_symbols:
action:
set_view: "numbers"
outline: "altline"
label: "123"
show_letters:
action:
set_view: "base"
outline: "wide"
label: "ABC"
show_symbols:
action:
set_view: "symbols"
outline: "altline"
label: "*/="
period:
outline: "special"
label: "."
space:
outline: "spaceline"
label: " "
Return:
outline: "wide"
icon: "key-enter"
colon:
label: ":"
"\"":
keysym: "quotedbl"

View File

@ -6,5 +6,4 @@ Exec=squeekboard
Icon=squeekboard
Terminal=false
Type=Application
NoDisplay=true
Categories=GTK;Utility;

View File

@ -2,6 +2,10 @@
<gresources>
<gresource prefix="/sm/puri/squeekboard">
<file compressed="true">style.css</file>
<file compressed="true" preprocess="xml-stripblanks">keyboards/geometry/compact.xml</file>
<file compressed="true" preprocess="xml-stripblanks">keyboards/geometry/extended.xml</file>
<file compressed="true" preprocess="xml-stripblanks">keyboards/geometry/number-keypad.xml</file>
<file compressed="true" preprocess="xml-stripblanks">keyboards/keyboards.xml</file>
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ar.xml</file>
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/as-inscript.xml</file>
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/be.xml</file>
@ -19,6 +23,7 @@
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ml-inscript.xml</file>
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/mr-inscript.xml</file>
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/my.xml</file>
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/nb.xml</file>
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/or-inscript.xml</file>
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/pa-inscript.xml</file>
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ru.xml</file>
@ -28,7 +33,9 @@
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/th.xml</file>
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ua.xml</file>
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ug.xml</file>
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/us.xml</file>
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/zh-bopomofo.xml</file>
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/special/number.xml</file>
<file>icons/key-enter.svg</file>
<file>icons/key-shift.svg</file>
<file>icons/keyboard-mode-symbolic.svg</file>

View File

@ -1,41 +1,19 @@
sq_view {
.keyboard {
background-color: rgba(0, 0, 0, 255);
color: #ffffff;
font-family: cantarell, sans-serif;
}
sq_button {
.key {
color: #deddda;
background: #464448;
border-style: solid;
border-width: 1px;
border-color: #5e5c64;
border-radius: 3px;
border-radius: 2px;
}
sq_button:active {
background: #545256;
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 {
background: #1c71d8;
border-color: #1a5fb4
}
#Return:active {
.key:active {
background: #1c71d8;
border-color: #3584e4;
}

12
debian/cargo/config vendored
View File

@ -1,12 +0,0 @@
# When modifying this file, consider instead
# to take advantage of the method that Cargo packagers use
# to set up all the necessary stuff automatically:
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=907629#30
[source.crates-io]
registry = 'https://github.com/rust-lang/crates.io-index'
replace-with = 'vendored-sources'
[source.vendored-sources]
directory = '/usr/share/cargo/registry'

19
debian/changelog vendored
View File

@ -1,22 +1,3 @@
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
* Use Cargo-based dependencies
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Tue, 24 Sep 2019 10:42:15 +0000
squeekboard (1.1.0) unstable; urgency=medium
* Use new keyboard layout format
-- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm> Mon, 02 Sep 2019 10:12:02 +0000
squeekboard (1.0.10) unstable; urgency=medium
* Use a shared DBus definition

9
debian/control vendored
View File

@ -3,19 +3,12 @@ Section: x11
Priority: optional
Maintainer: Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm>
Build-Depends:
cargo,
debhelper (>= 10),
meson (>=0.51.0),
ninja-build,
meson (>=0.43.0),
pkg-config,
libglib2.0-dev,
libgtk-3-dev,
libcroco3-dev,
librust-bitflags-1-dev (>= 1.0),
librust-maplit-1-dev (>= 1.0),
librust-serde-derive-1-dev (>= 1.0),
librust-serde-yaml-0.8-dev (>= 0.8),
librust-xkbcommon-0.4+wayland-dev (>= 0.4),
libwayland-dev (>= 1.16),
rustc,
wayland-protocols (>= 1.14),

7
debian/rules vendored
View File

@ -1,15 +1,8 @@
#!/usr/bin/make -f
export CARGO_HOME = $(CURDIR)/debian/cargo
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
%:
dh $@ --builddirectory=_build --buildsystem=meson
# The Debian version of linked-hash-map doesn't provide any hash,
# causing Cargo to refuse to build with a crates.io copy
build-arch:
rm Cargo.lock
dh $@ --builddirectory=_build --buildsystem=meson
override_dh_autoreconf:

View File

@ -1,2 +0,0 @@
# yaml-rust 0.4.3 shares some roots with libyaml, including the string which lintian checks, creating a false positive
squeekboard binary: embedded-library usr/bin/squeekboard: libyaml

View File

@ -34,6 +34,7 @@
#include "eek-renderer.h"
#include "eek-keyboard.h"
#include "src/symbol.h"
#include "eek-gtk-keyboard.h"
@ -52,6 +53,8 @@ typedef struct _EekGtkKeyboardPrivate
{
EekRenderer *renderer;
LevelKeyboard *keyboard;
GtkCssProvider *css_provider;
GtkStyleContext *scontext;
GdkEventSequence *sequence; // unowned reference
} EekGtkKeyboardPrivate;
@ -59,15 +62,15 @@ typedef struct _EekGtkKeyboardPrivate
G_DEFINE_TYPE_WITH_PRIVATE (EekGtkKeyboard, eek_gtk_keyboard, GTK_TYPE_DRAWING_AREA)
static void on_button_pressed (struct squeek_button *button, struct squeek_view *view,
EekGtkKeyboard *self);
static void on_button_released (const struct squeek_button *button,
struct squeek_view *view,
EekGtkKeyboard *self);
static void render_pressed_button (GtkWidget *widget, struct button_place *place);
static void render_locked_button (GtkWidget *widget,
struct button_place *place);
static void render_released_button (GtkWidget *widget,
const struct squeek_button *button);
EekGtkKeyboard *self);
static void on_button_released (struct squeek_button *button,
struct squeek_view *view,
EekGtkKeyboard *self);
static void render_pressed_button (GtkWidget *widget, struct button_place *place);
static void render_locked_button (GtkWidget *widget,
struct button_place *place);
static void render_released_button (GtkWidget *widget,
struct squeek_button *button);
static void
eek_gtk_keyboard_real_realize (GtkWidget *self)
@ -78,8 +81,7 @@ eek_gtk_keyboard_real_realize (GtkWidget *self)
GDK_KEY_RELEASE_MASK |
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_BUTTON_MOTION_MASK |
GDK_TOUCH_MASK);
GDK_BUTTON_MOTION_MASK);
GTK_WIDGET_CLASS (eek_gtk_keyboard_parent_class)->realize (self);
}
@ -96,7 +98,7 @@ eek_gtk_keyboard_real_draw (GtkWidget *self,
if (!priv->renderer) {
PangoContext *pcontext = gtk_widget_get_pango_context (self);
priv->renderer = eek_renderer_new (priv->keyboard, pcontext);
priv->renderer = eek_renderer_new (priv->keyboard, pcontext, priv->scontext);
eek_renderer_set_allocation_size (priv->renderer,
allocation.width,
@ -107,23 +109,25 @@ eek_gtk_keyboard_real_draw (GtkWidget *self,
eek_renderer_render_keyboard (priv->renderer, cr);
struct squeek_view *view = squeek_layout_get_current_view(priv->keyboard->layout);
struct squeek_view *view = priv->keyboard->views[priv->keyboard->level];
/* redraw pressed key */
const GList *list = priv->keyboard->pressed_keys;
const GList *list = priv->keyboard->pressed_buttons;
for (const GList *head = list; head; head = g_list_next (head)) {
struct button_place place = squeek_view_find_key(
view, head->data
view, squeek_button_get_key(head->data)
);
if (place.button)
render_pressed_button (self, &place);
}
/* redraw locked key */
list = priv->keyboard->locked_keys;
list = priv->keyboard->locked_buttons;
for (const GList *head = list; head; head = g_list_next (head)) {
struct button_place place = squeek_view_find_key(
view, ((EekModifierKey *)head->data)->key
view, squeek_button_get_key(
((EekModifierKey *)head->data)->button
)
);
if (place.button)
render_locked_button (self, &place);
@ -156,78 +160,56 @@ static void depress(EekGtkKeyboard *self,
struct squeek_button *button = eek_renderer_find_button_by_position (priv->renderer, view, x, y);
if (button) {
eek_keyboard_press_key(
priv->keyboard,
squeek_button_get_key(button),
time
);
eek_keyboard_press_button(priv->keyboard, button, time);
on_button_pressed(button, view, self);
}
}
static void drag(EekGtkKeyboard *self,
gdouble x, gdouble y, guint32 time)
{
gdouble x, gdouble y, guint32 time) {
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
struct squeek_view *view = level_keyboard_current(priv->keyboard);
struct squeek_button *button = eek_renderer_find_button_by_position (priv->renderer, view, x, y);
GList *list, *head;
list = g_list_copy(priv->keyboard->pressed_keys);
list = g_list_copy(priv->keyboard->pressed_buttons);
if (button) {
gboolean found = FALSE;
for (head = list; head; head = g_list_next (head)) {
struct squeek_key *key = head->data;
if (squeek_button_has_key(button, key)) {
if (head->data == button) {
found = TRUE;
} else {
eek_keyboard_release_key(priv->keyboard, key, time);
// The released handler proceeds to ignore this info...
// let's do this for consistency nevertheless
struct button_place place = squeek_view_find_key(view, key);
on_button_released(place.button, view, self);
eek_keyboard_release_button(priv->keyboard, head->data, time);
on_button_released(button, view, self);
}
}
g_list_free (list);
if (!found) {
eek_keyboard_press_key(
priv->keyboard,
squeek_button_get_key(button),
time
);
eek_keyboard_press_button(priv->keyboard, button, time);
on_button_pressed(button, view, self);
}
} else {
for (head = list; head; head = g_list_next (head)) {
struct squeek_key *key = head->data;
eek_keyboard_release_key(priv->keyboard, key, time);
// The released handler proceeds to ignore this info...
// let's do this for consistency nevertheless
struct button_place place = squeek_view_find_key(view, key);
on_button_released(place.button, view, self);
eek_keyboard_release_button(priv->keyboard, head->data, time);
on_button_released(head->data, view, self);
}
g_list_free (list);
}
}
static void release(EekGtkKeyboard *self, guint32 time)
{
static void release(EekGtkKeyboard *self, guint32 time) {
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
struct squeek_view *view = level_keyboard_current(priv->keyboard);
GList *list = g_list_copy(priv->keyboard->pressed_keys);
GList *list = g_list_copy(priv->keyboard->pressed_buttons);
for (GList *head = list; head; head = g_list_next (head)) {
struct squeek_key *key = head->data;
eek_keyboard_release_key(priv->keyboard, key, time);
// The released handler proceeds to ignore this info...
// let's do this for consistency nevertheless
struct button_place place = squeek_view_find_key(view, key);
on_button_released(place.button, view, self);
struct squeek_button *button = head->data;
eek_keyboard_release_button(priv->keyboard, button, time);
on_button_released(button, view, self);
}
g_list_free (list);
}
@ -272,27 +254,27 @@ handle_touch_event (GtkWidget *widget,
EekGtkKeyboard *self = EEK_GTK_KEYBOARD (widget);
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
/* For each new touch, release the previous one and record the new event
sequence. */
if (event->type == GDK_TOUCH_BEGIN) {
release(self, event->time);
if (priv->sequence) {
// Ignore second and following touch points
return FALSE;
}
priv->sequence = event->sequence;
depress(self, event->x, event->y, event->time);
return TRUE;
}
/* Only allow the latest touch point to be dragged. */
if (event->type == GDK_TOUCH_UPDATE && event->sequence == priv->sequence) {
if (priv->sequence != event->sequence) {
return FALSE;
}
if (event->type == GDK_TOUCH_UPDATE) {
drag(self, event->x, event->y, event->time);
}
else if (event->type == GDK_TOUCH_END || event->type == GDK_TOUCH_CANCEL) {
if (event->type == GDK_TOUCH_END || event->type == GDK_TOUCH_CANCEL) {
// TODO: can the event have different coords than the previous update event?
/* Only respond to the release of the latest touch point. Previous
touches have already been released. */
if (event->sequence == priv->sequence) {
release(self, event->time);
priv->sequence = NULL;
}
release(self, event->time);
priv->sequence = NULL;
}
return TRUE;
}
@ -304,20 +286,49 @@ eek_gtk_keyboard_real_unmap (GtkWidget *self)
eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
if (priv->keyboard) {
GList *head;
GList *list, *head;
for (head = priv->keyboard->pressed_keys; head; head = g_list_next (head)) {
/* Unlike other places where we call this, we don't call
on_button_released afterwards since we don't want to queue a
redraw. */
eek_keyboard_release_key(priv->keyboard, head->data,
gdk_event_get_time(NULL));
/* Make a copy of HEAD before sending "released" signal on
elements, so that the default handler of
EekKeyboard::key-released signal can remove elements from its
internal copy */
list = g_list_copy(priv->keyboard->pressed_buttons);
for (head = list; head; head = g_list_next (head)) {
g_log("squeek", G_LOG_LEVEL_DEBUG, "emit EekKey released");
g_signal_emit_by_name (head->data, "released");
}
g_list_free (list);
}
GTK_WIDGET_CLASS (eek_gtk_keyboard_parent_class)->unmap (self);
}
static gboolean
eek_gtk_keyboard_real_query_tooltip (GtkWidget *widget,
gint x,
gint y,
gboolean keyboard_tooltip,
GtkTooltip *tooltip)
{
EekGtkKeyboard *self = EEK_GTK_KEYBOARD (widget);
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
struct squeek_view *view = level_keyboard_current(priv->keyboard);
struct squeek_button *button = eek_renderer_find_button_by_position (priv->renderer,
view,
(gdouble)x,
(gdouble)y);
if (button) {
//struct squeek_symbol *symbol = eek_key_get_symbol_at_index(key, 0, priv->keyboard->level);
const gchar *text = NULL; // FIXME
if (text) {
gtk_tooltip_set_text (tooltip, text);
return TRUE;
}
}
return FALSE;
}
static void
eek_gtk_keyboard_set_property (GObject *object,
guint prop_id,
@ -340,16 +351,17 @@ eek_gtk_keyboard_dispose (GObject *object)
if (priv->renderer) {
g_object_unref (priv->renderer);
priv->renderer = NULL;
priv->renderer = NULL;
}
if (priv->keyboard) {
GList *head;
GList *list, *head;
for (head = priv->keyboard->pressed_keys; head; head = g_list_next (head)) {
eek_keyboard_release_key(priv->keyboard, head->data,
gdk_event_get_time(NULL));
list = g_list_copy(priv->keyboard->pressed_buttons);
for (head = list; head; head = g_list_next (head)) {
g_log("squeek", G_LOG_LEVEL_DEBUG, "emit EekKey pressed");
g_signal_emit_by_name (head->data, "released", level_keyboard_current(priv->keyboard));
}
g_list_free (list);
priv->keyboard = NULL;
}
@ -373,6 +385,8 @@ eek_gtk_keyboard_class_init (EekGtkKeyboardClass *klass)
eek_gtk_keyboard_real_button_release_event;
widget_class->motion_notify_event =
eek_gtk_keyboard_real_motion_notify_event;
widget_class->query_tooltip =
eek_gtk_keyboard_real_query_tooltip;
widget_class->touch_event = handle_touch_event;
gobject_class->set_property = eek_gtk_keyboard_set_property;
@ -381,7 +395,22 @@ eek_gtk_keyboard_class_init (EekGtkKeyboardClass *klass)
static void
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:
@ -438,10 +467,9 @@ render_locked_button (GtkWidget *widget, struct button_place *place)
cairo_region_destroy (region);
}
// TODO: does it really redraw the entire keyboard?
static void
render_released_button (GtkWidget *widget,
const struct squeek_button *button)
struct squeek_button *button)
{
(void)button;
EekGtkKeyboard *self = EEK_GTK_KEYBOARD (widget);
@ -461,8 +489,8 @@ render_released_button (GtkWidget *widget,
static void
on_button_pressed (struct squeek_button *button,
struct squeek_view *view,
EekGtkKeyboard *self)
struct squeek_view *view,
EekGtkKeyboard *self)
{
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
@ -490,9 +518,9 @@ on_button_pressed (struct squeek_button *button,
}
static void
on_button_released (const struct squeek_button *button,
struct squeek_view *view,
EekGtkKeyboard *self)
on_button_released (struct squeek_button *button,
struct squeek_view *view,
EekGtkKeyboard *self)
{
(void)view;
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);

217
eek/eek-keyboard-drawing.c Normal file
View File

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

View File

@ -34,6 +34,7 @@
#include "eekboard/key-emitter.h"
#include "keymap.h"
#include "src/keyboard.h"
#include "src/symbol.h"
#include "eek-keyboard.h"
@ -49,52 +50,76 @@ eek_modifier_key_free (EekModifierKey *modkey)
g_slice_free (EekModifierKey, modkey);
}
void
eek_keyboard_set_key_locked (LevelKeyboard *keyboard,
struct squeek_key *key)
/// Updates the state of locked keys based on the key that was activated
/// FIXME: make independent of what the key are named,
/// and instead refer to the contained symbols
static guint
set_key_states (LevelKeyboard *keyboard,
struct squeek_button *button,
guint new_level)
{
EekModifierKey *modifier_key = g_slice_new (EekModifierKey);
modifier_key->modifiers = 0;
modifier_key->key = key;
keyboard->locked_keys =
g_list_prepend (keyboard->locked_keys, modifier_key);
}
/// Unlock all locked keys.
/// All locked keys will unlock at the next keypress (should be called "stuck")
/// Returns the number of handled keys
/// TODO: may need to check key type in order to chain locks
/// before pressing an "emitting" key
static int unlock_keys(LevelKeyboard *keyboard) {
int handled = 0;
for (GList *head = keyboard->locked_keys; head; ) {
EekModifierKey *modifier_key = head->data;
GList *next = g_list_next (head);
keyboard->locked_keys =
g_list_remove_link (keyboard->locked_keys, head);
//squeek_key_set_locked(squeek_button_get_key(modifier_key->button), false);
squeek_layout_set_state_from_press(keyboard->layout, keyboard, modifier_key->key);
g_list_free1 (head);
head = next;
handled++;
struct squeek_key *key = squeek_button_get_key(button);
// Keys locking rules hardcoded for the time being...
const gchar *name = squeek_symbol_get_name(squeek_key_get_symbol(key));
// Lock the shift whenever it's pressed on the baselevel
// TODO: need to lock shift on the destination level
if (g_strcmp0(name, "Shift_L") == 0 && keyboard->level == 0) {
EekModifierKey *modifier_key = g_slice_new (EekModifierKey);
modifier_key->modifiers = 0;
modifier_key->button = button;
keyboard->locked_buttons =
g_list_prepend (keyboard->locked_buttons, modifier_key);
squeek_key_set_locked(key, true);
}
return handled;
if (keyboard->level == 1) {
// Only shift is locked in this state, unlock on any key press
for (GList *head = keyboard->locked_buttons; head; ) {
EekModifierKey *modifier_key = head->data;
GList *next = g_list_next (head);
keyboard->locked_buttons =
g_list_remove_link (keyboard->locked_buttons, head);
squeek_key_set_locked(squeek_button_get_key(modifier_key->button), false);
g_list_free1 (head);
head = next;
}
return 0;
}
return new_level;
}
// FIXME: unhardcode, parse some user information as to which key triggers which view (level)
static void
set_level_from_press (LevelKeyboard *keyboard, struct squeek_key *key)
set_level_from_press (LevelKeyboard *keyboard, struct squeek_button *button)
{
// If the currently locked key was already handled in the unlock phase,
// then skip
if (unlock_keys(keyboard) == 0) {
squeek_layout_set_state_from_press(keyboard->layout, keyboard, key);
/* The levels are: 0 Letters, 1 Upper case letters, 2 Numbers, 3 Symbols */
guint level = keyboard->level;
/* Handle non-emitting keys */
if (button) {
const gchar *name = squeek_symbol_get_name(squeek_key_get_symbol(squeek_button_get_key(button)));
if (g_strcmp0(name, "show_numbers") == 0) {
level = 2;
} else if (g_strcmp0(name, "show_letters") == 0) {
level = 0;
} else if (g_strcmp0(name, "show_symbols") == 0) {
level = 3;
} else if (g_strcmp0(name, "Shift_L") == 0) {
level ^= 1;
}
}
keyboard->level = set_key_states(keyboard, button, level);
eek_layout_update_layout(keyboard);
}
void eek_keyboard_press_key(LevelKeyboard *keyboard, struct squeek_key *key, guint32 timestamp) {
void eek_keyboard_press_button(LevelKeyboard *keyboard, struct squeek_button *button, guint32 timestamp) {
struct squeek_key *key = squeek_button_get_key(button);
squeek_key_set_pressed(key, TRUE);
keyboard->pressed_keys = g_list_prepend (keyboard->pressed_keys, key);
keyboard->pressed_buttons = g_list_prepend (keyboard->pressed_buttons, button);
struct squeek_symbol *symbol = squeek_key_get_symbol(key);
if (!symbol)
return;
// Only take action about setting level *after* the key has taken effect, i.e. on release
//set_level_from_press (keyboard, key);
@ -106,28 +131,43 @@ void eek_keyboard_press_key(LevelKeyboard *keyboard, struct squeek_key *key, gui
emit_key_activated(keyboard->manager, keyboard, keycode, TRUE, timestamp);
}
void eek_keyboard_release_key(LevelKeyboard *keyboard,
struct squeek_key *key,
guint32 timestamp) {
for (GList *head = keyboard->pressed_keys; head; head = g_list_next (head)) {
if (squeek_key_equal(head->data, key)) {
keyboard->pressed_keys = g_list_remove_link (keyboard->pressed_keys, head);
void eek_keyboard_release_button(LevelKeyboard *keyboard,
struct squeek_button *button,
guint32 timestamp) {
for (GList *head = keyboard->pressed_buttons; head; head = g_list_next (head)) {
if (head->data == button) {
keyboard->pressed_buttons = g_list_remove_link (keyboard->pressed_buttons, head);
g_list_free1 (head);
break;
}
}
set_level_from_press (keyboard, key);
struct squeek_symbol *symbol = squeek_button_get_symbol(button);
if (!symbol)
return;
set_level_from_press (keyboard, button);
// "Borrowed" from eek-context-service; doesn't influence the state but forwards the event
guint keycode = squeek_key_get_keycode (key);
guint keycode = squeek_key_get_keycode (squeek_button_get_key(button));
emit_key_activated(keyboard->manager, keyboard, keycode, FALSE, timestamp);
}
void level_keyboard_deinit(LevelKeyboard *self) {
squeek_layout_free(self->layout);
g_hash_table_destroy (self->names);
for (guint i = 0; i < self->outline_array->len; i++) {
EekOutline *outline = &g_array_index (self->outline_array,
EekOutline,
i);
g_slice_free1 (sizeof (EekPoint) * outline->num_points,
outline->points);
}
g_array_free (self->outline_array, TRUE);
for (guint i = 0; i < 4; i++) {
// free self->view[i];
}
}
void level_keyboard_free(LevelKeyboard *self) {
@ -135,18 +175,115 @@ void level_keyboard_free(LevelKeyboard *self) {
g_free(self);
}
void level_keyboard_init(LevelKeyboard *self, struct squeek_layout *layout) {
self->layout = layout;
void level_keyboard_init(LevelKeyboard *self) {
self->outline_array = g_array_new (FALSE, TRUE, sizeof (EekOutline));
}
LevelKeyboard *level_keyboard_new(EekboardContextService *manager, struct squeek_layout *layout) {
LevelKeyboard *level_keyboard_new(EekboardContextService *manager, struct squeek_view *views[4], GHashTable *name_button_hash) {
LevelKeyboard *keyboard = g_new0(LevelKeyboard, 1);
level_keyboard_init(keyboard, layout);
level_keyboard_init(keyboard);
for (uint i = 0; i < 4; i++) {
keyboard->views[i] = views[i];
}
keyboard->manager = manager;
keyboard->names = name_button_hash;
return keyboard;
}
/**
* eek_keyboard_find_key_by_name:
* @keyboard: an #EekKeyboard
* @name: a key name
*
* Find an #EekKey whose name is @name.
* Return value: (transfer none): #EekKey whose name is @name
*/
struct squeek_button*
eek_keyboard_find_button_by_name (LevelKeyboard *keyboard,
const gchar *name)
{
return g_hash_table_lookup (keyboard->names, name);
}
/**
* eek_keyboard_get_outline:
* @keyboard: an #EekKeyboard
* @oref: ID of the outline
*
* Get an outline associated with @oref in @keyboard.
* Returns: an #EekOutline, which should not be released
*/
EekOutline *
level_keyboard_get_outline (LevelKeyboard *keyboard,
guint oref)
{
if (oref > keyboard->outline_array->len)
return NULL;
return &g_array_index (keyboard->outline_array, EekOutline, oref);
}
/**
* eek_keyboard_get_keymap:
* @keyboard: an #EekKeyboard
*
* Get the keymap for the keyboard.
* Returns: a string containing the XKB keymap.
*/
gchar *
eek_keyboard_get_keymap(LevelKeyboard *keyboard)
{
/* Start the keycodes and symbols sections with their respective headers. */
gchar *keycodes = g_strdup(keymap_keycodes_header);
gchar *symbols = g_strdup(keymap_symbols_header);
/* Iterate over the keys in the name-to-key hash table. */
GHashTableIter iter;
gchar *button_name;
gpointer button_ptr;
g_hash_table_iter_init(&iter, keyboard->names);
while (g_hash_table_iter_next(&iter, (gpointer)&button_name, &button_ptr)) {
gchar *current, *line;
struct squeek_button *button = button_ptr;
struct squeek_key *key = squeek_button_get_key(button);
guint keycode = squeek_key_get_keycode(key);
/* Don't include invalid keycodes in the keymap. */
if (keycode == EEK_INVALID_KEYCODE)
continue;
/* Append a key name-to-keycode definition to the keycodes section. */
current = keycodes;
line = g_strdup_printf(" <%s> = %i;\n", (char *)button_name, keycode);
keycodes = g_strconcat(current, line, NULL);
g_free(line);
g_free(current);
// FIXME: free
const char *key_str = squeek_key_to_keymap_entry(
(char*)button_name,
key
);
current = symbols;
symbols = g_strconcat(current, key_str, NULL);
g_free(current);
}
/* Assemble the keymap file from the header, sections and footer. */
gchar *keymap = g_strconcat(keymap_header,
keycodes, " };\n\n",
symbols, " };\n\n",
keymap_footer, NULL);
g_free(keycodes);
g_free(symbols);
return keymap;
}
struct squeek_view *level_keyboard_current(LevelKeyboard *keyboard)
{
return squeek_layout_get_current_view(keyboard->layout);
return keyboard->views[keyboard->level];
}

View File

@ -36,19 +36,24 @@ G_BEGIN_DECLS
struct _EekModifierKey {
/*< public >*/
EekModifierType modifiers;
struct squeek_key *key;
struct squeek_button *button;
};
typedef struct _EekModifierKey EekModifierKey;
/// Keyboard state holder
struct _LevelKeyboard {
struct squeek_layout *layout;
struct squeek_view *views[4];
guint level;
struct xkb_keymap *keymap;
int keymap_fd; // keymap formatted as XKB string
size_t keymap_len; // length of the data inside keymap_fd
GArray *outline_array;
GList *pressed_keys; // struct squeek_key*
GList *locked_keys; // struct EekModifierKey*
GList *pressed_buttons; // struct squeek_button*
GList *locked_buttons; // struct squeek_button*
/* Map button names to button objects: */
GHashTable *names;
guint id; // as a key to layout choices
@ -56,25 +61,31 @@ struct _LevelKeyboard {
};
typedef struct _LevelKeyboard LevelKeyboard;
struct squeek_button *eek_keyboard_find_button_by_name(LevelKeyboard *keyboard,
const gchar *name);
/// Represents the path to the button within a view
struct button_place {
const struct squeek_row *row;
const struct squeek_button *button;
};
EekOutline *level_keyboard_get_outline
(LevelKeyboard *keyboard,
guint oref);
EekModifierKey *eek_modifier_key_copy
(EekModifierKey *modkey);
void eek_modifier_key_free
(EekModifierKey *modkey);
void eek_keyboard_press_key(LevelKeyboard *keyboard, struct squeek_key *key, guint32 timestamp);
void eek_keyboard_release_key(LevelKeyboard *keyboard, struct squeek_key *key, guint32 timestamp);
void eek_keyboard_press_button(LevelKeyboard *keyboard, struct squeek_button *button, guint32 timestamp);
void eek_keyboard_release_button(LevelKeyboard *keyboard, struct squeek_button *button, guint32 timestamp);
gchar * eek_keyboard_get_keymap
(LevelKeyboard *keyboard);
struct squeek_view *level_keyboard_current(LevelKeyboard *keyboard);
LevelKeyboard *level_keyboard_new(EekboardContextService *manager, struct squeek_layout *layout);
LevelKeyboard *level_keyboard_new(EekboardContextService *manager, struct squeek_view *views[], GHashTable *name_button_hash);
void level_keyboard_deinit(LevelKeyboard *self);
void level_keyboard_free(LevelKeyboard *self);

65
eek/eek-keysym.c Normal file
View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010-2011 Red Hat, Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
/**
* SECTION:eek-keysym
* @short_description: an #EekSymbol represents an X keysym
*/
#include "config.h"
#include "eek-keysym.h"
/* modifier keys */
#define EEK_KEYSYM_Shift_L 0xffe1
#define EEK_KEYSYM_Shift_R 0xffe2
#define EEK_KEYSYM_ISO_Level3_Shift 0xfe03
#define EEK_KEYSYM_Caps_Lock 0xffe5
#define EEK_KEYSYM_Shift_Lock 0xffe6
#define EEK_KEYSYM_Control_L 0xffe3
#define EEK_KEYSYM_Control_R 0xffe4
#define EEK_KEYSYM_Alt_L 0xffe9
#define EEK_KEYSYM_Alt_R 0xffea
#define EEK_KEYSYM_Meta_L 0xffe7
#define EEK_KEYSYM_Meta_R 0xffe8
#define EEK_KEYSYM_Super_L 0xffeb
#define EEK_KEYSYM_Super_R 0xffec
#define EEK_KEYSYM_Hyper_L 0xffed
#define EEK_KEYSYM_Hyper_R 0xffee
struct _EekKeysymEntry {
guint xkeysym;
const gchar *name;
};
typedef struct _EekKeysymEntry EekKeysymEntry;
#include "eek-xkeysym-keysym-entries.h"
guint32
eek_keysym_from_name (const gchar *name)
{
for (uint i = 0; i < G_N_ELEMENTS(xkeysym_keysym_entries); i++) {
if (g_strcmp0 (xkeysym_keysym_entries[i].name, name) == 0) {
return xkeysym_keysym_entries[i].xkeysym;
}
}
return 0;
}

32
eek/eek-keysym.h Normal file
View File

@ -0,0 +1,32 @@
/*
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010-2011 Red Hat, Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#if !defined(__EEK_H_INSIDE__) && !defined(EEK_COMPILATION)
#error "Only <eek/eek.h> can be included directly."
#endif
#ifndef EEK_KEYSYM_H
#define EEK_KEYSYM_H 1
#include "glib.h"
guint32 eek_keysym_from_name (const gchar *name);
#endif /* EEK_KEYSYM_H */

View File

@ -45,3 +45,9 @@ void
eek_layout_init (EekLayout *self)
{
}
void
eek_layout_update_layout(LevelKeyboard *keyboard)
{
squeek_view_place_contents(level_keyboard_current(keyboard), keyboard);
}

View File

@ -56,5 +56,14 @@ struct _EekLayoutClass
GType eek_layout_get_type (void) G_GNUC_CONST;
void eek_layout_place_rows(LevelKeyboard *keyboard, struct squeek_view *level);
void eek_layout_update_layout(LevelKeyboard *keyboard);
LevelKeyboard *
level_keyboard_from_layout (EekLayout *layout,
gdouble initial_width,
gdouble initial_height);
G_END_DECLS
#endif /* EEK_LAYOUT_H */

View File

@ -24,11 +24,14 @@
#include <string.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "src/symbol.h"
#include "eek-renderer.h"
enum {
PROP_0,
PROP_PCONTEXT,
PROP_STYLE_CONTEXT,
PROP_LAST
};
@ -37,9 +40,11 @@ typedef struct _EekRendererPrivate
LevelKeyboard *keyboard;
PangoContext *pcontext;
GtkCssProvider *css_provider;
GtkStyleContext *layout_context;
GtkStyleContext *button_context; // TODO: maybe move a copy to each button
GtkStyleContext *scontext;
GtkStyleContext *key_context;
EekColor default_foreground_color;
EekColor default_background_color;
gdouble border_width;
gdouble allocation_width;
@ -51,7 +56,6 @@ typedef struct _EekRendererPrivate
PangoFontDescription *ascii_font;
PangoFontDescription *font;
// TODO: Drop those or transform into general button surface caches
GHashTable *outline_surface_cache;
GHashTable *active_outline_surface_cache;
GHashTable *icons;
@ -61,7 +65,15 @@ typedef struct _EekRendererPrivate
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 */
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,
PangoLayout *layout,
const struct squeek_button *button);
@ -69,7 +81,7 @@ static void eek_renderer_real_render_button_label (EekRenderer *self,
static void invalidate (EekRenderer *renderer);
static void render_button (EekRenderer *self,
cairo_t *cr, struct button_place *place,
gboolean pressed, gboolean locked);
gboolean active);
struct _CreateKeyboardSurfaceCallbackData {
cairo_t *cr;
@ -99,7 +111,7 @@ create_keyboard_surface_button_callback (struct squeek_button *button,
.row = data->row,
.button = button,
};
render_button (data->renderer, data->cr, &place, FALSE, FALSE);
render_button (data->renderer, data->cr, &place, FALSE);
cairo_restore (data->cr);
}
@ -119,7 +131,8 @@ create_keyboard_surface_row_callback (struct squeek_row *row,
cairo_rotate (data->cr, angle * G_PI / 180);
data->row = row;
squeek_row_foreach(row, create_keyboard_surface_button_callback, data);
squeek_row_foreach(row, create_keyboard_surface_button_callback,
data);
cairo_restore (data->cr);
}
@ -130,7 +143,7 @@ render_keyboard_surface (EekRenderer *renderer, struct squeek_view *view)
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
EekColor foreground;
eek_renderer_get_foreground_color (renderer, priv->layout_context, &foreground);
eek_renderer_get_foreground_color (renderer, priv->scontext, &foreground);
EekBounds bounds = squeek_view_get_bounds (level_keyboard_current(priv->keyboard));
@ -141,11 +154,11 @@ render_keyboard_surface (EekRenderer *renderer, struct squeek_view *view)
};
/* Paint the background covering the entire widget area */
gtk_render_background (priv->layout_context,
gtk_render_background (priv->scontext,
data.cr,
0, 0,
priv->allocation_width, priv->allocation_height);
gtk_render_frame (priv->layout_context,
gtk_render_frame (priv->scontext,
data.cr,
0, 0,
priv->allocation_width, priv->allocation_height);
@ -162,33 +175,57 @@ render_keyboard_surface (EekRenderer *renderer, struct squeek_view *view)
/* draw rows */
squeek_view_foreach(level_keyboard_current(priv->keyboard),
create_keyboard_surface_row_callback,
&data);
create_keyboard_surface_row_callback,
&data);
cairo_restore (data.cr);
cairo_destroy (data.cr);
}
static void
render_outline (cairo_t *cr,
GtkStyleContext *ctx,
EekBounds bounds)
render_button_outline (EekRenderer *renderer,
cairo_t *cr,
const struct squeek_button *button,
gboolean active)
{
gtk_render_background (ctx, cr, 0, 0, bounds.width, bounds.height);
gtk_render_frame (ctx, cr, 0, 0, bounds.width, bounds.height);
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
EekOutline *outline;
guint oref = squeek_button_get_oref(button);
outline = level_keyboard_get_outline (priv->keyboard, oref);
if (outline == NULL)
return;
EekBounds bounds = squeek_button_get_bounds(button);
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 render_button_in_context(EekRenderer *self,
cairo_t *cr,
GtkStyleContext *ctx,
struct button_place *place,
gboolean active) {
static void
render_button (EekRenderer *self,
cairo_t *cr,
struct button_place *place,
gboolean active)
{
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
EekOutline *outline;
cairo_surface_t *outline_surface;
GHashTable *outline_surface_cache;
PangoLayout *layout;
PangoRectangle extents = { 0, };
EekColor foreground;
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
guint oref = squeek_button_get_oref (place->button);
outline = level_keyboard_get_outline (priv->keyboard, oref);
if (outline == NULL)
return;
/* render outline */
EekBounds bounds = squeek_button_get_bounds(place->button);
@ -198,8 +235,7 @@ static void render_button_in_context(EekRenderer *self,
else
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, outline);
if (!outline_surface) {
cairo_t *cr;
@ -217,26 +253,31 @@ static void render_button_in_context(EekRenderer *self,
cairo_save (cr);
eek_renderer_apply_transformation_for_button (self, cr, place, 1.0, FALSE);
render_outline (cr, ctx, bounds);
render_button_outline (self, cr, place->button, active);
cairo_restore (cr);
cairo_destroy (cr);
g_hash_table_insert (outline_surface_cache,
(gpointer)place->button,
outline,
outline_surface);
}
cairo_set_source_surface (cr, outline_surface, 0.0, 0.0);
cairo_paint (cr);
eek_renderer_get_foreground_color (self, ctx, &foreground);
eek_renderer_get_foreground_color (self, priv->key_context, &foreground);
/* render icon (if any) */
const char *icon_name = squeek_button_get_icon_name(place->button);
struct squeek_symbol *symbol = squeek_button_get_symbol(place->button);
if (!symbol)
return;
if (icon_name) {
if (squeek_symbol_get_icon_name (symbol)) {
gint scale = priv->scale_factor;
cairo_surface_t *icon_surface =
eek_renderer_get_icon_surface (self, icon_name, 16 / priv->scale,
eek_renderer_get_icon_surface (self,
squeek_symbol_get_icon_name (symbol),
16 / priv->scale,
scale);
if (icon_surface) {
gint width = cairo_image_surface_get_width (icon_surface);
@ -260,6 +301,7 @@ static void render_button_in_context(EekRenderer *self,
return;
}
}
/* render label */
layout = pango_cairo_create_layout (cr);
eek_renderer_real_render_button_label (self, layout, place->button);
@ -276,49 +318,10 @@ static void render_button_in_context(EekRenderer *self,
foreground.green,
foreground.blue,
foreground.alpha);
pango_cairo_show_layout (cr, layout);
cairo_restore (cr);
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");
}
}
/**
@ -369,16 +372,18 @@ eek_renderer_real_render_button_label (EekRenderer *self,
{
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
const gchar *label = squeek_button_get_label(button);
if (!label) {
return;
}
const gchar *label;
PangoFontDescription *font;
PangoLayoutLine *line;
gdouble scale;
struct squeek_symbol *symbol = squeek_button_get_symbol(button);
if (!symbol)
return;
label = squeek_symbol_get_label (symbol);
if (!label)
return;
if (!priv->font) {
const PangoFontDescription *base_font;
@ -449,8 +454,7 @@ eek_renderer_real_render_button (EekRenderer *self,
struct squeek_key *key = squeek_button_get_key(place->button);
render_button (
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);
}
@ -477,7 +481,7 @@ eek_renderer_real_render_keyboard (EekRenderer *self,
cairo_get_target (cr), 0, 0,
priv->allocation_width, priv->allocation_height);
render_keyboard_surface (self, squeek_layout_get_current_view(priv->keyboard->layout));
render_keyboard_surface (self, priv->keyboard->views[priv->keyboard->level]);
cairo_set_source_surface (cr, priv->keyboard_surface, 0.0, 0.0);
source = cairo_get_source (cr);
@ -501,6 +505,10 @@ eek_renderer_set_property (GObject *object,
priv->pcontext = g_value_get_object (value);
g_object_ref (priv->pcontext);
break;
case PROP_STYLE_CONTEXT:
priv->scontext = g_value_get_object (value);
g_object_ref (priv->scontext);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -577,33 +585,15 @@ eek_renderer_class_init (EekRendererClass *klass)
g_object_class_install_property (gobject_class,
PROP_PCONTEXT,
pspec);
}
static GType new_type(char *name) {
GTypeInfo info = {0};
info.class_size = sizeof(GtkWidgetClass);
info.instance_size = sizeof(GtkWidget);
return g_type_register_static(GTK_TYPE_WIDGET, name, &info,
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;
pspec = g_param_spec_object ("style-context",
"GTK Style Context",
"GTK Style Context",
GTK_TYPE_STYLE_CONTEXT,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE);
g_object_class_install_property (gobject_class,
PROP_STYLE_CONTEXT,
pspec);
}
static void
@ -613,6 +603,8 @@ eek_renderer_init (EekRenderer *self)
priv->keyboard = NULL;
priv->pcontext = NULL;
priv->default_foreground_color = DEFAULT_FOREGROUND_COLOR;
priv->default_background_color = DEFAULT_BACKGROUND_COLOR;
priv->border_width = 1.0;
priv->allocation_width = 0.0;
priv->allocation_height = 0.0;
@ -644,30 +636,18 @@ eek_renderer_init (EekRenderer *self)
gtk_css_provider_load_from_resource (priv->css_provider,
"/sm/puri/squeekboard/style.css");
/* Create a style context for the layout */
GtkWidgetPath *path = gtk_widget_path_new();
gtk_widget_path_append_type(path, layout_type());
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,
/* Create a style context for keys */
priv->key_context = gtk_style_context_new ();
gtk_style_context_add_class (priv->key_context, "key");
gtk_style_context_add_provider (priv->key_context,
GTK_STYLE_PROVIDER(priv->css_provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
/* Create a style context for the buttons */
path = gtk_widget_path_new();
gtk_widget_path_append_type(path, layout_type());
gtk_widget_path_append_type(path, button_type());
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);
g_autoptr (GtkWidgetPath) path = NULL;
path = gtk_widget_path_new ();
gtk_widget_path_append_type (path, GTK_TYPE_BUTTON);
gtk_style_context_set_path (priv->key_context, path);
gtk_style_context_set_state (priv->key_context, GTK_STATE_FLAG_NORMAL);
}
static void
@ -689,10 +669,12 @@ invalidate (EekRenderer *renderer)
EekRenderer *
eek_renderer_new (LevelKeyboard *keyboard,
PangoContext *pcontext)
PangoContext *pcontext,
GtkStyleContext *scontext)
{
EekRenderer *renderer = g_object_new (EEK_TYPE_RENDERER,
"pango-context", pcontext,
"style-context", scontext,
NULL);
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
priv->keyboard = keyboard;
@ -786,7 +768,7 @@ eek_renderer_get_button_bounds (EekRenderer *renderer,
min = points[2];
max = points[0];
for (unsigned i = 0; i < G_N_ELEMENTS(points); i++) {
for (uint i = 0; i < G_N_ELEMENTS(points); i++) {
eek_point_rotate (&points[i], angle);
if (points[i].x < min.x)
min.x = points[i].x;
@ -889,6 +871,30 @@ eek_renderer_render_keyboard (EekRenderer *renderer,
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
eek_renderer_get_foreground_color (EekRenderer *renderer,
GtkStyleContext *context,
@ -930,7 +936,7 @@ eek_are_bounds_inside (EekBounds bounds, EekPoint point, EekPoint origin, int32_
points[3].x = points[0].x;
points[3].y = points[2].y;
for (unsigned i = 0; i < G_N_ELEMENTS(points); i++) {
for (uint i = 0; i < G_N_ELEMENTS(points); i++) {
eek_point_rotate (&points[i], angle);
points[i].x += origin.x;
points[i].y += origin.y;
@ -965,15 +971,14 @@ eek_are_bounds_inside (EekBounds bounds, EekPoint point, EekPoint origin, int32_
**/
struct squeek_button *
eek_renderer_find_button_by_position (EekRenderer *renderer,
struct squeek_view *view,
gdouble x,
gdouble y)
struct squeek_view *view,
gdouble x,
gdouble y)
{
g_return_val_if_fail (EEK_IS_RENDERER(renderer), NULL);
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
/* Transform from widget coordinates to keyboard coordinates */
EekPoint point = {
.x = (x - priv->origin_x)/priv->scale,
.y = (y - priv->origin_y)/priv->scale,

View File

@ -57,7 +57,8 @@ struct _EekRendererClass
GType eek_renderer_get_type (void) G_GNUC_CONST;
EekRenderer *eek_renderer_new (LevelKeyboard *keyboard,
PangoContext *pcontext);
PangoContext *pcontext,
GtkStyleContext *scontext);
void eek_renderer_set_allocation_size
(EekRenderer *renderer,
gdouble width,

56
eek/eek-section.c Normal file
View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010-2011 Red Hat, Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include "config.h"
#include "eek-section.h"
EekBounds eek_get_outline_size(LevelKeyboard *keyboard, uint32_t oref) {
EekOutline *outline = level_keyboard_get_outline (keyboard, oref);
if (outline && outline->num_points > 0) {
double minx = outline->points[0].x;
double maxx = minx;
double miny = outline->points[0].y;
double maxy = miny;
for (uint i = 1; i < outline->num_points; i++) {
EekPoint p = outline->points[i];
if (p.x < minx) {
minx = p.x;
} else if (p.x > maxx) {
maxx = p.x;
}
if (p.y < miny) {
miny = p.y;
} else if (p.y > maxy) {
maxy = p.y;
}
}
EekBounds key_bounds = {
.height = maxy - miny,
.width = maxx - minx,
.x = 0,
.y = 0,
};
return key_bounds;
}
EekBounds bounds = {0, 0, 0, 0};
return bounds;
}

34
eek/eek-section.h Normal file
View File

@ -0,0 +1,34 @@
/*
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010-2011 Red Hat, Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#if !defined(__EEK_H_INSIDE__) && !defined(EEK_COMPILATION)
#error "Only <eek/eek.h> can be included directly."
#endif
#ifndef EEK_SECTION_H
#define EEK_SECTION_H 1
/* Contains row-related functions that couldn't be done in Rust easily. */
#include <glib-object.h>
#include "eek-keyboard.h"
#include "src/layout.h"
#endif /* EEK_SECTION_H */

File diff suppressed because it is too large Load Diff

View File

@ -23,11 +23,47 @@
#ifndef EEK_XML_LAYOUT_H
#define EEK_XML_LAYOUT_H 1
#include "eek-types.h"
#include <gio/gio.h>
#include "eek-layout.h"
G_BEGIN_DECLS
#define EEK_TYPE_XML_LAYOUT (eek_xml_layout_get_type())
G_DECLARE_DERIVABLE_TYPE (EekXmlLayout, eek_xml_layout, EEK, XML_LAYOUT, EekLayout)
/**
* EekXmlLayoutClass:
*/
struct _EekXmlLayoutClass
{
/*< private >*/
EekLayoutClass parent_class;
/* padding */
gpointer pdummy[24];
};
struct _EekXmlKeyboardDesc
{
gchar *id;
gchar *name;
gchar *geometry;
gchar *symbols;
gchar *language;
gchar *longname;
};
typedef struct _EekXmlKeyboardDesc EekXmlKeyboardDesc;
GType eek_xml_layout_get_type (void) G_GNUC_CONST;
EekLayout *eek_xml_layout_new (const gchar *id,
GError **error);
GList *eek_xml_list_keyboards (void);
EekXmlKeyboardDesc *eek_xml_keyboard_desc_copy (EekXmlKeyboardDesc *desc);
void eek_xml_keyboard_desc_free (EekXmlKeyboardDesc *desc);
LevelKeyboard *
eek_xml_layout_real_create_keyboard (const char *keyboard_type,
eek_xml_layout_real_create_keyboard (EekLayout *self,
EekboardContextService *manager);
G_END_DECLS
#endif /* EEK_XML_LAYOUT_H */

View File

@ -24,6 +24,7 @@
#include "eek-keyboard.h"
#include "eek-layout.h"
#include "eek-keysym.h"
void eek_init (void);

50
eek/gen-keysym-entries.py Executable file
View File

@ -0,0 +1,50 @@
#!/usr/bin/env python3
# Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
# Copyright (C) 2010-2011 Red Hat, Inc.
# Copyright (C) 2019 Purism, SPC
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
# This library is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
import sys
import re
if len(sys.argv) > 3:
print("Usage: %s TABLE-NAME [INPUT_FILE]" % sys.argv[0], file=sys.stderr)
sys.exit(-1)
if len(sys.argv) < 3:
in_stream = sys.stdin
else:
in_stream = open(sys.argv[2])
table = dict()
for line in in_stream:
match = re.match(r'\s*(0x[0-9A-F]+)\s+"(.*)"\s+(\S*)', line, re.I)
if match:
table[int(match.group(1), 16)] = (match.group(2), match.group(3))
sys.stdout.write("static const EekKeysymEntry %s[] = {\n" %
sys.argv[1])
for index, (keysym, (l, c)) in enumerate([(keysym, table[keysym])
for keysym in sorted(table.keys())]):
sys.stdout.write(" { 0x%X, \"%s\" }" %
(keysym, l))
if index < len(table) - 1:
sys.stdout.write(",")
sys.stdout.write("\n")
sys.stdout.write("};\n")

View File

@ -6,3 +6,34 @@ squeek_keymap_get_entries_for_keyval (struct xkb_keymap *xkb_keymap,
guint keyval,
GdkKeymapKey **keys,
guint *n_keys);
static const char *keymap_header = "xkb_keymap {\n\
\n";
static const char *keymap_keycodes_header = "\
xkb_keycodes \"squeekboard\" {\n\n\
minimum = 8;\n\
maximum = 255;\n\
\n";
static const char *keymap_symbols_header = "\
xkb_symbols \"squeekboard\" {\n\
\n\
name[Group1] = \"Letters\";\n\
name[Group2] = \"Numbers/Symbols\";\n\
\n";
static const char *keymap_footer = "\
xkb_types \"squeekboard\" {\n\
\n\
type \"TWO_LEVEL\" {\n\
modifiers = Shift;\n\
map[Shift] = Level2;\n\
level_name[Level1] = \"Base\";\n\
level_name[Level2] = \"Shift\";\n\
};\n\
};\n\
\n\
xkb_compatibility \"squeekboard\" {\n\
};\n\
};";

View File

@ -6,3 +6,15 @@ enum_headers = [
enums = gnome.mkenums_simple('eek-enumtypes', sources: enum_headers)
python = find_program('python3')
gen_keysym_entries_xkeysym = generator(
python,
arguments: ['@CURRENT_SOURCE_DIR@/gen-keysym-entries.py', 'xkeysym_keysym_entries', '@INPUT@'],
capture: true,
output: 'eek-@BASENAME@.h',
)
keysym_entries = [
gen_keysym_entries_xkeysym.process('./xkeysym-keysym-entries.txt'),
]

File diff suppressed because it is too large Load Diff

View File

@ -29,7 +29,6 @@
#include <fcntl.h>
#include <stdio.h>
#define _XOPEN_SOURCE 500
#include <string.h>
#include <sys/mman.h>
#include <sys/random.h> // TODO: this is Linux-specific
@ -51,6 +50,7 @@ enum {
PROP_0, // Magic: without this, keyboard is not useable in g_object_notify
PROP_KEYBOARD,
PROP_VISIBLE,
PROP_FULLSCREEN,
PROP_LAST
};
@ -69,6 +69,7 @@ static guint signals[LAST_SIGNAL] = { 0, };
struct _EekboardContextServicePrivate {
gboolean enabled;
gboolean visible;
gboolean fullscreen;
LevelKeyboard *keyboard; // currently used keyboard
GHashTable *keyboard_hash; // a table of available keyboards, per layout
@ -88,17 +89,66 @@ static LevelKeyboard *
eekboard_context_service_real_create_keyboard (EekboardContextService *self,
const gchar *keyboard_type)
{
LevelKeyboard *keyboard = eek_xml_layout_real_create_keyboard(keyboard_type, self);
EekLayout *layout;
GError *error;
if (g_str_has_prefix (keyboard_type, "xkb:")) {
/* TODO: Depends on xklavier
XklConfigRec *rec =
eekboard_xkl_config_rec_from_string (&keyboard_type[4]);
if (display == NULL)
//display = XOpenDisplay (NULL);
return NULL; // FIXME: replace with wl display
error = NULL;
layout = eek_xkl_layout_new (display, &error);
if (layout == NULL) {
g_warning ("can't create keyboard %s: %s",
keyboard_type, error->message);
g_error_free (error);
return NULL;
}
if (!eek_xkl_layout_set_config (EEK_XKL_LAYOUT(layout), rec)) {
g_object_unref (layout);
return NULL;
}
*/
return NULL;
} else {
error = NULL;
layout = eek_xml_layout_new (keyboard_type, &error);
if (layout == NULL) {
g_warning ("can't create keyboard %s: %s",
keyboard_type, error->message);
g_error_free (error);
keyboard_type = "us";
error = NULL;
layout = eek_xml_layout_new (keyboard_type, &error);
if (layout == NULL) {
g_error ("failed to create fallback layout: %s", error->message);
g_error_free (error);
return NULL;
}
}
}
LevelKeyboard *keyboard = eek_xml_layout_real_create_keyboard(layout, self);
if (!keyboard) {
g_error("Failed to create a keyboard");
}
g_object_unref (layout);
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!context) {
g_error("No context created");
}
const gchar *keymap_str = squeek_layout_get_keymap(keyboard->layout);
gchar *keymap_str = eek_keyboard_get_keymap(keyboard);
int f = open("maprs.map", O_CREAT | O_WRONLY, 0600);
write(f, keymap_str, strlen(keymap_str));
close(f);
struct xkb_keymap *keymap = xkb_keymap_new_from_string(context, keymap_str,
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
@ -106,6 +156,8 @@ eekboard_context_service_real_create_keyboard (EekboardContextService *self,
if (!keymap)
g_error("Bad keymap:\n%s", keymap_str);
free(keymap_str);
xkb_context_unref(context);
keyboard->keymap = keymap;
@ -115,7 +167,7 @@ eekboard_context_service_real_create_keyboard (EekboardContextService *self,
g_autofree char *path = strdup("/eek_keymap-XXXXXX");
char *r = &path[strlen(path) - 6];
getrandom(r, 6, GRND_NONBLOCK);
for (unsigned i = 0; i < 6; i++) {
for (uint i = 0; i < 6; i++) {
r[i] = (r[i] & 0b1111111) | 0b1000000; // A-z
r[i] = r[i] > 'z' ? '?' : r[i]; // The randomizer doesn't need to be good...
}
@ -133,8 +185,9 @@ eekboard_context_service_real_create_keyboard (EekboardContextService *self,
if ((void*)ptr == (void*)-1) {
g_error("Failed to set up mmap");
}
strncpy(ptr, keymap_str, keyboard->keymap_len);
strcpy(ptr, keymap_str);
munmap(ptr, keyboard->keymap_len);
free(keymap_str);
return keyboard;
}
@ -168,6 +221,9 @@ eekboard_context_service_set_property (GObject *object,
case PROP_VISIBLE:
context->priv->visible = g_value_get_boolean (value);
break;
case PROP_FULLSCREEN:
context->priv->fullscreen = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -189,6 +245,9 @@ eekboard_context_service_get_property (GObject *object,
case PROP_VISIBLE:
g_value_set_boolean (value, context->priv->visible);
break;
case PROP_FULLSCREEN:
g_value_set_boolean (value, context->priv->fullscreen);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -393,6 +452,20 @@ eekboard_context_service_class_init (EekboardContextServiceClass *klass)
g_object_class_install_property (gobject_class,
PROP_VISIBLE,
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
@ -502,6 +575,19 @@ eekboard_context_service_get_keyboard (EekboardContextService *context)
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,
const LevelKeyboard *keyboard)
{

View File

@ -98,6 +98,8 @@ void eekboard_context_service_hide_keyboard
(EekboardContextService *context);
void eekboard_context_service_destroy (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,
const LevelKeyboard *keyboard);

654
eekboard/eekboard-context.c Normal file
View File

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

View File

@ -0,0 +1,94 @@
/*
* 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 */

View File

@ -90,9 +90,12 @@ send_fake_key (SeatEmitter *emitter,
gboolean pressed,
uint32_t timestamp)
{
zwp_virtual_keyboard_v1_modifiers(emitter->virtual_keyboard, 0, 0, 0, 0);
guint level = keyboard->level;
uint32_t group = (level / 2);
zwp_virtual_keyboard_v1_modifiers(emitter->virtual_keyboard, 0, 0, 0, group);
send_virtual_keyboard_key (emitter->virtual_keyboard, keycode - 8, (unsigned)pressed, timestamp);
zwp_virtual_keyboard_v1_modifiers(emitter->virtual_keyboard, 0, 0, 0, 0);
zwp_virtual_keyboard_v1_modifiers(emitter->virtual_keyboard, 0, 0, 0, group);
}
void

View File

@ -1,29 +0,0 @@
extern crate rs;
extern crate xkbcommon;
use std::env;
use rs::data::{ Layout, LoadError };
use xkbcommon::xkb;
fn check_layout(name: &str) {
let layout = Layout::from_resource(name)
.and_then(|layout| layout.build().map_err(LoadError::BadKeyMap))
.expect("layout broken");
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
xkb::Keymap::new_from_string(
&context,
layout.keymap_str
.clone()
.into_string().expect("Failed to decode keymap string"),
xkb::KEYMAP_FORMAT_TEXT_V1,
xkb::KEYMAP_COMPILE_NO_FLAGS,
).expect("Failed to create keymap");
}
fn main() -> () {
check_layout(env::args().nth(1).expect("No argument given").as_str());
}

View File

@ -1,9 +1,9 @@
project(
'squeekboard',
'c', 'rust',
version: '1.2.1',
version: '1.0.10',
license: 'GPLv3',
meson_version: '>=0.51.0',
meson_version: '>=0.49.0',
default_options: [
'warning_level=1',
'buildtype=debugoptimized',
@ -53,9 +53,6 @@ summary = [
]
message('\n'.join(summary))
cargo = find_program('cargo')
cargo_script = find_program('cargo.sh')
subdir('data')
subdir('protocols')
subdir('eek')

1350
src/bitflags.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,698 +0,0 @@
/**! The parsing of the data files for layouts */
use std::cell::RefCell;
use std::collections::{ HashMap, HashSet };
use std::env;
use std::ffi::CString;
use std::fmt;
use std::fs;
use std::io;
use std::path::PathBuf;
use std::rc::Rc;
use std::vec::Vec;
use xkbcommon::xkb;
use ::keyboard::{
KeyState,
generate_keymap, generate_keycodes, FormattingError
};
use ::resources;
use ::util::c::as_str;
use ::xdg;
// traits, derives
use std::io::BufReader;
use std::iter::FromIterator;
use serde::Deserialize;
/// Gathers stuff defined in C or called by C
pub mod c {
use super::*;
use std::os::raw::c_char;
#[no_mangle]
pub extern "C"
fn squeek_load_layout(name: *const c_char) -> *mut ::layout::Layout {
let name = as_str(&name)
.expect("Bad layout name")
.expect("Empty layout name");
let layout = load_layout_with_fallback(name);
Box::into_raw(Box::new(layout))
}
}
const FALLBACK_LAYOUT_NAME: &str = "us";
#[derive(Debug)]
pub enum LoadError {
BadData(Error),
MissingResource,
BadResource(serde_yaml::Error),
BadKeyMap(FormattingError),
}
impl fmt::Display for LoadError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::LoadError::*;
match self {
BadData(e) => write!(f, "Bad data: {}", e),
MissingResource => write!(f, "Missing resource"),
BadResource(e) => write!(f, "Bad resource: {}", e),
BadKeyMap(e) => write!(f, "Bad key map: {}", e),
}
}
}
#[derive(Debug, PartialEq)]
enum DataSource {
File(PathBuf),
Resource(String),
}
impl fmt::Display for DataSource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DataSource::File(path) => write!(f, "Path: {:?}", path.display()),
DataSource::Resource(name) => write!(f, "Resource: {}", name),
}
}
}
/// Tries to load the layout from the first place where it's present.
/// If the layout exists, but is broken, fallback is activated.
fn load_layout(
name: &str,
keyboards_path: Option<PathBuf>,
) -> (
Result<::layout::Layout, LoadError>, // last attempted
DataSource, // last attempt source
Option<(LoadError, DataSource)>, // first attempt source
) {
let path = keyboards_path.map(|path|
path.join(name).with_extension("yaml")
);
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(
name: &str
) -> ::layout::Layout {
let path = env::var_os("SQUEEKBOARD_KEYBOARDSDIR")
.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) => {
eprintln!(
"Failed to load layout from {}: {}, using fallback",
source, e
);
load_layout(FALLBACK_LAYOUT_NAME, path)
},
(res, source) => (res, source, None),
};
log_attempt_info(attempt);
match (layout, source) {
(Err(e), source) => {
panic!(
format!("Failed to load hardcoded layout from {}: {:?}",
source, e
)
);
},
(Ok(layout), source) => {
eprintln!("Loaded layout from {}", source);
layout
}
}
}
/// The root element describing an entire keyboard
#[derive(Debug, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct Layout {
row_spacing: f64,
button_spacing: f64,
bounds: Bounds,
views: HashMap<String, Vec<ButtonIds>>,
#[serde(default)]
buttons: HashMap<String, ButtonMeta>,
outlines: HashMap<String, Outline>
}
#[derive(Debug, Clone, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
struct Bounds {
x: f64,
y: f64,
width: f64,
height: f64,
}
/// Buttons are embedded in a single string
type ButtonIds = String;
#[derive(Debug, Default, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
struct ButtonMeta {
/// Action other than keysym (conflicts with keysym)
action: Option<Action>,
/// The name of the outline. If not present, will be "default"
outline: Option<String>,
/// FIXME: start using it
keysym: Option<String>,
/// If not present, will be derived from the button ID
label: Option<String>,
/// Conflicts with label
icon: Option<String>,
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
enum Action {
#[serde(rename="locking")]
Locking { lock_view: String, unlock_view: String },
#[serde(rename="set_view")]
SetView(String),
#[serde(rename="show_prefs")]
ShowPrefs,
}
#[derive(Debug, Clone, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
struct Outline {
bounds: Bounds,
}
/// Errors encountered loading the layout into yaml
#[derive(Debug)]
pub enum Error {
Yaml(serde_yaml::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 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Yaml(e) => write!(f, "YAML: {}", 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 {
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(
fs::OpenOptions::new()
.read(true)
.open(&path)?
);
serde_yaml::from_reader(infile).map_err(Error::Yaml)
}
pub fn build(self) -> Result<::layout::Layout, FormattingError> {
let button_names = self.views.values()
.flat_map(|rows| {
rows.iter()
.flat_map(|row| row.split_ascii_whitespace())
});
let button_names: HashSet<&str>
= HashSet::from_iter(button_names);
let keycodes = generate_keycodes(
button_names.iter()
.map(|name| *name)
.filter(|name| {
match self.buttons.get(*name) {
// buttons with defined action can't emit keysyms
// and so don't need keycodes
Some(ButtonMeta { action: Some(_), .. }) => false,
_ => true,
}
})
);
let button_states = button_names.iter().map(|name| {(
String::from(*name),
Rc::new(RefCell::new(KeyState {
pressed: false,
locked: false,
keycode: keycodes.get(*name).map(|k| *k),
symbol: create_symbol(
&self.buttons,
name,
self.views.keys().collect()
),
}))
)});
let button_states =
HashMap::<String, Rc<RefCell<KeyState>>>::from_iter(
button_states
);
// TODO: generate from symbols
let keymap_str = generate_keymap(&button_states)?;
let views = HashMap::from_iter(
self.views.iter().map(|(name, view)| {(
name.clone(),
Box::new(::layout::View {
bounds: ::layout::c::Bounds {
x: self.bounds.x,
y: self.bounds.y,
width: self.bounds.width,
height: self.bounds.height,
},
spacing: ::layout::Spacing {
row: self.row_spacing,
button: self.button_spacing,
},
rows: view.iter().map(|row| {
Box::new(::layout::Row {
angle: 0,
bounds: None,
buttons: row.split_ascii_whitespace().map(|name| {
Box::new(create_button(
&self.buttons,
&self.outlines,
name,
button_states.get(name.into())
.expect("Button state not created")
.clone()
))
}).collect(),
})
}).collect(),
})
)})
);
Ok(::layout::Layout {
current_view: "base".into(),
views: views,
keymap_str: {
CString::new(keymap_str)
.expect("Invalid keymap string generated")
},
})
}
}
fn create_symbol(
button_info: &HashMap<String, ButtonMeta>,
name: &str,
view_names: Vec<&String>,
) -> ::symbol::Symbol {
let default_meta = ButtonMeta::default();
let symbol_meta = button_info.get(name)
.unwrap_or(&default_meta);
fn filter_view_name(
button_name: &str,
view_name: String,
view_names: &Vec<&String>
) -> String {
if view_names.contains(&&view_name) {
view_name
} else {
eprintln!(
"Button {} switches to missing view {}",
button_name,
view_name
);
"base".into()
}
}
fn keysym_valid(name: &str) -> bool {
xkb::keysym_from_name(name, xkb::KEYSYM_NO_FLAGS) != xkb::KEY_NoSymbol
}
let keysym = match &symbol_meta.action {
Some(_) => None,
None => Some(match &symbol_meta.keysym {
Some(keysym) => match keysym_valid(keysym.as_str()) {
true => keysym.clone(),
false => {
eprintln!("Keysym name invalid: {}", keysym);
"space".into() // placeholder
},
},
None => match keysym_valid(name) {
true => String::from(name),
false => match name.chars().count() {
1 => format!("U{:04X}", name.chars().next().unwrap() as u32),
// If the name is longer than 1 char,
// then it's not a single Unicode char,
// but was trying to be an identifier
_ => {
eprintln!(
"Could not derive a valid keysym for key {}",
name
);
"space".into() // placeholder
}
},
},
}),
};
match &symbol_meta.action {
Some(Action::SetView(view_name)) => ::symbol::Symbol {
action: ::symbol::Action::SetLevel(
filter_view_name(name, view_name.clone(), &view_names)
),
},
Some(Action::Locking { lock_view, unlock_view }) => ::symbol::Symbol {
action: ::symbol::Action::LockLevel {
lock: filter_view_name(name, lock_view.clone(), &view_names),
unlock: filter_view_name(
name,
unlock_view.clone(),
&view_names
),
},
},
Some(Action::ShowPrefs) => ::symbol::Symbol {
action: ::symbol::Action::Submit {
text: None,
keys: Vec::new(),
},
},
None => ::symbol::Symbol {
action: ::symbol::Action::Submit {
text: None,
keys: vec!(
::symbol::KeySym(keysym.unwrap()),
),
},
},
}
}
/// TODO: Since this will receive user-provided data,
/// all .expect() on them should be turned into soft fails
fn create_button(
button_info: &HashMap<String, ButtonMeta>,
outlines: &HashMap<String, Outline>,
name: &str,
state: Rc<RefCell<KeyState>>,
) -> ::layout::Button {
let cname = CString::new(name.clone())
.expect("Bad name");
// don't remove, because multiple buttons with the same name are allowed
let default_meta = ButtonMeta::default();
let button_meta = button_info.get(name)
.unwrap_or(&default_meta);
// TODO: move conversion to the C/Rust boundary
let label = if let Some(label) = &button_meta.label {
::layout::Label::Text(CString::new(label.as_str())
.expect("Bad label"))
} else if let Some(icon) = &button_meta.icon {
::layout::Label::IconName(CString::new(icon.as_str())
.expect("Bad icon"))
} else {
::layout::Label::Text(cname.clone())
};
let outline_name = match &button_meta.outline {
Some(outline) => {
if outlines.contains_key(outline) {
outline.clone()
} else {
eprintln!("Outline named {} does not exist! Using default for button {}", outline, name);
"default".into()
}
}
None => "default".into(),
};
let outline = outlines.get(&outline_name)
.map(|outline| (*outline).clone())
.unwrap_or_else(|| {
eprintln!("No default outline defied Using 1x1!");
Outline {
bounds: Bounds { x: 0f64, y: 0f64, width: 1f64, height: 1f64 },
}
});
::layout::Button {
name: cname,
outline_name: CString::new(outline_name).expect("Bad outline"),
// TODO: do layout before creating buttons
bounds: ::layout::c::Bounds {
x: outline.bounds.x,
y: outline.bounds.y,
width: outline.bounds.width,
height: outline.bounds.height,
},
label: label,
state: state,
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error as ErrorTrait;
#[test]
fn test_parse_path() {
assert_eq!(
Layout::from_file(PathBuf::from("tests/layout.yaml")).unwrap(),
Layout {
row_spacing: 0f64,
button_spacing: 0f64,
bounds: Bounds { x: 0f64, y: 0f64, width: 0f64, height: 0f64 },
views: hashmap!(
"base".into() => vec!("test".into()),
),
buttons: hashmap!{
"test".into() => ButtonMeta {
icon: None,
keysym: None,
action: None,
label: Some("test".into()),
outline: None,
}
},
outlines: hashmap!{
"default".into() => Outline {
bounds: Bounds {
x: 0f64, y: 0f64, width: 0f64, height: 0f64
},
}
},
}
);
}
/// Check if the default protection works
#[test]
fn test_empty_views() {
let out = Layout::from_file(PathBuf::from("tests/layout2.yaml"));
match out {
Ok(_) => assert!(false, "Data mistakenly accepted"),
Err(e) => {
let mut handled = false;
if let Error::Yaml(ye) = &e {
handled = ye.description() == "missing field `views`";
};
if !handled {
println!("Unexpected error {:?}", e);
assert!(false)
}
}
}
}
#[test]
fn test_extra_field() {
let out = Layout::from_file(PathBuf::from("tests/layout3.yaml"));
match out {
Ok(_) => assert!(false, "Data mistakenly accepted"),
Err(e) => {
let mut handled = false;
if let Error::Yaml(ye) = &e {
handled = ye.description()
.starts_with("unknown field `bad_field`");
};
if !handled {
println!("Unexpected error {:?}", e);
assert!(false)
}
}
}
}
#[test]
fn test_layout_punctuation() {
let out = Layout::from_file(PathBuf::from("tests/layout_key1.yaml"))
.unwrap()
.build()
.unwrap();
assert_eq!(
out.views["base"]
.rows[0]
.buttons[0]
.label,
::layout::Label::Text(CString::new("test").unwrap())
);
}
#[test]
fn test_layout_unicode() {
let out = Layout::from_file(PathBuf::from("tests/layout_key2.yaml"))
.unwrap()
.build()
.unwrap();
assert_eq!(
out.views["base"]
.rows[0]
.buttons[0]
.label,
::layout::Label::Text(CString::new("test").unwrap())
);
}
#[test]
fn parsing_fallback() {
assert!(Layout::from_resource(FALLBACK_LAYOUT_NAME)
.and_then(|layout| layout.build().map_err(LoadError::BadKeyMap))
.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]
fn unicode_keysym() {
let keysym = xkb::keysym_from_name(
format!("U{:X}", "å".chars().next().unwrap() as u32).as_str(),
xkb::KEYSYM_NO_FLAGS,
);
let keysym = xkb::keysym_to_utf8(keysym);
assert_eq!(keysym, "å\0");
}
#[test]
fn test_key_unicode() {
assert_eq!(
create_symbol(
&hashmap!{
".".into() => ButtonMeta {
icon: None,
keysym: None,
action: None,
label: Some("test".into()),
outline: None,
}
},
".",
Vec::new()
),
::symbol::Symbol {
action: ::symbol::Action::Submit {
text: None,
keys: vec!(::symbol::KeySym("U002E".into())),
},
}
);
}
}

View File

@ -5,10 +5,6 @@
/* Adapted from https://github.com/notriddle/rust-float-ord revision e995165f
* maintained by Michael Howell <michael@notriddle.com>
* licensed under MIT / Apache-2.0 licenses
*
* This version drops any dependency on rand.
* Caution: Don't pull the version from crates.io
* before making sure rand is optional.
*/
extern crate core;
@ -71,7 +67,6 @@ float_ord_impl!(f64, u64, 64);
/// # Example
///
/// ```
/// use rs::float_ord;
/// let mut v = [-5.0, 4.0, 1.0, -3.0, 2.0];
///
/// float_ord::sort(&mut v);

View File

@ -3,7 +3,7 @@ use std::ffi::CString;
use std::num::Wrapping;
use std::string::String;
use ::util::c::into_cstring;
use super::bitflags;
// Traits
use std::convert::TryFrom;
@ -13,8 +13,15 @@ use std::convert::TryFrom;
pub mod c {
use super::*;
use std::ffi::CStr;
use std::os::raw::{c_char, c_void};
fn into_cstring(s: *const c_char) -> Result<CString, std::ffi::NulError> {
CString::new(
unsafe {CStr::from_ptr(s)}.to_bytes()
)
}
// The following defined in C
/// struct zwp_input_method_v2*
@ -84,9 +91,7 @@ pub mod c {
{
let imservice = check_imservice(imservice, im).unwrap();
imservice.pending = IMProtocolState {
surrounding_text: into_cstring(text)
.expect("Received invalid string")
.expect("Received null string"),
surrounding_text: into_cstring(text).expect("Received invalid string"),
surrounding_cursor: cursor,
..imservice.pending.clone()
};
@ -225,8 +230,7 @@ bitflags!{
/// Map to `text_input_unstable_v3.content_purpose` values
///
/// ```
/// use rs::imservice::ContentPurpose;
/// assert_eq!(ContentPurpose::Alpha as u32, 1);
/// assert_eq!(ContentPurpose::Alpha as u32, 0);
/// ```
#[derive(Debug, Clone)]
pub enum ContentPurpose {

View File

@ -1,5 +1,5 @@
#ifndef __KEYBOARD_H
#define __KEYBOARD_H
#define __KYBOARD_H
#include "stdbool.h"
#include "inttypes.h"
@ -19,7 +19,6 @@ uint32_t squeek_key_is_locked(struct squeek_key *key);
void squeek_key_set_locked(struct squeek_key *key, uint32_t pressed);
uint32_t squeek_key_get_keycode(struct squeek_key *key);
void squeek_key_set_keycode(struct squeek_key *key, uint32_t keycode);
uint32_t squeek_key_equal(struct squeek_key* key, struct squeek_key* key1);
struct squeek_symbol *squeek_key_get_symbol(struct squeek_key* key);
const char* squeek_key_to_keymap_entry(const char *key_name, struct squeek_key *key);

View File

@ -1,41 +1,90 @@
/*! State of the emulated keyboard and keys */
use std::vec::Vec;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
use std::io;
use std::rc::Rc;
use std::string::FromUtf8Error;
use ::symbol::{ Symbol, Action };
use std::io::Write;
use std::iter::{ FromIterator, IntoIterator };
use super::symbol;
/// Gathers stuff defined in C or called by C
pub mod c {
use super::*;
use ::util::c;
use ::util::c::as_cstr;
use ::util::c::{ as_cstr, into_cstring };
use std::cell::RefCell;
use std::ffi::CString;
use std::os::raw::c_char;
use std::ptr;
use std::rc::Rc;
pub type CKeyState = c::Wrapped<KeyState>;
// traits
use std::borrow::ToOwned;
// The following defined in C
#[no_mangle]
extern "C" {
fn eek_keysym_from_name(name: *const c_char) -> u32;
}
/// The wrapped structure for KeyState suitable for handling in C
/// Since C doesn't respect borrowing rules,
/// RefCell will enforce them dynamically (only 1 writer/many readers)
/// Rc is implied and will ensure timely dropping
#[repr(transparent)]
pub struct CKeyState(*const RefCell<KeyState>);
impl Clone for CKeyState {
fn clone(&self) -> Self {
CKeyState(self.0.clone())
}
}
impl CKeyState {
pub fn wrap(state: Rc<RefCell<KeyState>>) -> CKeyState {
CKeyState(Rc::into_raw(state))
}
pub fn unwrap(self) -> Rc<RefCell<KeyState>> {
unsafe { Rc::from_raw(self.0) }
}
fn to_owned(self) -> KeyState {
let rc = self.unwrap();
let state = rc.borrow().to_owned();
Rc::into_raw(rc); // Prevent dropping
state
}
fn borrow_mut<F, T>(self, f: F) -> T where F: FnOnce(&mut KeyState) -> T {
let rc = self.unwrap();
let ret = {
let mut state = rc.borrow_mut();
f(&mut state)
};
Rc::into_raw(rc); // Prevent dropping
ret
}
}
// TODO: unwrapping
// The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
// TODO: this will receive data from the filesystem,
// so it should handle garbled strings in the future
#[no_mangle]
pub extern "C"
fn squeek_key_new(keycode: u32) -> CKeyState {
let state: Rc<RefCell<KeyState>> = Rc::new(RefCell::new(
KeyState {
pressed: false,
locked: false,
keycode: keycode,
symbol: None,
}
));
CKeyState::wrap(state)
}
#[no_mangle]
pub extern "C"
fn squeek_key_free(key: CKeyState) {
unsafe { key.unwrap() }; // reference dropped
}
/// Compares pointers to the data
#[no_mangle]
pub extern "C"
fn squeek_key_equal(key: CKeyState, key2: CKeyState) -> u32 {
return Rc::ptr_eq(&key.clone_ref(), &key2.clone_ref()) as u32
key.unwrap(); // reference dropped
}
#[no_mangle]
@ -48,9 +97,7 @@ pub mod c {
#[no_mangle]
pub extern "C"
fn squeek_key_set_pressed(key: CKeyState, pressed: u32) {
let key = key.clone_ref();
let mut key = key.borrow_mut();
key.pressed = pressed != 0;
key.borrow_mut(|key| key.pressed = pressed != 0);
}
#[no_mangle]
@ -62,15 +109,109 @@ pub mod c {
#[no_mangle]
pub extern "C"
fn squeek_key_set_locked(key: CKeyState, locked: u32) {
let key = key.clone_ref();
let mut key = key.borrow_mut();
key.locked = locked != 0;
key.borrow_mut(|key| key.locked = locked != 0);
}
#[no_mangle]
pub extern "C"
fn squeek_key_get_keycode(key: CKeyState) -> u32 {
return key.to_owned().keycode.unwrap_or(0u32);
return key.to_owned().keycode as u32;
}
#[no_mangle]
pub extern "C"
fn squeek_key_set_keycode(key: CKeyState, code: u32) {
key.borrow_mut(|key| key.keycode = code);
}
// TODO: this will receive data from the filesystem,
// so it should handle garbled strings in the future
#[no_mangle]
pub extern "C"
fn squeek_key_add_symbol(
key: CKeyState,
element: *const c_char,
text_raw: *const c_char, keyval: u32,
label: *const c_char, icon: *const c_char,
tooltip: *const c_char,
) {
let element = as_cstr(&element)
.expect("Missing element name");
let text = into_cstring(text_raw)
.unwrap_or_else(|e| {
eprintln!("Text unreadable: {}", e);
None
})
.and_then(|text| {
if text.as_bytes() == b"" {
None
} else {
Some(text)
}
});
let icon = into_cstring(icon)
.unwrap_or_else(|e| {
eprintln!("Icon name unreadable: {}", e);
None
});
use symbol::*;
// Only read label if there's no icon
let label = match icon {
Some(icon) => Label::IconName(icon),
None => Label::Text(
into_cstring(label)
.unwrap_or_else(|e| {
eprintln!("Label unreadable: {}", e);
Some(CString::new(" ").unwrap())
})
.unwrap_or_else(|| {
eprintln!("Label missing");
CString::new(" ").unwrap()
})
),
};
let tooltip = into_cstring(tooltip)
.unwrap_or_else(|e| {
eprintln!("Tooltip unreadable: {}", e);
None
});
key.borrow_mut(|key| {
if let Some(_) = key.symbol {
eprintln!("Key {:?} already has a symbol defined", text);
return;
}
key.symbol = Some(match element.to_bytes() {
b"symbol" => Symbol {
action: Action::Submit {
text: text,
keys: Vec::new(),
},
label: label,
tooltip: tooltip,
},
_ => panic!("unsupported element type {:?}", element),
});
});
}
#[no_mangle]
pub extern "C"
fn squeek_key_get_symbol(key: CKeyState) -> *const symbol::Symbol {
key.borrow_mut(|key| {
match key.symbol {
// This pointer stays after the function exits,
// so it must reference borrowed data and not any copy
Some(ref symbol) => symbol as *const symbol::Symbol,
None => ptr::null(),
}
})
}
#[no_mangle]
@ -84,14 +225,20 @@ pub mod c {
.to_str()
.expect("Bad key name");
let symbol_name = match key.to_owned().symbol.action {
Action::Submit { text: Some(text), .. } => {
Some(
text.clone()
.into_string().expect("Bad symbol")
)
let symbol_name = match key.to_owned().symbol {
Some(ref symbol) => match &symbol.action {
symbol::Action::Submit { text: Some(text), .. } => {
Some(
text.clone()
.into_string().expect("Bad symbol")
)
},
_ => None
},
None => {
eprintln!("Key {} has no symbol", key_name);
None
},
_ => None,
};
let inner = match symbol_name {
@ -104,7 +251,7 @@ pub mod c {
.into_raw()
}
#[no_mangle]
#[no_mangle]
pub extern "C"
fn squeek_key_get_action_name(
key_name: *const c_char,
@ -115,14 +262,20 @@ pub mod c {
.to_str()
.expect("Bad key name");
let symbol_name = match key.to_owned().symbol.action {
Action::Submit { text: Some(text), .. } => {
Some(
text.clone()
.into_string().expect("Bad symbol text")
)
let symbol_name = match key.to_owned().symbol {
Some(ref symbol) => match &symbol.action {
symbol::Action::Submit { text: Some(text), .. } => {
Some(
text.clone()
.into_string().expect("Bad symbol")
)
},
_ => None
},
None => {
eprintln!("Key {} has no symbol", key_name);
None
},
_ => None
};
let inner = match symbol_name {
@ -134,126 +287,14 @@ pub mod c {
.expect("Couldn't convert string")
.into_raw()
}
}
#[derive(Debug, Clone)]
pub struct KeyState {
pub pressed: bool,
pub locked: bool,
pub keycode: Option<u32>,
pub symbol: Symbol,
}
/// Generates a mapping where each key gets a keycode, starting from 8
pub fn generate_keycodes<'a, C: IntoIterator<Item=&'a str>>(
key_names: C
) -> HashMap<String, u32> {
HashMap::from_iter(
key_names.into_iter()
.map(|name| String::from(name))
.zip(8..)
)
}
#[derive(Debug)]
pub enum FormattingError {
Utf(FromUtf8Error),
Format(io::Error),
}
impl fmt::Display for FormattingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FormattingError::Utf(e) => write!(f, "UTF: {}", e),
FormattingError::Format(e) => write!(f, "Format: {}", e),
}
}
}
impl From<io::Error> for FormattingError {
fn from(e: io::Error) -> Self {
FormattingError::Format(e)
}
}
/// Generates a de-facto single level keymap. TODO: actually drop second level
pub fn generate_keymap(
keystates: &HashMap::<String, Rc<RefCell<KeyState>>>
) -> Result<String, FormattingError> {
let mut buf: Vec<u8> = Vec::new();
writeln!(
buf,
"xkb_keymap {{
xkb_keycodes \"squeekboard\" {{
minimum = 8;
maximum = 255;"
)?;
for (name, state) in keystates.iter() {
let state = state.borrow();
if let ::symbol::Action::Submit { text: _, keys } = &state.symbol.action {
match keys.len() {
0 => eprintln!("Key {} has no keysyms", name),
a => {
// TODO: don't ignore any keysyms
if a > 1 {
eprintln!("Key {} multiple keysyms", name);
}
write!(
buf,
"
<{}> = {};",
keys[0].0,
state.keycode.unwrap()
)?;
},
};
}
}
writeln!(
buf,
"
}};
xkb_symbols \"squeekboard\" {{
name[Group1] = \"Letters\";
name[Group2] = \"Numbers/Symbols\";"
)?;
for (name, state) in keystates.iter() {
if let ::symbol::Action::Submit { text: _, keys } = &state.borrow().symbol.action {
if let Some(keysym) = keys.iter().next() {
write!(
buf,
"
key <{}> {{ [ {0} ] }};",
keysym.0,
)?;
}
}
}
writeln!(
buf,
"
}};
xkb_types \"squeekboard\" {{
type \"TWO_LEVEL\" {{
modifiers = Shift;
map[Shift] = Level2;
level_name[Level1] = \"Base\";
level_name[Level2] = \"Shift\";
}};
}};
xkb_compatibility \"squeekboard\" {{
}};
}};"
)?;
String::from_utf8(buf).map_err(FormattingError::Utf)
pub keycode: u32,
// TODO: remove the optionality of a symbol
pub symbol: Option<symbol::Symbol>,
}

View File

@ -9,11 +9,17 @@
struct squeek_button;
struct squeek_row;
struct squeek_view;
struct squeek_layout;
struct squeek_row *squeek_row_new(int32_t angle);
struct squeek_button *squeek_row_create_button (struct squeek_row *row,
guint keycode, guint oref);
struct squeek_button *squeek_row_create_button_with_state(struct squeek_row *row,
struct squeek_button *source);
void squeek_row_set_angle(struct squeek_row *row, int32_t angle);
int32_t squeek_row_get_angle(const struct squeek_row*);
EekBounds squeek_row_get_bounds(const struct squeek_row*);
void squeek_row_set_bounds(struct squeek_row* row, EekBounds bounds);
uint32_t squeek_row_contains(struct squeek_row*, struct squeek_button *button);
@ -27,20 +33,34 @@ void squeek_row_foreach(struct squeek_row*,
void squeek_row_free(struct squeek_row*);
/*
struct squeek_button *squeek_buttons_find_by_position(
const struct squeek_buttons *buttons,
double x, double y,
double origin_x, double origin_y,
double angle);
void squeek_buttons_add(struct squeek_buttons*, const struct squeek_button* button);
void squeek_buttons_remove_key(struct squeek_buttons*, const struct squeek_key* key);
*/
struct squeek_button *squeek_button_new(uint32_t keycode, uint32_t oref);
struct squeek_button *squeek_button_new_with_state(const struct squeek_button* source);
uint32_t squeek_button_get_oref(const struct squeek_button*);
EekBounds squeek_button_get_bounds(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_name(const struct squeek_button*);
const char *squeek_button_get_outline_name(const struct squeek_button*);
void squeek_button_set_bounds(struct squeek_button* button, EekBounds bounds);
struct squeek_symbol *squeek_button_get_symbol (
const struct squeek_button *button);
struct squeek_key *squeek_button_get_key(const struct squeek_button*);
uint32_t *squeek_button_has_key(const struct squeek_button* button,
const struct squeek_key *key);
void squeek_button_print(const struct squeek_button* button);
struct squeek_view *squeek_view_new(EekBounds bounds);
struct squeek_row *squeek_view_create_row(struct squeek_view *, int32_t angle);
EekBounds squeek_view_get_bounds(const struct squeek_view*);
void squeek_view_set_bounds(const struct squeek_view*, EekBounds bounds);
typedef void (*RowCallback) (struct squeek_row *row, gpointer user_data);
void squeek_view_foreach(struct squeek_view*,
@ -50,16 +70,9 @@ void squeek_view_foreach(struct squeek_view*,
struct squeek_row *squeek_view_get_row(struct squeek_view *view,
struct squeek_button *button);
struct squeek_button *squeek_view_find_button_by_position(struct squeek_view *view, EekPoint point);
void
squeek_layout_place_contents(struct squeek_layout*);
struct squeek_view *squeek_layout_get_current_view(struct squeek_layout*);
void squeek_layout_set_state_from_press(struct squeek_layout* layout, LevelKeyboard *keyboard, struct squeek_key* key);
squeek_view_place_contents(struct squeek_view *view, LevelKeyboard *keyboard);
struct squeek_layout *squeek_load_layout(const char *type);
const char *squeek_layout_get_keymap(const struct squeek_layout*);
void squeek_layout_free(struct squeek_layout*);
struct squeek_button *squeek_view_find_button_by_position(struct squeek_view *view, EekPoint point);
#endif

View File

@ -1,4 +1,4 @@
/*!
/**
* Layout-related data.
*
* The `View` contains `Row`s and each `Row` contains `Button`s.
@ -18,8 +18,6 @@
*/
use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::CString;
use std::rc::Rc;
use std::vec::Vec;
@ -31,14 +29,18 @@ use ::symbol::*;
pub mod c {
use super::*;
use std::ffi::CStr;
use std::os::raw::{ c_char, c_void };
use std::os::raw::c_void;
use std::ptr;
// The following defined in C
#[repr(transparent)]
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
#[repr(C)]
@ -58,19 +60,58 @@ pub mod c {
pub height: f64
}
impl Bounds {
pub fn zero() -> Bounds {
Bounds { x: 0f64, y: 0f64, width: 0f64, height: 0f64 }
}
}
type ButtonCallback = unsafe extern "C" fn(button: *mut ::layout::Button, data: *mut UserData);
type RowCallback = unsafe extern "C" fn(row: *mut ::layout::Row, data: *mut UserData);
// The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
#[no_mangle]
pub extern "C"
fn squeek_view_new(bounds: Bounds) -> *mut ::layout::View {
Box::into_raw(Box::new(::layout::View {
rows: Vec::new(),
bounds: bounds,
}))
}
#[no_mangle]
pub extern "C"
fn squeek_view_get_bounds(view: *const ::layout::View) -> Bounds {
unsafe { &*view }.bounds.clone()
}
#[no_mangle]
pub extern "C"
fn squeek_view_set_bounds(view: *mut ::layout::View, bounds: Bounds) {
unsafe { &mut *view }.bounds = bounds;
}
/// Places a row into the view and returns a reference to it
#[no_mangle]
pub extern "C"
fn squeek_view_create_row(
view: *mut ::layout::View,
angle: i32,
) -> *mut ::layout::Row {
let view = unsafe { &mut *view };
view.rows.push(Box::new(::layout::Row::new(angle)));
// Return the reference directly instead of a Box, it's not on the stack
// It will live as long as the Vec
let last_idx = view.rows.len() - 1;
// Caution: Box can't be returned directly,
// so returning a reference to its innards
view.rows[last_idx].as_mut() as *mut ::layout::Row
}
#[no_mangle]
pub extern "C"
fn squeek_view_foreach(
view: *mut ::layout::View,
callback: RowCallback,
@ -82,6 +123,68 @@ pub mod c {
unsafe { callback(row, data) };
}
}
#[no_mangle]
pub extern "C"
fn squeek_row_new(angle: i32) -> *mut ::layout::Row {
Box::into_raw(Box::new(::layout::Row::new(angle)))
}
/// Places a button into the row and returns a reference to it
#[no_mangle]
pub extern "C"
fn squeek_row_create_button(
row: *mut ::layout::Row,
keycode: u32, oref: u32
) -> *mut ::layout::Button {
let row = unsafe { &mut *row };
let state: Rc<RefCell<::keyboard::KeyState>> = Rc::new(RefCell::new(
::keyboard::KeyState {
pressed: false,
locked: false,
keycode: keycode,
symbol: None,
}
));
row.buttons.push(Box::new(::layout::Button {
oref: OutlineRef(oref),
bounds: None,
state: state,
}));
// Return the reference directly instead of a Box, it's not on the stack
// It will live as long as the Vec
let last_idx = row.buttons.len() - 1;
// Caution: Box can't be returned directly,
// so returning a reference to its innards
row.buttons[last_idx].as_mut() as *mut ::layout::Button
}
/// Places a button into the row, copying its state,
/// and returns a reference to it
#[no_mangle]
pub extern "C"
fn squeek_row_create_button_with_state(
row: *mut ::layout::Row,
button: *const ::layout::Button,
) -> *mut ::layout::Button {
let row = unsafe { &mut *row };
let source = unsafe { &*button };
row.buttons.push(Box::new(source.clone()));
// Return the reference directly instead of a Box, it's not on the stack
// It will live as long as the Vec
let last_idx = row.buttons.len() - 1;
// Caution: Box can't be returned directly,
// so returning a reference to its innards directly
row.buttons[last_idx].as_mut() as *mut ::layout::Button
}
#[no_mangle]
pub extern "C"
fn squeek_row_set_angle(row: *mut ::layout::Row, angle: i32) {
let row = unsafe { &mut *row };
row.angle = angle;
}
#[no_mangle]
pub extern "C"
@ -130,11 +233,57 @@ pub mod c {
#[no_mangle]
pub extern "C"
fn squeek_button_get_bounds(button: *const ::layout::Button) -> Bounds {
let button = unsafe { &*button };
button.bounds.clone()
fn squeek_button_new(keycode: u32, oref: u32) -> *mut ::layout::Button {
let state: Rc<RefCell<::keyboard::KeyState>> = Rc::new(RefCell::new(
::keyboard::KeyState {
pressed: false,
locked: false,
keycode: keycode,
symbol: None,
}
));
Box::into_raw(Box::new(::layout::Button {
oref: OutlineRef(oref),
bounds: None,
state: state,
}))
}
#[no_mangle]
pub extern "C"
fn squeek_button_new_with_state(source: *mut ::layout::Button) -> *mut ::layout::Button {
let source = unsafe { &*source };
let button = Box::new(source.clone());
Box::into_raw(button)
}
#[no_mangle]
pub extern "C"
fn squeek_button_get_oref(button: *const ::layout::Button) -> u32 {
let button = unsafe { &*button };
button.oref.0
}
// Bounds transparently mapped to C, therefore no pointer needed
#[no_mangle]
pub extern "C"
fn squeek_button_get_bounds(button: *const ::layout::Button) -> Bounds {
let button = unsafe { &*button };
match &button.bounds {
Some(bounds) => bounds.clone(),
None => panic!("Button doesn't have any bounds yet"),
}
}
/// Set bounds by consuming the value
#[no_mangle]
pub extern "C"
fn squeek_button_set_bounds(button: *mut ::layout::Button, bounds: Bounds) {
let button = unsafe { &mut *button };
button.bounds = Some(bounds);
}
/// Borrow a new reference to key state. Doesn't need freeing
#[no_mangle]
pub extern "C"
@ -153,50 +302,12 @@ pub mod c {
) -> *const Symbol {
let button = unsafe { &*button };
let state = button.state.borrow();
&state.symbol as *const Symbol
}
#[no_mangle]
pub extern "C"
fn squeek_button_get_label(
button: *const ::layout::Button
) -> *const c_char {
let button = unsafe { &*button };
match &button.label {
Label::Text(text) => text.as_ptr(),
// returning static strings to C is a bit cumbersome
Label::IconName(_) => unsafe {
// CStr doesn't allocate anything, so it only points to
// the 'static str, avoiding a memory leak
CStr::from_bytes_with_nul_unchecked(b"icon\0")
}.as_ptr(),
match state.symbol {
Some(ref symbol) => symbol as *const Symbol,
None => ptr::null(),
}
}
#[no_mangle]
pub extern "C"
fn squeek_button_get_icon_name(button: *const Button) -> *const c_char {
let button = unsafe { &*button };
match &button.label {
Label::Text(_) => ptr::null(),
Label::IconName(name) => name.as_ptr(),
}
}
#[no_mangle]
pub extern "C"
fn squeek_button_get_name(button: *const Button) -> *const c_char {
let button = unsafe { &*button };
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]
pub extern "C"
fn squeek_button_has_key(
@ -204,8 +315,9 @@ pub mod c {
state: ::keyboard::c::CKeyState,
) -> u32 {
let button = unsafe { &*button };
let state = state.clone_ref();
let state = state.unwrap();
let equal = Rc::ptr_eq(&button.state, &state);
Rc::into_raw(state); // Prevent dropping
equal as u32
}
@ -216,46 +328,27 @@ pub mod c {
println!("{:?}", button);
}
#[no_mangle]
pub extern "C"
fn squeek_layout_get_current_view(layout: *const Layout) -> *const View {
let layout = unsafe { &*layout };
let view_name = layout.current_view.clone();
layout.views.get(&view_name)
.expect("Current view doesn't exist")
.as_ref() as *const View
}
#[no_mangle]
pub extern "C"
fn squeek_layout_get_keymap(layout: *const Layout) -> *const c_char {
let layout = unsafe { &*layout };
layout.keymap_str.as_ptr()
}
#[no_mangle]
pub extern "C"
fn squeek_layout_free(layout: *mut Layout) {
unsafe { Box::from_raw(layout) };
}
/// Entry points for more complex procedures and algoithms which span multiple modules
pub mod procedures {
use super::*;
#[repr(transparent)]
pub struct LevelKeyboard(*const c_void);
#[repr(C)]
#[derive(PartialEq, Debug)]
pub struct ButtonPlace {
row: *const Row,
button: *const Button,
}
#[repr(transparent)]
pub struct LevelKeyboard(*const c_void);
#[no_mangle]
extern "C" {
fn eek_get_outline_size(
keyboard: *const LevelKeyboard,
outline: u32
) -> Bounds;
/// Checks if point falls within bounds,
/// which are relative to origin and rotated by angle (I think)
pub fn eek_are_bounds_inside (bounds: Bounds,
@ -263,53 +356,15 @@ pub mod c {
origin: Point,
angle: i32
) -> u32;
pub fn eek_keyboard_set_key_locked(
keyboard: *mut LevelKeyboard,
key: ::keyboard::c::CKeyState,
);
}
#[no_mangle]
pub extern "C"
fn squeek_layout_set_state_from_press(
layout: *mut Layout,
keyboard: *mut LevelKeyboard,
key: ::keyboard::c::CKeyState,
) {
let layout = unsafe { &mut *layout };
let view_name = match key.to_owned().symbol.action {
::symbol::Action::SetLevel(name) => {
Some(name.clone())
},
::symbol::Action::LockLevel { lock, unlock } => {
let locked = {
let key = key.clone_ref();
let mut key = key.borrow_mut();
key.locked ^= true;
key.locked
};
if locked {
unsafe {
eek_keyboard_set_key_locked(
keyboard,
key
)
}
}
Some(if locked { lock } else { unlock }.clone())
},
_ => None,
};
if let Some(view_name) = view_name {
if let Err(_e) = layout.set_view(view_name.clone()) {
eprintln!("No such view: {}, ignoring switch", view_name)
};
};
fn squeek_buttons_get_outlines(
buttons: &Vec<Box<Button>>,
keyboard: *const LevelKeyboard,
) -> Vec<Bounds> {
buttons.iter().map(|button| {
unsafe { eek_get_outline_size(keyboard, button.oref.0) }
}).collect()
}
/// Places each button in order, starting from 0 on the left,
@ -320,19 +375,17 @@ pub mod c {
/// Sets button and row sizes according to their contents.
#[no_mangle]
pub extern "C"
fn squeek_layout_place_contents(
layout: *mut Layout,
fn squeek_view_place_contents(
view: *mut ::layout::View,
keyboard: *const LevelKeyboard, // source of outlines
) {
let layout = unsafe { &mut *layout };
for view in layout.views.values_mut() {
let sizes: Vec<Vec<Bounds>> = view.rows.iter().map(|row| {
row.buttons.iter()
.map(|button| button.bounds.clone())
.collect()
}).collect();
let spacing = view.spacing.clone();
view.place_buttons_with_sizes(sizes, spacing);
}
let view = unsafe { &mut *view };
let sizes: Vec<Vec<Bounds>> = view.rows.iter().map(|row|
squeek_buttons_get_outlines(&row.buttons, keyboard)
).collect();
view.place_buttons_with_sizes(sizes);
}
fn squeek_row_contains(row: &Row, needle: *const Button) -> bool {
@ -365,10 +418,13 @@ pub mod c {
needle: ::keyboard::c::CKeyState,
) -> ButtonPlace {
let view = unsafe { &*view };
let state = needle.clone_ref();
let state = needle.unwrap();
let paths = ::layout::procedures::find_key_paths(view, &state);
// Iterators used up, can turn the reference back into pointer
Rc::into_raw(state);
// Can only return 1 entry back to C
let (row, button) = match paths.get(0) {
Some((row, button)) => (
@ -398,36 +454,35 @@ pub mod c {
mod test {
use super::*;
use super::super::test::*;
#[test]
fn row_has_button() {
let state = make_state();
let button = make_button_with_state(
"test".into(),
state.clone()
);
let button_ptr = button_as_raw(&button);
let mut row = Row::new(0);
row.buttons.push(button);
assert_eq!(squeek_row_contains(&row, button_ptr), true);
let shared_button = make_button_with_state(
"test2".into(),
state
let button = squeek_row_create_button(&mut row as *mut Row, 0, 0);
assert_eq!(squeek_row_contains(&row, button), true);
let shared_button = squeek_row_create_button_with_state(
&mut row as *mut Row,
button
);
let shared_button_ptr = button_as_raw(&shared_button);
row.buttons.push(shared_button);
assert_eq!(squeek_row_contains(&row, shared_button_ptr), true);
assert_eq!(squeek_row_contains(&row, shared_button), true);
let row = Row::new(0);
assert_eq!(squeek_row_contains(&row, button_ptr), false);
assert_eq!(squeek_row_contains(&row, button), false);
}
#[test]
fn view_has_button() {
let state = make_state();
let state = Rc::new(RefCell::new(::keyboard::KeyState {
pressed: false,
locked: false,
keycode: 0,
symbol: None,
}));
let state_clone = ::keyboard::c::CKeyState::wrap(state.clone());
let button = make_button_with_state("1".into(), state);
let button = Box::new(Button {
oref: OutlineRef(0),
bounds: None,
state: state,
});
let button_ptr = button.as_ref() as *const Button;
let row = Box::new(Row {
@ -442,10 +497,6 @@ pub mod c {
x: 0f64, y: 0f64,
width: 0f64, height: 0f64
},
spacing: Spacing {
button: 0f64,
row: 0f64,
},
rows: vec!(row),
};
@ -465,10 +516,6 @@ pub mod c {
x: 0f64, y: 0f64,
width: 0f64, height: 0f64
},
spacing: Spacing {
button: 0f64,
row: 0f64,
},
rows: Vec::new(),
};
assert_eq!(
@ -488,64 +535,18 @@ pub mod c {
#[cfg(test)]
mod test {
use super::*;
use ::keyboard::c::CKeyState;
pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> {
Rc::new(RefCell::new(::keyboard::KeyState {
pressed: false,
locked: false,
keycode: None,
symbol: Symbol {
action: Action::SetLevel("default".into()),
}
}))
}
pub fn make_button_with_state(
name: String,
state: Rc<RefCell<::keyboard::KeyState>>,
) -> Box<Button> {
Box::new(Button {
name: CString::new(name.clone()).unwrap(),
bounds: c::Bounds {
x: 0f64, y: 0f64, width: 0f64, height: 0f64
},
outline_name: CString::new("test").unwrap(),
label: Label::Text(CString::new(name).unwrap()),
state: state,
})
}
pub fn button_as_raw(button: &Box<Button>) -> *const Button {
button.as_ref() as *const Button
}
#[test]
fn button_has_key() {
let state = make_state();
let button = make_button_with_state("1".into(), state.clone());
assert_eq!(
squeek_button_has_key(
button_as_raw(&button),
CKeyState::wrap(state.clone())
),
1
);
let other_state = make_state();
let other_button = make_button_with_state("1".into(), other_state);
assert_eq!(
squeek_button_has_key(
button_as_raw(&other_button),
CKeyState::wrap(state.clone())
),
0
);
let orphan_state = CKeyState::wrap(make_state());
assert_eq!(
squeek_button_has_key(button_as_raw(&button), orphan_state),
0
);
let button = squeek_button_new(0, 0);
let state = squeek_button_get_key(button);
assert_eq!(squeek_button_has_key(button, state.clone()), 1);
let other_button = squeek_button_new(0, 0);
assert_eq!(squeek_button_has_key(other_button, state.clone()), 0);
let other_state = ::keyboard::c::squeek_key_new(0);
assert_eq!(squeek_button_has_key(button, other_state), 0);
let shared_button = squeek_button_new_with_state(button);
assert_eq!(squeek_button_has_key(shared_button, state), 1);
}
}
}
@ -556,37 +557,28 @@ pub struct Size {
pub height: f64,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Label {
/// Text used to display the symbol
Text(CString),
/// Icon name used to render the symbol
IconName(CString),
}
/// The graphical representation of a button
#[derive(Clone, Debug)]
pub struct Button {
/// ID string, e.g. for CSS
pub name: CString,
/// Label to display to the user
pub label: Label,
/// TODO: position the buttons before they get initial bounds
oref: c::OutlineRef,
/// TODO: abolish Option, buttons should be created with bounds fully formed
/// Position relative to some origin (i.e. parent/row)
pub bounds: c::Bounds,
/// The name of the visual class applied
pub outline_name: CString,
bounds: Option<c::Bounds>,
/// current state, shared with other buttons
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
pub struct Row {
pub buttons: Vec<Box<Button>>,
buttons: Vec<Box<Button>>,
/// Angle is not really used anywhere...
pub angle: i32,
angle: i32,
/// Position relative to some origin (i.e. parent/view origin)
pub bounds: Option<c::Bounds>,
bounds: Option<c::Bounds>,
}
impl Row {
@ -606,7 +598,7 @@ impl Row {
}
}
fn calculate_button_positions(outlines: Vec<c::Bounds>, button_spacing: f64)
fn calculate_button_positions(outlines: Vec<c::Bounds>)
-> Vec<c::Bounds>
{
let mut x_offset = 0f64;
@ -616,7 +608,7 @@ impl Row {
x: x_offset,
..outline.clone()
};
x_offset += outline.width + button_spacing;
x_offset += outline.width + BUTTON_SPACING;
position
}).collect()
}
@ -647,7 +639,9 @@ impl Row {
};
let angle = self.angle;
self.buttons.iter_mut().find(|button| {
let bounds = button.bounds.clone();
let bounds = button.bounds
.as_ref().expect("Missing bounds on button")
.clone();
let point = point.clone();
let origin = origin.clone();
procedures::is_point_inside(bounds, point, origin, angle)
@ -655,17 +649,10 @@ impl Row {
}
}
#[derive(Clone, Debug)]
pub struct Spacing {
pub row: f64,
pub button: f64,
}
pub struct View {
/// Position relative to keyboard origin
pub bounds: c::Bounds,
pub spacing: Spacing,
pub rows: Vec<Box<Row>>,
bounds: c::Bounds,
rows: Vec<Box<Row>>,
}
impl View {
@ -676,9 +663,7 @@ impl View {
/// and derive a scaling factor that lets contents fit into view)
/// (or TODO: blow up view bounds to match contents
/// and then scale the entire thing)
fn calculate_row_positions(&self, sizes: Vec<Size>, row_spacing: f64)
-> Vec<c::Bounds>
{
fn calculate_row_positions(&self, sizes: Vec<Size>) -> Vec<c::Bounds> {
let mut y_offset = self.bounds.y;
sizes.into_iter().map(|size| {
let position = c::Bounds {
@ -687,7 +672,7 @@ impl View {
width: size.width,
height: size.height,
};
y_offset += size.height + row_spacing;
y_offset += size.height + ROW_SPACING;
position
}).collect()
}
@ -696,23 +681,19 @@ impl View {
/// The view itself will not be affected by the sizes
fn place_buttons_with_sizes(
&mut self,
button_outlines: Vec<Vec<c::Bounds>>,
spacing: Spacing,
button_outlines: Vec<Vec<c::Bounds>>
) {
// Determine all positions
let button_positions: Vec<_>
= button_outlines.into_iter()
.map(|outlines| {
Row::calculate_button_positions(outlines, spacing.button)
})
.map(Row::calculate_button_positions)
.collect();
let row_sizes = button_positions.iter()
.map(Row::calculate_row_size)
.collect();
let row_positions
= self.calculate_row_positions(row_sizes, spacing.row);
let row_positions = self.calculate_row_positions(row_sizes);
// Apply all positions
for ((mut row, row_position), button_positions)
@ -723,7 +704,7 @@ impl View {
for (mut button, button_position)
in row.buttons.iter_mut()
.zip(button_positions) {
button.bounds = button_position;
button.bounds = Some(button_position);
}
}
}
@ -746,26 +727,6 @@ impl View {
}
}
pub struct Layout {
pub current_view: String,
pub views: HashMap<String, Box<View>>,
// TODO: move to ::keyboard::Keyboard
pub keymap_str: CString,
}
struct NoSuchView;
impl Layout {
fn set_view(&mut self, view: String) -> Result<(), NoSuchView> {
if self.views.contains_key(&view) {
self.current_view = view;
Ok(())
} else {
Err(NoSuchView)
}
}
}
mod procedures {
use super::*;

View File

@ -1,16 +1,9 @@
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate maplit;
extern crate serde;
extern crate xkbcommon;
mod bitflags;
pub mod data;
pub mod float_ord;
pub mod imservice;
mod float_ord;
mod imservice;
mod keyboard;
mod layout;
mod resources;
mod symbol;
mod util;
mod xdg;

View File

@ -19,15 +19,20 @@ sources = [
'../eek/eek-element.c',
'../eek/eek-gtk-keyboard.c',
'../eek/eek-keyboard.c',
'../eek/eek-keyboard-drawing.c',
'../eek/eek-keysym.c',
'../eek/eek-layout.c',
'../eek/eek-renderer.c',
'../eek/eek-section.c',
'../eek/eek-types.c',
'../eek/eek-xml-layout.c',
'../eek/layersurface.c',
dbus_src,
enums,
keysym_entries,
'../eekboard/key-emitter.c',
'../eekboard/eekboard-context-service.c',
'../eekboard/eekboard-context.c',
'../eekboard/eekboard-service.c',
# '../eekboard/eekboard-xklutil.c',
squeekboard_resources,
@ -51,41 +56,25 @@ deps = [
# dependency('libxklavier'), # FIXME remove
]
rslibs = custom_target(
'rslibs',
build_by_default: true,
build_always_stale: true,
output: ['librs.a'],
install: false,
console: true,
command: [cargo_script, '@OUTPUT@', 'build']
# Replacement for eekboard-server
rslib = static_library(
'rslib',
sources: ['lib.rs'],
rust_crate_type: 'staticlib'
)
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
rstests = executable(
'rstests',
sources: ['lib.rs'],
rust_args: ['--test'],
install: false
)
test(
'rstest',
cargo_script,
args: ['', 'test'],
depends: build_rstests,
)
test('rstests', rstests)
libsqueekboard = static_library('libsqueekboard',
sources,
link_with: [rslibs],
link_with: rslib,
include_directories: [include_directories('..'), include_directories('../eek')],
dependencies: deps,
c_args: [

View File

@ -1,27 +0,0 @@
/*! Statically linked resources.
* This could be done using GResource, but that would need additional work.
*/
const KEYBOARDS: &[(*const str, *const str)] = &[
("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")),
("number", include_str!("../data/keyboards/number.yaml")),
];
pub fn get_keyboard(needle: &str) -> Option<&'static str> {
// Need to dereference in unsafe code
// comparing *const str to &str will compare pointers
KEYBOARDS.iter()
.find(|(name, _)| {
let name: *const str = *name;
(unsafe { &*name }) == needle
})
.map(|(_, value)| {
let value: *const str = *value;
unsafe { &*value }
})
}

View File

@ -40,7 +40,6 @@ struct _ServerContextService {
GtkWidget *window;
GtkWidget *widget;
guint hiding;
gdouble size_constraint_landscape[2];
gdouble size_constraint_portrait[2];
@ -52,6 +51,17 @@ struct _ServerContextServiceClass {
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
on_destroy (GtkWidget *widget, gpointer user_data)
{
@ -97,6 +107,15 @@ on_notify_keyboard (GObject *object,
}
}
static void
on_notify_fullscreen (GObject *object,
GParamSpec *spec,
ServerContextService *context)
{
if (context->window)
set_geometry (context);
}
static void
on_notify_map (GObject *object,
ServerContextService *context)
@ -112,6 +131,49 @@ on_notify_unmap (GObject *object,
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
static void
make_window (ServerContextService *context)
@ -175,6 +237,7 @@ make_widget (ServerContextService *context)
gtk_widget_set_has_tooltip (context->widget, TRUE);
gtk_container_add (GTK_CONTAINER(context->window), context->widget);
gtk_widget_show (context->widget);
set_geometry (context);
}
static void
@ -182,11 +245,6 @@ server_context_service_real_show_keyboard (EekboardContextService *_context)
{
ServerContextService *context = SERVER_CONTEXT_SERVICE(_context);
if (context->hiding) {
g_source_remove (context->hiding);
context->hiding = 0;
}
if (!context->window)
make_window (context);
if (!context->widget)
@ -197,22 +255,12 @@ server_context_service_real_show_keyboard (EekboardContextService *_context)
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
server_context_service_real_hide_keyboard (EekboardContextService *_context)
{
ServerContextService *context = SERVER_CONTEXT_SERVICE(_context);
if (!context->hiding)
context->hiding = g_timeout_add (200, (GSourceFunc) on_hide, context);
gtk_widget_hide (context->window);
EEKBOARD_CONTEXT_SERVICE_CLASS (server_context_service_parent_class)->
hide_keyboard (_context);
@ -301,10 +349,21 @@ server_context_service_class_init (ServerContextServiceClass *klass)
static void
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,
"notify::keyboard",
G_CALLBACK(on_notify_keyboard),
context);
g_signal_connect (context,
"notify::fullscreen",
G_CALLBACK(on_notify_fullscreen),
context);
}
EekboardContextService *

51
src/server-context.h Normal file
View File

@ -0,0 +1,51 @@
/*
* 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 */

24
src/symbol.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef __SYMBOL_H
#define __SYMBOL_H
#include "stdbool.h"
#include "inttypes.h"
// Defined in Rust
struct squeek_symbol;
struct squeek_symbols;
void squeek_symbols_add(struct squeek_symbols*,
const char *element_name,
const char *text, uint32_t keyval,
const char *label, const char *icon,
const char *tooltip);
const char *squeek_symbol_get_name(struct squeek_symbol* symbol);
const char *squeek_symbol_get_label(struct squeek_symbol* symbol);
const char *squeek_symbol_get_icon_name(struct squeek_symbol* symbol);
uint32_t squeek_symbol_get_modifier_mask(struct squeek_symbol* symbol);
void squeek_symbol_print(struct squeek_symbol* symbol);
#endif

View File

@ -1,46 +1,138 @@
/*! The symbol object, defining actions that the key can do when activated */
use std::ffi::CString;
/// Name of the keysym
#[derive(Debug, Clone, PartialEq)]
pub struct KeySym(pub String);
/// Gathers stuff defined in C or called by C
pub mod c {
use super::*;
use std::ffi::CStr;
use std::os::raw::c_char;
use std::ptr;
// The following defined in C
// Legacy; Will never be used in Rust as a bit field
enum ModifierMask {
Nothing = 0,
Shift = 1,
}
// The following defined in Rust.
// TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
// Symbols are owned by Rust and will move towards no C manipulation, so it may make sense not to wrap them
#[no_mangle]
pub extern "C"
fn squeek_symbol_get_name(symbol: *const Symbol) -> *const c_char {
let symbol = unsafe { &*symbol };
match &symbol.action {
Action::Submit { text: Some(text), .. } => text.as_ptr(),
_ => ptr::null(),
}
}
#[no_mangle]
pub extern "C"
fn squeek_symbol_get_label(symbol: *const Symbol) -> *const c_char {
let symbol = unsafe { &*symbol };
match &symbol.label {
Label::Text(text) => text.as_ptr(),
// returning static strings to C is a bit cumbersome
Label::IconName(_) => unsafe {
CStr::from_bytes_with_nul_unchecked(b"icon\0")
}.as_ptr(),
}
}
#[no_mangle]
pub extern "C"
fn squeek_symbol_get_icon_name(symbol: *const Symbol) -> *const c_char {
let symbol = unsafe { &*symbol };
match &symbol.label {
Label::Text(_) => ptr::null(),
Label::IconName(name) => name.as_ptr(),
}
}
// Legacy; throw away
#[no_mangle]
pub extern "C"
fn squeek_symbol_get_modifier_mask(symbol: *const Symbol) -> u32 {
let symbol = unsafe { &*symbol };
(match &symbol.action {
Action::SetLevel(1) => ModifierMask::Shift,
_ => ModifierMask::Nothing,
}) as u32
}
#[no_mangle]
pub extern "C"
fn squeek_symbol_print(symbol: *const Symbol) {
let symbol = unsafe { &*symbol };
println!("{:?}", symbol);
}
}
/// Just defines some int->identifier mappings for convenience
#[derive(Debug, Clone)]
pub enum KeySym {
Unknown = 0,
Shift = 0xffe1,
}
impl KeySym {
pub fn from_u32(num: u32) -> KeySym {
match num {
0xffe1 => KeySym::Shift,
_ => KeySym::Unknown,
}
}
}
#[derive(Debug, Clone)]
pub struct XKeySym(pub u32);
#[derive(Debug, Clone)]
pub enum Label {
/// Text used to display the symbol
Text(CString),
/// Icon name used to render the symbol
IconName(CString),
}
/// Use to switch layouts
type Level = String;
type Level = u8;
/// Use to send modified keypresses
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone)]
pub enum Modifier {
Control,
Alt,
}
/// Action to perform on the keypress and, in reverse, on keyrelease
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone)]
pub enum Action {
/// Switch to this view
/// Switch to this level TODO: reverse?
SetLevel(Level),
/// Switch to a view and latch
LockLevel {
lock: Level,
/// When unlocked by pressing it or emitting a key
unlock: Level,
},
/// Set this modifier TODO: release?
SetModifier(Modifier),
/// Submit some text
Submit {
/// Text to submit with input-method
/// orig: Canonical name of the symbol
text: Option<CString>,
/// The key events this symbol submits when submitting text is not possible
keys: Vec<KeySym>,
keys: Vec<XKeySym>,
},
}
/// Contains a static description of a particular key's actions
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone)]
pub struct Symbol {
/// The action that this key performs
pub action: Action,
/// Label to display to the user
pub label: Label,
// FIXME: is it used?
pub tooltip: Option<CString>,
}

View File

@ -1,14 +1,9 @@
pub mod c {
use std::cell::RefCell;
use std::ffi::{ CStr, CString };
use std::os::raw::c_char;
use std::rc::Rc;
use std::str::Utf8Error;
// traits
use std::borrow::ToOwned;
#[allow(dead_code)]
pub fn as_str(s: &*const c_char) -> Result<Option<&str>, Utf8Error> {
if s.is_null() {
Ok(None)
@ -18,7 +13,7 @@ pub mod c {
.map(Some)
}
}
pub fn as_cstr(s: &*const c_char) -> Option<&CStr> {
if s.is_null() {
None
@ -52,52 +47,4 @@ pub mod c {
assert_eq!(as_str(&ptr::null()), Ok(None))
}
}
/// Wraps structures to pass them safely to/from C
/// Since C doesn't respect borrowing rules,
/// RefCell will enforce them dynamically (only 1 writer/many readers)
/// Rc is implied and will ensure timely dropping
#[repr(transparent)]
pub struct Wrapped<T: Clone>(*const RefCell<T>);
// It would be nice to implement `Borrow`
// directly on the raw pointer to avoid one conversion call,
// but the `borrow()` call needs to extract a `Rc`,
// and at the same time return a reference to it (`std::cell::Ref`)
// to take advantage of `Rc<RefCell>::borrow()` runtime checks.
// Unfortunately, that needs a `Ref` struct with self-referential fields,
// which is a bit too complex for now.
impl<T: Clone> Wrapped<T> {
pub fn wrap(state: Rc<RefCell<T>>) -> Wrapped<T> {
Wrapped(Rc::into_raw(state))
}
/// Extracts the reference to the data.
/// It may cause problems if attempted in more than one place
pub unsafe fn unwrap(self) -> Rc<RefCell<T>> {
Rc::from_raw(self.0)
}
/// Creates a new Rc reference to the same data
pub fn clone_ref(&self) -> Rc<RefCell<T>> {
// A bit dangerous: the Rc may be in use elsewhere
let used_rc = unsafe { Rc::from_raw(self.0) };
let rc = used_rc.clone();
Rc::into_raw(used_rc); // prevent dropping the original reference
rc
}
/// Create a copy of the underlying data
pub fn to_owned(&self) -> T {
let rc = self.clone_ref();
let r = rc.borrow();
r.to_owned()
}
}
impl<T: Clone> Clone for Wrapped<T> {
fn clone(&self) -> Wrapped<T> {
Wrapped::wrap(self.clone_ref())
}
}
}

View File

@ -1,63 +0,0 @@
/*! XDG directory handling. */
/* Based on dirs-sys https://github.com/soc/dirs-sys-rs
* by "Simon Ochsenreither <simon@ochsenreither.de>",
* Licensed under either of
Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
*
* Based on dirs https://github.com/soc/dirs-rs
* by "Simon Ochsenreither <simon@ochsenreither.de>",
* Licensed under either of
Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
*
* Based on xdg https://github.com/whitequark/rust-xdg
* by "Ben Longbons <b.r.longbons@gmail.com>",
* "whitequark <whitequark@whitequark.org>",
rust-xdg is distributed under the terms of both the MIT license and the Apache License (Version 2.0).
*
* The above crates were used to get
* a version without all the excessive dependencies.
*/
use std::env;
use std::ffi::OsString;
use std::path::{ Path, PathBuf };
fn is_absolute_path(path: OsString) -> Option<PathBuf> {
let path = PathBuf::from(path);
if path.is_absolute() {
Some(path)
} else {
None
}
}
fn home_dir() -> Option<PathBuf> {
return env::var_os("HOME")
.and_then(|h| if h.is_empty() { None } else { Some(h) })
.map(PathBuf::from);
}
fn data_dir() -> Option<PathBuf> {
env::var_os("XDG_DATA_HOME")
.and_then(is_absolute_path)
.or_else(|| home_dir().map(|h| h.join(".local/share")))
}
/// Returns the path to the directory within the data dir
pub fn data_path<P>(path: P) -> Option<PathBuf>
where P: AsRef<Path>
{
data_dir().map(|dir| {
dir.join(path.as_ref())
})
}

45
tests/eek-simple-test.c Normal file
View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010-2011 Red Hat, Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include "eek/eek.h"
#include <gtk/gtk.h>
static void
test_create (void)
{
EekBounds bounds = {0};
struct squeek_view *view = squeek_view_new(bounds);
struct squeek_button *button0, *button1;
struct squeek_row *row = squeek_view_create_row (view, 0);
g_assert (row);
button0 = squeek_row_create_button (row, 1, 0);
g_assert (button0);
button1 = squeek_row_create_button (row, 2, 0);
g_assert (button1);
}
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/eek-simple-test/create", test_create);
return g_test_run ();
}

55
tests/eek-xml-test.c Normal file
View File

@ -0,0 +1,55 @@
/*
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010-2011 Red Hat, Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
/* For gdk_x11_display_get_xdisplay(). See main(). */
#include <gtk/gtk.h>
#include "config.h"
#include "eek/eek.h"
#include "eek/eek-xml-layout.h"
static void
test_output_parse (void)
{
EekLayout *layout;
LevelKeyboard *keyboard;
GError *error;
error = NULL;
layout = eek_xml_layout_new ("us", &error);
g_assert_no_error (error);
/* We don't need the context service to parse an XML file, so we can pass
NULL when creating a keyboard. */
keyboard = eek_xml_layout_real_create_keyboard(layout, NULL);
g_object_unref (layout);
level_keyboard_free(keyboard);
}
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/eek-xml-test/output-parse", test_output_parse);
return g_test_run ();
}

View File

@ -1,41 +0,0 @@
#!/usr/bin/env python3
import gi
import sys
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class App(Gtk.Application):
purposes = [
("Free form", Gtk.InputPurpose.FREE_FORM),
("Alphabetical", Gtk.InputPurpose.ALPHA),
("Digits", Gtk.InputPurpose.DIGITS),
("Number", Gtk.InputPurpose.NUMBER),
("Phone", Gtk.InputPurpose.PHONE),
("URL", Gtk.InputPurpose.URL),
("E-mail", Gtk.InputPurpose.EMAIL),
("Name", Gtk.InputPurpose.NAME),
("Password", Gtk.InputPurpose.PASSWORD),
("PIN", Gtk.InputPurpose.PIN)
]
def do_activate(self):
w = Gtk.ApplicationWindow(application=self)
grid = Gtk.Grid(orientation='vertical', column_spacing=8, row_spacing=8)
i = 0
for text, purpose in self.purposes:
l = Gtk.Label(label=text)
e = Gtk.Entry(hexpand=True)
e.set_input_purpose(purpose)
grid.attach(l, 0, i, 1, 1)
grid.attach(e, 1, i, 1, 1)
i += 1
w.add(grid)
w.show_all()
app = App()
app.run(sys.argv)

View File

@ -1,19 +0,0 @@
---
row_spacing: 0
button_spacing: 0
bounds:
x: 0
y: 0
width: 0
height: 0
views:
base:
- "test"
outlines:
default:
bounds: { x: 0, y: 0, width: 0, height: 0 }
buttons:
test:
label: "test"

View File

@ -1,14 +0,0 @@
---
# missing views
row_spacing: 0
button_spacing: 0
bounds:
x: 0
y: 0
width: 0
height: 0
outlines:
default:
bounds: { x: 0, y: 0, width: 0, height: 0 }

View File

@ -1,18 +0,0 @@
---
# extra field
row_spacing: 0
button_spacing: 0
bounds:
x: 0
y: 0
width: 0
height: 0
views:
base:
- "test"
outlines:
default:
bounds: { x: 0, y: 0, width: 0, height: 0 }
bad_field: false

View File

@ -1,20 +0,0 @@
---
# punctuation
row_spacing: 0
button_spacing: 0
bounds:
x: 0
y: 0
width: 0
height: 0
views:
base:
- "."
outlines:
default:
bounds: { x: 0, y: 0, width: 0, height: 0 }
buttons:
".":
label: "test"

View File

@ -1,20 +0,0 @@
---
# punctuation
row_spacing: 0
button_spacing: 0
bounds:
x: 0
y: 0
width: 0
height: 0
views:
base:
- "å"
outlines:
default:
bounds: { x: 0, y: 0, width: 0, height: 0 }
buttons:
å:
label: "test"

View File

@ -19,10 +19,13 @@ test_link_args = [
'-fPIC',
]
c_tests = [
tests = [
'eek-simple-test',
'eek-xml-test',
'test-keymap-generation'
]
foreach name : c_tests
foreach name : tests
test_sources = [name + '.c']
@ -44,15 +47,4 @@ foreach name : c_tests
endforeach
# The layout test is in the examples directory
# due to the way Cargo builds executables
# and the need to call it manually
foreach layout : ['us', 'el', 'es', 'it', 'nb', 'number']
test(
'test_layout_' + layout,
cargo_script,
args: ['', 'run', '--example', 'test_layout', layout]
)
endforeach
endif

View File

@ -0,0 +1,72 @@
/*
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010-2011 Red Hat, Inc.
* Copyright (C) 2019 Purism SPC
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
/* For gdk_x11_display_get_xdisplay(). See main(). */
#include <gtk/gtk.h>
#include <xkbcommon/xkbcommon.h>
#include "config.h"
#include "eek/eek.h"
#include "eek/eek-xml-layout.h"
static void
test_check_xkb (void)
{
EekLayout *layout;
LevelKeyboard *keyboard;
GError *error;
error = NULL;
layout = eek_xml_layout_new ("us", &error);
g_assert_no_error (error);
keyboard = eek_xml_layout_real_create_keyboard(layout, NULL);
gchar *keymap_str = eek_keyboard_get_keymap(keyboard);
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!context) {
g_error("No context created");
}
struct xkb_keymap *keymap = xkb_keymap_new_from_string(context, keymap_str,
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
free(keymap_str);
xkb_context_unref(context);
if (!keymap) {
g_error("Bad keymap");
}
g_object_unref (layout);
level_keyboard_free(keyboard);
}
int
main (int argc, char **argv)
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/test-keymap-generation/check-xkb", test_check_xkb);
return g_test_run ();
}