Compare commits

...

94 Commits

Author SHA1 Message Date
2b8fa0902e Remove unnecessary trigger 2019-07-02 14:57:20 +02:00
ab2a27345d Merge branch 'modifiers' into 'squeekboard'
modifiers: Seem to be emitted correctly

See merge request Librem5/squeekboard!17
2019-07-02 11:54:34 +00:00
484eb3303c modifiers: Seem to be emitted correctly 2019-07-02 11:52:45 +00:00
60250ca5e5 Merge branch 'font-size' into 'squeekboard'
hack: Adjust font size

See merge request Librem5/squeekboard!15
2019-07-01 15:41:10 +00:00
d729bb3a89 hack: Adjust font size 2019-07-01 15:27:06 +00:00
4dc55635d7 Merge branch 'new_layout' into 'squeekboard'
New layout

See merge request Librem5/squeekboard!13
2019-07-01 15:04:32 +00:00
4af49ef7b6 layout: Make keys higher 2019-07-01 13:31:53 +00:00
3a42e66504 layout: Position keys closer to the left edge 2019-07-01 12:57:52 +00:00
250b196b81 theme: Use mockup colors 2019-07-01 12:57:52 +00:00
9ba1987cab layout: Moved keys a bit to the left 2019-07-01 12:57:52 +00:00
909b1e2a28 theme: Adjust colors to match the mockup closer 2019-07-01 12:57:52 +00:00
e46af41abf layouts: Fixed spacing 2019-07-01 12:57:52 +00:00
17671a3b08 layout: Use outlines for key bounds 2019-07-01 12:57:52 +00:00
b8eb7752e7 layout: Using bigger keys 2019-07-01 12:57:52 +00:00
5e92f45111 layout: Arrange keys similar to mockup 2019-07-01 12:57:52 +00:00
baf848c791 layout: Ignore keycodes without corresponding keys 2019-07-01 12:57:52 +00:00
737d57c1f4 Merge branch 'install-rslib' into 'squeekboard'
Install rslib

See merge request Librem5/squeekboard!12
2019-07-01 12:57:22 +00:00
9985ad7ee1 Fix lintian package-must-activate-ldconfig-trigger error 2019-07-01 01:46:16 +02:00
025b55e1a2 Install rslib 2019-07-01 00:05:42 +02:00
14fbabe8d7 Merge branch 'popup' into 'squeekboard'
Pop up when input requested

See merge request Librem5/squeekboard!10
2019-06-30 19:04:42 +00:00
b746f7a70e input method: Pop up the keyboard 2019-06-30 19:03:24 +00:00
0d3b003aac input method: Initialize the protocol and pretend to handle a few things 2019-06-30 19:03:24 +00:00
9428927879 Merge branch 'build-dep' into 'squeekboard'
ci: Use build-dep instead of a dependency list

See merge request Librem5/squeekboard!11
2019-06-30 19:03:08 +00:00
511b2f7186 ci: Use build-dep instead of a dependency list 2019-06-30 19:01:36 +00:00
d8c83e3c65 Merge branch 'touch' into 'squeekboard'
Touch support

See merge request Librem5/squeekboard!9
2019-06-30 06:11:16 +00:00
050fd6f3ba Touch support
Single stream of touch events.
2019-06-29 12:56:04 +00:00
72d6a8d4e1 Merge branch 'wayland-gen' into 'squeekboard'
build: Use 'client-code' instead of 'code' for protocols

See merge request Librem5/squeekboard!8
2019-06-29 12:47:27 +00:00
30d35216f6 build: Use 'client-code' instead of 'code' for protocols 2019-06-29 12:46:08 +00:00
bcd0d40912 Merge branch 'fixes' into 'squeekboard'
fix: Remove leftover debug print

See merge request Librem5/squeekboard!7
2019-06-29 12:18:59 +00:00
4b8a6bbbe0 fix: Remove leftover debug print 2019-06-29 12:16:34 +00:00
752dc467a8 Merge branch 'desktop-file' into 'squeekboard'
Add a desktop file

See merge request Librem5/squeekboard!5
2019-06-29 10:13:20 +00:00
bde45b262a Merge branch 'debian-files' into 'squeekboard'
Add Debian packaging files

See merge request Librem5/squeekboard!4
2019-06-29 10:10:15 +00:00
fc338f5723 Add Debian packaging files 2019-06-29 10:10:15 +00:00
346ed453ef Start working on a desktop file 2019-06-26 18:17:54 +02:00
664f05edba Remove unnecessary build dependency 2019-06-26 17:45:23 +02:00
edcff44f4b Add another build dependency, add an empty rule to override autoreconf 2019-06-26 17:35:13 +02:00
42ee5d2ddb Update packaging files 2019-06-26 17:35:05 +02:00
54e421d7e6 Add initial Debian packaging 2019-06-26 17:35:00 +02:00
9e5629d1e0 Enable Wayland's virtual-keyboard protocol
This commit includes a little restructuring necessary for keeping wayland objects properly.
It doesn't fix broken modifier functionality yet.
2019-06-25 18:12:15 +00:00
c0fdffac28 Separate keyboards from the dbus handler 2019-06-23 10:59:45 +00:00
e94e64d204 Move dbus setup closer together 2019-06-23 10:42:20 +00:00
e503e35b84 Rename squeak_ to squeek_ for consitency 2019-06-23 10:30:25 +00:00
752592a3d8 Fixed build 2019-06-23 10:29:18 +00:00
2e6d194a6f Remove server-service 2019-06-23 10:26:24 +00:00
63dfb07b51 Simplify the storage of context 2019-06-23 09:54:09 +00:00
02525056d6 Removed X11 header, added some clarifications 2019-06-22 16:20:03 +00:00
8292429648 Context: removing more unused things 2019-06-22 16:04:33 +00:00
765c496068 Removed more unused stuff in context 2019-06-22 15:57:48 +00:00
d6feec8010 Removed d-bus paths from service class 2019-06-22 15:40:20 +00:00
f1fbb37547 Kill connection in context service 2019-06-22 15:31:08 +00:00
5a6386dd24 Fixed rendering deprecation warnings 2019-06-22 13:13:55 +00:00
0809db9e32 Remove some rendering code with no effect and warnings 2019-06-22 12:56:33 +00:00
15a3315854 Fix dragging across the keyboard 2019-06-22 12:34:10 +00:00
82d1f256b2 Remove released and cancelled key events 2019-06-22 12:23:04 +00:00
e7ba2a0eb0 Got rid of signals in the pressed path 2019-06-19 17:00:30 +00:00
eff0449b3a Redrawing key after press is happening directly 2019-06-19 16:51:57 +00:00
3b9e066ec8 Simplify key press handling 2019-06-19 16:05:37 +00:00
260ab42b9e Forward press timestamps 2019-06-19 15:56:19 +00:00
a3d745edd0 Moved key pressing from context to keyboard 2019-06-19 15:27:29 +00:00
40a92fe730 Ignoring section.key-pressed 2019-06-19 14:11:23 +00:00
e30bb23711 build: Add debug/release options 2019-06-18 13:37:10 +00:00
292c1d08d8 fixes: Minor type and include mismatches 2019-06-16 12:55:50 +00:00
be56447614 readme: Update development installation info 2019-06-16 12:13:43 +00:00
70fda8ba64 Fix releasing buttons when dragged 2019-04-06 18:46:33 +00:00
5cc407986b Ignore multi-clicks and non-left-buttons 2019-04-06 18:35:06 +00:00
53af829f46 Send both press and release events 2019-04-06 17:45:06 +00:00
53065a6d95 Fix crash on double click 2019-04-05 18:42:11 +00:00
862cfdb55d Showing and hiding 2019-04-05 18:36:25 +00:00
b065b16bf1 Use layer shell 2019-04-05 15:39:57 +00:00
6ff33b48d1 dbus: Add missing schema 2019-03-27 13:48:53 +00:00
d04020f79c readme: Use language with non-US layout 2019-03-27 13:33:40 +00:00
6b15072764 dbus: Use generated code 2019-03-23 09:36:53 +00:00
f261115ac4 ci: Change job name to meson 2019-03-22 19:06:29 +00:00
116f130c4c readme: Remove settings schema variable 2019-03-22 19:00:03 +00:00
cad1b02482 settings: Switching layouts according to input settings 2019-03-22 17:18:12 +00:00
09fe69f63a cleanup: Remove Context dbus interface remains 2019-03-22 16:35:02 +00:00
8ecd81d51c settings: Fall back to "us" layout when no file found 2019-03-22 16:20:39 +00:00
8f71b010cc settings: Removed custom settings schema 2019-03-22 15:52:35 +00:00
b817c6189d build: Update keysym generator to Python3 2019-03-22 13:09:21 +00:00
a00d41930d readme: Update features 2019-03-22 07:23:17 +00:00
caee942796 build, readme: Update build and run instructions 2019-03-22 07:23:17 +00:00
d3410fdc61 Keyboard shows up on a single ShowKeyboard 2019-03-22 07:23:12 +00:00
8087c3e5d4 build: Use only meson for squeekboard
This breaks autoconf. The only resulting binary is the squeekboard GUI. It still needs the autotools-built eekboard client in order to do anything useful. That one needs to be built using a different branch, making this a WIP.
2019-03-15 20:59:29 +00:00
10bd0ea09e build: Remove eekboard-server 2019-03-14 20:40:27 +00:00
5803222e68 build: Remove libeekboard dependency 2019-03-14 18:03:10 +00:00
a243fce1ae build: Squeekboard build in meson 2019-03-14 17:29:13 +00:00
c8059ebf50 stubbing: Key generation events
Only enabled when Xtest is in use. It's probably always meant to be in use though, as this piece of code also opens the preferences dialog
2019-03-14 11:09:35 +00:00
ce2d270e7c ci: Add config flags relevant for Wayland builds 2019-03-13 18:08:56 +00:00
0c945bdc7e readme: Update build and run instructions 2019-03-13 17:59:59 +00:00
60ec684853 readme: Moved to Markdown 2019-03-13 17:59:54 +00:00
b159625e62 Add gitlab CI 2019-02-14 16:57:39 +00:00
e212262f29 Stop key-repeat when the server receives a new D-Bus event.
Key-repeat timer should be cleared when the server receives a new D-Bus.
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=857977
2012-10-01 11:55:04 +09:00
c71167d893 Fix out-of-tree build. 2012-10-01 11:55:00 +09:00
2d4e4c7a13 Fix compiler warnings. 2012-10-01 11:37:47 +09:00
72 changed files with 3527 additions and 2216 deletions

19
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,19 @@
image: debian:buster
stages:
- build
before_script:
- apt-get -y update
- apt-get -y build-dep .
build_meson:
stage: build
tags:
- librem5
script:
- mkdir -p ../build
- meson ../build/
- cd ../build
- ninja install

View File

@ -19,6 +19,8 @@
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = eek eekboard src tests bindings docs po data examples
DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc --enable-introspection
AUTOMAKE_OPTIONS = foreign # allow README.md to exist
GITIGNOREFILES = \
INSTALL \

35
README
View File

@ -1,35 +0,0 @@
eekboard - an easy to use virtual keyboard toolkit -*- outline -*-
eekboard is a virtual keyboard software package, including a set of
tools to implement desktop virtual keyboards.
* Building
** Dependencies
REQUIRED: GLib2, GTK, PangoCairo, libxklavier, libcroco
OPTIONAL: libXtst, at-spi2-core, IBus, Clutter, Clutter-Gtk, Python, Vala, gobject-introspection, libcanberra
** Build from git repo
$ git clone git://github.com/ueno/eekboard.git
$ cd eekboard
$ ./autogen.sh --prefix=/usr --enable-gtk-doc
$ make
$ sudo make install
** Build from tarball
$ ./configure --prefix=/usr
$ make
$ sudo make install
* Running
$ eekboard
$ eekboard -f # show/hide automatically based on focus-in/focus-out events
Even though eekboard -f watches a11y events by default, it currently
works better with IBus. To use IBus, do:
$ gsettings set org.fedorahosted.eekboard focus-listener 'ibus'

68
README.md Normal file
View File

@ -0,0 +1,68 @@
*squeekboard* - a Wayland virtual keyboard
========================================
*Squeekboard* is a virtual keyboard supporting Wayland, built primarily for the *Librem 5* phone.
Features
--------
### Present
- GTK3
- Custom xml-defined keyboards
- DBus interface to show and hide
### Temporarily dropped
- A settings interface
### TODO
- Use Wayland virtual keyboard protocol
- Use Wayland text input protocol
- Use Wayland input method protocol
- Pick up DBus interface files from /usr/share
Building
--------
### Dependencies
See `.gitlab-ci.yml`.
### Build from git repo
```
$ git clone https://source.puri.sm/Librem5/eekboard.git
$ cd eekboard
$ mkdir ../build
$ meson ../build/
$ cd ../build
$ ninja install
```
For development, alter the `meson` call:
```
$ meson ../build/ --prefix=../install
```
and don't skip `ninja install` before running. The last step is necessary in order to find the keyboard definition files.
Running
-------
```
$ rootston
$ cd ../build/
$ src/squeekboard
```
### Testing
```
$ busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b true
$ busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b false
$ gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us'), ('xkb', 'ua')]"
$ gsettings set org.gnome.desktop.input-sources current 1
```

View File

@ -7,17 +7,12 @@ test -z "$srcdir" && srcdir=.
PKG_NAME="eekboard"
(test -f $srcdir/configure.ac \
&& test -f $srcdir/README ) || {
&& test -f $srcdir/README.md ) || {
echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
echo " top-level $PKG_NAME directory"
exit 1
}
which gnome-autogen.sh || {
echo "You need to install gnome-common from the GNOME CVS"
exit 1
}
ACLOCAL_FLAGS="$ACLOCAL_FLAGS -I m4"
REQUIRED_AUTOMAKE_VERSION=1.10
REQUIRED_AUTOCONF_VERSION=2.60

View File

@ -16,6 +16,8 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
NULL =
if ENABLE_VALA
vapidir = $(datadir)/vala/vapi
dist_vapi_DATA = \
@ -45,42 +47,45 @@ maintainer-clean-local:
eek_vapi_deps = \
$(srcdir)/Eek-$(EEK_API_VERSION).metadata \
| \
$(top_srcdir)/eek/Eek-$(EEK_API_VERSION).gir \
$(top_builddir)/eek/Eek-$(EEK_API_VERSION).gir \
$(NULL)
eek-$(EEK_API_VERSION).vapi: $(eek_vapi_deps)
$(VAPIGEN_V)$(VAPIGEN) \
--library eek-$(EEK_API_VERSION) \
--pkg gio-2.0 \
--metadatadir=$(srcdir) \
$(top_srcdir)/eek/Eek-$(EEK_API_VERSION).gir
$(VAPIGEN_V)$(VAPIGEN) \
--library eek-$(EEK_API_VERSION) \
--pkg gio-2.0 \
--metadatadir=$(srcdir) \
$(top_builddir)/eek/Eek-$(EEK_API_VERSION).gir
eek_gtk_vapi_deps = \
$(srcdir)/EekGtk-$(EEK_API_VERSION).metadata \
| \
$(top_srcdir)/eek/EekGtk-$(EEK_API_VERSION).gir \
$(top_builddir)/eek/EekGtk-$(EEK_API_VERSION).gir \
$(NULL)
eek-gtk-$(EEK_API_VERSION).vapi: $(eek_gtk_vapi_deps)
$(VAPIGEN_V)$(VAPIGEN) --vapidir=$(builddir) \
--library eek-gtk-$(EEK_API_VERSION) \
--pkg eek-$(EEK_API_VERSION) \
--pkg gtk+-3.0 \
--metadatadir=$(srcdir) \
$(top_srcdir)/eek/EekGtk-$(EEK_API_VERSION).gir
$(VAPIGEN_V)$(VAPIGEN) --vapidir=$(builddir) \
--library eek-gtk-$(EEK_API_VERSION) \
--pkg eek-$(EEK_API_VERSION) \
--pkg gio-2.0 \
--pkg gtk+-3.0 \
--metadatadir=$(srcdir) \
$(top_builddir)/eek/EekGtk-$(EEK_API_VERSION).gir
eek_xkl_vapi_deps = \
$(srcdir)/EekXkl-$(EEK_API_VERSION).metadata \
| \
$(top_srcdir)/eek/EekXkl-$(EEK_API_VERSION).gir \
eek_xkl_vapi_deps = \
$(srcdir)/EekXkl-$(EEK_API_VERSION).metadata \
| \
$(top_builddir)/eek/EekXkl-$(EEK_API_VERSION).gir \
$(NULL)
eek-xkl-$(EEK_API_VERSION).vapi: $(eek_xkl_vapi_deps)
$(VAPIGEN_V)$(VAPIGEN) --vapidir=$(builddir) \
--library eek-xkl-$(EEK_API_VERSION) \
--pkg eek-$(EEK_API_VERSION) \
--metadatadir=$(srcdir) \
$(top_srcdir)/eek/EekXkl-$(EEK_API_VERSION).gir
$(VAPIGEN_V)$(VAPIGEN) \
--vapidir=$(builddir) \
--library eek-xkl-$(EEK_API_VERSION) \
--pkg eek-$(EEK_API_VERSION) \
--pkg gio-2.0 \
--metadatadir=$(srcdir) \
$(top_builddir)/eek/EekXkl-$(EEK_API_VERSION).gir
# set up the verbosity rules to avoid some build noise
VAPIGEN_V = $(VAPIGEN_V_$(V))

View File

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

View File

@ -0,0 +1,20 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
<interface name="sm.puri.OSK0">
<method name="SetVisible">
<arg name="visible" type="b" direction="in"/>
<doc:doc><doc:description>
Switch keyboard visibility
</doc:description></doc:doc>
</method>
<method name="GetVisible">
<arg name="visible" type="b" direction="out"/>
<doc:doc><doc:description>
Get keyboard visibility
</doc:description></doc:doc>
</method>
<property name="Visible" type="b" access="read">
</property>
</interface>
</node>

View File

@ -1,102 +1,11 @@
<?xml version="1.0"?>
<geometry version="0.90">
<bounds x="0.000000" y="0.000000" width="640.0000" height="296.5853"/>
<bounds x="0.000000" y="0.000000" width="410.0000" height="296.5853"/>
<section angle="0">
<bounds x="15.60975" y="15.60975" width="640.0000" height="39.02439"/>
<bounds x="0" y="0" width="608.7804" height="201.3658"/>
<row orientation="1">
<key keycode="9" name="ESC" oref="outline2">
<bounds x="3.121951" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="67" name="FK01" oref="outline2">
<bounds x="84.29268" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="68" name="FK02" oref="outline2">
<bounds x="124.8780" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="69" name="FK03" oref="outline2">
<bounds x="165.4634" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="70" name="FK04" oref="outline2">
<bounds x="206.0487" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="71" name="FK05" oref="outline2">
<bounds x="266.9268" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="72" name="FK06" oref="outline2">
<bounds x="307.5121" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="73" name="FK07" oref="outline2">
<bounds x="348.0975" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="74" name="FK08" oref="outline2">
<bounds x="388.6829" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="75" name="FK09" oref="outline2">
<bounds x="449.5609" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="76" name="FK10" oref="outline2">
<bounds x="490.1463" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="95" name="FK11" oref="outline2">
<bounds x="530.7317" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="96" name="FK12" oref="outline2">
<bounds x="571.3170" y="1.560976" width="37.46341" height="37.46341"/>
</key>
</row>
</section>
<section angle="0">
<bounds x="15.60975" y="78.04878" width="608.7804" height="201.3658"/>
<row orientation="1">
<key keycode="49" name="TLDE" oref="outline2">
<bounds x="3.121951" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="10" name="AE01" oref="outline2">
<bounds x="43.70731" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="11" name="AE02" oref="outline2">
<bounds x="84.29268" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="12" name="AE03" oref="outline2">
<bounds x="124.8780" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="13" name="AE04" oref="outline2">
<bounds x="165.4634" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="14" name="AE05" oref="outline2">
<bounds x="206.0487" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="15" name="AE06" oref="outline2">
<bounds x="245.0731" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="16" name="AE07" oref="outline2">
<bounds x="285.6585" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="17" name="AE08" oref="outline2">
<bounds x="326.2439" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="18" name="AE09" oref="outline2">
<bounds x="366.8292" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="19" name="AE10" oref="outline2">
<bounds x="407.4146" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="20" name="AE11" oref="outline2">
<bounds x="448.0000" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="21" name="AE12" oref="outline2">
<bounds x="488.5853" y="1.560976" width="37.46341" height="37.46341"/>
</key>
<key keycode="22" name="BKSP" oref="outline13">
<bounds x="529.1707" y="1.560976" width="79.60975" height="37.46341"/>
</key>
</row>
<row orientation="1">
<key keycode="23" name="TAB" oref="outline4">
<bounds x="3.121951" y="42.14634" width="59.31707" height="37.46341"/>
</key>
<key keycode="24" name="AD01" oref="outline2">
<bounds x="65.56097" y="42.14634" width="37.46341" height="37.46341"/>
<bounds x="65.56097" y="42.14634" width="37.46341" height="52.44877"/>
</key>
<key keycode="25" name="AD02" oref="outline2">
<bounds x="106.1463" y="42.14634" width="37.46341" height="37.46341"/>
@ -125,20 +34,11 @@
<key keycode="33" name="AD10" oref="outline2">
<bounds x="429.2682" y="42.14634" width="37.46341" height="37.46341"/>
</key>
<key keycode="34" name="AD11" oref="outline2">
<bounds x="468.2926" y="42.14634" width="37.46341" height="37.46341"/>
</key>
<key keycode="35" name="AD12" oref="outline2">
<bounds x="508.8780" y="42.14634" width="37.46341" height="37.46341"/>
</key>
<key keycode="51" name="BKSL" oref="outline5">
<bounds x="549.4634" y="42.14634" width="59.31707" height="37.46341"/>
</key>
</row>
</section>
<section angle="0">
<bounds x="0" y="0" width="608.7804" height="201.3658"/>
<row orientation="1">
<key keycode="66" name="CAPS" oref="outline6">
<bounds x="3.121951" y="82.73170" width="68.68292" height="37.46341"/>
</key>
<key keycode="38" name="AC01" oref="outline2">
<bounds x="76.48780" y="82.73170" width="37.46341" height="37.46341"/>
</key>
@ -166,18 +66,12 @@
<key keycode="46" name="AC09" oref="outline2">
<bounds x="399.6097" y="82.73170" width="37.46341" height="37.46341"/>
</key>
<key keycode="47" name="AC10" oref="outline2">
<bounds x="438.6341" y="82.73170" width="37.46341" height="37.46341"/>
</key>
<key keycode="48" name="AC11" oref="outline2">
<bounds x="479.2195" y="82.73170" width="37.46341" height="37.46341"/>
</key>
<key keycode="36" name="RTRN" oref="outline7">
<bounds x="519.8048" y="82.73170" width="88.97561" height="37.46341"/>
</key>
</row>
</section>
<section angle="0">
<bounds x="0" y="0" width="608.7804" height="201.3658"/>
<row orientation="1">
<key keycode="50" name="LFSH" oref="outline8">
<key keycode="50" name="LFSH" oref="altline">
<bounds x="3.121951" y="121.7560" width="88.97561" height="37.46341"/>
</key>
<key keycode="52" name="AB01" oref="outline2">
@ -201,113 +95,95 @@
<key keycode="58" name="AB07" oref="outline2">
<bounds x="337.1707" y="121.7560" width="37.46341" height="37.46341"/>
</key>
<key keycode="59" name="AB08" oref="outline2">
<bounds x="377.7560" y="121.7560" width="37.46341" height="37.46341"/>
</key>
<key keycode="60" name="AB09" oref="outline2">
<bounds x="418.3414" y="121.7560" width="37.46341" height="37.46341"/>
</key>
<key keycode="61" name="AB10" oref="outline2">
<bounds x="458.9268" y="121.7560" width="37.46341" height="37.46341"/>
</key>
<key keycode="62" name="RTSH" oref="outline9">
<bounds x="499.5121" y="121.7560" width="109.2682" height="37.46341"/>
<key keycode="22" name="BKSP" oref="altline">
<bounds x="529.1707" y="1.560976" width="79.60975" height="37.46341"/>
</key>
</row>
</section>
<section angle="0">
<bounds x="0" y="0" width="608.7804" height="201.3658"/>
<row orientation="1">
<key keycode="149" name="I149" oref="outline10">
<bounds x="3.121951" y="162.3414" width="37.46341" height="37.46341"/>
</key>
<key keycode="37" name="LCTL" oref="outline1">
<key keycode="37" name="LCTL" oref="altline">
<bounds x="62.43902" y="162.3414" width="48.39024" height="37.46341"/>
</key>
<key keycode="64" name="LALT" oref="outline1">
<key keycode="64" name="LALT" oref="altline">
<bounds x="113.9512" y="162.3414" width="48.39024" height="37.46341"/>
</key>
<key keycode="65" name="SPCE" oref="outline3">
<key keycode="65" name="SPCE" oref="spaceline">
<bounds x="165.4634" y="162.3414" width="217.5853" height="37.46341"/>
</key>
<key keycode="113" name="LEFT" oref="outline1">
<bounds x="368.0487" y="162.3414" width="48.39024" height="37.46341"/>
<key keycode="60" name="AB09" oref="outline2">
<bounds x="418.3414" y="121.7560" width="37.46341" height="37.46341"/>
</key>
<key keycode="111" name="UP" oref="outline1">
<bounds x="419.43894" y="162.3414" width="48.39024" height="37.46341"/>
</key>
<key keycode="116" name="DOWN" oref="outline1">
<bounds x="470.82918" y="162.3414" width="48.39024" height="37.46341"/>
</key>
<key keycode="114" name="RGHT" oref="outline1">
<bounds x="522.21942" y="162.3414" width="48.39024" height="37.46341"/>
</key>
<key keycode="150" name="I150" oref="outline10">
<bounds x="573.60966" y="162.3414" width="37.46341" height="37.46341"/>
<key keycode="36" name="RTRN" oref="outline7">
<bounds x="519.8048" y="82.73170" width="88.97561" height="37.46341"/>
</key>
</row>
</section>
<outline id="outline2" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="37.46341" y="0.000000"/>
<point x="37.46341" y="37.46341"/>
<point x="0.000000" y="37.46341"/>
<point x="37.46341" y="52.44877"/>
<point x="0.000000" y="52.44877"/>
</outline>
<outline id="outline1" corner-radius="1.000000">
<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="37.46341"/>
<point x="0.000000" y="37.46341"/>
<point x="48.39024" y="52.44877"/>
<point x="0.000000" y="52.44877"/>
</outline>
<outline id="outline4" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="59.31707" y="0.000000"/>
<point x="59.31707" y="37.46341"/>
<point x="0.000000" y="37.46341"/>
<point x="59.31707" y="52.44877"/>
<point x="0.000000" y="52.44877"/>
</outline>
<outline id="outline5" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="59.31707" y="0.000000"/>
<point x="59.31707" y="37.46341"/>
<point x="0.000000" y="37.46341"/>
<point x="59.31707" y="52.44877"/>
<point x="0.000000" y="52.44877"/>
</outline>
<outline id="outline6" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="68.68292" y="0.000000"/>
<point x="68.68292" y="37.46341"/>
<point x="0.000000" y="37.46341"/>
<point x="68.68292" 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="37.46341"/>
<point x="0.000000" y="37.46341"/>
<point x="88.97561" y="52.44877"/>
<point x="0.000000" y="52.44877"/>
</outline>
<outline id="outline8" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="88.97561" y="0.000000"/>
<point x="88.97561" y="37.46341"/>
<point x="0.000000" y="37.46341"/>
<point x="88.97561" y="52.44877"/>
<point x="0.000000" y="52.44877"/>
</outline>
<outline id="outline9" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="109.2682" y="0.000000"/>
<point x="109.2682" y="37.46341"/>
<point x="0.000000" y="37.46341"/>
<point x="109.2682" y="52.44877"/>
<point x="0.000000" y="52.44877"/>
</outline>
<outline id="outline10" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="37.46341" y="0.000000"/>
<point x="37.46341" y="37.46341"/>
<point x="0.000000" y="37.46341"/>
<point x="37.46341" y="52.44877"/>
<point x="0.000000" y="52.44877"/>
</outline>
<outline id="outline13" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="79.60975" y="0.000000"/>
<point x="79.60975" y="37.46341"/>
<point x="0.000000" y="37.46341"/>
<point x="79.60975" y="52.44877"/>
<point x="0.000000" y="52.44877"/>
</outline>
<outline id="outline3" corner-radius="1.000000">
<outline id="spaceline" corner-radius="1.000000">
<point x="0.000000" y="0.000000"/>
<point x="217.5853" y="0.000000"/>
<point x="217.5853" y="37.46341"/>
<point x="0.000000" y="37.46341"/>
<point x="150.5853" y="0.000000"/>
<point x="150.5853" y="52.44877"/>
<point x="0.000000" y="52.44877"/>
</outline>
</geometry>

