mirror of
https://git.hmsn.ink/flutter/vnc_viewer.git
synced 2026-03-20 00:02:22 +09:00
first
This commit is contained in:
313
windows/runner/flutter_window.cpp
Normal file
313
windows/runner/flutter_window.cpp
Normal file
@@ -0,0 +1,313 @@
|
||||
#include "flutter_window.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <flutter/generated_plugin_registrant.h>
|
||||
#include <flutter/method_channel.h>
|
||||
#include <flutter/standard_method_codec.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <iostream>
|
||||
|
||||
static HHOOK g_hHook = nullptr;
|
||||
|
||||
constexpr wchar_t kFlutterTitleName[] = L"Desktop";
|
||||
|
||||
FlutterWindow* g_flutter_window = nullptr;
|
||||
|
||||
enum KeyState {
|
||||
kNone,
|
||||
kWinPressed,
|
||||
kOtherKeyPressed
|
||||
};
|
||||
|
||||
KeyState g_keyState = kNone;
|
||||
|
||||
bool g_isLeftShiftPressed = false;
|
||||
bool g_isRightShiftPressed = false;
|
||||
|
||||
void UpdateShiftState(UINT vkCode, bool isKeyDown) {
|
||||
if (vkCode == VK_LSHIFT) {
|
||||
g_isLeftShiftPressed = isKeyDown;
|
||||
} else if (vkCode == VK_RSHIFT) {
|
||||
g_isRightShiftPressed = isKeyDown;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsShiftPressed() {
|
||||
return g_isLeftShiftPressed || g_isRightShiftPressed;
|
||||
}
|
||||
|
||||
int GetFlutterKeyId(UINT vkCode) {
|
||||
bool shiftPressed = IsShiftPressed();
|
||||
// std::wcout << L"[GetFlutterKeyId] shiftPressed=" << shiftPressed
|
||||
// << L", vkCode=" << vkCode
|
||||
// << L"\n" << std::flush;
|
||||
|
||||
if(shiftPressed) {
|
||||
if (vkCode >= 0x41 && vkCode <= 0x5A) {
|
||||
return vkCode + 0x20; // 'A' = 0x0041
|
||||
}
|
||||
|
||||
if (vkCode >= 0x61 && vkCode <= 0x7A) {
|
||||
return vkCode - 0x20; // 'a' ~ 'z'
|
||||
}
|
||||
} else {
|
||||
if (vkCode >= 0x41 && vkCode <= 0x5A) {
|
||||
return vkCode; // 'A' ~ 'Z' = 0x0041
|
||||
}
|
||||
|
||||
if (vkCode >= 0x61 && vkCode <= 0x7A) {
|
||||
return vkCode; // 'a' ~ 'z'
|
||||
}
|
||||
}
|
||||
|
||||
if (vkCode >= 0x30 && vkCode <= 0x39) return vkCode; // '0'~'9'
|
||||
|
||||
|
||||
if (vkCode >= 0x60 && vkCode <= 0x69) {
|
||||
return 0xFFB0 + (vkCode - 0x60);
|
||||
}
|
||||
|
||||
if (vkCode >= 0x70 && vkCode <= 0x7B) {
|
||||
return 0xFFBE + (vkCode - 0x70);
|
||||
}
|
||||
|
||||
switch (vkCode) {
|
||||
// Modifier Keys
|
||||
case 0x25: return 0xFF51; // Left
|
||||
case 0x26: return 0xFF52; // Up
|
||||
case 0x27: return 0xFF53; // Right
|
||||
case 0x28: return 0xFF54; // Down
|
||||
|
||||
case 0x08: return 0xFF08; // Backspace
|
||||
case 0x09: return 0xFF09; // Tab
|
||||
case 0x0D: return 0xFF0D; // Enter
|
||||
case 0x1B: return 0xFF1B; // Escape
|
||||
case 0x20: return 0x0020; // Space
|
||||
case 0x2D: return 0xFF63; // Insert
|
||||
case 0x2E: return 0xFFFF; // Delete
|
||||
case 0x24: return 0xFF50; // Home
|
||||
case 0x23: return 0xFF57; // End
|
||||
case 0x21: return 0xFF55; // Page Up
|
||||
case 0x22: return 0xFF56; // Page Down
|
||||
|
||||
case 0xBA: return 0x003B; // Semicolon (;)
|
||||
case 0xBB: return 0x003D; // Equal (=)
|
||||
case 0xBC: return 0x002C; // Comma (,)
|
||||
case 0xBD: return 0x002D; // Minus (-)
|
||||
case 0xBE: return 0x002E; // Period (.)
|
||||
case 0xBF: return 0x002F; // Slash (/)
|
||||
case 0xC0: return 0x0060; // Backquote (`) or ~
|
||||
case 0xDB: return 0x005B; // Open Bracket ([)
|
||||
case 0xDC: return 0x005C; // Backslash (\)
|
||||
case 0xDD: return 0x005D; // Close Bracket (])
|
||||
case 0xDE: return 0x0027; // Quote (')
|
||||
|
||||
case 0xA0: return 0xFFE1; // Left Shift
|
||||
case 0xA1: return 0xFFE2; // Right Shift
|
||||
case 0xA2: return 0xFFE3; // Left Control
|
||||
case 0xA3: return 0xFFE4; // Right Control
|
||||
case 0xA4: return 0xFFE9; // Left Alt
|
||||
case 0xA5: return 0xFFEA; // Right Alt
|
||||
case 0x5B: return 0xFFEB; // Left Meta (Win)
|
||||
case 0x5C: return 0xFFEC; // Right Meta (Win)
|
||||
case 0x5D: return 0xFF67; // Context Menu
|
||||
|
||||
case 0x90: return 0xFF7F; // Num Lock
|
||||
case 0x91: return 0xFF14; // Scroll Lock
|
||||
case 0x14: return 0xFFE5; // Caps Lock
|
||||
|
||||
case 0xAD: return 0xFF12; // Mute
|
||||
case 0xAE: return 0xFF11; // Volume Down
|
||||
case 0xAF: return 0xFF13; // Volume Up
|
||||
case 0xB0: return 0xFF16; // Media Previous
|
||||
case 0xB1: return 0xFF17; // Media Next
|
||||
case 0xB2: return 0xFF14; // Play/Pause
|
||||
case 0xB3: return 0xFF15; // Stop
|
||||
default:
|
||||
return 0x0000; // Unknown
|
||||
}
|
||||
}
|
||||
|
||||
void SendKeyEventToDart(UINT vkCode, bool down) {
|
||||
if (g_flutter_window) {
|
||||
auto messenger = g_flutter_window->GetMessenger();
|
||||
if (messenger) {
|
||||
int flutterKeyId = GetFlutterKeyId(vkCode);
|
||||
if (flutterKeyId == 0) return;
|
||||
auto channel = std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
|
||||
messenger,
|
||||
"com.example.vnc_viewer/keyboard",
|
||||
&flutter::StandardMethodCodec::GetInstance());
|
||||
|
||||
auto args = std::make_unique<flutter::EncodableValue>(
|
||||
flutter::EncodableMap{
|
||||
{"key", flutter::EncodableValue(flutterKeyId)},
|
||||
{"vkCode", flutter::EncodableValue(vkCode)},
|
||||
{"down", flutter::EncodableValue(down)},
|
||||
}
|
||||
);
|
||||
|
||||
// std::wcout << L"[send] Pressed=" << g_keyState
|
||||
// << L", args=" << args
|
||||
// << L"\n" << std::flush;
|
||||
channel->InvokeMethod("onPhysicalKeyEvent", std::move(args));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK LLKeyProc(int nCode, WPARAM w, LPARAM l) {
|
||||
if (nCode == HC_ACTION) {
|
||||
std::string type = "";
|
||||
auto* kbd = reinterpret_cast<KBDLLHOOKSTRUCT*>(l);
|
||||
|
||||
HWND hwndFore = GetForegroundWindow();
|
||||
if (hwndFore == nullptr) return CallNextHookEx(nullptr, nCode, w, l);
|
||||
|
||||
wchar_t titleName[256] = {0};
|
||||
GetWindowText(hwndFore, titleName, 256);
|
||||
|
||||
// std::wcout << L"[g_keyState] Pressed=" << g_keyState
|
||||
// << L", kFlutterTitleName=" << kFlutterTitleName
|
||||
// << L", titleName=" << titleName
|
||||
// << L", compare=" << wcscmp(titleName, kFlutterTitleName)
|
||||
// << L"\n" << std::flush;
|
||||
|
||||
if (wcscmp(titleName, kFlutterTitleName) == 0) {
|
||||
bool isKeyDown = (w == WM_KEYDOWN || w == WM_SYSKEYDOWN);
|
||||
if (kbd->vkCode == VK_LSHIFT || kbd->vkCode == VK_RSHIFT) {
|
||||
UpdateShiftState(kbd->vkCode, isKeyDown);
|
||||
}
|
||||
// std::wcout << L"[g_keyState] Pressed=" << g_keyState
|
||||
// << L", kWinPressed=" << kWinPressed
|
||||
// << L", vkCode=" << kbd->vkCode
|
||||
// << L"\n" << std::flush;
|
||||
|
||||
if(kbd->vkCode == 20 && !isKeyDown) {
|
||||
return 1;
|
||||
}
|
||||
SendKeyEventToDart(kbd->vkCode, isKeyDown);
|
||||
return 1;
|
||||
} else {
|
||||
return CallNextHookEx(nullptr, nCode, w, l);
|
||||
}
|
||||
}
|
||||
return CallNextHookEx(nullptr, nCode, w, l);
|
||||
}
|
||||
|
||||
static void InstallLLHook() {
|
||||
if (!g_hHook) {
|
||||
g_hHook = SetWindowsHookEx(
|
||||
WH_KEYBOARD_LL,
|
||||
LLKeyProc,
|
||||
GetModuleHandle(nullptr),
|
||||
0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static void RemoveLLHook() {
|
||||
if (g_hHook) {
|
||||
UnhookWindowsHookEx(g_hHook);
|
||||
g_hHook = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FlutterWindow::CreateMessageChannel() {
|
||||
if (!flutter_controller_ || !flutter_controller_->engine()) {
|
||||
return;
|
||||
}
|
||||
|
||||
message_channel_ =
|
||||
std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(
|
||||
flutter_controller_->engine()->messenger(),
|
||||
"com.example.vnc_viewer/keyboard",
|
||||
&flutter::StandardMethodCodec::GetInstance());
|
||||
|
||||
message_channel_->SetMethodCallHandler(&FlutterWindow::HandleMethodCall);
|
||||
}
|
||||
|
||||
void FlutterWindow::HandleMethodCall(
|
||||
const flutter::MethodCall<flutter::EncodableValue>& method_call,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
|
||||
result->NotImplemented();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
||||
FlutterWindow::FlutterWindow(const flutter::DartProject& project)
|
||||
: project_(project) {
|
||||
g_flutter_window = this;
|
||||
}
|
||||
|
||||
FlutterWindow::~FlutterWindow() {
|
||||
// RemoveLLHook();
|
||||
}
|
||||
|
||||
bool FlutterWindow::OnCreate() {
|
||||
if (!Win32Window::OnCreate()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RECT frame = GetClientArea();
|
||||
|
||||
// The size here must match the window dimensions to avoid unnecessary surface
|
||||
// creation / destruction in the startup path.
|
||||
flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
|
||||
frame.right - frame.left, frame.bottom - frame.top, project_);
|
||||
// Ensure that basic setup of the controller was successful.
|
||||
if (!flutter_controller_->engine() || !flutter_controller_->view()) {
|
||||
return false;
|
||||
}
|
||||
RegisterPlugins(flutter_controller_->engine());
|
||||
SetChildContent(flutter_controller_->view()->GetNativeWindow());
|
||||
|
||||
flutter_controller_->engine()->SetNextFrameCallback([&]() {
|
||||
this->Show();
|
||||
});
|
||||
|
||||
|
||||
CreateMessageChannel();
|
||||
|
||||
// Flutter can complete the first frame before the "show window" callback is
|
||||
// registered. The following call ensures a frame is pending to ensure the
|
||||
// window is shown. It is a no-op if the first frame hasn't completed yet.
|
||||
flutter_controller_->ForceRedraw();
|
||||
|
||||
InstallLLHook();
|
||||
return true;
|
||||
}
|
||||
|
||||
void FlutterWindow::OnDestroy() {
|
||||
RemoveLLHook();
|
||||
if (flutter_controller_) {
|
||||
flutter_controller_ = nullptr;
|
||||
}
|
||||
|
||||
Win32Window::OnDestroy();
|
||||
}
|
||||
|
||||
LRESULT
|
||||
FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
|
||||
WPARAM const wparam,
|
||||
LPARAM const lparam) noexcept {
|
||||
// Give Flutter, including plugins, an opportunity to handle window messages.
|
||||
if (flutter_controller_) {
|
||||
std::optional<LRESULT> result =
|
||||
flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
|
||||
lparam);
|
||||
if (result) {
|
||||
return *result;
|
||||
}
|
||||
}
|
||||
|
||||
switch (message) {
|
||||
case WM_FONTCHANGE:
|
||||
flutter_controller_->engine()->ReloadSystemFonts();
|
||||
break;
|
||||
}
|
||||
|
||||
return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
|
||||
}
|
||||
Reference in New Issue
Block a user