Jon Salmon
Committed by GitHub

Functional Windows implementation (#96)

* Basic windows implementation

* Added details to of platform assign blocking issue

* Handled response code on state set

* Add additional docs
@@ -40,6 +40,8 @@ dependency_overrides: @@ -40,6 +40,8 @@ dependency_overrides:
40 # current platform implementations. 40 # current platform implementations.
41 wakelock_macos: 41 wakelock_macos:
42 path: ../../wakelock_macos 42 path: ../../wakelock_macos
  43 + wakelock_windows:
  44 + path: ../../wakelock_windows
43 wakelock_platform_interface: 45 wakelock_platform_interface:
44 path: ../../wakelock_platform_interface 46 path: ../../wakelock_platform_interface
45 wakelock_web: 47 wakelock_web:
  1 +flutter/ephemeral/
  2 +
  3 +# Visual Studio user-specific files.
  4 +*.suo
  5 +*.user
  6 +*.userosscache
  7 +*.sln.docstates
  8 +
  9 +# Visual Studio build-related files.
  10 +x64/
  11 +x86/
  12 +
  13 +# Visual Studio cache files
  14 +# files ending in .cache can be ignored
  15 +*.[Cc]ache
  16 +# but keep track of directories ending in .cache
  17 +!*.[Cc]ache/
  1 +cmake_minimum_required(VERSION 3.15)
  2 +project(example LANGUAGES CXX)
  3 +
  4 +set(BINARY_NAME "example")
  5 +
  6 +cmake_policy(SET CMP0063 NEW)
  7 +
  8 +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
  9 +
  10 +# Configure build options.
  11 +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
  12 +if(IS_MULTICONFIG)
  13 + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
  14 + CACHE STRING "" FORCE)
  15 +else()
  16 + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
  17 + set(CMAKE_BUILD_TYPE "Debug" CACHE
  18 + STRING "Flutter build mode" FORCE)
  19 + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
  20 + "Debug" "Profile" "Release")
  21 + endif()
  22 +endif()
  23 +
  24 +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
  25 +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
  26 +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
  27 +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
  28 +
  29 +# Use Unicode for all projects.
  30 +add_definitions(-DUNICODE -D_UNICODE)
  31 +
  32 +# Compilation settings that should be applied to most targets.
  33 +function(APPLY_STANDARD_SETTINGS TARGET)
  34 + target_compile_features(${TARGET} PUBLIC cxx_std_17)
  35 + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
  36 + target_compile_options(${TARGET} PRIVATE /EHsc)
  37 + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
  38 + target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
  39 +endfunction()
  40 +
  41 +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
  42 +
  43 +# Flutter library and tool build rules.
  44 +add_subdirectory(${FLUTTER_MANAGED_DIR})
  45 +
  46 +# Application build
  47 +add_subdirectory("runner")
  48 +
  49 +# Generated plugin build rules, which manage building the plugins and adding
  50 +# them to the application.
  51 +include(flutter/generated_plugins.cmake)
  52 +
  53 +
  54 +# === Installation ===
  55 +# Support files are copied into place next to the executable, so that it can
  56 +# run in place. This is done instead of making a separate bundle (as on Linux)
  57 +# so that building and running from within Visual Studio will work.
  58 +set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>")
  59 +# Make the "install" step default, as it's required to run.
  60 +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
  61 +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
  62 + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
  63 +endif()
  64 +
  65 +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
  66 +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
  67 +
  68 +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
  69 + COMPONENT Runtime)
  70 +
  71 +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
  72 + COMPONENT Runtime)
  73 +
  74 +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
  75 + COMPONENT Runtime)
  76 +
  77 +if(PLUGIN_BUNDLED_LIBRARIES)
  78 + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
  79 + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
  80 + COMPONENT Runtime)
  81 +endif()
  82 +
  83 +# Fully re-copy the assets directory on each build to avoid having stale files
  84 +# from a previous install.
  85 +set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
  86 +install(CODE "
  87 + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
  88 + " COMPONENT Runtime)
  89 +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
  90 + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
  91 +
  92 +# Install the AOT library on non-Debug builds only.
  93 +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
  94 + CONFIGURATIONS Profile;Release
  95 + COMPONENT Runtime)
  1 +cmake_minimum_required(VERSION 3.15)
  2 +
  3 +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
  4 +
  5 +# Configuration provided via flutter tool.
  6 +include(${EPHEMERAL_DIR}/generated_config.cmake)
  7 +
  8 +# TODO: Move the rest of this into files in ephemeral. See
  9 +# https://github.com/flutter/flutter/issues/57146.
  10 +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
  11 +
  12 +# === Flutter Library ===
  13 +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
  14 +
  15 +# Published to parent scope for install step.
  16 +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
  17 +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
  18 +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
  19 +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
  20 +
  21 +list(APPEND FLUTTER_LIBRARY_HEADERS
  22 + "flutter_export.h"
  23 + "flutter_windows.h"
  24 + "flutter_messenger.h"
  25 + "flutter_plugin_registrar.h"
  26 + "flutter_texture_registrar.h"
  27 +)
  28 +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
  29 +add_library(flutter INTERFACE)
  30 +target_include_directories(flutter INTERFACE
  31 + "${EPHEMERAL_DIR}"
  32 +)
  33 +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
  34 +add_dependencies(flutter flutter_assemble)
  35 +
  36 +# === Wrapper ===
  37 +list(APPEND CPP_WRAPPER_SOURCES_CORE
  38 + "core_implementations.cc"
  39 + "standard_codec.cc"
  40 +)
  41 +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
  42 +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
  43 + "plugin_registrar.cc"
  44 +)
  45 +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
  46 +list(APPEND CPP_WRAPPER_SOURCES_APP
  47 + "flutter_engine.cc"
  48 + "flutter_view_controller.cc"
  49 +)
  50 +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
  51 +
  52 +# Wrapper sources needed for a plugin.
  53 +add_library(flutter_wrapper_plugin STATIC
  54 + ${CPP_WRAPPER_SOURCES_CORE}
  55 + ${CPP_WRAPPER_SOURCES_PLUGIN}
  56 +)
  57 +apply_standard_settings(flutter_wrapper_plugin)
  58 +set_target_properties(flutter_wrapper_plugin PROPERTIES
  59 + POSITION_INDEPENDENT_CODE ON)
  60 +set_target_properties(flutter_wrapper_plugin PROPERTIES
  61 + CXX_VISIBILITY_PRESET hidden)
  62 +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
  63 +target_include_directories(flutter_wrapper_plugin PUBLIC
  64 + "${WRAPPER_ROOT}/include"
  65 +)
  66 +add_dependencies(flutter_wrapper_plugin flutter_assemble)
  67 +
  68 +# Wrapper sources needed for the runner.
  69 +add_library(flutter_wrapper_app STATIC
  70 + ${CPP_WRAPPER_SOURCES_CORE}
  71 + ${CPP_WRAPPER_SOURCES_APP}
  72 +)
  73 +apply_standard_settings(flutter_wrapper_app)
  74 +target_link_libraries(flutter_wrapper_app PUBLIC flutter)
  75 +target_include_directories(flutter_wrapper_app PUBLIC
  76 + "${WRAPPER_ROOT}/include"
  77 +)
  78 +add_dependencies(flutter_wrapper_app flutter_assemble)
  79 +
  80 +# === Flutter tool backend ===
  81 +# _phony_ is a non-existent file to force this command to run every time,
  82 +# since currently there's no way to get a full input/output list from the
  83 +# flutter tool.
  84 +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
  85 +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
  86 +add_custom_command(
  87 + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
  88 + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
  89 + ${CPP_WRAPPER_SOURCES_APP}
  90 + ${PHONY_OUTPUT}
  91 + COMMAND ${CMAKE_COMMAND} -E env
  92 + ${FLUTTER_TOOL_ENVIRONMENT}
  93 + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
  94 + windows-x64 $<CONFIG>
  95 + VERBATIM
  96 +)
  97 +add_custom_target(flutter_assemble DEPENDS
  98 + "${FLUTTER_LIBRARY}"
  99 + ${FLUTTER_LIBRARY_HEADERS}
  100 + ${CPP_WRAPPER_SOURCES_CORE}
  101 + ${CPP_WRAPPER_SOURCES_PLUGIN}
  102 + ${CPP_WRAPPER_SOURCES_APP}
  103 +)
  1 +//
  2 +// Generated file. Do not edit.
  3 +//
  4 +
  5 +#ifndef GENERATED_PLUGIN_REGISTRANT_
  6 +#define GENERATED_PLUGIN_REGISTRANT_
  7 +
  8 +#include <flutter/plugin_registry.h>
  9 +
  10 +// Registers Flutter plugins.
  11 +void RegisterPlugins(flutter::PluginRegistry* registry);
  12 +
  13 +#endif // GENERATED_PLUGIN_REGISTRANT_
  1 +#
  2 +# Generated file, do not edit.
  3 +#
  4 +
  5 +list(APPEND FLUTTER_PLUGIN_LIST
  6 +)
  7 +
  8 +set(PLUGIN_BUNDLED_LIBRARIES)
  9 +
  10 +foreach(plugin ${FLUTTER_PLUGIN_LIST})
  11 + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
  12 + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
  13 + list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
  14 + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
  15 +endforeach(plugin)
  1 +cmake_minimum_required(VERSION 3.15)
  2 +project(runner LANGUAGES CXX)
  3 +
  4 +add_executable(${BINARY_NAME} WIN32
  5 + "flutter_window.cpp"
  6 + "main.cpp"
  7 + "run_loop.cpp"
  8 + "utils.cpp"
  9 + "win32_window.cpp"
  10 + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
  11 + "Runner.rc"
  12 + "runner.exe.manifest"
  13 +)
  14 +apply_standard_settings(${BINARY_NAME})
  15 +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
  16 +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
  17 +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
  18 +add_dependencies(${BINARY_NAME} flutter_assemble)
  1 +// Microsoft Visual C++ generated resource script.
  2 +//
  3 +#pragma code_page(65001)
  4 +#include "resource.h"
  5 +
  6 +#define APSTUDIO_READONLY_SYMBOLS
  7 +/////////////////////////////////////////////////////////////////////////////
  8 +//
  9 +// Generated from the TEXTINCLUDE 2 resource.
  10 +//
  11 +#include "winres.h"
  12 +
  13 +/////////////////////////////////////////////////////////////////////////////
  14 +#undef APSTUDIO_READONLY_SYMBOLS
  15 +
  16 +/////////////////////////////////////////////////////////////////////////////
  17 +// English (United States) resources
  18 +
  19 +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
  20 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
  21 +
  22 +#ifdef APSTUDIO_INVOKED
  23 +/////////////////////////////////////////////////////////////////////////////
  24 +//
  25 +// TEXTINCLUDE
  26 +//
  27 +
  28 +1 TEXTINCLUDE
  29 +BEGIN
  30 + "resource.h\0"
  31 +END
  32 +
  33 +2 TEXTINCLUDE
  34 +BEGIN
  35 + "#include ""winres.h""\r\n"
  36 + "\0"
  37 +END
  38 +
  39 +3 TEXTINCLUDE
  40 +BEGIN
  41 + "\r\n"
  42 + "\0"
  43 +END
  44 +
  45 +#endif // APSTUDIO_INVOKED
  46 +
  47 +
  48 +/////////////////////////////////////////////////////////////////////////////
  49 +//
  50 +// Icon
  51 +//
  52 +
  53 +// Icon with lowest ID value placed first to ensure application icon
  54 +// remains consistent on all systems.
  55 +IDI_APP_ICON ICON "resources\\app_icon.ico"
  56 +
  57 +
  58 +/////////////////////////////////////////////////////////////////////////////
  59 +//
  60 +// Version
  61 +//
  62 +
  63 +#ifdef FLUTTER_BUILD_NUMBER
  64 +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
  65 +#else
  66 +#define VERSION_AS_NUMBER 1,0,0
  67 +#endif
  68 +
  69 +#ifdef FLUTTER_BUILD_NAME
  70 +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME
  71 +#else
  72 +#define VERSION_AS_STRING "1.0.0"
  73 +#endif
  74 +
  75 +VS_VERSION_INFO VERSIONINFO
  76 + FILEVERSION VERSION_AS_NUMBER
  77 + PRODUCTVERSION VERSION_AS_NUMBER
  78 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
  79 +#ifdef _DEBUG
  80 + FILEFLAGS VS_FF_DEBUG
  81 +#else
  82 + FILEFLAGS 0x0L
  83 +#endif
  84 + FILEOS VOS__WINDOWS32
  85 + FILETYPE VFT_APP
  86 + FILESUBTYPE 0x0L
  87 +BEGIN
  88 + BLOCK "StringFileInfo"
  89 + BEGIN
  90 + BLOCK "040904e4"
  91 + BEGIN
  92 + VALUE "CompanyName", "creativemaybeno" "\0"
  93 + VALUE "FileDescription", "A new Flutter project." "\0"
  94 + VALUE "FileVersion", VERSION_AS_STRING "\0"
  95 + VALUE "InternalName", "example" "\0"
  96 + VALUE "LegalCopyright", "Copyright (C) 2021 creativemaybeno. All rights reserved." "\0"
  97 + VALUE "OriginalFilename", "example.exe" "\0"
  98 + VALUE "ProductName", "example" "\0"
  99 + VALUE "ProductVersion", VERSION_AS_STRING "\0"
  100 + END
  101 + END
  102 + BLOCK "VarFileInfo"
  103 + BEGIN
  104 + VALUE "Translation", 0x409, 1252
  105 + END
  106 +END
  107 +
  108 +#endif // English (United States) resources
  109 +/////////////////////////////////////////////////////////////////////////////
  110 +
  111 +
  112 +
  113 +#ifndef APSTUDIO_INVOKED
  114 +/////////////////////////////////////////////////////////////////////////////
  115 +//
  116 +// Generated from the TEXTINCLUDE 3 resource.
  117 +//
  118 +
  119 +
  120 +/////////////////////////////////////////////////////////////////////////////
  121 +#endif // not APSTUDIO_INVOKED
  1 +#include "flutter_window.h"
  2 +
  3 +#include <optional>
  4 +
  5 +#include "flutter/generated_plugin_registrant.h"
  6 +
  7 +FlutterWindow::FlutterWindow(RunLoop* run_loop,
  8 + const flutter::DartProject& project)
  9 + : run_loop_(run_loop), project_(project) {}
  10 +
  11 +FlutterWindow::~FlutterWindow() {}
  12 +
  13 +bool FlutterWindow::OnCreate() {
  14 + if (!Win32Window::OnCreate()) {
  15 + return false;
  16 + }
  17 +
  18 + RECT frame = GetClientArea();
  19 +
  20 + // The size here must match the window dimensions to avoid unnecessary surface
  21 + // creation / destruction in the startup path.
  22 + flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
  23 + frame.right - frame.left, frame.bottom - frame.top, project_);
  24 + // Ensure that basic setup of the controller was successful.
  25 + if (!flutter_controller_->engine() || !flutter_controller_->view()) {
  26 + return false;
  27 + }
  28 + RegisterPlugins(flutter_controller_->engine());
  29 + run_loop_->RegisterFlutterInstance(flutter_controller_->engine());
  30 + SetChildContent(flutter_controller_->view()->GetNativeWindow());
  31 + return true;
  32 +}
  33 +
  34 +void FlutterWindow::OnDestroy() {
  35 + if (flutter_controller_) {
  36 + run_loop_->UnregisterFlutterInstance(flutter_controller_->engine());
  37 + flutter_controller_ = nullptr;
  38 + }
  39 +
  40 + Win32Window::OnDestroy();
  41 +}
  42 +
  43 +LRESULT
  44 +FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
  45 + WPARAM const wparam,
  46 + LPARAM const lparam) noexcept {
  47 + // Give Flutter, including plugins, an opporutunity to handle window messages.
  48 + if (flutter_controller_) {
  49 + std::optional<LRESULT> result =
  50 + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
  51 + lparam);
  52 + if (result) {
  53 + return *result;
  54 + }
  55 + }
  56 +
  57 + switch (message) {
  58 + case WM_FONTCHANGE:
  59 + flutter_controller_->engine()->ReloadSystemFonts();
  60 + break;
  61 + }
  62 +
  63 + return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
  64 +}
  1 +#ifndef RUNNER_FLUTTER_WINDOW_H_
  2 +#define RUNNER_FLUTTER_WINDOW_H_
  3 +
  4 +#include <flutter/dart_project.h>
  5 +#include <flutter/flutter_view_controller.h>
  6 +
  7 +#include <memory>
  8 +
  9 +#include "run_loop.h"
  10 +#include "win32_window.h"
  11 +
  12 +// A window that does nothing but host a Flutter view.
  13 +class FlutterWindow : public Win32Window {
  14 + public:
  15 + // Creates a new FlutterWindow driven by the |run_loop|, hosting a
  16 + // Flutter view running |project|.
  17 + explicit FlutterWindow(RunLoop* run_loop,
  18 + const flutter::DartProject& project);
  19 + virtual ~FlutterWindow();
  20 +
  21 + protected:
  22 + // Win32Window:
  23 + bool OnCreate() override;
  24 + void OnDestroy() override;
  25 + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
  26 + LPARAM const lparam) noexcept override;
  27 +
  28 + private:
  29 + // The run loop driving events for this window.
  30 + RunLoop* run_loop_;
  31 +
  32 + // The project to run.
  33 + flutter::DartProject project_;
  34 +
  35 + // The Flutter instance hosted by this window.
  36 + std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
  37 +};
  38 +
  39 +#endif // RUNNER_FLUTTER_WINDOW_H_
  1 +#include <flutter/dart_project.h>
  2 +#include <flutter/flutter_view_controller.h>
  3 +#include <windows.h>
  4 +
  5 +#include "flutter_window.h"
  6 +#include "run_loop.h"
  7 +#include "utils.h"
  8 +
  9 +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
  10 + _In_ wchar_t *command_line, _In_ int show_command) {
  11 + // Attach to console when present (e.g., 'flutter run') or create a
  12 + // new console when running with a debugger.
  13 + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
  14 + CreateAndAttachConsole();
  15 + }
  16 +
  17 + // Initialize COM, so that it is available for use in the library and/or
  18 + // plugins.
  19 + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
  20 +
  21 + RunLoop run_loop;
  22 +
  23 + flutter::DartProject project(L"data");
  24 +
  25 + std::vector<std::string> command_line_arguments =
  26 + GetCommandLineArguments();
  27 +
  28 + project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
  29 +
  30 + FlutterWindow window(&run_loop, project);
  31 + Win32Window::Point origin(10, 10);
  32 + Win32Window::Size size(1280, 720);
  33 + if (!window.CreateAndShow(L"example", origin, size)) {
  34 + return EXIT_FAILURE;
  35 + }
  36 + window.SetQuitOnClose(true);
  37 +
  38 + run_loop.Run();
  39 +
  40 + ::CoUninitialize();
  41 + return EXIT_SUCCESS;
  42 +}
  1 +//{{NO_DEPENDENCIES}}
  2 +// Microsoft Visual C++ generated include file.
  3 +// Used by Runner.rc
  4 +//
  5 +#define IDI_APP_ICON 101
  6 +
  7 +// Next default values for new objects
  8 +//
  9 +#ifdef APSTUDIO_INVOKED
  10 +#ifndef APSTUDIO_READONLY_SYMBOLS
  11 +#define _APS_NEXT_RESOURCE_VALUE 102
  12 +#define _APS_NEXT_COMMAND_VALUE 40001
  13 +#define _APS_NEXT_CONTROL_VALUE 1001
  14 +#define _APS_NEXT_SYMED_VALUE 101
  15 +#endif
  16 +#endif
  1 +#include "run_loop.h"
  2 +
  3 +#include <windows.h>
  4 +
  5 +#include <algorithm>
  6 +
  7 +RunLoop::RunLoop() {}
  8 +
  9 +RunLoop::~RunLoop() {}
  10 +
  11 +void RunLoop::Run() {
  12 + bool keep_running = true;
  13 + TimePoint next_flutter_event_time = TimePoint::clock::now();
  14 + while (keep_running) {
  15 + std::chrono::nanoseconds wait_duration =
  16 + std::max(std::chrono::nanoseconds(0),
  17 + next_flutter_event_time - TimePoint::clock::now());
  18 + ::MsgWaitForMultipleObjects(
  19 + 0, nullptr, FALSE, static_cast<DWORD>(wait_duration.count() / 1000),
  20 + QS_ALLINPUT);
  21 + bool processed_events = false;
  22 + MSG message;
  23 + // All pending Windows messages must be processed; MsgWaitForMultipleObjects
  24 + // won't return again for items left in the queue after PeekMessage.
  25 + while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) {
  26 + processed_events = true;
  27 + if (message.message == WM_QUIT) {
  28 + keep_running = false;
  29 + break;
  30 + }
  31 + ::TranslateMessage(&message);
  32 + ::DispatchMessage(&message);
  33 + // Allow Flutter to process messages each time a Windows message is
  34 + // processed, to prevent starvation.
  35 + next_flutter_event_time =
  36 + std::min(next_flutter_event_time, ProcessFlutterMessages());
  37 + }
  38 + // If the PeekMessage loop didn't run, process Flutter messages.
  39 + if (!processed_events) {
  40 + next_flutter_event_time =
  41 + std::min(next_flutter_event_time, ProcessFlutterMessages());
  42 + }
  43 + }
  44 +}
  45 +
  46 +void RunLoop::RegisterFlutterInstance(
  47 + flutter::FlutterEngine* flutter_instance) {
  48 + flutter_instances_.insert(flutter_instance);
  49 +}
  50 +
  51 +void RunLoop::UnregisterFlutterInstance(
  52 + flutter::FlutterEngine* flutter_instance) {
  53 + flutter_instances_.erase(flutter_instance);
  54 +}
  55 +
  56 +RunLoop::TimePoint RunLoop::ProcessFlutterMessages() {
  57 + TimePoint next_event_time = TimePoint::max();
  58 + for (auto instance : flutter_instances_) {
  59 + std::chrono::nanoseconds wait_duration = instance->ProcessMessages();
  60 + if (wait_duration != std::chrono::nanoseconds::max()) {
  61 + next_event_time =
  62 + std::min(next_event_time, TimePoint::clock::now() + wait_duration);
  63 + }
  64 + }
  65 + return next_event_time;
  66 +}
  1 +#ifndef RUNNER_RUN_LOOP_H_
  2 +#define RUNNER_RUN_LOOP_H_
  3 +
  4 +#include <flutter/flutter_engine.h>
  5 +
  6 +#include <chrono>
  7 +#include <set>
  8 +
  9 +// A runloop that will service events for Flutter instances as well
  10 +// as native messages.
  11 +class RunLoop {
  12 + public:
  13 + RunLoop();
  14 + ~RunLoop();
  15 +
  16 + // Prevent copying
  17 + RunLoop(RunLoop const&) = delete;
  18 + RunLoop& operator=(RunLoop const&) = delete;
  19 +
  20 + // Runs the run loop until the application quits.
  21 + void Run();
  22 +
  23 + // Registers the given Flutter instance for event servicing.
  24 + void RegisterFlutterInstance(
  25 + flutter::FlutterEngine* flutter_instance);
  26 +
  27 + // Unregisters the given Flutter instance from event servicing.
  28 + void UnregisterFlutterInstance(
  29 + flutter::FlutterEngine* flutter_instance);
  30 +
  31 + private:
  32 + using TimePoint = std::chrono::steady_clock::time_point;
  33 +
  34 + // Processes all currently pending messages for registered Flutter instances.
  35 + TimePoint ProcessFlutterMessages();
  36 +
  37 + std::set<flutter::FlutterEngine*> flutter_instances_;
  38 +};
  39 +
  40 +#endif // RUNNER_RUN_LOOP_H_
  1 +<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  2 +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  3 + <application xmlns="urn:schemas-microsoft-com:asm.v3">
  4 + <windowsSettings>
  5 + <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
  6 + </windowsSettings>
  7 + </application>
  8 + <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
  9 + <application>
  10 + <!-- Windows 10 -->
  11 + <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
  12 + <!-- Windows 8.1 -->
  13 + <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
  14 + <!-- Windows 8 -->
  15 + <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
  16 + <!-- Windows 7 -->
  17 + <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
  18 + </application>
  19 + </compatibility>
  20 +</assembly>
  1 +#include "utils.h"
  2 +
  3 +#include <flutter_windows.h>
  4 +#include <io.h>
  5 +#include <stdio.h>
  6 +#include <windows.h>
  7 +
  8 +#include <iostream>
  9 +
  10 +void CreateAndAttachConsole() {
  11 + if (::AllocConsole()) {
  12 + FILE *unused;
  13 + if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
  14 + _dup2(_fileno(stdout), 1);
  15 + }
  16 + if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
  17 + _dup2(_fileno(stdout), 2);
  18 + }
  19 + std::ios::sync_with_stdio();
  20 + FlutterDesktopResyncOutputStreams();
  21 + }
  22 +}
  23 +
  24 +std::vector<std::string> GetCommandLineArguments() {
  25 + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
  26 + int argc;
  27 + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
  28 + if (argv == nullptr) {
  29 + return std::vector<std::string>();
  30 + }
  31 +
  32 + std::vector<std::string> command_line_arguments;
  33 +
  34 + // Skip the first argument as it's the binary name.
  35 + for (int i = 1; i < argc; i++) {
  36 + command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
  37 + }
  38 +
  39 + ::LocalFree(argv);
  40 +
  41 + return command_line_arguments;
  42 +}
  43 +
  44 +std::string Utf8FromUtf16(const wchar_t* utf16_string) {
  45 + if (utf16_string == nullptr) {
  46 + return std::string();
  47 + }
  48 + int target_length = ::WideCharToMultiByte(
  49 + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
  50 + -1, nullptr, 0, nullptr, nullptr);
  51 + if (target_length == 0) {
  52 + return std::string();
  53 + }
  54 + std::string utf8_string;
  55 + utf8_string.resize(target_length);
  56 + int converted_length = ::WideCharToMultiByte(
  57 + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
  58 + -1, utf8_string.data(),
  59 + target_length, nullptr, nullptr);
  60 + if (converted_length == 0) {
  61 + return std::string();
  62 + }
  63 + return utf8_string;
  64 +}
  1 +#ifndef RUNNER_UTILS_H_
  2 +#define RUNNER_UTILS_H_
  3 +
  4 +#include <string>
  5 +#include <vector>
  6 +
  7 +// Creates a console for the process, and redirects stdout and stderr to
  8 +// it for both the runner and the Flutter library.
  9 +void CreateAndAttachConsole();
  10 +
  11 +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
  12 +// encoded in UTF-8. Returns an empty std::string on failure.
  13 +std::string Utf8FromUtf16(const wchar_t* utf16_string);
  14 +
  15 +// Gets the command line arguments passed in as a std::vector<std::string>,
  16 +// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
  17 +std::vector<std::string> GetCommandLineArguments();
  18 +
  19 +#endif // RUNNER_UTILS_H_
  1 +#include "win32_window.h"
  2 +
  3 +#include <flutter_windows.h>
  4 +
  5 +#include "resource.h"
  6 +
  7 +namespace {
  8 +
  9 +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
  10 +
  11 +// The number of Win32Window objects that currently exist.
  12 +static int g_active_window_count = 0;
  13 +
  14 +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
  15 +
  16 +// Scale helper to convert logical scaler values to physical using passed in
  17 +// scale factor
  18 +int Scale(int source, double scale_factor) {
  19 + return static_cast<int>(source * scale_factor);
  20 +}
  21 +
  22 +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
  23 +// This API is only needed for PerMonitor V1 awareness mode.
  24 +void EnableFullDpiSupportIfAvailable(HWND hwnd) {
  25 + HMODULE user32_module = LoadLibraryA("User32.dll");
  26 + if (!user32_module) {
  27 + return;
  28 + }
  29 + auto enable_non_client_dpi_scaling =
  30 + reinterpret_cast<EnableNonClientDpiScaling*>(
  31 + GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
  32 + if (enable_non_client_dpi_scaling != nullptr) {
  33 + enable_non_client_dpi_scaling(hwnd);
  34 + FreeLibrary(user32_module);
  35 + }
  36 +}
  37 +
  38 +} // namespace
  39 +
  40 +// Manages the Win32Window's window class registration.
  41 +class WindowClassRegistrar {
  42 + public:
  43 + ~WindowClassRegistrar() = default;
  44 +
  45 + // Returns the singleton registar instance.
  46 + static WindowClassRegistrar* GetInstance() {
  47 + if (!instance_) {
  48 + instance_ = new WindowClassRegistrar();
  49 + }
  50 + return instance_;
  51 + }
  52 +
  53 + // Returns the name of the window class, registering the class if it hasn't
  54 + // previously been registered.
  55 + const wchar_t* GetWindowClass();
  56 +
  57 + // Unregisters the window class. Should only be called if there are no
  58 + // instances of the window.
  59 + void UnregisterWindowClass();
  60 +
  61 + private:
  62 + WindowClassRegistrar() = default;
  63 +
  64 + static WindowClassRegistrar* instance_;
  65 +
  66 + bool class_registered_ = false;
  67 +};
  68 +
  69 +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
  70 +
  71 +const wchar_t* WindowClassRegistrar::GetWindowClass() {
  72 + if (!class_registered_) {
  73 + WNDCLASS window_class{};
  74 + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
  75 + window_class.lpszClassName = kWindowClassName;
  76 + window_class.style = CS_HREDRAW | CS_VREDRAW;
  77 + window_class.cbClsExtra = 0;
  78 + window_class.cbWndExtra = 0;
  79 + window_class.hInstance = GetModuleHandle(nullptr);
  80 + window_class.hIcon =
  81 + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
  82 + window_class.hbrBackground = 0;
  83 + window_class.lpszMenuName = nullptr;
  84 + window_class.lpfnWndProc = Win32Window::WndProc;
  85 + RegisterClass(&window_class);
  86 + class_registered_ = true;
  87 + }
  88 + return kWindowClassName;
  89 +}
  90 +
  91 +void WindowClassRegistrar::UnregisterWindowClass() {
  92 + UnregisterClass(kWindowClassName, nullptr);
  93 + class_registered_ = false;
  94 +}
  95 +
  96 +Win32Window::Win32Window() {
  97 + ++g_active_window_count;
  98 +}
  99 +
  100 +Win32Window::~Win32Window() {
  101 + --g_active_window_count;
  102 + Destroy();
  103 +}
  104 +
  105 +bool Win32Window::CreateAndShow(const std::wstring& title,
  106 + const Point& origin,
  107 + const Size& size) {
  108 + Destroy();
  109 +
  110 + const wchar_t* window_class =
  111 + WindowClassRegistrar::GetInstance()->GetWindowClass();
  112 +
  113 + const POINT target_point = {static_cast<LONG>(origin.x),
  114 + static_cast<LONG>(origin.y)};
  115 + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
  116 + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
  117 + double scale_factor = dpi / 96.0;
  118 +
  119 + HWND window = CreateWindow(
  120 + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
  121 + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
  122 + Scale(size.width, scale_factor), Scale(size.height, scale_factor),
  123 + nullptr, nullptr, GetModuleHandle(nullptr), this);
  124 +
  125 + if (!window) {
  126 + return false;
  127 + }
  128 +
  129 + return OnCreate();
  130 +}
  131 +
  132 +// static
  133 +LRESULT CALLBACK Win32Window::WndProc(HWND const window,
  134 + UINT const message,
  135 + WPARAM const wparam,
  136 + LPARAM const lparam) noexcept {
  137 + if (message == WM_NCCREATE) {
  138 + auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
  139 + SetWindowLongPtr(window, GWLP_USERDATA,
  140 + reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
  141 +
  142 + auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
  143 + EnableFullDpiSupportIfAvailable(window);
  144 + that->window_handle_ = window;
  145 + } else if (Win32Window* that = GetThisFromHandle(window)) {
  146 + return that->MessageHandler(window, message, wparam, lparam);
  147 + }
  148 +
  149 + return DefWindowProc(window, message, wparam, lparam);
  150 +}
  151 +
  152 +LRESULT
  153 +Win32Window::MessageHandler(HWND hwnd,
  154 + UINT const message,
  155 + WPARAM const wparam,
  156 + LPARAM const lparam) noexcept {
  157 + switch (message) {
  158 + case WM_DESTROY:
  159 + window_handle_ = nullptr;
  160 + Destroy();
  161 + if (quit_on_close_) {
  162 + PostQuitMessage(0);
  163 + }
  164 + return 0;
  165 +
  166 + case WM_DPICHANGED: {
  167 + auto newRectSize = reinterpret_cast<RECT*>(lparam);
  168 + LONG newWidth = newRectSize->right - newRectSize->left;
  169 + LONG newHeight = newRectSize->bottom - newRectSize->top;
  170 +
  171 + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
  172 + newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
  173 +
  174 + return 0;
  175 + }
  176 + case WM_SIZE: {
  177 + RECT rect = GetClientArea();
  178 + if (child_content_ != nullptr) {
  179 + // Size and position the child window.
  180 + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
  181 + rect.bottom - rect.top, TRUE);
  182 + }
  183 + return 0;
  184 + }
  185 +
  186 + case WM_ACTIVATE:
  187 + if (child_content_ != nullptr) {
  188 + SetFocus(child_content_);
  189 + }
  190 + return 0;
  191 + }
  192 +
  193 + return DefWindowProc(window_handle_, message, wparam, lparam);
  194 +}
  195 +
  196 +void Win32Window::Destroy() {
  197 + OnDestroy();
  198 +
  199 + if (window_handle_) {
  200 + DestroyWindow(window_handle_);
  201 + window_handle_ = nullptr;
  202 + }
  203 + if (g_active_window_count == 0) {
  204 + WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
  205 + }
  206 +}
  207 +
  208 +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
  209 + return reinterpret_cast<Win32Window*>(
  210 + GetWindowLongPtr(window, GWLP_USERDATA));
  211 +}
  212 +
  213 +void Win32Window::SetChildContent(HWND content) {
  214 + child_content_ = content;
  215 + SetParent(content, window_handle_);
  216 + RECT frame = GetClientArea();
  217 +
  218 + MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
  219 + frame.bottom - frame.top, true);
  220 +
  221 + SetFocus(child_content_);
  222 +}
  223 +
  224 +RECT Win32Window::GetClientArea() {
  225 + RECT frame;
  226 + GetClientRect(window_handle_, &frame);
  227 + return frame;
  228 +}
  229 +
  230 +HWND Win32Window::GetHandle() {
  231 + return window_handle_;
  232 +}
  233 +
  234 +void Win32Window::SetQuitOnClose(bool quit_on_close) {
  235 + quit_on_close_ = quit_on_close;
  236 +}
  237 +
  238 +bool Win32Window::OnCreate() {
  239 + // No-op; provided for subclasses.
  240 + return true;
  241 +}
  242 +
  243 +void Win32Window::OnDestroy() {
  244 + // No-op; provided for subclasses.
  245 +}
  1 +#ifndef RUNNER_WIN32_WINDOW_H_
  2 +#define RUNNER_WIN32_WINDOW_H_
  3 +
  4 +#include <windows.h>
  5 +
  6 +#include <functional>
  7 +#include <memory>
  8 +#include <string>
  9 +
  10 +// A class abstraction for a high DPI-aware Win32 Window. Intended to be
  11 +// inherited from by classes that wish to specialize with custom
  12 +// rendering and input handling
  13 +class Win32Window {
  14 + public:
  15 + struct Point {
  16 + unsigned int x;
  17 + unsigned int y;
  18 + Point(unsigned int x, unsigned int y) : x(x), y(y) {}
  19 + };
  20 +
  21 + struct Size {
  22 + unsigned int width;
  23 + unsigned int height;
  24 + Size(unsigned int width, unsigned int height)
  25 + : width(width), height(height) {}
  26 + };
  27 +
  28 + Win32Window();
  29 + virtual ~Win32Window();
  30 +
  31 + // Creates and shows a win32 window with |title| and position and size using
  32 + // |origin| and |size|. New windows are created on the default monitor. Window
  33 + // sizes are specified to the OS in physical pixels, hence to ensure a
  34 + // consistent size to will treat the width height passed in to this function
  35 + // as logical pixels and scale to appropriate for the default monitor. Returns
  36 + // true if the window was created successfully.
  37 + bool CreateAndShow(const std::wstring& title,
  38 + const Point& origin,
  39 + const Size& size);
  40 +
  41 + // Release OS resources associated with window.
  42 + void Destroy();
  43 +
  44 + // Inserts |content| into the window tree.
  45 + void SetChildContent(HWND content);
  46 +
  47 + // Returns the backing Window handle to enable clients to set icon and other
  48 + // window properties. Returns nullptr if the window has been destroyed.
  49 + HWND GetHandle();
  50 +
  51 + // If true, closing this window will quit the application.
  52 + void SetQuitOnClose(bool quit_on_close);
  53 +
  54 + // Return a RECT representing the bounds of the current client area.
  55 + RECT GetClientArea();
  56 +
  57 + protected:
  58 + // Processes and route salient window messages for mouse handling,
  59 + // size change and DPI. Delegates handling of these to member overloads that
  60 + // inheriting classes can handle.
  61 + virtual LRESULT MessageHandler(HWND window,
  62 + UINT const message,
  63 + WPARAM const wparam,
  64 + LPARAM const lparam) noexcept;
  65 +
  66 + // Called when CreateAndShow is called, allowing subclass window-related
  67 + // setup. Subclasses should return false if setup fails.
  68 + virtual bool OnCreate();
  69 +
  70 + // Called when Destroy is called.
  71 + virtual void OnDestroy();
  72 +
  73 + private:
  74 + friend class WindowClassRegistrar;
  75 +
  76 + // OS callback called by message pump. Handles the WM_NCCREATE message which
  77 + // is passed when the non-client area is being created and enables automatic
  78 + // non-client DPI scaling so that the non-client area automatically
  79 + // responsponds to changes in DPI. All other messages are handled by
  80 + // MessageHandler.
  81 + static LRESULT CALLBACK WndProc(HWND const window,
  82 + UINT const message,
  83 + WPARAM const wparam,
  84 + LPARAM const lparam) noexcept;
  85 +
  86 + // Retrieves a class instance pointer for |window|
  87 + static Win32Window* GetThisFromHandle(HWND const window) noexcept;
  88 +
  89 + bool quit_on_close_ = false;
  90 +
  91 + // window handle for top level window.
  92 + HWND window_handle_ = nullptr;
  93 +
  94 + // window handle for hosted content.
  95 + HWND child_content_ = nullptr;
  96 +};
  97 +
  98 +#endif // RUNNER_WIN32_WINDOW_H_
