Initial commit

This is the outline of the API intended with a minimal compiler just to verify
there are no immediate parsing issues.
This commit is contained in:
Peter Hutterer 2020-07-14 14:41:32 +10:00
commit 5e649f682a
10 changed files with 1272 additions and 0 deletions

30
.editorconfig Normal file
View file

@ -0,0 +1,30 @@
root = true
[*]
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[meson.build]
indent_style = space
indent_size = 4
tab_width = 4
[*.md]
indent_style = space
tab_width = 4
[*.{c,h}]
indent_style = tab
tab_width = 8
[*.py]
indent_style = space
indent_size = 4
charset = utf-8
[*.{yaml,tmpl}]
indent_style = space
indent_size = 2
charset = utf-8

20
COPYING Normal file
View file

@ -0,0 +1,20 @@
Copyright © 2020 Red Hat, Inc.
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.

138
README.md Normal file
View file

@ -0,0 +1,138 @@
libei
=====
**libei** is a library for Emulated Input, primarily aimed at the Wayland
stack. It provides two parts:
- 🥚 EI for the client side (`libei`)
- 🍦 EIS for the server side (`libeis`)
The communication between the two is an implementation detail, neither
client nor server need to care about the details. Let's call it the BRidge
for EI, or 🥣 brei.
For the purpose of this document, **libei** refers to the project,
`libei`/`libeis` to the two libraries provided.
In the Wayland stack, the EIS server component is part of the
compositor, the EI client component is part of the Wayland client.
```
+--------------------+ +------------------+
| Wayland compositor |---wayland---| Wayland client B |
+--------------------+\ +------------------+
| libinput | libeis | \_wayland______
+----------+---------+ \
| | +-------+------------------+
/dev/input/ +---brei----| libei | Wayland client A |
+-------+------------------+
```
The use-cases **libei** attempts to solve are:
- on-demand input device emulation, e.g. `xdotool` or more generically the
XTEST extension
- input forwarding, e.g. `synergy`
**libei** provides three benefits:
- separation
- distinction
- control
**libei** provides **separation** of emulated input from normal input.
Emulated input is a distinct channel for the compositor and can thus be
handled accordingly. For example, the server may show warning sign in the
task bar while emulated input is active.
The second benefit is **distinction**. Each **libei** client has its own
input device set, the server is always aware of which client is requesting
input at any time. It is possible for the server to treat input from
different emulated input devices differently.
The server is in **control** of emulated input - it can filter input or
discard at will. For example, if the current focus window is a password
prompt, the server can simply discard any emulated input. If the screen is
locked, the server can cancel all emulated input devices.
For the use-case of fowarding input (e.g. `synergy`) **libei** provides
capability monitoring. As with input emulation same benefits apply -
input can only be forwarded if the compositor explicitly does so.
High-level summary
------------------
A pseudo-code implementation for server and client are available in
the [`examples/`](https://gitlab.freedesktop.org/whot/libei/-/tree/master/examples)
directory.
The server starts a `libeis` context (which can be integrated with flatpak
portals) and uses the `libeis` file descriptor to monitor for
client requests.
A client starts a `libei` context and connects to the server - either
directly, via DBus or via a portal. The server (or the portal) approves or
denies the client. After successful authentications the client can request
the creation of a device with capabilities `pointer`, `keyboard` or `touch`.
The client triggers input events on this device, the server receives those
as events through `libeis` and can forwards them as if they were libinput
events. The server has control of the client stream. If the stream is
paused, events from the client are discarded. If the stream is resumed, the
server will receive the events (but may discard them anyway depending on
local state).
The above caters for the `xdotool` use-case.
The client may request to monitor a capability. When the server deems the
client to be in-focus, it forwards events from real devices to the client.
The decision of what constitutes logical focus and what events to forward
are up to the server.
For a `synergy` use-case, the setup requires:
- `synergy-client` on host A monitoring the mouse and keyboard capabilities
- `synergy-server` on host B requesting a mouse/keyboard capability device
- when `synergy-client` receives events via `libei` from compositor A it
forwards those to the remote `synergy-server` which sends them via `libei`
to the compositor B.
The compositor may choose to implement a hotkey to start/stop the events or
it may implement the screen edges to be the hot key.
Open questions
--------------
### Flatpak integration
Where flatpak portals are in use, `libei` will communicate with
the portal and `libeis` with the portal implementation (e.g.
`xdg-desktop-portal-gdk`). The portal is reponsible for
allowing the client to connect and restrictions on the devices a client may
create. `libeis` will run in a private namespace of the compositor.
The portal **may** control suspending/resuming devices (in addition to the
server). The UI for this is not yet sorted.
### Authentication
Sandboxing is addressed via flatpak portals but a further level is likely
desirable, esp. outside flatpak. The simplest solution is the client
announcing the name so the UI can be adjusted accordingly. API wise-maybe an
opaque key/value system so the exact auth can be left to the implementation.
### Triggers
For `synergy` we need capability monitoring started by triggers, e.g. the
client requests a pointer capability monitoring when the real pointer hits
the screen edge. Or in response to a keyboard shortcut.
### Keyboard layouts
The emulated input may require a specific keyboard layout, we need some
keymap notification. Keymaps are effectively one-directional (keycode →
keysym is easy, keysym → keycode can be ambiguous), if the server decides on
a different layout than the client provides it's hard for the client to
figure out what keycodes it needs to send.
Possible solution here: protocol guarantees that keymap events are only sent
while the keyboard is suspended, this way the client knows that if *no*
keymap event has been sent before resume, the client keymap was accepted.

View file

@ -0,0 +1,75 @@
# This is pseudocode, illustrating a libei client implementation
function main():
ctx = ei_new()
ei_portal_connect(ctx)
# let's say this is a blocking wait
event = ei_get_event();
if ei_event_get_type(event) == EI_EVENT_TYPE_DISCONNECT:
print("Sorry, server denied us")
return
# Could also create one device here with both caps
ptr = ei_create_device(ctx, CAP_POINTER, "pseudopointer");
kbd = ei_create_device(ctx, CAP_KEYBOARD, "pseudokeyboard");
keymap_fd = compile_keymap("us", "dvorak")
ei_device_keyboard_set_keymap(kbd, FORMAT_XKB, keymap_fd);
event = ei_get_event()
device = ei_event_get_device()
if ei_event_get_type(event) == EI_EVENT_TYPE_DEVICE_ADDED:
if device == ptr:
# The server may not use the name we suggested but it does
# tell us the chosen name
print("Pointer was created: %s", ei_device_get_name(device))
else:
print("Keyboard was created: %s", ei_device_get_name(device))
else if ei_event_get_type(event) == EI_EVENT_TYPE_DEVICE_REMOVED:
if device == ptr:
print("We're not allowed a pointer device")
elif device == kbd:
print("We're not allowed a keyboard device")
# Our devices start in suspended mode
suspended = True
while True:
poll(ei_get_fd()):
if suspended:
wait_for_event(EI_EVENT_POINTER_RESUME or
EI_EVENT_KEYBOARD_RESUME)
suspended = False
continue
event = ei_get_event();
handler = functions[ei_event_get_type(event)]
handler(event)
ei_disconnect(ctx)
function event_disconnect(event):
print("Ooops, the server kicked us off")
ei_unref(ctx);
sys.exit(1)
function event_keyboard_keymap(event):
# see README comments
keymap_fd = ei_event_keyboard_get_keymap(event))
recompile_macros(keymap_fd)
# Called by our actual application to emulate input
function move_pointer(x, y):
ei_device_pointer_motion(ptr, x, y)
function macro(macro):
for key in macro:
ei_device_keyboard_key(kbd, key, true)
msleep(12); # fake an interval
ei_device_keyboard_key(kbd, key, false)
msleep(12); # fake an interval

