diff --git a/lib/logic/display.dart b/lib/logic/display.dart index 454b1d67..5e7fae0f 100644 --- a/lib/logic/display.dart +++ b/lib/logic/display.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:collection'; +import 'dart:ui'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; @@ -259,14 +260,31 @@ class DisplayServerSurface extends ChangeNotifier { int get monitor => _monitor; Future close() => sendRequest('close'); - Future enter() => sendRequest('enter'); + + Future enter(Offset position) => sendRequest('enter', { + 'position': { + 'x': position.dx, + 'y': position.dy, + }, + }); + Future leave() => sendRequest('leave'); - Future sendRequest(String name) async { + Future key(List state, int logicalKey, int physicalKey, Duration timestamp) => sendRequest('key', { + 'state': state, + 'logicalKey': logicalKey, + 'physicalKey': physicalKey, + 'timestamp': timestamp.inMilliseconds, + }); + + Future sendRequest(String name, [ + Map data = const {}, + ]) async { await DisplayManager.channel.invokeMethod('requestSurface', { 'name': _server.name, 'id': id, 'reqName': name, + ...data, }); } diff --git a/lib/widgets/surface.dart b/lib/widgets/surface.dart index 10b940f2..f775d06e 100644 --- a/lib/widgets/surface.dart +++ b/lib/widgets/surface.dart @@ -1,4 +1,8 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; import 'package:flutter/scheduler.dart'; +import 'package:flutter/services.dart'; import 'package:libtokyo_flutter/libtokyo.dart' hide ColorScheme; import 'package:libtokyo/libtokyo.dart' hide TokyoApp; import 'package:provider/provider.dart'; @@ -106,6 +110,7 @@ class SurfaceView extends StatefulWidget { class _SurfaceViewState extends State { GlobalKey key = GlobalKey(); + FocusNode _node = FocusNode(); void _sendSize() { if (key.currentContext != null && widget.surface.texture != null && widget.isSizable) { @@ -123,6 +128,12 @@ class _SurfaceViewState extends State { }); } + @override + void dispose() { + super.dispose(); + _node.dispose(); + } + @override Widget _buildContent(BuildContext context, DisplayServerSurface surface) { Widget content = surface.texture == null @@ -135,11 +146,49 @@ class _SurfaceViewState extends State { onFocusChange: (isFocused) { surface.setActive(isFocused); surface.setSuspended(!isFocused); + + if (isFocused) _node.requestFocus(); + else _node.unfocus(); }, child: MouseRegion( - onEnter: (_) => surface.enter(), + onEnter: (ev) => surface.enter(ev.localPosition), onExit: (_) => surface.leave(), - child: content, + child: KeyboardListener( + focusNode: _node, + onKeyEvent: (ev) { + List state = []; + + if (ev is KeyUpEvent) state.add('up'); + if (ev is KeyDownEvent) state.add('down'); + if (ev is KeyRepeatEvent) state.add('repeat'); + + final kPlatformToLogicalKey = kIsWeb ? kWebToLogicalKey : switch (Platform.operatingSystem) { + "android" => kAndroidToLogicalKey, + "fuchsia" => kFuchsiaToLogicalKey, + "ios" => kIosToLogicalKey, + "linux" => kGtkToLogicalKey, + "macos" => kMacOsToLogicalKey, + "windows" => kWindowsToLogicalKey, + (_) => const {}, + }; + + final kPlatformToPhysicalKey = kIsWeb ? kWebToPhysicalKey : switch (Platform.operatingSystem) { + "android" => kAndroidToPhysicalKey, + "fuchsia" => kFuchsiaToPhysicalKey, + "ios" => kIosToPhysicalKey, + "linux" => kLinuxToPhysicalKey, + "macos" => kMacOsToPhysicalKey, + "windows" => kWindowsToPhysicalKey, + (_) => const {}, + }; + + final logicalKey = kPlatformToLogicalKey.map((key, value) => MapEntry(value, key))[ev.logicalKey]; + final physicalKey = kPlatformToPhysicalKey.map((key, value) => MapEntry(value, key))[ev.physicalKey]; + + surface.key(state, logicalKey ?? 0, physicalKey ?? 0, ev.timeStamp); + }, + child: content, + ), ), ); } diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index ea6b0298..9b7736d7 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -121,6 +121,7 @@ add_executable(${BINARY_NAME} "channels/auth.cc" "channels/display/backend/dummy.c" "channels/display/backend/wayland.c" + "channels/display/input/keyboard.c" "channels/display/backend.c" "channels/display/pixel-format.c" "channels/display/texture.c" diff --git a/linux/channels/display.c b/linux/channels/display.c index 86f6d7cd..82ad6864 100644 --- a/linux/channels/display.c +++ b/linux/channels/display.c @@ -111,6 +111,10 @@ static void method_call_handler(FlMethodChannel* channel, FlMethodCall* method_c disp->seat = wlr_seat_create(wl_display, session_name); wlr_seat_set_capabilities(disp->seat, WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_KEYBOARD | WL_SEAT_CAPABILITY_TOUCH); + + keyboard_init(&disp->keyboard); + wlr_seat_set_keyboard(disp->seat, &disp->keyboard); + disp->xdg_shell = wlr_xdg_shell_create(wl_display, 6); disp->xdg_decor = wlr_xdg_decoration_manager_v1_create(wl_display); @@ -355,12 +359,17 @@ static void method_call_handler(FlMethodChannel* channel, FlMethodCall* method_c wlr_xdg_toplevel_send_close(surface->xdg); response = FL_METHOD_RESPONSE(fl_method_success_response_new(NULL)); } else if (strcmp(req_name, "enter") == 0) { + FlValue* pos = fl_value_lookup_string(args, "position"); + double sx = fl_value_get_float(fl_value_lookup_string(pos, "x")); + double sy = fl_value_get_float(fl_value_lookup_string(pos, "y")); + struct wlr_output* output = g_list_nth_data(disp->outputs, surface->monitor); if (output == NULL) { fl_method_call_respond_error(method_call, "Linux", "Output does not exist", fl_value_new_int(surface->monitor), NULL); return; } + wlr_seat_pointer_notify_enter(disp->seat, surface->xdg->base->surface, sx, sy); wlr_surface_send_enter(surface->xdg->base->surface, output); response = FL_METHOD_RESPONSE(fl_method_success_response_new(NULL)); } else if (strcmp(req_name, "leave") == 0) { @@ -371,6 +380,37 @@ static void method_call_handler(FlMethodChannel* channel, FlMethodCall* method_c } wlr_surface_send_leave(surface->xdg->base->surface, output); + wlr_seat_pointer_notify_clear_focus(disp->seat); + response = FL_METHOD_RESPONSE(fl_method_success_response_new(NULL)); + } else if (strcmp(req_name, "key") == 0) { + FlValue* state = fl_value_lookup_string(args, "state"); + int physical = fl_value_get_int(fl_value_lookup_string(args, "physicalKey")); + int logical = fl_value_get_int(fl_value_lookup_string(args, "logicalKey")); + int timestamp = fl_value_get_int(fl_value_lookup_string(args, "timestamp")); + + uint32_t state_value = 0; + + for (size_t i = 0; i < fl_value_get_length(state); i++) { + const char* state_item = fl_value_get_string(fl_value_get_list_value(state, i)); + if (strcmp(state_item, "up") == 0) { + state_value |= WL_KEYBOARD_KEY_STATE_RELEASED; + } else if (strcmp(state_item, "down") == 0) { + state_value |= WL_KEYBOARD_KEY_STATE_PRESSED; + } else { + break; + } + } + + if (state_value > 0) { + if (disp->seat->keyboard_state.focused_surface != surface->xdg->base->surface) { + wlr_seat_keyboard_enter(disp->seat, surface->xdg->base->surface, NULL, 0, NULL); + } + + wlr_seat_keyboard_send_key(disp->seat, timestamp, logical, state_value); + + g_message("%s %d %d %d", fl_value_to_string(state), physical, logical, timestamp); + } + response = FL_METHOD_RESPONSE(fl_method_success_response_new(NULL)); } else { response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new()); diff --git a/linux/channels/display.h b/linux/channels/display.h index 783f04e4..efabb040 100644 --- a/linux/channels/display.h +++ b/linux/channels/display.h @@ -25,6 +25,7 @@ extern "C" { #include #include "display/backend.h" +#include "display/input/keyboard.h" #ifdef __cplusplus #undef static @@ -40,6 +41,7 @@ typedef struct _DisplayChannelDisplay { struct wlr_seat* seat; struct wlr_xdg_shell* xdg_shell; struct wlr_xdg_decoration_manager_v1* xdg_decor; + struct wlr_keyboard keyboard; const gchar* prev_wl_disp; const char* socket; diff --git a/linux/channels/display/input/keyboard.c b/linux/channels/display/input/keyboard.c new file mode 100644 index 00000000..d99e417e --- /dev/null +++ b/linux/channels/display/input/keyboard.c @@ -0,0 +1,10 @@ +#include "keyboard.h" +#include + +static const struct wlr_keyboard_impl keyboard_impl = { + .name = "keyboard", +}; + +void keyboard_init(struct wlr_keyboard* keyboard) { + wlr_keyboard_init(keyboard, &keyboard_impl, "keyboard"); +} diff --git a/linux/channels/display/input/keyboard.h b/linux/channels/display/input/keyboard.h new file mode 100644 index 00000000..4c50a8c0 --- /dev/null +++ b/linux/channels/display/input/keyboard.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +void keyboard_init(struct wlr_keyboard* keyboard);