Skip to content

Commit

Permalink
Merge pull request #38 from mysteriumnetwork/33-conn-status-win
Browse files Browse the repository at this point in the history
Windows real time connection status
  • Loading branch information
tadaskay committed Feb 7, 2024
2 parents 14d9f34 + 9acc0e8 commit 127e03c
Show file tree
Hide file tree
Showing 17 changed files with 382 additions and 45 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## [0.6.0](https://github.com/mysteriumnetwork/wireguard_dart/tree/0.6.0) (2024-02-05)

- android: Update API to match darwin implementation
- windows: Implement connection status streaming via event channel
- windows: minor fixes

[Full Changelog](https://github.com/mysteriumnetwork/wireguard_dart/compare/0.5.0...0.6.0)

## [0.5.0](https://github.com/mysteriumnetwork/wireguard_dart/tree/0.5.0) (2024-01-18)

- darwin: Fix connection status streaming. Two available methods are `.status()` and `.statusStream()`
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ To use this plugin, add `wireguard_dart` as a [dependency in your pubspec.yaml f
- Add [minor] if it has new features
- Otherwise, it's a patch release, don't add anything
- After status checks are passed and PR is approved, merge it
- Changes are automatically released as a new semantic version based on tags in the title
- ~~Changes are automatically released as a new semantic version based on tags in the title~~ Changelog should be provided and committed manually
2 changes: 1 addition & 1 deletion example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ android {

defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.wireguard_dart_example"
applicationId "network.mysterium.wireguard_dart_example"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
minSdkVersion 21
Expand Down
7 changes: 6 additions & 1 deletion example/windows/flutter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake)
# https://github.com/flutter/flutter/issues/57146.
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")

# Set fallback configurations for older versions of the flutter tool.
if (NOT DEFINED FLUTTER_TARGET_PLATFORM)
set(FLUTTER_TARGET_PLATFORM "windows-x64")
endif()

# === Flutter Library ===
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")

Expand Down Expand Up @@ -92,7 +97,7 @@ add_custom_command(
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
windows-x64 $<CONFIG>
${FLUTTER_TARGET_PLATFORM} $<CONFIG>
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
Expand Down
1 change: 1 addition & 0 deletions example/windows/runner/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
# Add dependency libraries and include directories. Add any application-specific
# dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib")
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")

# Run the Flutter tool portions of the build. This must not be removed.
Expand Down
55 changes: 49 additions & 6 deletions example/windows/runner/win32_window.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
#include "win32_window.h"

#include <dwmapi.h>
#include <flutter_windows.h>

#include "resource.h"

namespace {

/// 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

constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";

/// 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";

// The number of Win32Window objects that currently exist.
static int g_active_window_count = 0;

Expand All @@ -31,8 +49,8 @@ void EnableFullDpiSupportIfAvailable(HWND hwnd) {
GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
if (enable_non_client_dpi_scaling != nullptr) {
enable_non_client_dpi_scaling(hwnd);
FreeLibrary(user32_module);
}
FreeLibrary(user32_module);
}

} // namespace
Expand All @@ -42,7 +60,7 @@ class WindowClassRegistrar {
public:
~WindowClassRegistrar() = default;

// Returns the singleton registar instance.
// Returns the singleton registrar instance.
static WindowClassRegistrar* GetInstance() {
if (!instance_) {
instance_ = new WindowClassRegistrar();
Expand Down Expand Up @@ -102,9 +120,9 @@ Win32Window::~Win32Window() {
Destroy();
}

bool Win32Window::CreateAndShow(const std::wstring& title,
const Point& origin,
const Size& size) {
bool Win32Window::Create(const std::wstring& title,
const Point& origin,
const Size& size) {
Destroy();

const wchar_t* window_class =
Expand All @@ -117,7 +135,7 @@ bool Win32Window::CreateAndShow(const std::wstring& title,
double scale_factor = dpi / 96.0;

HWND window = CreateWindow(
window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
window_class, title.c_str(), WS_OVERLAPPEDWINDOW,
Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
Scale(size.width, scale_factor), Scale(size.height, scale_factor),
nullptr, nullptr, GetModuleHandle(nullptr), this);
Expand All @@ -126,9 +144,15 @@ bool Win32Window::CreateAndShow(const std::wstring& title,
return false;
}

UpdateTheme(window);

return OnCreate();
}

bool Win32Window::Show() {
return ShowWindow(window_handle_, SW_SHOWNORMAL);
}

// static
LRESULT CALLBACK Win32Window::WndProc(HWND const window,
UINT const message,
Expand Down Expand Up @@ -188,6 +212,10 @@ Win32Window::MessageHandler(HWND hwnd,
SetFocus(child_content_);
}
return 0;

case WM_DWMCOLORIZATIONCOLORCHANGED:
UpdateTheme(hwnd);
return 0;
}

return DefWindowProc(window_handle_, message, wparam, lparam);
Expand Down Expand Up @@ -243,3 +271,18 @@ bool Win32Window::OnCreate() {
void Win32Window::OnDestroy() {
// No-op; provided for subclasses.
}

void Win32Window::UpdateTheme(HWND const window) {
DWORD light_mode;
DWORD light_mode_size = sizeof(light_mode);
LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,
kGetPreferredBrightnessRegValue,
RRF_RT_REG_DWORD, nullptr, &light_mode,
&light_mode_size);

if (result == ERROR_SUCCESS) {
BOOL enable_dark_mode = light_mode == 0;
DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE,
&enable_dark_mode, sizeof(enable_dark_mode));
}
}
20 changes: 12 additions & 8 deletions example/windows/runner/win32_window.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,16 @@ class Win32Window {
Win32Window();
virtual ~Win32Window();

// Creates and shows a win32 window with |title| and position and size using
// Creates a win32 window with |title| that is positioned and sized using
// |origin| and |size|. New windows are created on the default monitor. Window
// sizes are specified to the OS in physical pixels, hence to ensure a
// consistent size to will treat the width height passed in to this function
// as logical pixels and scale to appropriate for the default monitor. Returns
// true if the window was created successfully.
bool CreateAndShow(const std::wstring& title,
const Point& origin,
const Size& size);
// consistent size this function will scale the inputted width and height as
// as appropriate for the default monitor. The window is invisible until
// |Show| is called. Returns true if the window was created successfully.
bool Create(const std::wstring& title, const Point& origin, const Size& size);

// Show the current window. Returns true if the window was successfully shown.
bool Show();

// Release OS resources associated with window.
void Destroy();
Expand Down Expand Up @@ -76,7 +77,7 @@ class Win32Window {
// OS callback called by message pump. Handles the WM_NCCREATE message which
// is passed when the non-client area is being created and enables automatic
// non-client DPI scaling so that the non-client area automatically
// responsponds to changes in DPI. All other messages are handled by
// responds to changes in DPI. All other messages are handled by
// MessageHandler.
static LRESULT CALLBACK WndProc(HWND const window,
UINT const message,
Expand All @@ -86,6 +87,9 @@ class Win32Window {
// Retrieves a class instance pointer for |window|
static Win32Window* GetThisFromHandle(HWND const window) noexcept;

// Update the window frame's theme to match the system theme.
static void UpdateTheme(HWND const window);

bool quit_on_close_ = false;

// window handle for top level window.
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
flutter_lints: ^3.0.1

flutter:
plugin:
Expand Down
4 changes: 4 additions & 0 deletions windows/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ list(APPEND PLUGIN_SOURCES
"config_writer.h"
"service_control.cpp"
"service_control.h"
"connection_status.h"
"connection_status.cpp"
"connection_status_observer.h"
"connection_status_observer.cpp"
"utils.cpp"
"utils.h"
)
Expand Down
42 changes: 42 additions & 0 deletions windows/connection_status.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include "connection_status.h"

#include <windows.h>

#include <string>

namespace wireguard_dart {

std::string ConnectionStatusToString(const ConnectionStatus status) {
switch (status) {
case ConnectionStatus::connected:
return "connected";
case ConnectionStatus::disconnected:
return "disconnected";
case ConnectionStatus::connecting:
return "connecting";
case ConnectionStatus::disconnecting:
return "disconnecting";
default:
return "unknown";
}
}

ConnectionStatus ConnectionStatusFromWinSvcState(DWORD dwCurrentState) {
switch (dwCurrentState) {
case SERVICE_RUNNING:
return ConnectionStatus::connected;
case SERVICE_STOPPED:
case SERVICE_PAUSED:
return ConnectionStatus::disconnected;
case SERVICE_START_PENDING:
case SERVICE_CONTINUE_PENDING:
return ConnectionStatus::connecting;
case SERVICE_STOP_PENDING:
case SERVICE_PAUSE_PENDING:
return ConnectionStatus::disconnecting;
default:
return ConnectionStatus::unknown;
}
}

} // namespace wireguard_dart
18 changes: 18 additions & 0 deletions windows/connection_status.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef WIREGUARD_DART_CONNECTION_STATUS_H
#define WIREGUARD_DART_CONNECTION_STATUS_H

#include <windows.h>

#include <string>

namespace wireguard_dart {

enum ConnectionStatus { connected, disconnected, connecting, disconnecting, unknown };

std::string ConnectionStatusToString(const ConnectionStatus status);

ConnectionStatus ConnectionStatusFromWinSvcState(DWORD dwCurrentState);

} // namespace wireguard_dart

#endif
94 changes: 94 additions & 0 deletions windows/connection_status_observer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#include "connection_status_observer.h"

#include <winsvc.h>

#include <iostream>
#include <thread>

#include "connection_status.h"

namespace wireguard_dart {

ConnectionStatusObserver::ConnectionStatusObserver() {}

ConnectionStatusObserver::~ConnectionStatusObserver() { Shutdown(); }

void ConnectionStatusObserver::StartObserving(std::wstring service_name) {
if (m_running.load() == true) {
return;
}
watch_thread = std::thread(&ConnectionStatusObserver::StartObservingThreadProc, this, service_name);
}

void ConnectionStatusObserver::StopObserving() { m_watch_thread_stop.store(true); }

void ConnectionStatusObserver::Shutdown() {
m_watch_thread_stop.store(true);
if (watch_thread.joinable()) {
watch_thread.join();
}
}

void ConnectionStatusObserver::StartObservingThreadProc(std::wstring service_name) {
m_running.store(true);
SC_HANDLE service_manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (service_manager == NULL) {
return;
}
SC_HANDLE service = OpenService(service_manager, &service_name[0], SERVICE_QUERY_STATUS | SERVICE_INTERROGATE);
if (service == NULL) {
CloseServiceHandle(service_manager);
return;
}
SERVICE_NOTIFY s_notify = {0};
s_notify.dwVersion = SERVICE_NOTIFY_STATUS_CHANGE;
s_notify.pfnNotifyCallback = &ServiceNotifyCallback;
s_notify.pContext = static_cast<void*>(this);
while (m_watch_thread_stop.load() == false) {
if (NotifyServiceStatusChange(service,
SERVICE_NOTIFY_RUNNING | SERVICE_NOTIFY_START_PENDING | SERVICE_NOTIFY_STOPPED |
SERVICE_NOTIFY_STOP_PENDING,
&s_notify) == ERROR_SUCCESS) {
::SleepEx(INFINITE, true);
} else {
CloseServiceHandle(service);
CloseServiceHandle(service_manager);
break;
}
}
m_running.store(false);
}

void CALLBACK ConnectionStatusObserver::ServiceNotifyCallback(void* ptr) {
SERVICE_NOTIFY* serviceNotify = static_cast<SERVICE_NOTIFY*>(ptr);
ConnectionStatusObserver* instance = static_cast<ConnectionStatusObserver*>(serviceNotify->pContext);

if (!instance || serviceNotify->dwNotificationStatus != ERROR_SUCCESS) {
return;
}

auto service_status = &serviceNotify->ServiceStatus;
auto status = ConnectionStatusFromWinSvcState(service_status->dwCurrentState);

if (instance->sink_) {
instance->sink_->Success(flutter::EncodableValue(ConnectionStatusToString(status)));
}
}

std::unique_ptr<flutter::StreamHandlerError<flutter::EncodableValue>> ConnectionStatusObserver::OnListenInternal(
const flutter::EncodableValue* arguments, std::unique_ptr<flutter::EventSink<flutter::EncodableValue>>&& events) {
sink_ = std::move(events);
// sink_->Success(flutter::EncodableValue(ConnectionStatusToString(ConnectionStatus::disconnected)));
return nullptr;
}

std::unique_ptr<flutter::StreamHandlerError<flutter::EncodableValue>> ConnectionStatusObserver::OnCancelInternal(
const flutter::EncodableValue* arguments) {
if (sink_) {
sink_.reset();
}

return nullptr;
}

} // namespace wireguard_dart
Loading

0 comments on commit 127e03c

Please sign in to comment.