View file

@ -0,0 +1,117 @@
# This is pseudocode, illustrating a libeis server implementation
#
# This pseudocode assumes to be part of a compositor that can handle
# input events. It assumes policy decisions as to whether a client may
# connect are made in the portal or the compositor.
#
function main():
ctx = eis_new()
eis_portal_init(ctx)
while True:
poll(eis_get_fd()):
event = eis_get_event();
handler = functions[eis_event_get_type(event)]
handler(event)
for client in myclients:
eis_client_disconnect(client)
eis_unref(ctx)
function event_client_connect(event):
client = eis_event_get_client(event)
if do_not_allow_emulated_input:
eis_client_disconnect(client)
return
# for the portal backend we're assuming that the client has been
# authenticated by the user, otherwise some authentication needs to
# be done here
eis_client_connect(client)
myclients[client] = eis_client_ref(client)
function event_client_disconnect(event):
client = eis_event_get_client(event)
eis_client_unref(client)
function event_device_added(event):
client = eis_event_get_client(event)
device = eis_event_get_device(event)
if client has too many devices:
eis_device_disconnect(device)
return
capabilities = eis_device_get_capabilties()
if disallow_touch_input:
capabilities &= ~EIS_CAP_TOUCH
if capabilities & EIS_CAP_POINTER_ABSOLUTE:
# this is the fixed range we give the client, actual screen
# mapping is performed by us later
eis_device_pointer_set_width(1024)
eis_device_pointer_set_height(768)
eis_device_connect(device)
# Allow the client to send events for all capabilities
eis_device_resume_capability(capabilities)
function event_pointer_motion(event):
x = eis_event_pointer_get_x(event)
y = eis_event_pointer_get_y(event)
compositor_handle_pointer_motion(x, y)
function event_pointer_motion_absolute(event):
x = eis_event_pointer_get_absolute_x(event)
y = eis_event_pointer_get_absolute_y(event)
screen = compositor_get_screen()
sx = x/1024 * screen->width
sy = y/768 * screen->height
compositor_handle_pointer_motion_absolute(sx, sy)
function event_pointer_keyboard_key(event):
device = eis_event_get_device(event)
keymap = eis_device_keyboard_get_keymap(device)
# do some libxkbcommon stuff if need be, or send the keymap down the
# wire
key = eis_event_keyboard_get_key(event)
press = eis_event_keyboard_get_key_is_press(event)
# double escape toggles pointer input, as an example
if press and key == KEY_ESC and last_key == KEY_ESC:
if pointer_is_suspended:
eis_device_resume_capability(EIS_CAP_POINTER_ABSOLUTE|EIS_CAP_POINTER)
else
eis_device_suspend_capability(EIS_CAP_POINTER_ABSOLUTE|EIS_CAP_POINTER)
return
compositor_handle_key(key, press)
function compositor_vt_switch_callback():
for client in myclients:
for device in devices[client]:
if vt_in:
eis_device_resume_capability(all_caps)
else
eis_device_suspend_capability(all_caps)
# Compositor callback if no more emulated input should be handled
# from a specific client
function compositor_callback_terminate_EI(condition):
for client in myclients:
if client matches condition:
eis_client_disconnect(client)

