mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-07-31 22:11:34 +08:00
fix: restore remote keyboard control in Slint client, refs #91
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
#include "keyboard_capturer.h"
|
#include "keyboard_capturer.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "keyboard_converter.h"
|
#include "keyboard_converter.h"
|
||||||
@@ -10,6 +11,7 @@ namespace crossdesk {
|
|||||||
static OnKeyAction g_on_key_action = nullptr;
|
static OnKeyAction g_on_key_action = nullptr;
|
||||||
static void* g_user_ptr = nullptr;
|
static void* g_user_ptr = nullptr;
|
||||||
static std::unordered_map<int, int> g_unmapped_keycode_to_vk;
|
static std::unordered_map<int, int> g_unmapped_keycode_to_vk;
|
||||||
|
constexpr int64_t kCrossDeskInjectedKeyboardEvent = 0x43524f5353444553;
|
||||||
|
|
||||||
static int VkCodeFromUnicode(UniChar ch) {
|
static int VkCodeFromUnicode(UniChar ch) {
|
||||||
if (ch >= 'a' && ch <= 'z') {
|
if (ch >= 'a' && ch <= 'z') {
|
||||||
@@ -103,6 +105,10 @@ static int ResolveVkCodeFromMacEvent(CGEventRef event, CGKeyCode key_code,
|
|||||||
CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type,
|
CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type,
|
||||||
CGEventRef event, void* userInfo) {
|
CGEventRef event, void* userInfo) {
|
||||||
(void)proxy;
|
(void)proxy;
|
||||||
|
if (CGEventGetIntegerValueField(event, kCGEventSourceUserData) ==
|
||||||
|
kCrossDeskInjectedKeyboardEvent) {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
if (!g_on_key_action) {
|
if (!g_on_key_action) {
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
@@ -302,6 +308,8 @@ int KeyboardCapturer::SendKeyboardCommand(int key_code, bool is_down,
|
|||||||
}
|
}
|
||||||
|
|
||||||
CGEventSetFlags(event, ToCGEventFlags(injected_flags));
|
CGEventSetFlags(event, ToCGEventFlags(injected_flags));
|
||||||
|
CGEventSetIntegerValueField(event, kCGEventSourceUserData,
|
||||||
|
kCrossDeskInjectedKeyboardEvent);
|
||||||
CGEventPost(kCGHIDEventTap, event);
|
CGEventPost(kCGHIDEventTap, event);
|
||||||
CFRelease(event);
|
CFRelease(event);
|
||||||
|
|
||||||
@@ -314,6 +322,8 @@ int KeyboardCapturer::SendKeyboardCommand(int key_code, bool is_down,
|
|||||||
LOG_ERROR("CGEventCreateKeyboardEvent failed for fn release");
|
LOG_ERROR("CGEventCreateKeyboardEvent failed for fn release");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
CGEventSetIntegerValueField(fn_release_event, kCGEventSourceUserData,
|
||||||
|
kCrossDeskInjectedKeyboardEvent);
|
||||||
CGEventPost(kCGHIDEventTap, fn_release_event);
|
CGEventPost(kCGHIDEventTap, fn_release_event);
|
||||||
CFRelease(fn_release_event);
|
CFRelease(fn_release_event);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ static bool PreferSideSpecificVkInjection(int key_code) {
|
|||||||
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
|
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
|
||||||
if (nCode == HC_ACTION && g_on_key_action) {
|
if (nCode == HC_ACTION && g_on_key_action) {
|
||||||
KBDLLHOOKSTRUCT* kbData = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
|
KBDLLHOOKSTRUCT* kbData = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
|
||||||
|
if ((kbData->flags & LLKHF_INJECTED) != 0) {
|
||||||
|
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
||||||
|
}
|
||||||
const int key_code = NormalizeModifierVkCode(kbData);
|
const int key_code = NormalizeModifierVkCode(kbData);
|
||||||
|
|
||||||
if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) {
|
if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ struct InteractionState {
|
|||||||
bool start_keyboard_capturer_ = false;
|
bool start_keyboard_capturer_ = false;
|
||||||
bool show_cursor_ = false;
|
bool show_cursor_ = false;
|
||||||
bool keyboard_capturer_is_started_ = false;
|
bool keyboard_capturer_is_started_ = false;
|
||||||
bool keyboard_capturer_uses_sdl_events_ = false;
|
bool keyboard_capturer_uses_window_events_ = false;
|
||||||
bool foucs_on_main_window_ = false;
|
bool foucs_on_main_window_ = false;
|
||||||
bool focus_on_stream_window_ = false;
|
bool focus_on_stream_window_ = false;
|
||||||
bool audio_capture_ = false;
|
bool audio_capture_ = false;
|
||||||
|
|||||||
@@ -30,6 +30,8 @@
|
|||||||
#include "localization.h"
|
#include "localization.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
#include "platform/tray/win_tray.h"
|
#include "platform/tray/win_tray.h"
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
#include "platform/tray/mac_tray.h"
|
#include "platform/tray/mac_tray.h"
|
||||||
@@ -1726,6 +1728,8 @@ void GuiApplication::BindStreamCallbacks() {
|
|||||||
bool control, bool alt, bool shift, bool meta) {
|
bool control, bool alt, bool shift, bool meta) {
|
||||||
SendKeyInput(std::string(text), pressed, control, alt, shift, meta);
|
SendKeyInput(std::string(text), pressed, control, alt, shift, meta);
|
||||||
});
|
});
|
||||||
|
stream->on_keyboard_focus_changed(
|
||||||
|
[this](bool focused) { SetStreamKeyboardFocus(focused); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiApplication::BindServerCallbacks() {
|
void GuiApplication::BindServerCallbacks() {
|
||||||
@@ -1831,6 +1835,11 @@ void GuiApplication::Tick() {
|
|||||||
HandlePendingPresenceProbe();
|
HandlePendingPresenceProbe();
|
||||||
HandleConnectionTimeouts();
|
HandleConnectionTimeouts();
|
||||||
HandleWindowsServiceIntegration();
|
HandleWindowsServiceIntegration();
|
||||||
|
#if defined(__linux__) && !defined(__APPLE__)
|
||||||
|
SyncXWaylandWindowActivation();
|
||||||
|
#else
|
||||||
|
SyncStreamKeyboardFocus();
|
||||||
|
#endif
|
||||||
devices_.UpdateInteractions();
|
devices_.UpdateInteractions();
|
||||||
|
|
||||||
UpdateLocalization();
|
UpdateLocalization();
|
||||||
@@ -1839,9 +1848,6 @@ void GuiApplication::Tick() {
|
|||||||
SyncPlatformDialogs();
|
SyncPlatformDialogs();
|
||||||
SyncStreamWindow();
|
SyncStreamWindow();
|
||||||
SyncServerWindow();
|
SyncServerWindow();
|
||||||
#if defined(__linux__) && !defined(__APPLE__)
|
|
||||||
SyncXWaylandWindowActivation();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiApplication::UpdateLocalization() {
|
void GuiApplication::UpdateLocalization() {
|
||||||
@@ -2228,6 +2234,7 @@ void GuiApplication::SyncXWaylandWindowActivation() {
|
|||||||
Display* display = ui_->x11_focus_display;
|
Display* display = ui_->x11_focus_display;
|
||||||
const ::Window active_window = X11GetActiveWindow(display);
|
const ::Window active_window = X11GetActiveWindow(display);
|
||||||
if (!active_window) {
|
if (!active_window) {
|
||||||
|
SetStreamKeyboardFocus(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2237,19 +2244,49 @@ void GuiApplication::SyncXWaylandWindowActivation() {
|
|||||||
if (ui_->stream) {
|
if (ui_->stream) {
|
||||||
(*ui_->stream)->set_window_active(false);
|
(*ui_->stream)->set_window_active(false);
|
||||||
}
|
}
|
||||||
|
SetStreamKeyboardFocus(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ui_->main->set_window_active(
|
ui_->main->set_window_active(
|
||||||
X11WindowMatches(display, active_window, ui_->main->window()));
|
X11WindowMatches(display, active_window, ui_->main->window()));
|
||||||
if (ui_->stream) {
|
if (ui_->stream) {
|
||||||
(*ui_->stream)
|
const bool stream_active =
|
||||||
->set_window_active(
|
X11WindowMatches(display, active_window, (*ui_->stream)->window());
|
||||||
X11WindowMatches(display, active_window, (*ui_->stream)->window()));
|
(*ui_->stream)->set_window_active(stream_active);
|
||||||
|
SetStreamKeyboardFocus(stream_active);
|
||||||
|
} else {
|
||||||
|
SetStreamKeyboardFocus(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void GuiApplication::SetStreamKeyboardFocus(bool focused) {
|
||||||
|
if (focus_on_stream_window_ == focused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
focus_on_stream_window_ = focused;
|
||||||
|
if (!focused) {
|
||||||
|
keyboard_.ForceReleasePressedKeys();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GuiApplication::SyncStreamKeyboardFocus() {
|
||||||
|
if (!ui_ || !ui_->stream || !(*ui_->stream)->window().is_visible()) {
|
||||||
|
SetStreamKeyboardFocus(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
const HWND stream_hwnd = (*ui_->stream)->window().win32_hwnd();
|
||||||
|
SetStreamKeyboardFocus(stream_hwnd != nullptr &&
|
||||||
|
GetForegroundWindow() == stream_hwnd);
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
SetStreamKeyboardFocus(IsStreamWindowActive());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void GuiApplication::SyncStreamWindow() {
|
void GuiApplication::SyncStreamWindow() {
|
||||||
bool has_sessions = false;
|
bool has_sessions = false;
|
||||||
{
|
{
|
||||||
@@ -2306,6 +2343,7 @@ void GuiApplication::SyncStreamWindow() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!has_sessions) {
|
if (!has_sessions) {
|
||||||
|
SetStreamKeyboardFocus(false);
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
SetStreamWindowFullscreen(false);
|
SetStreamWindowFullscreen(false);
|
||||||
#endif
|
#endif
|
||||||
@@ -2787,14 +2825,25 @@ void GuiApplication::SelectStreamTab(int index) {
|
|||||||
if (!ui_ || index < 0 || index >= static_cast<int>(ui_->tab_ids.size())) {
|
if (!ui_ || index < 0 || index >= static_cast<int>(ui_->tab_ids.size())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
focused_remote_id_ = ui_->tab_ids[index];
|
const std::string selected_remote_id = ui_->tab_ids[index];
|
||||||
controlled_remote_id_ = focused_remote_id_;
|
if (!controlled_remote_id_.empty() &&
|
||||||
|
controlled_remote_id_ != selected_remote_id) {
|
||||||
|
keyboard_.ForceReleasePressedKeys();
|
||||||
|
}
|
||||||
|
focused_remote_id_ = selected_remote_id;
|
||||||
|
controlled_remote_id_ = selected_remote_id;
|
||||||
std::shared_lock lock(remote_sessions_mutex_);
|
std::shared_lock lock(remote_sessions_mutex_);
|
||||||
for (auto& [id, props] : remote_sessions_) {
|
for (auto& [id, props] : remote_sessions_) {
|
||||||
if (props) {
|
if (props) {
|
||||||
props->tab_selected_ = id == focused_remote_id_;
|
props->tab_selected_ = id == focused_remote_id_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const auto selected = remote_sessions_.find(selected_remote_id);
|
||||||
|
start_keyboard_capturer_ =
|
||||||
|
selected != remote_sessions_.end() && selected->second &&
|
||||||
|
selected->second->control_mouse_ &&
|
||||||
|
selected->second->connection_status_.load() ==
|
||||||
|
ConnectionStatus::Connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiApplication::ReorderStreamTab(int from, float drop_x, float tab_width) {
|
void GuiApplication::ReorderStreamTab(int from, float drop_x, float tab_width) {
|
||||||
@@ -2982,10 +3031,23 @@ void GuiApplication::SendKeyInput(const std::string& text, bool pressed,
|
|||||||
(void)alt;
|
(void)alt;
|
||||||
(void)shift;
|
(void)shift;
|
||||||
(void)meta;
|
(void)meta;
|
||||||
if (auto props = SelectedSession()) {
|
auto props = SelectedSession();
|
||||||
|
if (!props || !props->peer_ || !props->control_mouse_ ||
|
||||||
|
props->connection_status_.load() != ConnectionStatus::Connected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
controlled_remote_id_ = props->remote_id_;
|
controlled_remote_id_ = props->remote_id_;
|
||||||
focused_remote_id_ = props->remote_id_;
|
focused_remote_id_ = props->remote_id_;
|
||||||
|
|
||||||
|
// Native hooks see the same physical key before Slint does. When a native
|
||||||
|
// hook is active, forwarding the FocusScope callback as well would duplicate
|
||||||
|
// the event on platforms whose hook does not consume the local key.
|
||||||
|
if (keyboard_capturer_is_started_ &&
|
||||||
|
!keyboard_capturer_uses_window_events_) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int virtual_key = SlintKeyToWindowsVk(text);
|
const int virtual_key = SlintKeyToWindowsVk(text);
|
||||||
if (virtual_key >= 0) {
|
if (virtual_key >= 0) {
|
||||||
keyboard_.SendKeyCommand(virtual_key, pressed);
|
keyboard_.SendKeyCommand(virtual_key, pressed);
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ private:
|
|||||||
void SyncConnectionDialog();
|
void SyncConnectionDialog();
|
||||||
void SyncPlatformDialogs();
|
void SyncPlatformDialogs();
|
||||||
void SyncStreamWindow();
|
void SyncStreamWindow();
|
||||||
|
void SyncStreamKeyboardFocus();
|
||||||
|
void SetStreamKeyboardFocus(bool focused);
|
||||||
void SyncServerWindow();
|
void SyncServerWindow();
|
||||||
#if defined(__linux__) && !defined(__APPLE__)
|
#if defined(__linux__) && !defined(__APPLE__)
|
||||||
void SyncXWaylandWindowActivation();
|
void SyncXWaylandWindowActivation();
|
||||||
|
|||||||
@@ -200,7 +200,8 @@ void GuiApplication::ProcessSdlEvent(const SDL_Event &event) {
|
|||||||
|
|
||||||
case SDL_EVENT_KEY_DOWN:
|
case SDL_EVENT_KEY_DOWN:
|
||||||
case SDL_EVENT_KEY_UP:
|
case SDL_EVENT_KEY_UP:
|
||||||
if (keyboard_capturer_is_started_ && keyboard_capturer_uses_sdl_events_ &&
|
if (keyboard_capturer_is_started_ &&
|
||||||
|
keyboard_capturer_uses_window_events_ &&
|
||||||
focus_on_stream_window_ && stream_window_ &&
|
focus_on_stream_window_ && stream_window_ &&
|
||||||
SDL_GetWindowID(stream_window_) == event.key.windowID) {
|
SDL_GetWindowID(stream_window_) == event.key.windowID) {
|
||||||
ProcessKeyboardEvent(event);
|
ProcessKeyboardEvent(event);
|
||||||
|
|||||||
@@ -228,27 +228,27 @@ int SessionDeviceManager::StopMouseController() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int SessionDeviceManager::StartKeyboardCapturer() {
|
int SessionDeviceManager::StartKeyboardCapturer() {
|
||||||
owner_.keyboard_capturer_uses_sdl_events_ = false;
|
owner_.keyboard_capturer_uses_window_events_ = false;
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
if (!owner_.EnsureMacAccessibilityPermission()) {
|
if (!owner_.EnsureMacAccessibilityPermission()) {
|
||||||
owner_.keyboard_capturer_uses_sdl_events_ = true;
|
owner_.keyboard_capturer_uses_window_events_ = true;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__linux__) && !defined(__APPLE__)
|
#if defined(__linux__) && !defined(__APPLE__)
|
||||||
if (IsWaylandSession()) {
|
if (IsWaylandSession()) {
|
||||||
owner_.keyboard_capturer_uses_sdl_events_ = true;
|
owner_.keyboard_capturer_uses_window_events_ = true;
|
||||||
LOG_INFO("Start keyboard capturer with SDL Wayland backend");
|
LOG_INFO("Start keyboard capturer with Slint Wayland backend");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!keyboard_capturer_) {
|
if (!keyboard_capturer_) {
|
||||||
owner_.keyboard_capturer_uses_sdl_events_ = true;
|
owner_.keyboard_capturer_uses_window_events_ = true;
|
||||||
LOG_WARN(
|
LOG_WARN(
|
||||||
"keyboard capturer is nullptr, falling back to SDL keyboard events");
|
"keyboard capturer is nullptr, falling back to Slint keyboard events");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,9 +263,9 @@ int SessionDeviceManager::StartKeyboardCapturer() {
|
|||||||
},
|
},
|
||||||
&owner_);
|
&owner_);
|
||||||
if (hook_ret != 0) {
|
if (hook_ret != 0) {
|
||||||
owner_.keyboard_capturer_uses_sdl_events_ = true;
|
owner_.keyboard_capturer_uses_window_events_ = true;
|
||||||
LOG_WARN(
|
LOG_WARN(
|
||||||
"Start keyboard capturer failed, falling back to SDL keyboard events");
|
"Start keyboard capturer failed, falling back to Slint keyboard events");
|
||||||
} else {
|
} else {
|
||||||
LOG_INFO("Start keyboard capturer with native hook");
|
LOG_INFO("Start keyboard capturer with native hook");
|
||||||
}
|
}
|
||||||
@@ -273,9 +273,9 @@ int SessionDeviceManager::StartKeyboardCapturer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int SessionDeviceManager::StopKeyboardCapturer() {
|
int SessionDeviceManager::StopKeyboardCapturer() {
|
||||||
if (owner_.keyboard_capturer_uses_sdl_events_) {
|
if (owner_.keyboard_capturer_uses_window_events_) {
|
||||||
owner_.keyboard_capturer_uses_sdl_events_ = false;
|
owner_.keyboard_capturer_uses_window_events_ = false;
|
||||||
LOG_INFO("Stop keyboard capturer with SDL keyboard backend");
|
LOG_INFO("Stop keyboard capturer with Slint keyboard backend");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ bool HideDisabledMainWindowZoomButton();
|
|||||||
// fullscreen action with an immediate, single-window fullscreen transition.
|
// fullscreen action with an immediate, single-window fullscreen transition.
|
||||||
bool ConfigureStreamWindowLiveResize();
|
bool ConfigureStreamWindowLiveResize();
|
||||||
|
|
||||||
|
// Returns whether the configured stream window is currently the active AppKit
|
||||||
|
// key window.
|
||||||
|
bool IsStreamWindowActive();
|
||||||
|
|
||||||
// Enters or leaves the stream window's animation-free fullscreen mode.
|
// Enters or leaves the stream window's animation-free fullscreen mode.
|
||||||
bool SetStreamWindowFullscreen(bool fullscreen);
|
bool SetStreamWindowFullscreen(bool fullscreen);
|
||||||
bool IsStreamWindowFullscreen();
|
bool IsStreamWindowFullscreen();
|
||||||
|
|||||||
@@ -146,6 +146,13 @@ bool ConfigureStreamWindowLiveResize() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsStreamWindowActive() {
|
||||||
|
@autoreleasepool {
|
||||||
|
return stream_window != nil && [NSApp isActive] &&
|
||||||
|
[stream_window isKeyWindow];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool SetStreamWindowFullscreen(bool fullscreen) {
|
bool SetStreamWindowFullscreen(bool fullscreen) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
if (stream_window == nil || stream_fullscreen == fullscreen) {
|
if (stream_window == nil || stream_fullscreen == fullscreen) {
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ export component StreamWindow inherits Window {
|
|||||||
callback pointer-input(PointerEventButton, PointerEventKind, length, length);
|
callback pointer-input(PointerEventButton, PointerEventKind, length, length);
|
||||||
callback scroll-input(length, length, length, length);
|
callback scroll-input(length, length, length, length);
|
||||||
callback key-input(string, bool, bool, bool, bool, bool);
|
callback key-input(string, bool, bool, bool, bool, bool);
|
||||||
|
callback keyboard-focus-changed(bool);
|
||||||
|
|
||||||
private property <bool> control-expanded: true;
|
private property <bool> control-expanded: true;
|
||||||
private property <bool> display-menu-open: false;
|
private property <bool> display-menu-open: false;
|
||||||
@@ -486,6 +487,8 @@ export component StreamWindow inherits Window {
|
|||||||
|
|
||||||
input-focus := FocusScope {
|
input-focus := FocusScope {
|
||||||
focus-on-click: true;
|
focus-on-click: true;
|
||||||
|
focus-gained => { root.keyboard-focus-changed(true); }
|
||||||
|
focus-lost => { root.keyboard-focus-changed(false); }
|
||||||
key-pressed(event) => {
|
key-pressed(event) => {
|
||||||
root.key-input(event.text, true, event.modifiers.control, event.modifiers.alt, event.modifiers.shift, event.modifiers.meta);
|
root.key-input(event.text, true, event.modifiers.control, event.modifiers.alt, event.modifiers.shift, event.modifiers.meta);
|
||||||
accept
|
accept
|
||||||
@@ -500,6 +503,9 @@ export component StreamWindow inherits Window {
|
|||||||
&& self.mouse-x <= control.x + control.width
|
&& self.mouse-x <= control.x + control.width
|
||||||
&& self.mouse-y >= control.y
|
&& self.mouse-y >= control.y
|
||||||
&& self.mouse-y <= control.y + control.height;
|
&& self.mouse-y <= control.y + control.height;
|
||||||
|
if event.kind == PointerEventKind.down && !over-control {
|
||||||
|
input-focus.focus();
|
||||||
|
}
|
||||||
if event.kind == PointerEventKind.down
|
if event.kind == PointerEventKind.down
|
||||||
&& event.button == PointerEventButton.left
|
&& event.button == PointerEventButton.left
|
||||||
&& over-control {
|
&& over-control {
|
||||||
|
|||||||
@@ -187,6 +187,26 @@ int main() {
|
|||||||
stream->invoke_toggle_maximize_stream_window();
|
stream->invoke_toggle_maximize_stream_window();
|
||||||
assert(maximize_requested);
|
assert(maximize_requested);
|
||||||
|
|
||||||
|
bool keyboard_focus_changed = false;
|
||||||
|
bool keyboard_input_received = false;
|
||||||
|
stream->on_keyboard_focus_changed(
|
||||||
|
[&](bool focused) { keyboard_focus_changed = focused; });
|
||||||
|
stream->on_key_input(
|
||||||
|
[&](slint::SharedString text, bool pressed, bool, bool, bool, bool) {
|
||||||
|
keyboard_input_received = std::string(text) == "a" && pressed;
|
||||||
|
});
|
||||||
|
stream->window().set_size(
|
||||||
|
slint::LogicalSize(slint::Size<float>{1280.0f, 720.0f}));
|
||||||
|
stream->window().dispatch_pointer_press_event(
|
||||||
|
slint::LogicalPosition(slint::Point<float>{640.0f, 360.0f}),
|
||||||
|
slint::PointerEventButton::Left);
|
||||||
|
stream->window().dispatch_pointer_release_event(
|
||||||
|
slint::LogicalPosition(slint::Point<float>{640.0f, 360.0f}),
|
||||||
|
slint::PointerEventButton::Left);
|
||||||
|
stream->window().dispatch_key_press_event("a");
|
||||||
|
assert(keyboard_focus_changed);
|
||||||
|
assert(keyboard_input_received);
|
||||||
|
|
||||||
crossdesk::ui::FileTransferEntry transfer;
|
crossdesk::ui::FileTransferEntry transfer;
|
||||||
transfer.name = "archive.zip";
|
transfer.name = "archive.zip";
|
||||||
transfer.status = "Sending";
|
transfer.status = "Sending";
|
||||||
|
|||||||
Reference in New Issue
Block a user