Skip to content

improve input handling #150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ set(FLUTTER_PI_SRC
src/collection.c
src/cursor.c
src/keyboard.c
src/user_input.c
src/plugins/services.c
)

Expand Down Expand Up @@ -163,7 +164,7 @@ target_compile_options(flutter-pi PRIVATE
${LIBINPUT_CFLAGS}
${LIBUDEV_CFLAGS}
${LIBXKBCOMMON_CFLAGS}
$<$<CONFIG:DEBUG>:-O0 -ggdb>
$<$<CONFIG:DEBUG>:-O0 -ggdb -DDEBUG>
)

if (BUILD_TEXT_INPUT_PLUGIN)
Expand Down
23 changes: 23 additions & 0 deletions include/collection.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include <stdlib.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <assert.h>

#include <pthread.h>

Expand Down Expand Up @@ -404,4 +406,25 @@ static inline void *memdup(const void *restrict src, const size_t n) {
#define BMAP_CLEAR(p_bmap, i_bit) ((p_bmap)[(i_bit) / sizeof(*(p_bmap))] &= ~(1 << ((i_bit) & (sizeof(*(p_bmap)) - 1))))
#define BMAP_ZERO(p_bmap, n_bits) (memset((p_bmap), 0, (((n_bits) - 1) / 8) + 1))

#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))

/**
* @brief Get the current time of the system monotonic clock.
* @returns time in nanoseconds.
*/
static inline uint64_t get_monotonic_time(void) {
struct timespec time;
clock_gettime(CLOCK_MONOTONIC, &time);
return time.tv_nsec + time.tv_sec*1000000000ull;
}

#ifdef DEBUG
#define DEBUG_ASSERT(__cond) assert(__cond)
#define DEBUG_ASSERT_MSG(__cond, __msg) assert((__msg, (__cond))
#else
#define DEBUG_ASSERT(__cond) do {} while (false)
#define DEBUG_ASSERT_MSG(__cond, __msg) do {} while (false)
#endif

#endif
45 changes: 22 additions & 23 deletions include/flutter-pi.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#ifndef _FLUTTERPI_H
#define _FLUTTERPI_H

#define LOG_FLUTTERPI_ERROR(...) fprintf(stderr, "[flutter-pi] " __VA_ARGS__)

#include <limits.h>
#include <linux/input.h>
#include <stdbool.h>
Expand Down Expand Up @@ -211,11 +213,17 @@ struct libudev {
.skewY = 0, .scaleY = 1, .transY = 0, \
.pers0 = -sin(((double) (deg))/180.0*M_PI), .pers1 = 0, .pers2 = cos(((double) (deg))/180.0*M_PI)})

/**
* A flutter transformation that rotates any coords around the z-axis, counter-clockwise.
*/
#define FLUTTER_ROTZ_TRANSFORMATION(deg) ((FlutterTransformation) \
{.scaleX = cos(((double) (deg))/180.0*M_PI), .skewX = -sin(((double) (deg))/180.0*M_PI), .transX = 0, \
.skewY = sin(((double) (deg))/180.0*M_PI), .scaleY = cos(((double) (deg))/180.0*M_PI), .transY = 0, \
.pers0 = 0, .pers1 = 0, .pers2 = 1})

/**
* A transformation that is the result of multiplying a with b.
*/
#define FLUTTER_MULTIPLIED_TRANSFORMATIONS(a, b) ((FlutterTransformation) \
{.scaleX = a.scaleX * b.scaleX + a.skewX * b.skewY + a.transX * b.pers0, \
.skewX = a.scaleX * b.skewX + a.skewX * b.scaleY + a.transX * b.pers1, \
Expand All @@ -227,12 +235,24 @@ struct libudev {
.pers1 = a.pers0 * b.skewX + a.pers1 * b.scaleY + a.pers2 * b.pers1, \
.pers2 = a.pers0 * b.transX + a.pers1 * b.transY + a.pers2 * b.pers2})

/**
* A transformation that is the result of adding a with b.
*/
#define FLUTTER_ADDED_TRANSFORMATIONS(a, b) ((FlutterTransformation) \
{.scaleX = a.scaleX + b.scaleX, .skewX = a.skewX + b.skewX, .transX = a.transX + b.transX, \
.skewY = a.skewY + b.skewY, .scaleY = a.scaleY + b.scaleY, .transY = a.transY + b.transY, \
.pers0 = a.pers0 + b.pers0, .pers1 = a.pers1 + b.pers1, .pers2 = a.pers2 + b.pers2 \
})