39
meson.build Normal file
View file

@ -0,0 +1,39 @@
project('libei', 'c',
version: '0.1',
license: 'MIT/Expat',
default_options: [ 'c_std=gnu99', 'warning_level=2' ],
meson_version: '>= 0.50.0')
pkgconfig = import('pkgconfig')
cc = meson.get_compiler('c')
cppflags = ['-Wno-unused-parameter', '-fvisibility=hidden']
cflags = cppflags + ['-Wmissing-prototypes', '-Wstrict-prototypes']
add_project_arguments(cflags, language : 'c')
add_project_arguments(cppflags, language : 'cpp')
lib_libei = shared_library('ei',
'src/libei.h',
'src/libei.c',
install: true
)
pkgconfig.generate(lib_libei,
filebase: 'libei',
name : 'libEI',
description : 'Emulated Input client library',
version : meson.project_version(),
)
lib_libeis = shared_library('eis',
'src/libeis.h',
'src/libeis.c',
install: true
)
pkgconfig.generate(lib_libeis,
filebase: 'libeis',
name : 'libEIS',
description : 'Emulated Input server library',
version : meson.project_version(),
libraries : lib_libei
)

24
src/libei.c Normal file
View file

@ -0,0 +1,24 @@
/*
* Copyright © 2020 Red Hat, Inc.
*
* 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.
*/
#include "libei.h"

413
src/libei.h Normal file
View file

