Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows real time connection status #38

Merged
merged 10 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading