flutter_window.cpp 5.39 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2014 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_window.h"

#include <optional>
8 9
#include <mutex>

10
#include <dwmapi.h>
11 12 13 14
#include <flutter/method_channel.h>
#include <flutter/standard_method_codec.h>

#include "flutter/generated_plugin_registrant.h"
15
#include "utils.h"
16

17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
/// Window attribute that enables dark mode window decorations.
///
/// Redefined in case the developer's machine has a Windows SDK older than
/// version 10.0.22000.0.
/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
#endif

/// Registry key for app theme preference.
///
/// A value of 0 indicates apps should use dark mode. A non-zero or missing
/// value indicates apps should use light mode.
constexpr const wchar_t kGetPreferredBrightnessRegKey[] =
  L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme";

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
FlutterWindow::FlutterWindow(const flutter::DartProject& project)
    : project_(project) {}

FlutterWindow::~FlutterWindow() {}

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());

57 58 59 60 61 62 63 64 65 66
  static std::mutex visible_mutex;
  static bool visible = false;

  flutter_controller_->engine()->SetNextFrameCallback([&]() {
    std::scoped_lock lock(visible_mutex);
    this->Show();
    visible = true;
  });

  // Create a method channel to check the window's visibility.
67 68 69 70 71
  flutter::MethodChannel<> channel(
      flutter_controller_->engine()->messenger(), "tests.flutter.dev/windows_startup_test",
      &flutter::StandardMethodCodec::GetInstance());

  channel.SetMethodCallHandler(
72
    [&](const flutter::MethodCall<>& call,
73
       std::unique_ptr<flutter::MethodResult<>> result) {
74 75 76 77
       std::string method = call.method_name();

      if (method == "isWindowVisible") {
        std::scoped_lock lock(visible_mutex);
78
        result->Success(visible);
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
      } else if (method == "isAppDarkModeEnabled") {
        BOOL enabled;
        HRESULT hr = DwmGetWindowAttribute(GetHandle(),
                                           DWMWA_USE_IMMERSIVE_DARK_MODE,
                                           &enabled, sizeof(enabled));
        if (SUCCEEDED(hr)) {
          result->Success((bool)enabled);
        } else if (hr == E_INVALIDARG) {
          // Fallback if the operating system doesn't support dark mode.
          result->Success(false);
        } else {
          result->Error("error", "Received result handle " + hr);
        }
      } else if (method == "isSystemDarkModeEnabled") {
        DWORD data;
        DWORD data_size = sizeof(data);
        LONG status = RegGetValue(HKEY_CURRENT_USER,
                                  kGetPreferredBrightnessRegKey,
                                  kGetPreferredBrightnessRegValue,
                                  RRF_RT_REG_DWORD, nullptr, &data, &data_size);

        if (status == ERROR_SUCCESS) {
          // Preferred brightness is 0 if dark mode is enabled,
          // otherwise non-zero.
          result->Success(data == 0);
        } else if (status == ERROR_FILE_NOT_FOUND) {
          // Fallback if the operating system doesn't support dark mode.
          result->Success(false);
        } else {
          result->Error("error", "Received status " + status);
        }
110 111 112 113 114 115 116
      } else if (method == "convertString") {
        const flutter::EncodableValue* argument = call.arguments();
        const std::vector<int32_t> code_points = std::get<std::vector<int32_t>>(*argument);
        std::vector<wchar_t> wide_str;
        for (int32_t code_point : code_points) {
          wide_str.push_back((wchar_t)(code_point));
        }
117
        wide_str.push_back((wchar_t)0);
118 119
        const std::string string = Utf8FromUtf16(wide_str.data());
        result->Success(string);
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
      } else {
        result->NotImplemented();
      }
    });

  return true;
}

void FlutterWindow::OnDestroy() {
  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);
}