@ -0,0 +1,413 @@
/*
* Copyright © 2020 Red Hat, Inc.
*
* 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.
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
struct ei_ctx;
struct ei_device;
struct ei_event;
struct ei_event_client;
struct ei_event_pointer;
struct ei_event_keyboard;
struct ei_event_touch;
enum ei_device_capability {
EI_DEVICE_CAP_POINTER,
EI_DEVICE_CAP_POINTER_ABSOLUTE,
EI_DEVICE_CAP_KEYBOARD,
EI_DEVICE_CAP_TOUCH,
};
enum ei_keymap_type {
EI_KEYMAP_TYPE_XKB = 1,
};
enum ei_event_type {
/**
* No event ever has this type, this type exists only as potential
* return value for ei_next_event_type().
*/
EI_EVENT_NONE = 0,
/**
* The server has approved the connection to this client.
*/
EI_EVENT_CONNECT,
/**
* The server has disconnected this client - all resources left to
* reference this server are now obsolete. Once this event has been
* received, the @ref struct ei_ctx and all its associated resources
* should be released.
*/
EI_EVENT_DISCONNECT,
/**
* The server has added a device for this client. The capabilities
* of the device may not match the requested capabilities - it is up
* to the client to verify the minimum required capabilities are
* indeed set.
*/
EI_EVENT_DEVICE_ADDED,
/**
* The server has removed a device belonging to this client.
*/
EI_EVENT_DEVICE_REMOVED,
/**
* The client may send events.
*/
EI_EVENT_POINTER_RESUME = 300,
/**
* Any events sent will be discarded until the next resume.
*/
EI_EVENT_POINTER_SUSPEND,
/**
* Update pointer ranges. This provides the client with the allowed
* axis ranges for absolute pointer motion. The axis ranges may not
* match the available screen ranges and the axis range may only map
* to a portion of the available screen size.
*/
EI_EVENT_POINTER_RANGES_CHANGED,
/**
* The client may send events.
*/
EI_EVENT_KEYBOARD_RESUME = 400,
/**
* Any events sent will be discarded until the next resume.
*/
EI_EVENT_KEYBOARD_SUSPEND,
/**
* Notification of the keymap to be used by the client.
*/
EI_EVENT_KEYBOARD_KEYMAP,
/**
* The client may send events.
*/
EI_EVENT_TOUCH_RESUME = 500,
/**
* Any events sent will be discarded until the next resume.
*/
EI_EVENT_TOUCH_SUSPEND,
/**
* Update touch ranges. This provides the client with the allowed
* axis ranges for absolute pointer motion. The axis ranges may not
* match the available screen ranges and the axis range may only map
* to a portion of the available screen size.
*/
EI_EVENT_TOUCH_RANGES_CHANGED,
};
struct ei_ctx *
ei_new(void);
struct ei_ctx *
ei_ref(struct ei_ctx *ctx);
struct ei_ctx *
ei_unref(struct ei_ctx *ei);
/**
* Connect to the libeis server via the org.freedesktop.portal
*
* @return 0 on success or a negative errno on failure
*/
int
ei_portal_connect(struct ei_ctx *ei);
/**
* Connect to the libeis server on the org.freedesktop.Ei bus name.
*
* @return 0 on success or a negative errno on failure
*/
int
ei_dbus_connect(struct ei_ctx *ei);
int
ei_get_fd(struct ei_ctx *ei);
void
ei_dispatch(struct ei_ctx *ei);
struct ei_event *
ei_get_event(struct ei_ctx *ei);
enum ei_event_type
ei_next_event_type(struct ei_ctx *ei);
struct ei_event *
ei_event_unref(struct ei_event *event);
struct ei_device *
ei_device_ref(struct ei_device *device);
struct ei_device *
ei_device_unref(struct ei_device *device);
/**
* Request the creation of a device with suggested capabilities and a
* suggested name. Note that the server may ignore the name and may ignore
* all capabilities.
*
* The device returned is a stub device maintained by the library and should
* only used for initial device configuration. It does not represent the
* server-created device until the @ref EI_EVENT_DEVICE_ADDED for this
* device has been received.
*
* This does not increase the refcount of the device. Use ei_device_ref() to
* keep a reference beyond the immediate scope.
*
* @param ei The context
* @param capabilities A bitmask of @ref ei_device_capability
* @param name A name suggestion for the device
*/
struct ei_device *
ei_create_device(struct ei_ctx *ei, uint32_t capabilities, const char *name);
/**
* Request that the device be added to the server.
* The server will respond with an @ref EI_EVENT_DEVICE_ADDED or @ref
* EI_EVENT_DEVICE_REMOVED event.
*/
void
ei_device_add(struct ei_device *device);
/**
* Notify the server that the device is no longer required. The server will
* reply with a @ref EI_EVENT_DEVICE_REMOVED event once the device has been
* removed.
*
* This does not release any resources associated with this device, use
* ei_device_unref() for any references held by the client.
*/
void
ei_device_remove(struct ei_device *device);
/**
* @return the name of the device (if any) or NULL
*/
const char *
ei_device_get_name(struct ei_device *device);
bool
ei_device_has_capability(struct ei_device *device,
enum ei_device_capability cap);
struct ei_ctx *
ei_device_get_context(struct ei_device *device);
/**
* Generate a relative motion event on a device with
* the @ref EI_DEVICE_CAP_POINTER capability.
*
* @param x The x movement in 1/1000th of a logical pixel
* @param y The y movement in 1/1000th of a logical pixel
*/
void
ei_device_pointer_motion(struct ei_device *device, int32_t x, int32_t y);
/**
* Generate an absolute motion event on a device with
* the @ref EI_DEVICE_CAP_POINTER_ABSOLUTE capability.
*
* The required conditions are:
* - 0 <= x < width
* - 0 <= y < height
*
* @param x The x position in 1/1000th of a logical pixel
* @param y The y position in 1/1000th of a logical pixel
*/
void
ei_device_pointer_motion_absolute(struct ei_device *device,
uint32_t x, uint32_t y);
/**
* Generate a button event on a device with
* the @ref EI_DEVICE_CAP_POINTER_ABSOLUTE or
* @ref EI_DEVICE_CAP_POINTER capability.
*
* Buttons are merely sequentially numbered. It is up to the server to map
* button numbers into semantic button codes (if any). A server may silently
* ignore button events outside meaningful ranges.
*
* @param button A sequentially numbered button, starting at index 1
* @param is_press true for button press, false for button release
*/
void
ei_device_pointer_button(struct ei_device *device,
uint32_t button, bool is_press);
/**
* Generate a scroll event on a device with
* the @ref EI_DEVICE_CAP_POINTER_ABSOLUTE or
* @ref EI_DEVICE_CAP_POINTER capability.
*
* The server emulates discrete scrolling based on the pixel value,
* do not call ei_device_pointer_scroll_discrete() for the
* same input event.
*
* @param x The x scroll distance in 1/1000th of a logical pixel
* @param y The y scroll distance in 1/1000th of a logical pixel
*
* @see ei_device_pointer_scroll_discrete
*/
void
ei_device_pointer_scroll(struct ei_device *device, int32_t x, int32_t y);
/**
* Generate a discrete scroll event on a device with
* the @ref EI_DEVICE_CAP_POINTER_ABSOLUTE or
* @ref EI_DEVICE_CAP_POINTER capability.
*
* A discrete scroll event is based logical scroll units (equivalent to one
* mouse wheel click). The value for one scroll unit is 120, a fraction or
* multiple thereof represents a fraction or multiple of a wheel click.
*
* The server emulates pixel-based scrolling based on the discrete value,
* do not call ei_device_pointer_scroll() for the
* same input event.
*
* @param x The x scroll distance in fractions or multiples of 120
* @param y The y scroll distance in fractions or multiples of 120
*
* @see ei_device_pointer_scroll
*/
void
ei_device_pointer_scroll_discrete(struct ei_device *device, int32_t x, int32_t y);
/**
* Generate a key event on a device with
* the @ref EI_DEVICE_CAP_KEYBOARD capability.
*
* Keys use the evdev scan codes as defined in
* linux/input-event-codes.h
*
* @param keycode The key code
* @param is_press true for key down, false for key up
*/
void
ei_device_keyboard_key(struct ei_device *device, uint32_t keycode, bool is_press);
/**
* Set the keymap on the device. This function has no effect if called
* after ei_device_add(). The keymap for this device is a suggestion to
* the server, the actual keymap used by this device is provided with
* the @ref EI_EVENT_KEYBOARD_KEYMAP event.
*
* @param type the type of the keymap
* @param fd a memmap-able file descriptor to the keymap
*/
void
ei_device_keyboard_set_keymap(struct ei_device *device,
enum ei_keymap_type type,
int fd);
/**
* Return the device from this event.
*
* This does not increase the refcount of the device. Use eis_device_ref()
* to keep a reference beyond the immediate scope.
*/
struct ei_device *
ei_event_get_device(struct ei_event *event);
/**
* @return the event time in microseconds
*/
uint64_t
ei_event_pointer_get_time(struct ei_event_pointer *event);
/**
* For an event of type @ref EI_EVENT_POINTER_RANGES_CHANGED, this function
* defines the allowable range for x - 0 (inclusive) to width (exclusive).
*
* The value is in 1/1000th of logical pixels, i.e. the value 1000 000
* refers to 1000 pixels.
*
* It is a client bug to send pointer values outside this range.
*
* If the touch range changes at runtime, an event of @ref
* EI_EVENT_POINTER_RANGES_CHANGED is generated.
*/
uint32_t
ei_event_pointer_get_width(struct ei_event_pointer *event);
/**
* @see ei_event_pointer_get_width
*/
uint32_t
ei_event_pointer_get_height(struct ei_event_pointer *event);
/**
* @return the event time in microseconds
*/
uint64_t
ei_event_keyboard_get_time(struct ei_event_keyboard *event);
/**
* For an event of type @ref EI_EVENT_TOUCH_RANGES_CHANGED, this function
* defines the allowable range for x - 0 (inclusive) to width (exclusive).
*
* The value is in 1/1000th of logical pixels, i.e. the value 1000 000
* refers to 1000 pixels.
*
* It is a client bug to send touch values outside this range.
*
* If the touch range changes at runtime, an event of @ref
* EI_EVENT_TOUCH_RANGES_CHANGED is generated.
*/
uint32_t
ei_event_touch_get_width(struct ei_event_touch *event);
/**
* @see ei_event_touch_get_width
*/
uint32_t
ei_event_touch_get_height(struct ei_event_touch *event);
/**
* @return the event time in microseconds
*/
uint64_t
ei_event_touch_get_time(struct ei_event_touch *event);
/**
* For an event of type @ref EI_EVENT_KEYBOARD_KEYMAP return the
* type of the keyamp.
*/
enum ei_keymap_type
ei_event_keyboard_get_keymap_type(struct ei_event_keyboard *kbdev);
/**
* For an event of type @ref EI_EVENT_KEYBOARD_KEYMAP return the
* memmap-able file descriptor to the keymap.
*/
int
ei_event_keyboard_get_keymap(struct ei_event_keyboard *kbdev);

