Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e2def7069 | |||
| aa5e1d87dd | |||
| 0a0f7a09a4 | |||
| 9ef38ecf30 |
@ -9,10 +9,6 @@ stages:
|
|||||||
- librem5
|
- librem5
|
||||||
|
|
||||||
before_script:
|
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 update
|
||||||
- apt-get -y build-dep .
|
- apt-get -y build-dep .
|
||||||
|
|
||||||
@ -26,26 +22,6 @@ build_meson:
|
|||||||
- meson . _build/ -Ddepdatadir=/usr/share
|
- meson . _build/ -Ddepdatadir=/usr/share
|
||||||
- ninja -C _build install
|
- 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:
|
test:
|
||||||
<<: *tags
|
<<: *tags
|
||||||
stage: test
|
stage: test
|
||||||
|
|||||||
162
Cargo.lock
generated
162
Cargo.lock
generated
@ -1,162 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "1.2.0"
|
|
||||||
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.2.0 (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.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2"
|
|
||||||
"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"
|
|
||||||
15
Cargo.toml
15
Cargo.toml
@ -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"]
|
|
||||||
58
HACKING.md
58
HACKING.md
@ -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 /source_path '' 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 /source_path '' update
|
|
||||||
ninja test
|
|
||||||
```
|
|
||||||
29
README.md
29
README.md
@ -3,18 +3,14 @@
|
|||||||
|
|
||||||
*Squeekboard* is a virtual keyboard supporting Wayland, built primarily for the *Librem 5* phone.
|
*Squeekboard* is a virtual keyboard supporting Wayland, built primarily for the *Librem 5* phone.
|
||||||
|
|
||||||
It squeaks because some Rust got inside.
|
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
|
||||||
### Present
|
### Present
|
||||||
|
|
||||||
- GTK3
|
- GTK3
|
||||||
- Custom yaml-defined keyboards
|
- Custom xml-defined keyboards
|
||||||
- DBus interface to show and hide
|
- DBus interface to show and hide
|
||||||
- Use Wayland input method protocol to show and hide
|
|
||||||
- Use Wayland virtual keyboard protocol
|
|
||||||
|
|
||||||
### Temporarily dropped
|
### Temporarily dropped
|
||||||
|
|
||||||
@ -22,6 +18,8 @@ Features
|
|||||||
|
|
||||||
### TODO
|
### TODO
|
||||||
|
|
||||||
|
- Use Wayland virtual keyboard protocol
|
||||||
|
- Use Wayland text input protocol
|
||||||
- Use Wayland input method protocol
|
- Use Wayland input method protocol
|
||||||
- Pick up DBus interface files from /usr/share
|
- Pick up DBus interface files from /usr/share
|
||||||
|
|
||||||
@ -40,20 +38,31 @@ $ cd squeekboard
|
|||||||
$ mkdir ../build
|
$ mkdir ../build
|
||||||
$ meson ../build/
|
$ meson ../build/
|
||||||
$ cd ../build
|
$ cd ../build
|
||||||
$ ninja test
|
|
||||||
$ ninja install
|
$ 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
|
Running
|
||||||
-------
|
-------
|
||||||
|
|
||||||
```
|
```
|
||||||
$ phoc # if no compatible Wayland compositor is running yet
|
$ rootston
|
||||||
$ cd ../build/
|
$ cd ../build/
|
||||||
$ src/squeekboard
|
$ 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
|
||||||
|
```
|
||||||
|
|||||||
1
bindings/vala/Eek-0.90.metadata
Normal file
1
bindings/vala/Eek-0.90.metadata
Normal file
@ -0,0 +1 @@
|
|||||||
|
Eek cheader_filename="eek/eek.h"
|
||||||
1
bindings/vala/EekGtk-0.90.metadata
Normal file
1
bindings/vala/EekGtk-0.90.metadata
Normal file
@ -0,0 +1 @@
|
|||||||
|
EekGtk cheader_filename="eek/eek-gtk.h"
|
||||||
1
bindings/vala/EekXkl-0.90.metadata
Normal file
1
bindings/vala/EekXkl-0.90.metadata
Normal file
@ -0,0 +1 @@
|
|||||||
|
EekXkl cheader_filename="eek/eek-xkl.h"
|
||||||
1
bindings/vala/eek-0.90.deps
Normal file
1
bindings/vala/eek-0.90.deps
Normal file
@ -0,0 +1 @@
|
|||||||
|
gio-2.0
|
||||||
1
bindings/vala/eek-gtk-0.90.deps
Normal file
1
bindings/vala/eek-gtk-0.90.deps
Normal file
@ -0,0 +1 @@
|
|||||||
|
eek-0.90
|
||||||
2
bindings/vala/eek-xkl-0.90.deps
Normal file
2
bindings/vala/eek-xkl-0.90.deps
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
eek-0.90
|
||||||
|
x11
|
||||||
23
cargo.sh
23
cargo.sh
@ -1,23 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# This script manages Cargo operations
|
|
||||||
# while keeping the artifact directory within the build tree
|
|
||||||
# instead of the source tree
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
SOURCE_DIR="$1"
|
|
||||||
|
|
||||||
export CARGO_TARGET_DIR=`pwd`
|
|
||||||
if [ ! -z ${2} ]; then
|
|
||||||
OUT_PATH=`realpath "${2}"`
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd $SOURCE_DIR
|
|
||||||
shift
|
|
||||||
shift
|
|
||||||
cargo $BUILD_ARG $@
|
|
||||||
|
|
||||||
if [ ! -z ${OUT_PATH} ]; then
|
|
||||||
cp "${CARGO_TARGET_DIR}"/debug/librs.a "${OUT_PATH}"
|
|
||||||
fi
|
|
||||||
64
data/keyboards/geometry/compact.xml
Normal file
64
data/keyboards/geometry/compact.xml
Normal 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>
|
||||||
105
data/keyboards/geometry/extended.xml
Normal file
105
data/keyboards/geometry/extended.xml
Normal 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>
|
||||||
40
data/keyboards/geometry/number-keypad.xml
Normal file
40
data/keyboards/geometry/number-keypad.xml
Normal 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>
|
||||||
100
data/keyboards/keyboards.xml
Normal file
100
data/keyboards/keyboards.xml
Normal 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>
|
||||||
@ -1,179 +0,0 @@
|
|||||||
---
|
|
||||||
bounds: { x: 0, y: 10, width: 426, height: 229 }
|
|
||||||
|
|
||||||
outlines:
|
|
||||||
default:
|
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 32, height: 52 }
|
|
||||||
altline:
|
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 48.39024, height: 52 }
|
|
||||||
outline7:
|
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 88.97561, height: 52 }
|
|
||||||
spaceline:
|
|
||||||
corner_radius: 1
|
|
||||||
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: "]"
|
|
||||||
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
---
|
|
||||||
bounds: { x: 0, y: 10, width: 410, height: 229 }
|
|
||||||
|
|
||||||
outlines:
|
|
||||||
default:
|
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 37.46341, height: 52 }
|
|
||||||
altline:
|
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 48.39024, height: 52 }
|
|
||||||
outline7:
|
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 88.97561, height: 52 }
|
|
||||||
spaceline:
|
|
||||||
corner_radius: 1
|
|
||||||
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: ")"
|
|
||||||
|
|
||||||
132
data/keyboards/symbols/nb.xml
Normal file
132
data/keyboards/symbols/nb.xml
Normal 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="τ">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="·">U00B7</symbol>
|
||||||
|
<symbol label="t">t</symbol>
|
||||||
|
<symbol label="T">T</symbol>
|
||||||
|
<symbol label="5">5</symbol>
|
||||||
|
<symbol label="√">squareroot</symbol>
|
||||||
|
<symbol label="y">y</symbol>
|
||||||
|
<symbol label="Y">Y</symbol>
|
||||||
|
<symbol label="6">6</symbol>
|
||||||
|
<symbol label="π">Greek_pi</symbol>
|
||||||
|
<symbol label="u">u</symbol>
|
||||||
|
<symbol label="U">U</symbol>
|
||||||
|
<symbol label="7">7</symbol>
|
||||||
|
<symbol label="÷">division</symbol>
|
||||||
|
<symbol label="i">i</symbol>
|
||||||
|
<symbol label="I">I</symbol>
|
||||||
|
<symbol label="8">8</symbol>
|
||||||
|
<symbol label="×">multiply</symbol>
|
||||||
|
<symbol label="o">o</symbol>
|
||||||
|
<symbol label="O">O</symbol>
|
||||||
|
<symbol label="9">9</symbol>
|
||||||
|
<symbol label="¶">paragraph</symbol>
|
||||||
|
<symbol label="p">p</symbol>
|
||||||
|
<symbol label="P">P</symbol>
|
||||||
|
<symbol label="0">0</symbol>
|
||||||
|
<symbol label="△">U25B3</symbol>
|
||||||
|
<symbol keyval="229" label="å">aring</symbol>
|
||||||
|
<symbol keyval="197" label="Å">Aring</symbol>
|
||||||
|
<symbol label="a">a</symbol>
|
||||||
|
<symbol label="A">A</symbol>
|
||||||
|
<symbol label="@">at</symbol>
|
||||||
|
<symbol label="©">copyright</symbol>
|
||||||
|
<symbol label="s">s</symbol>
|
||||||
|
<symbol label="S">S</symbol>
|
||||||
|
<symbol label="#">numbersign</symbol>
|
||||||
|
<symbol label="®">U00AE</symbol>
|
||||||
|
<symbol label="d">d</symbol>
|
||||||
|
<symbol label="D">D</symbol>
|
||||||
|
<symbol label="$">dollar</symbol>
|
||||||
|
<symbol label="£">U00A3</symbol>
|
||||||
|
<symbol label="f">f</symbol>
|
||||||
|
<symbol label="F">F</symbol>
|
||||||
|
<symbol label="%">percent</symbol>
|
||||||
|
<symbol label="€">EuroSign</symbol>
|
||||||
|
<symbol label="g">g</symbol>
|
||||||
|
<symbol label="G">G</symbol>
|
||||||
|
<symbol label="&">ampersand</symbol>
|
||||||
|
<symbol label="¥">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="°">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="ø">oslash</symbol>
|
||||||
|
<symbol keyval="216" label="Ø">Oslash</symbol>
|
||||||
|
<symbol label=")">parenright</symbol>
|
||||||
|
<symbol label="}">braceright</symbol>
|
||||||
|
<symbol keyval="230" label="æ">ae</symbol>
|
||||||
|
<symbol keyval="198" label="Æ">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=""">quotedbl</symbol>
|
||||||
|
<symbol label="/">slash</symbol>
|
||||||
|
<symbol label="c">c</symbol>
|
||||||
|
<symbol label="C">C</symbol>
|
||||||
|
<symbol label="'">quoteright</symbol>
|
||||||
|
<symbol label="<">less</symbol>
|
||||||
|
<symbol label="v">v</symbol>
|
||||||
|
<symbol label="V">V</symbol>
|
||||||
|
<symbol label=":">colon</symbol>
|
||||||
|
<symbol label=">">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="☺" icon="keyboard-mode-symbolic" tooltip="Setup">preferences</symbol>
|
||||||
|
<symbol label=" ">space</symbol>
|
||||||
|
<symbol keyval="65288" icon="edit-clear-symbolic">BackSpace</symbol>
|
||||||
|
</symbols>
|
||||||
22
data/keyboards/symbols/special/number.xml
Normal file
22
data/keyboards/symbols/special/number.xml
Normal 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>
|
||||||
118
data/keyboards/symbols/us.xml
Normal file
118
data/keyboards/symbols/us.xml
Normal 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="·">U00B7</symbol>
|
||||||
|
<symbol label="t">t</symbol>
|
||||||
|
<symbol label="T">T</symbol>
|
||||||
|
<symbol label="5">5</symbol>
|
||||||
|
<symbol label="√">squareroot</symbol>
|
||||||
|
<symbol label="y">y</symbol>
|
||||||
|
<symbol label="Y">Y</symbol>
|
||||||
|
<symbol label="6">6</symbol>
|
||||||
|
<symbol label="π">Greek_pi</symbol>
|
||||||
|
<symbol label="u">u</symbol>
|
||||||
|
<symbol label="U">U</symbol>
|
||||||
|
<symbol label="7">7</symbol>
|
||||||
|
<symbol label="÷">division</symbol>
|
||||||
|
<symbol label="i">i</symbol>
|
||||||
|
<symbol label="I">I</symbol>
|
||||||
|
<symbol label="8">8</symbol>
|
||||||
|
<symbol label="×">multiply</symbol>
|
||||||
|
<symbol label="o">o</symbol>
|
||||||
|
<symbol label="O">O</symbol>
|
||||||
|
<symbol label="9">9</symbol>
|
||||||
|
<symbol label="¶">paragraph</symbol>
|
||||||
|
<symbol label="p">p</symbol>
|
||||||
|
<symbol label="P">P</symbol>
|
||||||
|
<symbol label="0">0</symbol>
|
||||||
|
<symbol label="τ">Greek_tau</symbol>
|
||||||
|
<symbol label="a">a</symbol>
|
||||||
|
<symbol label="A">A</symbol>
|
||||||
|
<symbol label="@">at</symbol>
|
||||||
|
<symbol label="©">copyright</symbol>
|
||||||
|
<symbol label="s">s</symbol>
|
||||||
|
<symbol label="S">S</symbol>
|
||||||
|
<symbol label="#">numbersign</symbol>
|
||||||
|
<symbol label="®">U00AE</symbol>
|
||||||
|
<symbol label="d">d</symbol>
|
||||||
|
<symbol label="D">D</symbol>
|
||||||
|
<symbol label="$">dollar</symbol>
|
||||||
|
<symbol label="£">U00A3</symbol>
|
||||||
|
<symbol label="f">f</symbol>
|
||||||
|
<symbol label="F">F</symbol>
|
||||||
|
<symbol label="%">percent</symbol>
|
||||||
|
<symbol label="€">EuroSign</symbol>
|
||||||
|
<symbol label="g">g</symbol>
|
||||||
|
<symbol label="G">G</symbol>
|
||||||
|
<symbol label="&">ampersand</symbol>
|
||||||
|
<symbol label="¥">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="°">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=""">quotedbl</symbol>
|
||||||
|
<symbol label="/">slash</symbol>
|
||||||
|
<symbol label="c">c</symbol>
|
||||||
|
<symbol label="C">C</symbol>
|
||||||
|
<symbol label="'">quoteright</symbol>
|
||||||
|
<symbol label="<">less</symbol>
|
||||||
|
<symbol label="v">v</symbol>
|
||||||
|
<symbol label="V">V</symbol>
|
||||||
|
<symbol label=":">colon</symbol>
|
||||||
|
<symbol label=">">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="☺" icon="keyboard-mode-symbolic" tooltip="Setup">preferences</symbol>
|
||||||
|
<symbol label=" ">space</symbol>
|
||||||
|
<symbol keyval="65288" icon="edit-clear-symbolic">BackSpace</symbol>
|
||||||
|
</symbols>
|
||||||
@ -1,81 +0,0 @@
|
|||||||
---
|
|
||||||
bounds: { x: 10, y: 10, width: 410, height: 229 }
|
|
||||||
|
|
||||||
outlines:
|
|
||||||
default:
|
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 37.46341, height: 52 }
|
|
||||||
altline:
|
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 48.39024, height: 52 }
|
|
||||||
outline7:
|
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 88.97561, height: 52 }
|
|
||||||
spaceline:
|
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 150.5853, height: 52 }
|
|
||||||
|
|
||||||
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 . 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 . Return"
|
|
||||||
numbers:
|
|
||||||
- "1 2 3 4 5 6 7 8 9 0"
|
|
||||||
- "@ # $ % & - _ + ( )"
|
|
||||||
- "show_symbols , \" ' colon ; ! ? BackSpace"
|
|
||||||
- "show_letters preferences space . Return"
|
|
||||||
symbols:
|
|
||||||
- "~ ` | · √ π τ ÷ × ¶"
|
|
||||||
- "© ® £ € ¥ ^ ° * { }"
|
|
||||||
- "show_numbers \\ / < > = [ ] BackSpace"
|
|
||||||
- "show_letters preferences space . Return"
|
|
||||||
|
|
||||||
buttons:
|
|
||||||
Shift_L:
|
|
||||||
action:
|
|
||||||
locking:
|
|
||||||
lock_view: "upper"
|
|
||||||
unlock_view: "base"
|
|
||||||
outline: "altline"
|
|
||||||
icon: "key-shift"
|
|
||||||
BackSpace:
|
|
||||||
outline: "altline"
|
|
||||||
icon: "edit-clear-symbolic"
|
|
||||||
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: "*/="
|
|
||||||
".":
|
|
||||||
outline: altline
|
|
||||||
space:
|
|
||||||
outline: spaceline
|
|
||||||
label: " "
|
|
||||||
Return:
|
|
||||||
outline: outline7
|
|
||||||
icon: "key-enter"
|
|
||||||
colon:
|
|
||||||
label: ":"
|
|
||||||
"\"":
|
|
||||||
keysym: "quotedbl"
|
|
||||||
@ -6,5 +6,4 @@ Exec=squeekboard
|
|||||||
Icon=squeekboard
|
Icon=squeekboard
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Type=Application
|
Type=Application
|
||||||
NoDisplay=true
|
|
||||||
Categories=GTK;Utility;
|
Categories=GTK;Utility;
|
||||||
|
|||||||
@ -2,6 +2,10 @@
|
|||||||
<gresources>
|
<gresources>
|
||||||
<gresource prefix="/sm/puri/squeekboard">
|
<gresource prefix="/sm/puri/squeekboard">
|
||||||
<file compressed="true">style.css</file>
|
<file compressed="true">style.css</file>
|
||||||
|
<file compressed="true" 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/ar.xml</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/as-inscript.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>
|
<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/ml-inscript.xml</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/mr-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/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/or-inscript.xml</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/pa-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>
|
<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/th.xml</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ua.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/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/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-enter.svg</file>
|
||||||
<file>icons/key-shift.svg</file>
|
<file>icons/key-shift.svg</file>
|
||||||
<file>icons/keyboard-mode-symbolic.svg</file>
|
<file>icons/keyboard-mode-symbolic.svg</file>
|
||||||
|
|||||||
@ -17,23 +17,3 @@
|
|||||||
background: #1c71d8;
|
background: #1c71d8;
|
||||||
border-color: #3584e4;
|
border-color: #3584e4;
|
||||||
}
|
}
|
||||||
|
|
||||||
#Return {
|
|
||||||
background: #1c71d8;
|
|
||||||
border-color: #1a5fb4
|
|
||||||
}
|
|
||||||
|
|
||||||
#Return:active {
|
|
||||||
background: #1c71d8;
|
|
||||||
border-color: #3584e4;
|
|
||||||
}
|
|
||||||
|
|
||||||
#Shift_L {
|
|
||||||
background: #2b292f;
|
|
||||||
border-color: #3e3a44
|
|
||||||
}
|
|
||||||
|
|
||||||
#Shift_L:active {
|
|
||||||
background: #1c71d8;
|
|
||||||
border-color: #3584e4;
|
|
||||||
}
|
|
||||||
|
|||||||
12
debian/cargo/config
vendored
12
debian/cargo/config
vendored
@ -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'
|
|
||||||
|
|
||||||
13
debian/changelog
vendored
13
debian/changelog
vendored
@ -1,16 +1,3 @@
|
|||||||
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
|
squeekboard (1.0.10) unstable; urgency=medium
|
||||||
|
|
||||||
* Use a shared DBus definition
|
* Use a shared DBus definition
|
||||||
|
|||||||
9
debian/control
vendored
9
debian/control
vendored
@ -3,19 +3,12 @@ Section: x11
|
|||||||
Priority: optional
|
Priority: optional
|
||||||
Maintainer: Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm>
|
Maintainer: Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm>
|
||||||
Build-Depends:
|
Build-Depends:
|
||||||
cargo,
|
|
||||||
debhelper (>= 10),
|
debhelper (>= 10),
|
||||||
meson (>=0.51.0),
|
meson (>=0.43.0),
|
||||||
ninja-build,
|
|
||||||
pkg-config,
|
pkg-config,
|
||||||
libglib2.0-dev,
|
libglib2.0-dev,
|
||||||
libgtk-3-dev,
|
libgtk-3-dev,
|
||||||
libcroco3-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),
|
libwayland-dev (>= 1.16),
|
||||||
rustc,
|
rustc,
|
||||||
wayland-protocols (>= 1.14),
|
wayland-protocols (>= 1.14),
|
||||||
|
|||||||
7
debian/rules
vendored
7
debian/rules
vendored
@ -1,15 +1,8 @@
|
|||||||
#!/usr/bin/make -f
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
export CARGO_HOME = $(CURDIR)/debian/cargo
|
|
||||||
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||||
|
|
||||||
%:
|
%:
|
||||||
dh $@ --builddirectory=_build --buildsystem=meson
|
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:
|
|
||||||
rm Cargo.lock
|
|
||||||
dh $@ --builddirectory=_build --buildsystem=meson
|
|
||||||
|
|
||||||
override_dh_autoreconf:
|
override_dh_autoreconf:
|
||||||
|
|||||||
2
debian/squeekboard.lintian-overrides
vendored
2
debian/squeekboard.lintian-overrides
vendored
@ -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
|
|
||||||
@ -34,6 +34,7 @@
|
|||||||
|
|
||||||
#include "eek-renderer.h"
|
#include "eek-renderer.h"
|
||||||
#include "eek-keyboard.h"
|
#include "eek-keyboard.h"
|
||||||
|
#include "src/symbol.h"
|
||||||
|
|
||||||
#include "eek-gtk-keyboard.h"
|
#include "eek-gtk-keyboard.h"
|
||||||
|
|
||||||
@ -62,14 +63,14 @@ G_DEFINE_TYPE_WITH_PRIVATE (EekGtkKeyboard, eek_gtk_keyboard, GTK_TYPE_DRAWING_A
|
|||||||
|
|
||||||
static void on_button_pressed (struct squeek_button *button, struct squeek_view *view,
|
static void on_button_pressed (struct squeek_button *button, struct squeek_view *view,
|
||||||
EekGtkKeyboard *self);
|
EekGtkKeyboard *self);
|
||||||
static void on_button_released (const struct squeek_button *button,
|
static void on_button_released (struct squeek_button *button,
|
||||||
struct squeek_view *view,
|
struct squeek_view *view,
|
||||||
EekGtkKeyboard *self);
|
EekGtkKeyboard *self);
|
||||||
static void render_pressed_button (GtkWidget *widget, struct button_place *place);
|
static void render_pressed_button (GtkWidget *widget, struct button_place *place);
|
||||||
static void render_locked_button (GtkWidget *widget,
|
static void render_locked_button (GtkWidget *widget,
|
||||||
struct button_place *place);
|
struct button_place *place);
|
||||||
static void render_released_button (GtkWidget *widget,
|
static void render_released_button (GtkWidget *widget,
|
||||||
const struct squeek_button *button);
|
struct squeek_button *button);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
eek_gtk_keyboard_real_realize (GtkWidget *self)
|
eek_gtk_keyboard_real_realize (GtkWidget *self)
|
||||||
@ -80,8 +81,7 @@ eek_gtk_keyboard_real_realize (GtkWidget *self)
|
|||||||
GDK_KEY_RELEASE_MASK |
|
GDK_KEY_RELEASE_MASK |
|
||||||
GDK_BUTTON_PRESS_MASK |
|
GDK_BUTTON_PRESS_MASK |
|
||||||
GDK_BUTTON_RELEASE_MASK |
|
GDK_BUTTON_RELEASE_MASK |
|
||||||
GDK_BUTTON_MOTION_MASK |
|
GDK_BUTTON_MOTION_MASK);
|
||||||
GDK_TOUCH_MASK);
|
|
||||||
|
|
||||||
GTK_WIDGET_CLASS (eek_gtk_keyboard_parent_class)->realize (self);
|
GTK_WIDGET_CLASS (eek_gtk_keyboard_parent_class)->realize (self);
|
||||||
}
|
}
|
||||||
@ -109,23 +109,25 @@ eek_gtk_keyboard_real_draw (GtkWidget *self,
|
|||||||
|
|
||||||
eek_renderer_render_keyboard (priv->renderer, cr);
|
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 */
|
/* 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)) {
|
for (const GList *head = list; head; head = g_list_next (head)) {
|
||||||
struct button_place place = squeek_view_find_key(
|
struct button_place place = squeek_view_find_key(
|
||||||
view, head->data
|
view, squeek_button_get_key(head->data)
|
||||||
);
|
);
|
||||||
if (place.button)
|
if (place.button)
|
||||||
render_pressed_button (self, &place);
|
render_pressed_button (self, &place);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* redraw locked key */
|
/* redraw locked key */
|
||||||
list = priv->keyboard->locked_keys;
|
list = priv->keyboard->locked_buttons;
|
||||||
for (const GList *head = list; head; head = g_list_next (head)) {
|
for (const GList *head = list; head; head = g_list_next (head)) {
|
||||||
struct button_place place = squeek_view_find_key(
|
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)
|
if (place.button)
|
||||||
render_locked_button (self, &place);
|
render_locked_button (self, &place);
|
||||||
@ -158,78 +160,56 @@ static void depress(EekGtkKeyboard *self,
|
|||||||
struct squeek_button *button = eek_renderer_find_button_by_position (priv->renderer, view, x, y);
|
struct squeek_button *button = eek_renderer_find_button_by_position (priv->renderer, view, x, y);
|
||||||
|
|
||||||
if (button) {
|
if (button) {
|
||||||
eek_keyboard_press_key(
|
eek_keyboard_press_button(priv->keyboard, button, time);
|
||||||
priv->keyboard,
|
|
||||||
squeek_button_get_key(button),
|
|
||||||
time
|
|
||||||
);
|
|
||||||
on_button_pressed(button, view, self);
|
on_button_pressed(button, view, self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drag(EekGtkKeyboard *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);
|
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
|
||||||
struct squeek_view *view = level_keyboard_current(priv->keyboard);
|
struct squeek_view *view = level_keyboard_current(priv->keyboard);
|
||||||
struct squeek_button *button = eek_renderer_find_button_by_position (priv->renderer, view, x, y);
|
struct squeek_button *button = eek_renderer_find_button_by_position (priv->renderer, view, x, y);
|
||||||
GList *list, *head;
|
GList *list, *head;
|
||||||
|
|
||||||
list = g_list_copy(priv->keyboard->pressed_keys);
|
list = g_list_copy(priv->keyboard->pressed_buttons);
|
||||||
|
|
||||||
if (button) {
|
if (button) {
|
||||||
gboolean found = FALSE;
|
gboolean found = FALSE;
|
||||||
|
|
||||||
for (head = list; head; head = g_list_next (head)) {
|
for (head = list; head; head = g_list_next (head)) {
|
||||||
struct squeek_key *key = head->data;
|
if (head->data == button) {
|
||||||
if (squeek_button_has_key(button, key)) {
|
|
||||||
found = TRUE;
|
found = TRUE;
|
||||||
} else {
|
} else {
|
||||||
eek_keyboard_release_key(priv->keyboard, key, time);
|
eek_keyboard_release_button(priv->keyboard, head->data, time);
|
||||||
// The released handler proceeds to ignore this info...
|
on_button_released(button, view, self);
|
||||||
// let's do this for consistency nevertheless
|
|
||||||
struct button_place place = squeek_view_find_key(view, key);
|
|
||||||
on_button_released(place.button, view, self);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g_list_free (list);
|
g_list_free (list);
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
eek_keyboard_press_key(
|
eek_keyboard_press_button(priv->keyboard, button, time);
|
||||||
priv->keyboard,
|
|
||||||
squeek_button_get_key(button),
|
|
||||||
time
|
|
||||||
);
|
|
||||||
|
|
||||||
on_button_pressed(button, view, self);
|
on_button_pressed(button, view, self);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (head = list; head; head = g_list_next (head)) {
|
for (head = list; head; head = g_list_next (head)) {
|
||||||
struct squeek_key *key = head->data;
|
eek_keyboard_release_button(priv->keyboard, head->data, time);
|
||||||
eek_keyboard_release_key(priv->keyboard, key, time);
|
on_button_released(head->data, view, self);
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
g_list_free (list);
|
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);
|
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
|
||||||
|
|
||||||
struct squeek_view *view = level_keyboard_current(priv->keyboard);
|
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)) {
|
for (GList *head = list; head; head = g_list_next (head)) {
|
||||||
struct squeek_key *key = head->data;
|
struct squeek_button *button = head->data;
|
||||||
eek_keyboard_release_key(priv->keyboard, key, time);
|
eek_keyboard_release_button(priv->keyboard, button, time);
|
||||||
// The released handler proceeds to ignore this info...
|
on_button_released(button, view, self);
|
||||||
// let's do this for consistency nevertheless
|
|
||||||
struct button_place place = squeek_view_find_key(view, key);
|
|
||||||
on_button_released(place.button, view, self);
|
|
||||||
}
|
}
|
||||||
g_list_free (list);
|
g_list_free (list);
|
||||||
}
|
}
|
||||||
@ -274,28 +254,28 @@ handle_touch_event (GtkWidget *widget,
|
|||||||
EekGtkKeyboard *self = EEK_GTK_KEYBOARD (widget);
|
EekGtkKeyboard *self = EEK_GTK_KEYBOARD (widget);
|
||||||
EekGtkKeyboardPrivate *priv = eek_gtk_keyboard_get_instance_private (self);
|
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) {
|
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;
|
priv->sequence = event->sequence;
|
||||||
depress(self, event->x, event->y, event->time);
|
depress(self, event->x, event->y, event->time);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Only allow the latest touch point to be dragged. */
|
if (priv->sequence != event->sequence) {
|
||||||
if (event->type == GDK_TOUCH_UPDATE && event->sequence == priv->sequence) {
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event->type == GDK_TOUCH_UPDATE) {
|
||||||
drag(self, event->x, event->y, event->time);
|
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?
|
// 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);
|
release(self, event->time);
|
||||||
priv->sequence = NULL;
|
priv->sequence = NULL;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,20 +286,49 @@ eek_gtk_keyboard_real_unmap (GtkWidget *self)
|
|||||||
eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
|
eek_gtk_keyboard_get_instance_private (EEK_GTK_KEYBOARD (self));
|
||||||
|
|
||||||
if (priv->keyboard) {
|
if (priv->keyboard) {
|
||||||
GList *head;
|
GList *list, *head;
|
||||||
|
|
||||||
for (head = priv->keyboard->pressed_keys; head; head = g_list_next (head)) {
|
/* Make a copy of HEAD before sending "released" signal on
|
||||||
/* Unlike other places where we call this, we don't call
|
elements, so that the default handler of
|
||||||
on_button_released afterwards since we don't want to queue a
|
EekKeyboard::key-released signal can remove elements from its
|
||||||
redraw. */
|
internal copy */
|
||||||
eek_keyboard_release_key(priv->keyboard, head->data,
|
list = g_list_copy(priv->keyboard->pressed_buttons);
|
||||||
gdk_event_get_time(NULL));
|
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);
|
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
|
static void
|
||||||
eek_gtk_keyboard_set_property (GObject *object,
|
eek_gtk_keyboard_set_property (GObject *object,
|
||||||
guint prop_id,
|
guint prop_id,
|
||||||
@ -342,16 +351,17 @@ eek_gtk_keyboard_dispose (GObject *object)
|
|||||||
if (priv->renderer) {
|
if (priv->renderer) {
|
||||||
g_object_unref (priv->renderer);
|
g_object_unref (priv->renderer);
|
||||||
priv->renderer = NULL;
|
priv->renderer = NULL;
|
||||||
priv->renderer = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priv->keyboard) {
|
if (priv->keyboard) {
|
||||||
GList *head;
|
GList *list, *head;
|
||||||
|
|
||||||
for (head = priv->keyboard->pressed_keys; head; head = g_list_next (head)) {
|
list = g_list_copy(priv->keyboard->pressed_buttons);
|
||||||
eek_keyboard_release_key(priv->keyboard, head->data,
|
for (head = list; head; head = g_list_next (head)) {
|
||||||
gdk_event_get_time(NULL));
|
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;
|
priv->keyboard = NULL;
|
||||||
}
|
}
|
||||||
@ -375,6 +385,8 @@ eek_gtk_keyboard_class_init (EekGtkKeyboardClass *klass)
|
|||||||
eek_gtk_keyboard_real_button_release_event;
|
eek_gtk_keyboard_real_button_release_event;
|
||||||
widget_class->motion_notify_event =
|
widget_class->motion_notify_event =
|
||||||
eek_gtk_keyboard_real_motion_notify_event;
|
eek_gtk_keyboard_real_motion_notify_event;
|
||||||
|
widget_class->query_tooltip =
|
||||||
|
eek_gtk_keyboard_real_query_tooltip;
|
||||||
widget_class->touch_event = handle_touch_event;
|
widget_class->touch_event = handle_touch_event;
|
||||||
|
|
||||||
gobject_class->set_property = eek_gtk_keyboard_set_property;
|
gobject_class->set_property = eek_gtk_keyboard_set_property;
|
||||||
@ -455,10 +467,9 @@ render_locked_button (GtkWidget *widget, struct button_place *place)
|
|||||||
cairo_region_destroy (region);
|
cairo_region_destroy (region);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: does it really redraw the entire keyboard?
|
|
||||||
static void
|
static void
|
||||||
render_released_button (GtkWidget *widget,
|
render_released_button (GtkWidget *widget,
|
||||||
const struct squeek_button *button)
|
struct squeek_button *button)
|
||||||
{
|
{
|
||||||
(void)button;
|
(void)button;
|
||||||
EekGtkKeyboard *self = EEK_GTK_KEYBOARD (widget);
|
EekGtkKeyboard *self = EEK_GTK_KEYBOARD (widget);
|
||||||
@ -507,7 +518,7 @@ on_button_pressed (struct squeek_button *button,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_button_released (const struct squeek_button *button,
|
on_button_released (struct squeek_button *button,
|
||||||
struct squeek_view *view,
|
struct squeek_view *view,
|
||||||
EekGtkKeyboard *self)
|
EekGtkKeyboard *self)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -34,6 +34,7 @@
|
|||||||
#include "eekboard/key-emitter.h"
|
#include "eekboard/key-emitter.h"
|
||||||
#include "keymap.h"
|
#include "keymap.h"
|
||||||
#include "src/keyboard.h"
|
#include "src/keyboard.h"
|
||||||
|
#include "src/symbol.h"
|
||||||
|
|
||||||
#include "eek-keyboard.h"
|
#include "eek-keyboard.h"
|
||||||
|
|
||||||
@ -49,52 +50,76 @@ eek_modifier_key_free (EekModifierKey *modkey)
|
|||||||
g_slice_free (EekModifierKey, modkey);
|
g_slice_free (EekModifierKey, modkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
/// Updates the state of locked keys based on the key that was activated
|
||||||
eek_keyboard_set_key_locked (LevelKeyboard *keyboard,
|
/// FIXME: make independent of what the key are named,
|
||||||
struct squeek_key *key)
|
/// and instead refer to the contained symbols
|
||||||
|
static guint
|
||||||
|
set_key_states (LevelKeyboard *keyboard,
|
||||||
|
struct squeek_button *button,
|
||||||
|
guint new_level)
|
||||||
{
|
{
|
||||||
|
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);
|
EekModifierKey *modifier_key = g_slice_new (EekModifierKey);
|
||||||
modifier_key->modifiers = 0;
|
modifier_key->modifiers = 0;
|
||||||
modifier_key->key = key;
|
modifier_key->button = button;
|
||||||
keyboard->locked_keys =
|
keyboard->locked_buttons =
|
||||||
g_list_prepend (keyboard->locked_keys, modifier_key);
|
g_list_prepend (keyboard->locked_buttons, modifier_key);
|
||||||
|
squeek_key_set_locked(key, true);
|
||||||
}
|
}
|
||||||
|
if (keyboard->level == 1) {
|
||||||
/// Unlock all locked keys.
|
// Only shift is locked in this state, unlock on any key press
|
||||||
/// All locked keys will unlock at the next keypress (should be called "stuck")
|
for (GList *head = keyboard->locked_buttons; head; ) {
|
||||||
/// 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;
|
EekModifierKey *modifier_key = head->data;
|
||||||
GList *next = g_list_next (head);
|
GList *next = g_list_next (head);
|
||||||
keyboard->locked_keys =
|
keyboard->locked_buttons =
|
||||||
g_list_remove_link (keyboard->locked_keys, head);
|
g_list_remove_link (keyboard->locked_buttons, head);
|
||||||
//squeek_key_set_locked(squeek_button_get_key(modifier_key->button), false);
|
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);
|
g_list_free1 (head);
|
||||||
head = next;
|
head = next;
|
||||||
handled++;
|
|
||||||
}
|
}
|
||||||
return handled;
|
return 0;
|
||||||
|
}
|
||||||
|
return new_level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: unhardcode, parse some user information as to which key triggers which view (level)
|
||||||
static void
|
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,
|
/* The levels are: 0 Letters, 1 Upper case letters, 2 Numbers, 3 Symbols */
|
||||||
// then skip
|
guint level = keyboard->level;
|
||||||
if (unlock_keys(keyboard) == 0) {
|
/* Handle non-emitting keys */
|
||||||
squeek_layout_set_state_from_press(keyboard->layout, keyboard, key);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void eek_keyboard_press_key(LevelKeyboard *keyboard, struct squeek_key *key, guint32 timestamp) {
|
keyboard->level = set_key_states(keyboard, button, level);
|
||||||
|
|
||||||
|
eek_layout_update_layout(keyboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
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
|
// Only take action about setting level *after* the key has taken effect, i.e. on release
|
||||||
//set_level_from_press (keyboard, key);
|
//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);
|
emit_key_activated(keyboard->manager, keyboard, keycode, TRUE, timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void eek_keyboard_release_key(LevelKeyboard *keyboard,
|
void eek_keyboard_release_button(LevelKeyboard *keyboard,
|
||||||
struct squeek_key *key,
|
struct squeek_button *button,
|
||||||
guint32 timestamp) {
|
guint32 timestamp) {
|
||||||
for (GList *head = keyboard->pressed_keys; head; head = g_list_next (head)) {
|
for (GList *head = keyboard->pressed_buttons; head; head = g_list_next (head)) {
|
||||||
if (squeek_key_equal(head->data, key)) {
|
if (head->data == button) {
|
||||||
keyboard->pressed_keys = g_list_remove_link (keyboard->pressed_keys, head);
|
keyboard->pressed_buttons = g_list_remove_link (keyboard->pressed_buttons, head);
|
||||||
g_list_free1 (head);
|
g_list_free1 (head);
|
||||||
break;
|
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
|
// "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);
|
emit_key_activated(keyboard->manager, keyboard, keycode, FALSE, timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void level_keyboard_deinit(LevelKeyboard *self) {
|
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) {
|
void level_keyboard_free(LevelKeyboard *self) {
|
||||||
@ -135,18 +175,115 @@ void level_keyboard_free(LevelKeyboard *self) {
|
|||||||
g_free(self);
|
g_free(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
void level_keyboard_init(LevelKeyboard *self, struct squeek_layout *layout) {
|
void level_keyboard_init(LevelKeyboard *self) {
|
||||||
self->layout = layout;
|
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);
|
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->manager = manager;
|
||||||
|
keyboard->names = name_button_hash;
|
||||||
return keyboard;
|
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)
|
struct squeek_view *level_keyboard_current(LevelKeyboard *keyboard)
|
||||||
{
|
{
|
||||||
return squeek_layout_get_current_view(keyboard->layout);
|
return keyboard->views[keyboard->level];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,19 +36,24 @@ G_BEGIN_DECLS
|
|||||||
struct _EekModifierKey {
|
struct _EekModifierKey {
|
||||||
/*< public >*/
|
/*< public >*/
|
||||||
EekModifierType modifiers;
|
EekModifierType modifiers;
|
||||||
struct squeek_key *key;
|
struct squeek_button *button;
|
||||||
};
|
};
|
||||||
typedef struct _EekModifierKey EekModifierKey;
|
typedef struct _EekModifierKey EekModifierKey;
|
||||||
|
|
||||||
/// Keyboard state holder
|
/// Keyboard state holder
|
||||||
struct _LevelKeyboard {
|
struct _LevelKeyboard {
|
||||||
struct squeek_layout *layout;
|
struct squeek_view *views[4];
|
||||||
|
guint level;
|
||||||
struct xkb_keymap *keymap;
|
struct xkb_keymap *keymap;
|
||||||
int keymap_fd; // keymap formatted as XKB string
|
int keymap_fd; // keymap formatted as XKB string
|
||||||
size_t keymap_len; // length of the data inside keymap_fd
|
size_t keymap_len; // length of the data inside keymap_fd
|
||||||
|
GArray *outline_array;
|
||||||
|
|
||||||
GList *pressed_keys; // struct squeek_key*
|
GList *pressed_buttons; // struct squeek_button*
|
||||||
GList *locked_keys; // struct EekModifierKey*
|
GList *locked_buttons; // struct squeek_button*
|
||||||
|
|
||||||
|
/* Map button names to button objects: */
|
||||||
|
GHashTable *names;
|
||||||
|
|
||||||
guint id; // as a key to layout choices
|
guint id; // as a key to layout choices
|
||||||
|
|
||||||
@ -56,25 +61,31 @@ struct _LevelKeyboard {
|
|||||||
};
|
};
|
||||||
typedef struct _LevelKeyboard 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
|
/// Represents the path to the button within a view
|
||||||
struct button_place {
|
struct button_place {
|
||||||
const struct squeek_row *row;
|
const struct squeek_row *row;
|
||||||
const struct squeek_button *button;
|
const struct squeek_button *button;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
EekOutline *level_keyboard_get_outline
|
||||||
|
(LevelKeyboard *keyboard,
|
||||||
|
guint oref);
|
||||||
EekModifierKey *eek_modifier_key_copy
|
EekModifierKey *eek_modifier_key_copy
|
||||||
(EekModifierKey *modkey);
|
(EekModifierKey *modkey);
|
||||||
void eek_modifier_key_free
|
void eek_modifier_key_free
|
||||||
(EekModifierKey *modkey);
|
(EekModifierKey *modkey);
|
||||||
|
|
||||||
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);
|
||||||
void eek_keyboard_release_key(LevelKeyboard *keyboard, struct squeek_key *key, guint32 timestamp);
|
void eek_keyboard_release_button(LevelKeyboard *keyboard, struct squeek_button *button, guint32 timestamp);
|
||||||
|
|
||||||
gchar * eek_keyboard_get_keymap
|
gchar * eek_keyboard_get_keymap
|
||||||
(LevelKeyboard *keyboard);
|
(LevelKeyboard *keyboard);
|
||||||
|
|
||||||
struct squeek_view *level_keyboard_current(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_deinit(LevelKeyboard *self);
|
||||||
void level_keyboard_free(LevelKeyboard *self);
|
void level_keyboard_free(LevelKeyboard *self);
|
||||||
|
|
||||||
|
|||||||
65
eek/eek-keysym.c
Normal file
65
eek/eek-keysym.c
Normal 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
32
eek/eek-keysym.h
Normal 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 */
|
||||||
@ -45,3 +45,9 @@ void
|
|||||||
eek_layout_init (EekLayout *self)
|
eek_layout_init (EekLayout *self)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
eek_layout_update_layout(LevelKeyboard *keyboard)
|
||||||
|
{
|
||||||
|
squeek_view_place_contents(level_keyboard_current(keyboard), keyboard);
|
||||||
|
}
|
||||||
|
|||||||
@ -56,5 +56,14 @@ struct _EekLayoutClass
|
|||||||
|
|
||||||
GType eek_layout_get_type (void) G_GNUC_CONST;
|
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
|
G_END_DECLS
|
||||||
#endif /* EEK_LAYOUT_H */
|
#endif /* EEK_LAYOUT_H */
|
||||||
|
|||||||
@ -24,6 +24,8 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||||
|
|
||||||
|
#include "src/symbol.h"
|
||||||
|
|
||||||
#include "eek-renderer.h"
|
#include "eek-renderer.h"
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@ -129,7 +131,8 @@ create_keyboard_surface_row_callback (struct squeek_row *row,
|
|||||||
cairo_rotate (data->cr, angle * G_PI / 180);
|
cairo_rotate (data->cr, angle * G_PI / 180);
|
||||||
|
|
||||||
data->row = row;
|
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);
|
cairo_restore (data->cr);
|
||||||
}
|
}
|
||||||
@ -186,20 +189,14 @@ render_button_outline (EekRenderer *renderer,
|
|||||||
gboolean active)
|
gboolean active)
|
||||||
{
|
{
|
||||||
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
|
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);
|
EekBounds bounds = squeek_button_get_bounds(button);
|
||||||
|
|
||||||
/* Set the name of the button on the widget path, using the name obtained
|
|
||||||
from the button's symbol. */
|
|
||||||
g_autoptr (GtkWidgetPath) path = NULL;
|
|
||||||
path = gtk_widget_path_copy (gtk_style_context_get_path (priv->key_context));
|
|
||||||
const char *name = squeek_button_get_name(button);
|
|
||||||
gtk_widget_path_iter_set_name (path, -1, name);
|
|
||||||
|
|
||||||
/* Update the style context with the updated widget path. */
|
|
||||||
gtk_style_context_set_path (priv->key_context, path);
|
|
||||||
|
|
||||||
/* Set the state to take into account whether the button is active
|
|
||||||
(pressed) or normal. */
|
|
||||||
gtk_style_context_set_state(priv->key_context,
|
gtk_style_context_set_state(priv->key_context,
|
||||||
active ? GTK_STATE_FLAG_ACTIVE : GTK_STATE_FLAG_NORMAL);
|
active ? GTK_STATE_FLAG_ACTIVE : GTK_STATE_FLAG_NORMAL);
|
||||||
|
|
||||||
@ -218,12 +215,18 @@ render_button (EekRenderer *self,
|
|||||||
gboolean active)
|
gboolean active)
|
||||||
{
|
{
|
||||||
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
|
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
|
||||||
|
EekOutline *outline;
|
||||||
cairo_surface_t *outline_surface;
|
cairo_surface_t *outline_surface;
|
||||||
GHashTable *outline_surface_cache;
|
GHashTable *outline_surface_cache;
|
||||||
PangoLayout *layout;
|
PangoLayout *layout;
|
||||||
PangoRectangle extents = { 0, };
|
PangoRectangle extents = { 0, };
|
||||||
EekColor foreground;
|
EekColor foreground;
|
||||||
|
|
||||||
|
guint oref = squeek_button_get_oref (place->button);
|
||||||
|
outline = level_keyboard_get_outline (priv->keyboard, oref);
|
||||||
|
if (outline == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
/* render outline */
|
/* render outline */
|
||||||
EekBounds bounds = squeek_button_get_bounds(place->button);
|
EekBounds bounds = squeek_button_get_bounds(place->button);
|
||||||
|
|
||||||
@ -232,7 +235,7 @@ render_button (EekRenderer *self,
|
|||||||
else
|
else
|
||||||
outline_surface_cache = priv->outline_surface_cache;
|
outline_surface_cache = priv->outline_surface_cache;
|
||||||
|
|
||||||
outline_surface = g_hash_table_lookup (outline_surface_cache, place->button);
|
outline_surface = g_hash_table_lookup (outline_surface_cache, outline);
|
||||||
if (!outline_surface) {
|
if (!outline_surface) {
|
||||||
cairo_t *cr;
|
cairo_t *cr;
|
||||||
|
|
||||||
@ -256,7 +259,7 @@ render_button (EekRenderer *self,
|
|||||||
cairo_destroy (cr);
|
cairo_destroy (cr);
|
||||||
|
|
||||||
g_hash_table_insert (outline_surface_cache,
|
g_hash_table_insert (outline_surface_cache,
|
||||||
(gpointer)place->button,
|
outline,
|
||||||
outline_surface);
|
outline_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,12 +268,16 @@ render_button (EekRenderer *self,
|
|||||||
|
|
||||||
eek_renderer_get_foreground_color (self, priv->key_context, &foreground);
|
eek_renderer_get_foreground_color (self, priv->key_context, &foreground);
|
||||||
/* render icon (if any) */
|
/* 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;
|
gint scale = priv->scale_factor;
|
||||||
cairo_surface_t *icon_surface =
|
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);
|
scale);
|
||||||
if (icon_surface) {
|
if (icon_surface) {
|
||||||
gint width = cairo_image_surface_get_width (icon_surface);
|
gint width = cairo_image_surface_get_width (icon_surface);
|
||||||
@ -365,16 +372,18 @@ eek_renderer_real_render_button_label (EekRenderer *self,
|
|||||||
{
|
{
|
||||||
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
|
EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
|
||||||
|
|
||||||
const gchar *label = squeek_button_get_label(button);
|
const gchar *label;
|
||||||
|
|
||||||
if (!label) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PangoFontDescription *font;
|
PangoFontDescription *font;
|
||||||
PangoLayoutLine *line;
|
PangoLayoutLine *line;
|
||||||
gdouble scale;
|
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) {
|
if (!priv->font) {
|
||||||
const PangoFontDescription *base_font;
|
const PangoFontDescription *base_font;
|
||||||
@ -472,7 +481,7 @@ eek_renderer_real_render_keyboard (EekRenderer *self,
|
|||||||
cairo_get_target (cr), 0, 0,
|
cairo_get_target (cr), 0, 0,
|
||||||
priv->allocation_width, priv->allocation_height);
|
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);
|
cairo_set_source_surface (cr, priv->keyboard_surface, 0.0, 0.0);
|
||||||
source = cairo_get_source (cr);
|
source = cairo_get_source (cr);
|
||||||
@ -970,7 +979,6 @@ eek_renderer_find_button_by_position (EekRenderer *renderer,
|
|||||||
|
|
||||||
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
|
EekRendererPrivate *priv = eek_renderer_get_instance_private (renderer);
|
||||||
|
|
||||||
/* Transform from widget coordinates to keyboard coordinates */
|
|
||||||
EekPoint point = {
|
EekPoint point = {
|
||||||
.x = (x - priv->origin_x)/priv->scale,
|
.x = (x - priv->origin_x)/priv->scale,
|
||||||
.y = (y - priv->origin_y)/priv->scale,
|
.y = (y - priv->origin_y)/priv->scale,
|
||||||
|
|||||||
56
eek/eek-section.c
Normal file
56
eek/eek-section.c
Normal 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
34
eek/eek-section.h
Normal 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 */
|
||||||
1399
eek/eek-xml-layout.c
1399
eek/eek-xml-layout.c
File diff suppressed because it is too large
Load Diff
@ -23,11 +23,47 @@
|
|||||||
#ifndef EEK_XML_LAYOUT_H
|
#ifndef EEK_XML_LAYOUT_H
|
||||||
#define EEK_XML_LAYOUT_H 1
|
#define EEK_XML_LAYOUT_H 1
|
||||||
|
|
||||||
#include "eek-types.h"
|
#include <gio/gio.h>
|
||||||
|
#include "eek-layout.h"
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
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 *
|
LevelKeyboard *
|
||||||
eek_xml_layout_real_create_keyboard (const char *keyboard_type,
|
eek_xml_layout_real_create_keyboard (EekLayout *self,
|
||||||
EekboardContextService *manager);
|
EekboardContextService *manager);
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
#endif /* EEK_XML_LAYOUT_H */
|
#endif /* EEK_XML_LAYOUT_H */
|
||||||
|
|||||||
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include "eek-keyboard.h"
|
#include "eek-keyboard.h"
|
||||||
#include "eek-layout.h"
|
#include "eek-layout.h"
|
||||||
|
#include "eek-keysym.h"
|
||||||
|
|
||||||
void eek_init (void);
|
void eek_init (void);
|
||||||
|
|
||||||
|
|||||||
50
eek/gen-keysym-entries.py
Executable file
50
eek/gen-keysym-entries.py
Executable 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")
|
||||||
31
eek/keymap.h
31
eek/keymap.h
@ -6,3 +6,34 @@ squeek_keymap_get_entries_for_keyval (struct xkb_keymap *xkb_keymap,
|
|||||||
guint keyval,
|
guint keyval,
|
||||||
GdkKeymapKey **keys,
|
GdkKeymapKey **keys,
|
||||||
guint *n_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\
|
||||||
|
};";
|
||||||
|
|||||||
@ -6,3 +6,15 @@ enum_headers = [
|
|||||||
|
|
||||||
enums = gnome.mkenums_simple('eek-enumtypes', sources: 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'),
|
||||||
|
]
|
||||||
|
|||||||
1306
eek/xkeysym-keysym-entries.txt
Normal file
1306
eek/xkeysym-keysym-entries.txt
Normal file
File diff suppressed because it is too large
Load Diff
@ -29,7 +29,6 @@
|
|||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#define _XOPEN_SOURCE 500
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/random.h> // TODO: this is Linux-specific
|
#include <sys/random.h> // TODO: this is Linux-specific
|
||||||
@ -90,17 +89,66 @@ static LevelKeyboard *
|
|||||||
eekboard_context_service_real_create_keyboard (EekboardContextService *self,
|
eekboard_context_service_real_create_keyboard (EekboardContextService *self,
|
||||||
const gchar *keyboard_type)
|
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) {
|
if (!keyboard) {
|
||||||
g_error("Failed to create a keyboard");
|
g_error("Failed to create a keyboard");
|
||||||
}
|
}
|
||||||
|
g_object_unref (layout);
|
||||||
|
|
||||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
g_error("No context created");
|
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,
|
struct xkb_keymap *keymap = xkb_keymap_new_from_string(context, keymap_str,
|
||||||
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
@ -108,6 +156,8 @@ eekboard_context_service_real_create_keyboard (EekboardContextService *self,
|
|||||||
if (!keymap)
|
if (!keymap)
|
||||||
g_error("Bad keymap:\n%s", keymap_str);
|
g_error("Bad keymap:\n%s", keymap_str);
|
||||||
|
|
||||||
|
free(keymap_str);
|
||||||
|
|
||||||
xkb_context_unref(context);
|
xkb_context_unref(context);
|
||||||
keyboard->keymap = keymap;
|
keyboard->keymap = keymap;
|
||||||
|
|
||||||
@ -117,7 +167,7 @@ eekboard_context_service_real_create_keyboard (EekboardContextService *self,
|
|||||||
g_autofree char *path = strdup("/eek_keymap-XXXXXX");
|
g_autofree char *path = strdup("/eek_keymap-XXXXXX");
|
||||||
char *r = &path[strlen(path) - 6];
|
char *r = &path[strlen(path) - 6];
|
||||||
getrandom(r, 6, GRND_NONBLOCK);
|
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] & 0b1111111) | 0b1000000; // A-z
|
||||||
r[i] = r[i] > 'z' ? '?' : r[i]; // The randomizer doesn't need to be good...
|
r[i] = r[i] > 'z' ? '?' : r[i]; // The randomizer doesn't need to be good...
|
||||||
}
|
}
|
||||||
@ -135,8 +185,9 @@ eekboard_context_service_real_create_keyboard (EekboardContextService *self,
|
|||||||
if ((void*)ptr == (void*)-1) {
|
if ((void*)ptr == (void*)-1) {
|
||||||
g_error("Failed to set up mmap");
|
g_error("Failed to set up mmap");
|
||||||
}
|
}
|
||||||
strncpy(ptr, keymap_str, keyboard->keymap_len);
|
strcpy(ptr, keymap_str);
|
||||||
munmap(ptr, keyboard->keymap_len);
|
munmap(ptr, keyboard->keymap_len);
|
||||||
|
free(keymap_str);
|
||||||
|
|
||||||
return keyboard;
|
return keyboard;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -90,9 +90,12 @@ send_fake_key (SeatEmitter *emitter,
|
|||||||
gboolean pressed,
|
gboolean pressed,
|
||||||
uint32_t timestamp)
|
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);
|
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
|
void
|
||||||
|
|||||||
@ -1,29 +0,0 @@
|
|||||||
extern crate rs;
|
|
||||||
extern crate xkbcommon;
|
|
||||||
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
use rs::data::{ load_layout_from_resource, LoadError };
|
|
||||||
|
|
||||||
use xkbcommon::xkb;
|
|
||||||
|
|
||||||
|
|
||||||
fn check_layout(name: &str) {
|
|
||||||
let layout = load_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());
|
|
||||||
}
|
|
||||||
@ -1,9 +1,9 @@
|
|||||||
project(
|
project(
|
||||||
'squeekboard',
|
'squeekboard',
|
||||||
'c', 'rust',
|
'c', 'rust',
|
||||||
version: '1.2.0',
|
version: '1.0.10',
|
||||||
license: 'GPLv3',
|
license: 'GPLv3',
|
||||||
meson_version: '>=0.51.0',
|
meson_version: '>=0.49.0',
|
||||||
default_options: [
|
default_options: [
|
||||||
'warning_level=1',
|
'warning_level=1',
|
||||||
'buildtype=debugoptimized',
|
'buildtype=debugoptimized',
|
||||||
@ -53,9 +53,6 @@ summary = [
|
|||||||
]
|
]
|
||||||
message('\n'.join(summary))
|
message('\n'.join(summary))
|
||||||
|
|
||||||
cargo = find_program('cargo')
|
|
||||||
cargo_script = find_program('cargo.sh')
|
|
||||||
|
|
||||||
subdir('data')
|
subdir('data')
|
||||||
subdir('protocols')
|
subdir('protocols')
|
||||||
subdir('eek')
|
subdir('eek')
|
||||||
|
|||||||
1350
src/bitflags.rs
Normal file
1350
src/bitflags.rs
Normal file
File diff suppressed because it is too large
Load Diff
640
src/data.rs
640
src/data.rs
@ -1,640 +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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_layout_from_resource(
|
|
||||||
name: &str
|
|
||||||
) -> Result<Layout, LoadError> {
|
|
||||||
let data = resources::get_keyboard(name)
|
|
||||||
.ok_or(LoadError::MissingResource)?;
|
|
||||||
serde_yaml::from_str(data)
|
|
||||||
.map_err(|e| LoadError::BadResource(e))
|
|
||||||
}
|
|
||||||
|
|
||||||
enum DataSource {
|
|
||||||
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
|
|
||||||
) -> (Result<::layout::Layout, LoadError>, DataSource) {
|
|
||||||
let path = env::var_os("SQUEEKBOARD_KEYBOARDSDIR")
|
|
||||||
.map(PathBuf::from)
|
|
||||||
.or_else(|| xdg::data_path("squeekboard/keyboards"))
|
|
||||||
.map(|path| path.join(name).with_extension("yaml"));
|
|
||||||
|
|
||||||
let (layout, source) = match path {
|
|
||||||
Some(path) => {(
|
|
||||||
Layout::from_yaml_stream(path.clone())
|
|
||||||
.map_err(|e| LoadError::BadData(e)),
|
|
||||||
DataSource::File(path)
|
|
||||||
)},
|
|
||||||
None => {(
|
|
||||||
load_layout_from_resource(name),
|
|
||||||
DataSource::Resource(name.into())
|
|
||||||
)},
|
|
||||||
};
|
|
||||||
|
|
||||||
let layout = layout.and_then(
|
|
||||||
|layout| layout.build().map_err(LoadError::BadKeyMap)
|
|
||||||
);
|
|
||||||
|
|
||||||
(layout, source)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_layout_with_fallback(
|
|
||||||
name: &str
|
|
||||||
) -> ::layout::Layout {
|
|
||||||
let (layout, source) = load_layout(name);
|
|
||||||
let (layout, source) = match (layout, source) {
|
|
||||||
(Err(e), source) => {
|
|
||||||
eprintln!(
|
|
||||||
"Failed to load layout from {}: {}, using fallback",
|
|
||||||
source, e
|
|
||||||
);
|
|
||||||
load_layout(FALLBACK_LAYOUT_NAME)
|
|
||||||
},
|
|
||||||
res => res,
|
|
||||||
};
|
|
||||||
|
|
||||||
let (layout, source) = match (layout, source) {
|
|
||||||
(Err(e), source) => {
|
|
||||||
eprintln!(
|
|
||||||
"Failed to load fallback layout from {}: {}, using hardcoded",
|
|
||||||
source, e
|
|
||||||
);
|
|
||||||
(
|
|
||||||
load_layout_from_resource(FALLBACK_LAYOUT_NAME)
|
|
||||||
.and_then(
|
|
||||||
|layout| layout.build().map_err(LoadError::BadKeyMap)
|
|
||||||
),
|
|
||||||
DataSource::Resource(FALLBACK_LAYOUT_NAME.into()),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
res => res,
|
|
||||||
};
|
|
||||||
|
|
||||||
match (layout, source) {
|
|
||||||
(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 {
|
|
||||||
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 {
|
|
||||||
corner_radius: f64,
|
|
||||||
bounds: Bounds,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
Yaml(serde_yaml::Error),
|
|
||||||
Io(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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Layout {
|
|
||||||
fn from_yaml_stream(path: PathBuf) -> Result<Layout, Error> {
|
|
||||||
let infile = BufReader::new(
|
|
||||||
fs::OpenOptions::new()
|
|
||||||
.read(true)
|
|
||||||
.open(&path)
|
|
||||||
.map_err(Error::Io)?
|
|
||||||
);
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
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 {
|
|
||||||
corner_radius: 0f64,
|
|
||||||
bounds: Bounds { x: 0f64, y: 0f64, width: 1f64, height: 1f64 },
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
::layout::Button {
|
|
||||||
name: cname,
|
|
||||||
// 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,
|
|
||||||
},
|
|
||||||
corner_radius: outline.corner_radius,
|
|
||||||
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_yaml_stream(
|
|
||||||
PathBuf::from("tests/layout.yaml")
|
|
||||||
).unwrap(),
|
|
||||||
Layout {
|
|
||||||
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 {
|
|
||||||
corner_radius: 1f64,
|
|
||||||
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_yaml_stream(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_yaml_stream(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_yaml_stream(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_yaml_stream(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!(load_layout_from_resource(FALLBACK_LAYOUT_NAME)
|
|
||||||
.and_then(|layout| layout.build().map_err(LoadError::BadKeyMap))
|
|
||||||
.is_ok()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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())),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -5,10 +5,6 @@
|
|||||||
/* Adapted from https://github.com/notriddle/rust-float-ord revision e995165f
|
/* Adapted from https://github.com/notriddle/rust-float-ord revision e995165f
|
||||||
* maintained by Michael Howell <michael@notriddle.com>
|
* maintained by Michael Howell <michael@notriddle.com>
|
||||||
* licensed under MIT / Apache-2.0 licenses
|
* 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;
|
extern crate core;
|
||||||
@ -71,7 +67,6 @@ float_ord_impl!(f64, u64, 64);
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use rs::float_ord;
|
|
||||||
/// let mut v = [-5.0, 4.0, 1.0, -3.0, 2.0];
|
/// let mut v = [-5.0, 4.0, 1.0, -3.0, 2.0];
|
||||||
///
|
///
|
||||||
/// float_ord::sort(&mut v);
|
/// float_ord::sort(&mut v);
|
||||||
|
|||||||
@ -4,7 +4,6 @@ use std::num::Wrapping;
|
|||||||
use std::string::String;
|
use std::string::String;
|
||||||
|
|
||||||
use super::bitflags;
|
use super::bitflags;
|
||||||
use ::util::c::into_cstring;
|
|
||||||
|
|
||||||
// Traits
|
// Traits
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
@ -14,8 +13,15 @@ use std::convert::TryFrom;
|
|||||||
pub mod c {
|
pub mod c {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
use std::ffi::CStr;
|
||||||
use std::os::raw::{c_char, c_void};
|
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
|
// The following defined in C
|
||||||
|
|
||||||
/// struct zwp_input_method_v2*
|
/// struct zwp_input_method_v2*
|
||||||
@ -85,9 +91,7 @@ pub mod c {
|
|||||||
{
|
{
|
||||||
let imservice = check_imservice(imservice, im).unwrap();
|
let imservice = check_imservice(imservice, im).unwrap();
|
||||||
imservice.pending = IMProtocolState {
|
imservice.pending = IMProtocolState {
|
||||||
surrounding_text: into_cstring(text)
|
surrounding_text: into_cstring(text).expect("Received invalid string"),
|
||||||
.expect("Received invalid string")
|
|
||||||
.expect("Received null string"),
|
|
||||||
surrounding_cursor: cursor,
|
surrounding_cursor: cursor,
|
||||||
..imservice.pending.clone()
|
..imservice.pending.clone()
|
||||||
};
|
};
|
||||||
@ -226,8 +230,7 @@ bitflags!{
|
|||||||
/// Map to `text_input_unstable_v3.content_purpose` values
|
/// Map to `text_input_unstable_v3.content_purpose` values
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use rs::imservice::ContentPurpose;
|
/// assert_eq!(ContentPurpose::Alpha as u32, 0);
|
||||||
/// assert_eq!(ContentPurpose::Alpha as u32, 1);
|
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ContentPurpose {
|
pub enum ContentPurpose {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#ifndef __KEYBOARD_H
|
#ifndef __KEYBOARD_H
|
||||||
#define __KEYBOARD_H
|
#define __KYBOARD_H
|
||||||
|
|
||||||
#include "stdbool.h"
|
#include "stdbool.h"
|
||||||
#include "inttypes.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);
|
void squeek_key_set_locked(struct squeek_key *key, uint32_t pressed);
|
||||||
uint32_t squeek_key_get_keycode(struct squeek_key *key);
|
uint32_t squeek_key_get_keycode(struct squeek_key *key);
|
||||||
void squeek_key_set_keycode(struct squeek_key *key, uint32_t keycode);
|
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);
|
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);
|
const char* squeek_key_to_keymap_entry(const char *key_name, struct squeek_key *key);
|
||||||
|
|||||||
345
src/keyboard.rs
345
src/keyboard.rs
@ -1,41 +1,90 @@
|
|||||||
/*! State of the emulated keyboard and keys */
|
use std::vec::Vec;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use super::symbol;
|
||||||
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 };
|
|
||||||
|
|
||||||
/// Gathers stuff defined in C or called by C
|
/// Gathers stuff defined in C or called by C
|
||||||
pub mod c {
|
pub mod c {
|
||||||
use super::*;
|
use super::*;
|
||||||
use ::util::c;
|
use ::util::c::{ as_cstr, into_cstring };
|
||||||
use ::util::c::as_cstr;
|
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::os::raw::c_char;
|
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
|
// 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]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_key_free(key: CKeyState) {
|
fn squeek_key_free(key: CKeyState) {
|
||||||
unsafe { key.unwrap() }; // reference dropped
|
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -48,9 +97,7 @@ pub mod c {
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_key_set_pressed(key: CKeyState, pressed: u32) {
|
fn squeek_key_set_pressed(key: CKeyState, pressed: u32) {
|
||||||
let key = key.clone_ref();
|
key.borrow_mut(|key| key.pressed = pressed != 0);
|
||||||
let mut key = key.borrow_mut();
|
|
||||||
key.pressed = pressed != 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -62,15 +109,109 @@ pub mod c {
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_key_set_locked(key: CKeyState, locked: u32) {
|
fn squeek_key_set_locked(key: CKeyState, locked: u32) {
|
||||||
let key = key.clone_ref();
|
key.borrow_mut(|key| key.locked = locked != 0);
|
||||||
let mut key = key.borrow_mut();
|
|
||||||
key.locked = locked != 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_key_get_keycode(key: CKeyState) -> u32 {
|
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]
|
#[no_mangle]
|
||||||
@ -84,14 +225,20 @@ pub mod c {
|
|||||||
.to_str()
|
.to_str()
|
||||||
.expect("Bad key name");
|
.expect("Bad key name");
|
||||||
|
|
||||||
let symbol_name = match key.to_owned().symbol.action {
|
let symbol_name = match key.to_owned().symbol {
|
||||||
Action::Submit { text: Some(text), .. } => {
|
Some(ref symbol) => match &symbol.action {
|
||||||
|
symbol::Action::Submit { text: Some(text), .. } => {
|
||||||
Some(
|
Some(
|
||||||
text.clone()
|
text.clone()
|
||||||
.into_string().expect("Bad symbol")
|
.into_string().expect("Bad symbol")
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
eprintln!("Key {} has no symbol", key_name);
|
||||||
|
None
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let inner = match symbol_name {
|
let inner = match symbol_name {
|
||||||
@ -115,14 +262,20 @@ pub mod c {
|
|||||||
.to_str()
|
.to_str()
|
||||||
.expect("Bad key name");
|
.expect("Bad key name");
|
||||||
|
|
||||||
let symbol_name = match key.to_owned().symbol.action {
|
let symbol_name = match key.to_owned().symbol {
|
||||||
Action::Submit { text: Some(text), .. } => {
|
Some(ref symbol) => match &symbol.action {
|
||||||
|
symbol::Action::Submit { text: Some(text), .. } => {
|
||||||
Some(
|
Some(
|
||||||
text.clone()
|
text.clone()
|
||||||
.into_string().expect("Bad symbol text")
|
.into_string().expect("Bad symbol")
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
_ => None
|
_ => None
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
eprintln!("Key {} has no symbol", key_name);
|
||||||
|
None
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let inner = match symbol_name {
|
let inner = match symbol_name {
|
||||||
@ -134,126 +287,14 @@ pub mod c {
|
|||||||
.expect("Couldn't convert string")
|
.expect("Couldn't convert string")
|
||||||
.into_raw()
|
.into_raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct KeyState {
|
pub struct KeyState {
|
||||||
pub pressed: bool,
|
pub pressed: bool,
|
||||||
pub locked: bool,
|
pub locked: bool,
|
||||||
pub keycode: Option<u32>,
|
pub keycode: u32,
|
||||||
pub symbol: Symbol,
|
// TODO: remove the optionality of a symbol
|
||||||
}
|
pub symbol: Option<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)
|
|
||||||
}
|
}
|
||||||
|
|||||||
40
src/layout.h
40
src/layout.h
@ -9,11 +9,17 @@
|
|||||||
struct squeek_button;
|
struct squeek_button;
|
||||||
struct squeek_row;
|
struct squeek_row;
|
||||||
struct squeek_view;
|
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*);
|
int32_t squeek_row_get_angle(const struct squeek_row*);
|
||||||
|
|
||||||
EekBounds squeek_row_get_bounds(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);
|
uint32_t squeek_row_contains(struct squeek_row*, struct squeek_button *button);
|
||||||
|
|
||||||
@ -27,19 +33,34 @@ void squeek_row_foreach(struct squeek_row*,
|
|||||||
|
|
||||||
void squeek_row_free(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*);
|
uint32_t squeek_button_get_oref(const struct squeek_button*);
|
||||||
EekBounds squeek_button_get_bounds(const struct squeek_button*);
|
EekBounds squeek_button_get_bounds(const struct squeek_button*);
|
||||||
const char *squeek_button_get_label(const struct squeek_button*);
|
void squeek_button_set_bounds(struct squeek_button* button, EekBounds bounds);
|
||||||
const char *squeek_button_get_icon_name(const struct squeek_button*);
|
|
||||||
const char *squeek_button_get_name(const struct squeek_button*);
|
|
||||||
|
|
||||||
|
struct squeek_symbol *squeek_button_get_symbol (
|
||||||
|
const struct squeek_button *button);
|
||||||
struct squeek_key *squeek_button_get_key(const struct squeek_button*);
|
struct squeek_key *squeek_button_get_key(const struct squeek_button*);
|
||||||
uint32_t *squeek_button_has_key(const struct squeek_button* button,
|
uint32_t *squeek_button_has_key(const struct squeek_button* button,
|
||||||
const struct squeek_key *key);
|
const struct squeek_key *key);
|
||||||
void squeek_button_print(const struct squeek_button* button);
|
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*);
|
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);
|
typedef void (*RowCallback) (struct squeek_row *row, gpointer user_data);
|
||||||
void squeek_view_foreach(struct squeek_view*,
|
void squeek_view_foreach(struct squeek_view*,
|
||||||
@ -49,16 +70,9 @@ void squeek_view_foreach(struct squeek_view*,
|
|||||||
struct squeek_row *squeek_view_get_row(struct squeek_view *view,
|
struct squeek_row *squeek_view_get_row(struct squeek_view *view,
|
||||||
struct squeek_button *button);
|
struct squeek_button *button);
|
||||||
|
|
||||||
struct squeek_button *squeek_view_find_button_by_position(struct squeek_view *view, EekPoint point);
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
squeek_layout_place_contents(struct squeek_layout*);
|
squeek_view_place_contents(struct squeek_view *view, LevelKeyboard *keyboard);
|
||||||
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);
|
|
||||||
|
|
||||||
|
struct squeek_button *squeek_view_find_button_by_position(struct squeek_view *view, EekPoint point);
|
||||||
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*);
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
461
src/layout.rs
461
src/layout.rs
@ -1,4 +1,4 @@
|
|||||||
/*!
|
/**
|
||||||
* Layout-related data.
|
* Layout-related data.
|
||||||
*
|
*
|
||||||
* The `View` contains `Row`s and each `Row` contains `Button`s.
|
* The `View` contains `Row`s and each `Row` contains `Button`s.
|
||||||
@ -18,8 +18,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::ffi::CString;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
@ -31,8 +29,7 @@ use ::symbol::*;
|
|||||||
pub mod c {
|
pub mod c {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use std::ffi::CStr;
|
use std::os::raw::c_void;
|
||||||
use std::os::raw::{ c_char, c_void };
|
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
// The following defined in C
|
// The following defined in C
|
||||||
@ -63,11 +60,26 @@ pub mod c {
|
|||||||
pub height: f64
|
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 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);
|
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
|
// 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]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_view_get_bounds(view: *const ::layout::View) -> Bounds {
|
fn squeek_view_get_bounds(view: *const ::layout::View) -> Bounds {
|
||||||
@ -76,6 +88,30 @@ pub mod c {
|
|||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
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(
|
fn squeek_view_foreach(
|
||||||
view: *mut ::layout::View,
|
view: *mut ::layout::View,
|
||||||
callback: RowCallback,
|
callback: RowCallback,
|
||||||
@ -88,6 +124,68 @@ pub mod c {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[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]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_row_get_angle(row: *const ::layout::Row) -> i32 {
|
fn squeek_row_get_angle(row: *const ::layout::Row) -> i32 {
|
||||||
@ -133,11 +231,57 @@ pub mod c {
|
|||||||
unsafe { Box::from_raw(row) }; // gets dropped
|
unsafe { Box::from_raw(row) }; // gets dropped
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C"
|
||||||
|
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]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_button_get_bounds(button: *const ::layout::Button) -> Bounds {
|
fn squeek_button_get_bounds(button: *const ::layout::Button) -> Bounds {
|
||||||
let button = unsafe { &*button };
|
let button = unsafe { &*button };
|
||||||
button.bounds.clone()
|
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
|
/// Borrow a new reference to key state. Doesn't need freeing
|
||||||
@ -158,41 +302,10 @@ pub mod c {
|
|||||||
) -> *const Symbol {
|
) -> *const Symbol {
|
||||||
let button = unsafe { &*button };
|
let button = unsafe { &*button };
|
||||||
let state = button.state.borrow();
|
let state = button.state.borrow();
|
||||||
&state.symbol as *const Symbol
|
match state.symbol {
|
||||||
|
Some(ref symbol) => symbol as *const Symbol,
|
||||||
|
None => ptr::null(),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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]
|
#[no_mangle]
|
||||||
@ -202,8 +315,9 @@ pub mod c {
|
|||||||
state: ::keyboard::c::CKeyState,
|
state: ::keyboard::c::CKeyState,
|
||||||
) -> u32 {
|
) -> u32 {
|
||||||
let button = unsafe { &*button };
|
let button = unsafe { &*button };
|
||||||
let state = state.clone_ref();
|
let state = state.unwrap();
|
||||||
let equal = Rc::ptr_eq(&button.state, &state);
|
let equal = Rc::ptr_eq(&button.state, &state);
|
||||||
|
Rc::into_raw(state); // Prevent dropping
|
||||||
equal as u32
|
equal as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,34 +328,13 @@ pub mod c {
|
|||||||
println!("{:?}", button);
|
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
|
/// Entry points for more complex procedures and algoithms which span multiple modules
|
||||||
pub mod procedures {
|
pub mod procedures {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct LevelKeyboard(*const c_void);
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub struct ButtonPlace {
|
pub struct ButtonPlace {
|
||||||
@ -249,11 +342,13 @@ pub mod c {
|
|||||||
button: *const Button,
|
button: *const Button,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct LevelKeyboard(*const c_void);
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
fn eek_get_outline_size(
|
||||||
|
keyboard: *const LevelKeyboard,
|
||||||
|
outline: u32
|
||||||
|
) -> Bounds;
|
||||||
|
|
||||||
/// Checks if point falls within bounds,
|
/// Checks if point falls within bounds,
|
||||||
/// which are relative to origin and rotated by angle (I think)
|
/// which are relative to origin and rotated by angle (I think)
|
||||||
pub fn eek_are_bounds_inside (bounds: Bounds,
|
pub fn eek_are_bounds_inside (bounds: Bounds,
|
||||||
@ -261,53 +356,15 @@ pub mod c {
|
|||||||
origin: Point,
|
origin: Point,
|
||||||
angle: i32
|
angle: i32
|
||||||
) -> u32;
|
) -> u32;
|
||||||
|
|
||||||
pub fn eek_keyboard_set_key_locked(
|
|
||||||
keyboard: *mut LevelKeyboard,
|
|
||||||
key: ::keyboard::c::CKeyState,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
fn squeek_buttons_get_outlines(
|
||||||
pub extern "C"
|
buttons: &Vec<Box<Button>>,
|
||||||
fn squeek_layout_set_state_from_press(
|
keyboard: *const LevelKeyboard,
|
||||||
layout: *mut Layout,
|
) -> Vec<Bounds> {
|
||||||
keyboard: *mut LevelKeyboard,
|
buttons.iter().map(|button| {
|
||||||
key: ::keyboard::c::CKeyState,
|
unsafe { eek_get_outline_size(keyboard, button.oref.0) }
|
||||||
) {
|
}).collect()
|
||||||
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)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Places each button in order, starting from 0 on the left,
|
/// Places each button in order, starting from 0 on the left,
|
||||||
@ -318,20 +375,18 @@ pub mod c {
|
|||||||
/// Sets button and row sizes according to their contents.
|
/// Sets button and row sizes according to their contents.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C"
|
pub extern "C"
|
||||||
fn squeek_layout_place_contents(
|
fn squeek_view_place_contents(
|
||||||
layout: *mut Layout,
|
view: *mut ::layout::View,
|
||||||
|
keyboard: *const LevelKeyboard, // source of outlines
|
||||||
) {
|
) {
|
||||||
let layout = unsafe { &mut *layout };
|
let view = unsafe { &mut *view };
|
||||||
for view in layout.views.values_mut() {
|
|
||||||
let sizes: Vec<Vec<Bounds>> = view.rows.iter().map(|row| {
|
let sizes: Vec<Vec<Bounds>> = view.rows.iter().map(|row|
|
||||||
row.buttons.iter()
|
squeek_buttons_get_outlines(&row.buttons, keyboard)
|
||||||
.map(|button| button.bounds.clone())
|
).collect();
|
||||||
.collect()
|
|
||||||
}).collect();
|
|
||||||
|
|
||||||
view.place_buttons_with_sizes(sizes);
|
view.place_buttons_with_sizes(sizes);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn squeek_row_contains(row: &Row, needle: *const Button) -> bool {
|
fn squeek_row_contains(row: &Row, needle: *const Button) -> bool {
|
||||||
row.buttons.iter().position(
|
row.buttons.iter().position(
|
||||||
@ -363,10 +418,13 @@ pub mod c {
|
|||||||
needle: ::keyboard::c::CKeyState,
|
needle: ::keyboard::c::CKeyState,
|
||||||
) -> ButtonPlace {
|
) -> ButtonPlace {
|
||||||
let view = unsafe { &*view };
|
let view = unsafe { &*view };
|
||||||
let state = needle.clone_ref();
|
let state = needle.unwrap();
|
||||||
|
|
||||||
let paths = ::layout::procedures::find_key_paths(view, &state);
|
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
|
// Can only return 1 entry back to C
|
||||||
let (row, button) = match paths.get(0) {
|
let (row, button) = match paths.get(0) {
|
||||||
Some((row, button)) => (
|
Some((row, button)) => (
|
||||||
@ -396,36 +454,35 @@ pub mod c {
|
|||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use super::super::test::*;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn row_has_button() {
|
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);
|
let mut row = Row::new(0);
|
||||||
row.buttons.push(button);
|
let button = squeek_row_create_button(&mut row as *mut Row, 0, 0);
|
||||||
assert_eq!(squeek_row_contains(&row, button_ptr), true);
|
assert_eq!(squeek_row_contains(&row, button), true);
|
||||||
let shared_button = make_button_with_state(
|
let shared_button = squeek_row_create_button_with_state(
|
||||||
"test2".into(),
|
&mut row as *mut Row,
|
||||||
state
|
button
|
||||||
);
|
);
|
||||||
let shared_button_ptr = button_as_raw(&shared_button);
|
assert_eq!(squeek_row_contains(&row, shared_button), true);
|
||||||
row.buttons.push(shared_button);
|
|
||||||
assert_eq!(squeek_row_contains(&row, shared_button_ptr), true);
|
|
||||||
let row = Row::new(0);
|
let row = Row::new(0);
|
||||||
assert_eq!(squeek_row_contains(&row, button_ptr), false);
|
assert_eq!(squeek_row_contains(&row, button), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn view_has_button() {
|
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 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 button_ptr = button.as_ref() as *const Button;
|
||||||
|
|
||||||
let row = Box::new(Row {
|
let row = Box::new(Row {
|
||||||
@ -479,63 +536,17 @@ pub mod c {
|
|||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
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(),
|
|
||||||
corner_radius: 0f64,
|
|
||||||
bounds: c::Bounds {
|
|
||||||
x: 0f64, y: 0f64, width: 0f64, height: 0f64
|
|
||||||
},
|
|
||||||
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]
|
#[test]
|
||||||
fn button_has_key() {
|
fn button_has_key() {
|
||||||
let state = make_state();
|
let button = squeek_button_new(0, 0);
|
||||||
let button = make_button_with_state("1".into(), state.clone());
|
let state = squeek_button_get_key(button);
|
||||||
assert_eq!(
|
assert_eq!(squeek_button_has_key(button, state.clone()), 1);
|
||||||
squeek_button_has_key(
|
let other_button = squeek_button_new(0, 0);
|
||||||
button_as_raw(&button),
|
assert_eq!(squeek_button_has_key(other_button, state.clone()), 0);
|
||||||
CKeyState::wrap(state.clone())
|
let other_state = ::keyboard::c::squeek_key_new(0);
|
||||||
),
|
assert_eq!(squeek_button_has_key(button, other_state), 0);
|
||||||
1
|
let shared_button = squeek_button_new_with_state(button);
|
||||||
);
|
assert_eq!(squeek_button_has_key(shared_button, state), 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
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -546,25 +557,13 @@ pub struct Size {
|
|||||||
pub height: f64,
|
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
|
/// The graphical representation of a button
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Button {
|
pub struct Button {
|
||||||
/// ID string, e.g. for CSS
|
oref: c::OutlineRef,
|
||||||
pub name: CString,
|
/// TODO: abolish Option, buttons should be created with bounds fully formed
|
||||||
/// Label to display to the user
|
|
||||||
pub label: Label,
|
|
||||||
pub corner_radius: f64,
|
|
||||||
/// TODO: position the buttons before they get initial bounds
|
|
||||||
/// Position relative to some origin (i.e. parent/row)
|
/// Position relative to some origin (i.e. parent/row)
|
||||||
pub bounds: c::Bounds,
|
bounds: Option<c::Bounds>,
|
||||||
/// current state, shared with other buttons
|
/// current state, shared with other buttons
|
||||||
pub state: Rc<RefCell<KeyState>>,
|
pub state: Rc<RefCell<KeyState>>,
|
||||||
}
|
}
|
||||||
@ -575,11 +574,11 @@ const ROW_SPACING: f64 = 7.0;
|
|||||||
|
|
||||||
/// The graphical representation of a row of buttons
|
/// The graphical representation of a row of buttons
|
||||||
pub struct Row {
|
pub struct Row {
|
||||||
pub buttons: Vec<Box<Button>>,
|
buttons: Vec<Box<Button>>,
|
||||||
/// Angle is not really used anywhere...
|
/// Angle is not really used anywhere...
|
||||||
pub angle: i32,
|
angle: i32,
|
||||||
/// Position relative to some origin (i.e. parent/view origin)
|
/// Position relative to some origin (i.e. parent/view origin)
|
||||||
pub bounds: Option<c::Bounds>,
|
bounds: Option<c::Bounds>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Row {
|
impl Row {
|
||||||
@ -640,7 +639,9 @@ impl Row {
|
|||||||
};
|
};
|
||||||
let angle = self.angle;
|
let angle = self.angle;
|
||||||
self.buttons.iter_mut().find(|button| {
|
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 point = point.clone();
|
||||||
let origin = origin.clone();
|
let origin = origin.clone();
|
||||||
procedures::is_point_inside(bounds, point, origin, angle)
|
procedures::is_point_inside(bounds, point, origin, angle)
|
||||||
@ -650,8 +651,8 @@ impl Row {
|
|||||||
|
|
||||||
pub struct View {
|
pub struct View {
|
||||||
/// Position relative to keyboard origin
|
/// Position relative to keyboard origin
|
||||||
pub bounds: c::Bounds,
|
bounds: c::Bounds,
|
||||||
pub rows: Vec<Box<Row>>,
|
rows: Vec<Box<Row>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl View {
|
impl View {
|
||||||
@ -703,7 +704,7 @@ impl View {
|
|||||||
for (mut button, button_position)
|
for (mut button, button_position)
|
||||||
in row.buttons.iter_mut()
|
in row.buttons.iter_mut()
|
||||||
.zip(button_positions) {
|
.zip(button_positions) {
|
||||||
button.bounds = button_position;
|
button.bounds = Some(button_position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -726,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 {
|
mod procedures {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|||||||
13
src/lib.rs
13
src/lib.rs
@ -1,16 +1,9 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bitflags;
|
mod bitflags;
|
||||||
#[macro_use]
|
|
||||||
extern crate maplit;
|
|
||||||
extern crate serde;
|
|
||||||
extern crate xkbcommon;
|
|
||||||
|
|
||||||
pub mod data;
|
mod float_ord;
|
||||||
pub mod float_ord;
|
mod imservice;
|
||||||
pub mod imservice;
|
|
||||||
mod keyboard;
|
mod keyboard;
|
||||||
mod layout;
|
mod layout;
|
||||||
mod resources;
|
|
||||||
mod symbol;
|
mod symbol;
|
||||||
mod util;
|
mod util;
|
||||||
mod xdg;
|
|
||||||
|
|||||||
@ -20,13 +20,16 @@ sources = [
|
|||||||
'../eek/eek-gtk-keyboard.c',
|
'../eek/eek-gtk-keyboard.c',
|
||||||
'../eek/eek-keyboard.c',
|
'../eek/eek-keyboard.c',
|
||||||
'../eek/eek-keyboard-drawing.c',
|
'../eek/eek-keyboard-drawing.c',
|
||||||
|
'../eek/eek-keysym.c',
|
||||||
'../eek/eek-layout.c',
|
'../eek/eek-layout.c',
|
||||||
'../eek/eek-renderer.c',
|
'../eek/eek-renderer.c',
|
||||||
|
'../eek/eek-section.c',
|
||||||
'../eek/eek-types.c',
|
'../eek/eek-types.c',
|
||||||
'../eek/eek-xml-layout.c',
|
'../eek/eek-xml-layout.c',
|
||||||
'../eek/layersurface.c',
|
'../eek/layersurface.c',
|
||||||
dbus_src,
|
dbus_src,
|
||||||
enums,
|
enums,
|
||||||
|
keysym_entries,
|
||||||
'../eekboard/key-emitter.c',
|
'../eekboard/key-emitter.c',
|
||||||
'../eekboard/eekboard-context-service.c',
|
'../eekboard/eekboard-context-service.c',
|
||||||
'../eekboard/eekboard-context.c',
|
'../eekboard/eekboard-context.c',
|
||||||
@ -53,25 +56,25 @@ deps = [
|
|||||||
# dependency('libxklavier'), # FIXME remove
|
# dependency('libxklavier'), # FIXME remove
|
||||||
]
|
]
|
||||||
|
|
||||||
rslibs = custom_target(
|
# Replacement for eekboard-server
|
||||||
'rslibs',
|
rslib = static_library(
|
||||||
build_by_default: true,
|
'rslib',
|
||||||
build_always_stale: true,
|
sources: ['lib.rs'],
|
||||||
output: ['librs.a'],
|
rust_crate_type: 'staticlib'
|
||||||
install: false,
|
|
||||||
console: true,
|
|
||||||
command: [cargo_script, '@CURRENT_SOURCE_DIR@', '@OUTPUT@', 'build']
|
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
rstests = executable(
|
||||||
'rstest',
|
'rstests',
|
||||||
cargo_script,
|
sources: ['lib.rs'],
|
||||||
args: [meson.source_root(), '', 'test']
|
rust_args: ['--test'],
|
||||||
|
install: false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
test('rstests', rstests)
|
||||||
|
|
||||||
libsqueekboard = static_library('libsqueekboard',
|
libsqueekboard = static_library('libsqueekboard',
|
||||||
sources,
|
sources,
|
||||||
link_with: [rslibs],
|
link_with: rslib,
|
||||||
include_directories: [include_directories('..'), include_directories('../eek')],
|
include_directories: [include_directories('..'), include_directories('../eek')],
|
||||||
dependencies: deps,
|
dependencies: deps,
|
||||||
c_args: [
|
c_args: [
|
||||||
|
|||||||
@ -1,24 +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")),
|
|
||||||
("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 }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
24
src/symbol.h
Normal file
24
src/symbol.h
Normal 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
|
||||||
128
src/symbol.rs
128
src/symbol.rs
@ -1,46 +1,138 @@
|
|||||||
/*! The symbol object, defining actions that the key can do when activated */
|
|
||||||
|
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
|
|
||||||
/// Name of the keysym
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
/// Gathers stuff defined in C or called by C
|
||||||
pub struct KeySym(pub String);
|
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
|
/// Use to switch layouts
|
||||||
type Level = String;
|
type Level = u8;
|
||||||
|
|
||||||
/// Use to send modified keypresses
|
/// Use to send modified keypresses
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Modifier {
|
pub enum Modifier {
|
||||||
Control,
|
Control,
|
||||||
Alt,
|
Alt,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Action to perform on the keypress and, in reverse, on keyrelease
|
/// Action to perform on the keypress and, in reverse, on keyrelease
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
/// Switch to this view
|
/// Switch to this level TODO: reverse?
|
||||||
SetLevel(Level),
|
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?
|
/// Set this modifier TODO: release?
|
||||||
SetModifier(Modifier),
|
SetModifier(Modifier),
|
||||||
/// Submit some text
|
/// Submit some text
|
||||||
Submit {
|
Submit {
|
||||||
/// Text to submit with input-method
|
/// orig: Canonical name of the symbol
|
||||||
text: Option<CString>,
|
text: Option<CString>,
|
||||||
/// The key events this symbol submits when submitting text is not possible
|
/// 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
|
/// Contains a static description of a particular key's actions
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Symbol {
|
pub struct Symbol {
|
||||||
/// The action that this key performs
|
/// The action that this key performs
|
||||||
pub action: Action,
|
pub action: Action,
|
||||||
|
/// Label to display to the user
|
||||||
|
pub label: Label,
|
||||||
|
// FIXME: is it used?
|
||||||
|
pub tooltip: Option<CString>,
|
||||||
}
|
}
|
||||||
|
|||||||
55
src/util.rs
55
src/util.rs
@ -1,14 +1,9 @@
|
|||||||
pub mod c {
|
pub mod c {
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::ffi::{ CStr, CString };
|
use std::ffi::{ CStr, CString };
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
use std::rc::Rc;
|
|
||||||
use std::str::Utf8Error;
|
use std::str::Utf8Error;
|
||||||
|
|
||||||
// traits
|
#[allow(dead_code)]
|
||||||
|
|
||||||
use std::borrow::ToOwned;
|
|
||||||
|
|
||||||
pub fn as_str(s: &*const c_char) -> Result<Option<&str>, Utf8Error> {
|
pub fn as_str(s: &*const c_char) -> Result<Option<&str>, Utf8Error> {
|
||||||
if s.is_null() {
|
if s.is_null() {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
@ -52,52 +47,4 @@ pub mod c {
|
|||||||
assert_eq!(as_str(&ptr::null()), Ok(None))
|
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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
63
src/xdg.rs
63
src/xdg.rs
@ -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
45
tests/eek-simple-test.c
Normal 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
55
tests/eek-xml-test.c
Normal 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 ();
|
||||||
|
}
|
||||||
@ -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)
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
---
|
|
||||||
bounds:
|
|
||||||
x: 0
|
|
||||||
y: 0
|
|
||||||
width: 0
|
|
||||||
height: 0
|
|
||||||
views:
|
|
||||||
base:
|
|
||||||
- "test"
|
|
||||||
outlines:
|
|
||||||
default:
|
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 0, height: 0 }
|
|
||||||
|
|
||||||
buttons:
|
|
||||||
test:
|
|
||||||
label: "test"
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
---
|
|
||||||
# missing views
|
|
||||||
bounds:
|
|
||||||
x: 0
|
|
||||||
y: 0
|
|
||||||
width: 0
|
|
||||||
height: 0
|
|
||||||
outlines:
|
|
||||||
default:
|
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 0, height: 0 }
|
|
||||||
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
---
|
|
||||||
# extra field
|
|
||||||
bounds:
|
|
||||||
x: 0
|
|
||||||
y: 0
|
|
||||||
width: 0
|
|
||||||
height: 0
|
|
||||||
views:
|
|
||||||
base:
|
|
||||||
- "test"
|
|
||||||
outlines:
|
|
||||||
default:
|
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 0, height: 0 }
|
|
||||||
|
|
||||||
bad_field: false
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
---
|
|
||||||
# punctuation
|
|
||||||
bounds:
|
|
||||||
x: 0
|
|
||||||
y: 0
|
|
||||||
width: 0
|
|
||||||
height: 0
|
|
||||||
views:
|
|
||||||
base:
|
|
||||||
- "."
|
|
||||||
outlines:
|
|
||||||
default:
|
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 0, height: 0 }
|
|
||||||
|
|
||||||
buttons:
|
|
||||||
".":
|
|
||||||
label: "test"
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
---
|
|
||||||
# punctuation
|
|
||||||
bounds:
|
|
||||||
x: 0
|
|
||||||
y: 0
|
|
||||||
width: 0
|
|
||||||
height: 0
|
|
||||||
views:
|
|
||||||
base:
|
|
||||||
- "å"
|
|
||||||
outlines:
|
|
||||||
default:
|
|
||||||
corner_radius: 1
|
|
||||||
bounds: { x: 0, y: 0, width: 0, height: 0 }
|
|
||||||
|
|
||||||
buttons:
|
|
||||||
å:
|
|
||||||
label: "test"
|
|
||||||
@ -19,10 +19,13 @@ test_link_args = [
|
|||||||
'-fPIC',
|
'-fPIC',
|
||||||
]
|
]
|
||||||
|
|
||||||
c_tests = [
|
tests = [
|
||||||
|
'eek-simple-test',
|
||||||
|
'eek-xml-test',
|
||||||
|
'test-keymap-generation'
|
||||||
]
|
]
|
||||||
|
|
||||||
foreach name : c_tests
|
foreach name : tests
|
||||||
|
|
||||||
test_sources = [name + '.c']
|
test_sources = [name + '.c']
|
||||||
|
|
||||||
@ -44,15 +47,4 @@ foreach name : c_tests
|
|||||||
|
|
||||||
endforeach
|
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', 'nb', 'number']
|
|
||||||
test(
|
|
||||||
'test_layout_' + layout,
|
|
||||||
cargo_script,
|
|
||||||
args: [meson.source_root(), '', 'run', '--example', 'test_layout', layout]
|
|
||||||
)
|
|
||||||
endforeach
|
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|||||||
72
tests/test-keymap-generation.c
Normal file
72
tests/test-keymap-generation.c
Normal 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 ();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user