68
data/meson.build Normal file
View File

@ -0,0 +1,68 @@
install_data(
'themes/default.css',
install_dir: pkgdatadir + '/themes',
)
install_data(
'keyboards/keyboards.xml',
install_dir: pkgdatadir + '/keyboards/',
)
install_data(
'keyboards/geometry/compact.xml',
install_dir: pkgdatadir + '/keyboards/geometry/',
)
install_data('dbus/sm.puri.OSK0.xml',
install_dir: dbusdir
)
symbols = [
'ar.xml',
'as-inscript.xml',
'be.xml',
'bn-inscript.xml',
'fa.xml',
'gu-inscript.xml',
'he.xml',
'hi-inscript.xml',
'ja-kana.xml',
'kk.xml',
'kn-inscript.xml',
'ks-inscript.xml',
'ks.xml',
'mai-inscript.xml',
'ml-inscript.xml',
'mr-inscript.xml',
'my.xml',
'or-inscript.xml',
'pa-inscript.xml',
'ru.xml',
'sd-inscript.xml',
'ta-inscript.xml',
'te-inscript.xml',
'th.xml',
'ua.xml',
'ug.xml',
'us.xml',
'zh-bopomofo.xml',
]
foreach symbol: symbols
install_data(
'keyboards/symbols/' + symbol,
install_dir: pkgdatadir + '/keyboards/symbols/',
)
endforeach
desktop_file = 'sm.puri.Squeekboard.desktop'
i18n.merge_file('desktop',
input: desktop_file + '.in',
output: desktop_file,
po_dir: '../po',
install: true,
install_dir: join_paths(datadir, 'applications'),
type: 'desktop'
)

View File

@ -1,60 +0,0 @@
<?xml version="1.0"?>
<schemalist>
<schema id="org.fedorahosted.eekboard" path="/org/fedorahosted/eekboard/">
<key name="keyboards" type="as">
<default>['us']</default>
<summary>Keyboard types</summary>
<description>keyboard types.</description>
</key>
<key name="focus-listener" type="s">
<default>'atspi'</default>
<summary>Use the given focus listener</summary>
<description>The name of the focus listener (either 'atspi' or 'ibus') used to detect focus events.</description>
</key>
<key name="auto-hide" type="b">
<default>true</default>
<summary>Hide keyboard automatically when focus is out</summary>
<description>If true, hide keyboard automatically when focus is out.</description>
</key>
<key name="auto-hide-delay" type="u">
<default>500</default>
<summary>Delay before hiding keyboard</summary>
<description>Delay before hiding keyboard in milliseconds. This is useful when focus listener is enabled.</description>
</key>
<key type="b" name="repeat">
<default>true</default>
<summary>Key repeat</summary>
<description>Generate key-press/release event repeatedly while a key is held down</description>
</key>
<key type="u" name="repeat-interval">
<default>100</default>
<summary>Key repeat interval</summary>
<description>Delay between repeats in milliseconds.</description>
</key>
<key type="u" name="repeat-delay">
<default>1000</default>
<summary>Initial key repeat delay</summary>
<description>Initial key repeat delay in milliseconds.</description>
</key>
<key name="start-fullscreen" type="b">
<default>false</default>
<summary>Switch to fullscreen mode when startup</summary>
<description>If true, switch to fullscreen mode when startup.</description>
</key>
<key name="size-constraint-landscape" type="(dd)">
<default>(1.0, 0.3)</default>
<summary>Constraint of the maximum window size on landscape screen</summary>
<description>Constraint of maximum window size on landscape screen</description>
</key>
<key name="size-constraint-portrait" type="(dd)">
<default>(1.0, 0.5)</default>
<summary>Constraint of the maximum window size on portrait screen</summary>
<description>Constraint of maximum window size on portrait screen</description>
</key>
<key name="theme" type="s">
<default>'default'</default>
<summary>Theme</summary>
<description>Base name of the theme to apply.</description>
</key>
</schema>
</schemalist>

View File

@ -0,0 +1,9 @@
[Desktop Entry]
Name=Squeekboard
GenericName=Squeekboard Virtual Keyboard
Comment=Virtual Keyboard
Exec=squeekboard
Icon=squeekboard
Terminal=false
Type=Application
Categories=GTK;Utility;

View File

@ -5,18 +5,14 @@
}
.key {
color: #ffffff;
background-gradient-direction: vertical;
background-gradient-start: rgba(0, 0, 0, 255);
background-gradient-end: rgba(64, 64, 64, 255);
border-width: 2px;
border-color: rgba(128, 128, 128, 255);
border-radius: 3px;
color: #deddda;
background: #464448;
border-width: 0.5px;
border-color: #5e5c64;
border-radius: 2px;
}
.key:active {
background-gradient-direction: vertical;
background-gradient-start: rgba(0, 0, 255, 255);
background-gradient-end: rgba(64, 64, 255, 255);
border-color: rgba(160, 160, 255, 255);
background: #1c71d8;
border-color: #3584e4;
}

5
debian/changelog vendored Normal file
View File

@ -0,0 +1,5 @@
squeekboard (1.0.9) unstable; urgency=medium
* Initial release.
-- David Boddie <david.boddie@puri.sm> Tue, 25 Jun 2019 19:33:00 +0200

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
10

24
debian/control vendored Normal file
View File

@ -0,0 +1,24 @@
Source: squeekboard
Section: x11
Priority: optional
Maintainer: Dorota Czaplejewicz <dorota.czaplejewicz@puri.sm>
Build-Depends:
debhelper (>= 10),
meson (>=0.43.0),
pkg-config,
libglib2.0-dev,
libgtk-3-dev,
libcroco3-dev,
libwayland-dev (>= 1.16),
rustc,
wayland-protocols (>= 1.14)
Standards-Version: 4.1.3
Homepage: https://source.puri.sm/Librem5/squeekboard
Package: squeekboard
Architecture: linux-any
Depends:
${shlibs:Depends}
${misc:Depends}
Description: On-screen keyboard for Wayland
Virtual keyboard supporting Wayland, built primarily for the Librem 5 phone.

22
debian/copyright vendored Normal file
View File

@ -0,0 +1,22 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: evscript
Source: https://source.puri.sm/david.boddie/evscript
Files: *
Copyright: 2019 Purism SPC
License: GPL-3+
This package is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
.
This package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>
.
On Debian systems, the complete text of the GNU General
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".

8
debian/rules vendored Executable file
View File

@ -0,0 +1,8 @@
#!/usr/bin/make -f
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
%:
dh $@ --builddirectory=_build
override_dh_autoreconf:

View File

@ -16,10 +16,13 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
NULL =
lib_LTLIBRARIES = \
libeek.la \
libeek-gtk.la \
libeek-xkl.la
libeek-xkl.la \
$(NULL)
libeek_public_headers = \
$(srcdir)/eek-layout.h \
@ -37,17 +40,17 @@ libeek_public_headers = \
$(srcdir)/eek-xml-layout.h \
$(srcdir)/eek-serializable.h \
$(srcdir)/eek-theme.h \
$(srcdir)/eek.h
$(srcdir)/eek.h \
$(NULL)
libeek_private_headers = \
$(srcdir)/eek-renderer.h \
$(srcdir)/eek-special-keysym-entries.h \
$(srcdir)/eek-unicode-keysym-entries.h \
$(srcdir)/eek-xkeysym-keysym-entries.h \
$(srcdir)/eek-marshalers.h \
$(libeek_keysym_headers) \
$(builddir)/eek-marshalers.h \
$(srcdir)/eek-theme-context.h \
$(srcdir)/eek-theme-private.h \
$(srcdir)/eek-theme-node.h
$(srcdir)/eek-theme-node.h \
$(NULL)
libeek_sources = \
$(srcdir)/eek.c \
@ -69,30 +72,36 @@ libeek_sources = \
$(srcdir)/eek-keyboard-drawing.c \
$(srcdir)/eek-theme.c \
$(srcdir)/eek-theme-context.c \
$(srcdir)/eek-theme-node.c
$(srcdir)/eek-theme-node.c \
$(NULL)
libeek_keysym_sources = \
$(srcdir)/eek-special-keysym-entries.h \
$(srcdir)/eek-unicode-keysym-entries.h \
$(srcdir)/eek-xkeysym-keysym-entries.h
libeek_keysym_headers = \
$(builddir)/eek-special-keysym-entries.h \
$(builddir)/eek-unicode-keysym-entries.h \
$(builddir)/eek-xkeysym-keysym-entries.h \
$(NULL)
libeek_enumtypes_sources = \
$(srcdir)/eek-enumtypes.c \
$(srcdir)/eek-enumtypes.h
$(builddir)/eek-enumtypes.c \
$(builddir)/eek-enumtypes.h \
$(NULL)
libeek_marshalers_sources = \
$(srcdir)/eek-marshalers.c \
$(srcdir)/eek-marshalers.h
$(builddir)/eek-marshalers.c \
$(builddir)/eek-marshalers.h \
$(NULL)
BUILT_SOURCES = \
$(libeek_keysym_sources) \
$(libeek_keysym_headers) \
$(libeek_enumtypes_sources) \
$(libeek_marshalers_sources)
$(libeek_marshalers_sources) \
$(NULL)
libeek_la_SOURCES = \
$(libeek_sources) \
$(srcdir)/eek-enumtypes.c \
$(srcdir)/eek-marshalers.c
$(builddir)/eek-enumtypes.c \
$(builddir)/eek-marshalers.c \
$(NULL)
libeek_la_CFLAGS = \
-DEEK_COMPILATION=1 \
@ -111,12 +120,15 @@ libeek_la_LIBADD = \
libeek_gtk_public_headers = \
$(srcdir)/eek-gtk-keyboard.h \
$(srcdir)/eek-gtk.h
$(srcdir)/eek-gtk.h \
$(NULL)
libeek_gtk_private_headers = \
$(srcdir)/eek-gtk-renderer.h
$(srcdir)/eek-gtk-renderer.h \
$(NULL)
libeek_gtk_sources = \
$(srcdir)/eek-gtk-keyboard.c \
$(srcdir)/eek-gtk-renderer.c
$(srcdir)/eek-gtk-renderer.c \
$(NULL)
libeek_gtk_la_SOURCES = $(libeek_gtk_sources)
libeek_gtk_la_CFLAGS = -DEEK_COMPILATION=1 $(GTK_CFLAGS) $(LIBCANBERRA_CFLAGS)
@ -126,11 +138,13 @@ libeek_xkl_public_headers = \
$(srcdir)/eek-xkl-layout.h \
$(srcdir)/eek-xkl.h \
$(srcdir)/eek-xkb-layout.h \
$(srcdir)/eek-xkb.h
$(srcdir)/eek-xkb.h \
$(NULL)
libeek_xkl_sources = \
$(srcdir)/eek-xkb-layout.c \
$(srcdir)/eek-xkl-layout.c
$(srcdir)/eek-xkl-layout.c \
$(NULL)
libeek_xkl_la_SOURCES = $(libeek_xkl_sources)
libeek_xkl_la_CFLAGS = -DEEK_COMPILATION=1 $(LIBXKLAVIER_CFLAGS)
@ -139,56 +153,68 @@ libeek_xkl_la_LIBADD = libeek.la $(LIBXKLAVIER_LIBS)
eekdir = $(includedir)/eek-$(EEK_API_VERSION)/eek
eek_HEADERS = \
$(libeek_public_headers) \
$(srcdir)/eek-enumtypes.h \
$(builddir)/eek-enumtypes.h \
$(libeek_gtk_public_headers) \
$(libeek_xkl_public_headers)
$(libeek_xkl_public_headers) \
$(NULL)
noinst_HEADERS = \
$(libeek_private_headers) \
$(libeek_gtk_private_headers) \
$(libeek_xkl_private_headers)
$(libeek_xkl_private_headers) \
$(NULL)
eek-special-keysym-entries.h: special-keysym-entries.txt
$(AM_V_GEN) $(PYTHON) ./gen-keysym-entries.py special_keysym_entries \
$(AM_V_GEN) $(PYTHON) $(srcdir)/gen-keysym-entries.py \
special_keysym_entries \
< $< > $@
eek-unicode-keysym-entries.h: unicode-keysym-entries.txt
$(AM_V_GEN) $(PYTHON) ./gen-keysym-entries.py unicode_keysym_entries \
$(AM_V_GEN) $(PYTHON) $(srcdir)/gen-keysym-entries.py \
unicode_keysym_entries \
< $< > $@
eek-xkeysym-keysym-entries.h: xkeysym-keysym-entries.txt
$(AM_V_GEN) $(PYTHON) ./gen-keysym-entries.py xkeysym_keysym_entries \
$(AM_V_GEN) $(PYTHON) $(srcdir)/gen-keysym-entries.py \
xkeysym_keysym_entries \
< $< > $@
eek-enumtypes.h: $(libeek_public_headers) eek-enumtypes.h.template
$(AM_V_GEN) $(GLIB_MKENUMS) --template eek-enumtypes.h.template \
$(AM_V_GEN) $(GLIB_MKENUMS) \
--template $(srcdir)/eek-enumtypes.h.template \
$(libeek_public_headers) > eek-enumtypes.h.tmp && \
mv eek-enumtypes.h.tmp eek-enumtypes.h
eek-enumtypes.c: $(libeek_public_headers) eek-enumtypes.c.template
$(AM_V_GEN) $(GLIB_MKENUMS) --template eek-enumtypes.c.template \
$(AM_V_GEN) $(GLIB_MKENUMS) \
--template $(srcdir)/eek-enumtypes.c.template \
$(libeek_public_headers) > eek-enumtypes.c.tmp && \
mv eek-enumtypes.c.tmp eek-enumtypes.c
# gen marshal
eek-marshalers.h: eek-marshalers.list
$(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=_eek_marshal $(srcdir)/eek-marshalers.list --header --internal > $@.tmp && \
$(AM_V_GEN) $(GLIB_GENMARSHAL) \
--prefix=_eek_marshal $(srcdir)/eek-marshalers.list \
--header --internal > $@.tmp && \
mv $@.tmp $@
eek-marshalers.c: eek-marshalers.list eek-marshalers.h
$(AM_V_GEN) (echo "#include \"eek-marshalers.h\""; \
$(GLIB_GENMARSHAL) --prefix=_eek_marshal $(srcdir)/eek-marshalers.list --body --internal) > $@.tmp && \
$(GLIB_GENMARSHAL) --prefix=_eek_marshal \
$(srcdir)/eek-marshalers.list --body --internal) > $@.tmp && \
mv $@.tmp $@
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = \
eek-$(EEK_API_VERSION).pc \
eek-gtk-$(EEK_API_VERSION).pc \
eek-xkl-$(EEK_API_VERSION).pc
eek-xkl-$(EEK_API_VERSION).pc \
$(NULL)
CLEANFILES =
DISTCLEANFILES = \
$(BUILT_SOURCES) \
$(pkgconfig_DATA)
$(pkgconfig_DATA) \
$(NULL)
EXTRA_DIST = \
gen-keysym-entries.py \
@ -197,7 +223,8 @@ EXTRA_DIST = \
xkeysym-keysym-entries.txt \
eek-enumtypes.h.template \
eek-enumtypes.c.template \
eek-marshalers.list
eek-marshalers.list \
$(NULL)
-include $(INTROSPECTION_MAKEFILE)
INTROSPECTION_GIRS =
@ -207,29 +234,57 @@ INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir)
if HAVE_INTROSPECTION
Eek@EEK_LIBRARY_SUFFIX@.gir: libeek.la
Eek@EEK_LIBRARY_SUFFIX_U@_gir_SCANNERFLAGS = --strip-prefix=Eek --pkg=glib-2.0 --pkg-export=eek-$(EEK_API_VERSION)
Eek@EEK_LIBRARY_SUFFIX_U@_gir_SCANNERFLAGS = \
--identifier-prefix=Eek \
--symbol-prefix=eek \
--pkg=glib-2.0 \
--pkg-export=eek-$(EEK_API_VERSION) \
$(NULL)
Eek@EEK_LIBRARY_SUFFIX_U@_gir_INCLUDES = GLib-2.0 GObject-2.0 Gio-2.0
Eek@EEK_LIBRARY_SUFFIX_U@_gir_CFLAGS = $(libeek_la_CFLAGS)
Eek@EEK_LIBRARY_SUFFIX_U@_gir_LIBS = libeek.la
Eek@EEK_LIBRARY_SUFFIX_U@_gir_FILES = $(libeek_sources) $(libeek_public_headers) $(srcdir)/eek-enumtypes.h
Eek@EEK_LIBRARY_SUFFIX_U@_gir_FILES = \
$(libeek_sources) \
$(libeek_public_headers) \
$(builddir)/eek-enumtypes.h \
$(NULL)
EekGtk@EEK_LIBRARY_SUFFIX@.gir: libeek-gtk.la Eek@EEK_LIBRARY_SUFFIX@.gir
EekGtk@EEK_LIBRARY_SUFFIX_U@_gir_SCANNERFLAGS = --pkg-export=eek-gtk-$(EEK_API_VERSION)
EekGtk@EEK_LIBRARY_SUFFIX_U@_gir_INCLUDES = GObject-2.0 Gtk-@GTK_API_VERSION@ Eek@EEK_LIBRARY_SUFFIX@
EekGtk@EEK_LIBRARY_SUFFIX_U@_gir_SCANNERFLAGS = \
--identifier-prefix=Eek \
--symbol-prefix=eek \
--pkg-export=eek-gtk-$(EEK_API_VERSION) \
$(NULL)
EekGtk@EEK_LIBRARY_SUFFIX_U@_gir_INCLUDES = \
GObject-2.0 \
Gtk-@GTK_API_VERSION@ \
Eek@EEK_LIBRARY_SUFFIX@ \
$(NULL)
EekGtk@EEK_LIBRARY_SUFFIX_U@_gir_CFLAGS = $(libeek_gtk_la_CFLAGS)
EekGtk@EEK_LIBRARY_SUFFIX_U@_gir_LIBS = libeek-gtk.la
EekGtk@EEK_LIBRARY_SUFFIX_U@_gir_FILES = $(libeek_gtk_sources) $(libeek_gtk_public_headers)
EekGtk@EEK_LIBRARY_SUFFIX_U@_gir_FILES = \
$(libeek_gtk_sources) \
$(libeek_gtk_public_headers) \
$(NULL)
EekXkl@EEK_LIBRARY_SUFFIX@.gir: libeek-xkl.la Eek@EEK_LIBRARY_SUFFIX@.gir
EekXkl@EEK_LIBRARY_SUFFIX_U@_gir_SCANNERFLAGS = \
--identifier-prefix=Eek \
--symbol-prefix=eek \
$(NULL)
EekXkl@EEK_LIBRARY_SUFFIX_U@_gir_INCLUDES = GObject-2.0 Eek@EEK_LIBRARY_SUFFIX@
EekXkl@EEK_LIBRARY_SUFFIX_U@_gir_CFLAGS = $(libeek_xkl_la_CFLAGS)
EekXkl@EEK_LIBRARY_SUFFIX_U@_gir_LIBS = libeek-xkl.la
EekXkl@EEK_LIBRARY_SUFFIX_U@_gir_FILES = $(libeek_xkl_sources) $(libeek_xkl_public_headers)
EekXkl@EEK_LIBRARY_SUFFIX_U@_gir_FILES = \
$(libeek_xkl_sources) \
$(libeek_xkl_public_headers) \
$(NULL)
INTROSPECTION_GIRS += \
Eek@EEK_LIBRARY_SUFFIX@.gir \
EekGtk@EEK_LIBRARY_SUFFIX@.gir \
EekXkl@EEK_LIBRARY_SUFFIX@.gir
EekXkl@EEK_LIBRARY_SUFFIX@.gir \
$(NULL)
girdir = $(datadir)/gir-1.0
gir_DATA = $(INTROSPECTION_GIRS)

0
eek/config.h Normal file
View File

View File

