blob: 968744209f2148bb1f8ea49aa3108e927566ea4b [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/shell/platform/windows/flutter_window.h"
#include <WinUser.h>
#include <dwmapi.h>
#include <chrono>
#include <map>
#include "flutter/fml/logging.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/windows/flutter_windows_engine.h"
#include "flutter/shell/platform/windows/flutter_windows_view.h"
namespace flutter {
namespace {
// The Windows DPI system is based on this
// constant for machines running at 100% scaling.
constexpr int base_dpi = 96;
// Maps a Flutter cursor name to an HCURSOR.
//
// Returns the arrow cursor for unknown constants.
//
// This map must be kept in sync with Flutter framework's
// services/mouse_cursor.dart.
static HCURSOR GetCursorByName(const std::string& cursor_name) {
static auto* cursors = new std::map<std::string, const wchar_t*>{
{"allScroll", IDC_SIZEALL},
{"basic", IDC_ARROW},
{"click", IDC_HAND},
{"forbidden", IDC_NO},
{"help", IDC_HELP},
{"move", IDC_SIZEALL},
{"none", nullptr},
{"noDrop", IDC_NO},
{"precise", IDC_CROSS},
{"progress", IDC_APPSTARTING},
{"text", IDC_IBEAM},
{"resizeColumn", IDC_SIZEWE},
{"resizeDown", IDC_SIZENS},
{"resizeDownLeft", IDC_SIZENESW},
{"resizeDownRight", IDC_SIZENWSE},
{"resizeLeft", IDC_SIZEWE},
{"resizeLeftRight", IDC_SIZEWE},
{"resizeRight", IDC_SIZEWE},
{"resizeRow", IDC_SIZENS},
{"resizeUp", IDC_SIZENS},
{"resizeUpDown", IDC_SIZENS},
{"resizeUpLeft", IDC_SIZENWSE},
{"resizeUpRight", IDC_SIZENESW},
{"resizeUpLeftDownRight", IDC_SIZENWSE},
{"resizeUpRightDownLeft", IDC_SIZENESW},
{"wait", IDC_WAIT},
};
const wchar_t* idc_name = IDC_ARROW;
auto it = cursors->find(cursor_name);
if (it != cursors->end()) {
idc_name = it->second;
}
return ::LoadCursor(nullptr, idc_name);
}
} // namespace
FlutterWindow::FlutterWindow(int width, int height)
: binding_handler_delegate_(nullptr) {
Window::InitializeChild("FLUTTERVIEW", width, height);
current_cursor_ = ::LoadCursor(nullptr, IDC_ARROW);
}
FlutterWindow::~FlutterWindow() {}
void FlutterWindow::SetView(WindowBindingHandlerDelegate* window) {
binding_handler_delegate_ = window;
direct_manipulation_owner_->SetBindingHandlerDelegate(window);
}
WindowsRenderTarget FlutterWindow::GetRenderTarget() {
return WindowsRenderTarget(GetWindowHandle());
}
PlatformWindow FlutterWindow::GetPlatformWindow() {
return GetWindowHandle();
}
float FlutterWindow::GetDpiScale() {
return static_cast<float>(GetCurrentDPI()) / static_cast<float>(base_dpi);
}
bool FlutterWindow::IsVisible() {
return IsWindowVisible(GetWindowHandle());
}
PhysicalWindowBounds FlutterWindow::GetPhysicalWindowBounds() {
return {GetCurrentWidth(), GetCurrentHeight()};
}
void FlutterWindow::UpdateFlutterCursor(const std::string& cursor_name) {
current_cursor_ = GetCursorByName(cursor_name);
}
void FlutterWindow::OnWindowResized() {
// Blocking the raster thread until DWM flushes alleviates glitches where
// previous size surface is stretched over current size view.
DwmFlush();
}
// Translates button codes from Win32 API to FlutterPointerMouseButtons.
static uint64_t ConvertWinButtonToFlutterButton(UINT button) {
switch (button) {
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
return kFlutterPointerButtonMousePrimary;
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
return kFlutterPointerButtonMouseSecondary;
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
return kFlutterPointerButtonMouseMiddle;
case XBUTTON1:
return kFlutterPointerButtonMouseBack;
case XBUTTON2:
return kFlutterPointerButtonMouseForward;
}
FML_LOG(WARNING) << "Mouse button not recognized: " << button;
return 0;
}
void FlutterWindow::OnDpiScale(unsigned int dpi){};
// When DesktopWindow notifies that a WM_Size message has come in
// lets FlutterEngine know about the new size.
void FlutterWindow::OnResize(unsigned int width, unsigned int height) {
if (binding_handler_delegate_ != nullptr) {
binding_handler_delegate_->OnWindowSizeChanged(width, height);
}
}
void FlutterWindow::OnPaint() {
if (binding_handler_delegate_ != nullptr) {
binding_handler_delegate_->OnWindowRepaint();
}
}
void FlutterWindow::OnPointerMove(double x,
double y,
FlutterPointerDeviceKind device_kind,
int32_t device_id) {
binding_handler_delegate_->OnPointerMove(x, y, device_kind, device_id);
}
void FlutterWindow::OnPointerDown(double x,
double y,
FlutterPointerDeviceKind device_kind,
int32_t device_id,
UINT button) {
uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
if (flutter_button != 0) {
binding_handler_delegate_->OnPointerDown(
x, y, device_kind, device_id,
static_cast<FlutterPointerMouseButtons>(flutter_button));
}
}
void FlutterWindow::OnPointerUp(double x,
double y,
FlutterPointerDeviceKind device_kind,
int32_t device_id,
UINT button) {
uint64_t flutter_button = ConvertWinButtonToFlutterButton(button);
if (flutter_button != 0) {
binding_handler_delegate_->OnPointerUp(
x, y, device_kind, device_id,
static_cast<FlutterPointerMouseButtons>(flutter_button));
}
}
void FlutterWindow::OnPointerLeave(double x,
double y,
FlutterPointerDeviceKind device_kind,
int32_t device_id) {
binding_handler_delegate_->OnPointerLeave(x, y, device_kind, device_id);
}
void FlutterWindow::OnSetCursor() {
::SetCursor(current_cursor_);
}
void FlutterWindow::OnText(const std::u16string& text) {
binding_handler_delegate_->OnText(text);
}
void FlutterWindow::OnKey(int key,
int scancode,
int action,
char32_t character,
bool extended,
bool was_down,
KeyEventCallback callback) {
binding_handler_delegate_->OnKey(key, scancode, action, character, extended,
was_down, std::move(callback));
}
void FlutterWindow::OnComposeBegin() {
binding_handler_delegate_->OnComposeBegin();
}
void FlutterWindow::OnComposeCommit() {
binding_handler_delegate_->OnComposeCommit();
}
void FlutterWindow::OnComposeEnd() {
binding_handler_delegate_->OnComposeEnd();
}
void FlutterWindow::OnComposeChange(const std::u16string& text,
int cursor_pos) {
binding_handler_delegate_->OnComposeChange(text, cursor_pos);
}
void FlutterWindow::OnUpdateSemanticsEnabled(bool enabled) {
binding_handler_delegate_->OnUpdateSemanticsEnabled(enabled);
}
void FlutterWindow::OnScroll(double delta_x,
double delta_y,
FlutterPointerDeviceKind device_kind,
int32_t device_id) {
POINT point;
GetCursorPos(&point);
ScreenToClient(GetWindowHandle(), &point);
binding_handler_delegate_->OnScroll(point.x, point.y, delta_x, delta_y,
GetScrollOffsetMultiplier(), device_kind,
device_id);
}
void FlutterWindow::OnCursorRectUpdated(const Rect& rect) {
// Convert the rect from Flutter logical coordinates to device coordinates.
auto scale = GetDpiScale();
Point origin(rect.left() * scale, rect.top() * scale);
Size size(rect.width() * scale, rect.height() * scale);
UpdateCursorRect(Rect(origin, size));
}
void FlutterWindow::OnResetImeComposing() {
AbortImeComposing();
}
bool FlutterWindow::OnBitmapSurfaceUpdated(const void* allocation,
size_t row_bytes,
size_t height) {
HDC dc = ::GetDC(GetWindowHandle());
BITMAPINFO bmi = {};
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = row_bytes / 4;
bmi.bmiHeader.biHeight = -height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = 0;
int ret = SetDIBitsToDevice(dc, 0, 0, row_bytes / 4, height, 0, 0, 0, height,
allocation, &bmi, DIB_RGB_COLORS);
::ReleaseDC(GetWindowHandle(), dc);
return ret != 0;
}
gfx::NativeViewAccessible FlutterWindow::GetNativeViewAccessible() {
return binding_handler_delegate_->GetNativeViewAccessible();
}
PointerLocation FlutterWindow::GetPrimaryPointerLocation() {
POINT point;
GetCursorPos(&point);
ScreenToClient(GetWindowHandle(), &point);
return {(size_t)point.x, (size_t)point.y};
}
void FlutterWindow::OnThemeChange() {
binding_handler_delegate_->UpdateHighContrastEnabled(
GetHighContrastEnabled());
}
void FlutterWindow::SendInitialAccessibilityFeatures() {
OnThemeChange();
}
} // namespace flutter