Merge branch 'reparse' into 'master'
rewrite of the parsing See merge request Librem5/squeekboard!152
This commit is contained in:
		
							
								
								
									
										113
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							@ -0,0 +1,113 @@
 | 
			
		||||
# This file is automatically @generated by Cargo.
 | 
			
		||||
# It is not intended for manual editing.
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "bitflags"
 | 
			
		||||
version = "1.1.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 = "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 = "proc-macro2"
 | 
			
		||||
version = "1.0.2"
 | 
			
		||||
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.2 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "rs"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "bitflags 1.1.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.99 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
 "serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "serde"
 | 
			
		||||
version = "1.0.99"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "serde_derive"
 | 
			
		||||
version = "1.0.99"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2 1.0.2 (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.99 (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.2 (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 = "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.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
 | 
			
		||||
"checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e"
 | 
			
		||||
"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 proc-macro2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "175a40b9cf564ce9bf050654633dbf339978706b8ead1a907bb970b63185dd95"
 | 
			
		||||
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
 | 
			
		||||
"checksum serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)" = "fec2851eb56d010dc9a21b89ca53ee75e6528bab60c11e89d38390904982da9f"
 | 
			
		||||
"checksum serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)" = "cb4dc18c61206b08dc98216c98faa0232f4337e1e1b8574551d5bad29ea1b425"
 | 
			
		||||
"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 yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d"
 | 
			
		||||
@ -4,6 +4,9 @@ version = "0.1.0"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
bitflags = "1.1"
 | 
			
		||||
maplit = "1.0"
 | 
			
		||||
serde = { version = "1.0", features = ["derive"] }
 | 
			
		||||
serde_yaml = "0.8"
 | 
			
		||||
 | 
			
		||||
[lib]
 | 
			
		||||
name = "rs"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
								
							@ -3,14 +3,18 @@
 | 
			
		||||
 | 
			
		||||
*Squeekboard* is a virtual keyboard supporting Wayland, built primarily for the *Librem 5* phone.
 | 
			
		||||
 | 
			
		||||
It squeaks because some Rust got inside.
 | 
			
		||||
 | 
			
		||||
Features
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
### Present
 | 
			
		||||
 | 
			
		||||
- GTK3
 | 
			
		||||
- Custom xml-defined keyboards
 | 
			
		||||
- Custom yaml-defined keyboards
 | 
			
		||||
- DBus interface to show and hide
 | 
			
		||||
- Use Wayland input method protocol to show and hide
 | 
			
		||||
- Use Wayland virtual keyboard protocol
 | 
			
		||||
 | 
			
		||||
### Temporarily dropped
 | 
			
		||||
 | 
			
		||||
@ -18,8 +22,7 @@ Features
 | 
			
		||||
 | 
			
		||||
### TODO
 | 
			
		||||
 | 
			
		||||
- Use Wayland virtual keyboard protocol
 | 
			
		||||
- Use Wayland text input protocol
 | 
			
		||||
- Pick up user-defined layouts
 | 
			
		||||
- Use Wayland input method protocol
 | 
			
		||||
- Pick up DBus interface files from /usr/share
 | 
			
		||||
 | 
			
		||||
@ -38,6 +41,7 @@ $ cd squeekboard
 | 
			
		||||
$ mkdir ../build
 | 
			
		||||
$ meson ../build/
 | 
			
		||||
$ cd ../build
 | 
			
		||||
$ ninja test
 | 
			
		||||
$ ninja install
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,64 +0,0 @@
 | 
			
		||||
<?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>
 | 
			
		||||
@ -1,63 +0,0 @@
 | 
			
		||||
<?xml version="1.0"?>
 | 
			
		||||
<geometry version="0.90">
 | 
			
		||||
  <bounds x="0" y="10.000000" width="426" height="229"/>
 | 
			
		||||
 | 
			
		||||
  <outline id="default" 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="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="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>
 | 
			
		||||
@ -1,40 +0,0 @@
 | 
			
		||||
<?xml version="1.0"?>
 | 
			
		||||
<geometry version="0.90">
 | 
			
		||||
  <bounds x="0" y="10.000000" 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.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>
 | 
			
		||||
							
								
								
									
										179
									
								
								data/keyboards/nb.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								data/keyboards/nb.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,179 @@
 | 
			
		||||
---
 | 
			
		||||
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: "]"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										47
									
								
								data/keyboards/number.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								data/keyboards/number.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
			
		||||
---
 | 
			
		||||
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: ")"
 | 
			
		||||
 | 
			
		||||
@ -1,132 +0,0 @@
 | 
			
		||||
<?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>
 | 
			
		||||
@ -1,22 +0,0 @@
 | 
			
		||||
<?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>
 | 
			
		||||
@ -1,118 +0,0 @@
 | 
			
		||||
<?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>
 | 
			
		||||
							
								
								
									
										167
									
								
								data/keyboards/us.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								data/keyboards/us.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,167 @@
 | 
			
		||||
---
 | 
			
		||||
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        period    Return"
 | 
			
		||||
    upper:
 | 
			
		||||
        - "Q W E R T Y U I O P"
 | 
			
		||||
        - "A S D F G H J K L"
 | 
			
		||||
        - "Shift_L   Z X C V B N M  BackSpace"
 | 
			
		||||
        - "show_numbers preferences         space        period    Return"
 | 
			
		||||
    numbers:
 | 
			
		||||
        - "1 2 3 4 5 6 7 8 9 0"
 | 
			
		||||
        - "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"
 | 
			
		||||
    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: "]"
 | 
			
		||||
 | 
			
		||||
@ -2,9 +2,6 @@
 | 
			
		||||
<gresources>
 | 
			
		||||
  <gresource prefix="/sm/puri/squeekboard">
 | 
			
		||||
   <file compressed="true">style.css</file>
 | 
			
		||||
   <file compressed="true" preprocess="xml-stripblanks">keyboards/geometry/compact.xml</file>
 | 
			
		||||
   <file compressed="true" preprocess="xml-stripblanks">keyboards/geometry/extended.xml</file>
 | 
			
		||||
   <file compressed="true" preprocess="xml-stripblanks">keyboards/geometry/number-keypad.xml</file>
 | 
			
		||||
   <file compressed="true" preprocess="xml-stripblanks">keyboards/keyboards.xml</file>
 | 
			
		||||
   <file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ar.xml</file>
 | 
			
		||||
   <file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/as-inscript.xml</file>
 | 
			
		||||
@ -23,7 +20,6 @@
 | 
			
		||||
   <file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ml-inscript.xml</file>
 | 
			
		||||
   <file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/mr-inscript.xml</file>
 | 
			
		||||
   <file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/my.xml</file>
 | 
			
		||||
   <file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/nb.xml</file>
 | 
			
		||||
   <file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/or-inscript.xml</file>
 | 
			
		||||
   <file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/pa-inscript.xml</file>
 | 
			
		||||
   <file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ru.xml</file>
 | 
			
		||||
@ -33,9 +29,7 @@
 | 
			
		||||
   <file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/th.xml</file>
 | 
			
		||||
   <file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ua.xml</file>
 | 
			
		||||
   <file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/ug.xml</file>
 | 
			
		||||
   <file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/us.xml</file>
 | 
			
		||||
   <file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/zh-bopomofo.xml</file>
 | 
			
		||||
   <file compressed="true" preprocess="xml-stripblanks">keyboards/symbols/special/number.xml</file>
 | 
			
		||||
   <file>icons/key-enter.svg</file>
 | 
			
		||||
   <file>icons/key-shift.svg</file>
 | 
			
		||||
   <file>icons/keyboard-mode-symbolic.svg</file>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										7
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@ -1,3 +1,10 @@
 | 
			
		||||
squeekboard (1.1.0) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  * Use new keyboard layout format
 | 
			
		||||
 | 
			
		||||
 -- Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm>  Mon, 02 Sep 2019 10:12:02 +0000
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
squeekboard (1.0.10) unstable; urgency=medium
 | 
			
		||||
 | 
			
		||||
  * Use a shared DBus definition
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,6 @@
 | 
			
		||||
 | 
			
		||||
#include "eek-renderer.h"
 | 
			
		||||
#include "eek-keyboard.h"
 | 
			
		||||
#include "src/symbol.h"
 | 
			
		||||
 | 
			
		||||
#include "eek-gtk-keyboard.h"
 | 
			
		||||
 | 
			
		||||
@ -110,7 +109,7 @@ eek_gtk_keyboard_real_draw (GtkWidget *self,
 | 
			
		||||
 | 
			
		||||
    eek_renderer_render_keyboard (priv->renderer, cr);
 | 
			
		||||
 | 
			
		||||
    struct squeek_view *view = priv->keyboard->views[priv->keyboard->level];
 | 
			
		||||
    struct squeek_view *view = squeek_layout_get_current_view(priv->keyboard->layout);
 | 
			
		||||
 | 
			
		||||
    /* redraw pressed key */
 | 
			
		||||
    const GList *list = priv->keyboard->pressed_keys;
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,6 @@
 | 
			
		||||
#include "eekboard/key-emitter.h"
 | 
			
		||||
#include "keymap.h"
 | 
			
		||||
#include "src/keyboard.h"
 | 
			
		||||
#include "src/symbol.h"
 | 
			
		||||
 | 
			
		||||
#include "eek-keyboard.h"
 | 
			
		||||
 | 
			
		||||
@ -50,75 +49,53 @@ eek_modifier_key_free (EekModifierKey *modkey)
 | 
			
		||||
    g_slice_free (EekModifierKey, modkey);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Updates the state of locked keys based on the key that was activated
 | 
			
		||||
/// FIXME: make independent of what the key are named,
 | 
			
		||||
/// and instead refer to the contained symbols
 | 
			
		||||
static guint
 | 
			
		||||
set_key_states (LevelKeyboard    *keyboard,
 | 
			
		||||
                struct squeek_key *key,
 | 
			
		||||
                guint new_level)
 | 
			
		||||
void
 | 
			
		||||
eek_keyboard_set_key_locked (LevelKeyboard    *keyboard,
 | 
			
		||||
                            struct squeek_key *key)
 | 
			
		||||
{
 | 
			
		||||
    // Keys locking rules hardcoded for the time being...
 | 
			
		||||
    const gchar *name = squeek_symbol_get_name(squeek_key_get_symbol(key));
 | 
			
		||||
    // Lock the shift whenever it's pressed on the baselevel
 | 
			
		||||
    // TODO: need to lock shift on the destination level
 | 
			
		||||
    if (g_strcmp0(name, "Shift_L") == 0 && keyboard->level == 0) {
 | 
			
		||||
        EekModifierKey *modifier_key = g_slice_new (EekModifierKey);
 | 
			
		||||
        modifier_key->modifiers = 0;
 | 
			
		||||
        modifier_key->key = key;
 | 
			
		||||
        keyboard->locked_keys =
 | 
			
		||||
    EekModifierKey *modifier_key = g_slice_new (EekModifierKey);
 | 
			
		||||
    modifier_key->modifiers = 0;
 | 
			
		||||
    modifier_key->key = key;
 | 
			
		||||
    keyboard->locked_keys =
 | 
			
		||||
            g_list_prepend (keyboard->locked_keys, modifier_key);
 | 
			
		||||
        squeek_key_set_locked(key, true);
 | 
			
		||||
    }
 | 
			
		||||
    if (keyboard->level == 1) {
 | 
			
		||||
        // Only shift is locked in this state, unlock on any key press
 | 
			
		||||
        for (GList *head = keyboard->locked_keys; head; ) {
 | 
			
		||||
            EekModifierKey *modifier_key = head->data;
 | 
			
		||||
            GList *next = g_list_next (head);
 | 
			
		||||
            keyboard->locked_keys =
 | 
			
		||||
                g_list_remove_link (keyboard->locked_keys, head);
 | 
			
		||||
            squeek_key_set_locked(modifier_key->key, false);
 | 
			
		||||
            g_list_free1 (head);
 | 
			
		||||
            head = next;
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    return new_level;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FIXME: unhardcode, parse some user information as to which key triggers which view (level)
 | 
			
		||||
/// Unlock all locked keys.
 | 
			
		||||
/// All locked keys will unlock at the next keypress (should be called "stuck")
 | 
			
		||||
/// Returns the number of handled keys
 | 
			
		||||
/// TODO: may need to check key type in order to chain locks
 | 
			
		||||
/// before pressing an "emitting" key
 | 
			
		||||
static int unlock_keys(LevelKeyboard *keyboard) {
 | 
			
		||||
    int handled = 0;
 | 
			
		||||
    for (GList *head = keyboard->locked_keys; head; ) {
 | 
			
		||||
        EekModifierKey *modifier_key = head->data;
 | 
			
		||||
        GList *next = g_list_next (head);
 | 
			
		||||
        keyboard->locked_keys =
 | 
			
		||||
                g_list_remove_link (keyboard->locked_keys, head);
 | 
			
		||||
        //squeek_key_set_locked(squeek_button_get_key(modifier_key->button), false);
 | 
			
		||||
 | 
			
		||||
        squeek_layout_set_state_from_press(keyboard->layout, keyboard, modifier_key->key);
 | 
			
		||||
        g_list_free1 (head);
 | 
			
		||||
        head = next;
 | 
			
		||||
        handled++;
 | 
			
		||||
    }
 | 
			
		||||
    return handled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
set_level_from_press (LevelKeyboard *keyboard, struct squeek_key *key)
 | 
			
		||||
{
 | 
			
		||||
    /* The levels are: 0 Letters, 1 Upper case letters, 2 Numbers, 3 Symbols */
 | 
			
		||||
    guint level = keyboard->level;
 | 
			
		||||
    /* Handle non-emitting keys */
 | 
			
		||||
    if (key) {
 | 
			
		||||
        const gchar *name = squeek_symbol_get_name(squeek_key_get_symbol(key));
 | 
			
		||||
        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;
 | 
			
		||||
        }
 | 
			
		||||
    // If the currently locked key was already handled in the unlock phase,
 | 
			
		||||
    // then skip
 | 
			
		||||
    if (unlock_keys(keyboard) == 0) {
 | 
			
		||||
        squeek_layout_set_state_from_press(keyboard->layout, keyboard, key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    keyboard->level = set_key_states(keyboard, key, level);
 | 
			
		||||
 | 
			
		||||
    eek_layout_update_layout(keyboard);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void eek_keyboard_press_key(LevelKeyboard *keyboard, struct squeek_key *key, guint32 timestamp) {
 | 
			
		||||
    squeek_key_set_pressed(key, TRUE);
 | 
			
		||||
    keyboard->pressed_keys = g_list_prepend (keyboard->pressed_keys, key);
 | 
			
		||||
 | 
			
		||||
    struct squeek_symbol *symbol = squeek_key_get_symbol(key);
 | 
			
		||||
    if (!symbol)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    // Only take action about setting level *after* the key has taken effect, i.e. on release
 | 
			
		||||
    //set_level_from_press (keyboard, key);
 | 
			
		||||
 | 
			
		||||
@ -140,10 +117,6 @@ void eek_keyboard_release_key(LevelKeyboard *keyboard,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct squeek_symbol *symbol = squeek_key_get_symbol(key);
 | 
			
		||||
    if (!symbol)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    set_level_from_press (keyboard, key);
 | 
			
		||||
 | 
			
		||||
    // "Borrowed" from eek-context-service; doesn't influence the state but forwards the event
 | 
			
		||||
@ -154,18 +127,7 @@ void eek_keyboard_release_key(LevelKeyboard *keyboard,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void level_keyboard_deinit(LevelKeyboard *self) {
 | 
			
		||||
    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];
 | 
			
		||||
    }
 | 
			
		||||
    squeek_layout_free(self->layout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void level_keyboard_free(LevelKeyboard *self) {
 | 
			
		||||
@ -173,115 +135,18 @@ void level_keyboard_free(LevelKeyboard *self) {
 | 
			
		||||
    g_free(self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void level_keyboard_init(LevelKeyboard *self) {
 | 
			
		||||
    self->outline_array = g_array_new (FALSE, TRUE, sizeof (EekOutline));
 | 
			
		||||
void level_keyboard_init(LevelKeyboard *self, struct squeek_layout *layout) {
 | 
			
		||||
    self->layout = layout;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LevelKeyboard *level_keyboard_new(EekboardContextService *manager, struct squeek_view *views[4], GHashTable *name_button_hash) {
 | 
			
		||||
LevelKeyboard *level_keyboard_new(EekboardContextService *manager, struct squeek_layout *layout) {
 | 
			
		||||
    LevelKeyboard *keyboard = g_new0(LevelKeyboard, 1);
 | 
			
		||||
    level_keyboard_init(keyboard);
 | 
			
		||||
    for (uint i = 0; i < 4; i++) {
 | 
			
		||||
        keyboard->views[i] = views[i];
 | 
			
		||||
    }
 | 
			
		||||
    level_keyboard_init(keyboard, layout);
 | 
			
		||||
    keyboard->manager = manager;
 | 
			
		||||
    keyboard->names = name_button_hash;
 | 
			
		||||
    return keyboard;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * eek_keyboard_find_key_by_name:
 | 
			
		||||
 * @keyboard: an #EekKeyboard
 | 
			
		||||
 * @name: a key name
 | 
			
		||||
 *
 | 
			
		||||
 * Find an #EekKey whose name is @name.
 | 
			
		||||
 * Return value: (transfer none): #EekKey whose name is @name
 | 
			
		||||
 */
 | 
			
		||||
struct squeek_button*
 | 
			
		||||
eek_keyboard_find_button_by_name (LevelKeyboard *keyboard,
 | 
			
		||||
                                  const gchar *name)
 | 
			
		||||
{
 | 
			
		||||
    return g_hash_table_lookup (keyboard->names, name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * eek_keyboard_get_outline:
 | 
			
		||||
 * @keyboard: an #EekKeyboard
 | 
			
		||||
 * @oref: ID of the outline
 | 
			
		||||
 *
 | 
			
		||||
 * Get an outline associated with @oref in @keyboard.
 | 
			
		||||
 * Returns: an #EekOutline, which should not be released
 | 
			
		||||
 */
 | 
			
		||||
EekOutline *
 | 
			
		||||
level_keyboard_get_outline (LevelKeyboard *keyboard,
 | 
			
		||||
                          guint        oref)
 | 
			
		||||
{
 | 
			
		||||
    if (oref > keyboard->outline_array->len)
 | 
			
		||||
        return NULL;
 | 
			
		||||
 | 
			
		||||
    return &g_array_index (keyboard->outline_array, EekOutline, oref);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * eek_keyboard_get_keymap:
 | 
			
		||||
 * @keyboard: an #EekKeyboard
 | 
			
		||||
 *
 | 
			
		||||
 * Get the keymap for the keyboard.
 | 
			
		||||
 * Returns: a string containing the XKB keymap.
 | 
			
		||||
 */
 | 
			
		||||
gchar *
 | 
			
		||||
eek_keyboard_get_keymap(LevelKeyboard *keyboard)
 | 
			
		||||
{
 | 
			
		||||
    /* Start the keycodes and symbols sections with their respective headers. */
 | 
			
		||||
    gchar *keycodes = g_strdup(keymap_keycodes_header);
 | 
			
		||||
    gchar *symbols = g_strdup(keymap_symbols_header);
 | 
			
		||||
 | 
			
		||||
    /* Iterate over the keys in the name-to-key hash table. */
 | 
			
		||||
    GHashTableIter iter;
 | 
			
		||||
    gchar *button_name;
 | 
			
		||||
    gpointer button_ptr;
 | 
			
		||||
    g_hash_table_iter_init(&iter, keyboard->names);
 | 
			
		||||
 | 
			
		||||
    while (g_hash_table_iter_next(&iter, (gpointer)&button_name, &button_ptr)) {
 | 
			
		||||
 | 
			
		||||
        gchar *current, *line;
 | 
			
		||||
        struct squeek_button *button = button_ptr;
 | 
			
		||||
        struct squeek_key *key = squeek_button_get_key(button);
 | 
			
		||||
        guint keycode = squeek_key_get_keycode(key);
 | 
			
		||||
 | 
			
		||||
        /* Don't include invalid keycodes in the keymap. */
 | 
			
		||||
        if (keycode == EEK_INVALID_KEYCODE)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        /* Append a key name-to-keycode definition to the keycodes section. */
 | 
			
		||||
        current = keycodes;
 | 
			
		||||
        line = g_strdup_printf("        <%s> = %i;\n", (char *)button_name, keycode);
 | 
			
		||||
 | 
			
		||||
        keycodes = g_strconcat(current, line, NULL);
 | 
			
		||||
        g_free(line);
 | 
			
		||||
        g_free(current);
 | 
			
		||||
 | 
			
		||||
        // FIXME: free
 | 
			
		||||
        const char *key_str = squeek_key_to_keymap_entry(
 | 
			
		||||
            (char*)button_name,
 | 
			
		||||
            key
 | 
			
		||||
        );
 | 
			
		||||
        current = symbols;
 | 
			
		||||
        symbols = g_strconcat(current, key_str, NULL);
 | 
			
		||||
        g_free(current);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Assemble the keymap file from the header, sections and footer. */
 | 
			
		||||
    gchar *keymap = g_strconcat(keymap_header,
 | 
			
		||||
                                keycodes, "    };\n\n",
 | 
			
		||||
                                symbols, "    };\n\n",
 | 
			
		||||
                                keymap_footer, NULL);
 | 
			
		||||
 | 
			
		||||
    g_free(keycodes);
 | 
			
		||||
    g_free(symbols);
 | 
			
		||||
    return keymap;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct squeek_view *level_keyboard_current(LevelKeyboard *keyboard)
 | 
			
		||||
{
 | 
			
		||||
    return keyboard->views[keyboard->level];
 | 
			
		||||
    return squeek_layout_get_current_view(keyboard->layout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -42,37 +42,26 @@ typedef struct _EekModifierKey EekModifierKey;
 | 
			
		||||
 | 
			
		||||
/// Keyboard state holder
 | 
			
		||||
struct _LevelKeyboard {
 | 
			
		||||
    struct squeek_view *views[4];
 | 
			
		||||
    guint level;
 | 
			
		||||
    struct squeek_layout *layout;
 | 
			
		||||
    struct xkb_keymap *keymap;
 | 
			
		||||
    int keymap_fd; // keymap formatted as XKB string
 | 
			
		||||
    size_t keymap_len; // length of the data inside keymap_fd
 | 
			
		||||
    GArray *outline_array;
 | 
			
		||||
 | 
			
		||||
    GList *pressed_keys; // struct squeek_key*
 | 
			
		||||
    GList *locked_keys; // struct EekModifierKey*
 | 
			
		||||
 | 
			
		||||
    /* Map button names to button objects: */
 | 
			
		||||
    GHashTable *names;
 | 
			
		||||
 | 
			
		||||
    guint id; // as a key to layout choices
 | 
			
		||||
 | 
			
		||||
    EekboardContextService *manager; // unowned reference
 | 
			
		||||
};
 | 
			
		||||
typedef struct _LevelKeyboard LevelKeyboard;
 | 
			
		||||
 | 
			
		||||
struct squeek_button *eek_keyboard_find_button_by_name(LevelKeyboard *keyboard,
 | 
			
		||||
                                      const gchar        *name);
 | 
			
		||||
 | 
			
		||||
/// Represents the path to the button within a view
 | 
			
		||||
struct button_place {
 | 
			
		||||
    const struct squeek_row *row;
 | 
			
		||||
    const struct squeek_button *button;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
EekOutline         *level_keyboard_get_outline
 | 
			
		||||
                                     (LevelKeyboard        *keyboard,
 | 
			
		||||
                                      guint               oref);
 | 
			
		||||
EekModifierKey     *eek_modifier_key_copy
 | 
			
		||||
                                     (EekModifierKey     *modkey);
 | 
			
		||||
void                eek_modifier_key_free
 | 
			
		||||
@ -85,7 +74,7 @@ gchar *             eek_keyboard_get_keymap
 | 
			
		||||
                                     (LevelKeyboard *keyboard);
 | 
			
		||||
 | 
			
		||||
struct squeek_view *level_keyboard_current(LevelKeyboard *keyboard);
 | 
			
		||||
LevelKeyboard *level_keyboard_new(EekboardContextService *manager, struct squeek_view *views[], GHashTable *name_button_hash);
 | 
			
		||||
LevelKeyboard *level_keyboard_new(EekboardContextService *manager, struct squeek_layout *layout);
 | 
			
		||||
void level_keyboard_deinit(LevelKeyboard *self);
 | 
			
		||||
void level_keyboard_free(LevelKeyboard *self);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -45,9 +45,3 @@ void
 | 
			
		||||
eek_layout_init (EekLayout *self)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
eek_layout_update_layout(LevelKeyboard *keyboard)
 | 
			
		||||
{
 | 
			
		||||
    squeek_view_place_contents(level_keyboard_current(keyboard), keyboard);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -56,14 +56,5 @@ struct _EekLayoutClass
 | 
			
		||||
 | 
			
		||||
GType        eek_layout_get_type  (void) G_GNUC_CONST;
 | 
			
		||||
 | 
			
		||||
void         eek_layout_place_rows(LevelKeyboard *keyboard, struct squeek_view *level);
 | 
			
		||||
 | 
			
		||||
void         eek_layout_update_layout(LevelKeyboard *keyboard);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LevelKeyboard *
 | 
			
		||||
level_keyboard_from_layout (EekLayout *layout,
 | 
			
		||||
                  gdouble    initial_width,
 | 
			
		||||
                  gdouble    initial_height);
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
#endif  /* EEK_LAYOUT_H */
 | 
			
		||||
 | 
			
		||||
@ -24,8 +24,6 @@
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <gdk-pixbuf/gdk-pixbuf.h>
 | 
			
		||||
 | 
			
		||||
#include "src/symbol.h"
 | 
			
		||||
 | 
			
		||||
#include "eek-renderer.h"
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
@ -188,21 +186,13 @@ render_button_outline (EekRenderer *renderer,
 | 
			
		||||
                    gboolean     active)
 | 
			
		||||
{
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    /* 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));
 | 
			
		||||
    struct squeek_symbol *symbol = squeek_button_get_symbol(button);
 | 
			
		||||
    const char *name = squeek_symbol_get_name(symbol);
 | 
			
		||||
    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. */
 | 
			
		||||
@ -228,18 +218,12 @@ render_button (EekRenderer *self,
 | 
			
		||||
            gboolean     active)
 | 
			
		||||
{
 | 
			
		||||
    EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
 | 
			
		||||
    EekOutline *outline;
 | 
			
		||||
    cairo_surface_t *outline_surface;
 | 
			
		||||
    GHashTable *outline_surface_cache;
 | 
			
		||||
    PangoLayout *layout;
 | 
			
		||||
    PangoRectangle extents = { 0, };
 | 
			
		||||
    EekColor foreground;
 | 
			
		||||
 | 
			
		||||
    guint oref = squeek_button_get_oref (place->button);
 | 
			
		||||
    outline = level_keyboard_get_outline (priv->keyboard, oref);
 | 
			
		||||
    if (outline == NULL)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    /* render outline */
 | 
			
		||||
    EekBounds bounds = squeek_button_get_bounds(place->button);
 | 
			
		||||
 | 
			
		||||
@ -248,7 +232,7 @@ render_button (EekRenderer *self,
 | 
			
		||||
    else
 | 
			
		||||
        outline_surface_cache = priv->outline_surface_cache;
 | 
			
		||||
 | 
			
		||||
    outline_surface = g_hash_table_lookup (outline_surface_cache, outline);
 | 
			
		||||
    outline_surface = g_hash_table_lookup (outline_surface_cache, place->button);
 | 
			
		||||
    if (!outline_surface) {
 | 
			
		||||
        cairo_t *cr;
 | 
			
		||||
 | 
			
		||||
@ -272,7 +256,7 @@ render_button (EekRenderer *self,
 | 
			
		||||
        cairo_destroy (cr);
 | 
			
		||||
 | 
			
		||||
        g_hash_table_insert (outline_surface_cache,
 | 
			
		||||
                             outline,
 | 
			
		||||
                             (gpointer)place->button,
 | 
			
		||||
                             outline_surface);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -281,16 +265,12 @@ render_button (EekRenderer *self,
 | 
			
		||||
 | 
			
		||||
    eek_renderer_get_foreground_color (self, priv->key_context, &foreground);
 | 
			
		||||
    /* render icon (if any) */
 | 
			
		||||
    struct squeek_symbol *symbol = squeek_button_get_symbol(place->button);
 | 
			
		||||
    if (!symbol)
 | 
			
		||||
        return;
 | 
			
		||||
    const char *icon_name = squeek_button_get_icon_name(place->button);
 | 
			
		||||
 | 
			
		||||
    if (squeek_symbol_get_icon_name (symbol)) {
 | 
			
		||||
    if (icon_name) {
 | 
			
		||||
        gint scale = priv->scale_factor;
 | 
			
		||||
        cairo_surface_t *icon_surface =
 | 
			
		||||
            eek_renderer_get_icon_surface (self,
 | 
			
		||||
                                           squeek_symbol_get_icon_name (symbol),
 | 
			
		||||
                                           16 / priv->scale,
 | 
			
		||||
            eek_renderer_get_icon_surface (self, icon_name, 16 / priv->scale,
 | 
			
		||||
                                           scale);
 | 
			
		||||
        if (icon_surface) {
 | 
			
		||||
            gint width = cairo_image_surface_get_width (icon_surface);
 | 
			
		||||
@ -385,18 +365,16 @@ eek_renderer_real_render_button_label (EekRenderer *self,
 | 
			
		||||
{
 | 
			
		||||
    EekRendererPrivate *priv = eek_renderer_get_instance_private (self);
 | 
			
		||||
 | 
			
		||||
    const gchar *label;
 | 
			
		||||
    const gchar *label = squeek_button_get_label(button);
 | 
			
		||||
 | 
			
		||||
    if (!label) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    PangoFontDescription *font;
 | 
			
		||||
    PangoLayoutLine *line;
 | 
			
		||||
    gdouble scale;
 | 
			
		||||
 | 
			
		||||
    struct squeek_symbol *symbol = squeek_button_get_symbol(button);
 | 
			
		||||
    if (!symbol)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    label = squeek_symbol_get_label (symbol);
 | 
			
		||||
    if (!label)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (!priv->font) {
 | 
			
		||||
        const PangoFontDescription *base_font;
 | 
			
		||||
@ -494,7 +472,7 @@ eek_renderer_real_render_keyboard (EekRenderer *self,
 | 
			
		||||
        cairo_get_target (cr), 0, 0,
 | 
			
		||||
        priv->allocation_width, priv->allocation_height);
 | 
			
		||||
 | 
			
		||||
    render_keyboard_surface (self, priv->keyboard->views[priv->keyboard->level]);
 | 
			
		||||
    render_keyboard_surface (self, squeek_layout_get_current_view(priv->keyboard->layout));
 | 
			
		||||
 | 
			
		||||
    cairo_set_source_surface (cr, priv->keyboard_surface, 0.0, 0.0);
 | 
			
		||||
    source = cairo_get_source (cr);
 | 
			
		||||
 | 
			
		||||
@ -1,56 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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;
 | 
			
		||||
}
 | 
			
		||||
@ -1,34 +0,0 @@
 | 
			
		||||
/* 
 | 
			
		||||
 * 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 */
 | 
			
		||||
@ -29,7 +29,6 @@
 | 
			
		||||
 | 
			
		||||
#include "eek-keyboard.h"
 | 
			
		||||
#include "src/keyboard.h"
 | 
			
		||||
#include "src/symbol.h"
 | 
			
		||||
 | 
			
		||||
#include "squeekboard-resources.h"
 | 
			
		||||
 | 
			
		||||
@ -62,21 +61,6 @@ G_DEFINE_BOXED_TYPE(EekXmlKeyboardDesc, eek_xml_keyboard_desc, eek_xml_keyboard_
 | 
			
		||||
 | 
			
		||||
static GList        *parse_keyboards (const gchar         *path,
 | 
			
		||||
                                      GError             **error);
 | 
			
		||||
static GList        *parse_prerequisites
 | 
			
		||||
                                     (const gchar         *path,
 | 
			
		||||
                                      GError             **error);
 | 
			
		||||
static gboolean      parse_geometry  (const gchar         *path,
 | 
			
		||||
                                      struct squeek_view **views, GArray *outline_array, GHashTable *name_button_hash,
 | 
			
		||||
                                      GError             **error);
 | 
			
		||||
static gboolean      parse_symbols_with_prerequisites
 | 
			
		||||
                                     (const gchar         *keyboards_dir,
 | 
			
		||||
                                      const gchar         *name,
 | 
			
		||||
                                      LevelKeyboard *keyboard,
 | 
			
		||||
                                      GList             **loaded,
 | 
			
		||||
                                      GError             **error);
 | 
			
		||||
static gboolean      parse_symbols   (const gchar         *path,
 | 
			
		||||
                                      LevelKeyboard *keyboard,
 | 
			
		||||
                                      GError             **error);
 | 
			
		||||
 | 
			
		||||
static gboolean      validate        (const gchar        **valid_path_list,
 | 
			
		||||
                                      gsize                valid_path_list_len,
 | 
			
		||||
@ -253,380 +237,6 @@ struct _GeometryParseData {
 | 
			
		||||
};
 | 
			
		||||
typedef struct _GeometryParseData GeometryParseData;
 | 
			
		||||
 | 
			
		||||
static GeometryParseData *
 | 
			
		||||
geometry_parse_data_new (struct squeek_view **views, GHashTable *name_button_hash, GArray *outline_array)
 | 
			
		||||
{
 | 
			
		||||
    GeometryParseData *data = g_slice_new0 (GeometryParseData);
 | 
			
		||||
 | 
			
		||||
    data->views = views;
 | 
			
		||||
    data->outline_array = outline_array;
 | 
			
		||||
    data->keyname_oref_hash =
 | 
			
		||||
        g_hash_table_new_full (g_str_hash,
 | 
			
		||||
                               g_str_equal,
 | 
			
		||||
                               g_free,
 | 
			
		||||
                               NULL);
 | 
			
		||||
    data->outlineid_oref_hash =
 | 
			
		||||
        g_hash_table_new_full (g_str_hash,
 | 
			
		||||
                               g_str_equal,
 | 
			
		||||
                               g_free,
 | 
			
		||||
                               NULL);
 | 
			
		||||
 | 
			
		||||
    data->name_button_hash = name_button_hash;
 | 
			
		||||
    data->text = g_string_sized_new (BUFSIZE);
 | 
			
		||||
    data->keycode = 8;
 | 
			
		||||
    return data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
geometry_parse_data_free (GeometryParseData *data)
 | 
			
		||||
{
 | 
			
		||||
    g_hash_table_destroy (data->keyname_oref_hash);
 | 
			
		||||
    g_hash_table_destroy (data->outlineid_oref_hash);
 | 
			
		||||
    g_string_free (data->text, TRUE);
 | 
			
		||||
    g_slice_free (GeometryParseData, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const gchar *geometry_valid_path_list[] = {
 | 
			
		||||
    "geometry",
 | 
			
		||||
    "button/geometry",
 | 
			
		||||
    "bounds/geometry",
 | 
			
		||||
    "view/geometry",
 | 
			
		||||
    "section/view/geometry",
 | 
			
		||||
    "outline/geometry",
 | 
			
		||||
    "point/outline/geometry",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
geometry_start_element_callback (GMarkupParseContext *pcontext,
 | 
			
		||||
                                 const gchar         *element_name,
 | 
			
		||||
                                 const gchar        **attribute_names,
 | 
			
		||||
                                 const gchar        **attribute_values,
 | 
			
		||||
                                 gpointer             user_data,
 | 
			
		||||
                                 GError             **error)
 | 
			
		||||
{
 | 
			
		||||
    GeometryParseData *data = user_data;
 | 
			
		||||
    const gchar *attribute;
 | 
			
		||||
 | 
			
		||||
    if (!validate (geometry_valid_path_list,
 | 
			
		||||
                   G_N_ELEMENTS (geometry_valid_path_list),
 | 
			
		||||
                   element_name,
 | 
			
		||||
                   data->element_stack,
 | 
			
		||||
                   error)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "bounds") == 0) {
 | 
			
		||||
        EekBounds bounds;
 | 
			
		||||
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values, "x");
 | 
			
		||||
        if (attribute == NULL) {
 | 
			
		||||
            g_set_error (error,
 | 
			
		||||
                         G_MARKUP_ERROR,
 | 
			
		||||
                         G_MARKUP_ERROR_MISSING_ATTRIBUTE,
 | 
			
		||||
                         "no \"x\" attribute for \"bounds\"");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        bounds.x = g_strtod (attribute, NULL);
 | 
			
		||||
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "y");
 | 
			
		||||
        if (attribute == NULL) {
 | 
			
		||||
            g_set_error (error,
 | 
			
		||||
                         G_MARKUP_ERROR,
 | 
			
		||||
                         G_MARKUP_ERROR_MISSING_ATTRIBUTE,
 | 
			
		||||
                         "no \"y\" attribute for \"bounds\"");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        bounds.y = g_strtod (attribute, NULL);
 | 
			
		||||
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "width");
 | 
			
		||||
        if (attribute == NULL) {
 | 
			
		||||
            g_set_error (error,
 | 
			
		||||
                         G_MARKUP_ERROR,
 | 
			
		||||
                         G_MARKUP_ERROR_MISSING_ATTRIBUTE,
 | 
			
		||||
                         "no \"width\" attribute for \"bounds\"");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        bounds.width = g_strtod (attribute, NULL);
 | 
			
		||||
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "height");
 | 
			
		||||
        if (attribute == NULL) {
 | 
			
		||||
            g_set_error (error,
 | 
			
		||||
                         G_MARKUP_ERROR,
 | 
			
		||||
                         G_MARKUP_ERROR_MISSING_ATTRIBUTE,
 | 
			
		||||
                         "no \"height\" attribute for \"bounds\"");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        bounds.height = g_strtod (attribute, NULL);
 | 
			
		||||
        data->bounds = bounds;
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "view") == 0) {
 | 
			
		||||
        /* Create an empty keyboard to which geometry and symbols
 | 
			
		||||
           information are applied. */
 | 
			
		||||
        struct squeek_view *view = squeek_view_new(data->bounds);
 | 
			
		||||
        data->views[data->view_idx] = view;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "section") == 0) {
 | 
			
		||||
        gint angle = 0;
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "angle");
 | 
			
		||||
        if (attribute != NULL) {
 | 
			
		||||
            angle = strtol (attribute, NULL, 10);
 | 
			
		||||
        }
 | 
			
		||||
        data->row = squeek_view_create_row(data->views[data->view_idx], angle);
 | 
			
		||||
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "button") == 0) {
 | 
			
		||||
        const gchar *base_name = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "name");
 | 
			
		||||
        if (base_name == NULL) {
 | 
			
		||||
            g_set_error (error,
 | 
			
		||||
                         G_MARKUP_ERROR,
 | 
			
		||||
                         G_MARKUP_ERROR_MISSING_ATTRIBUTE,
 | 
			
		||||
                         "no \"name\" attribute for \"button\"");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const gchar *oref_name = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "oref");
 | 
			
		||||
        if (oref_name == NULL) {
 | 
			
		||||
            oref_name = "default";
 | 
			
		||||
        }
 | 
			
		||||
        const gchar *keycode_name = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "keycode");
 | 
			
		||||
 | 
			
		||||
        guint oref = GPOINTER_TO_UINT(g_hash_table_lookup(data->outlineid_oref_hash,
 | 
			
		||||
                                      oref_name));
 | 
			
		||||
        const gchar *name = base_name;
 | 
			
		||||
        g_hash_table_insert (data->keyname_oref_hash,
 | 
			
		||||
                             g_strdup(name),
 | 
			
		||||
                             GUINT_TO_POINTER(oref));
 | 
			
		||||
 | 
			
		||||
        struct squeek_button *button = g_hash_table_lookup(data->name_button_hash, name);
 | 
			
		||||
        // never gets used! this section gets executed before any buttons get defined
 | 
			
		||||
        if (button) {
 | 
			
		||||
            if (keycode_name != NULL) {
 | 
			
		||||
                // This sets the keycode for all buttons,
 | 
			
		||||
                // since they share state
 | 
			
		||||
                // TODO: get rid of this in the parser;
 | 
			
		||||
                // this belongs after keymap is defined
 | 
			
		||||
                struct squeek_key *key = squeek_button_get_key(button);
 | 
			
		||||
                squeek_key_set_keycode(key, strtol (keycode_name, NULL, 10));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "outline") == 0) {
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values, "id");
 | 
			
		||||
        if (attribute == NULL) {
 | 
			
		||||
            g_set_error (error,
 | 
			
		||||
                         G_MARKUP_ERROR,
 | 
			
		||||
                         G_MARKUP_ERROR_MISSING_ATTRIBUTE,
 | 
			
		||||
                         "no \"id\" attribute for \"outline\"");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        data->outline_id = g_strdup (attribute);
 | 
			
		||||
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "corner-radius");
 | 
			
		||||
        if (attribute != NULL)
 | 
			
		||||
            data->corner_radius = g_strtod (attribute, NULL);
 | 
			
		||||
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "point") == 0) {
 | 
			
		||||
        EekPoint *point;
 | 
			
		||||
        gdouble x, y;
 | 
			
		||||
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values, "x");
 | 
			
		||||
        if (attribute == NULL) {
 | 
			
		||||
            g_set_error (error,
 | 
			
		||||
                         G_MARKUP_ERROR,
 | 
			
		||||
                         G_MARKUP_ERROR_MISSING_ATTRIBUTE,
 | 
			
		||||
                         "no \"x\" attribute for \"bounds\"");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        x = g_strtod (attribute, NULL);
 | 
			
		||||
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values, "y");
 | 
			
		||||
        if (attribute == NULL) {
 | 
			
		||||
            g_set_error (error,
 | 
			
		||||
                         G_MARKUP_ERROR,
 | 
			
		||||
                         G_MARKUP_ERROR_MISSING_ATTRIBUTE,
 | 
			
		||||
                         "no \"y\" attribute for \"bounds\"");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        y = g_strtod (attribute, NULL);
 | 
			
		||||
 | 
			
		||||
        point = g_slice_new (EekPoint);
 | 
			
		||||
        point->x = x;
 | 
			
		||||
        point->y = y;
 | 
			
		||||
 | 
			
		||||
        data->points = g_slist_prepend (data->points, point);
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 out:
 | 
			
		||||
    data->element_stack = g_slist_prepend (data->element_stack,
 | 
			
		||||
                                           g_strdup (element_name));
 | 
			
		||||
    data->text->len = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * eek_keyboard_add_outline:
 | 
			
		||||
 * @keyboard: an #EekKeyboard
 | 
			
		||||
 * @outline: an #EekOutline
 | 
			
		||||
 *
 | 
			
		||||
 * Register an outline of @keyboard.
 | 
			
		||||
 * Returns: an unsigned integer ID of the registered outline, for
 | 
			
		||||
 * later reference
 | 
			
		||||
 */
 | 
			
		||||
static guint
 | 
			
		||||
add_outline (GArray *outline_array,
 | 
			
		||||
                          EekOutline  *outline)
 | 
			
		||||
{
 | 
			
		||||
    EekOutline *_outline;
 | 
			
		||||
 | 
			
		||||
    _outline = eek_outline_copy (outline);
 | 
			
		||||
    g_array_append_val (outline_array, *_outline);
 | 
			
		||||
    /* don't use eek_outline_free here, so as to keep _outline->points */
 | 
			
		||||
    g_slice_free (EekOutline, _outline);
 | 
			
		||||
    return outline_array->len - 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
geometry_end_element_callback (GMarkupParseContext *pcontext,
 | 
			
		||||
                               const gchar         *element_name,
 | 
			
		||||
                               gpointer             user_data,
 | 
			
		||||
                               GError             **error)
 | 
			
		||||
{
 | 
			
		||||
    GeometryParseData *data = user_data;
 | 
			
		||||
    GSList *head = data->element_stack;
 | 
			
		||||
 | 
			
		||||
    g_free (head->data);
 | 
			
		||||
    data->element_stack = g_slist_next (data->element_stack);
 | 
			
		||||
    g_slist_free1 (head);
 | 
			
		||||
 | 
			
		||||
    const gchar *text = g_strndup (data->text->str, data->text->len);
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "view") == 0) {
 | 
			
		||||
        data->view_idx++;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "section") == 0) {
 | 
			
		||||
        // Split text on spaces and process each part
 | 
			
		||||
        unsigned head = 0;
 | 
			
		||||
        while (head < strlen(text)) {
 | 
			
		||||
            // Skip to the first non-space character
 | 
			
		||||
            for (; head < strlen(text); head++) {
 | 
			
		||||
                if (text[head] != ' ') {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            unsigned start = head;
 | 
			
		||||
 | 
			
		||||
            // Skip to the first space character
 | 
			
		||||
            for (; head < strlen(text); head++) {
 | 
			
		||||
                if (text[head] == ' ') {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            unsigned end = head;
 | 
			
		||||
 | 
			
		||||
            /// Reached the end
 | 
			
		||||
            if (start == end) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            gchar *name = g_strndup (&text[start], end - start);
 | 
			
		||||
            struct squeek_button *button = g_hash_table_lookup(data->name_button_hash, name);
 | 
			
		||||
            if (!button) {
 | 
			
		||||
                // Save button name together with its level,
 | 
			
		||||
                // to account for buttons with the same name in multiple levels
 | 
			
		||||
                guint keycode = data->keycode++;
 | 
			
		||||
 | 
			
		||||
                guint oref = GPOINTER_TO_UINT(g_hash_table_lookup(data->keyname_oref_hash, name));
 | 
			
		||||
                // default value gives idx 0, which is guaranteed to be occupied
 | 
			
		||||
                button = squeek_row_create_button (data->row, keycode, oref);
 | 
			
		||||
                g_hash_table_insert (data->name_button_hash,
 | 
			
		||||
                                     g_strdup(name),
 | 
			
		||||
                                     button);
 | 
			
		||||
            } else {
 | 
			
		||||
                struct squeek_button *new_button = squeek_row_create_button_with_state(data->row, button);
 | 
			
		||||
                if (!new_button) {
 | 
			
		||||
                    g_set_error (error,
 | 
			
		||||
                                 G_MARKUP_ERROR,
 | 
			
		||||
                                 G_MARKUP_ERROR_MISSING_ATTRIBUTE,
 | 
			
		||||
                                 "Couldn't create a shared button");
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        data->row = NULL;
 | 
			
		||||
        data->num_rows = 0;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "outline") == 0) {
 | 
			
		||||
        EekOutline *outline = g_slice_new (EekOutline);
 | 
			
		||||
 | 
			
		||||
        outline->corner_radius = data->corner_radius;
 | 
			
		||||
        data->corner_radius = 0.0;
 | 
			
		||||
 | 
			
		||||
        outline->num_points = g_slist_length (data->points);
 | 
			
		||||
        outline->points = g_slice_alloc0 (sizeof (EekPoint) *
 | 
			
		||||
                                          outline->num_points);
 | 
			
		||||
        guint i;
 | 
			
		||||
        for (i = 0, head = data->points = g_slist_reverse (data->points);
 | 
			
		||||
             head && i < outline->num_points;
 | 
			
		||||
             head = g_slist_next (head), i++) {
 | 
			
		||||
            memcpy (&outline->points[i], head->data, sizeof (EekPoint));
 | 
			
		||||
            g_slice_free1 (sizeof (EekPoint), head->data);
 | 
			
		||||
        }
 | 
			
		||||
        g_slist_free (data->points);
 | 
			
		||||
        data->points = NULL;
 | 
			
		||||
 | 
			
		||||
        guint oref = add_outline (data->outline_array, outline);
 | 
			
		||||
 | 
			
		||||
        g_hash_table_insert (data->outlineid_oref_hash,
 | 
			
		||||
                             data->outline_id,
 | 
			
		||||
                             GUINT_TO_POINTER(oref));
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
geometry_text_callback (GMarkupParseContext *pcontext,
 | 
			
		||||
                       const gchar         *text,
 | 
			
		||||
                       gsize                text_len,
 | 
			
		||||
                       gpointer             user_data,
 | 
			
		||||
                       GError             **error)
 | 
			
		||||
{
 | 
			
		||||
    GeometryParseData *data = user_data;
 | 
			
		||||
    g_string_append_len (data->text, text, (gssize)text_len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const GMarkupParser geometry_parser = {
 | 
			
		||||
    geometry_start_element_callback,
 | 
			
		||||
    geometry_end_element_callback,
 | 
			
		||||
    geometry_text_callback,
 | 
			
		||||
    0,
 | 
			
		||||
    0
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _SymbolsParseData {
 | 
			
		||||
    GSList *element_stack;
 | 
			
		||||
    GString *text;
 | 
			
		||||
@ -641,139 +251,6 @@ struct _SymbolsParseData {
 | 
			
		||||
};
 | 
			
		||||
typedef struct _SymbolsParseData SymbolsParseData;
 | 
			
		||||
 | 
			
		||||
static SymbolsParseData *
 | 
			
		||||
symbols_parse_data_new (LevelKeyboard *keyboard)
 | 
			
		||||
{
 | 
			
		||||
    SymbolsParseData *data = g_slice_new0 (SymbolsParseData);
 | 
			
		||||
 | 
			
		||||
    data->keyboard = keyboard;
 | 
			
		||||
    data->text = g_string_sized_new (BUFSIZE);
 | 
			
		||||
    return data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
symbols_parse_data_free (SymbolsParseData *data)
 | 
			
		||||
{
 | 
			
		||||
    g_string_free (data->text, TRUE);
 | 
			
		||||
    g_slice_free (SymbolsParseData, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const gchar *symbols_valid_path_list[] = {
 | 
			
		||||
    "symbols",
 | 
			
		||||
    "symbol/symbols",
 | 
			
		||||
    "include/symbols",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
symbols_start_element_callback (GMarkupParseContext *pcontext,
 | 
			
		||||
                                const gchar         *element_name,
 | 
			
		||||
                                const gchar        **attribute_names,
 | 
			
		||||
                                const gchar        **attribute_values,
 | 
			
		||||
                                gpointer             user_data,
 | 
			
		||||
                                GError             **error)
 | 
			
		||||
{
 | 
			
		||||
    SymbolsParseData *data = user_data;
 | 
			
		||||
    const gchar *attribute;
 | 
			
		||||
 | 
			
		||||
    if (!validate (symbols_valid_path_list,
 | 
			
		||||
                   G_N_ELEMENTS (symbols_valid_path_list),
 | 
			
		||||
                   element_name,
 | 
			
		||||
                   data->element_stack,
 | 
			
		||||
                   error))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "symbol") == 0) {
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "label");
 | 
			
		||||
        if (attribute != NULL)
 | 
			
		||||
            data->label = g_strdup (attribute);
 | 
			
		||||
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "icon");
 | 
			
		||||
        if (attribute != NULL)
 | 
			
		||||
            data->icon = g_strdup (attribute);
 | 
			
		||||
 | 
			
		||||
        attribute = get_attribute (attribute_names, attribute_values,
 | 
			
		||||
                                   "tooltip");
 | 
			
		||||
        if (attribute != NULL)
 | 
			
		||||
            data->tooltip = g_strdup (attribute);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    data->element_stack = g_slist_prepend (data->element_stack,
 | 
			
		||||
                                           g_strdup (element_name));
 | 
			
		||||
    data->text->len = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
symbols_end_element_callback (GMarkupParseContext *pcontext,
 | 
			
		||||
                              const gchar         *element_name,
 | 
			
		||||
                              gpointer             user_data,
 | 
			
		||||
                              GError             **error)
 | 
			
		||||
{
 | 
			
		||||
    SymbolsParseData *data = user_data;
 | 
			
		||||
    GSList *head = data->element_stack;
 | 
			
		||||
    gchar *text;
 | 
			
		||||
 | 
			
		||||
    g_free (head->data);
 | 
			
		||||
    data->element_stack = g_slist_next (data->element_stack);
 | 
			
		||||
    g_slist_free1 (head);
 | 
			
		||||
 | 
			
		||||
    // TODO: this could all be moved to text handler
 | 
			
		||||
    text = g_strndup (data->text->str, data->text->len);
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "symbol") == 0) {
 | 
			
		||||
 | 
			
		||||
        gchar *name = text;
 | 
			
		||||
        struct squeek_button *button = eek_keyboard_find_button_by_name (data->keyboard,
 | 
			
		||||
                                                   name);
 | 
			
		||||
        if (button) {
 | 
			
		||||
            squeek_key_add_symbol(
 | 
			
		||||
                squeek_button_get_key(button),
 | 
			
		||||
                element_name,
 | 
			
		||||
                text,
 | 
			
		||||
                data->keyval,
 | 
			
		||||
                data->label,
 | 
			
		||||
                data->icon,
 | 
			
		||||
                data->tooltip
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        data->keyval = 0;
 | 
			
		||||
        g_free(data->label);
 | 
			
		||||
        data->label = NULL;
 | 
			
		||||
        g_free(data->icon);
 | 
			
		||||
        data->icon = NULL;
 | 
			
		||||
        g_free(data->tooltip);
 | 
			
		||||
        data->tooltip = NULL;
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "invalid") == 0) {
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 out:
 | 
			
		||||
    g_free (text);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
symbols_text_callback (GMarkupParseContext *pcontext,
 | 
			
		||||
                       const gchar         *text,
 | 
			
		||||
                       gsize                text_len,
 | 
			
		||||
                       gpointer             user_data,
 | 
			
		||||
                       GError             **error)
 | 
			
		||||
{
 | 
			
		||||
    SymbolsParseData *data = user_data;
 | 
			
		||||
    g_string_append_len (data->text, text, text_len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const GMarkupParser symbols_parser = {
 | 
			
		||||
    symbols_start_element_callback,
 | 
			
		||||
    symbols_end_element_callback,
 | 
			
		||||
    symbols_text_callback,
 | 
			
		||||
    0,
 | 
			
		||||
    0
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct _PrerequisitesParseData {
 | 
			
		||||
    GSList *element_stack;
 | 
			
		||||
@ -783,157 +260,13 @@ struct _PrerequisitesParseData {
 | 
			
		||||
};
 | 
			
		||||
typedef struct _PrerequisitesParseData PrerequisitesParseData;
 | 
			
		||||
 | 
			
		||||
static PrerequisitesParseData *
 | 
			
		||||
prerequisites_parse_data_new (void)
 | 
			
		||||
{
 | 
			
		||||
    PrerequisitesParseData *data = g_slice_new0 (PrerequisitesParseData);
 | 
			
		||||
    data->text = g_string_sized_new (BUFSIZE);
 | 
			
		||||
    return data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
prerequisites_parse_data_free (PrerequisitesParseData *data)
 | 
			
		||||
{
 | 
			
		||||
    g_list_free_full (data->prerequisites, g_free);
 | 
			
		||||
    g_string_free (data->text, TRUE);
 | 
			
		||||
    g_slice_free (PrerequisitesParseData, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
prerequisites_start_element_callback (GMarkupParseContext *pcontext,
 | 
			
		||||
                                      const gchar         *element_name,
 | 
			
		||||
                                      const gchar        **attribute_names,
 | 
			
		||||
                                      const gchar        **attribute_values,
 | 
			
		||||
                                      gpointer             user_data,
 | 
			
		||||
                                      GError             **error)
 | 
			
		||||
{
 | 
			
		||||
    PrerequisitesParseData *data = user_data;
 | 
			
		||||
 | 
			
		||||
    if (!validate (symbols_valid_path_list,
 | 
			
		||||
                   G_N_ELEMENTS (symbols_valid_path_list),
 | 
			
		||||
                   element_name,
 | 
			
		||||
                   data->element_stack,
 | 
			
		||||
                   error))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    data->element_stack = g_slist_prepend (data->element_stack,
 | 
			
		||||
                                           g_strdup (element_name));
 | 
			
		||||
    data->text->len = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
prerequisites_end_element_callback (GMarkupParseContext *pcontext,
 | 
			
		||||
                                    const gchar         *element_name,
 | 
			
		||||
                                    gpointer             user_data,
 | 
			
		||||
                                    GError             **error)
 | 
			
		||||
{
 | 
			
		||||
    PrerequisitesParseData *data = user_data;
 | 
			
		||||
    GSList *head = data->element_stack;
 | 
			
		||||
 | 
			
		||||
    g_free (head->data);
 | 
			
		||||
    data->element_stack = g_slist_next (data->element_stack);
 | 
			
		||||
    g_slist_free1 (head);
 | 
			
		||||
 | 
			
		||||
    if (g_strcmp0 (element_name, "include") == 0) {
 | 
			
		||||
        data->prerequisites = g_list_append (data->prerequisites,
 | 
			
		||||
                                              g_strndup (data->text->str,
 | 
			
		||||
                                                         data->text->len));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
prerequisites_text_callback (GMarkupParseContext *pcontext,
 | 
			
		||||
                             const gchar         *text,
 | 
			
		||||
                             gsize                text_len,
 | 
			
		||||
                             gpointer             user_data,
 | 
			
		||||
                             GError             **error)
 | 
			
		||||
{
 | 
			
		||||
    PrerequisitesParseData *data = user_data;
 | 
			
		||||
    g_string_append_len (data->text, text, text_len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const GMarkupParser prerequisites_parser = {
 | 
			
		||||
    prerequisites_start_element_callback,
 | 
			
		||||
    prerequisites_end_element_callback,
 | 
			
		||||
    prerequisites_text_callback,
 | 
			
		||||
    0,
 | 
			
		||||
    0
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
LevelKeyboard *
 | 
			
		||||
eek_xml_layout_real_create_keyboard (EekLayout *self,
 | 
			
		||||
eek_xml_layout_real_create_keyboard (const char *keyboard_type,
 | 
			
		||||
                                     EekboardContextService *manager)
 | 
			
		||||
{
 | 
			
		||||
    EekXmlLayout *layout = EEK_XML_LAYOUT (self);
 | 
			
		||||
    EekXmlLayoutPrivate *priv = eek_xml_layout_get_instance_private (layout);
 | 
			
		||||
    gboolean retval;
 | 
			
		||||
 | 
			
		||||
    /* Read geometry information. */
 | 
			
		||||
    gchar *filename = g_strdup_printf ("%s.xml", priv->desc->geometry);
 | 
			
		||||
    gchar *path = g_build_filename (priv->keyboards_dir, "geometry", filename, NULL);
 | 
			
		||||
    g_free (filename);
 | 
			
		||||
 | 
			
		||||
    GArray *outline_array = g_array_new (FALSE, TRUE, sizeof (EekOutline));
 | 
			
		||||
 | 
			
		||||
    // char* -> struct squeek_button*
 | 
			
		||||
    GHashTable *name_button_hash =
 | 
			
		||||
            g_hash_table_new_full (g_str_hash,
 | 
			
		||||
                                   g_str_equal,
 | 
			
		||||
                                   g_free,
 | 
			
		||||
                                   NULL);
 | 
			
		||||
    // One view for each level
 | 
			
		||||
    struct squeek_view *views[4] = {0};
 | 
			
		||||
 | 
			
		||||
    GError *error = NULL;
 | 
			
		||||
    retval = parse_geometry (path, views, outline_array, name_button_hash, &error);
 | 
			
		||||
    g_free (path);
 | 
			
		||||
    if (!retval) {
 | 
			
		||||
        for (uint i = 0; i < 4; i++) {
 | 
			
		||||
            g_object_unref (views[i]);
 | 
			
		||||
        }
 | 
			
		||||
        g_warning ("can't parse geometry file %s: %s",
 | 
			
		||||
                   priv->desc->geometry,
 | 
			
		||||
                   error->message);
 | 
			
		||||
        g_error_free (error);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    LevelKeyboard *keyboard = level_keyboard_new(manager, views, name_button_hash);
 | 
			
		||||
 | 
			
		||||
    keyboard->outline_array = outline_array;
 | 
			
		||||
    // FIXME: are symbols shared betwen views?
 | 
			
		||||
 | 
			
		||||
    /* Read symbols information. */
 | 
			
		||||
    GList *loaded = NULL;
 | 
			
		||||
    retval = parse_symbols_with_prerequisites (priv->keyboards_dir,
 | 
			
		||||
                                               priv->desc->symbols,
 | 
			
		||||
                                               keyboard,
 | 
			
		||||
                                               &loaded,
 | 
			
		||||
                                               &error);
 | 
			
		||||
    g_list_free_full (loaded, g_free);
 | 
			
		||||
    if (!retval) {
 | 
			
		||||
        for (uint i = 0; i < 4; i++) {
 | 
			
		||||
            if (views[i]) {
 | 
			
		||||
                g_object_unref(views[i]);
 | 
			
		||||
                views[i] = NULL;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        //g_object_unref (view);
 | 
			
		||||
        g_warning ("can't parse symbols file %s: %s",
 | 
			
		||||
                   priv->desc->symbols,
 | 
			
		||||
                   error->message);
 | 
			
		||||
        g_error_free (error);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (uint i = 0; i < 4; i++) {
 | 
			
		||||
        if (views[i]) {
 | 
			
		||||
            squeek_view_place_contents(views[i], keyboard);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return keyboard;
 | 
			
		||||
    struct squeek_layout *layout = squeek_load_layout(keyboard_type);
 | 
			
		||||
    squeek_layout_place_contents(layout);
 | 
			
		||||
    return level_keyboard_new(manager, layout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@ -1116,190 +449,6 @@ eek_xml_keyboard_desc_free (EekXmlKeyboardDesc *desc)
 | 
			
		||||
    g_slice_free (EekXmlKeyboardDesc, desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
parse_geometry (const gchar *path, struct squeek_view **views, GArray *outline_array, GHashTable *name_button_hash, GError **error)
 | 
			
		||||
{
 | 
			
		||||
    GeometryParseData *data;
 | 
			
		||||
    GMarkupParseContext *pcontext;
 | 
			
		||||
    GFile *file;
 | 
			
		||||
    GFileInputStream *input;
 | 
			
		||||
    gboolean retval;
 | 
			
		||||
 | 
			
		||||
    file = g_str_has_prefix (path, "resource://")
 | 
			
		||||
         ? g_file_new_for_uri  (path)
 | 
			
		||||
         : g_file_new_for_path (path);
 | 
			
		||||
 | 
			
		||||
    input = g_file_read (file, NULL, error);
 | 
			
		||||
    g_object_unref (file);
 | 
			
		||||
    if (input == NULL)
 | 
			
		||||
        return FALSE;
 | 
			
		||||
 | 
			
		||||
    data = geometry_parse_data_new (views, name_button_hash, outline_array);
 | 
			
		||||
    pcontext = g_markup_parse_context_new (&geometry_parser,
 | 
			
		||||
                                           0,
 | 
			
		||||
                                           data,
 | 
			
		||||
                                           NULL);
 | 
			
		||||
 | 
			
		||||
    retval = parse (pcontext, G_INPUT_STREAM (input), error);
 | 
			
		||||
    g_markup_parse_context_free (pcontext);
 | 
			
		||||
    g_object_unref (input);
 | 
			
		||||
    if (!retval) {
 | 
			
		||||
        geometry_parse_data_free (data);
 | 
			
		||||
        return FALSE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Resolve outline references. */
 | 
			
		||||
    /*
 | 
			
		||||
     * GHashTable *oref_hash;
 | 
			
		||||
    GHashTableIter iter;
 | 
			
		||||
    gpointer k, v;
 | 
			
		||||
 | 
			
		||||
    oref_hash = g_hash_table_new (g_str_hash, g_str_equal);
 | 
			
		||||
    g_hash_table_iter_init (&iter, data->oref_outline_hash);
 | 
			
		||||
    while (g_hash_table_iter_next (&iter, &k, &v)) {
 | 
			
		||||
        EekOutline *outline = v;
 | 
			
		||||
        gulong oref;
 | 
			
		||||
 | 
			
		||||
        oref = add_outline (outline_array, outline);
 | 
			
		||||
        g_hash_table_insert (oref_hash, k, GUINT_TO_POINTER(oref));
 | 
			
		||||
    }*/
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    g_hash_table_iter_init (&iter, data->key_oref_hash);
 | 
			
		||||
    while (g_hash_table_iter_next (&iter, &k, &v)) {
 | 
			
		||||
        gpointer oref;
 | 
			
		||||
        if (g_hash_table_lookup_extended (oref_hash, v, NULL, &oref))
 | 
			
		||||
            eek_key_set_oref (EEK_KEY(k), GPOINTER_TO_UINT(oref));
 | 
			
		||||
    }*/
 | 
			
		||||
//    g_hash_table_destroy (oref_hash);
 | 
			
		||||
 | 
			
		||||
    geometry_parse_data_free (data);
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
parse_symbols_with_prerequisites (const gchar *keyboards_dir,
 | 
			
		||||
                                  const gchar *name,
 | 
			
		||||
                                  LevelKeyboard *keyboard,
 | 
			
		||||
                                  GList     **loaded,
 | 
			
		||||
                                  GError     **error)
 | 
			
		||||
{
 | 
			
		||||
    gchar *filename, *path;
 | 
			
		||||
    GList *prerequisites, *p;
 | 
			
		||||
    GError *prerequisites_error;
 | 
			
		||||
    gboolean retval;
 | 
			
		||||
 | 
			
		||||
    for (p = *loaded; p; p = p->next) {
 | 
			
		||||
        if (g_strcmp0 (p->data, name) == 0) {
 | 
			
		||||
            g_set_error (error,
 | 
			
		||||
                         EEK_ERROR,
 | 
			
		||||
                         EEK_ERROR_LAYOUT_ERROR,
 | 
			
		||||
                         "%s already loaded",
 | 
			
		||||
                         name);
 | 
			
		||||
            return FALSE;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    *loaded = g_list_prepend (*loaded, g_strdup (name));
 | 
			
		||||
 | 
			
		||||
    filename = g_strdup_printf ("%s.xml", name);
 | 
			
		||||
    path = g_build_filename (keyboards_dir, "symbols", filename, NULL);
 | 
			
		||||
    g_free (filename);
 | 
			
		||||
 | 
			
		||||
    prerequisites_error = NULL;
 | 
			
		||||
    prerequisites = parse_prerequisites (path, &prerequisites_error);
 | 
			
		||||
    if (prerequisites_error != NULL) {
 | 
			
		||||
        g_propagate_error (error, prerequisites_error);
 | 
			
		||||
        return FALSE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (p = prerequisites; p; p = p->next) {
 | 
			
		||||
        retval = parse_symbols_with_prerequisites (keyboards_dir,
 | 
			
		||||
                                                   p->data,
 | 
			
		||||
                                                   keyboard,
 | 
			
		||||
                                                   loaded,
 | 
			
		||||
                                                   error);
 | 
			
		||||
        if (!retval)
 | 
			
		||||
            return FALSE;
 | 
			
		||||
    }
 | 
			
		||||
    g_list_free_full (prerequisites, (GDestroyNotify)g_free);
 | 
			
		||||
 | 
			
		||||
    retval = parse_symbols (path, keyboard, error);
 | 
			
		||||
    g_free (path);
 | 
			
		||||
 | 
			
		||||
    return retval;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
parse_symbols (const gchar *path, LevelKeyboard *keyboard, GError **error)
 | 
			
		||||
{
 | 
			
		||||
    SymbolsParseData *data;
 | 
			
		||||
    GMarkupParseContext *pcontext;
 | 
			
		||||
    GFile *file;
 | 
			
		||||
    GFileInputStream *input;
 | 
			
		||||
    gboolean retval;
 | 
			
		||||
 | 
			
		||||
    file = g_str_has_prefix (path, "resource://")
 | 
			
		||||
         ? g_file_new_for_uri  (path)
 | 
			
		||||
         : g_file_new_for_path (path);
 | 
			
		||||
 | 
			
		||||
    input = g_file_read (file, NULL, error);
 | 
			
		||||
    g_object_unref (file);
 | 
			
		||||
    if (input == NULL)
 | 
			
		||||
        return FALSE;
 | 
			
		||||
 | 
			
		||||
    data = symbols_parse_data_new (keyboard);
 | 
			
		||||
    pcontext = g_markup_parse_context_new (&symbols_parser,
 | 
			
		||||
                                           0,
 | 
			
		||||
                                           data,
 | 
			
		||||
                                           NULL);
 | 
			
		||||
    retval = parse (pcontext, G_INPUT_STREAM (input), error);
 | 
			
		||||
    g_markup_parse_context_free (pcontext);
 | 
			
		||||
    g_object_unref (input);
 | 
			
		||||
    if (!retval) {
 | 
			
		||||
        symbols_parse_data_free (data);
 | 
			
		||||
        return FALSE;
 | 
			
		||||
    }
 | 
			
		||||
    symbols_parse_data_free (data);
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static GList *
 | 
			
		||||
parse_prerequisites (const gchar *path, GError **error)
 | 
			
		||||
{
 | 
			
		||||
    PrerequisitesParseData *data;
 | 
			
		||||
    GMarkupParseContext *pcontext;
 | 
			
		||||
    GFile *file;
 | 
			
		||||
    GFileInputStream *input;
 | 
			
		||||
    GList *prerequisites;
 | 
			
		||||
    gboolean retval;
 | 
			
		||||
 | 
			
		||||
    file = g_str_has_prefix (path, "resource://")
 | 
			
		||||
         ? g_file_new_for_uri  (path)
 | 
			
		||||
         : g_file_new_for_path (path);
 | 
			
		||||
 | 
			
		||||
    input = g_file_read (file, NULL, error);
 | 
			
		||||
    g_object_unref (file);
 | 
			
		||||
    if (input == NULL)
 | 
			
		||||
        return FALSE;
 | 
			
		||||
 | 
			
		||||
    data = prerequisites_parse_data_new ();
 | 
			
		||||
    pcontext = g_markup_parse_context_new (&prerequisites_parser,
 | 
			
		||||
                                           0,
 | 
			
		||||
                                           data,
 | 
			
		||||
                                           NULL);
 | 
			
		||||
    retval = parse (pcontext, G_INPUT_STREAM (input), error);
 | 
			
		||||
    g_markup_parse_context_free (pcontext);
 | 
			
		||||
    g_object_unref (input);
 | 
			
		||||
    if (!retval) {
 | 
			
		||||
        prerequisites_parse_data_free (data);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    prerequisites = data->prerequisites;
 | 
			
		||||
    data->prerequisites = NULL;
 | 
			
		||||
    prerequisites_parse_data_free (data);
 | 
			
		||||
    return prerequisites;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static GList *
 | 
			
		||||
parse_keyboards (const gchar *path, GError **error)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -63,7 +63,7 @@ EekXmlKeyboardDesc *eek_xml_keyboard_desc_copy (EekXmlKeyboardDesc *desc);
 | 
			
		||||
void                eek_xml_keyboard_desc_free (EekXmlKeyboardDesc *desc);
 | 
			
		||||
 | 
			
		||||
LevelKeyboard *
 | 
			
		||||
eek_xml_layout_real_create_keyboard (EekLayout *self,
 | 
			
		||||
eek_xml_layout_real_create_keyboard (const char *keyboard_type,
 | 
			
		||||
                                     EekboardContextService *manager);
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
#endif  /* EEK_XML_LAYOUT_H */
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										31
									
								
								eek/keymap.h
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								eek/keymap.h
									
									
									
									
									
								
							@ -6,34 +6,3 @@ squeek_keymap_get_entries_for_keyval (struct xkb_keymap     *xkb_keymap,
 | 
			
		||||
                                      guint          keyval,
 | 
			
		||||
                                      GdkKeymapKey **keys,
 | 
			
		||||
                                      guint          *n_keys);
 | 
			
		||||
 | 
			
		||||
static const char *keymap_header = "xkb_keymap {\n\
 | 
			
		||||
\n";
 | 
			
		||||
 | 
			
		||||
static const char *keymap_keycodes_header = "\
 | 
			
		||||
    xkb_keycodes \"squeekboard\" {\n\n\
 | 
			
		||||
        minimum = 8;\n\
 | 
			
		||||
        maximum = 255;\n\
 | 
			
		||||
\n";
 | 
			
		||||
 | 
			
		||||
static const char *keymap_symbols_header = "\
 | 
			
		||||
    xkb_symbols \"squeekboard\" {\n\
 | 
			
		||||
\n\
 | 
			
		||||
        name[Group1] = \"Letters\";\n\
 | 
			
		||||
        name[Group2] = \"Numbers/Symbols\";\n\
 | 
			
		||||
\n";
 | 
			
		||||
 | 
			
		||||
static const char *keymap_footer = "\
 | 
			
		||||
    xkb_types \"squeekboard\" {\n\
 | 
			
		||||
\n\
 | 
			
		||||
	type \"TWO_LEVEL\" {\n\
 | 
			
		||||
            modifiers = Shift;\n\
 | 
			
		||||
            map[Shift] = Level2;\n\
 | 
			
		||||
            level_name[Level1] = \"Base\";\n\
 | 
			
		||||
            level_name[Level2] = \"Shift\";\n\
 | 
			
		||||
	};\n\
 | 
			
		||||
    };\n\
 | 
			
		||||
\n\
 | 
			
		||||
    xkb_compatibility \"squeekboard\" {\n\
 | 
			
		||||
    };\n\
 | 
			
		||||
};";
 | 
			
		||||
 | 
			
		||||
@ -89,62 +89,17 @@ static LevelKeyboard *
 | 
			
		||||
eekboard_context_service_real_create_keyboard (EekboardContextService *self,
 | 
			
		||||
                                               const gchar            *keyboard_type)
 | 
			
		||||
{
 | 
			
		||||
    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);
 | 
			
		||||
    LevelKeyboard *keyboard = eek_xml_layout_real_create_keyboard(keyboard_type, self);
 | 
			
		||||
    if (!keyboard) {
 | 
			
		||||
        g_error("Failed to create a keyboard");
 | 
			
		||||
    }
 | 
			
		||||
    g_object_unref (layout);
 | 
			
		||||
 | 
			
		||||
    struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
 | 
			
		||||
    if (!context) {
 | 
			
		||||
        g_error("No context created");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    gchar *keymap_str = eek_keyboard_get_keymap(keyboard);
 | 
			
		||||
    const gchar *keymap_str = squeek_layout_get_keymap(keyboard->layout);
 | 
			
		||||
 | 
			
		||||
    struct xkb_keymap *keymap = xkb_keymap_new_from_string(context, keymap_str,
 | 
			
		||||
        XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
 | 
			
		||||
@ -152,8 +107,6 @@ eekboard_context_service_real_create_keyboard (EekboardContextService *self,
 | 
			
		||||
    if (!keymap)
 | 
			
		||||
        g_error("Bad keymap:\n%s", keymap_str);
 | 
			
		||||
 | 
			
		||||
    free(keymap_str);
 | 
			
		||||
 | 
			
		||||
    xkb_context_unref(context);
 | 
			
		||||
    keyboard->keymap = keymap;
 | 
			
		||||
 | 
			
		||||
@ -183,7 +136,6 @@ eekboard_context_service_real_create_keyboard (EekboardContextService *self,
 | 
			
		||||
    }
 | 
			
		||||
    strcpy(ptr, keymap_str);
 | 
			
		||||
    munmap(ptr, keyboard->keymap_len);
 | 
			
		||||
    free(keymap_str);
 | 
			
		||||
 | 
			
		||||
    return keyboard;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -90,12 +90,9 @@ send_fake_key (SeatEmitter *emitter,
 | 
			
		||||
               gboolean pressed,
 | 
			
		||||
               uint32_t timestamp)
 | 
			
		||||
{
 | 
			
		||||
    guint level = keyboard->level;
 | 
			
		||||
    uint32_t group = (level / 2);
 | 
			
		||||
 | 
			
		||||
    zwp_virtual_keyboard_v1_modifiers(emitter->virtual_keyboard, 0, 0, 0, group);
 | 
			
		||||
    zwp_virtual_keyboard_v1_modifiers(emitter->virtual_keyboard, 0, 0, 0, 0);
 | 
			
		||||
    send_virtual_keyboard_key (emitter->virtual_keyboard, keycode - 8, (unsigned)pressed, timestamp);
 | 
			
		||||
    zwp_virtual_keyboard_v1_modifiers(emitter->virtual_keyboard, 0, 0, 0, group);
 | 
			
		||||
    zwp_virtual_keyboard_v1_modifiers(emitter->virtual_keyboard, 0, 0, 0, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
project(
 | 
			
		||||
    'squeekboard',
 | 
			
		||||
    'c', 'rust',
 | 
			
		||||
    version: '1.0.10',
 | 
			
		||||
    version: '1.1.0',
 | 
			
		||||
    license: 'GPLv3',
 | 
			
		||||
    meson_version: '>=0.51.0',
 | 
			
		||||
    default_options: [
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										533
									
								
								src/data.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										533
									
								
								src/data.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,533 @@
 | 
			
		||||
/**! 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 ::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)]
 | 
			
		||||
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),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)]
 | 
			
		||||
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)]
 | 
			
		||||
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)
 | 
			
		||||
    }
 | 
			
		||||
    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()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    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
 | 
			
		||||
                ),
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
        _ => ::symbol::Symbol {
 | 
			
		||||
            action: ::symbol::Action::Submit {
 | 
			
		||||
                text: None,
 | 
			
		||||
                // TODO: derive keysym name & value from button name
 | 
			
		||||
                keys: vec!(),
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// 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 parsing_fallback() {
 | 
			
		||||
        assert!(load_layout_from_resource(FALLBACK_LAYOUT_NAME)
 | 
			
		||||
            .and_then(|layout| layout.build().map_err(LoadError::BadKeyMap))
 | 
			
		||||
            .is_ok()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -4,6 +4,7 @@ use std::num::Wrapping;
 | 
			
		||||
use std::string::String;
 | 
			
		||||
 | 
			
		||||
use super::bitflags;
 | 
			
		||||
use ::util::c::into_cstring;
 | 
			
		||||
 | 
			
		||||
// Traits
 | 
			
		||||
use std::convert::TryFrom;
 | 
			
		||||
@ -13,15 +14,8 @@ use std::convert::TryFrom;
 | 
			
		||||
pub mod c {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    
 | 
			
		||||
    use std::ffi::CStr;
 | 
			
		||||
    use std::os::raw::{c_char, c_void};
 | 
			
		||||
 | 
			
		||||
    fn into_cstring(s: *const c_char) -> Result<CString, std::ffi::NulError> {
 | 
			
		||||
        CString::new(
 | 
			
		||||
            unsafe {CStr::from_ptr(s)}.to_bytes()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // The following defined in C
 | 
			
		||||
        
 | 
			
		||||
    /// struct zwp_input_method_v2*
 | 
			
		||||
@ -91,7 +85,9 @@ pub mod c {
 | 
			
		||||
    {
 | 
			
		||||
        let imservice = check_imservice(imservice, im).unwrap();
 | 
			
		||||
        imservice.pending = IMProtocolState {
 | 
			
		||||
            surrounding_text: into_cstring(text).expect("Received invalid string"),
 | 
			
		||||
            surrounding_text: into_cstring(text)
 | 
			
		||||
                .expect("Received invalid string")
 | 
			
		||||
                .expect("Received null string"),
 | 
			
		||||
            surrounding_cursor: cursor,
 | 
			
		||||
            ..imservice.pending.clone()
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										293
									
								
								src/keyboard.rs
									
									
									
									
									
								
							
							
						
						
									
										293
									
								
								src/keyboard.rs
									
									
									
									
									
								
							@ -1,46 +1,30 @@
 | 
			
		||||
use std::vec::Vec;
 | 
			
		||||
/*! State of the emulated keyboard and keys */
 | 
			
		||||
 | 
			
		||||
use super::symbol;
 | 
			
		||||
use std::cell::RefCell;
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use std::fmt;
 | 
			
		||||
use std::io;
 | 
			
		||||
use std::rc::Rc;
 | 
			
		||||
use std::string::FromUtf8Error;
 | 
			
		||||
    
 | 
			
		||||
use ::symbol::{ Symbol, Action };
 | 
			
		||||
 | 
			
		||||
use std::io::Write;
 | 
			
		||||
use std::iter::{ FromIterator, IntoIterator };
 | 
			
		||||
 | 
			
		||||
/// Gathers stuff defined in C or called by C
 | 
			
		||||
pub mod c {
 | 
			
		||||
    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::os::raw::c_char;
 | 
			
		||||
    use std::ptr;
 | 
			
		||||
    use std::rc::Rc;
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    // The following defined in C
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        fn eek_keysym_from_name(name: *const c_char) -> u32;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub type CKeyState = c::Wrapped<KeyState>;
 | 
			
		||||
 | 
			
		||||
    // The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
 | 
			
		||||
 | 
			
		||||
    // TODO: this will receive data from the filesystem,
 | 
			
		||||
    // so it should handle garbled strings in the future
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_key_new(keycode: u32) -> CKeyState {
 | 
			
		||||
        let state: Rc<RefCell<KeyState>> = Rc::new(RefCell::new(
 | 
			
		||||
            KeyState {
 | 
			
		||||
                pressed: false,
 | 
			
		||||
                locked: false,
 | 
			
		||||
                keycode: keycode,
 | 
			
		||||
                symbol: None,
 | 
			
		||||
            }
 | 
			
		||||
        ));
 | 
			
		||||
        CKeyState::wrap(state)
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_key_free(key: CKeyState) {
 | 
			
		||||
@ -86,105 +70,7 @@ pub mod c {
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_key_get_keycode(key: CKeyState) -> u32 {
 | 
			
		||||
        return key.to_owned().keycode as u32;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_key_set_keycode(key: CKeyState, code: u32) {
 | 
			
		||||
        let key = key.clone_ref();
 | 
			
		||||
        let mut key = key.borrow_mut();
 | 
			
		||||
        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
 | 
			
		||||
            });
 | 
			
		||||
        
 | 
			
		||||
        let key = key.clone_ref();
 | 
			
		||||
        let mut key = key.borrow_mut();
 | 
			
		||||
 | 
			
		||||
        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 {
 | 
			
		||||
        let key = key.clone_ref();
 | 
			
		||||
        let key = key.borrow();
 | 
			
		||||
        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(),
 | 
			
		||||
        }
 | 
			
		||||
        return key.to_owned().keycode.unwrap_or(0u32);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
@ -198,20 +84,14 @@ pub mod c {
 | 
			
		||||
            .to_str()
 | 
			
		||||
            .expect("Bad key name");
 | 
			
		||||
 | 
			
		||||
        let symbol_name = match key.to_owned().symbol {
 | 
			
		||||
            Some(ref symbol) => match &symbol.action {
 | 
			
		||||
                symbol::Action::Submit { text: Some(text), .. } => {
 | 
			
		||||
                    Some(
 | 
			
		||||
                        text.clone()
 | 
			
		||||
                            .into_string().expect("Bad symbol")
 | 
			
		||||
                    )
 | 
			
		||||
                },
 | 
			
		||||
                _ => None
 | 
			
		||||
            },
 | 
			
		||||
            None => {
 | 
			
		||||
                eprintln!("Key {} has no symbol", key_name);
 | 
			
		||||
                None
 | 
			
		||||
        let symbol_name = match key.to_owned().symbol.action {
 | 
			
		||||
            Action::Submit { text: Some(text), .. } => {
 | 
			
		||||
                Some(
 | 
			
		||||
                    text.clone()
 | 
			
		||||
                        .into_string().expect("Bad symbol")
 | 
			
		||||
                )
 | 
			
		||||
            },
 | 
			
		||||
            _ => None,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let inner = match symbol_name {
 | 
			
		||||
@ -224,7 +104,7 @@ pub mod c {
 | 
			
		||||
            .into_raw()
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
        #[no_mangle]
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_key_get_action_name(
 | 
			
		||||
        key_name: *const c_char,
 | 
			
		||||
@ -235,20 +115,14 @@ pub mod c {
 | 
			
		||||
            .to_str()
 | 
			
		||||
            .expect("Bad key name");
 | 
			
		||||
 | 
			
		||||
        let symbol_name = match key.to_owned().symbol {
 | 
			
		||||
            Some(ref symbol) => match &symbol.action {
 | 
			
		||||
                symbol::Action::Submit { text: Some(text), .. } => {
 | 
			
		||||
                    Some(
 | 
			
		||||
                        text.clone()
 | 
			
		||||
                            .into_string().expect("Bad symbol")
 | 
			
		||||
                    )
 | 
			
		||||
                },
 | 
			
		||||
                _ => None
 | 
			
		||||
            },
 | 
			
		||||
            None => {
 | 
			
		||||
                eprintln!("Key {} has no symbol", key_name);
 | 
			
		||||
                None
 | 
			
		||||
        let symbol_name = match key.to_owned().symbol.action {
 | 
			
		||||
            Action::Submit { text: Some(text), .. } => {
 | 
			
		||||
                Some(
 | 
			
		||||
                    text.clone()
 | 
			
		||||
                        .into_string().expect("Bad symbol text")
 | 
			
		||||
                )
 | 
			
		||||
            },
 | 
			
		||||
            _ => None
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let inner = match symbol_name {
 | 
			
		||||
@ -260,14 +134,115 @@ pub mod c {
 | 
			
		||||
            .expect("Couldn't convert string")
 | 
			
		||||
            .into_raw()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct KeyState {
 | 
			
		||||
    pub pressed: bool,
 | 
			
		||||
    pub locked: bool,
 | 
			
		||||
    pub keycode: u32,
 | 
			
		||||
    // TODO: remove the optionality of a symbol
 | 
			
		||||
    pub symbol: Option<symbol::Symbol>,
 | 
			
		||||
    pub keycode: Option<u32>,
 | 
			
		||||
    pub symbol: Symbol,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Generates a mapping where each key gets a keycode, starting from 8
 | 
			
		||||
pub fn generate_keycodes<'a, C: IntoIterator<Item=&'a str>>(
 | 
			
		||||
    key_names: C
 | 
			
		||||
) -> HashMap<String, u32> {
 | 
			
		||||
    HashMap::from_iter(
 | 
			
		||||
        key_names.into_iter()
 | 
			
		||||
            .map(|name| String::from(name))
 | 
			
		||||
            .zip(8..)
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub enum FormattingError {
 | 
			
		||||
    Utf(FromUtf8Error),
 | 
			
		||||
    Format(io::Error),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for FormattingError {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            FormattingError::Utf(e) => write!(f, "UTF: {}", e),
 | 
			
		||||
            FormattingError::Format(e) => write!(f, "Format: {}", e),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<io::Error> for FormattingError {
 | 
			
		||||
    fn from(e: io::Error) -> Self {
 | 
			
		||||
        FormattingError::Format(e)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Generates a de-facto single level keymap. TODO: actually drop second level
 | 
			
		||||
/// TODO: take in keysym->keycode mapping
 | 
			
		||||
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() {
 | 
			
		||||
        if let Some(keycode) = state.borrow().keycode {
 | 
			
		||||
            write!(
 | 
			
		||||
                buf,
 | 
			
		||||
                "
 | 
			
		||||
        <{}> = {};",
 | 
			
		||||
                name,
 | 
			
		||||
                keycode
 | 
			
		||||
            )?;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    writeln!(
 | 
			
		||||
        buf,
 | 
			
		||||
        "
 | 
			
		||||
    }};
 | 
			
		||||
    
 | 
			
		||||
    xkb_symbols \"squeekboard\" {{
 | 
			
		||||
 | 
			
		||||
        name[Group1] = \"Letters\";
 | 
			
		||||
        name[Group2] = \"Numbers/Symbols\";"
 | 
			
		||||
    )?;
 | 
			
		||||
    
 | 
			
		||||
    for (name, state) in keystates.iter() {
 | 
			
		||||
        if let Some(_) = state.borrow().keycode {
 | 
			
		||||
            write!(
 | 
			
		||||
                buf,
 | 
			
		||||
                "
 | 
			
		||||
        key <{}> {{ [ {0} ] }};",
 | 
			
		||||
                name,
 | 
			
		||||
            )?;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										37
									
								
								src/layout.h
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								src/layout.h
									
									
									
									
									
								
							@ -9,12 +9,8 @@
 | 
			
		||||
struct squeek_button;
 | 
			
		||||
struct squeek_row;
 | 
			
		||||
struct squeek_view;
 | 
			
		||||
struct squeek_layout;
 | 
			
		||||
 | 
			
		||||
struct squeek_row *squeek_row_new(int32_t angle);
 | 
			
		||||
struct squeek_button *squeek_row_create_button (struct squeek_row *row,
 | 
			
		||||
                                          guint keycode, guint oref);
 | 
			
		||||
struct squeek_button *squeek_row_create_button_with_state(struct squeek_row *row,
 | 
			
		||||
                                    struct squeek_button *source);
 | 
			
		||||
int32_t squeek_row_get_angle(const struct squeek_row*);
 | 
			
		||||
 | 
			
		||||
EekBounds squeek_row_get_bounds(const struct squeek_row*);
 | 
			
		||||
@ -31,33 +27,19 @@ void squeek_row_foreach(struct squeek_row*,
 | 
			
		||||
 | 
			
		||||
void squeek_row_free(struct squeek_row*);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
struct squeek_button *squeek_buttons_find_by_position(
 | 
			
		||||
    const struct squeek_buttons *buttons,
 | 
			
		||||
    double x, double y,
 | 
			
		||||
    double origin_x, double origin_y,
 | 
			
		||||
    double angle);
 | 
			
		||||
void squeek_buttons_add(struct squeek_buttons*, const struct squeek_button* button);
 | 
			
		||||
void squeek_buttons_remove_key(struct squeek_buttons*, const struct squeek_key* key);
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
struct squeek_button *squeek_button_new(uint32_t keycode, uint32_t oref);
 | 
			
		||||
struct squeek_button *squeek_button_new_with_state(const struct squeek_button* source);
 | 
			
		||||
uint32_t squeek_button_get_oref(const struct squeek_button*);
 | 
			
		||||
EekBounds squeek_button_get_bounds(const struct squeek_button*);
 | 
			
		||||
const char *squeek_button_get_label(const struct squeek_button*);
 | 
			
		||||
const char *squeek_button_get_icon_name(const struct squeek_button*);
 | 
			
		||||
const char *squeek_button_get_name(const struct squeek_button*);
 | 
			
		||||
 | 
			
		||||
struct squeek_symbol *squeek_button_get_symbol (
 | 
			
		||||
    const struct squeek_button *button);
 | 
			
		||||
struct squeek_key *squeek_button_get_key(const struct squeek_button*);
 | 
			
		||||
uint32_t *squeek_button_has_key(const struct squeek_button* button,
 | 
			
		||||
                                const struct squeek_key *key);
 | 
			
		||||
void squeek_button_print(const struct squeek_button* button);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct squeek_view *squeek_view_new(EekBounds bounds);
 | 
			
		||||
struct squeek_row *squeek_view_create_row(struct squeek_view *, int32_t angle);
 | 
			
		||||
EekBounds squeek_view_get_bounds(const struct squeek_view*);
 | 
			
		||||
void squeek_view_set_bounds(const struct squeek_view*, EekBounds bounds);
 | 
			
		||||
 | 
			
		||||
typedef void (*RowCallback) (struct squeek_row *row, gpointer user_data);
 | 
			
		||||
void squeek_view_foreach(struct squeek_view*,
 | 
			
		||||
@ -67,9 +49,16 @@ void squeek_view_foreach(struct squeek_view*,
 | 
			
		||||
struct squeek_row *squeek_view_get_row(struct squeek_view *view,
 | 
			
		||||
                                       struct squeek_button *button);
 | 
			
		||||
 | 
			
		||||
struct squeek_button *squeek_view_find_button_by_position(struct squeek_view *view, EekPoint point);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
squeek_view_place_contents(struct squeek_view *view, LevelKeyboard *keyboard);
 | 
			
		||||
squeek_layout_place_contents(struct squeek_layout*);
 | 
			
		||||
struct squeek_view *squeek_layout_get_current_view(struct squeek_layout*);
 | 
			
		||||
void squeek_layout_set_state_from_press(struct squeek_layout* layout, LevelKeyboard *keyboard, struct squeek_key* key);
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										450
									
								
								src/layout.rs
									
									
									
									
									
								
							
							
						
						
									
										450
									
								
								src/layout.rs
									
									
									
									
									
								
							@ -1,4 +1,4 @@
 | 
			
		||||
/**
 | 
			
		||||
/*!
 | 
			
		||||
 * Layout-related data.
 | 
			
		||||
 * 
 | 
			
		||||
 * The `View` contains `Row`s and each `Row` contains `Button`s.
 | 
			
		||||
@ -18,6 +18,8 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use std::cell::RefCell;
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use std::ffi::CString;
 | 
			
		||||
use std::rc::Rc;
 | 
			
		||||
use std::vec::Vec;
 | 
			
		||||
 | 
			
		||||
@ -29,7 +31,8 @@ use ::symbol::*;
 | 
			
		||||
pub mod c {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    use std::os::raw::c_void;
 | 
			
		||||
    use std::ffi::CStr;
 | 
			
		||||
    use std::os::raw::{ c_char, c_void };
 | 
			
		||||
    use std::ptr;
 | 
			
		||||
 | 
			
		||||
    // The following defined in C
 | 
			
		||||
@ -60,26 +63,11 @@ pub mod c {
 | 
			
		||||
        pub height: f64
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    impl Bounds {
 | 
			
		||||
        pub fn zero() -> Bounds {
 | 
			
		||||
            Bounds { x: 0f64, y: 0f64, width: 0f64, height: 0f64 }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    type ButtonCallback = unsafe extern "C" fn(button: *mut ::layout::Button, data: *mut UserData);
 | 
			
		||||
    type RowCallback = unsafe extern "C" fn(row: *mut ::layout::Row, data: *mut UserData);
 | 
			
		||||
 | 
			
		||||
    // The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_view_new(bounds: Bounds) -> *mut ::layout::View {
 | 
			
		||||
        Box::into_raw(Box::new(::layout::View {
 | 
			
		||||
            rows: Vec::new(),
 | 
			
		||||
            bounds: bounds,
 | 
			
		||||
        }))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_view_get_bounds(view: *const ::layout::View) -> Bounds {
 | 
			
		||||
@ -88,30 +76,6 @@ pub mod c {
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_view_set_bounds(view: *mut ::layout::View, bounds: Bounds) {
 | 
			
		||||
        unsafe { &mut *view }.bounds = bounds;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Places a row into the view and returns a reference to it
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_view_create_row(
 | 
			
		||||
        view: *mut ::layout::View,
 | 
			
		||||
        angle: i32,
 | 
			
		||||
    ) -> *mut ::layout::Row {
 | 
			
		||||
        let view = unsafe { &mut *view };
 | 
			
		||||
 | 
			
		||||
        view.rows.push(Box::new(::layout::Row::new(angle)));
 | 
			
		||||
        // Return the reference directly instead of a Box, it's not on the stack
 | 
			
		||||
        // It will live as long as the Vec
 | 
			
		||||
        let last_idx = view.rows.len() - 1;
 | 
			
		||||
        // Caution: Box can't be returned directly,
 | 
			
		||||
        // so returning a reference to its innards
 | 
			
		||||
        view.rows[last_idx].as_mut() as *mut ::layout::Row
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
        #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_view_foreach(
 | 
			
		||||
        view: *mut ::layout::View,
 | 
			
		||||
        callback: RowCallback,
 | 
			
		||||
@ -124,61 +88,6 @@ 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_get_angle(row: *const ::layout::Row) -> i32 {
 | 
			
		||||
@ -196,6 +105,14 @@ pub mod c {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set bounds by consuming the value
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_row_set_bounds(row: *mut ::layout::Row, bounds: Bounds) {
 | 
			
		||||
        let row = unsafe { &mut *row };
 | 
			
		||||
        row.bounds = Some(bounds);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_row_foreach(
 | 
			
		||||
@ -216,49 +133,11 @@ pub mod c {
 | 
			
		||||
        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]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_button_get_bounds(button: *const ::layout::Button) -> Bounds {
 | 
			
		||||
        let button = unsafe { &*button };
 | 
			
		||||
        match &button.bounds {
 | 
			
		||||
            Some(bounds) => bounds.clone(),
 | 
			
		||||
            None => panic!("Button doesn't have any bounds yet"),
 | 
			
		||||
        }
 | 
			
		||||
        button.bounds.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Borrow a new reference to key state. Doesn't need freeing
 | 
			
		||||
@ -279,12 +158,43 @@ pub mod c {
 | 
			
		||||
    ) -> *const Symbol {
 | 
			
		||||
        let button = unsafe { &*button };
 | 
			
		||||
        let state = button.state.borrow();
 | 
			
		||||
        match state.symbol {
 | 
			
		||||
            Some(ref symbol) => symbol as *const Symbol,
 | 
			
		||||
            None => ptr::null(),
 | 
			
		||||
        &state.symbol as *const Symbol
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_button_get_label(
 | 
			
		||||
        button: *const ::layout::Button
 | 
			
		||||
    ) -> *const c_char {
 | 
			
		||||
        let button = unsafe { &*button };
 | 
			
		||||
        match &button.label {
 | 
			
		||||
            Label::Text(text) => text.as_ptr(),
 | 
			
		||||
            // returning static strings to C is a bit cumbersome
 | 
			
		||||
            Label::IconName(_) => unsafe {
 | 
			
		||||
                // CStr doesn't allocate anything, so it only points to
 | 
			
		||||
                // the 'static str, avoiding a memory leak
 | 
			
		||||
                CStr::from_bytes_with_nul_unchecked(b"icon\0")
 | 
			
		||||
            }.as_ptr(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_button_get_icon_name(button: *const Button) -> *const c_char {
 | 
			
		||||
        let button = unsafe { &*button };
 | 
			
		||||
        match &button.label {
 | 
			
		||||
            Label::Text(_) => ptr::null(),
 | 
			
		||||
            Label::IconName(name) => name.as_ptr(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_button_get_name(button: *const Button) -> *const c_char {
 | 
			
		||||
        let button = unsafe { &*button };
 | 
			
		||||
        button.name.as_ptr()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_button_has_key(
 | 
			
		||||
@ -304,13 +214,34 @@ pub mod c {
 | 
			
		||||
        println!("{:?}", button);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_layout_get_current_view(layout: *const Layout) -> *const View {
 | 
			
		||||
        let layout = unsafe { &*layout };
 | 
			
		||||
        let view_name = layout.current_view.clone();
 | 
			
		||||
        layout.views.get(&view_name)
 | 
			
		||||
            .expect("Current view doesn't exist")
 | 
			
		||||
            .as_ref() as *const View
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_layout_get_keymap(layout: *const Layout) -> *const c_char {
 | 
			
		||||
        let layout = unsafe { &*layout };
 | 
			
		||||
        layout.keymap_str.as_ptr()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_layout_free(layout: *mut Layout) {
 | 
			
		||||
        unsafe { Box::from_raw(layout) };
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
    /// Entry points for more complex procedures and algoithms which span multiple modules
 | 
			
		||||
    pub mod procedures {
 | 
			
		||||
        use super::*;
 | 
			
		||||
 | 
			
		||||
        #[repr(transparent)]
 | 
			
		||||
        pub struct LevelKeyboard(*const c_void);
 | 
			
		||||
        
 | 
			
		||||
        #[repr(C)]
 | 
			
		||||
        #[derive(PartialEq, Debug)]
 | 
			
		||||
        pub struct ButtonPlace {
 | 
			
		||||
@ -318,13 +249,11 @@ pub mod c {
 | 
			
		||||
            button: *const Button,
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        #[repr(transparent)]
 | 
			
		||||
        pub struct LevelKeyboard(*const c_void);
 | 
			
		||||
 | 
			
		||||
        #[no_mangle]
 | 
			
		||||
        extern "C" {
 | 
			
		||||
            fn eek_get_outline_size(
 | 
			
		||||
                keyboard: *const LevelKeyboard,
 | 
			
		||||
                outline: u32
 | 
			
		||||
            ) -> Bounds;
 | 
			
		||||
 | 
			
		||||
            /// Checks if point falls within bounds,
 | 
			
		||||
            /// which are relative to origin and rotated by angle (I think)
 | 
			
		||||
            pub fn eek_are_bounds_inside (bounds: Bounds,
 | 
			
		||||
@ -332,15 +261,53 @@ pub mod c {
 | 
			
		||||
                origin: Point,
 | 
			
		||||
                angle: i32
 | 
			
		||||
            ) -> u32;
 | 
			
		||||
            
 | 
			
		||||
            pub fn eek_keyboard_set_key_locked(
 | 
			
		||||
                keyboard: *mut LevelKeyboard,
 | 
			
		||||
                key: ::keyboard::c::CKeyState,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        fn squeek_buttons_get_outlines(
 | 
			
		||||
            buttons: &Vec<Box<Button>>,
 | 
			
		||||
            keyboard: *const LevelKeyboard,
 | 
			
		||||
        ) -> Vec<Bounds> {
 | 
			
		||||
            buttons.iter().map(|button| {
 | 
			
		||||
                unsafe { eek_get_outline_size(keyboard, button.oref.0) }
 | 
			
		||||
            }).collect()
 | 
			
		||||
        #[no_mangle]
 | 
			
		||||
        pub extern "C"
 | 
			
		||||
        fn squeek_layout_set_state_from_press(
 | 
			
		||||
            layout: *mut Layout,
 | 
			
		||||
            keyboard: *mut LevelKeyboard,
 | 
			
		||||
            key: ::keyboard::c::CKeyState,
 | 
			
		||||
        ) {
 | 
			
		||||
            let layout = unsafe { &mut *layout };
 | 
			
		||||
 | 
			
		||||
            let view_name = match key.to_owned().symbol.action {
 | 
			
		||||
                ::symbol::Action::SetLevel(name) => {
 | 
			
		||||
                    Some(name.clone())
 | 
			
		||||
                },
 | 
			
		||||
                ::symbol::Action::LockLevel { lock, unlock } => {
 | 
			
		||||
                    let locked = {
 | 
			
		||||
                        let key = key.clone_ref();
 | 
			
		||||
                        let mut key = key.borrow_mut();
 | 
			
		||||
                        key.locked ^= true;
 | 
			
		||||
                        key.locked
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    if locked {
 | 
			
		||||
                        unsafe {
 | 
			
		||||
                            eek_keyboard_set_key_locked(
 | 
			
		||||
                                keyboard,
 | 
			
		||||
                                key
 | 
			
		||||
                            )
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    Some(if locked { lock } else { unlock }.clone())
 | 
			
		||||
                },
 | 
			
		||||
                _ => None,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            if let Some(view_name) = view_name {
 | 
			
		||||
                if let Err(_e) = layout.set_view(view_name.clone()) {
 | 
			
		||||
                    eprintln!("No such view: {}, ignoring switch", view_name)
 | 
			
		||||
                };
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// Places each button in order, starting from 0 on the left,
 | 
			
		||||
@ -351,17 +318,19 @@ pub mod c {
 | 
			
		||||
        /// Sets button and row sizes according to their contents.
 | 
			
		||||
        #[no_mangle]
 | 
			
		||||
        pub extern "C"
 | 
			
		||||
        fn squeek_view_place_contents(
 | 
			
		||||
            view: *mut ::layout::View,
 | 
			
		||||
            keyboard: *const LevelKeyboard, // source of outlines
 | 
			
		||||
        fn squeek_layout_place_contents(
 | 
			
		||||
            layout: *mut Layout,
 | 
			
		||||
        ) {
 | 
			
		||||
            let view = unsafe { &mut *view };
 | 
			
		||||
            let layout = unsafe { &mut *layout };
 | 
			
		||||
            for view in layout.views.values_mut() {
 | 
			
		||||
                let sizes: Vec<Vec<Bounds>> = view.rows.iter().map(|row| {
 | 
			
		||||
                    row.buttons.iter()
 | 
			
		||||
                        .map(|button| button.bounds.clone())
 | 
			
		||||
                        .collect()
 | 
			
		||||
                }).collect();
 | 
			
		||||
 | 
			
		||||
            let sizes: Vec<Vec<Bounds>> = view.rows.iter().map(|row|
 | 
			
		||||
                squeek_buttons_get_outlines(&row.buttons, keyboard)
 | 
			
		||||
            ).collect();
 | 
			
		||||
 | 
			
		||||
            view.place_buttons_with_sizes(sizes);
 | 
			
		||||
                view.place_buttons_with_sizes(sizes);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn squeek_row_contains(row: &Row, needle: *const Button) -> bool {
 | 
			
		||||
@ -427,35 +396,36 @@ pub mod c {
 | 
			
		||||
        mod test {
 | 
			
		||||
            use super::*;
 | 
			
		||||
 | 
			
		||||
            use super::super::test::*;
 | 
			
		||||
 | 
			
		||||
            #[test]
 | 
			
		||||
            fn row_has_button() {
 | 
			
		||||
                let mut row = Row::new(0);
 | 
			
		||||
                let button = squeek_row_create_button(&mut row as *mut Row, 0, 0);
 | 
			
		||||
                assert_eq!(squeek_row_contains(&row, button), true);
 | 
			
		||||
                let shared_button = squeek_row_create_button_with_state(
 | 
			
		||||
                    &mut row as *mut Row,
 | 
			
		||||
                    button
 | 
			
		||||
                let state = make_state();
 | 
			
		||||
                let button = make_button_with_state(
 | 
			
		||||
                    "test".into(),
 | 
			
		||||
                    state.clone()
 | 
			
		||||
                );
 | 
			
		||||
                assert_eq!(squeek_row_contains(&row, shared_button), true);
 | 
			
		||||
                let button_ptr = button_as_raw(&button);
 | 
			
		||||
                let mut row = Row::new(0);
 | 
			
		||||
                row.buttons.push(button);
 | 
			
		||||
                assert_eq!(squeek_row_contains(&row, button_ptr), true);
 | 
			
		||||
                let shared_button = make_button_with_state(
 | 
			
		||||
                    "test2".into(),
 | 
			
		||||
                    state
 | 
			
		||||
                );
 | 
			
		||||
                let shared_button_ptr = button_as_raw(&shared_button);
 | 
			
		||||
                row.buttons.push(shared_button);
 | 
			
		||||
                assert_eq!(squeek_row_contains(&row, shared_button_ptr), true);
 | 
			
		||||
                let row = Row::new(0);
 | 
			
		||||
                assert_eq!(squeek_row_contains(&row, button), false);
 | 
			
		||||
                assert_eq!(squeek_row_contains(&row, button_ptr), false);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            #[test]
 | 
			
		||||
            fn view_has_button() {
 | 
			
		||||
                let state = Rc::new(RefCell::new(::keyboard::KeyState {
 | 
			
		||||
                    pressed: false,
 | 
			
		||||
                    locked: false,
 | 
			
		||||
                    keycode: 0,
 | 
			
		||||
                    symbol: None,
 | 
			
		||||
                }));
 | 
			
		||||
                let state = make_state();
 | 
			
		||||
                let state_clone = ::keyboard::c::CKeyState::wrap(state.clone());
 | 
			
		||||
 | 
			
		||||
                let button = Box::new(Button {
 | 
			
		||||
                    oref: OutlineRef(0),
 | 
			
		||||
                    bounds: None,
 | 
			
		||||
                    state: state,
 | 
			
		||||
                });
 | 
			
		||||
                let button = make_button_with_state("1".into(), state);
 | 
			
		||||
                let button_ptr = button.as_ref() as *const Button;
 | 
			
		||||
                
 | 
			
		||||
                let row = Box::new(Row {
 | 
			
		||||
@ -509,17 +479,63 @@ pub mod c {
 | 
			
		||||
    mod test {
 | 
			
		||||
        use super::*;
 | 
			
		||||
        
 | 
			
		||||
        use ::keyboard::c::CKeyState;
 | 
			
		||||
 | 
			
		||||
        pub fn make_state() -> Rc<RefCell<::keyboard::KeyState>> {
 | 
			
		||||
            Rc::new(RefCell::new(::keyboard::KeyState {
 | 
			
		||||
                pressed: false,
 | 
			
		||||
                locked: false,
 | 
			
		||||
                keycode: None,
 | 
			
		||||
                symbol: Symbol {
 | 
			
		||||
                    action: Action::SetLevel("default".into()),
 | 
			
		||||
                }
 | 
			
		||||
            }))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pub fn make_button_with_state(
 | 
			
		||||
            name: String,
 | 
			
		||||
            state: Rc<RefCell<::keyboard::KeyState>>,
 | 
			
		||||
        ) -> Box<Button> {
 | 
			
		||||
            Box::new(Button {
 | 
			
		||||
                name: CString::new(name.clone()).unwrap(),
 | 
			
		||||
                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]
 | 
			
		||||
        fn button_has_key() {
 | 
			
		||||
            let button = squeek_button_new(0, 0);
 | 
			
		||||
            let state = squeek_button_get_key(button);
 | 
			
		||||
            assert_eq!(squeek_button_has_key(button, state.clone()), 1);
 | 
			
		||||
            let other_button = squeek_button_new(0, 0);
 | 
			
		||||
            assert_eq!(squeek_button_has_key(other_button, state.clone()), 0);
 | 
			
		||||
            let other_state = ::keyboard::c::squeek_key_new(0);
 | 
			
		||||
            assert_eq!(squeek_button_has_key(button, other_state), 0);
 | 
			
		||||
            let shared_button = squeek_button_new_with_state(button);
 | 
			
		||||
            assert_eq!(squeek_button_has_key(shared_button, state), 1);
 | 
			
		||||
            let state = make_state();
 | 
			
		||||
            let button = make_button_with_state("1".into(), state.clone());
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                squeek_button_has_key(
 | 
			
		||||
                    button_as_raw(&button),
 | 
			
		||||
                    CKeyState::wrap(state.clone())
 | 
			
		||||
                ),
 | 
			
		||||
                1
 | 
			
		||||
            );
 | 
			
		||||
            let other_state = make_state();
 | 
			
		||||
            let other_button = make_button_with_state("1".into(), other_state);
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                squeek_button_has_key(
 | 
			
		||||
                    button_as_raw(&other_button),
 | 
			
		||||
                    CKeyState::wrap(state.clone())
 | 
			
		||||
                ),
 | 
			
		||||
                0
 | 
			
		||||
            );
 | 
			
		||||
            let orphan_state = CKeyState::wrap(make_state());
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                squeek_button_has_key(button_as_raw(&button), orphan_state),
 | 
			
		||||
                0
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -530,13 +546,25 @@ pub struct Size {
 | 
			
		||||
    pub height: f64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub enum Label {
 | 
			
		||||
    /// Text used to display the symbol
 | 
			
		||||
    Text(CString),
 | 
			
		||||
    /// Icon name used to render the symbol
 | 
			
		||||
    IconName(CString),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// The graphical representation of a button
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub struct Button {
 | 
			
		||||
    oref: c::OutlineRef,
 | 
			
		||||
    /// TODO: abolish Option, buttons should be created with bounds fully formed
 | 
			
		||||
    /// ID string, e.g. for CSS 
 | 
			
		||||
    pub name: CString,
 | 
			
		||||
    /// 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)
 | 
			
		||||
    bounds: Option<c::Bounds>,
 | 
			
		||||
    pub bounds: c::Bounds,
 | 
			
		||||
    /// current state, shared with other buttons
 | 
			
		||||
    pub state: Rc<RefCell<KeyState>>,
 | 
			
		||||
}
 | 
			
		||||
@ -547,11 +575,11 @@ const ROW_SPACING: f64 = 7.0;
 | 
			
		||||
 | 
			
		||||
/// The graphical representation of a row of buttons
 | 
			
		||||
pub struct Row {
 | 
			
		||||
    buttons: Vec<Box<Button>>,
 | 
			
		||||
    pub buttons: Vec<Box<Button>>,
 | 
			
		||||
    /// Angle is not really used anywhere...
 | 
			
		||||
    angle: i32,
 | 
			
		||||
    pub angle: i32,
 | 
			
		||||
    /// Position relative to some origin (i.e. parent/view origin)
 | 
			
		||||
    bounds: Option<c::Bounds>,
 | 
			
		||||
    pub bounds: Option<c::Bounds>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Row {
 | 
			
		||||
@ -612,9 +640,7 @@ impl Row {
 | 
			
		||||
        };
 | 
			
		||||
        let angle = self.angle;
 | 
			
		||||
        self.buttons.iter_mut().find(|button| {
 | 
			
		||||
            let bounds = button.bounds
 | 
			
		||||
                .as_ref().expect("Missing bounds on button")
 | 
			
		||||
                .clone();
 | 
			
		||||
            let bounds = button.bounds.clone();
 | 
			
		||||
            let point = point.clone();
 | 
			
		||||
            let origin = origin.clone();
 | 
			
		||||
            procedures::is_point_inside(bounds, point, origin, angle)
 | 
			
		||||
@ -624,8 +650,8 @@ impl Row {
 | 
			
		||||
 | 
			
		||||
pub struct View {
 | 
			
		||||
    /// Position relative to keyboard origin
 | 
			
		||||
    bounds: c::Bounds,
 | 
			
		||||
    rows: Vec<Box<Row>>,
 | 
			
		||||
    pub bounds: c::Bounds,
 | 
			
		||||
    pub rows: Vec<Box<Row>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl View {
 | 
			
		||||
@ -677,7 +703,7 @@ impl View {
 | 
			
		||||
            for (mut button, button_position)
 | 
			
		||||
                in row.buttons.iter_mut()
 | 
			
		||||
                    .zip(button_positions) {
 | 
			
		||||
                button.bounds = Some(button_position);
 | 
			
		||||
                button.bounds = button_position;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -700,6 +726,26 @@ impl View {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Layout {
 | 
			
		||||
    pub current_view: String,
 | 
			
		||||
    pub views: HashMap<String, Box<View>>,
 | 
			
		||||
    // TODO: move to ::keyboard::Keyboard
 | 
			
		||||
    pub keymap_str: CString,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct NoSuchView;
 | 
			
		||||
 | 
			
		||||
impl Layout {
 | 
			
		||||
    fn set_view(&mut self, view: String) -> Result<(), NoSuchView> {
 | 
			
		||||
        if self.views.contains_key(&view) {
 | 
			
		||||
            self.current_view = view;
 | 
			
		||||
            Ok(())
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(NoSuchView)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
mod procedures {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,15 @@
 | 
			
		||||
#[macro_use]
 | 
			
		||||
extern crate bitflags;
 | 
			
		||||
#[macro_use]
 | 
			
		||||
extern crate maplit;
 | 
			
		||||
extern crate serde;
 | 
			
		||||
 | 
			
		||||
mod data;
 | 
			
		||||
pub mod float_ord;
 | 
			
		||||
pub mod imservice;
 | 
			
		||||
mod keyboard;
 | 
			
		||||
mod layout;
 | 
			
		||||
mod resources;
 | 
			
		||||
mod symbol;
 | 
			
		||||
mod util;
 | 
			
		||||
mod xdg;
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,6 @@ sources = [
 | 
			
		||||
  '../eek/eek-keysym.c',
 | 
			
		||||
  '../eek/eek-layout.c',
 | 
			
		||||
  '../eek/eek-renderer.c',
 | 
			
		||||
  '../eek/eek-section.c',
 | 
			
		||||
  '../eek/eek-types.c',
 | 
			
		||||
  '../eek/eek-xml-layout.c',
 | 
			
		||||
  '../eek/layersurface.c',
 | 
			
		||||
@ -65,6 +64,7 @@ rslibs = custom_target(
 | 
			
		||||
    build_always_stale: true,
 | 
			
		||||
    output: ['librs.a'],
 | 
			
		||||
    install: false,
 | 
			
		||||
    console: true,
 | 
			
		||||
    command: [cargo_script, '@CURRENT_SOURCE_DIR@', '@OUTPUT@', 'build']
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										23
									
								
								src/resources.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/resources.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
/*! 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")),
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
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
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								src/symbol.h
									
									
									
									
									
								
							@ -1,24 +0,0 @@
 | 
			
		||||
#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
 | 
			
		||||
@ -1,78 +1,7 @@
 | 
			
		||||
/*! The symbol object, defining actions that the key can do when activated */
 | 
			
		||||
 | 
			
		||||
use std::ffi::CString;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Gathers stuff defined in C or called by C
 | 
			
		||||
pub mod c {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    
 | 
			
		||||
    use std::ffi::CStr;
 | 
			
		||||
    use std::os::raw::c_char;
 | 
			
		||||
    use std::ptr;
 | 
			
		||||
    
 | 
			
		||||
    // The following defined in C
 | 
			
		||||
    
 | 
			
		||||
    // Legacy; Will never be used in Rust as a bit field
 | 
			
		||||
    enum ModifierMask {
 | 
			
		||||
        Nothing = 0,
 | 
			
		||||
        Shift = 1,
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // The following defined in Rust.
 | 
			
		||||
    
 | 
			
		||||
    // TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
 | 
			
		||||
    // Symbols are owned by Rust and will move towards no C manipulation, so it may make sense not to wrap them
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_symbol_get_name(symbol: *const Symbol) -> *const c_char {
 | 
			
		||||
        let symbol = unsafe { &*symbol };
 | 
			
		||||
        match &symbol.action {
 | 
			
		||||
            Action::Submit { text: Some(text), .. } => text.as_ptr(),
 | 
			
		||||
            _ => ptr::null(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_symbol_get_label(symbol: *const Symbol) -> *const c_char {
 | 
			
		||||
        let symbol = unsafe { &*symbol };
 | 
			
		||||
        match &symbol.label {
 | 
			
		||||
            Label::Text(text) => text.as_ptr(),
 | 
			
		||||
            // returning static strings to C is a bit cumbersome
 | 
			
		||||
            Label::IconName(_) => unsafe {
 | 
			
		||||
                CStr::from_bytes_with_nul_unchecked(b"icon\0")
 | 
			
		||||
            }.as_ptr(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_symbol_get_icon_name(symbol: *const Symbol) -> *const c_char {
 | 
			
		||||
        let symbol = unsafe { &*symbol };
 | 
			
		||||
        match &symbol.label {
 | 
			
		||||
            Label::Text(_) => ptr::null(),
 | 
			
		||||
            Label::IconName(name) => name.as_ptr(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Legacy; throw away
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_symbol_get_modifier_mask(symbol: *const Symbol) -> u32 {
 | 
			
		||||
        let symbol = unsafe { &*symbol };
 | 
			
		||||
        (match &symbol.action {
 | 
			
		||||
            Action::SetLevel(1) => ModifierMask::Shift,
 | 
			
		||||
            _ => ModifierMask::Nothing,
 | 
			
		||||
        }) as u32
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_symbol_print(symbol: *const Symbol) {
 | 
			
		||||
        let symbol = unsafe { &*symbol };
 | 
			
		||||
        println!("{:?}", symbol);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Just defines some int->identifier mappings for convenience
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub enum KeySym {
 | 
			
		||||
@ -92,16 +21,8 @@ impl KeySym {
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct XKeySym(pub u32);
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub enum Label {
 | 
			
		||||
    /// Text used to display the symbol
 | 
			
		||||
    Text(CString),
 | 
			
		||||
    /// Icon name used to render the symbol
 | 
			
		||||
    IconName(CString),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Use to switch layouts
 | 
			
		||||
type Level = u8;
 | 
			
		||||
type Level = String;
 | 
			
		||||
 | 
			
		||||
/// Use to send modified keypresses
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
@ -113,13 +34,19 @@ pub enum Modifier {
 | 
			
		||||
/// Action to perform on the keypress and, in reverse, on keyrelease
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub enum Action {
 | 
			
		||||
    /// Switch to this level TODO: reverse?
 | 
			
		||||
    /// Switch to this view
 | 
			
		||||
    SetLevel(Level),
 | 
			
		||||
    /// Switch to a view and latch
 | 
			
		||||
    LockLevel {
 | 
			
		||||
        lock: Level,
 | 
			
		||||
        /// When unlocked by pressing it or emitting a key
 | 
			
		||||
        unlock: Level,
 | 
			
		||||
    },
 | 
			
		||||
    /// Set this modifier TODO: release?
 | 
			
		||||
    SetModifier(Modifier),
 | 
			
		||||
    /// Submit some text
 | 
			
		||||
    Submit {
 | 
			
		||||
        /// orig: Canonical name of the symbol
 | 
			
		||||
        /// Text to submit with input-method
 | 
			
		||||
        text: Option<CString>,
 | 
			
		||||
        /// The key events this symbol submits when submitting text is not possible
 | 
			
		||||
        keys: Vec<XKeySym>,
 | 
			
		||||
@ -131,8 +58,4 @@ pub enum Action {
 | 
			
		||||
pub struct Symbol {
 | 
			
		||||
    /// The action that this key performs
 | 
			
		||||
    pub action: Action,
 | 
			
		||||
    /// Label to display to the user
 | 
			
		||||
    pub label: Label,
 | 
			
		||||
    // FIXME: is it used?
 | 
			
		||||
    pub tooltip: Option<CString>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,6 @@ pub mod c {
 | 
			
		||||
    
 | 
			
		||||
    use std::borrow::ToOwned;
 | 
			
		||||
    
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    pub fn as_str(s: &*const c_char) -> Result<Option<&str>, Utf8Error> {
 | 
			
		||||
        if s.is_null() {
 | 
			
		||||
            Ok(None)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										63
									
								
								src/xdg.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/xdg.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
			
		||||
/*! 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())
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
@ -1,45 +0,0 @@
 | 
			
		||||
/* 
 | 
			
		||||
 * 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 ();
 | 
			
		||||
}
 | 
			
		||||
@ -1,55 +0,0 @@
 | 
			
		||||
/* 
 | 
			
		||||
 * 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 ();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								tests/layout.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/layout.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
---
 | 
			
		||||
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"
 | 
			
		||||
							
								
								
									
										12
									
								
								tests/layout2.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								tests/layout2.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
---
 | 
			
		||||
# 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 }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										16
									
								
								tests/layout3.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								tests/layout3.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
			
		||||
---
 | 
			
		||||
# 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
 | 
			
		||||
@ -20,8 +20,6 @@ test_link_args = [
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
tests = [
 | 
			
		||||
  'eek-simple-test',
 | 
			
		||||
  'eek-xml-test',
 | 
			
		||||
  'test-keymap-generation'
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -25,22 +25,16 @@
 | 
			
		||||
 | 
			
		||||
#include "config.h"
 | 
			
		||||
 | 
			
		||||
#include "eek/eek.h"
 | 
			
		||||
#include "eek/eek-xml-layout.h"
 | 
			
		||||
#include "eek/eek-keyboard.h"
 | 
			
		||||
 | 
			
		||||
#include "src/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);
 | 
			
		||||
    LevelKeyboard *keyboard = eek_xml_layout_real_create_keyboard("us", NULL);
 | 
			
		||||
    const gchar *keymap_str = squeek_layout_get_keymap(keyboard->layout);
 | 
			
		||||
 | 
			
		||||
    struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
 | 
			
		||||
    if (!context) {
 | 
			
		||||
@ -50,14 +44,11 @@ test_check_xkb (void)
 | 
			
		||||
    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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user