@ -1,17 +1,17 @@
/*
/*
* 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
@ -61,31 +61,22 @@ struct _EekGtkKeyboardPrivate
{
EekRenderer *renderer;
EekKeyboard *keyboard;
gulong key_pressed_handler;
gulong key_released_handler;
gulong key_locked_handler;
gulong key_unlocked_handler;
gulong key_cancelled_handler;
gulong symbol_index_changed_handler;
EekTheme *theme;
};
static EekColor * color_from_gdk_color (GdkColor *gdk_color);
static void on_key_pressed (EekKeyboard *keyboard,
EekKey *key,
gpointer user_data);
static void on_key_released (EekKeyboard *keyboard,
EekKey *key,
gpointer user_data);
static void on_key_pressed (EekKey *key,
EekGtkKeyboard *self);
static void on_key_released (EekKey *key,
EekGtkKeyboard *self);
static void on_key_locked (EekKeyboard *keyboard,
EekKey *key,
gpointer user_data);
static void on_key_unlocked (EekKeyboard *keyboard,
EekKey *key,
gpointer user_data);
static void on_key_cancelled (EekKeyboard *keyboard,
EekKey *key,
gpointer user_data);
static void on_symbol_index_changed (EekKeyboard *keyboard,
gint group,
gint level,
@ -100,7 +91,6 @@ static void render_released_key (GtkWidget *widget,
static void
eek_gtk_keyboard_real_realize (GtkWidget *self)
{
gtk_widget_set_double_buffered (self, FALSE);
gtk_widget_set_events (self,
GDK_EXPOSURE_MASK |
GDK_KEY_PRESS_MASK |
@ -118,16 +108,12 @@ eek_gtk_keyboard_real_draw (GtkWidget *self,
{
EekGtkKeyboardPrivate *priv = EEK_GTK_KEYBOARD_GET_PRIVATE(self);
GtkAllocation allocation;
EekColor background;
GList *list, *head;
gtk_widget_get_allocation (self, &allocation);
if (!priv->renderer) {
GtkStyle *style;
GtkStateType state;
PangoContext *pcontext;
EekColor *color;
pcontext = gtk_widget_get_pango_context (self);
priv->renderer = eek_gtk_renderer_new (priv->keyboard, pcontext, self);
@ -137,30 +123,8 @@ eek_gtk_keyboard_real_draw (GtkWidget *self,
eek_renderer_set_allocation_size (priv->renderer,
allocation.width,
allocation.height);
style = gtk_widget_get_style (self);
state = gtk_widget_get_state (self);
color = color_from_gdk_color (&style->text[state]);
eek_renderer_set_default_foreground_color (priv->renderer, color);
eek_color_free (color);
color = color_from_gdk_color (&style->base[state]);
eek_renderer_set_default_background_color (priv->renderer, color);
eek_color_free (color);
}
/* blank background */
eek_renderer_get_background_color (priv->renderer,
EEK_ELEMENT(priv->keyboard),
&background);
cairo_set_source_rgba (cr,
background.red,
background.green,
background.blue,
background.alpha);
cairo_paint (cr);
eek_renderer_render_keyboard (priv->renderer, cr);
/* redraw pressed key */
@ -195,49 +159,21 @@ eek_gtk_keyboard_real_size_allocate (GtkWidget *self,
size_allocate (self, allocation);
}
static gboolean
eek_gtk_keyboard_real_button_press_event (GtkWidget *self,
GdkEventButton *event)
{
static void depress(EekGtkKeyboard *self,
gdouble x, gdouble y, guint32 time) {
EekGtkKeyboardPrivate *priv = EEK_GTK_KEYBOARD_GET_PRIVATE(self);
EekKey *key;
key = eek_renderer_find_key_by_position (priv->renderer,
(gdouble)event->x,
(gdouble)event->y);
if (key)
g_signal_emit_by_name (key, "pressed", priv->keyboard);
return TRUE;
EekKey *key = eek_renderer_find_key_by_position (priv->renderer, x, y);
if (key) {
eek_keyboard_press_key(priv->keyboard, key, time);
on_key_pressed(key, self);
}
}
static gboolean
eek_gtk_keyboard_real_button_release_event (GtkWidget *self,
GdkEventButton *event)
{
static void drag(EekGtkKeyboard *self,
gdouble x, gdouble y, guint32 time) {
EekGtkKeyboardPrivate *priv = EEK_GTK_KEYBOARD_GET_PRIVATE(self);
GList *list, *head;
EekKey *key = eek_renderer_find_key_by_position (priv->renderer, x, y);
list = eek_keyboard_get_pressed_keys (priv->keyboard);
for (head = list; head; head = g_list_next (head))
g_signal_emit_by_name (head->data, "released", priv->keyboard);
g_list_free (list);
return TRUE;
}
static gboolean
eek_gtk_keyboard_real_motion_notify_event (GtkWidget *self,
GdkEventMotion *event)
{
EekGtkKeyboardPrivate *priv = EEK_GTK_KEYBOARD_GET_PRIVATE(self);
EekKey *key;
if (event->state == 0)
return FALSE;
key = eek_renderer_find_key_by_position (priv->renderer,
(gdouble)event->x,
(gdouble)event->y);
if (key) {
GList *list, *head;
gboolean found = FALSE;
@ -246,17 +182,95 @@ eek_gtk_keyboard_real_motion_notify_event (GtkWidget *self,
for (head = list; head; head = g_list_next (head)) {
if (head->data == key)
found = TRUE;
else
g_signal_emit_by_name (head->data, "cancelled", priv->keyboard);
else {
eek_keyboard_release_key(priv->keyboard, EEK_KEY(head->data), time);
on_key_released(key, self);
}
}
g_list_free (list);
if (!found)
g_signal_emit_by_name (key, "pressed", priv->keyboard);
if (!found) {
eek_keyboard_press_key(priv->keyboard, key, time);
on_key_pressed(key, self);
}
}
}
static void release(EekGtkKeyboard *self, guint32 time) {
EekGtkKeyboardPrivate *priv = EEK_GTK_KEYBOARD_GET_PRIVATE(self);
GList *list = eek_keyboard_get_pressed_keys (priv->keyboard);
for (GList *head = list; head; head = g_list_next (head)) {
EekKey *key = EEK_KEY(head->data);
eek_keyboard_release_key(priv->keyboard, key, time);
on_key_released(key, self);
}
g_list_free (list);
}
static gboolean
eek_gtk_keyboard_real_button_press_event (GtkWidget *self,
GdkEventButton *event)
{
if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
depress(EEK_GTK_KEYBOARD(self), event->x, event->y, event->time);
}
return TRUE;
}
// TODO: this belongs more in gtk_keyboard, with a way to find out which key to re-render
static gboolean
eek_gtk_keyboard_real_button_release_event (GtkWidget *self,
GdkEventButton *event)
{
if (event->type == GDK_BUTTON_RELEASE && event->button == 1) {
// TODO: can the event have different coords than the previous move event?
release(EEK_GTK_KEYBOARD(self), event->time);
}
return TRUE;
}
static gboolean
eek_gtk_keyboard_real_motion_notify_event (GtkWidget *self,
GdkEventMotion *event)
{
if (event->state & GDK_BUTTON1_MASK) {
drag(EEK_GTK_KEYBOARD(self), event->x, event->y, event->time);
}
return TRUE;
}
// Only one touch stream at a time allowed. Others will be completely ignored.
static gboolean
handle_touch_event (GtkWidget *widget,
GdkEventTouch *event) {
EekGtkKeyboard *self = EEK_GTK_KEYBOARD(widget);
if (event->type == GDK_TOUCH_BEGIN) {
if (self->sequence) {
// Ignore second and following touch points
return FALSE;
}
self->sequence = event->sequence;
depress(self, event->x, event->y, event->time);
return TRUE;
}
if (self->sequence != event->sequence) {
return FALSE;
}
if (event->type == GDK_TOUCH_UPDATE) {
drag(self, event->x, event->y, event->time);
}
if (event->type == GDK_TOUCH_END || event->type == GDK_TOUCH_CANCEL) {
// TODO: can the event have different coords than the previous update event?
release(self, event->time);
self->sequence = NULL;
}
return TRUE;
}
static void
eek_gtk_keyboard_real_unmap (GtkWidget *self)
{
@ -270,8 +284,10 @@ eek_gtk_keyboard_real_unmap (GtkWidget *self)
EekKeyboard::key-released signal can remove elements from its
internal copy */
list = eek_keyboard_get_pressed_keys (priv->keyboard);
for (head = list; head; head = g_list_next (head))
g_signal_emit_by_name (head->data, "released", priv->keyboard);
for (head = list; head; head = g_list_next (head)) {
g_log("squeek", G_LOG_LEVEL_DEBUG, "emit EekKey released");
g_signal_emit_by_name (head->data, "released");
}
g_list_free (list);
}
@ -309,21 +325,12 @@ eek_gtk_keyboard_set_keyboard (EekGtkKeyboard *self,
EekGtkKeyboardPrivate *priv = EEK_GTK_KEYBOARD_GET_PRIVATE(self);
priv->keyboard = g_object_ref (keyboard);
priv->key_pressed_handler =
g_signal_connect (priv->keyboard, "key-pressed",
G_CALLBACK(on_key_pressed), self);
priv->key_released_handler =
g_signal_connect (priv->keyboard, "key-released",
G_CALLBACK(on_key_released), self);
priv->key_locked_handler =
g_signal_connect (priv->keyboard, "key-locked",
G_CALLBACK(on_key_locked), self);
priv->key_unlocked_handler =
g_signal_connect (priv->keyboard, "key-unlocked",
G_CALLBACK(on_key_unlocked), self);
priv->key_cancelled_handler =
g_signal_connect (priv->keyboard, "key-cancelled",
G_CALLBACK(on_key_cancelled), self);
priv->symbol_index_changed_handler =
g_signal_connect (priv->keyboard, "symbol-index-changed",
G_CALLBACK(on_symbol_index_changed), self);
@ -359,14 +366,6 @@ eek_gtk_keyboard_dispose (GObject *object)
}
if (priv->keyboard) {
if (g_signal_handler_is_connected (priv->keyboard,
priv->key_pressed_handler))
g_signal_handler_disconnect (priv->keyboard,
priv->key_pressed_handler);
if (g_signal_handler_is_connected (priv->keyboard,
priv->key_released_handler))
g_signal_handler_disconnect (priv->keyboard,
priv->key_released_handler);
if (g_signal_handler_is_connected (priv->keyboard,
priv->key_locked_handler))
g_signal_handler_disconnect (priv->keyboard,
@ -375,19 +374,16 @@ eek_gtk_keyboard_dispose (GObject *object)
priv->key_unlocked_handler))
g_signal_handler_disconnect (priv->keyboard,
priv->key_unlocked_handler);
if (g_signal_handler_is_connected (priv->keyboard,
priv->key_cancelled_handler))
g_signal_handler_disconnect (priv->keyboard,
priv->key_cancelled_handler);
if (g_signal_handler_is_connected (priv->keyboard,
priv->symbol_index_changed_handler))
g_signal_handler_disconnect (priv->keyboard,
priv->symbol_index_changed_handler);
GList *list, *head;
list = eek_keyboard_get_pressed_keys (priv->keyboard);
for (head = list; head; head = g_list_next (head)) {
g_log("squeek", G_LOG_LEVEL_DEBUG, "emit EekKey pressed");
g_signal_emit_by_name (head->data, "released", priv->keyboard);
}
g_list_free (list);
@ -426,6 +422,7 @@ eek_gtk_keyboard_class_init (EekGtkKeyboardClass *klass)
eek_gtk_keyboard_real_motion_notify_event;
widget_class->query_tooltip =
eek_gtk_keyboard_real_query_tooltip;
widget_class->touch_event = handle_touch_event;
gobject_class->set_property = eek_gtk_keyboard_set_property;
gobject_class->dispose = eek_gtk_keyboard_dispose;
@ -459,15 +456,6 @@ eek_gtk_keyboard_new (EekKeyboard *keyboard)
return g_object_new (EEK_TYPE_GTK_KEYBOARD, "keyboard", keyboard, NULL);
}
static EekColor *
color_from_gdk_color (GdkColor *gdk_color)
{
return eek_color_new (gdk_color->red / (gdouble)0xFFFF,
gdk_color->green / (gdouble)0xFFFF,
gdk_color->blue / (gdouble)0xFFFF,
1.0);
}
static void
magnify_bounds (GtkWidget *self,
EekBounds *bounds,
@ -497,13 +485,17 @@ render_pressed_key (GtkWidget *widget,
{
EekGtkKeyboardPrivate *priv = EEK_GTK_KEYBOARD_GET_PRIVATE(widget);
EekBounds bounds, large_bounds;
cairo_t *cr;
cr = gdk_cairo_create (GDK_DRAWABLE (gtk_widget_get_window (widget)));
eek_renderer_get_key_bounds (priv->renderer, key, &bounds, TRUE);
magnify_bounds (widget, &bounds, &large_bounds, 1.5);
GdkWindow *window = GDK_DRAWABLE (gtk_widget_get_window (widget));
cairo_region_t *region = gdk_window_get_clip_region (window);
GdkDrawingContext *context = gdk_window_begin_draw_frame(
window, region
);
cairo_t *cr = gdk_drawing_context_get_cairo_context(context);
cairo_save (cr);
cairo_translate (cr, bounds.x, bounds.y);
eek_renderer_render_key (priv->renderer, cr, key, 1.0, TRUE);
@ -514,7 +506,8 @@ render_pressed_key (GtkWidget *widget,
eek_renderer_render_key (priv->renderer, cr, key, 1.5, TRUE);
cairo_restore (cr);
cairo_destroy (cr);
gdk_window_end_draw_frame(window, context);
cairo_region_destroy(region);
}
static void
@ -563,18 +556,16 @@ render_released_key (GtkWidget *widget,
}
static void
on_key_pressed (EekKeyboard *keyboard,
EekKey *key,
gpointer user_data)
on_key_pressed (EekKey *key,
EekGtkKeyboard *self)
{
GtkWidget *widget = user_data;
EekGtkKeyboardPrivate *priv = EEK_GTK_KEYBOARD_GET_PRIVATE(widget);
EekGtkKeyboardPrivate *priv = EEK_GTK_KEYBOARD_GET_PRIVATE(self);
/* renderer may have not been set yet if the widget is a popup */
if (!priv->renderer)
return;
render_pressed_key (widget, key);
render_pressed_key (GTK_WIDGET(self), key);
#if HAVE_LIBCANBERRA
ca_gtk_play_for_widget (widget, 0,
@ -586,18 +577,16 @@ on_key_pressed (EekKeyboard *keyboard,
}
static void
on_key_released (EekKeyboard *keyboard,
EekKey *key,
gpointer user_data)
on_key_released (EekKey *key,
EekGtkKeyboard *self)
{
GtkWidget *widget = user_data;
EekGtkKeyboardPrivate *priv = EEK_GTK_KEYBOARD_GET_PRIVATE(widget);
EekGtkKeyboardPrivate *priv = EEK_GTK_KEYBOARD_GET_PRIVATE(self);
/* renderer may have not been set yet if the widget is a popup */
if (!priv->renderer)
return;
render_released_key (widget, key);
render_released_key (GTK_WIDGET(self), key);
#if HAVE_LIBCANBERRA
ca_gtk_play_for_widget (widget, 0,
@ -608,21 +597,6 @@ on_key_released (EekKeyboard *keyboard,
#endif
}
static void
on_key_cancelled (EekKeyboard *keyboard,
EekKey *key,
gpointer user_data)
{
GtkWidget *widget = user_data;
EekGtkKeyboardPrivate *priv = EEK_GTK_KEYBOARD_GET_PRIVATE(widget);
/* renderer may have not been set yet if the widget is a popup */
if (!priv->renderer)
return;
render_released_key (widget, key);
}
static void
on_key_locked (EekKeyboard *keyboard,
EekKey *key,

View File

@ -47,6 +47,8 @@ struct _EekGtkKeyboard
/*< private >*/
GtkDrawingArea parent;
GdkEventSequence *sequence; // unowned reference
EekGtkKeyboardPrivate *priv;
};

View File

@ -30,10 +30,6 @@
#endif /* HAVE_CONFIG_H */
#include <string.h>
#define DEBUG 0
#if DEBUG
#include <stdio.h>
#endif
#include "eek-key.h"
#include "eek-section.h"
@ -51,11 +47,8 @@ enum {
};
enum {
PRESSED,
RELEASED,
LOCKED,
UNLOCKED,
CANCELLED,
LAST_SIGNAL
};
@ -73,33 +66,11 @@ struct _EekKeyPrivate
EekSymbolMatrix *symbol_matrix;
gint column;
gint row;
gulong oref;
gulong oref; // UI outline reference
gboolean is_pressed;
gboolean is_locked;
};
static void
eek_key_real_pressed (EekKey *self)
{
EekKeyPrivate *priv = EEK_KEY_GET_PRIVATE(self);
priv->is_pressed = TRUE;
#if DEBUG
g_debug ("pressed %X", eek_key_get_keycode (self));
#endif
}
static void
eek_key_real_released (EekKey *self)
{
EekKeyPrivate *priv = EEK_KEY_GET_PRIVATE(self);
priv->is_pressed = FALSE;
#if DEBUG
g_debug ("released %X", eek_key_get_keycode (self));
#endif
}
static void
eek_key_real_locked (EekKey *self)
{
@ -122,17 +93,6 @@ eek_key_real_unlocked (EekKey *self)
#endif
}
static void
eek_key_real_cancelled (EekKey *self)
{
EekKeyPrivate *priv = EEK_KEY_GET_PRIVATE(self);
priv->is_pressed = FALSE;
#if DEBUG
g_debug ("cancelled %X", eek_key_get_keycode (self));
#endif
}
static void
eek_key_finalize (GObject *object)
{
@ -222,11 +182,8 @@ eek_key_class_init (EekKeyClass *klass)
gobject_class->finalize = eek_key_finalize;
/* signals */
klass->pressed = eek_key_real_pressed;
klass->released = eek_key_real_released;
klass->locked = eek_key_real_locked;
klass->unlocked = eek_key_real_unlocked;
klass->cancelled = eek_key_real_cancelled;
/**
* EekKey:keycode:
@ -288,42 +245,6 @@ eek_key_class_init (EekKeyClass *klass)
G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_OREF, pspec);
/**
* EekKey::pressed:
* @key: an #EekKey
*
* The ::pressed signal is emitted each time @key is shifted to
* the pressed state. The class handler runs before signal
* handlers to allow signal handlers to read the status of @key
* with eek_key_is_pressed().
*/
signals[PRESSED] =
g_signal_new (I_("pressed"),
G_TYPE_FROM_CLASS(gobject_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET(EekKeyClass, pressed),
NULL,
NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/**
* EekKey::released:
* @key: an #EekKey
*
* The ::released signal is emitted each time @key is shifted to
* the released state.
*/
signals[RELEASED] =
g_signal_new (I_("released"),
G_TYPE_FROM_CLASS(gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(EekKeyClass, released),
NULL,
NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/**
* EekKey::locked:
* @key: an #EekKey
@ -359,23 +280,6 @@ eek_key_class_init (EekKeyClass *klass)
NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/**
* EekKey::cancelled:
* @key: an #EekKey
*
* The ::cancelled signal is emitted each time @key is shifted to
* the cancelled state.
*/
signals[CANCELLED] =
g_signal_new (I_("cancelled"),
G_TYPE_FROM_CLASS(gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(EekKeyClass, cancelled),
NULL,
NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
static void
@ -677,3 +581,9 @@ eek_key_is_locked (EekKey *key)
g_return_val_if_fail (EEK_IS_KEY(key), FALSE);
return key->priv->is_locked;
}
void eek_key_set_pressed(EekKey *key, gboolean value)
{
g_return_if_fail (EEK_IS_KEY(key));
key->priv->is_pressed = value;
}

View File

@ -43,6 +43,9 @@ typedef struct _EekKeyPrivate EekKeyPrivate;
/**
* EekKey:
*
* Contains information about the state of a key.
* TODO: rewrite as a plain struct
*
* The #EekKey structure contains only private data and should only be
* accessed using the provided API.
*/
@ -71,11 +74,8 @@ struct _EekKeyClass
/*< public >*/
/* signals */
void (* pressed) (EekKey *key);
void (* released) (EekKey *key);
void (* locked) (EekKey *key);
void (* unlocked) (EekKey *key);
void (* cancelled) (EekKey *key);
};
GType eek_key_get_type (void) G_GNUC_CONST;
@ -110,6 +110,8 @@ guint eek_key_get_oref (EekKey *key);
gboolean eek_key_is_pressed (EekKey *key);
gboolean eek_key_is_locked (EekKey *key);
void eek_key_set_pressed (EekKey *key,
gboolean value);
G_END_DECLS
#endif /* EEK_KEY_H */

View File

@ -199,32 +199,21 @@ void
_eek_rounded_polygon (cairo_t *cr,
gdouble radius,
EekPoint *points,
gint num_points)
guint num_points)
{
gint i, j;
cairo_move_to (cr,
(gdouble) (points[num_points - 1].x +
points[0].x) / 2,
(gdouble) (points[num_points - 1].y +
points[0].y) / 2);
#ifdef KBDRAW_DEBUG
printf (" rounded polygon of radius %f:\n", radius);
#endif
for (i = 0; i < num_points; i++) {
j = (i + 1) % num_points;
for (guint i = 0; i < num_points; i++) {
guint j = (i + 1) % num_points;
rounded_corner (cr, (gdouble) points[i].x,
(gdouble) points[i].y,
(gdouble) (points[i].x + points[j].x) / 2,
(gdouble) (points[i].y + points[j].y) / 2,
radius);
#ifdef KBDRAW_DEBUG
printf (" corner (%d, %d) -> (%d, %d):\n",
points[i].x, points[i].y, points[j].x,
points[j].y);
#endif
};
}
cairo_close_path (cr);
}

View File

@ -32,10 +32,12 @@
#endif /* HAVE_CONFIG_H */
#include "eek-keyboard.h"
#include "eek-marshalers.h"
#include "eek-section.h"
#include "eek-key.h"
#include "eek-symbol.h"
#include "eek-enumtypes.h"
#include "eekboard/key-emitter.h"
enum {
PROP_0,
@ -45,11 +47,9 @@ enum {
};
enum {
KEY_PRESSED,
KEY_RELEASED,
KEY_LOCKED,
KEY_UNLOCKED,
KEY_CANCELLED,
LAST_SIGNAL
};
@ -91,22 +91,6 @@ eek_modifier_key_free (EekModifierKey *modkey)
g_slice_free (EekModifierKey, modkey);
}
static void
on_key_pressed (EekSection *section,
EekKey *key,
EekKeyboard *keyboard)
{
g_signal_emit (keyboard, signals[KEY_PRESSED], 0, key);
}
static void
on_key_released (EekSection *section,
EekKey *key,
EekKeyboard *keyboard)
{
g_signal_emit (keyboard, signals[KEY_RELEASED], 0, key);
}
static void
on_key_locked (EekSection *section,
EekKey *key,
@ -123,14 +107,6 @@ on_key_unlocked (EekSection *section,
g_signal_emit (keyboard, signals[KEY_UNLOCKED], 0, key);
}
static void
on_key_cancelled (EekSection *section,
EekKey *key,
EekKeyboard *keyboard)
{
g_signal_emit (keyboard, signals[KEY_CANCELLED], 0, key);
}
static void
on_symbol_index_changed (EekSection *section,
gint group,
@ -278,74 +254,76 @@ set_modifiers_with_key (EekKeyboard *self,
priv->modifiers = modifiers;
}
static void
eek_keyboard_real_key_pressed (EekKeyboard *self,
EekKey *key)
{
EekKeyboardPrivate *priv = EEK_KEYBOARD_GET_PRIVATE(self);
EekSymbol *symbol;
EekModifierType modifier;
void eek_keyboard_press_key(EekKeyboard *keyboard, EekKey *key, guint32 timestamp) {
EekKeyboardPrivate *priv = EEK_KEYBOARD_GET_PRIVATE(keyboard);
eek_key_set_pressed(key, TRUE);
priv->pressed_keys = g_list_prepend (priv->pressed_keys, key);
symbol = eek_key_get_symbol_with_fallback (key, 0, 0);
EekSymbol *symbol = eek_key_get_symbol_with_fallback (key, 0, 0);
if (!symbol)
return;
modifier = eek_symbol_get_modifier_mask (symbol);
EekModifierType modifier = eek_symbol_get_modifier_mask (symbol);
if (priv->modifier_behavior == EEK_MODIFIER_BEHAVIOR_NONE) {
set_modifiers_with_key (self, key, priv->modifiers | modifier);
set_level_from_modifiers (self);
set_modifiers_with_key (keyboard, key, priv->modifiers | modifier);
set_level_from_modifiers (keyboard);
}
// "Borrowed" from eek-context-service; doesn't influence the state but forwards the event
guint keycode = eek_key_get_keycode (key);
EekModifierType modifiers = eek_keyboard_get_modifiers (keyboard);
emit_key_activated(keyboard->manager, keyboard, keycode, symbol, modifiers, TRUE, timestamp);
}
static void
eek_keyboard_real_key_released (EekKeyboard *self,
EekKey *key)
{
EekKeyboardPrivate *priv = EEK_KEYBOARD_GET_PRIVATE(self);
EekSymbol *symbol;
EekModifierType modifier;
void eek_keyboard_release_key( EekKeyboard *keyboard,
EekKey *key,
guint32 timestamp) {
EekKeyboardPrivate *priv = EEK_KEYBOARD_GET_PRIVATE(keyboard);
EEK_KEYBOARD_GET_CLASS (self)->key_cancelled (self, key);
symbol = eek_key_get_symbol_with_fallback (key, 0, 0);
if (!symbol)
return;
modifier = eek_symbol_get_modifier_mask (symbol);
switch (priv->modifier_behavior) {
case EEK_MODIFIER_BEHAVIOR_NONE:
set_modifiers_with_key (self, key, priv->modifiers & ~modifier);
break;
case EEK_MODIFIER_BEHAVIOR_LOCK:
priv->modifiers ^= modifier;
break;
case EEK_MODIFIER_BEHAVIOR_LATCH:
if (modifier)
set_modifiers_with_key (self, key, priv->modifiers ^ modifier);
else
set_modifiers_with_key (self, key,
(priv->modifiers ^ modifier) & modifier);
break;
}
set_level_from_modifiers (self);
}
static void
eek_keyboard_real_key_cancelled (EekKeyboard *self,
EekKey *key)
{
EekKeyboardPrivate *priv = EEK_KEYBOARD_GET_PRIVATE(self);
GList *head;
for (head = priv->pressed_keys; head; head = g_list_next (head)) {
for (GList *head = priv->pressed_keys; head; head = g_list_next (head)) {
if (head->data == key) {
priv->pressed_keys = g_list_remove_link (priv->pressed_keys, head);
g_list_free1 (head);
break;
}
}
EekSymbol *symbol = eek_key_get_symbol_with_fallback (key, 0, 0);
if (!symbol)
return;
EekModifierType modifier = eek_symbol_get_modifier_mask (symbol);
if (!symbol)
return;
modifier = eek_symbol_get_modifier_mask (symbol);
switch (priv->modifier_behavior) {
case EEK_MODIFIER_BEHAVIOR_NONE:
set_modifiers_with_key (keyboard, key, priv->modifiers & ~modifier);
break;
case EEK_MODIFIER_BEHAVIOR_LOCK:
priv->modifiers ^= modifier;
break;
case EEK_MODIFIER_BEHAVIOR_LATCH:
if (modifier)
set_modifiers_with_key (keyboard, key, priv->modifiers ^ modifier);
else
set_modifiers_with_key (keyboard, key,
(priv->modifiers ^ modifier) & modifier);
break;
}
set_level_from_modifiers (keyboard);
// "Borrowed" from eek-context-service; doesn't influence the state but forwards the event
guint keycode = eek_key_get_keycode (key);
guint modifiers = eek_keyboard_get_modifiers (keyboard);
emit_key_activated(keyboard->manager, keyboard, keycode, symbol, modifiers, FALSE, timestamp);
}
static void
@ -365,7 +343,7 @@ static void
eek_keyboard_finalize (GObject *object)
{
EekKeyboardPrivate *priv = EEK_KEYBOARD_GET_PRIVATE(object);
gint i;
guint i;
g_list_free (priv->pressed_keys);
g_list_free_full (priv->locked_keys,
@ -389,16 +367,10 @@ static void
eek_keyboard_real_child_added (EekContainer *self,
EekElement *element)
{
g_signal_connect (element, "key-pressed",
G_CALLBACK(on_key_pressed), self);
g_signal_connect (element, "key-released",
G_CALLBACK(on_key_released), self);
g_signal_connect (element, "key-locked",
G_CALLBACK(on_key_locked), self);
g_signal_connect (element, "key-unlocked",
G_CALLBACK(on_key_unlocked), self);
g_signal_connect (element, "key-cancelled",
G_CALLBACK(on_key_cancelled), self);
g_signal_connect (element, "symbol-index-changed",
G_CALLBACK(on_symbol_index_changed), self);
}
@ -407,11 +379,8 @@ static void
eek_keyboard_real_child_removed (EekContainer *self,
EekElement *element)
{
g_signal_handlers_disconnect_by_func (element, on_key_pressed, self);
g_signal_handlers_disconnect_by_func (element, on_key_released, self);
g_signal_handlers_disconnect_by_func (element, on_key_locked, self);
g_signal_handlers_disconnect_by_func (element, on_key_unlocked, self);
g_signal_handlers_disconnect_by_func (element, on_key_cancelled, self);
}
static void
@ -427,10 +396,6 @@ eek_keyboard_class_init (EekKeyboardClass *klass)
klass->create_section = eek_keyboard_real_create_section;
/* signals */
klass->key_pressed = eek_keyboard_real_key_pressed;
klass->key_released = eek_keyboard_real_key_released;
klass->key_cancelled = eek_keyboard_real_key_cancelled;
container_class->child_added = eek_keyboard_real_child_added;
container_class->child_removed = eek_keyboard_real_child_removed;
@ -468,46 +433,6 @@ eek_keyboard_class_init (EekKeyboardClass *klass)
PROP_MODIFIER_BEHAVIOR,
pspec);
/**
* EekKeyboard::key-pressed:
* @keyboard: an #EekKeyboard
* @key: an #EekKey
*
* The ::key-pressed signal is emitted each time a key in @keyboard
* is shifted to the pressed state.
*/
signals[KEY_PRESSED] =
g_signal_new (I_("key-pressed"),
G_TYPE_FROM_CLASS(gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(EekKeyboardClass, key_pressed),
NULL,
NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE,
1,
EEK_TYPE_KEY);
/**
* EekKeyboard::key-released:
* @keyboard: an #EekKeyboard
* @key: an #EekKey
*
* The ::key-released signal is emitted each time a key in @keyboard
* is shifted to the released state.
*/
signals[KEY_RELEASED] =
g_signal_new (I_("key-released"),
G_TYPE_FROM_CLASS(gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(EekKeyboardClass, key_released),
NULL,
NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE,
1,
EEK_TYPE_KEY);
/**
* EekKeyboard::key-locked:
* @keyboard: an #EekKeyboard
@ -547,26 +472,6 @@ eek_keyboard_class_init (EekKeyboardClass *klass)
G_TYPE_NONE,
1,
EEK_TYPE_KEY);
/**
* EekKeyboard::key-cancelled:
* @keyboard: an #EekKeyboard
* @key: an #EekKey
*
* The ::key-cancelled signal is emitted each time a key in @keyboard
* is shifted to the cancelled state.
*/
signals[KEY_CANCELLED] =
g_signal_new (I_("key-cancelled"),
G_TYPE_FROM_CLASS(gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(EekKeyboardClass, key_cancelled),
NULL,
NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE,
1,
EEK_TYPE_KEY);
}
static void

View File

@ -26,6 +26,7 @@
#define EEK_KEYBOARD_H 1
#include <glib-object.h>
#include <xkbcommon/xkbcommon.h>
#include "eek-container.h"
#include "eek-types.h"
#include "eek-layout.h"
@ -45,6 +46,10 @@ typedef struct _EekKeyboardPrivate EekKeyboardPrivate;
/**
* EekKeyboard:
*
* Contains the state of the physical keyboard.
*
* Is also a graphical element...
*
* The #EekKeyboard structure contains only private data and should
* only be accessed using the provided API.
*/
@ -54,6 +59,11 @@ struct _EekKeyboard
EekContainer parent;
EekKeyboardPrivate *priv;
struct xkb_keymap *keymap;
int keymap_fd; // keymap formatted as XKB string
size_t keymap_len; // length of the data inside keymap_fd
EekboardContextService *manager; // unowned reference
};
/**
@ -82,12 +92,6 @@ struct _EekKeyboardClass
EekKey *(* find_key_by_keycode) (EekKeyboard *self,
guint keycode);
/* signals */
void (* key_pressed) (EekKeyboard *self,
EekKey *key);
void (* key_released) (EekKeyboard *self,
EekKey *key);
/*< private >*/
/* obsolete members moved to EekElement */
gpointer symbol_index_changed;
@ -98,8 +102,6 @@ struct _EekKeyboardClass
EekKey *key);
void (* key_unlocked) (EekKeyboard *self,
EekKey *key);
void (* key_cancelled) (EekKeyboard *self,
EekKey *key);
/*< private >*/
/* padding */
@ -121,12 +123,13 @@ struct _EekModifierKey {
};
typedef struct _EekModifierKey EekModifierKey;
GType eek_keyboard_get_type
(void) G_GNUC_CONST;
EekKeyboard *eek_keyboard_new (EekLayout *layout,
EekKeyboard *eek_keyboard_new (EekboardContextService *manager,
EekLayout *layout,
gdouble initial_width,
gdouble initial_height);
GType eek_keyboard_get_type
(void) G_GNUC_CONST;
EekLayout *eek_keyboard_get_layout
(EekKeyboard *keyboard);
void eek_keyboard_get_size
@ -188,5 +191,8 @@ EekModifierKey *eek_modifier_key_copy
void eek_modifier_key_free
(EekModifierKey *modkey);
void eek_keyboard_press_key(EekKeyboard *keyboard, EekKey *key, guint32 timestamp);
void eek_keyboard_release_key(EekKeyboard *keyboard, EekKey *key, guint32 timestamp);
G_END_DECLS
#endif /* EEK_KEYBOARD_H */

View File

@ -32,6 +32,7 @@
#include "eek-layout.h"
#include "eek-keyboard.h"
#include "eekboard/eekboard-context-service.h"
G_DEFINE_ABSTRACT_TYPE (EekLayout, eek_layout, G_TYPE_OBJECT);
@ -55,14 +56,16 @@ eek_layout_init (EekLayout *self)
* Create a new #EekKeyboard based on @layout.
*/
EekKeyboard *
eek_keyboard_new (EekLayout *layout,
eek_keyboard_new (EekboardContextService *manager,
EekLayout *layout,
gdouble initial_width,
gdouble initial_height)
{
g_assert (EEK_IS_LAYOUT(layout));
g_assert (EEK_LAYOUT_GET_CLASS(layout)->create_keyboard);
return EEK_LAYOUT_GET_CLASS(layout)->create_keyboard (layout,
return EEK_LAYOUT_GET_CLASS(layout)->create_keyboard (manager,
layout,
initial_width,
initial_height);
}

View File

@ -56,7 +56,8 @@ struct _EekLayoutClass
GObjectClass parent_class;
/*< public >*/
EekKeyboard* (* create_keyboard) (EekLayout *self,
EekKeyboard* (* create_keyboard) (EekboardContextService *manager,
EekLayout *self,
gdouble initial_width,
gdouble initial_height);

View File

@ -79,7 +79,7 @@ typedef struct _TextProperty TextProperty;
extern void _eek_rounded_polygon (cairo_t *cr,
gdouble radius,
EekPoint *points,
gint num_points);
guint num_points);
static void eek_renderer_real_render_key_label (EekRenderer *self,
PangoLayout *layout,
@ -116,8 +116,8 @@ create_keyboard_surface_key_callback (EekElement *element,
cairo_rectangle (data->cr,
0.0,
0.0,
bounds.width * priv->scale,
bounds.height * priv->scale);
bounds.width * priv->scale + 100,
bounds.height * priv->scale + 100);
cairo_clip (data->cr);
render_key (data->renderer, data->cr, EEK_KEY(element), FALSE);
@ -166,8 +166,8 @@ create_keyboard_surface (EekRenderer *renderer)
eek_element_get_bounds (EEK_ELEMENT(priv->keyboard), &bounds);
keyboard_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
bounds.width * priv->scale,
bounds.height * priv->scale);
ceil(bounds.width * priv->scale),
ceil(bounds.height * priv->scale));
data.cr = cairo_create (keyboard_surface);
data.renderer = renderer;
@ -205,8 +205,6 @@ render_key_outline (EekRenderer *renderer,
EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(renderer);
EekOutline *outline;
EekBounds bounds;
gdouble scale;
gint i;
guint oref;
EekThemeNode *theme_node;
EekColor foreground, background, gradient_start, gradient_end, border_color;
@ -233,14 +231,14 @@ render_key_outline (EekRenderer *renderer,
border_width = eek_theme_node_get_border_width (theme_node,
EEK_SIDE_TOP);
border_radius = eek_theme_node_get_border_radius (theme_node,
EEK_SIDE_TOP);
EEK_CORNER_TOPLEFT);
eek_theme_node_get_border_color (theme_node, EEK_SIDE_TOP,
&border_color);
} else {
foreground = priv->default_foreground_color;
background = priv->default_background_color;
gradient_type = EEK_GRADIENT_NONE;
border_width = priv->border_width;
border_width = (gint)round(priv->border_width);
border_radius = -1;
border_color.red = ABS(background.red - foreground.red) * 0.7;
border_color.green = ABS(background.green - foreground.green) * 0.7;
@ -248,21 +246,15 @@ render_key_outline (EekRenderer *renderer,
border_color.alpha = foreground.alpha;
}
/* need to rescale so that the border fit inside the clipping
region */
eek_element_get_bounds (EEK_ELEMENT(key), &bounds);
scale = MIN((bounds.width - border_width * 2) / bounds.width,
(bounds.height - border_width * 2) / bounds.height);
outline = eek_outline_copy (outline);
for (i = 0; i < outline->num_points; i++) {
outline->points[i].x *= priv->scale * scale;
outline->points[i].y *= priv->scale * scale;
for (guint i = 0; i < outline->num_points; i++) {
outline->points[i].x *= priv->scale;
outline->points[i].y *= priv->scale;
}
cairo_translate (cr,
border_width * priv->scale * scale,
border_width * priv->scale * scale);
border_width * priv->scale,
border_width * priv->scale);
if (gradient_type != EEK_GRADIENT_NONE) {
cairo_pattern_t *pat;
@ -340,6 +332,10 @@ render_key_outline (EekRenderer *renderer,
outline->num_points);
cairo_stroke (cr);
cairo_translate (cr,
-border_width * priv->scale,
-border_width * priv->scale);
eek_outline_free (outline);
}
@ -464,10 +460,11 @@ render_key (EekRenderer *self,
if (!outline_surface) {
cairo_t *cr;
// Outline will be drawn on the outside of the button, so the surface needs to be bigger than the button
outline_surface =
cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
bounds.width,
bounds.height);
(int)ceil(bounds.width) + 10,
(int)ceil(bounds.height) + 10);
cr = cairo_create (outline_surface);
/* blank background */
@ -640,7 +637,7 @@ eek_renderer_real_render_key_label (EekRenderer *self,
size = calculate_font_size (self, base_font, FALSE);
priv->font = pango_font_description_copy (base_font);
pango_font_description_set_size (priv->font, size);
pango_font_description_set_size (priv->font, size * 0.6);
}
eek_element_get_bounds (EEK_ELEMENT(key), &bounds);

View File

@ -45,11 +45,8 @@ enum {
};
enum {
KEY_PRESSED,
KEY_RELEASED,
KEY_LOCKED,
KEY_UNLOCKED,
KEY_CANCELLED,
LAST_SIGNAL
};
@ -114,20 +111,6 @@ eek_section_real_get_row (EekSection *self,
*orientation = row->orientation;
}
static void
on_pressed (EekKey *key,
EekSection *section)
{
g_signal_emit (section, signals[KEY_PRESSED], 0, key);
}
static void
on_released (EekKey *key,
EekSection *section)
{
g_signal_emit (section, signals[KEY_RELEASED], 0, key);
}
static void
on_locked (EekKey *key,
EekSection *section)
@ -142,18 +125,11 @@ on_unlocked (EekKey *key,
g_signal_emit (section, signals[KEY_UNLOCKED], 0, key);
}
static void
on_cancelled (EekKey *key,
EekSection *section)
{
g_signal_emit (section, signals[KEY_CANCELLED], 0, key);
}
static EekKey *
eek_section_real_create_key (EekSection *self,
guint keycode,
gint column_index,
gint row_index)
guint row_index)
{
EekKey *key;
gint num_rows;
@ -296,22 +272,16 @@ static void
eek_section_real_child_added (EekContainer *self,
EekElement *element)
{
g_signal_connect (element, "pressed", G_CALLBACK(on_pressed), self);
g_signal_connect (element, "released", G_CALLBACK(on_released), self);
g_signal_connect (element, "locked", G_CALLBACK(on_locked), self);
g_signal_connect (element, "unlocked", G_CALLBACK(on_unlocked), self);
g_signal_connect (element, "cancelled", G_CALLBACK(on_cancelled), self);
}
static void
eek_section_real_child_removed (EekContainer *self,
EekElement *element)
{
g_signal_handlers_disconnect_by_func (element, on_pressed, self);
g_signal_handlers_disconnect_by_func (element, on_released, self);
g_signal_handlers_disconnect_by_func (element, on_locked, self);
g_signal_handlers_disconnect_by_func (element, on_unlocked, self);
g_signal_handlers_disconnect_by_func (element, on_cancelled, self);
}
static void
@ -353,46 +323,6 @@ eek_section_class_init (EekSectionClass *klass)
PROP_ANGLE,
pspec);
/**
* EekSection::key-pressed:
* @section: an #EekSection
* @key: an #EekKey
*
* The ::key-pressed signal is emitted each time a key in @section
* is shifted to the pressed state.
*/
signals[KEY_PRESSED] =
g_signal_new (I_("key-pressed"),
G_TYPE_FROM_CLASS(gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(EekSectionClass, key_pressed),
NULL,
NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE,
1,
EEK_TYPE_KEY);
/**
* EekSection::key-released:
* @section: an #EekSection
* @key: an #EekKey
*
* The ::key-released signal is emitted each time a key in @section
* is shifted to the released state.
*/
signals[KEY_RELEASED] =
g_signal_new (I_("key-released"),
G_TYPE_FROM_CLASS(gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(EekSectionClass, key_released),
NULL,
NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE,
1,
EEK_TYPE_KEY);
/**
* EekSection::key-locked:
* @section: an #EekSection
@ -432,26 +362,6 @@ eek_section_class_init (EekSectionClass *klass)
G_TYPE_NONE,
1,
EEK_TYPE_KEY);
/**
* EekSection::key-cancelled:
* @section: an #EekSection
* @key: an #EekKey
*
* The ::key-cancelled signal is emitted each time a key in @section
* is shifted to the cancelled state.
*/
signals[KEY_CANCELLED] =
g_signal_new (I_("key-cancelled"),
G_TYPE_FROM_CLASS(gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(EekSectionClass, key_cancelled),
NULL,
NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE,
1,
EEK_TYPE_KEY);
}
static void
@ -569,3 +479,80 @@ eek_section_create_key (EekSection *section,
column,
row);
}
static void keysizer(EekElement *element, gpointer user_data) {
EekKey *key = EEK_KEY(element);
EekKeyboard *keyboard = EEK_KEYBOARD(user_data);
uint oref = eek_key_get_oref (key);
EekOutline *outline = eek_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 = {0};
eek_element_get_bounds(element, &key_bounds);
key_bounds.height = maxy - miny;
key_bounds.width = maxx - minx;
eek_element_set_bounds(element, &key_bounds);
}
}
struct keys_info {
uint count;
double total_width;
double biggest_height;
};
static void keycounter (EekElement *element, gpointer user_data) {
struct keys_info *data = user_data;
data->count++;
EekBounds key_bounds = {0};
eek_element_get_bounds(element, &key_bounds);
data->total_width += key_bounds.width;
if (key_bounds.height > data->biggest_height) {
data->biggest_height = key_bounds.height;
}
}
const double keyspacing = 3.0;
static void keyplacer(EekElement *element, gpointer user_data) {
double *current_offset = user_data;
EekBounds key_bounds = {0};
eek_element_get_bounds(element, &key_bounds);
key_bounds.x = *current_offset;
key_bounds.y = 0;
eek_element_set_bounds(element, &key_bounds);
*current_offset += key_bounds.width + keyspacing;
}
void eek_section_place_keys(EekSection *section, EekKeyboard *keyboard)
{
eek_container_foreach_child(EEK_CONTAINER(section), keysizer, keyboard);
struct keys_info keyinfo = {0};
eek_container_foreach_child(EEK_CONTAINER(section), keycounter, &keyinfo);
EekBounds section_bounds = {0};
eek_element_get_bounds(EEK_ELEMENT(section), &section_bounds);
double key_offset = (section_bounds.width - (keyinfo.total_width + (keyinfo.count - 1) * keyspacing)) / 2;
eek_container_foreach_child(EEK_CONTAINER(section), keyplacer, &key_offset);
section_bounds.height = keyinfo.biggest_height;
eek_element_set_bounds(EEK_ELEMENT(section), &section_bounds);
}

View File

@ -127,5 +127,7 @@ EekKey *eek_section_create_key (EekSection *section,
EekKey *eek_section_find_key_by_keycode (EekSection *section,
guint keycode);
void eek_section_place_keys (EekSection *section, EekKeyboard *keyboard);
G_END_DECLS
#endif /* EEK_SECTION_H */

View File

@ -150,6 +150,8 @@ typedef struct _EekBounds EekBounds;
typedef struct _EekOutline EekOutline;
typedef struct _EekColor EekColor;
typedef struct _EekboardContextService EekboardContextService;
/**
* EekPoint:
* @x: X coordinate of the point
@ -212,7 +214,7 @@ struct _EekOutline
/*< public >*/
gdouble corner_radius;
EekPoint *points;
gint num_points;
guint num_points;
};
GType eek_outline_get_type (void) G_GNUC_CONST;

View File

@ -31,14 +31,14 @@
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "eek-xkb-layout.h"
#include <X11/keysym.h>
#include <X11/XKBlib.h>
#include <X11/extensions/XKBgeom.h>
#include <string.h>
#include <stdarg.h>
#include <gio/gio.h>
#include "eek-xkb-layout.h"
#include "eek-keyboard.h"
#include "eek-section.h"
#include "eek-key.h"
@ -320,21 +320,24 @@ create_keyboard (EekXkbLayout *layout, EekKeyboard *keyboard)
}
static EekKeyboard *
eek_xkb_layout_real_create_keyboard (EekLayout *self,
eek_xkb_layout_real_create_keyboard (EekboardContextService *manager,
EekLayout *self,
gdouble initial_width,
gdouble initial_height)
{
EekXkbLayoutPrivate *priv = EEK_XKB_LAYOUT_GET_PRIVATE (self);
EekBounds bounds;
EekKeyboard *keyboard;
EekKeyboard *keyboard = g_object_new (EEK_TYPE_KEYBOARD, "layout", self, NULL);
keyboard->manager = manager;
keyboard = g_object_new (EEK_TYPE_KEYBOARD, "layout", self, NULL);
bounds.x = bounds.y = 0.0;
bounds.width = initial_width;
bounds.height = initial_height;
EekBounds bounds = {
.x = 0.0,
.y = 0.0,
.width = initial_width,
.height = initial_height
};
eek_element_set_bounds (EEK_ELEMENT(keyboard), &bounds);
/* resolve modifiers dynamically assigned at run time */
EekXkbLayoutPrivate *priv = EEK_XKB_LAYOUT_GET_PRIVATE (self);
eek_keyboard_set_num_lock_mask (keyboard,
XkbKeysymToModifiers (priv->display,
XK_Num_Lock));

View File

@ -25,7 +25,6 @@
#ifndef EEK_XKB_LAYOUT_H
#define EEK_XKB_LAYOUT_H 1
#include <X11/Xlib.h>
#include <X11/XKBlib.h>
#include "eek-layout.h"

View File

@ -362,9 +362,6 @@ geometry_start_element_callback (GMarkupParseContext *pcontext,
eek_element_set_bounds (EEK_ELEMENT(data->keyboard), &bounds);
else if (g_strcmp0 (data->element_stack->data, "section") == 0)
eek_element_set_bounds (EEK_ELEMENT(data->section), &bounds);
else if (g_strcmp0 (data->element_stack->data, "key") == 0)
eek_element_set_bounds (EEK_ELEMENT(data->key), &bounds);
goto out;
}
@ -504,7 +501,6 @@ geometry_end_element_callback (GMarkupParseContext *pcontext,
{
GeometryParseData *data = user_data;
GSList *head = data->element_stack;
gint i;
g_free (head->data);
data->element_stack = g_slist_next (data->element_stack);
@ -536,7 +532,8 @@ geometry_end_element_callback (GMarkupParseContext *pcontext,
outline->num_points = g_slist_length (data->points);
outline->points = g_slice_alloc0 (sizeof (EekPoint) *
outline->num_points);
for (head = data->points = g_slist_reverse (data->points), i = 0;
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));
@ -640,13 +637,13 @@ symbols_start_element_callback (GMarkupParseContext *pcontext,
data->key = eek_keyboard_find_key_by_keycode (data->keyboard,
keycode);
if (data->key == NULL) {
/*if (data->key == NULL) {
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_INVALID_CONTENT,
"no such keycode %u", keycode);
return;
}
}*/
attribute = get_attribute (attribute_names, attribute_values,
"groups");
@ -721,6 +718,10 @@ symbols_end_element_callback (GMarkupParseContext *pcontext,
text = g_strndup (data->text->str, data->text->len);
if (g_strcmp0 (element_name, "key") == 0) {
if (!data->key) {
return;
}
gint num_symbols = g_slist_length (data->symbols);
gint levels = num_symbols / data->groups;
EekSymbolMatrix *matrix = eek_symbol_matrix_new (data->groups,
@ -896,27 +897,25 @@ static const GMarkupParser prerequisites_parser = {
};
static EekKeyboard *
eek_xml_layout_real_create_keyboard (EekLayout *self,
eek_xml_layout_real_create_keyboard (EekboardContextService *manager,
EekLayout *self,
gdouble initial_width,
gdouble initial_height)
{
EekXmlLayout *layout = EEK_XML_LAYOUT (self);
EekKeyboard *keyboard;
gchar *filename, *path;
GList *loaded;
GError *error;
gboolean retval;
/* Create an empty keyboard to which geometry and symbols
information are applied. */
keyboard = g_object_new (EEK_TYPE_KEYBOARD, "layout", layout, NULL);
EekKeyboard *keyboard = g_object_new (EEK_TYPE_KEYBOARD, "layout", layout, NULL);
keyboard->manager = manager;
/* Read geometry information. */
filename = g_strdup_printf ("%s.xml", layout->priv->desc->geometry);
path = g_build_filename (layout->priv->keyboards_dir, "geometry", filename, NULL);
gchar *filename = g_strdup_printf ("%s.xml", layout->priv->desc->geometry);
gchar *path = g_build_filename (layout->priv->keyboards_dir, "geometry", filename, NULL);
g_free (filename);
error = NULL;
GError *error = NULL;
retval = parse_geometry (path, keyboard, &error);
g_free (path);
if (!retval) {
@ -929,7 +928,7 @@ eek_xml_layout_real_create_keyboard (EekLayout *self,
}
/* Read symbols information. */
loaded = NULL;
GList *loaded = NULL;
retval = parse_symbols_with_prerequisites (layout->priv->keyboards_dir,
layout->priv->desc->symbols,
keyboard,
@ -1134,6 +1133,38 @@ eek_xml_keyboard_desc_free (EekXmlKeyboardDesc *desc)
g_slice_free (EekXmlKeyboardDesc, desc);
}
struct place_data {
double desired_width;
double current_offset;
EekKeyboard *keyboard;
};
const double section_spacing = 7.0;
static void section_placer(EekElement *element, gpointer user_data) {
struct place_data *data = (struct place_data*)user_data;
EekBounds section_bounds = {0};
eek_element_get_bounds(element, &section_bounds);
section_bounds.width = data->desired_width;
eek_element_set_bounds(element, &section_bounds);
// Sections are rows now. Gather up all the keys and adjust their bounds.
eek_section_place_keys(EEK_SECTION(element), EEK_KEYBOARD(data->keyboard));
eek_element_get_bounds(element, &section_bounds);
section_bounds.y = data->current_offset;
eek_element_set_bounds(element, &section_bounds);
data->current_offset += section_bounds.height + section_spacing;
}
static void section_counter(EekElement *element, gpointer user_data) {
double *total_height = user_data;
EekBounds section_bounds = {0};
eek_element_get_bounds(element, &section_bounds);
*total_height += section_bounds.height + section_spacing;
}
static gboolean
parse_geometry (const gchar *path, EekKeyboard *keyboard, GError **error)
{
@ -1186,6 +1217,27 @@ parse_geometry (const gchar *path, EekKeyboard *keyboard, GError **error)
}
g_hash_table_destroy (oref_hash);
/* Order rows */
// This needs to be done after outlines, because outlines define key sizes
// TODO: do this only for rows without bounds
// The keyboard width is given by the user via screen size. The height will be given dynamically.
// TODO: calculate max line width beforehand for button centering. Leave keyboard centering to the renderer later
EekBounds keyboard_bounds = {0};
eek_element_get_bounds(EEK_ELEMENT(keyboard), &keyboard_bounds);
struct place_data placer_data = {
.desired_width = keyboard_bounds.width,
.current_offset = 0,
.keyboard = keyboard,
};
eek_container_foreach_child(EEK_CONTAINER(keyboard), section_placer, &placer_data);
double total_height = 0;
eek_container_foreach_child(EEK_CONTAINER(keyboard), section_counter, &total_height);
keyboard_bounds.height = total_height;
eek_element_set_bounds(EEK_ELEMENT(keyboard), &keyboard_bounds);
geometry_parse_data_free (data);
return TRUE;
}

View File

@ -1,7 +1,8 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
# Copyright (C) 2010-2011 Red Hat, Inc.
# Copyright (C) 2019 Purism, SPC
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
@ -21,13 +22,17 @@
import sys
import re
if len(sys.argv) != 2:
print >> sys.stderr, "Usage: %s TABLE-NAME" % sys.argv[0]
if len(sys.argv) > 3:
print("Usage: %s TABLE-NAME [INPUT_FILE]" % sys.argv[0], file=sys.stderr)
sys.exit(-1)
if len(sys.argv) < 3:
in_stream = sys.stdin
else:
in_stream = open(sys.argv[2])
table = dict()
for line in sys.stdin:
line = line.decode('UTF-8')
for line in in_stream:
match = re.match(r'\s*(0x[0-9A-F]+)\s+(\S*)\s+(\S*)', line, re.I)
if match:
table[int(match.group(1), 16)] = (match.group(2), match.group(3))
@ -38,7 +43,7 @@ sys.stdout.write("static const EekKeysymEntry %s[] = {\n" %
for index, (keysym, (l, c)) in enumerate([(keysym, table[keysym])
for keysym in sorted(table.keys())]):
sys.stdout.write(" { 0x%X, %s, %s }" %
(keysym, l.encode('UTF-8'), c.encode('UTF-8')))
(keysym, l, c))
if index < len(table) - 1:
sys.stdout.write(",")
sys.stdout.write("\n")

374
eek/layersurface.c Normal file
View File

@ -0,0 +1,374 @@
/*
* Copyright (C) 2018 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
* Author: Guido Günther <agx@sigxcpu.org>
*/
/*
WARNING: this file is taken directly from phosh, with no modificaions apart from this message. Please update phosh instead of changing this file. Please copy the file back here afterwards, with the same notice.
*/
#define G_LOG_DOMAIN "phosh-layer-surface"
#include "config.h"
#include "layersurface.h"
#include <gdk/gdkwayland.h>
enum {
PHOSH_LAYER_SURFACE_PROP_0,
PHOSH_LAYER_SURFACE_PROP_LAYER_SHELL,
PHOSH_LAYER_SURFACE_PROP_WL_OUTPUT,
PHOSH_LAYER_SURFACE_PROP_ANCHOR,
PHOSH_LAYER_SURFACE_PROP_LAYER,
PHOSH_LAYER_SURFACE_PROP_KBD_INTERACTIVITY,
PHOSH_LAYER_SURFACE_PROP_EXCLUSIVE_ZONE,
PHOSH_LAYER_SURFACE_PROP_LAYER_WIDTH,
PHOSH_LAYER_SURFACE_PROP_LAYER_HEIGHT,
PHOSH_LAYER_SURFACE_PROP_NAMESPACE,
PHOSH_LAYER_SURFACE_PROP_LAST_PROP
};
static GParamSpec *props[PHOSH_LAYER_SURFACE_PROP_LAST_PROP];
enum {
CONFIGURED,
N_SIGNALS
};
static guint signals [N_SIGNALS];
typedef struct {
struct wl_surface *wl_surface;
struct zwlr_layer_surface_v1 *layer_surface;
/* Properties */
guint anchor;
guint layer;
gboolean kbd_interactivity;
gint exclusive_zone;
gint width, height;
gchar *namespace;
struct zwlr_layer_shell_v1 *layer_shell;
struct wl_output *wl_output;
} PhoshLayerSurfacePrivate;
G_DEFINE_TYPE_WITH_PRIVATE (PhoshLayerSurface, phosh_layer_surface, GTK_TYPE_WINDOW)
static void layer_surface_configure(void *data,
struct zwlr_layer_surface_v1 *surface,
uint32_t serial,
uint32_t width,
uint32_t height)
{
PhoshLayerSurface *self = data;
gtk_window_resize (GTK_WINDOW (self), width, height);
zwlr_layer_surface_v1_ack_configure(surface, serial);
gtk_widget_show_all (GTK_WIDGET (self));
g_signal_emit (self, signals[CONFIGURED], 0);
}
static void layer_surface_closed (void *data,
struct zwlr_layer_surface_v1 *surface)
{
PhoshLayerSurface *self = data;
PhoshLayerSurfacePrivate *priv = phosh_layer_surface_get_instance_private (self);
g_return_if_fail (priv->layer_surface == surface);
zwlr_layer_surface_v1_destroy(priv->layer_surface);
priv->layer_surface = NULL;
gtk_widget_destroy (GTK_WIDGET (self));
}
static struct zwlr_layer_surface_v1_listener layer_surface_listener = {
.configure = layer_surface_configure,
.closed = layer_surface_closed,
};
static void
phosh_layer_surface_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PhoshLayerSurface *self = PHOSH_LAYER_SURFACE (object);
PhoshLayerSurfacePrivate *priv = phosh_layer_surface_get_instance_private (self);
switch (property_id) {
case PHOSH_LAYER_SURFACE_PROP_LAYER_SHELL:
priv->layer_shell = g_value_get_pointer (value);
break;
case PHOSH_LAYER_SURFACE_PROP_WL_OUTPUT:
priv->wl_output = g_value_get_pointer (value);
break;
case PHOSH_LAYER_SURFACE_PROP_ANCHOR:
priv->anchor = g_value_get_uint (value);
break;
case PHOSH_LAYER_SURFACE_PROP_LAYER:
priv->layer = g_value_get_uint (value);
break;
case PHOSH_LAYER_SURFACE_PROP_KBD_INTERACTIVITY:
priv->kbd_interactivity = g_value_get_boolean (value);
break;
case PHOSH_LAYER_SURFACE_PROP_EXCLUSIVE_ZONE:
priv->exclusive_zone = g_value_get_int (value);
break;
case PHOSH_LAYER_SURFACE_PROP_LAYER_WIDTH:
priv->width = g_value_get_uint (value);
break;
case PHOSH_LAYER_SURFACE_PROP_LAYER_HEIGHT:
priv->height = g_value_get_uint (value);
break;
case PHOSH_LAYER_SURFACE_PROP_NAMESPACE:
g_free (priv->namespace);
priv->namespace = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
phosh_layer_surface_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PhoshLayerSurface *self = PHOSH_LAYER_SURFACE (object);
PhoshLayerSurfacePrivate *priv = phosh_layer_surface_get_instance_private (self);
switch (property_id) {
case PHOSH_LAYER_SURFACE_PROP_LAYER_SHELL:
g_value_set_pointer (value, priv->layer_shell);
break;
case PHOSH_LAYER_SURFACE_PROP_WL_OUTPUT:
g_value_set_pointer (value, priv->wl_output);
break;
case PHOSH_LAYER_SURFACE_PROP_ANCHOR:
g_value_set_uint (value, priv->anchor);
break;
case PHOSH_LAYER_SURFACE_PROP_LAYER:
g_value_set_uint (value, priv->layer);
break;
case PHOSH_LAYER_SURFACE_PROP_KBD_INTERACTIVITY:
g_value_set_boolean (value, priv->kbd_interactivity);
break;
case PHOSH_LAYER_SURFACE_PROP_EXCLUSIVE_ZONE:
g_value_set_boolean (value, priv->exclusive_zone);
break;
case PHOSH_LAYER_SURFACE_PROP_LAYER_WIDTH:
g_value_set_uint (value, priv->width);
break;
case PHOSH_LAYER_SURFACE_PROP_LAYER_HEIGHT:
g_value_set_uint (value, priv->height);
break;
case PHOSH_LAYER_SURFACE_PROP_NAMESPACE:
g_value_set_string (value, priv->namespace);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
phosh_layer_surface_constructed (GObject *object)
{
PhoshLayerSurface *self = PHOSH_LAYER_SURFACE (object);
PhoshLayerSurfacePrivate *priv = phosh_layer_surface_get_instance_private (self);
GdkWindow *gdk_window;
G_OBJECT_CLASS (phosh_layer_surface_parent_class)->constructed (object);
gtk_window_set_decorated (GTK_WINDOW (self), FALSE);
/* Realize the window so we can get the GDK window */
gtk_widget_realize(GTK_WIDGET (self));
gdk_window = gtk_widget_get_window (GTK_WIDGET (self));
gdk_wayland_window_set_use_custom_surface (gdk_window);
priv->wl_surface = gdk_wayland_window_get_wl_surface (gdk_window);
priv->layer_surface = zwlr_layer_shell_v1_get_layer_surface(priv->layer_shell,
priv->wl_surface,
priv->wl_output,
priv->layer,
priv->namespace);
zwlr_layer_surface_v1_set_exclusive_zone(priv->layer_surface, priv->exclusive_zone);
zwlr_layer_surface_v1_set_size(priv->layer_surface, priv->width, priv->height);
zwlr_layer_surface_v1_set_anchor(priv->layer_surface, priv->anchor);
zwlr_layer_surface_v1_set_keyboard_interactivity(priv->layer_surface, priv->kbd_interactivity);
zwlr_layer_surface_v1_add_listener(priv->layer_surface,
&layer_surface_listener,
self);
wl_surface_commit(priv->wl_surface);
}
static void
phosh_layer_surface_dispose (GObject *object)
{
PhoshLayerSurface *self = PHOSH_LAYER_SURFACE (object);
PhoshLayerSurfacePrivate *priv = phosh_layer_surface_get_instance_private (self);
if (priv->layer_surface) {
zwlr_layer_surface_v1_destroy(priv->layer_surface);
priv->layer_surface = NULL;
}
g_clear_pointer (&priv->namespace, g_free);
G_OBJECT_CLASS (phosh_layer_surface_parent_class)->dispose (object);
}
static void
phosh_layer_surface_class_init (PhoshLayerSurfaceClass *klass)
{
GObjectClass *object_class = (GObjectClass *)klass;
object_class->constructed = phosh_layer_surface_constructed;
object_class->dispose = phosh_layer_surface_dispose;
object_class->set_property = phosh_layer_surface_set_property;
object_class->get_property = phosh_layer_surface_get_property;
props[PHOSH_LAYER_SURFACE_PROP_LAYER_SHELL] =
g_param_spec_pointer (
"layer-shell",
"Wayland Layer Shell Global",
"The layer shell wayland global",
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
props[PHOSH_LAYER_SURFACE_PROP_WL_OUTPUT] =
g_param_spec_pointer (
"wl-output",
"Wayland Output",
"The wl_output associated with this surface",
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
props[PHOSH_LAYER_SURFACE_PROP_ANCHOR] =
g_param_spec_uint (
"anchor",
"Anchor edges",
"The edges to anchor the surface to",
0,
G_MAXUINT,
0,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
props[PHOSH_LAYER_SURFACE_PROP_LAYER] =
g_param_spec_uint (
"layer",
"Layer",
"The layer the surface should be attached to",
0,
G_MAXUINT,
0,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
props[PHOSH_LAYER_SURFACE_PROP_KBD_INTERACTIVITY] =
g_param_spec_boolean (
"kbd-interactivity",
"Keyboard interactivity",
"Whether the surface interacts with the keyboard",
FALSE,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
props[PHOSH_LAYER_SURFACE_PROP_EXCLUSIVE_ZONE] =
g_param_spec_int (
"exclusive-zone",
"Exclusive Zone",
"Set area that is not occluded with other surfaces",
-1,
G_MAXINT,
0,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
props[PHOSH_LAYER_SURFACE_PROP_LAYER_WIDTH] =
g_param_spec_uint (
"width",
"Width",
"The width of the layer surface",
0,
G_MAXUINT,
0,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
props[PHOSH_LAYER_SURFACE_PROP_LAYER_HEIGHT] =
g_param_spec_uint (
"height",
"Height",
"The height of the layer surface",
0,
G_MAXUINT,
0,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
props[PHOSH_LAYER_SURFACE_PROP_NAMESPACE] =
g_param_spec_string (
"namespace",
"Namespace",
"Namespace of the layer surface",
"",
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, PHOSH_LAYER_SURFACE_PROP_LAST_PROP, props);
/**
* PhoshLayersurface::configured
* @self: The #PhoshLayersurface instance.
*
* This signal is emitted once we received the configure event from the
* compositor.
*/
signals[CONFIGURED] =
g_signal_new ("configured",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (PhoshLayerSurfaceClass, configured),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
}
static void
phosh_layer_surface_init (PhoshLayerSurface *self)
{
}
GtkWidget *
phosh_layer_surface_new (gpointer layer_shell,
gpointer wl_output)
{
return g_object_new (PHOSH_TYPE_LAYER_SURFACE,
"layer-shell", layer_shell,
"wl-output", wl_output);
}
struct zwlr_layer_surface_v1 *
phosh_layer_surface_get_layer_surface(PhoshLayerSurface *self)
{
PhoshLayerSurfacePrivate *priv;
g_return_val_if_fail (PHOSH_IS_LAYER_SURFACE (self), NULL);
priv = phosh_layer_surface_get_instance_private (self);
return priv->layer_surface;
}
struct wl_surface *
phosh_layer_surface_get_wl_surface(PhoshLayerSurface *self)
{
PhoshLayerSurfacePrivate *priv;
g_return_val_if_fail (PHOSH_IS_LAYER_SURFACE (self), NULL);
priv = phosh_layer_surface_get_instance_private (self);
return priv->wl_surface;
}

41
eek/layersurface.h Normal file
View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2018 Purism SPC
*
* SPDX-License-Identifier: GPL-3.0+
*/
/*
WARNING: this file is taken directly from phosh, with no modificaions apart from this message. Please update phosh instead of changing this file. Please copy the file back here afterwards, with the same notice.
*/
#pragma once
#include <gtk/gtk.h>
/* TODO: We use the enum constants from here, use glib-mkenums */
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
#define PHOSH_TYPE_LAYER_SURFACE (phosh_layer_surface_get_type ())
G_DECLARE_DERIVABLE_TYPE (PhoshLayerSurface, phosh_layer_surface, PHOSH, LAYER_SURFACE, GtkWindow)
/**
* PhoshLayerSurfaceClass
* @parent_class: The parent class
*/
struct _PhoshLayerSurfaceClass
{
GtkWindowClass parent_class;
/* Signals
*/
void (*configured) (PhoshLayerSurface *self);
};
GtkWidget *phosh_layer_surface_new (gpointer layer_shell,
gpointer wl_output);
struct zwlr_layer_surface_v1 *phosh_layer_surface_get_layer_surface(PhoshLayerSurface *self);
struct wl_surface *phosh_layer_surface_get_wl_surface(PhoshLayerSurface *self);

44
eek/meson.build Normal file
View File

@ -0,0 +1,44 @@
gnome = import('gnome')
enum_headers = [
'eek-symbol.h',
'eek-types.h',
]
enums = gnome.mkenums_simple('eek-enumtypes', sources: enum_headers)
marshalers = gnome.genmarshal(
'eek-marshalers',
sources: ['eek-marshalers.list'],
prefix: '_eek_marshal',
internal: true,
)
python = find_program('python3')
gen_keysym_entries_special = generator(
python,
arguments: ['@CURRENT_SOURCE_DIR@/gen-keysym-entries.py', 'special_keysym_entries', '@INPUT@'],
capture: true,
output: 'eek-@BASENAME@.h',
)
gen_keysym_entries_unicode = generator(
python,
arguments: ['@CURRENT_SOURCE_DIR@/gen-keysym-entries.py', 'unicode_keysym_entries', '@INPUT@'],
capture: true,
output: 'eek-@BASENAME@.h',
)
gen_keysym_entries_xkeysym = generator(
python,
arguments: ['@CURRENT_SOURCE_DIR@/gen-keysym-entries.py', 'xkeysym_keysym_entries', '@INPUT@'],
capture: true,
output: 'eek-@BASENAME@.h',
)
keysym_entries = [
gen_keysym_entries_special.process('./special-keysym-entries.txt'),
gen_keysym_entries_unicode.process('./unicode-keysym-entries.txt'),
gen_keysym_entries_xkeysym.process('./xkeysym-keysym-entries.txt'),
]

View File

@ -16,55 +16,69 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
NULL =
lib_LTLIBRARIES = libeekboard.la
libeekboard_headers = \
eekboard-service.h \
eekboard-context-service.h \
eekboard-client.h \
eekboard-context.h \
eekboard-xklutil.h
$(srcdir)/eekboard-service.h \
$(srcdir)/eekboard-context-service.h \
$(srcdir)/eekboard-client.h \
$(srcdir)/eekboard-context.h \
$(srcdir)/eekboard-xklutil.h \
$(NULL)
libeekboard_private_headers = \
eekboard-marshalers.h
$(builddir)/eekboard-marshalers.h \
$(NULL)
libeekboard_sources = \
eekboard-service.c \
eekboard-context-service.c \
eekboard-client.c \
eekboard-context.c \
eekboard-xklutil.c
$(srcdir)/eekboard-service.c \
$(srcdir)/eekboard-context-service.c \
$(srcdir)/eekboard-client.c \
$(srcdir)/eekboard-context.c \
$(srcdir)/eekboard-xklutil.c \
$(NULL)
libeekboard_marshalers_sources = \
eekboard-marshalers.c \
eekboard-marshalers.h
$(builddir)/eekboard-marshalers.c \
$(builddir)/eekboard-marshalers.h \
$(NULL)
BUILT_SOURCES = \
$(libeekboard_marshalers_sources)
$(libeekboard_marshalers_sources) \
$(NULL)
libeekboard_la_SOURCES = \
$(libeekboard_sources) \
eekboard-marshalers.c
$(builddir)/eekboard-marshalers.c \
$(NULL)
libeekboard_la_CFLAGS = \
-DEEKBOARD_COMPILATION=1 \
-DKEYBOARDDIR=\"$(pkgdatadir)/keyboards\" \
-I$(top_srcdir) \
$(GIO2_CFLAGS) \
$(LIBXKLAVIER_CFLAGS)
$(LIBXKLAVIER_CFLAGS) \
$(NULL)
libeekboard_la_LIBADD = \
$(top_builddir)/eek/libeek.la \
$(top_builddir)/eek/libeek-xkl.la \
$(GIO2_LIBS) \
$(LIBXKLAVIER_LIBS)
$(LIBXKLAVIER_LIBS) \
$(NULL)
eekboarddir = $(includedir)/eekboard-$(EEK_API_VERSION)/eekboard
eekboard_HEADERS = $(libeekboard_headers)
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = \
eekboard-$(EEK_API_VERSION).pc
eekboard-$(EEK_API_VERSION).pc \
$(NULL)
DISTCLEANFILES = \
$(BUILT_SOURCES) \
$(pkgconfig_DATA)
$(pkgconfig_DATA) \
$(NULL)
CLEANFILES =
@ -72,22 +86,37 @@ EXTRA_DIST = eekboard-marshalers.list
# gen marshal
eekboard-marshalers.h: eekboard-marshalers.list
$(AM_V_GEN) $(GLIB_GENMARSHAL) --prefix=_eekboard_marshal $(srcdir)/eekboard-marshalers.list --header --internal > $@.tmp && \
$(AM_V_GEN) $(GLIB_GENMARSHAL) \
--prefix=_eekboard_marshal \
$(srcdir)/eekboard-marshalers.list --header --internal \
> $@.tmp && \
mv $@.tmp $@
eekboard-marshalers.c: eekboard-marshalers.list eekboard-marshalers.h
$(AM_V_GEN) (echo "#include \"eekboard-marshalers.h\""; \
$(GLIB_GENMARSHAL) --prefix=_eekboard_marshal $(srcdir)/eekboard-marshalers.list --body --internal) > $@.tmp && \
$(GLIB_GENMARSHAL) \
--prefix=_eekboard_marshal \
$(srcdir)/eekboard-marshalers.list --body --internal) \
> $@.tmp && \
mv $@.tmp $@
-include $(INTROSPECTION_MAKEFILE)
INTROSPECTION_GIRS =
INTROSPECTION_SCANNER_ARGS = --add-include-path=$(builddir) --add-include-path=$(top_builddir)/eek
INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir) --includedir=$(top_srcdir)/eek
INTROSPECTION_SCANNER_ARGS = \
--add-include-path=$(builddir) \
--add-include-path=$(top_builddir)/eek \
$(NULL)
INTROSPECTION_COMPILER_ARGS = \
--includedir=$(builddir) \
--includedir=$(top_builddir)/eek \
$(NULL)
if HAVE_INTROSPECTION
Eekboard@EEK_LIBRARY_SUFFIX@.gir: libeekboard.la
Eekboard@EEK_LIBRARY_SUFFIX_U@_gir_SCANNERFLAGS = --strip-prefix=Eekboard
Eekboard@EEK_LIBRARY_SUFFIX_U@_gir_SCANNERFLAGS = \
--identifier-prefix=Eekboard \
--symbol-prefix=eekboard \
$(NULL)
Eekboard@EEK_LIBRARY_SUFFIX_U@_gir_INCLUDES = Eek@EEK_LIBRARY_SUFFIX@
Eekboard@EEK_LIBRARY_SUFFIX_U@_gir_CFLAGS = $(libeekboard_la_CFLAGS)
Eekboard@EEK_LIBRARY_SUFFIX_U@_gir_LIBS = libeekboard.la

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,8 @@
#include <eek/eek.h>
#include "virtual-keyboard-unstable-v1-client-protocol.h"
G_BEGIN_DECLS
#define EEKBOARD_CONTEXT_SERVICE_PATH "/org/fedorahosted/Eekboard/Context_%d"
@ -36,13 +38,16 @@ G_BEGIN_DECLS
#define EEKBOARD_IS_CONTEXT_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EEKBOARD_TYPE_CONTEXT_SERVICE))
#define EEKBOARD_CONTEXT_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EEKBOARD_TYPE_CONTEXT_SERVICE, EekboardContextServiceClass))
typedef struct _EekboardContextService EekboardContextService;
typedef struct _EekboardContextServiceClass EekboardContextServiceClass;
typedef struct _EekboardContextServicePrivate EekboardContextServicePrivate;
/**
* EekboardContextService:
*
* TODO: Restrict to managing keyboard layouts, and maybe button repeats,
* and the virtual keyboard protocol.
*
* The #EekboardContextService structure contains only private data
* and should only be accessed using the provided API.
*/
@ -50,6 +55,8 @@ struct _EekboardContextService {
GObject parent;
EekboardContextServicePrivate *priv;
struct zwp_virtual_keyboard_v1 *virtual_keyboard;
};
/**
@ -93,8 +100,9 @@ EekKeyboard *eekboard_context_service_get_keyboard
(EekboardContextService *context);
gboolean eekboard_context_service_get_fullscreen
(EekboardContextService *context);
const gchar * eekboard_context_service_get_client_name
(EekboardContextService *context);
void eekboard_context_service_set_keymap(EekboardContextService *context,
const EekKeyboard *keyboard);
G_END_DECLS
#endif /* EEKBOARD_CONTEXT_SERVICE_H */

View File

@ -29,7 +29,7 @@
#endif /* HAVE_CONFIG_H */
#include "eekboard/eekboard-context.h"
#include "eekboard/eekboard-marshalers.h"
//#include "eekboard/eekboard-marshalers.h"
#define I_(string) g_intern_static_string (string)
@ -151,7 +151,7 @@ eekboard_context_real_destroyed (EekboardContext *self)
static void
eekboard_context_real_key_activated (EekboardContext *self,
const gchar *keyname,
guint keycode,
EekSymbol *symbol,
guint modifiers)
{
@ -251,6 +251,7 @@ eekboard_context_class_init (EekboardContextClass *klass)
* The ::key-activated signal is emitted each time a key is
* pressed in @context.
*/
/*
signals[KEY_ACTIVATED] =
g_signal_new (I_("key-activated"),
G_TYPE_FROM_CLASS(gobject_class),
@ -264,7 +265,7 @@ eekboard_context_class_init (EekboardContextClass *klass)
G_TYPE_UINT,
G_TYPE_OBJECT,
G_TYPE_UINT);
*/
/**
* EekboardContext::destroyed:
* @context: an #EekboardContext

View File

@ -1 +0,0 @@
VOID:UINT,OBJECT,UINT

View File

@ -1,7 +1,7 @@
/*
/*
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010-2011 Red Hat, Inc.
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
@ -18,7 +18,9 @@
/**
* SECTION:eekboard-service
* @short_description: base server implementation of eekboard service
* @short_description: base implementation of eekboard service
*
* Provides a dbus object, and contains the context.
*
* The #EekboardService class provides a base server side
* implementation of eekboard service.
@ -30,6 +32,10 @@
#include "eekboard/eekboard-service.h"
#include "sm.puri.OSK0.h"
#include <stdio.h>
enum {
PROP_0,
PROP_OBJECT_PATH,
@ -49,51 +55,16 @@ static guint signals[LAST_SIGNAL] = { 0, };
struct _EekboardServicePrivate {
GDBusConnection *connection;
SmPuriOSK0 *dbus_interface;
GDBusNodeInfo *introspection_data;
guint registration_id;
char *object_path;
GHashTable *context_hash;
GSList *context_stack;
gboolean visible;
EekboardContextService *context; // unowned reference
};
G_DEFINE_TYPE (EekboardService, eekboard_service, G_TYPE_OBJECT);
static const gchar introspection_xml[] =
"<node>"
" <interface name='org.fedorahosted.Eekboard'>"
" <method name='CreateContext'>"
" <arg direction='in' type='s' name='client_name'/>"
" <arg direction='out' type='s' name='object_path'/>"
" </method>"
" <method name='PushContext'>"
" <arg direction='in' type='s' name='object_path'/>"
" </method>"
" <method name='PopContext'/>"
" <method name='ShowKeyboard'/>"
" <method name='HideKeyboard'/>"
" <method name='Destroy'/>"
/* signals */
" </interface>"
"</node>";
static void handle_method_call (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data);
static const GDBusInterfaceVTable interface_vtable =
{
handle_method_call,
NULL,
NULL
};
static void
eekboard_service_set_property (GObject *object,
guint prop_id,
@ -146,19 +117,6 @@ static void
eekboard_service_dispose (GObject *object)
{
EekboardService *service = EEKBOARD_SERVICE(object);
GSList *head;
if (service->priv->context_hash) {
g_hash_table_destroy (service->priv->context_hash);
service->priv->context_hash = NULL;
}
for (head = service->priv->context_stack; head; head = service->priv->context_stack) {
g_object_unref (head->data);
service->priv->context_stack = g_slist_next (head);
g_slist_free1 (head);
}
if (service->priv->connection) {
if (service->priv->registration_id > 0) {
g_dbus_connection_unregister_object (service->priv->connection,
@ -188,26 +146,40 @@ eekboard_service_finalize (GObject *object)
G_OBJECT_CLASS (eekboard_service_parent_class)->finalize (object);
}
static gboolean
handle_set_visible(SmPuriOSK0 *object, GDBusMethodInvocation *invocation,
gboolean arg_visible, gpointer user_data) {
EekboardService *service = user_data;
if (service->priv->context) {
if (arg_visible) {
eekboard_context_service_show_keyboard (service->priv->context);
} else {
eekboard_context_service_hide_keyboard (service->priv->context);
}
}
sm_puri_osk0_complete_set_visible(object, invocation);
return TRUE;
}
static void
eekboard_service_constructed (GObject *object)
{
EekboardService *service = EEKBOARD_SERVICE(object);
service->priv->dbus_interface = sm_puri_osk0_skeleton_new();
sm_puri_osk0_set_visible(service->priv->dbus_interface, FALSE); // TODO: use actual value
g_signal_connect(service->priv->dbus_interface, "handle-set-visible",
G_CALLBACK(handle_set_visible), service);
if (service->priv->connection && service->priv->object_path) {
GError *error = NULL;
service->priv->registration_id = g_dbus_connection_register_object
(service->priv->connection,
service->priv->object_path,
service->priv->introspection_data->interfaces[0],
&interface_vtable,
object,
NULL,
&error);
if (service->priv->registration_id == 0) {
g_warning ("failed to register context object: %s",
error->message);
g_error_free (error);
if (!g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(service->priv->dbus_interface),
service->priv->connection,
service->priv->object_path,
&error)) {
g_warning("Error registering dbus object: %s\n", error->message);
g_clear_error(&error);
}
}
}
@ -278,237 +250,14 @@ eekboard_service_class_init (EekboardServiceClass *klass)
static void
eekboard_service_init (EekboardService *self)
{
GError *error;
self->priv = EEKBOARD_SERVICE_GET_PRIVATE(self);
error = NULL;
self->priv->introspection_data =
g_dbus_node_info_new_for_xml (introspection_xml, &error);
if (self->priv->introspection_data == NULL) {
g_warning ("failed to parse D-Bus XML: %s", error->message);
g_error_free (error);
g_assert_not_reached ();
}
self->priv->context_hash =
g_hash_table_new_full (g_str_hash,
g_str_equal,
(GDestroyNotify)g_free,
(GDestroyNotify)g_object_unref);
}
static void
remove_context_from_stack (EekboardService *service,
EekboardContextService *context)
{
GSList *head;
head = g_slist_find (service->priv->context_stack, context);
if (head) {
service->priv->context_stack = g_slist_remove_link (service->priv->context_stack, head);
g_object_unref (head->data);
g_slist_free1 (head);
}
if (service->priv->context_stack)
eekboard_context_service_enable (service->priv->context_stack->data);
}
static void
service_name_vanished_callback (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
EekboardService *service = user_data;
GSList *head;
GHashTableIter iter;
gpointer k, v;
g_hash_table_iter_init (&iter, service->priv->context_hash);
while (g_hash_table_iter_next (&iter, &k, &v)) {
const gchar *owner = g_object_get_data (G_OBJECT(v), "owner");
if (g_strcmp0 (owner, name) == 0)
g_hash_table_iter_remove (&iter);
}
for (head = service->priv->context_stack; head; ) {
const gchar *owner = g_object_get_data (G_OBJECT(head->data), "owner");
GSList *next = g_slist_next (head);
if (g_strcmp0 (owner, name) == 0) {
service->priv->context_stack =
g_slist_remove_link (service->priv->context_stack, head);
g_object_unref (head->data);
g_slist_free1 (head);
}
head = next;
}
if (service->priv->context_stack)
eekboard_context_service_enable (service->priv->context_stack->data);
}
static void
context_destroyed_cb (EekboardContextService *context, EekboardService *service)
{
gchar *object_path = NULL;
remove_context_from_stack (service, context);
g_object_get (G_OBJECT(context), "object-path", &object_path, NULL);
g_hash_table_remove (service->priv->context_hash, object_path);
g_free (object_path);
}
static void
on_notify_visible (GObject *object, GParamSpec *spec, gpointer user_data)
{
EekboardService *service = user_data;
g_object_get (object, "visible", &service->priv->visible, NULL);
}
static void
handle_method_call (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
EekboardService *service = user_data;
EekboardServiceClass *klass = EEKBOARD_SERVICE_GET_CLASS(service);
if (g_strcmp0 (method_name, "CreateContext") == 0) {
const gchar *client_name;
gchar *object_path;
static gint context_id = 0;
EekboardContextService *context;
g_variant_get (parameters, "(&s)", &client_name);
object_path = g_strdup_printf (EEKBOARD_CONTEXT_SERVICE_PATH, context_id++);
g_assert (klass->create_context);
context = klass->create_context (service, client_name, object_path);
g_object_set_data_full (G_OBJECT(context),
"owner", g_strdup (sender),
(GDestroyNotify)g_free);
g_hash_table_insert (service->priv->context_hash,
object_path,
context);
/* the vanished callback is called when clients are disconnected */
g_bus_watch_name_on_connection (service->priv->connection,
sender,
G_BUS_NAME_WATCHER_FLAGS_NONE,
NULL,
service_name_vanished_callback,
service,
NULL);
g_signal_connect (G_OBJECT(context), "destroyed",
G_CALLBACK(context_destroyed_cb), service);
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(s)",
object_path));
return;
}
if (g_strcmp0 (method_name, "PushContext") == 0) {
const gchar *object_path;
EekboardContextService *context;
g_variant_get (parameters, "(&s)", &object_path);
context = g_hash_table_lookup (service->priv->context_hash, object_path);
if (!context) {
g_dbus_method_invocation_return_error (invocation,
G_IO_ERROR,
G_IO_ERROR_FAILED_HANDLED,
"context not found");
return;
}
if (service->priv->context_stack)
eekboard_context_service_disable (service->priv->context_stack->data);
service->priv->context_stack = g_slist_prepend (service->priv->context_stack,
g_object_ref (context));
eekboard_context_service_enable (context);
g_signal_connect (context, "notify::visible",
G_CALLBACK(on_notify_visible), service);
if (service->priv->visible)
eekboard_context_service_show_keyboard (context);
g_dbus_method_invocation_return_value (invocation, NULL);
return;
}
if (g_strcmp0 (method_name, "PopContext") == 0) {
if (service->priv->context_stack) {
EekboardContextService *context = service->priv->context_stack->data;
gchar *object_path;
const gchar *owner;
g_object_get (G_OBJECT(context), "object-path", &object_path, NULL);
owner = g_object_get_data (G_OBJECT(context), "owner");
if (g_strcmp0 (owner, sender) != 0) {
g_dbus_method_invocation_return_error
(invocation,
G_IO_ERROR,
G_IO_ERROR_FAILED_HANDLED,
"context at %s not owned by %s",
object_path, sender);
return;
}
g_free (object_path);
g_signal_handlers_disconnect_by_func (context,
G_CALLBACK(on_notify_visible),
service);
eekboard_context_service_disable (context);
service->priv->context_stack = g_slist_next (service->priv->context_stack);
if (service->priv->context_stack)
eekboard_context_service_enable (service->priv->context_stack->data);
}
g_dbus_method_invocation_return_value (invocation, NULL);
return;
}
if (g_strcmp0 (method_name, "ShowKeyboard") == 0) {
if (service->priv->context_stack) {
eekboard_context_service_show_keyboard (service->priv->context_stack->data);
} else {
service->priv->visible = TRUE;
}
return;
}
if (g_strcmp0 (method_name, "HideKeyboard") == 0) {
if (service->priv->context_stack) {
eekboard_context_service_hide_keyboard (service->priv->context_stack->data);
} else {
service->priv->visible = FALSE;
}
return;
}
if (g_strcmp0 (method_name, "Destroy") == 0) {
g_signal_emit (service, signals[DESTROYED], 0);
g_dbus_method_invocation_return_value (invocation, NULL);
return;
}
g_return_if_reached ();
self->priv->context = NULL;
}
/**
* eekboard_service_new:
* @connection: a #GDBusConnection
* @object_path: object path
*
* Create an empty server for testing purpose.
*/
EekboardService *
eekboard_service_new (GDBusConnection *connection,
@ -519,3 +268,10 @@ eekboard_service_new (GDBusConnection *connection,
"connection", connection,
NULL);
}
void
eekboard_service_set_context(EekboardService *service,
EekboardContextService *context) {
service->priv->context = context;
}

View File

@ -24,8 +24,8 @@
G_BEGIN_DECLS
#define EEKBOARD_SERVICE_PATH "/org/fedorahosted/Eekboard"
#define EEKBOARD_SERVICE_INTERFACE "org.fedorahosted.Eekboard"
#define EEKBOARD_SERVICE_PATH "/sm/puri/OSK0"
#define EEKBOARD_SERVICE_INTERFACE "sm.puri.OSK0"
#define EEKBOARD_TYPE_SERVICE (eekboard_service_get_type())
#define EEKBOARD_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEKBOARD_TYPE_SERVICE, EekboardService))
@ -41,6 +41,8 @@ typedef struct _EekboardServicePrivate EekboardServicePrivate;
/**
* EekboardService:
*
* Manages DBus interaction.
*
* The #EekboardService structure contains only private data and
* should only be accessed using the provided API.
*/
@ -60,9 +62,7 @@ struct _EekboardServiceClass {
GObjectClass parent_class;
/*< public >*/
EekboardContextService *(*create_context) (EekboardService *self,
const gchar *client_name,
const gchar *object_path);
EekboardContextService *(*create_context) (EekboardService *self);
/*< private >*/
/* padding */
@ -72,6 +72,7 @@ struct _EekboardServiceClass {
GType eekboard_service_get_type (void) G_GNUC_CONST;
EekboardService * eekboard_service_new (GDBusConnection *connection,
const gchar *object_path);
void eekboard_service_set_context(EekboardService *service,
EekboardContextService *context);
G_END_DECLS
#endif /* EEKBOARD_SERVICE_H */

327
eekboard/key-emitter.c Normal file
View File

@ -0,0 +1,327 @@
/*
* Copyright (C) 2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2011 Red Hat, Inc.
* Copyright (C) 2019 Purism, SPC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* This file is responsible for managing keycode data and emitting keycodes. */
#include "eekboard/key-emitter.h"
#include "eekboard/keymap.h"
#include <gdk/gdk.h>
#include "eekboard/eekboard-context-service.h"
// TODO: decide whether it's this struct that carries the keyboard around in key-emitter or if the whole manager should be dragged around
// if this is the carrier, then it should be made part of the manager
// hint: check which fields need to be persisted between keypresses; which between keyboards
typedef struct {
struct zwp_virtual_keyboard_v1 *virtual_keyboard; // unowned copy
struct xkb_keymap *keymap; // unowned copy
XkbDescRec *xkb;
guint modifier_keycodes[8];
guint modifier_indices[MOD_IDX_LAST];
guint group;
} SeatEmitter;
/* The following functions for keyboard mapping change are direct
translation of the code in Caribou (in libcaribou/xadapter.vala):
- get_replaced_keycode (Caribou: get_reserved_keycode)
- replace_keycode
- get_keycode_from_gdk_keymap (Caribou: best_keycode_keyval_match)
*/
/* Find an unused keycode where a keysym can be assigned. Restricted to Level 1 */
static guint
get_replaced_keycode (SeatEmitter *client)
{
guint keycode;
return 0; // FIXME: no xkb allocated yet
for (keycode = client->xkb->max_key_code;
keycode >= client->xkb->min_key_code;
--keycode) {
guint offset = client->xkb->map->key_sym_map[keycode].offset;
if (client->xkb->map->key_sym_map[keycode].kt_index[0] == XkbOneLevelIndex &&
client->xkb->map->syms[offset] != NoSymbol) {
return keycode;
}
}
return 0;
}
/* Replace keysym assigned to KEYCODE to KEYSYM. Both args are used
as in-out. If KEYCODE points to 0, this function picks a keycode
from the current map and replace the associated keysym to KEYSYM.
In that case, the replaced keycode is stored in KEYCODE and the old
keysym is stored in KEYSYM. If otherwise (KEYCODE points to
non-zero keycode), it simply changes the current map with the
specified KEYCODE and KEYSYM. */
static gboolean
replace_keycode (SeatEmitter *emitter,
guint keycode,
guint *keysym)
{
GdkDisplay *display = gdk_display_get_default ();
//Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
guint old_keysym;
int keysyms_per_keycode;
KeySym *syms;
return TRUE; // FIXME: no xkb allocated at the moment, pretending all is fine
g_return_val_if_fail (emitter->xkb->min_key_code <= keycode &&
keycode <= emitter->xkb->max_key_code,
FALSE);
g_return_val_if_fail (keysym != NULL, FALSE);
/*
* Update keyboard mapping. Wayland receives keyboard mapping as a string, so XChangeKeyboardMapping needs to translate from the symbol tbale t the string. TODO.
*
syms = XGetKeyboardMapping (xdisplay, keycode, 1, &keysyms_per_keycode);
old_keysym = syms[0];
syms[0] = *keysym;
XChangeKeyboardMapping (xdisplay, keycode, 1, syms, 1);
XSync (xdisplay, False);
XFree (syms);
*keysym = old_keysym;
*/
return TRUE;
}
static gboolean
get_keycode_from_gdk_keymap (SeatEmitter *emitter,
guint keysym,
guint *keycode,
guint *modifiers)
{
GdkKeymapKey *keys, *best_match = NULL;
guint n_keys, i;
if (!squeek_keymap_get_entries_for_keyval (emitter->keymap, keysym, &keys, &n_keys))
return FALSE;
for (i = 0; i < n_keys; i++)
if ((guint)keys[i].group == emitter->group)
best_match = &keys[i];
if (!best_match) {
g_free (keys);
return FALSE;
}
*keycode = best_match->keycode;
*modifiers = best_match->level == 1 ? EEK_SHIFT_MASK : 0;
g_free (keys);
return TRUE;
}
int send_virtual_keyboard_key(
struct zwp_virtual_keyboard_v1 *keyboard,
unsigned int keycode,
unsigned is_press,
uint32_t timestamp
) {
zwp_virtual_keyboard_v1_key(keyboard, timestamp, keycode, (unsigned)is_press);
return 0;
}
static void
send_fake_modifiers_events (SeatEmitter *emitter,
EekModifierType modifiers,
uint32_t timestamp)
{
(void)timestamp;
uint32_t proto_modifiers = 0;
if (modifiers & EEK_SHIFT_MASK) {
proto_modifiers |= 1<<MOD_IDX_SHIFT;
}
if (modifiers & EEK_CONTROL_MASK) {
proto_modifiers |= 1<<MOD_IDX_CTRL;
}
if (modifiers & EEK_MOD1_MASK) {
proto_modifiers |= 1<<MOD_IDX_ALT;
}
zwp_virtual_keyboard_v1_modifiers(emitter->virtual_keyboard, proto_modifiers, 0, 0, emitter->group);
}
static void
send_fake_key_event (SeatEmitter *emitter,
guint xkeysym,
guint keyboard_modifiers,
gboolean pressed,
uint32_t timestamp)
{
EekModifierType modifiers;
guint old_keysym = xkeysym;
g_return_if_fail (xkeysym > 0);
guint keycode;
if (!get_keycode_from_gdk_keymap (emitter, xkeysym, &keycode, &modifiers)) {
keycode = get_replaced_keycode (emitter);
if (keycode == 0) {
g_warning ("no available keycode to replace");
return;
}
if (!replace_keycode (emitter, keycode, &old_keysym)) {
g_warning ("failed to lookup X keysym %X", xkeysym);
return;
}
}
/* Clear level shift modifiers */
keyboard_modifiers &= (unsigned)~EEK_SHIFT_MASK;
keyboard_modifiers &= (unsigned)~EEK_LOCK_MASK;
/* FIXME: may need to remap ISO_Level3_Shift and NumLock */
modifiers |= keyboard_modifiers;
send_fake_modifiers_events (emitter, modifiers, timestamp);
// There's something magical about subtracting/adding 8 to keycodes for some reason
send_virtual_keyboard_key (emitter->virtual_keyboard, keycode - 8, (unsigned)pressed, timestamp);
send_fake_modifiers_events (emitter, modifiers, timestamp);
if (old_keysym != xkeysym)
replace_keycode (emitter, keycode, &old_keysym);
}
static void
send_fake_key_events (SeatEmitter *emitter,
EekSymbol *symbol,
EekModifierType keyboard_modifiers,
gboolean pressed,
uint32_t timestamp)
{
/* Ignore modifier keys */
if (eek_symbol_is_modifier (symbol))
return;
/* If symbol is a text, convert chars in it to keysym */
if (EEK_IS_TEXT(symbol)) {
const gchar *utf8 = eek_text_get_text (EEK_TEXT(symbol));
printf("Attempting to send text %s\n", utf8);
/* FIXME:
glong items_written;
gunichar *ucs4 = g_utf8_to_ucs4_fast (utf8, -1, &items_written);
gint i;
for (i = 0; i < items_written; i++) {
guint xkeysym;
EekKeysym *keysym;
gchar *name;
name = g_strdup_printf ("U%04X", ucs4[i]);
xkeysym = XStringToKeysym (name); // TODO: use xkb_get_keysym_from_name
g_free (name);
keysym = eek_keysym_new (xkeysym);
send_fake_key_events (client,
EEK_SYMBOL(keysym),
keyboard_modifiers);
}
g_free (ucs4);
*/
return;
}
if (EEK_IS_KEYSYM(symbol)) {
guint xkeysym = eek_keysym_get_xkeysym (EEK_KEYSYM(symbol));
send_fake_key_event (emitter, xkeysym, keyboard_modifiers, pressed, timestamp);
}
}
/* Finds the first key code for each modifier and saves it in modifier_keycodes */
static void
update_modifier_info (SeatEmitter *client)
{
client->modifier_indices[MOD_IDX_SHIFT] = xkb_keymap_mod_get_index(client->keymap, XKB_MOD_NAME_SHIFT);
client->modifier_indices[MOD_IDX_CAPS] = xkb_keymap_mod_get_index(client->keymap, XKB_MOD_NAME_CAPS);
client->modifier_indices[MOD_IDX_CTRL] = xkb_keymap_mod_get_index(client->keymap, XKB_MOD_NAME_CTRL);
client->modifier_indices[MOD_IDX_ALT] = xkb_keymap_mod_get_index(client->keymap, XKB_MOD_NAME_ALT);
client->modifier_indices[MOD_IDX_NUM] = xkb_keymap_mod_get_index(client->keymap, XKB_MOD_NAME_NUM);
client->modifier_indices[MOD_IDX_MOD3] = xkb_keymap_mod_get_index(client->keymap, "Mod3");
client->modifier_indices[MOD_IDX_LOGO] = xkb_keymap_mod_get_index(client->keymap, XKB_MOD_NAME_LOGO);
client->modifier_indices[MOD_IDX_ALTGR] = xkb_keymap_mod_get_index(client->keymap, "Mod5");
client->modifier_indices[MOD_IDX_NUMLK] = xkb_keymap_mod_get_index(client->keymap, "NumLock");
client->modifier_indices[MOD_IDX_ALSO_ALT] = xkb_keymap_mod_get_index(client->keymap, "Alt");
client->modifier_indices[MOD_IDX_LVL3] = xkb_keymap_mod_get_index(client->keymap, "LevelThree");
client->modifier_indices[MOD_IDX_LALT] = xkb_keymap_mod_get_index(client->keymap, "LAlt");
client->modifier_indices[MOD_IDX_RALT] = xkb_keymap_mod_get_index(client->keymap, "RAlt");
client->modifier_indices[MOD_IDX_RCONTROL] = xkb_keymap_mod_get_index(client->keymap, "RControl");
client->modifier_indices[MOD_IDX_LCONTROL] = xkb_keymap_mod_get_index(client->keymap, "LControl");
client->modifier_indices[MOD_IDX_SCROLLLK] = xkb_keymap_mod_get_index(client->keymap, "ScrollLock");
client->modifier_indices[MOD_IDX_LVL5] = xkb_keymap_mod_get_index(client->keymap, "LevelFive");
client->modifier_indices[MOD_IDX_ALSO_ALTGR] = xkb_keymap_mod_get_index(client->keymap, "AltGr");
client->modifier_indices[MOD_IDX_META] = xkb_keymap_mod_get_index(client->keymap, "Meta");
client->modifier_indices[MOD_IDX_SUPER] = xkb_keymap_mod_get_index(client->keymap, "Super");
client->modifier_indices[MOD_IDX_HYPER] = xkb_keymap_mod_get_index(client->keymap, "Hyper");
/*
for (xkb_mod_index_t i = 0;
i < xkb_keymap_num_mods(client->keymap);
i++) {
g_log("squeek", G_LOG_LEVEL_DEBUG, "%s", xkb_keymap_mod_get_name(client->keymap, i));
}*/
}
void
emit_key_activated (EekboardContextService *manager,
EekKeyboard *keyboard,
guint keycode,
EekSymbol *symbol,
EekModifierType modifiers,
gboolean pressed,
uint32_t timestamp)
{
/* FIXME: figure out how to deal with Client after key presses go through
if (g_strcmp0 (eek_symbol_get_name (symbol), "cycle-keyboard") == 0) {
client->keyboards_head = g_slist_next (client->keyboards_head);
if (client->keyboards_head == NULL)
client->keyboards_head = client->keyboards;
eekboard_context_set_keyboard (client->context,
GPOINTER_TO_UINT(client->keyboards_head->data),
NULL);
return;
}
if (g_strcmp0 (eek_symbol_get_name (symbol), "preferences") == 0) {
gchar *argv[2];
GError *error;
argv[0] = g_build_filename (LIBEXECDIR, "eekboard-setup", NULL);
argv[1] = NULL;
error = NULL;
if (!g_spawn_async (NULL, argv, NULL, 0, NULL, NULL, NULL, &error)) {
g_warning ("can't spawn %s: %s", argv[0], error->message);
g_error_free (error);
}
g_free (argv[0]);
return;
}
*/
SeatEmitter emitter = {0};
emitter.virtual_keyboard = manager->virtual_keyboard;
emitter.keymap = keyboard->keymap;
update_modifier_info (&emitter);
send_fake_key_events (&emitter, symbol, modifiers, pressed, timestamp);
}

48
eekboard/key-emitter.h Normal file
View File

@ -0,0 +1,48 @@
#ifndef KEYEMITTER_H
#define KEYEMITTER_H
#include <inttypes.h>
#include <glib.h>
#include <X11/XKBlib.h>
#include "eek/eek.h"
#include "virtual-keyboard-unstable-v1-client-protocol.h"
/// Indices obtained by xkb_keymap_mod_get_name
enum mod_indices {
MOD_IDX_SHIFT,
MOD_IDX_CAPS,
MOD_IDX_CTRL,
MOD_IDX_ALT,
MOD_IDX_NUM,
MOD_IDX_MOD3,
MOD_IDX_LOGO,
MOD_IDX_ALTGR,
MOD_IDX_NUMLK, // Caution, not sure which is the right one
MOD_IDX_ALSO_ALT, // Not sure why, alt emits the first alt on my setup
MOD_IDX_LVL3,
// Not sure if the next 4 are used at all
MOD_IDX_LALT,
MOD_IDX_RALT,
MOD_IDX_RCONTROL,
MOD_IDX_LCONTROL,
MOD_IDX_SCROLLLK,
MOD_IDX_LVL5,
MOD_IDX_ALSO_ALTGR, // Not used on my layout
MOD_IDX_META,
MOD_IDX_SUPER,
MOD_IDX_HYPER,
MOD_IDX_LAST,
};
void
emit_key_activated (EekboardContextService *manager, EekKeyboard *keyboard,
guint keycode,
EekSymbol *symbol,
guint modifiers,
gboolean pressed, uint32_t timestamp);
#endif // KEYEMITTER_H

66
eekboard/keymap.c Normal file
View File

@ -0,0 +1,66 @@
/* GDK - The GIMP Drawing Kit
* Copyright (C) 2000 Red Hat, Inc.
* Copyright (C) 2019 Purism, SPC
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Modified for squeekboard based on GTK
*/
#include "keymap.h"
gboolean
squeek_keymap_get_entries_for_keyval (struct xkb_keymap *xkb_keymap,
guint keyval,
GdkKeymapKey **keys,
guint *n_keys)
{
GArray *retval;
guint keycode;
xkb_keycode_t min_keycode, max_keycode;
retval = g_array_new (FALSE, FALSE, sizeof (GdkKeymapKey));
min_keycode = xkb_keymap_min_keycode (xkb_keymap);
max_keycode = xkb_keymap_max_keycode (xkb_keymap);
for (keycode = min_keycode; keycode < max_keycode; keycode++)
{
xkb_layout_index_t num_layouts, layout;
num_layouts = xkb_keymap_num_layouts_for_key (xkb_keymap, keycode);
for (layout = 0; layout < num_layouts; layout++)
{
xkb_layout_index_t num_levels, level;
num_levels = xkb_keymap_num_levels_for_key (xkb_keymap, keycode, layout);
for (level = 0; level < num_levels; level++)
{
const xkb_keysym_t *syms;
gint num_syms, sym;
num_syms = xkb_keymap_key_get_syms_by_level (xkb_keymap, keycode, layout, level, &syms);
for (sym = 0; sym < num_syms; sym++)
{
if (syms[sym] == keyval)
{
GdkKeymapKey key;
key.keycode = keycode;
key.group = (gint)layout;
key.level = (gint)level;
g_array_append_val (retval, key);
}
}
}
}
}
*n_keys = retval->len;
*keys = (GdkKeymapKey*) g_array_free (retval, FALSE);
return TRUE;
}

8
eekboard/keymap.h Normal file
View File

@ -0,0 +1,8 @@
#include <gdk/gdk.h>
#include <xkbcommon/xkbcommon.h>
gboolean
squeek_keymap_get_entries_for_keyval (struct xkb_keymap *xkb_keymap,
guint keyval,
GdkKeymapKey **keys,
guint *n_keys);

28
meson.build Normal file
View File

@ -0,0 +1,28 @@
project(
'squeekboard',
'c', 'rust',
version: '1.0.9',
license: 'GPLv3',
meson_version: '>=0.43.0',
default_options: [ 'warning_level=1', 'buildtype=debugoptimized', 'c_std=gnu11' ],
)
i18n = import('i18n')
if get_option('buildtype').startswith('debug')
add_project_arguments('-DDEBUG=1', language : 'c')
endif
if get_option('buildtype') != 'plain'
add_project_arguments('-fstack-protector-strong', language: 'c')
endif
prefix = get_option('prefix')
datadir = join_paths(prefix, get_option('datadir'))
pkgdatadir = join_paths(datadir, meson.project_name())
dbusdir = join_paths(datadir, 'dbus-1/interfaces')
subdir('data')
subdir('protocols')
subdir('eek')
subdir('src')
subdir('po')

9
po/meson.build Normal file
View File

@ -0,0 +1,9 @@
i18n.gettext('squeekboard',
preset: 'glib',
args: [
'--copyright-holder=Purism SPC',
'--package-name=Squeekboard',
'--package-version=' + meson.project_version(),
'--msgid-bugs-address=dorota.czaplejewicz@puri.sm'
]
)

View File

@ -0,0 +1,404 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="input_method_unstable_v2">
<copyright>
Copyright © 2012, 2013 Intel Corporation
Copyright © 2015, 2016 Jan Arne Petersen
Copyright © 2017, 2018 Red Hat, Inc.
Copyright © 2018 Purism SPC
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<description summary="Protocol for creating input methods">
This protocol allows applications to act as input methods for compositors.
An input method context is used to manage the state of the input method.
Text strings are UTF-8 encoded, their indices and lengths are in bytes.
This document adheres to the RFC 2119 when using words like "must",
"should", "may", etc.
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible changes
may be added together with the corresponding interface version bump.
Backward incompatible changes are done by bumping the version number in
the protocol and interface names and resetting the interface version.
Once the protocol is to be declared stable, the 'z' prefix and the
version number in the protocol and interface names are removed and the
interface version number is reset.
</description>
<interface name="zwp_input_method_v2" version="1">
<description summary="input method">
An input method object allows for clients to compose text.
The objects connects the client to a text input in an application, and
lets the client to serve as an input method for a seat.
The zwp_input_method_v2 object can occupy two distinct states: active and
inactive. In the active state, the object is associated to and
communicates with a text input. In the inactive state, there is no
associated text input, and the only communication is with the compositor.
Initially, the input method is in the inactive state.
Requests issued in the inactive state must be accepted by the compositor.
Because of the serial mechanism, and the state reset on activate event,
they will not have any effect on the state of the next text input.
There must be no more than one input method object per seat.
</description>
<event name="activate">
<description summary="input method has been requested">
Notification that a text input focused on this seat requested the input
method to be activated.
This request must be issued every time a text input requests an input
method.
This request resets all state associated with previous enable, disable,
set_surrounding_text, set_text_change_cause, set_content_type, and
set_cursor_rectangle requests, as well as the state associated with
preedit_string, commit_string, and delete_surrounding_text events. In
addition, it marks the input method object as active.
The set_surrounding_text, set_content_type and set_cursor_rectangle
requests must follow before the next done event if the text input
supports the respective functionality.
State set with this event is double-buffered. It will get applied on
the next zwp_input_method_v2.done event, and stay valid until changed.
</description>
</event>
<event name="deactivate">
<description summary="deactivate event">
Notification that this seat's current text input requested the input
method to be deactivated.
This event mrks the zwp_input_method_v2 object as inactive.
When the seat has the keyboard capability the text-input focus follows
the keyboard focus.
State set with this event is double-buffered. It will get applied on
the next zwp_input_method_v2.done event, and stay valid until changed.
</description>
</event>
<event name="surrounding_text">
<description summary="surrounding text event">
Sets the surrounding plain text around the cursor, excluding the
preedit text.
If any preedit text is present, it is replaced with the cursor for the
purpose of this event.
The argument text is a buffer containing the preedit string, and must
include the cursor position, and the complete selection. It should
contain additional characters before and after these. There is a
maximum length of wayland messages, so text can not be longer than 4000
bytes.
cursor is the byte offset of the cursor within the text buffer.
anchor is the byte offset of the selection anchor within the text
buffer. If there is no selected text, anchor must be the same as
cursor.
If this request does not arrive before the first done event, the input
method may assume that the text input does not support this
functionality and ignore following surrounding_text events.
Values set with this event are double-buffered. They will get applied
and set to initial values on the next zwp_input_method_v2.done
event.
The initial state for affected fields is empty, meaning that the text
input does not support sending surrounding text. If the empty values
get applied, subsequent attempts to change them may have no effect.
</description>
<arg name="text" type="string"/>
<arg name="cursor" type="uint"/>
<arg name="anchor" type="uint"/>
</event>
<event name="text_change_cause">
<description summary="indicates the cause of surrounding text change">
Tells the input method why the text surrounding the cursor changed.
Whenever the client detects an external change in text, cursor, or
anchor position, it must issue this request to the compositor. This
request is intended to give the input method a chance to update the
preedit text in an appropriate way, e.g. by removing it when the user
starts typing with a keyboard.
cause describes the source of the change.
The value set with this event is double-buffered. It will get applied
and set to its initial value on the next zwp_input_method_v2.done
event.
The initial value of cause is input_method.
</description>
<arg name="cause" type="uint" enum="zwp_text_input_v3.change_cause"/>
</event>
<event name="content_type">
<description summary="content purpose and hint">
Indicates the content type and hint for the current
input_method_context instance.
Values set with this event are double-buffered. They will get applied
on the next zwp_input_method_v2.done event.
The initial value for hint is none, and the initial value for purpose
is normal.
</description>
<arg name="hint" type="uint" enum="zwp_text_input_v3.content_hint"/>
<arg name="purpose" type="uint" enum="zwp_text_input_v3.content_purpose"/>
</event>
<event name="done">
<description summary="apply state">
Atomically applies state changes recently sent to the client.
The done event establishes and updates the state of the client, and
must be issued after any changes to apply them.
Text input state (content purpose, content hint, surrounding text, and
change cause) is conceptually double-buffered within an input method
context.
Events modify the pending state, as opposed to the current state in use
by the input method. A done event atomically applies all pending state,
replacing the current state. After done, the new pending state is as
documented for each related request.
Events must be applied in the order of arrival.
Neither current nor pending state are modified unless noted otherwise.
</description>
</event>
<request name="commit_string">
<description summary="commit string">
Send the commit string text for insertion to the application.
Inserts a string at current cursor position (see commit event
sequence). The string to commit could be either just a single character
after a key press or the result of some composing.
The argument text is a buffer containing the string to insert. There is
a maximum length of wayland messages, so text can not be longer than
4000 bytes.
Values set with this event are double-buffered. They must be applied
and reset to initial on the next zwp_text_input_v3.done event.
The initial value of text is an empty string.
</description>
<arg name="text" type="string"/>
</request>
<request name="preedit_string">
<description summary="pre-edit string">
Send the pre-edit string text to the application text input.
Place a new composing text (pre-edit) at the current cursor position.
Any previously set composing text must be removed. Any previously
existing selected text must be removed. The cursor is moved to a new
position within the preedit string.
The argument text is a buffer containing the preedit string. There is
a maximum length of wayland messages, so text can not be longer than
4000 bytes.
The arguments cursor_begin and cursor_end are counted in bytes relative
to the beginning of the submitted string buffer. Cursor should be
hidden by the text input when both are equal to -1.
cursor_begin indicates the beginning of the cursor. cursor_end
indicates the end of the cursor. It may be equal or different than
cursor_begin.
Values set with this event are double-buffered. They must be applied on
the next zwp_input_method_v2.commit event.
The initial value of text is an empty string. The initial value of
cursor_begin, and cursor_end are both 0.
</description>
<arg name="text" type="string"/>
<arg name="cursor_begin" type="int"/>
<arg name="cursor_end" type="int"/>
</request>
<request name="delete_surrounding_text">
<description summary="delete text">
Remove the surrounding text.
before_length and after_length are the number of bytes before and after
the current cursor index (excluding the preedit text) to delete.
If any preedit text is present, it is replaced with the cursor for the
purpose of this event. In effect before_length is counted from the
beginning of preedit text, and after_length from its end (see commit
event sequence).
Values set with this event are double-buffered. They must be applied
and reset to initial on the next zwp_input_method_v2.commit request.
The initial values of both before_length and after_length are 0.
</description>
<arg name="before_length" type="uint"/>
<arg name="after_length" type="uint"/>
</request>
<request name="commit">
<description summary="apply state">
Apply state changes from commit_string, preedit_string and
delete_surrounding_text requests.
The state relating to these events is double-buffered, and each one
modifies the pending state. This request replaces the current state
with the pending state.
The connected text input is expected to proceed by evaluating the
changes in the following order:
1. Replace existing preedit string with the cursor.
2. Delete requested surrounding text.
3. Insert commit string with the cursor at its end.
4. Calculate surrounding text to send.
5. Insert new preedit text in cursor position.
6. Place cursor inside preedit text.
The serial number reflects the last state of the zwp_input_method_v2
object known to the client. The value of the serial argument must be
equal to the number of commit requests already issued on that object.
When the compositor receives a done event with a serial different than
the number of past commit requests, it must proceed as normal, except
it should not change the current state of the zwp_input_method_v2
object.
</description>
<arg name="serial" type="uint"/>
</request>
<request name="get_input_popup_surface">
<description summary="create popup surface">
Creates a new zwp_input_popup_surface_v2 object wrapping a given
surface.
</description>
<arg name="id" type="new_id" interface="zwp_input_popup_surface_v2"/>
<arg name="surface" type="object" interface="wl_surface"/>
</request>
<request name="grab_keyboard">
<description summary="grab hardware keyboard">
Allow an input method to receive hardware keyboard input and process
key events to generate text events (with pre-edit) over the wire. This
allows input methods which compose multiple key events for inputting
text like it is done for CJK languages.
The compositor should send all keyboard events on the seat to the grab
holder via the returned wl_keyboard object. Nevertheless, the
compositor may decide not to forward any particular event. The
compositor must not further process any event after it has been
forwarded to the grab holder.
Releasing the resulting wl_keyboard object releases the grab.
</description>
<arg name="keyboard" type="new_id" interface="wl_keyboard"/>
</request>
<event name="unavailable">
<description summary="input method unavailable">
The input method ceased to be available.
The compositor must issue this event as the only event on the object if
there was another input_method object associated with the same seat at
the time of its creation.
The compositor must issue this request when the object is no longer
useable, e.g. due to seat removal.
The input method context becomes inert and should be destroyed after
deactivation is handled. Any further requests and events except for the
destroy request must be ignored.
</description>
</event>
<request name="destroy" type="destructor"/>
</interface>
<interface name="zwp_input_popup_surface_v2" version="1">
<description summary="popup surface">
This surface is a popup for interacting with an input method.
The compositor should place it near the active text input area. It must
be visible if and only if the input method is in the active state.
</description>
<event name="text_input_rectangle">
<description summary="set text input area position">
Notify about the position of the area of the text input expressed as a
rectangle in surface local coordinates.
This is a hint to the input method telling it the relative position of
the text being entered.
</description>
<arg name="x" type="int"/>
<arg name="y" type="int"/>
<arg name="width" type="int"/>
<arg name="height" type="int"/>
</event>
<request name="destroy" type="destructor"/>
</interface>
<interface name="zwp_input_method_manager_v2" version="1">
<description summary="input method manager">
The input method manager allows the client to become the input method on
a chosen seat.
No more than one input method must be associated with any seat at any
given time.
</description>
<request name="get_input_method">
<description summary="request an input method object">
Request a new input zwp_input_method_v2 object associated with a given
seat.
</description>
<arg name="seat" type="object" interface="wl_seat"/>
<arg name="input_method" type="new_id" interface="zwp_input_method_v2"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the input method manager">
Destroys the zwp_input_method_manager_v2 object.
The zwp_input_method_v2 objects originating from it remain valid.
</description>
</request>
</interface>
</protocol>

22
protocols/meson.build Normal file
View File

@ -0,0 +1,22 @@
wayland_protos = dependency('wayland-protocols', version: '>=1.12')
wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir')
wl_scanner = find_program('wayland-scanner')
gen_scanner_client_header = generator(wl_scanner,
output: '@BASENAME@-client-protocol.h',
arguments: ['client-header', '@INPUT@', '@OUTPUT@'])
gen_scanner_client_code = generator(wl_scanner,
output: '@BASENAME@-protocol.c',
arguments: ['private-code', '@INPUT@', '@OUTPUT@'])
wl_protos = [
wl_protocol_dir + '/stable/xdg-shell/xdg-shell.xml',
'wlr-layer-shell-unstable-v1.xml',
'virtual-keyboard-unstable-v1.xml',
'input-method-unstable-v2.xml',
]
wl_proto_sources = []
foreach proto: wl_protos
wl_proto_sources += gen_scanner_client_header.process(proto)
wl_proto_sources += gen_scanner_client_code.process(proto)
endforeach

View File

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="virtual_keyboard_unstable_v1">
<copyright>
Copyright © 2008-2011 Kristian Høgsberg
Copyright © 2010-2013 Intel Corporation
Copyright © 2012-2013 Collabora, Ltd.
Copyright © 2018 Purism SPC
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<interface name="zwp_virtual_keyboard_v1" version="1">
<description summary="virtual keyboard">
The virtual keyboard provides an application with requests which emulate
the behaviour of a physical keyboard.
This interface can be used by clients on its own to provide raw input
events, or it can accompany the input method protocol.
</description>
<request name="keymap">
<description summary="keyboard mapping">
Provide a file descriptor to the compositor which can be
memory-mapped to provide a keyboard mapping description.
Format carries a value from the keymap_format enumeration.
</description>
<arg name="format" type="uint" summary="keymap format"/>
<arg name="fd" type="fd" summary="keymap file descriptor"/>
<arg name="size" type="uint" summary="keymap size, in bytes"/>
</request>
<enum name="error">
<entry name="no_keymap" value="0" summary="No keymap was set"/>
</enum>
<request name="key">
<description summary="key event">
A key was pressed or released.
The time argument is a timestamp with millisecond granularity, with an
undefined base. All requests regarding a single object must share the
same clock.
Keymap must be set before issuing this request.
State carries a value from the key_state enumeration.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="key" type="uint" summary="key that produced the event"/>
<arg name="state" type="uint" summary="physical state of the key"/>
</request>
<request name="modifiers">
<description summary="modifier and group state">
Notifies the compositor that the modifier and/or group state has
changed, and it should update state.
The client should use wl_keyboard.modifiers event to synchronize its
internal state with seat state.
Keymap must be set before issuing this request.
</description>
<arg name="mods_depressed" type="uint" summary="depressed modifiers"/>
<arg name="mods_latched" type="uint" summary="latched modifiers"/>
<arg name="mods_locked" type="uint" summary="locked modifiers"/>
<arg name="group" type="uint" summary="keyboard layout"/>
</request>
<request name="destroy" type="destructor" since="1">
<description summary="destroy the virtual keyboard keyboard object"/>
</request>
</interface>
<interface name="zwp_virtual_keyboard_manager_v1" version="1">
<description summary="virtual keyboard manager">
A virtual keyboard manager allows an application to provide keyboard
input events as if they came from a physical keyboard.
</description>
<enum name="error">
<entry name="unauthorized" value="0" summary="client not authorized to use the interface"/>
</enum>
<request name="create_virtual_keyboard">
<description summary="Create a new virtual keyboard">
Creates a new virtual keyboard associated to a seat.
If the compositor enables a keyboard to perform arbitrary actions, it
should present an error when an untrusted client requests a new
keyboard.
</description>
<arg name="seat" type="object" interface="wl_seat"/>
<arg name="id" type="new_id" interface="zwp_virtual_keyboard_v1"/>
</request>
</interface>
</protocol>

View File

@ -0,0 +1,285 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_layer_shell_unstable_v1">
<copyright>
Copyright © 2017 Drew DeVault
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
</copyright>
<interface name="zwlr_layer_shell_v1" version="1">
<description summary="create surfaces that are layers of the desktop">
Clients can use this interface to assign the surface_layer role to
wl_surfaces. Such surfaces are assigned to a "layer" of the output and
rendered with a defined z-depth respective to each other. They may also be
anchored to the edges and corners of a screen and specify input handling
semantics. This interface should be suitable for the implementation of
many desktop shell components, and a broad number of other applications
that interact with the desktop.
</description>
<request name="get_layer_surface">
<description summary="create a layer_surface from a surface">
Create a layer surface for an existing surface. This assigns the role of
layer_surface, or raises a protocol error if another role is already
assigned.
Creating a layer surface from a wl_surface which has a buffer attached
or committed is a client error, and any attempts by a client to attach
or manipulate a buffer prior to the first layer_surface.configure call
must also be treated as errors.
You may pass NULL for output to allow the compositor to decide which
output to use. Generally this will be the one that the user most
recently interacted with.
Clients can specify a namespace that defines the purpose of the layer
surface.
</description>
<arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
<arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
<arg name="namespace" type="string" summary="namespace for the layer surface"/>
</request>
<enum name="error">
<entry name="role" value="0" summary="wl_surface has another role"/>
<entry name="invalid_layer" value="1" summary="layer value is invalid"/>
<entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
</enum>
<enum name="layer">
<description summary="available layers for surfaces">
These values indicate which layers a surface can be rendered in. They
are ordered by z depth, bottom-most first. Traditional shell surfaces
will typically be rendered between the bottom and top layers.
Fullscreen shell surfaces are typically rendered at the top layer.
Multiple surfaces can share a single layer, and ordering within a
single layer is undefined.
</description>
<entry name="background" value="0"/>
<entry name="bottom" value="1"/>
<entry name="top" value="2"/>
<entry name="overlay" value="3"/>
</enum>
</interface>
<interface name="zwlr_layer_surface_v1" version="1">
<description summary="layer metadata interface">
An interface that may be implemented by a wl_surface, for surfaces that
are designed to be rendered as a layer of a stacked desktop-like
environment.
Layer surface state (size, anchor, exclusive zone, margin, interactivity)
is double-buffered, and will be applied at the time wl_surface.commit of
the corresponding wl_surface is called.
</description>
<request name="set_size">
<description summary="sets the size of the surface">
Sets the size of the surface in surface-local coordinates. The
compositor will display the surface centered with respect to its
anchors.
If you pass 0 for either value, the compositor will assign it and
inform you of the assignment in the configure event. You must set your
anchor to opposite edges in the dimensions you omit; not doing so is a
protocol error. Both values are 0 by default.
Size is double-buffered, see wl_surface.commit.
</description>
<arg name="width" type="uint"/>
<arg name="height" type="uint"/>
</request>
<request name="set_anchor">
<description summary="configures the anchor point of the surface">
Requests that the compositor anchor the surface to the specified edges
and corners. If two orthogonal edges are specified (e.g. 'top' and
'left'), then the anchor point will be the intersection of the edges
(e.g. the top left corner of the output); otherwise the anchor point
will be centered on that edge, or in the center if none is specified.
Anchor is double-buffered, see wl_surface.commit.
</description>
<arg name="anchor" type="uint" enum="anchor"/>
</request>
<request name="set_exclusive_zone">
<description summary="configures the exclusive geometry of this surface">
Requests that the compositor avoids occluding an area of the surface
with other surfaces. The compositor's use of this information is
implementation-dependent - do not assume that this region will not
actually be occluded.
A positive value is only meaningful if the surface is anchored to an
edge, rather than a corner. The zone is the number of surface-local
coordinates from the edge that is considered exclusive.
Surfaces that do not wish to have an exclusive zone may instead specify
how they should interact with surfaces that do. If set to zero, the
surface indicates that it would like to be moved to avoid occluding
surfaces with a positive exclusive zone. If set to -1, the surface
indicates that it would not like to be moved to accommodate for other
surfaces, and the compositor should extend it all the way to the edges
it is anchored to.
For example, a panel might set its exclusive zone to 10, so that
maximized shell surfaces are not shown on top of it. A notification
might set its exclusive zone to 0, so that it is moved to avoid
occluding the panel, but shell surfaces are shown underneath it. A
wallpaper or lock screen might set their exclusive zone to -1, so that
they stretch below or over the panel.
The default value is 0.
Exclusive zone is double-buffered, see wl_surface.commit.
</description>
<arg name="zone" type="int"/>
</request>
<request name="set_margin">
<description summary="sets a margin from the anchor point">
Requests that the surface be placed some distance away from the anchor
point on the output, in surface-local coordinates. Setting this value
for edges you are not anchored to has no effect.
The exclusive zone includes the margin.
Margin is double-buffered, see wl_surface.commit.
</description>
<arg name="top" type="int"/>
<arg name="right" type="int"/>
<arg name="bottom" type="int"/>
<arg name="left" type="int"/>
</request>
<request name="set_keyboard_interactivity">
<description summary="requests keyboard events">
Set to 1 to request that the seat send keyboard events to this layer
surface. For layers below the shell surface layer, the seat will use
normal focus semantics. For layers above the shell surface layers, the
seat will always give exclusive keyboard focus to the top-most layer
which has keyboard interactivity set to true.
Layer surfaces receive pointer, touch, and tablet events normally. If
you do not want to receive them, set the input region on your surface
to an empty region.
Events is double-buffered, see wl_surface.commit.
</description>
<arg name="keyboard_interactivity" type="uint"/>
</request>
<request name="get_popup">
<description summary="assign this layer_surface as an xdg_popup parent">
This assigns an xdg_popup's parent to this layer_surface. This popup
should have been created via xdg_surface::get_popup with the parent set
to NULL, and this request must be invoked before committing the popup's
initial state.
See the documentation of xdg_popup for more details about what an
xdg_popup is and how it is used.
</description>
<arg name="popup" type="object" interface="xdg_popup"/>
</request>
<request name="ack_configure">
<description summary="ack a configure event">
When a configure event is received, if a client commits the
surface in response to the configure event, then the client
must make an ack_configure request sometime before the commit
request, passing along the serial of the configure event.
If the client receives multiple configure events before it
can respond to one, it only has to ack the last configure event.
A client is not required to commit immediately after sending
an ack_configure request - it may even ack_configure several times
before its next surface commit.
A client may send multiple ack_configure requests before committing, but
only the last request sent before a commit indicates which configure
event the client really is responding to.
</description>
<arg name="serial" type="uint" summary="the serial from the configure event"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the layer_surface">
This request destroys the layer surface.
</description>
</request>
<event name="configure">
<description summary="suggest a surface change">
The configure event asks the client to resize its surface.
Clients should arrange their surface for the new states, and then send
an ack_configure request with the serial sent in this configure event at
some point before committing the new surface.
The client is free to dismiss all but the last configure event it
received.
The width and height arguments specify the size of the window in
surface-local coordinates.
The size is a hint, in the sense that the client is free to ignore it if
it doesn't resize, pick a smaller size (to satisfy aspect ratio or
resize in steps of NxM pixels). If the client picks a smaller size and
is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
surface will be centered on this axis.
If the width or height arguments are zero, it means the client should
decide its own window dimension.
</description>
<arg name="serial" type="uint"/>
<arg name="width" type="uint"/>
<arg name="height" type="uint"/>
</event>
<event name="closed">
<description summary="surface should be closed">
The closed event is sent by the compositor when the surface will no
longer be shown. The output may have been destroyed or the user may
have asked for it to be removed. Further changes to the surface will be
ignored. The client should destroy the resource after receiving this
event, and create a new surface if they so choose.
</description>
</event>
<enum name="error">
<entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
<entry name="invalid_size" value="1" summary="size is invalid"/>
<entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
</enum>
<enum name="anchor" bitfield="true">
<entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
<entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
<entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
<entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
</enum>
</interface>
</protocol>

View File

@ -18,7 +18,6 @@
bin_PROGRAMS = \
eekboard \
eekboard-server \
$(NULL)
libexec_PROGRAMS = \
@ -63,40 +62,6 @@ eekboard_SOURCES = \
client-main.c \
$(NULL)
eekboard_server_CFLAGS = \
-I$(top_srcdir) \
$(GIO2_CFLAGS) \
$(GTK_CFLAGS) \
$(LIBXKLAVIER_CFLAGS) \
-DTHEMESDIR=\"$(pkgdatadir)/themes\" \
$(NULL)
eekboard_server_LDADD = \
$(top_builddir)/eekboard/libeekboard.la \
$(top_builddir)/eek/libeek.la \
$(top_builddir)/eek/libeek-gtk.la \
$(top_builddir)/eek/libeek-xkl.la \
$(GIO2_LIBS) \
$(GTK_LIBS) \
$(LIBXKLAVIER_LIBS) \
$(NULL)
if ENABLE_XDOCK
eekboard_server_CFLAGS += $(XDOCK_CFLAGS)
eekboard_server_LDADD += $(XDOCK_LIBS)
endif
eekboard_server_headers = \
server-service.h \
server-context-service.h \
$(NULL)
eekboard_server_SOURCES = \
server-service.c \
server-context-service.c \
server-main.c \
$(NULL)
eekboard_setup_CFLAGS = \
-I$(top_srcdir) \
$(GIO2_CFLAGS) \
@ -125,7 +90,6 @@ dist_pkgdata_DATA = preferences-dialog.ui
noinst_HEADERS = \
$(eekboard_headers) \
$(eekboard_server_headers) \
$(eekboard_setup_headers) \
$(NULL)

View File

@ -37,7 +37,9 @@ static gboolean opt_session = FALSE;
static gchar *opt_address = NULL;
static gboolean opt_focus = FALSE;
#ifdef HAVE_ATSPI
static gboolean opt_keystroke = FALSE;
#endif /* HAVE_ATSPI */
static gchar *opt_keyboards = NULL;
@ -86,7 +88,7 @@ enum FocusListenerType {
};
static gboolean
set_keyboards (Client *client,
set_keyboards (SeatEmitter *client,
const gchar * const *keyboards)
{
if (g_strv_length ((gchar **)keyboards) == 0) {
@ -108,7 +110,7 @@ set_keyboards (Client *client,
int
main (int argc, char **argv)
{
Client *client = NULL;
SeatEmitter *client = NULL;
EekboardClient *eekboard;
EekboardContext *context;
GBusType bus_type;
@ -268,13 +270,13 @@ main (int argc, char **argv)
}
#endif /* HAVE_IBUS */
#ifdef HAVE_XTEST
//#ifdef HAVE_XTEST
if (!client_enable_xtest (client)) {
g_printerr ("Can't init xtest\n");
g_object_unref (client);
exit (1);
}
#endif /* HAVE_XTEST */
//#endif /* HAVE_XTEST */
if (!opt_focus) {
g_object_get (client, "context", &context, NULL);

View File

@ -91,10 +91,8 @@ struct _Client {
AtspiDeviceListener *keystroke_listener;
#endif /* HAVE_ATSPI */
#ifdef HAVE_XTEST
guint modifier_keycodes[8];
XkbDescRec *xkb;
#endif /* HAVE_XTEST */
GSettings *settings;
};
@ -103,7 +101,7 @@ struct _ClientClass {
GObjectClass parent_class;
};
G_DEFINE_TYPE (Client, client, G_TYPE_OBJECT);
G_DEFINE_TYPE (SeatEmitter, client, G_TYPE_OBJECT);
#if ENABLE_FOCUS_LISTENER
#define IS_KEYBOARD_VISIBLE(client) (!client->follows_focus)
@ -130,10 +128,10 @@ static void focus_listener_cb (const AtspiEvent *event,
static gboolean keystroke_listener_cb (const AtspiDeviceEvent *stroke,
void *user_data);
#endif /* HAVE_ATSPI */
static gboolean set_keyboards (Client *client,
static gboolean set_keyboards (SeatEmitter *client,
const gchar * const *keyboard);
static gboolean set_keyboards_from_xkl
(Client *client);
(SeatEmitter *client);
#ifdef HAVE_XTEST
static void update_modifier_keycodes
(Client *client);
@ -145,7 +143,7 @@ client_set_property (GObject *object,
const GValue *value,
GParamSpec *pspec)
{
Client *client = CLIENT(object);
SeatEmitter *client = CLIENT(object);
GDBusConnection *connection;
gchar **keyboards;
@ -190,7 +188,7 @@ client_get_property (GObject *object,
GValue *value,
GParamSpec *pspec)
{
Client *client = CLIENT(object);
SeatEmitter *client = CLIENT(object);
switch (prop_id) {
case PROP_EEKBOARD:
@ -208,7 +206,7 @@ client_get_property (GObject *object,
static void
client_dispose (GObject *object)
{
Client *client = CLIENT(object);
SeatEmitter *client = CLIENT(object);
client_disable_xkl (client);
@ -248,7 +246,7 @@ client_dispose (GObject *object)
static void
client_finalize (GObject *object)
{
Client *client = CLIENT(object);
SeatEmitter *client = CLIENT(object);
g_slist_free (client->keyboards);
G_OBJECT_CLASS (client_parent_class)->finalize (object);
@ -303,13 +301,13 @@ client_class_init (ClientClass *klass)
}
static void
client_init (Client *client)
client_init (SeatEmitter *client)
{
client->settings = g_settings_new ("org.fedorahosted.eekboard");
}
gboolean
client_set_keyboards (Client *client,
client_set_keyboards (SeatEmitter *client,
const gchar * const *keyboards)
{
gboolean retval;
@ -320,7 +318,7 @@ client_set_keyboards (Client *client,
}
gboolean
client_enable_xkl (Client *client)
client_enable_xkl (SeatEmitter *client)
{
GdkDisplay *display = gdk_display_get_default ();
gboolean retval;
@ -363,7 +361,7 @@ client_enable_xkl (Client *client)
}
void
client_disable_xkl (Client *client)
client_disable_xkl (SeatEmitter *client)
{
if (client->xkl_engine) {
xkl_engine_stop_listen (client->xkl_engine, XKLL_TRACK_KEYBOARD_STATE);
@ -620,7 +618,7 @@ add_match_rule (GDBusConnection *connection,
}
static gboolean
on_hide_keyboard_timeout (Client *client)
on_hide_keyboard_timeout (SeatEmitter *client)
{
eekboard_client_hide_keyboard (client->eekboard, NULL);
client->hide_keyboard_timeout_id = 0;
@ -633,7 +631,7 @@ focus_message_filter (GDBusConnection *connection,
gboolean incoming,
gpointer user_data)
{
Client *client = user_data;
SeatEmitter *client = user_data;
if (incoming &&
g_strcmp0 (g_dbus_message_get_interface (message),
@ -663,7 +661,7 @@ focus_message_filter (GDBusConnection *connection,
static void
_ibus_connect_focus_handlers (GDBusConnection *connection, gpointer user_data)
{
Client *client = user_data;
SeatEmitter *client = user_data;
add_match_rule (connection,
"type='method_call',"
@ -681,7 +679,7 @@ _ibus_connect_focus_handlers (GDBusConnection *connection, gpointer user_data)
}
gboolean
client_enable_ibus_focus (Client *client)
client_enable_ibus_focus (SeatEmitter *client)
{
if (client->ibus_connection == NULL) {
const gchar *ibus_address;
@ -713,7 +711,7 @@ client_enable_ibus_focus (Client *client)
}
void
client_disable_ibus_focus (Client *client)
client_disable_ibus_focus (SeatEmitter *client)
{
client->follows_focus = FALSE;
@ -727,10 +725,10 @@ client_disable_ibus_focus (Client *client)
}
}
Client *
SeatEmitter *
client_new (GDBusConnection *connection)
{
Client *client = g_object_new (TYPE_CLIENT,
SeatEmitter *client = g_object_new (TYPE_CLIENT,
"connection", connection,
NULL);
if (client->context)
@ -743,7 +741,7 @@ filter_xkl_event (GdkXEvent *xev,
GdkEvent *event,
gpointer user_data)
{
Client *client = user_data;
SeatEmitter *client = user_data;
XEvent *xevent = (XEvent *)xev;
xkl_engine_filter_events (client->xkl_engine, xevent);
@ -754,7 +752,7 @@ static void
on_xkl_config_changed (XklEngine *xklengine,
gpointer user_data)
{
Client *client = user_data;
SeatEmitter *client = user_data;
gboolean retval;
retval = set_keyboards_from_xkl (client);
@ -766,7 +764,7 @@ on_xkl_config_changed (XklEngine *xklengine,
}
static gboolean
set_keyboards (Client *client,
set_keyboards (SeatEmitter *client,
const gchar * const *keyboards)
{
guint keyboard_id;
@ -807,7 +805,7 @@ set_keyboards (Client *client,
}
static gboolean
set_keyboards_from_xkl (Client *client)
set_keyboards_from_xkl (SeatEmitter *client)
{
XklConfigRec *rec;
gchar *layout, *keyboard;
@ -839,13 +837,12 @@ on_xkl_state_changed (XklEngine *xklengine,
gboolean restore,
gpointer user_data)
{
Client *client = user_data;
SeatEmitter *client = user_data;
if (type == GROUP_CHANGED)
eekboard_context_set_group (client->context, value, NULL);
}
#ifdef HAVE_XTEST
/* The following functions for keyboard mapping change are direct
translation of the code in Caribou (in libcaribou/xadapter.vala):
@ -854,7 +851,7 @@ on_xkl_state_changed (XklEngine *xklengine,
- get_keycode_from_gdk_keymap (Caribou: best_keycode_keyval_match)
*/
static guint
get_replaced_keycode (Client *client)
get_replaced_keycode (SeatEmitter *client)
{
guint keycode;
@ -879,7 +876,7 @@ get_replaced_keycode (Client *client)
non-zero keycode), it simply changes the current map with the
specified KEYCODE and KEYSYM. */
static gboolean
replace_keycode (Client *client,
replace_keycode (SeatEmitter *client,
guint keycode,
guint *keysym)
{
@ -888,12 +885,13 @@ replace_keycode (Client *client,
guint old_keysym;
int keysyms_per_keycode;
KeySym *syms;
return TRUE; // FIXME: no xkb allocated at the moment, pretending all is fine
g_return_val_if_fail (client->xkb->min_key_code <= keycode &&
keycode <= client->xkb->max_key_code,
FALSE);
g_return_val_if_fail (keysym != NULL, FALSE);
/*
* Switch keyboard mapping?
syms = XGetKeyboardMapping (xdisplay, keycode, 1, &keysyms_per_keycode);
old_keysym = syms[0];
syms[0] = *keysym;
@ -901,12 +899,12 @@ replace_keycode (Client *client,
XSync (xdisplay, False);
XFree (syms);
*keysym = old_keysym;
*/
return TRUE;
}
static gboolean
get_keycode_from_gdk_keymap (Client *client,
get_keycode_from_gdk_keymap (SeatEmitter *client,
guint keysym,
guint *keycode,
guint *modifiers)
@ -934,8 +932,18 @@ get_keycode_from_gdk_keymap (Client *client,
return TRUE;
}
int send_virtual_keyboard_key(
Display* dpy,
unsigned int keycode,
Bool is_press,
unsigned long delay
) {
printf("Sending fake event %d press %d delay %d\n", keycode, is_press, delay);
}
/* never actually used? */
static void
send_fake_modifier_key_event (Client *client,
send_fake_modifier_key_event (SeatEmitter *client,
EekModifierType modifiers,
gboolean is_pressed)
{
@ -946,20 +954,20 @@ send_fake_modifier_key_event (Client *client,
for (i = 0; i < G_N_ELEMENTS(client->modifier_keycodes); i++) {
if (modifiers & (1 << i)) {
guint keycode = client->modifier_keycodes[i];
printf("Trying to send a modifier %d press %d\n", i, is_pressed);
g_return_if_fail (keycode > 0);
XTestFakeKeyEvent (xdisplay,
send_virtual_keyboard_key (xdisplay,
keycode,
is_pressed,
CurrentTime);
XSync (xdisplay, False);
//XSync (xdisplay, False);
}
}
}
static void
send_fake_key_event (Client *client,
send_fake_key_event (SeatEmitter *client,
guint xkeysym,
guint keyboard_modifiers)
{
@ -998,10 +1006,10 @@ send_fake_key_event (Client *client,
modifiers |= keyboard_modifiers;
send_fake_modifier_key_event (client, modifiers, TRUE);
XTestFakeKeyEvent (xdisplay, keycode, TRUE, 20);
XSync (xdisplay, False);
XTestFakeKeyEvent (xdisplay, keycode, FALSE, 20);
XSync (xdisplay, False);
send_virtual_keyboard_key (xdisplay, keycode, TRUE, 20);
//XSync (xdisplay, False);
send_virtual_keyboard_key (xdisplay, keycode, FALSE, 20);
// XSync (xdisplay, False);
send_fake_modifier_key_event (client, modifiers, FALSE);
if (old_keysym != xkeysym)
@ -1009,7 +1017,7 @@ send_fake_key_event (Client *client,
}
static void
send_fake_key_events (Client *client,
send_fake_key_events (SeatEmitter *client,
EekSymbol *symbol,
guint keyboard_modifiers)
{
@ -1055,7 +1063,7 @@ on_key_activated (EekboardContext *context,
guint modifiers,
gpointer user_data)
{
Client *client = user_data;
SeatEmitter *client = user_data;
if (g_strcmp0 (eek_symbol_get_name (symbol), "cycle-keyboard") == 0) {
client->keyboards_head = g_slist_next (client->keyboards_head);
@ -1086,6 +1094,8 @@ on_key_activated (EekboardContext *context,
send_fake_key_events (client, symbol, modifiers);
}
#if 0
/* Finds the first key code for each modifier and saves it in modifier_keycodes */
static void
update_modifier_keycodes (Client *client)
{
@ -1107,14 +1117,15 @@ update_modifier_keycodes (Client *client)
}
XFreeModifiermap (mods);
}
#endif
gboolean
client_enable_xtest (Client *client)
client_enable_xtest (SeatEmitter *client)
{
GdkDisplay *display = gdk_display_get_default ();
Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
//GdkDisplay *display = gdk_display_get_default ();
//Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
int opcode, event_base, error_base, major_version, minor_version;
/* FIXME: need at least to fetch an xkb keymap (but what for?)
g_assert (display);
if (!XTestQueryExtension (xdisplay,
@ -1136,7 +1147,7 @@ client_enable_xtest (Client *client)
g_assert (client->xkb);
update_modifier_keycodes (client);
*/
client->key_activated_handler =
g_signal_connect (client->context, "key-activated",
G_CALLBACK(on_key_activated), client);
@ -1145,11 +1156,11 @@ client_enable_xtest (Client *client)
}
void
client_disable_xtest (Client *client)
client_disable_xtest (SeatEmitter *client)
{
if (client->xkb) {
XkbFreeKeyboard (client->xkb, 0, TRUE); /* free_all = TRUE */
client->xkb = NULL;
}
//if (client->xkb) {
// XkbFreeKeyboard (client->xkb, 0, TRUE); /* free_all = TRUE */
//client->xkb = NULL;
//}
}
#endif /* HAVE_XTEST */
//#endif /* HAVE_XTEST */

View File

@ -29,27 +29,27 @@ G_BEGIN_DECLS
#define IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_CLIENT))
#define CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_CLIENT, ClientClass))
typedef struct _Client Client;
typedef struct _Client SeatEmitter;
Client *client_new (GDBusConnection *connection);
SeatEmitter *client_new (GDBusConnection *connection);
gboolean client_set_keyboards (Client *client,
gboolean client_set_keyboards (SeatEmitter *client,
const gchar * const *keyboard);
gboolean client_enable_xkl (Client *client);
void client_disable_xkl (Client *client);
gboolean client_enable_xkl (SeatEmitter *client);
void client_disable_xkl (SeatEmitter *client);
gboolean client_enable_atspi_focus (Client *client);
void client_disable_atspi_focus (Client *client);
gboolean client_enable_atspi_focus (SeatEmitter *client);
void client_disable_atspi_focus (SeatEmitter *client);
gboolean client_enable_atspi_keystroke (Client *client);
void client_disable_atspi_keystroke (Client *client);
gboolean client_enable_atspi_keystroke (SeatEmitter *client);
void client_disable_atspi_keystroke (SeatEmitter *client);
gboolean client_enable_xtest (Client *client);
void client_disable_xtest (Client *client);
gboolean client_enable_xtest (SeatEmitter *client);
void client_disable_xtest (SeatEmitter *client);
gboolean client_enable_ibus_focus (Client *client);
void client_disable_ibus_focus (Client *client);
gboolean client_enable_ibus_focus (SeatEmitter *client);
void client_disable_ibus_focus (SeatEmitter *client);
G_END_DECLS
#endif /* CLIENT_H */

43
src/imservice.c Normal file
View File

@ -0,0 +1,43 @@
#include "imservice.h"
#include <glib.h>
#include "eekboard/eekboard-context-service.h"
void imservice_handle_text_change_cause(void *data, struct zwp_input_method_v2 *input_method) {}
void imservice_handle_content_type(void *data, struct zwp_input_method_v2 *input_method) {}
void imservice_handle_unavailable(void *data, struct zwp_input_method_v2 *input_method) {}
static const struct zwp_input_method_v2_listener input_method_listener = {
.activate = imservice_handle_input_method_activate,
.deactivate = imservice_handle_input_method_deactivate,
.surrounding_text = imservice_handle_surrounding_text,
.text_change_cause = imservice_handle_text_change_cause,
.content_type = imservice_handle_content_type,
.done = imservice_handle_commit_state,
.unavailable = imservice_handle_unavailable,
};
struct imservice* get_imservice(EekboardContextService *context,
struct zwp_input_method_manager_v2 *manager,
struct wl_seat *seat) {
struct zwp_input_method_v2 *im = zwp_input_method_manager_v2_get_input_method(manager, seat);
struct imservice *imservice = imservice_new(im, context);
zwp_input_method_v2_add_listener(im,
&input_method_listener, imservice);
return imservice;
}
void imservice_make_visible(EekboardContextService *context,
struct zwp_input_method_v2 *im) {
(void)im;
eekboard_context_service_show_keyboard (context);
}
void imservice_try_hide(EekboardContextService *context,
struct zwp_input_method_v2 *im) {
(void)im;
eekboard_context_service_hide_keyboard (context);
}

22
src/imservice.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef __IMSERVICE_H
#define __IMSERVICE_H
#include "input-method-unstable-v2-client-protocol.h"
#include "eek/eek-types.h"
struct imservice;
struct imservice* get_imservice(EekboardContextService *context,
struct zwp_input_method_manager_v2 *manager,
struct wl_seat *seat);
// Defined in Rust
struct imservice* imservice_new(struct zwp_input_method_v2 *im,
EekboardContextService *context);
void imservice_handle_input_method_activate(void *data, struct zwp_input_method_v2 *input_method);
void imservice_handle_input_method_deactivate(void *data, struct zwp_input_method_v2 *input_method);
void imservice_handle_surrounding_text(void *data, struct zwp_input_method_v2 *input_method,
const char *text, uint32_t cursor, uint32_t anchor);
void imservice_handle_commit_state(void *data, struct zwp_input_method_v2 *input_method);
#endif

137
src/imservice.rs Normal file
View File

@ -0,0 +1,137 @@
use std::boxed::Box;
use std::ffi::CString;
use std::num::Wrapping;
use std::string::String;
/// Gathers stuff defined in C or called by C
pub mod c {
use super::*;
use std::ffi::CStr;
use std::os::raw::{c_char, 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*
#[repr(transparent)]
pub struct InputMethod(*const c_void);
/// EekboardContextService*
#[repr(transparent)]
pub struct UIManager(*const c_void);
#[no_mangle]
extern "C" {
fn imservice_make_visible(imservice: *const UIManager);
fn imservice_try_hide(imservice: *const UIManager);
}
// The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
#[no_mangle]
pub unsafe extern "C"
fn imservice_new(im: *const InputMethod, ui_manager: *const UIManager) -> *mut IMService {
Box::<IMService>::into_raw(Box::new(
IMService {
im: im,
ui_manager: ui_manager,
pending: IMProtocolState::default(),
current: IMProtocolState::default(),
preedit_string: String::new(),
serial: Wrapping(0u32),
}
))
}
// TODO: is unsafe needed here?
#[no_mangle]
pub unsafe extern "C"
fn imservice_handle_input_method_activate(imservice: *mut IMService,
_im: *const InputMethod)
{
let imservice = &mut *imservice;
imservice.preedit_string = String::new();
imservice.pending = IMProtocolState {
active: true,
..IMProtocolState::default()
};
}
#[no_mangle]
pub unsafe extern "C"
fn imservice_handle_input_method_deactivate(imservice: *mut IMService,
_im: *const InputMethod)
{
let imservice = &mut *imservice;
imservice.pending = IMProtocolState {
active: false,
..imservice.pending.clone()
};
}
#[no_mangle]
pub unsafe extern "C"
fn imservice_handle_surrounding_text(imservice: *mut IMService,
_im: *const InputMethod,
text: *const c_char, cursor: u32, _anchor: u32)
{
let imservice = &mut *imservice;
imservice.pending = IMProtocolState {
surrounding_text: into_cstring(text).expect("Received invalid string"),
surrounding_cursor: cursor,
..imservice.pending
};
}
#[no_mangle]
pub unsafe extern "C"
fn imservice_handle_commit_state(imservice: *mut IMService,
_im: *const InputMethod)
{
let imservice = &mut *imservice;
let active_changed = imservice.current.active ^ imservice.pending.active;
imservice.serial += Wrapping(1u32);
imservice.current = imservice.pending.clone();
imservice.pending = IMProtocolState {
active: imservice.current.active,
..IMProtocolState::default()
};
if active_changed {
if imservice.current.active {
imservice_make_visible(imservice.ui_manager);
} else {
imservice_try_hide(imservice.ui_manager);
}
}
}
// FIXME: destroy and deallocate
}
/// Describes the desired state of the input method as requested by the server
#[derive(Default, Clone)]
struct IMProtocolState {
surrounding_text: CString,
surrounding_cursor: u32,
active: bool,
}
pub struct IMService {
/// Owned reference (still created and destroyed in C)
im: *const c::InputMethod,
/// Unowned reference. Be careful, it's shared with C at large
ui_manager: *const c::UIManager,
pending: IMProtocolState,
current: IMProtocolState, // turn current into an idiomatic representation?
preedit_string: String,
serial: Wrapping<u32>,
}

81
src/meson.build Normal file
View File

@ -0,0 +1,81 @@
gnome = import('gnome')
dbus_src = gnome.gdbus_codegen(
'sm.puri.OSK0',
'../data/dbus/sm.puri.OSK0.xml'
)
sources = [
'imservice.c',
'server-context-service.c',
'server-main.c',
'wayland.c',
'../eek/eek.c',
'../eek/eek-container.c',
'../eek/eek-element.c',
'../eek/eek-gtk-keyboard.c',
'../eek/eek-gtk-renderer.c',
'../eek/eek-key.c',
'../eek/eek-keyboard.c',
'../eek/eek-keyboard-drawing.c',
'../eek/eek-keysym.c',
'../eek/eek-layout.c',
'../eek/eek-renderer.c',
'../eek/eek-section.c',
'../eek/eek-serializable.c',
'../eek/eek-symbol.c',
'../eek/eek-symbol-matrix.c',
'../eek/eek-text.c',
'../eek/eek-theme.c',
'../eek/eek-theme-context.c',
'../eek/eek-theme-node.c',
'../eek/eek-types.c',
'../eek/eek-xml-layout.c',
'../eek/layersurface.c',
dbus_src,
enums,
keysym_entries,
marshalers,
'../eekboard/keymap.c',
'../eekboard/key-emitter.c',
'../eekboard/eekboard-context-service.c',
'../eekboard/eekboard-context.c',
'../eekboard/eekboard-service.c',
# '../eekboard/eekboard-xklutil.c',
wl_proto_sources,
]
cc = meson.get_compiler('c')
deps = [
# dependency('glib-2.0', version: '>=2.26.0'),
dependency('gio-2.0', version: '>=2.26.0'),
dependency('gtk+-3.0', version: '>=3.0'),
dependency('libcroco-0.6'),
dependency('wayland-client', version: '>=1.14'),
dependency('xkbcommon'),
cc.find_library('m'),
cc.find_library('rt'),
# dependency('libxklavier'), # FIXME remove
]
# Replacement for eekboard-server
rslib = library(
'rslib',
sources:['imservice.rs'],
install: true
)
squeekboard = executable('squeekboard',
sources,
link_with: rslib,
include_directories: [include_directories('..'), include_directories('../eek')],
dependencies: deps,
install: true,
c_args: [
'-DTHEMESDIR="' + pkgdatadir + '/themes"',
'-DKEYBOARDSDIR="' + pkgdatadir + '/keyboards"',
'-DEEKBOARD_COMPILATION=1',
'-DEEK_COMPILATION=1'],
)

View File

@ -22,10 +22,11 @@
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <X11/Xatom.h>
#include <gdk/gdkx.h>
#include "eek/eek-gtk.h"
#include "eek/layersurface.h"
#include "wayland.h"
#include "server-context-service.h"
@ -48,7 +49,6 @@ struct _ServerContextService {
gulong notify_visible_handler;
GSettings *settings;
gdouble size_constraint_landscape[2];
gdouble size_constraint_portrait[2];
};
@ -95,6 +95,17 @@ on_notify_keyboard (GObject *object,
const EekKeyboard *keyboard;
keyboard = eekboard_context_service_get_keyboard (EEKBOARD_CONTEXT_SERVICE(context));
if (!keyboard) {
g_error("Programmer error: keyboard layout was unset!");
}
// The keymap will get set even if the window is hidden.
// It's not perfect,
// but simpler than adding a check in the window showing procedure
eekboard_context_service_set_keymap(EEKBOARD_CONTEXT_SERVICE(context),
keyboard);
if (context->window) {
if (keyboard == NULL) {
gtk_widget_hide (context->window);
@ -106,8 +117,9 @@ on_notify_keyboard (GObject *object,
g_signal_handler_block (context->window,
context->notify_visible_handler);
update_widget (context);
if (was_visible)
if (was_visible) {
gtk_widget_show_all (context->window);
}
g_signal_handler_unblock (context->window,
context->notify_visible_handler);
}
@ -250,8 +262,6 @@ set_geometry (ServerContextService *context)
gtk_window_set_decorated (GTK_WINDOW(context->window), FALSE);
gtk_window_set_resizable (GTK_WINDOW(context->window), FALSE);
gtk_window_set_opacity (GTK_WINDOW(context->window), 0.8);
g_signal_connect_after (context->window, "realize",
G_CALLBACK(on_realize_set_dock),
context);
@ -271,13 +281,56 @@ set_geometry (ServerContextService *context)
}
}
static void
make_window (ServerContextService *context) {
if (context->window) {
g_error("Window already present");
return;
}
context->window = GTK_WIDGET(g_object_new (
PHOSH_TYPE_LAYER_SURFACE,
"layer-shell", squeek_wayland->layer_shell,
"wl-output", g_ptr_array_index(squeek_wayland->outputs, 0), // TODO: select output as needed,
"height", 200,
"anchor", ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM
| ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
| ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT,
"layer", ZWLR_LAYER_SHELL_V1_LAYER_TOP,
"kbd-interactivity", FALSE,
"exclusive-zone", 200,
//"namespace", "phosh home",
NULL
));
g_signal_connect (context->window, "destroy",
G_CALLBACK(on_destroy), context);
context->notify_visible_handler =
g_signal_connect (context->window, "notify::visible",
G_CALLBACK(on_notify_visible), context);
// The properties below are just to make hacking easier.
// The way we use layer-shell overrides some,
// and there's no space in the protocol for others.
// Those may still be useful in the future,
// or for hacks with regular windows.
gtk_widget_set_can_focus (context->window, FALSE);
g_object_set (G_OBJECT(context->window), "accept_focus", FALSE, NULL);
gtk_window_set_title (GTK_WINDOW(context->window),
_("Squeekboard"));
gtk_window_set_icon_name (GTK_WINDOW(context->window), "squeekboard");
gtk_window_set_keep_above (GTK_WINDOW(context->window), TRUE);
}
static void
destroy_window (ServerContextService *context) {
context->window = NULL;
}
static void
update_widget (ServerContextService *context)
{
EekKeyboard *keyboard;
const gchar *client_name;
EekBounds bounds;
gchar *theme_name, *theme_filename, *theme_path;
gchar *theme_path;
EekTheme *theme;
if (context->widget) {
@ -285,12 +338,7 @@ update_widget (ServerContextService *context)
context->widget = NULL;
}
theme_name = g_settings_get_string (context->settings, "theme");
theme_filename = g_strdup_printf ("%s.css", theme_name);
g_free (theme_name);
theme_path = g_build_filename (THEMESDIR, theme_filename, NULL);
g_free (theme_filename);
theme_path = g_build_filename (THEMESDIR, "default.css", NULL);
theme = eek_theme_new (theme_path, NULL, NULL);
g_free (theme_path);
@ -302,23 +350,6 @@ update_widget (ServerContextService *context)
g_object_unref (theme);
gtk_widget_set_has_tooltip (context->widget, TRUE);
if (!context->window) {
context->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (context->window, "destroy",
G_CALLBACK(on_destroy), context);
context->notify_visible_handler =
g_signal_connect (context->window, "notify::visible",
G_CALLBACK(on_notify_visible), context);
gtk_widget_set_can_focus (context->window, FALSE);
g_object_set (G_OBJECT(context->window), "accept_focus", FALSE, NULL);
client_name = eekboard_context_service_get_client_name (EEKBOARD_CONTEXT_SERVICE(context));
gtk_window_set_title (GTK_WINDOW(context->window),
client_name ? client_name : _("Keyboard"));
gtk_window_set_icon_name (GTK_WINDOW(context->window), "eekboard");
gtk_window_set_keep_above (GTK_WINDOW(context->window), TRUE);
}
gtk_container_add (GTK_CONTAINER(context->window), context->widget);
set_geometry (context);
}
@ -328,10 +359,8 @@ server_context_service_real_show_keyboard (EekboardContextService *_context)
{
ServerContextService *context = SERVER_CONTEXT_SERVICE(_context);
if (!context->window)
update_widget (context);
g_assert (context->window);
gtk_widget_show_all (context->window);
make_window (context);
update_widget (context);
EEKBOARD_CONTEXT_SERVICE_CLASS (server_context_service_parent_class)->
show_keyboard (_context);
@ -342,8 +371,9 @@ server_context_service_real_hide_keyboard (EekboardContextService *_context)
{
ServerContextService *context = SERVER_CONTEXT_SERVICE(_context);
if (context->window)
gtk_widget_hide (context->window);
gtk_widget_hide (context->window);
gtk_container_remove(GTK_CONTAINER(context->window), context->widget);
destroy_window (context);
EEKBOARD_CONTEXT_SERVICE_CLASS (server_context_service_parent_class)->
hide_keyboard (_context);
@ -472,32 +502,11 @@ server_context_service_init (ServerContextService *context)
"notify::fullscreen",
G_CALLBACK(on_notify_fullscreen),
context);
context->settings = g_settings_new ("org.fedorahosted.eekboard");
g_settings_bind_with_mapping (context->settings, "size-constraint-landscape",
context, "size-constraint-landscape",
G_SETTINGS_BIND_GET,
(GSettingsBindGetMapping)g_value_set_variant,
NULL,
NULL,
NULL);
g_settings_bind_with_mapping (context->settings, "size-constraint-portrait",
context, "size-constraint-portrait",
G_SETTINGS_BIND_GET,
(GSettingsBindGetMapping)g_value_set_variant,
NULL,
NULL,
NULL);
}
ServerContextService *
server_context_service_new (const gchar *client_name,
const gchar *object_path,
GDBusConnection *connection)
EekboardContextService *
server_context_service_new ()
{
return g_object_new (SERVER_TYPE_CONTEXT_SERVICE,
"client-name", client_name,
"object-path", object_path,
"connection", connection,
NULL);
return EEKBOARD_CONTEXT_SERVICE(g_object_new (SERVER_TYPE_CONTEXT_SERVICE,
NULL));
}

View File

@ -18,10 +18,10 @@
#ifndef SERVER_CONTEXT_SERVICE_H
#define SERVER_CONTEXT_SERVICE_H 1
G_BEGIN_DECLS
#include "eekboard/eekboard-service.h"
G_BEGIN_DECLS
#define SERVER_TYPE_CONTEXT_SERVICE (server_context_service_get_type())
#define SERVER_CONTEXT_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SERVER_TYPE_CONTEXT_SERVICE, ServerContextService))
#define SERVER_CONTEXT_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SERVER_TYPE_CONTEXT_SERVICE, ServerContextServiceClass))
@ -29,11 +29,10 @@ G_BEGIN_DECLS
#define SERVER_IS_CONTEXT_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SERVER_TYPE_CONTEXT_SERVICE))
#define SERVER_CONTEXT_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SERVER_TYPE_CONTEXT_SERVICE, ServerContextServiceClass))
/** Manages the liecycle of the window displaying layouts. */
typedef struct _ServerContextService ServerContextService;
ServerContextService *server_context_service_new (const gchar *client_name,
const gchar *object_path,
GDBusConnection *connection);
EekboardContextService *server_context_service_new ();
G_END_DECLS
#endif /* SERVER_CONTEXT_SERVICE_H */

View File

@ -1,6 +1,9 @@
/*
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010-2011 Red Hat, Inc.
* Copyright (C) 2018-2019 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
* Author: Guido Günther <agx@sigxcpu.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -28,28 +31,36 @@
#include <clutter-gtk/clutter-gtk.h>
#endif
#include "server-service.h"
#include "eekboard/eekboard-service.h"
#include "eek/eek.h"
#include "imservice.h"
#include "server-context-service.h"
#include "wayland.h"
#include <gdk/gdkwayland.h>
/// Global application state
struct squeekboard {
struct squeek_wayland wayland;
EekboardContextService *context;
struct imservice *imservice;
};
static gboolean opt_system = FALSE;
static gboolean opt_session = FALSE;
static gchar *opt_address = NULL;
static const GOptionEntry options[] = {
{"system", 'y', 0, G_OPTION_ARG_NONE, &opt_system,
N_("Connect to the system bus")},
{"session", 'e', 0, G_OPTION_ARG_NONE, &opt_session,
N_("Connect to the session bus")},
{"address", 'a', 0, G_OPTION_ARG_STRING, &opt_address,
N_("Connect to the given D-Bus address")},
{NULL}
};
// D-Bus
static void
on_name_acquired (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
(void)connection;
(void)name;
(void)user_data;
}
static void
@ -57,42 +68,126 @@ on_name_lost (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
exit (1);
// TODO: could conceivable continue working
(void)connection;
(void)name;
(void)user_data;
exit (1);
}
static void
on_destroyed (ServerService *service,
on_destroyed (EekboardService *service,
gpointer user_data)
{
(void)service;
GMainLoop *loop = user_data;
g_main_loop_quit (loop);
}
static EekboardContextService *create_context() {
EekboardContextService *context = server_context_service_new ();
g_object_set_data_full (G_OBJECT(context),
"owner", g_strdup ("sender"),
(GDestroyNotify)g_free);
eekboard_context_service_enable (context);
return context;
}
// Wayland
static void
registry_handle_global (void *data,
struct wl_registry *registry,
uint32_t name,
const char *interface,
uint32_t version)
{
// currently only v1 supported for most interfaces,
// so there's no reason to check for available versions.
// Even when lower version would be served, it would not be supported,
// causing a hard exit
(void)version;
struct squeekboard *instance = data;
if (!strcmp (interface, zwlr_layer_shell_v1_interface.name)) {
instance->wayland.layer_shell = wl_registry_bind (registry, name,
&zwlr_layer_shell_v1_interface, 1);
} else if (!strcmp (interface, zwp_virtual_keyboard_manager_v1_interface.name)) {
instance->wayland.virtual_keyboard_manager = wl_registry_bind(registry, name,
&zwp_virtual_keyboard_manager_v1_interface, 1);
} else if (!strcmp (interface, zwp_input_method_manager_v2_interface.name)) {
instance->wayland.input_method_manager = wl_registry_bind(registry, name,
&zwp_input_method_manager_v2_interface, 1);
} else if (!strcmp (interface, "wl_output")) {
struct wl_output *output = wl_registry_bind (registry, name,
&wl_output_interface, 2);
g_ptr_array_add (instance->wayland.outputs, output);
} else if (!strcmp(interface, "wl_seat")) {
instance->wayland.seat = wl_registry_bind(registry, name,
&wl_seat_interface, 1);
}
}
static void
registry_handle_global_remove (void *data,
struct wl_registry *registry,
uint32_t name)
{
// TODO
}
static const struct wl_registry_listener registry_listener = {
registry_handle_global,
registry_handle_global_remove
};
int
main (int argc, char **argv)
{
ServerService *service;
GBusType bus_type;
GDBusConnection *connection;
GError *error;
GMainLoop *loop;
guint owner_id;
#if HAVE_CLUTTER_GTK
if (gtk_clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) {
g_printerr ("Can't init GTK with Clutter\n");
exit (1);
}
#else
if (!gtk_init_check (&argc, &argv)) {
g_printerr ("Can't init GTK\n");
exit (1);
}
#endif
eek_init ();
// Set up Wayland
gdk_set_allowed_backends ("wayland");
GdkDisplay *gdk_display = gdk_display_get_default ();
struct wl_display *display = gdk_wayland_display_get_wl_display (gdk_display);
if (display == NULL) {
g_error ("Failed to get display: %m\n");
exit(1);
}
struct squeekboard instance = {0};
squeek_wayland_init (&instance.wayland);
struct wl_registry *registry = wl_display_get_registry (display);
wl_registry_add_listener (registry, &registry_listener, &instance);
wl_display_roundtrip(display); // wait until the registry is actually populated
squeek_wayland_set_global(&instance.wayland);
if (!instance.wayland.seat) {
g_error("No seat Wayland global available.");
exit(1);
}
if (!instance.wayland.virtual_keyboard_manager) {
g_error("No virtual keyboard manager Wayland global available.");
exit(1);
}
instance.context = create_context();
// set up dbus
// TODO: make dbus errors non-always-fatal
// dbus is not strictly necessary for the useful operation
// if text-input is used, as it can bring the keyboard in and out
GBusType bus_type;
if (opt_system)
bus_type = G_BUS_TYPE_SYSTEM;
else if (opt_address)
@ -100,6 +195,8 @@ main (int argc, char **argv)
else
bus_type = G_BUS_TYPE_SESSION;
GDBusConnection *connection = NULL;
GError *error = NULL;
switch (bus_type) {
case G_BUS_TYPE_SYSTEM:
case G_BUS_TYPE_SESSION:
@ -131,14 +228,16 @@ main (int argc, char **argv)
break;
}
service = server_service_new (EEKBOARD_SERVICE_PATH, connection);
EekboardService *service = eekboard_service_new (connection, EEKBOARD_SERVICE_PATH);
if (service == NULL) {
g_printerr ("Can't create server\n");
g_printerr ("Can't create dbus server\n");
exit (1);
} else {
eekboard_service_set_context(service, instance.context);
}
owner_id = g_bus_own_name_on_connection (connection,
guint owner_id = g_bus_own_name_on_connection (connection,
EEKBOARD_SERVICE_INTERFACE,
G_BUS_NAME_OWNER_FLAGS_NONE,
on_name_acquired,
@ -150,7 +249,19 @@ main (int argc, char **argv)
exit (1);
}
loop = g_main_loop_new (NULL, FALSE);
struct imservice *imservice = NULL;
if (instance.wayland.input_method_manager) {
imservice = get_imservice(instance.context,
instance.wayland.input_method_manager,
instance.wayland.seat);
if (imservice) {
instance.imservice = imservice;
} else {
g_warning("Failed to register as an input method");
}
}
GMainLoop *loop = g_main_loop_new (NULL, FALSE);
g_signal_connect (service, "destroyed", G_CALLBACK(on_destroyed), loop);
@ -161,5 +272,6 @@ main (int argc, char **argv)
g_object_unref (connection);
g_main_loop_unref (loop);
squeek_wayland_deinit (&instance.wayland);
return 0;
}

View File

@ -1,71 +0,0 @@
/*
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010-2011 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "server-service.h"
#include "server-context-service.h"
typedef struct _ServerServiceClass ServerServiceClass;
struct _ServerService {
EekboardService parent;
};
struct _ServerServiceClass {
EekboardServiceClass parent_class;
};
G_DEFINE_TYPE (ServerService, server_service, EEKBOARD_TYPE_SERVICE);
static EekboardContextService *
server_service_real_create_context (EekboardService *self,
const gchar *client_name,
const gchar *object_path)
{
GDBusConnection *connection;
ServerContextService *context;
g_object_get (G_OBJECT(self), "connection", &connection, NULL);
context = server_context_service_new (client_name, object_path, connection);
g_object_unref (connection);
return EEKBOARD_CONTEXT_SERVICE(context);
}
static void
server_service_class_init (ServerServiceClass *klass)
{
EekboardServiceClass *service_class = EEKBOARD_SERVICE_CLASS(klass);
service_class->create_context = server_service_real_create_context;
}
static void
server_service_init (ServerService *self)
{
}
ServerService *server_service_new (const gchar *object_path,
GDBusConnection *connection)
{
return g_object_new (SERVER_TYPE_SERVICE,
"object-path", object_path,
"connection", connection,
NULL);
}

View File

@ -1,38 +0,0 @@
/*
* Copyright (C) 2010-2011 Daiki Ueno <ueno@unixuser.org>
* Copyright (C) 2010-2011 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SERVER_SERVICE_H
#define SERVER_SERVICE_H 1
#include "eekboard/eekboard-service.h"
G_BEGIN_DECLS
#define SERVER_TYPE_SERVICE (server_service_get_type())
#define SERVER_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SERVER_TYPE_SERVICE, ServerService))
#define SERVER_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SERVER_TYPE_SERVICE, ServerServiceClass))
#define SERVER_IS_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SERVER_TYPE_SERVICE))
#define SERVER_IS_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SERVER_TYPE_SERVICE))
#define SERVER_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SERVER_TYPE_SERVICE, ServerServiceClass))
typedef struct _ServerService ServerService;
ServerService *server_service_new (const gchar *object_path,
GDBusConnection *connection);
G_END_DECLS
#endif /* SERVER_SERVICE_H */

3
src/wayland.c Normal file
View File

@ -0,0 +1,3 @@
#include "wayland.h"
struct squeek_wayland *squeek_wayland = NULL;

35
src/wayland.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef WAYLAND_H
#define WAYLAND_H
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
#include "virtual-keyboard-unstable-v1-client-protocol.h"
#include "input-method-unstable-v2-client-protocol.h"
#include <gmodule.h>
struct squeek_wayland {
struct zwlr_layer_shell_v1 *layer_shell;
struct zwp_virtual_keyboard_manager_v1 *virtual_keyboard_manager;
struct zwp_input_method_manager_v2 *input_method_manager;
GPtrArray *outputs; // *wl_output
struct wl_seat *seat;
};
extern struct squeek_wayland *squeek_wayland;
static inline void squeek_wayland_init(struct squeek_wayland *wayland) {
wayland->outputs = g_ptr_array_new();
}
static inline void squeek_wayland_set_global(struct squeek_wayland *wayland) {
squeek_wayland = wayland;
}
static inline void squeek_wayland_deinit(struct squeek_wayland *wayland) {
g_ptr_array_free(wayland->outputs, TRUE);
}
#endif // WAYLAND_H