@@ -2,6 +2,7 @@ import 'dart:io'; @@ -2,6 +2,7 @@ import 'dart:io';
2 2
3 import 'package:flutter/foundation.dart'; 3 import 'package:flutter/foundation.dart';
4 import 'package:wakelock_macos/wakelock_macos.dart'; 4 import 'package:wakelock_macos/wakelock_macos.dart';
  5 +import 'package:wakelock_windows/wakelock_windows.dart';
5 import 'package:wakelock_platform_interface/wakelock_platform_interface.dart'; 6 import 'package:wakelock_platform_interface/wakelock_platform_interface.dart';
6 7
7 /// The [WakelockPlatformInterface] that is used by [Wakelock]. 8 /// The [WakelockPlatformInterface] that is used by [Wakelock].
@@ -20,7 +21,10 @@ var wakelockPlatformInstance = !kIsWeb && @@ -20,7 +21,10 @@ var wakelockPlatformInstance = !kIsWeb &&
20 // on web. 21 // on web.
21 Platform.isMacOS 22 Platform.isMacOS
22 ? WakelockMacOS() 23 ? WakelockMacOS()
23 - : WakelockPlatformInterface.instance; 24 + // This doesn't feel like the correct way to assign the windows implementation,
  25 + // but platform channels aren't used due to the win32 package.
  26 + // See this issue for details: https://github.com/flutter/flutter/issues/52267.
  27 + : (!kIsWeb && Platform.isWindows ? WakelockWindows() : WakelockPlatformInterface.instance);