24
src/libeis.c Normal file
View file

@ -0,0 +1,24 @@
/*
* Copyright © 2020 Red Hat, Inc.
*
* 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.
*/
#include "libeis.h"

392
src/libeis.h Normal file
View file

@ -0,0 +1,392 @@
/*
* Copyright © 2020 Red Hat, Inc.
*
* 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.
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
struct eis_ctx;
struct eis_client;
struct eis_device;
struct eis_event;
struct eis_event_client;
struct eis_event_pointer;
struct eis_event_keyboard;
struct eis_event_touch;
enum eis_device_capability {
EIS_DEVICE_CAP_POINTER = (1 << 0),
EIS_DEVICE_CAP_POINTER_ABSOLUTE = (1 << 1),
EIS_DEVICE_CAP_KEYBOARD = (1 << 2),
EIS_DEVICE_CAP_TOUCH = (1 << 3),
};
enum eis_keymap_type {
EIS_KEYMAP_TYPE_XKB = 1,
};
enum eis_event_type {
/**
* No event ever has this type, this type exists only as potential
* return value for eis_next_event_type().
*/
EIS_EVENT_NONE = 0,
/**
* A client has connected. This is the first event from any new
* client.
* The server is expected to either call eis_event_client_allow() or
* eis_event_client_deny().
*/
EIS_EVENT_CLIENT_CONNECT,
/**
* The client has disconnected, any pending requests for this client
* should be discarded.
*/
EIS_EVENT_CLIENT_DISCONNECT,
/**
* The client requests creation of a device with a given set of
* capabilities. A client may create more than one device and more
* than one device with the same capabilities. The server may filter
* the capabilities or deny them altogether.
*
*/
EIS_EVENT_DEVICE_ADDED,
/**
* The device created by the client was removed.
*
* libeis guarantees this event is generated before
* @ref EIS_EVENT_CLIENT_DISCONNECT.
*/
EIS_EVENT_DEVICE_REMOVED,
/**
* The client requests monitoring of a capability.
*/
EIS_EVENT_REQUEST_CAPABILITY,
/**
* The client cancelled the request to monitor that capability.
*/
EIS_EVENT_CANCEL_CAPABILITY,
EIS_EVENT_POINTER_MOTION = 300,
EIS_EVENT_POINTER_MOTION_ABSOLUTE,
EIS_EVENT_POINTER_BUTTON,
EIS_EVENT_POINTER_SCROLL,
EIS_EVENT_POINTER_SCROLL_DISCRETE,
EIS_EVENT_KEYBOARD_KEY = 400,
EIS_EVENT_KEYBOARD_KEYMAP,
EIS_EVENT_TOUCH_DOWN = 500,
EIS_EVENT_TOUCH_UP,
EIS_EVENT_TOUCH_MOTION,
};
/**
* Create a new libeis context with a refcount of 1.
*/
struct eis_ctx *
eis_new(void);
struct eis_ctx *
eis_ref(struct eis_ctx *eis);
struct eis_ctx *
eis_unref(struct eis_ctx *eis);
/**
* Initialize the context with org.freedesktop.portal integration.
*/
int
eis_portal_init(struct eis_ctx *ctx);
/**
* Initialize the context with a DBus backend on the org.freedesktop.Ei bus
* name.
*/
int
eis_dbus_init(struct eis_ctx *ctx);
int
eis_get_fd(struct eis_ctx *eis);
void
eis_dispatch(struct eis_ctx *eis);
struct eis_event *
eis_get_event(struct eis_ctx *eis);
enum eis_event_type
eis_next_event_type(struct eis_ctx *eis);
struct eis_client *
eis_client_ref(struct eis_client *client);
struct eis_client *
eis_client_unref(struct eis_client *client);
/**
* Allow connection from the client. This can only be done once, further
* calls to this functions are ignored.
*
* When receiving an event of type @ref EIS_EVENT_CLIENT_CONNECT, the server
* should connect client as soon as possible to allow communication with the
* server. If the client is not authorized to talk to the server, call
* eis_client_disconnect().
*/
void
eis_client_connect(struct eis_client *client);
/**
* Disconnect this client. Once disconnected the client may no longer talk
* to this context, all resources associated with this client should be
* released.
*
* It is not necessary to call this function when an @ref
* EIS_EVENT_CLIENT_DISCONNECT event is received.
*/
void
eis_client_disconnect(struct eis_client *client);
struct eis_client *
eis_event_get_client(struct eis_event *event);
struct eis_device *
eis_device_ref(struct eis_device *device);
struct eis_device *
eis_device_unref(struct eis_device *device);
const char *
eis_device_get_name(struct eis_device *device);
/**
* Set the name of the device. This function has no effect if called after
* eis_device_connect()
*/
const char *
eis_device_set_name(struct eis_device *device, const char *name);
uint32_t
eis_device_get_capabilities(struct eis_device *device);
/**
* Set the capabilities of the device. This function has no effect if called after
* eis_device_connect()
*/
void
eis_device_set_capabilities(struct eis_device *device, uint32_t caps);
/**
* Connects the device.
*
* This function should be called in response to an @ref
* EIS_EVENT_DEVICE_ADDED if the server accepts the device creation.
* Any changes to the device, e.g. eis_device_set_name() and
* eis_device_disable_capability() must be performed before connecting the
* device.
*
* Calling eis_device_connect() on a device with all capabilities set to
* zero is a bug.
*
* If the device is rejected, call eis_device_disconnect() instead.
*
* The device's capabilities are suspended, use
* eis_device_resume_capability() to enable events from the client.
*/
void
eis_device_connect(struct eis_device *device);
/**
* Disconnect the device.
* This does not release any resources associated with this device, use
* eils_device_unref() for any references held by the caller.
*/
void
eis_device_disconnect(struct eis_device *device);
/**
* Notify the client that the capabilities are suspended and that no events
* from the client for this capability will be processed.
*
* @param device A connected device
* @param cap A bitmask of the capabilities to suspend.
*/
void
eis_device_suspend_capability(struct eis_device *device, uint32_t cap);
/**
* Notify the client that the capabilities are resumed and that events
* from the client for this capability will be processed.
*
* @param device A connected device
* @param cap A bitmask of the capabilities to resume.
*/
void
eis_device_resume_capability(struct eis_device *device, uint32_t cap);
/**
* Notify the client of a new range for absolute pointer events.
* The range is 0 (inclusive) to width (exclusive).
*
* The value is in 1/1000th of logical pixels, i.e. the value 1000 000
* refers to 1000 pixels.
*
* Due to inherent race conditions with changing the width of a device in an
* asynchronous communication channel, a server should alway suspend the
* pointer capability before changing the width.
*
* @param width The new width in 1/1000th of logical pixels
*/
void
eis_device_pointer_set_width(struct eis_device *device, uint32_t width);
/**
* @see eis_device_pointer_set_width
*/
void
eis_device_pointer_set_height(struct eis_device *device, uint32_t height);
/**
* Set the keymap on the device.
* Due to inherent race conditions with changing the keymap on a device in
* an asynchronous communication channel, a server should alway suspend the
* keyboard capability before changing the keymap.
*
* @param type the type of the keymap
* @param fd a memmap-able file descriptor to the keymap
*/
void
eis_device_keyboard_set_keymap(struct eis_device *device,
enum eis_keymap_type type,
int fd);
/**
* Return the device from this event.
*
* This does not increase the refcount of the device. Use eis_device_ref()
* to keep a reference beyond the immediate scope.
*/
struct eis_device *
eis_event_get_device(struct eis_event *event);
/**
* For an event of type @ref EIS_EVENT_POINTER_MOTION return the x movement
* in 1/1000th of a logical pixel.
*/
int32_t
eis_event_pointer_get_x(struct eis_event_pointer *ptrev);
/**
* For an event of type @ref EIS_EVENT_POINTER_MOTION return the y movement
* in 1/1000th of a logical pixel.
*/
int32_t
eis_event_pointer_get_y(struct eis_event_pointer *ptrev);
/**
* For an event of type @ref EIS_EVENT_POINTER_MOTION_ABSOLUTE return the x
* position in 1/1000th of a logical pixel.
*/
uint32_t
eis_event_pointer_get_absolute_x(struct eis_event_pointer *ptrev);
/**
* For an event of type @ref EIS_EVENT_POINTER_MOTION_ABSOLUTE return the y
* position in 1/1000th of a logical pixel.
*/
uint32_t
eis_event_pointer_get_absolute_y(struct eis_event_pointer *ptrev);
/**
* For an event of type @ref EIS_EVENT_POINTER_BUTTON return the button
* index (starting at 1).
*/
uint32_t
eis_event_pointer_get_button(struct eis_event_pointer *ptrev);
/**
* For an event of type @ref EIS_EVENT_POINTER_BUTTON return true if the
* event is a button press, false for a release.
*/
bool
eis_event_pointer_get_button_is_press(struct eis_event_pointer *ptrev);
/**
* For an event of type @ref EIS_EVENT_POINTER_SCROLL return the x scroll
* distance in 1/1000th of a logical pixel.
*/
int32_t
eis_event_pointer_get_scroll_x(struct eis_event_pointer *ptrev);
/**
* For an event of type @ref EIS_EVENT_POINTER_SCROLL return the y scroll
* distance in 1/1000th of a logical pixel.
*/
int32_t
eis_event_pointer_get_scroll_y(struct eis_event_pointer *ptrev);
/**
* For an event of type @ref EIS_EVENT_POINTER_SCROLL_DISCRETE return the x
* scroll distance in fractions or multiples of 120.
*/
int32_t
eis_event_pointer_get_scroll_discrete_x(struct eis_event_pointer *ptrev);
/**
* For an event of type @ref EIS_EVENT_POINTER_SCROLL_DISCRETE return the y
* scroll distance in fractions or multiples of 120.
*/
int32_t
eis_event_pointer_get_scroll_discrete_y(struct eis_event_pointer *ptrev);
/**
* For an event of type @ref EIS_EVENT_KEYBOARD_KEY return the key code (as
* defined in include/linux/input-event-codes.h).
*/
uint32_t
eis_event_keyboard_get_key(struct eis_event_keyboard *kbdev);
/**
* For an event of type @ref EIS_EVENT_KEYBOARD_KEY return true if the
* event is a key down, false for a release.
*/
bool
eis_event_keyboard_get_key_is_press(struct eis_event_keyboard *kbdev);
/**
* For an event of type @ref EIS_EVENT_KEYBOARD_KEYMAP return the
* type of the keyamp.
*/
enum eis_keymap_type
eis_event_keyboard_get_keymap_type(struct eis_event_keyboard *kbdev);
/**
* For an event of type @ref EIS_EVENT_KEYBOARD_KEYMAP return the
* memmap-able file descriptor to the keymap.
*/
int
eis_event_keyboard_get_keymap(struct eis_event_keyboard *kbdev);