/**
* A transformation that is the result of transponating a.
*/
#define FLUTTER_TRANSPONATED_TRANSFORMATION(a) ((FlutterTransformation) \
{.scaleX = a.scaleX, .skewX = a.skewY, .transX = a.pers0, \
.skewY = a.skewX, .scaleY = a.scaleY, .transY = a.pers1, \
.pers0 = a.transX, .pers1 = a.transY, .pers2 = a.pers2, \
})

static inline void apply_flutter_transformation(
const FlutterTransformation t,
double *px,
Expand Down Expand Up @@ -392,21 +412,8 @@ struct flutterpi {
struct compositor *compositor;

/// IO
struct {
bool use_paths;
bool disable_text_input;

glob_t input_devices_glob;
# ifndef BUILD_WITHOUT_UDEV_SUPPORT
struct libudev libudev;
# endif
struct libinput *libinput;
sd_event_source *libinput_event_source;
struct keyboard_config *keyboard_config;

int64_t next_unused_flutter_device_id;
double cursor_x, cursor_y;
} input;
sd_event_source *user_input_event_source;
struct user_input *user_input;

/// flutter stuff
struct {
Expand Down Expand Up @@ -453,14 +460,6 @@ struct platform_message {

extern struct flutterpi flutterpi;

struct input_device_data {
int64_t flutter_device_id_offset;
struct keyboard_state *keyboard_state;
double x, y;
int64_t buttons;
uint64_t timestamp;
};

int flutterpi_fill_view_properties(
bool has_orientation,
enum device_orientation orientation,
Expand Down
2 changes: 2 additions & 0 deletions include/keyboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#include <xkbcommon/xkbcommon.h>

#define LOG_KEYBOARD_ERROR(...) fprintf(stderr, "[keyboard] " __VA_ARGS__)

struct keyboard_config {
struct xkb_context *context;
struct xkb_keymap *default_keymap;
Expand Down
141 changes: 141 additions & 0 deletions include/user_input.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#ifndef USER_INPUT_H_
#define USER_INPUT_H_

#include <xkbcommon/xkbcommon.h>
#include <flutter_embedder.h>

#define LOG_USER_INPUT_ERROR(...) fprintf(stderr, "[user input] " __VA_ARGS__)
#define MAX_COLLECTED_FLUTTER_POINTER_EVENTS 64

#define FLUTTER_POINTER_EVENT(_phase, _timestamp, _x, _y, _device, _signal_kind, _scroll_delta_x, _scroll_delta_y, _device_kind, _buttons) \
(FlutterPointerEvent) { \
.struct_size = sizeof(FlutterPointerEvent), \
.phase = (_phase), \
.timestamp = (_timestamp), \
.x = (_x), .y = (_y), \
.device = (_device), \
.signal_kind = (_signal_kind), \
.scroll_delta_x = (_scroll_delta_x), \
.scroll_delta_y = (_scroll_delta_y), \
.device_kind = (_device_kind), \
.buttons = (_buttons) \
}

#define FLUTTER_POINTER_TOUCH_ADD_EVENT(_timestamp, _x, _y, _device_id) \
FLUTTER_POINTER_EVENT(kAdd, _timestamp, _x, _y, _device_id, kFlutterPointerSignalKindNone, 0.0, 0.0, kFlutterPointerDeviceKindTouch, 0)

#define FLUTTER_POINTER_TOUCH_REMOVE_EVENT(_timestamp, _x, _y, _device_id) \
FLUTTER_POINTER_EVENT(kRemove, _timestamp, _x, _y, _device_id, kFlutterPointerSignalKindNone, 0.0, 0.0, kFlutterPointerDeviceKindTouch, 0)

#define FLUTTER_POINTER_TOUCH_MOVE_EVENT(_timestamp, _x, _y, _device_id) \
FLUTTER_POINTER_EVENT(kMove, _timestamp, _x, _y, _device_id, kFlutterPointerSignalKindNone, 0.0, 0.0, kFlutterPointerDeviceKindTouch, 0)

#define FLUTTER_POINTER_TOUCH_DOWN_EVENT(_timestamp, _x, _y, _device_id) \
FLUTTER_POINTER_EVENT(kDown, _timestamp, _x, _y, _device_id, kFlutterPointerSignalKindNone, 0.0, 0.0, kFlutterPointerDeviceKindTouch, 0)

#define FLUTTER_POINTER_TOUCH_UP_EVENT(_timestamp, _x, _y, _device_id) \
FLUTTER_POINTER_EVENT(kUp, _timestamp, _x, _y, _device_id, kFlutterPointerSignalKindNone, 0.0, 0.0, kFlutterPointerDeviceKindTouch, 0)

#define FLUTTER_POINTER_MOUSE_BUTTON_EVENT(_phase, _timestamp, _x, _y, _device_id, _buttons) \
FLUTTER_POINTER_EVENT(_phase, _timestamp, _x, _y, _device_id, kFlutterPointerSignalKindNone, 0.0, 0.0, kFlutterPointerDeviceKindMouse, _buttons)

#define FLUTTER_POINTER_MOUSE_ADD_EVENT(_timestamp, _x, _y, _device_id, _buttons) \
FLUTTER_POINTER_EVENT(kAdd, _timestamp, _x, _y, _device_id, kFlutterPointerSignalKindNone, 0.0, 0.0, kFlutterPointerDeviceKindMouse, _buttons)

#define FLUTTER_POINTER_MOUSE_REMOVE_EVENT(_timestamp, _x, _y, _device_id, _buttons) \
FLUTTER_POINTER_EVENT(kRemove, _timestamp, _x, _y, _device_id, kFlutterPointerSignalKindNone, 0.0, 0.0, kFlutterPointerDeviceKindMouse, _buttons)

#define FLUTTER_POINTER_REMOVE_EVENT(_timestamp, _x, _y, _device, _buttons) \
FLUTTER_POINTER_EVENT(kRemove, _timestamp, _x, _y, _device_id, kFlutterPointerSignalKindNone, 0.0, 0.0, kFlutterPointerDeviceKindMouse, _buttons)

#define FLUTTER_POINTER_MOUSE_MOVE_EVENT(_timestamp, _x, _y, _device_id, _buttons) \
FLUTTER_POINTER_EVENT( \
(_buttons) & kFlutterPointerButtonMousePrimary ? kMove : kHover, \
_timestamp, \
_x, _y, \
_device_id, \
kFlutterPointerSignalKindNone, \
0.0, 0.0, \
kFlutterPointerDeviceKindMouse, \
_buttons\
)

typedef void (*flutter_pointer_event_callback_t)(void *userdata, const FlutterPointerEvent *events, size_t n_events);

typedef void (*utf8_character_callback_t)(void *userdata, uint8_t *character);

typedef void (*xkb_keysym_callback_t)(void *userdata, xkb_keysym_t keysym);

typedef void (*gtk_keyevent_callback_t)(
void *userdata,
uint32_t unicode_scalar_values,
uint32_t key_code,
uint32_t scan_code,
uint32_t modifiers,
bool is_down
);

typedef void (*set_cursor_enabled_callback_t)(void *userdata, bool enabled);

typedef void (*move_cursor_callback_t)(void *userdata, unsigned int x, unsigned int y);

struct user_input_interface {
flutter_pointer_event_callback_t on_flutter_pointer_event;
utf8_character_callback_t on_utf8_character;
xkb_keysym_callback_t on_xkb_keysym;
gtk_keyevent_callback_t on_gtk_keyevent;
set_cursor_enabled_callback_t on_set_cursor_enabled;
move_cursor_callback_t on_move_cursor;
};

struct user_input;

/**
* @brief Create a new user input instance. Will try to load the default keyboard config from /etc/default/keyboard
* and create a udev-backed libinput instance.
*/
struct user_input *user_input_new(
const struct user_input_interface *interface,
void *userdata,
const FlutterTransformation *display_to_view_transform,
const FlutterTransformation *view_to_display_transform,
unsigned int display_width,
unsigned int display_height
);

/**
* @brief Destroy this user input instance and free all allocated memory. This will not remove any input devices
* added to flutter and won't invoke any callbacks in the user input interface at all.
*/
void user_input_destroy(struct user_input *input);

/**
* @brief Set a 3x3 matrix and display width / height so user_input can transform any device coordinates into
* proper flutter view coordinates. (For example to account for a rotated display)
* Will also transform absolute & relative mouse movements.
*
* @param display_to_view_transform will be copied internally.
*/
void user_input_set_transform(
struct user_input *input,
const FlutterTransformation *display_to_view_transform,
const FlutterTransformation *view_to_display_transform,
unsigned int display_width,
unsigned int display_height
);

/**
* @brief Returns a filedescriptor used for input event notification. The returned
* filedescriptor should be listened to with EPOLLIN | EPOLLRDHUP | EPOLLPRI or equivalent.
* When the fd becomes ready, @ref user_input_on_fd_ready should be called not long after it
* became ready. (libinput somehow relies on that)
*/
int user_input_get_fd(struct user_input *input);

/**
* @brief Should be called when the fd returned by @ref user_input_get_fd becomes ready.
* The user_input_interface callbacks will be called inside this function.
*/
int user_input_on_fd_ready(struct user_input *input);

#endif
Loading