24 28
25 /// Class providing all wakelock functionality using static members. 29 /// Class providing all wakelock functionality using static members.
26 /// 30 ///
1 name: wakelock 1 name: wakelock
2 description: >-2 2 description: >-2
3 Plugin that allows you to keep the device screen awake, i.e. prevent the screen from sleeping on 3 Plugin that allows you to keep the device screen awake, i.e. prevent the screen from sleeping on
4 - Android, iOS, macOS, and web. 4 + Android, iOS, macOS, Windows and web.
5 version: 0.4.0 5 version: 0.4.0
6 homepage: https://github.com/creativecreatorormaybenot/wakelock/tree/master/wakelock 6 homepage: https://github.com/creativecreatorormaybenot/wakelock/tree/master/wakelock
7 7
@@ -16,6 +16,7 @@ dependencies: @@ -16,6 +16,7 @@ dependencies:
16 meta: ^1.2.0 16 meta: ^1.2.0
17 17
18 wakelock_macos: ^0.1.0 18 wakelock_macos: ^0.1.0
  19 + wakelock_windows: ^0.1.0
19 wakelock_platform_interface: ^0.2.0 20 wakelock_platform_interface: ^0.2.0
20 wakelock_web: ^0.2.0 21 wakelock_web: ^0.2.0
21 22
@@ -36,5 +37,7 @@ flutter: @@ -36,5 +37,7 @@ flutter:
36 pluginClass: WakelockPlugin 37 pluginClass: WakelockPlugin
37 macos: 38 macos:
38 default_package: wakelock_macos 39 default_package: wakelock_macos
  40 + windows:
  41 + default_package: wakelock_windows
39 web: 42 web:
40 default_package: wakelock_web 43 default_package: wakelock_web
  1 +# Miscellaneous
  2 +*.class
  3 +*.log
  4 +*.pyc
  5 +*.swp
  6 +.DS_Store
  7 +.atom/
  8 +.buildlog/
  9 +.history
  10 +.svn/
  11 +
  12 +# IntelliJ related
  13 +*.iml
  14 +*.ipr
  15 +*.iws
  16 +.idea/
  17 +
  18 +# The .vscode folder contains launch configuration and tasks you configure in
  19 +# VS Code which you may wish to be included in version control, so this line
  20 +# is commented out by default.
  21 +#.vscode/
  22 +
  23 +# Flutter/Dart/Pub related
  24 +**/doc/api/
  25 +.dart_tool/
  26 +.flutter-plugins
  27 +.flutter-plugins-dependencies
  28 +.packages
  29 +.pub-cache/
  30 +.pub/
  31 +build/
  32 +
  33 +# Android related
  34 +**/android/**/gradle-wrapper.jar
  35 +**/android/.gradle
  36 +**/android/captures/
  37 +**/android/gradlew
  38 +**/android/gradlew.bat
  39 +**/android/local.properties
  40 +**/android/**/GeneratedPluginRegistrant.java
  41 +
  42 +# iOS/XCode related
  43 +**/ios/**/*.mode1v3
  44 +**/ios/**/*.mode2v3
  45 +**/ios/**/*.moved-aside
  46 +**/ios/**/*.pbxuser
  47 +**/ios/**/*.perspectivev3
  48 +**/ios/**/*sync/
  49 +**/ios/**/.sconsign.dblite
  50 +**/ios/**/.tags*
  51 +**/ios/**/.vagrant/
  52 +**/ios/**/DerivedData/
  53 +**/ios/**/Icon?
  54 +**/ios/**/Pods/
  55 +**/ios/**/.symlinks/
  56 +**/ios/**/profile
  57 +**/ios/**/xcuserdata
  58 +**/ios/.generated/
  59 +**/ios/Flutter/App.framework
  60 +**/ios/Flutter/Flutter.framework
  61 +**/ios/Flutter/Flutter.podspec
  62 +**/ios/Flutter/Generated.xcconfig
  63 +**/ios/Flutter/app.flx
  64 +**/ios/Flutter/app.zip
  65 +**/ios/Flutter/flutter_assets/
  66 +**/ios/Flutter/flutter_export_environment.sh
  67 +**/ios/ServiceDefinitions.json
  68 +**/ios/Runner/GeneratedPluginRegistrant.*
  69 +
  70 +# Exceptions to above rules.
  71 +!**/ios/**/default.mode1v3
  72 +!**/ios/**/default.mode2v3
  73 +!**/ios/**/default.pbxuser
  74 +!**/ios/**/default.perspectivev3
  1 +## [0.1.0]
  2 +
  3 +* Initial release
  1 +BSD 3-Clause License
  2 +
  3 +Copyright (c) 2020-2021, creativecreatorormaybenot
  4 +All rights reserved.
  5 +
  6 +Redistribution and use in source and binary forms, with or without
  7 +modification, are permitted provided that the following conditions are met:
  8 +
  9 +1. Redistributions of source code must retain the above copyright notice, this
  10 +list of conditions and the following disclaimer.
  11 +
  12 +2. Redistributions in binary form must reproduce the above copyright notice,
  13 +this list of conditions and the following disclaimer in the documentation
  14 +and/or other materials provided with the distribution.
  15 +
  16 +3. Neither the name of the copyright holder nor the names of its
  17 +contributors may be used to endorse or promote products derived from
  18 +this software without specific prior written permission.
  19 +
  20 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  21 +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22 +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  23 +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  24 +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  25 +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  26 +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  27 +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  28 +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  1 +# wakelock_windows
  2 +
  3 +Windows platform implementation of the `wakelock_platform_interface` for the
  4 +[wakelock plugin][wakelock GitHub].
  5 +
  6 +## Getting started
  7 +
  8 +If you want to use the wakelock plugin on Windows, see the [main `wakelock` plugin package](https://pub.dev/packages/wakelock).
  9 +
  10 +## Implementation
  11 +
  12 +Note that the implementation does not use a `MethodChannel` implementation as relies on the win32 package.
  1 +include: package:pedantic/analysis_options.yaml
  2 +
  3 +linter:
  4 + rules:
  5 + - public_member_api_docs
  6 +
  7 +analyzer:
  8 + errors:
  9 + public_member_api_docs: warning
  1 +import 'dart:async';
  2 +
  3 +import 'package:wakelock_platform_interface/wakelock_platform_interface.dart';
  4 +import 'package:win32/win32.dart';
  5 +
  6 +const _ES_CONTINUOUS = 0x80000000;
  7 +const _ES_DISPLAY_REQUIRED = 0x00000002;
  8 +
  9 +
  10 +/// The windows implementation of the [WakelockPlatformInterface].
  11 +///
  12 +/// This class implements the `wakelock` plugin functionality for windows using
  13 +/// SetThreadExecutionState from win32 api.
  14 +///
  15 +/// https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setthreadexecutionstate#parameters
  16 +///
  17 +class WakelockWindows extends WakelockPlatformInterface {
  18 + bool _isEnabled = false;
  19 +
  20 + @override
  21 + Future<void> toggle({required bool enable}) async {
  22 + int response;
  23 + if (enable) {
  24 + response = SetThreadExecutionState(_ES_CONTINUOUS | _ES_DISPLAY_REQUIRED);
  25 + } else {
  26 + response = SetThreadExecutionState(_ES_CONTINUOUS);
  27 + }
  28 +
  29 + // SetThreadExecutionState returns 0 if the operation failed
  30 + if (response != 0) {
  31 + _isEnabled = enable;
  32 + }
  33 + }
  34 +
  35 + @override
  36 + Future<bool> get enabled async => _isEnabled;
  37 +
  38 + @override
  39 + bool get isMock => false;
  40 +}
  1 +name: wakelock_windows
  2 +description: Windows platform implementation of the wakelock_platform_interface for the wakelock plugin.
  3 +version: 0.1.0
  4 +
  5 +environment:
  6 + sdk: ">=2.12.0 <3.0.0"
  7 + flutter: ">=2.0.0"
  8 +
  9 +dependencies:
  10 + flutter:
  11 + sdk: flutter
  12 + wakelock_platform_interface: ^0.2.0
  13 + win32: ^2.0.0
  14 +
  15 +dev_dependencies:
  16 + flutter_test:
  17 + sdk: flutter
  18 +
  19 + pedantic: ^1.10.0