diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 4d61e36..de2ba32 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -7,7 +7,8 @@ project(${PROJECT_NAME} LANGUAGES CXX) set(PLUGIN_NAME "windows_taskbar_plugin") add_library(${PLUGIN_NAME} SHARED - "windows_taskbar_plugin.cpp" + "windows_taskbar.cc" + "windows_taskbar_plugin.cc" ) apply_standard_settings(${PLUGIN_NAME}) set_target_properties(${PLUGIN_NAME} PROPERTIES diff --git a/windows/utils.h b/windows/utils.h new file mode 100644 index 0000000..e876d95 --- /dev/null +++ b/windows/utils.h @@ -0,0 +1,29 @@ +/// This file is a part of windows_taskbar +/// (https://github.com/alexmercerind/windows_taskbar). +/// +/// Copyright (c) 2021 & 2022, Hitesh Kumar Saini . +/// All rights reserved. +/// Use of this source code is governed by MIT license that can be found in the +/// LICENSE file. + +#ifndef UTILS_H_ +#define UTILS_H_ + +#include + +static inline std::wstring Utf16FromUtf8(std::string string) { + int size_needed = + ::MultiByteToWideChar(CP_UTF8, 0, string.c_str(), -1, NULL, 0); + if (size_needed == 0) { + return std::wstring(); + } + std::wstring result(size_needed, 0); + int converted_length = ::MultiByteToWideChar(CP_UTF8, 0, string.c_str(), -1, + &result[0], size_needed); + if (converted_length == 0) { + return std::wstring(); + } + return result; +} + +#endif diff --git a/windows/windows_taskbar_plugin.cc b/windows/windows_taskbar_plugin.cc new file mode 100644 index 0000000..9ca612f --- /dev/null +++ b/windows/windows_taskbar_plugin.cc @@ -0,0 +1,206 @@ +/// This file is a part of windows_taskbar +/// (https://github.com/alexmercerind/windows_taskbar). +/// +/// Copyright (c) 2021 & 2022, Hitesh Kumar Saini . +/// All rights reserved. +/// Use of this source code is governed by MIT license that can be found in the +/// LICENSE file. + +#pragma once + +#include "include/windows_taskbar/windows_taskbar_plugin.h" + +#include +#include +#include + +#include "windows_taskbar.h" + +#pragma comment(lib, "comctl32.lib") +#pragma comment( \ + linker, \ + "\"/manifestdependency:type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") + +namespace { + +class WindowsTaskbarPlugin : public flutter::Plugin { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); + + WindowsTaskbarPlugin(flutter::PluginRegistrarWindows* registrar); + + virtual ~WindowsTaskbarPlugin(); + + private: + static constexpr auto kSetProgressMode = "SetProgressMode"; + static constexpr auto kSetProgress = "SetProgress"; + static constexpr auto kSetThumbnailToolbar = "SetThumbnailToolbar"; + static constexpr auto kResetThumbnailToolbar = "ResetThumbnailToolbar"; + static constexpr auto kSetThumbnailTooltip = "SetThumbnailTooltip"; + static constexpr auto kSetFlashTaskbarAppIcon = "SetFlashTaskbarAppIcon"; + static constexpr auto kResetFlashTaskbarAppIcon = "ResetFlashTaskbarAppIcon"; + static constexpr auto kSetOverlayIcon = "SetOverlayIcon"; + static constexpr auto kResetOverlayIcon = "ResetOverlayIcon"; + + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + + std::string GetErrorString(std::string method_name); + + flutter::PluginRegistrarWindows* registrar_; + std::unique_ptr> channel_; + std::unique_ptr windows_taskbar_ = nullptr; +}; + +void WindowsTaskbarPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows* registrar) { + auto plugin = std::make_unique(registrar); + registrar->AddPlugin(std::move(plugin)); +} + +WindowsTaskbarPlugin::WindowsTaskbarPlugin( + flutter::PluginRegistrarWindows* registrar) + : registrar_(registrar) { + channel_ = std::make_unique>( + registrar_->messenger(), "com.alexmercerind/windows_taskbar", + &flutter::StandardMethodCodec::GetInstance()); + channel_->SetMethodCallHandler([this](const auto& call, auto result) { + HandleMethodCall(call, std::move(result)); + }); + registrar_->RegisterTopLevelWindowProcDelegate( + [=](HWND hwnd, UINT message, WPARAM wparam, + LPARAM lparam) -> std::optional { + { + switch (message) { + case WM_COMMAND: { + int const button_id = LOWORD(wparam); + if (button_id >= kMinThumbButtonId && + button_id < kMinThumbButtonId + kMaxThumbButtonCount) { + int32_t index = button_id - kMinThumbButtonId; + channel_->InvokeMethod( + "WM_COMMAND", + std::make_unique(index)); + } + break; + } + default: + break; + } + return std::nullopt; + } + }); +} + +WindowsTaskbarPlugin::~WindowsTaskbarPlugin() {} + +void WindowsTaskbarPlugin::HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result) { + if (windows_taskbar_ == nullptr) { + windows_taskbar_ = std::make_unique( + ::GetAncestor(registrar_->GetView()->GetNativeWindow(), GA_ROOT)); + } + auto arguments = std::get(*method_call.arguments()); + if (method_call.method_name().compare(kSetProgressMode) == 0) { + auto mode = std::get(arguments[flutter::EncodableValue("mode")]); + if (windows_taskbar_->SetProgressMode(mode)) { + result->Success(flutter::EncodableValue(nullptr)); + } else { + result->Error("-1", GetErrorString(kSetProgressMode)); + } + } else if (method_call.method_name().compare(kSetProgress) == 0) { + auto completed = + std::get(arguments[flutter::EncodableValue("completed")]); + auto total = std::get(arguments[flutter::EncodableValue("total")]); + if (windows_taskbar_->SetProgress(completed, total)) { + result->Success(flutter::EncodableValue(nullptr)); + } else { + result->Error("-1", GetErrorString(kSetProgress)); + } + } else if (method_call.method_name().compare(kSetThumbnailToolbar) == 0) { + auto buttons = std::get( + arguments[flutter::EncodableValue("buttons")]); + std::vector thumbnail_toolbar_buttons; + thumbnail_toolbar_buttons.reserve(buttons.size()); + for (auto const& button : buttons) { + auto data = std::get(button); + auto icon = std::get(data[flutter::EncodableValue("icon")]); + auto tooltip = + std::get(data[flutter::EncodableValue("tooltip")]); + auto mode = std::get(data[flutter::EncodableValue("mode")]); + ThumbnailToolbarButton thumbnail_toolbar_button; + thumbnail_toolbar_button.icon = icon; + thumbnail_toolbar_button.tooltip = tooltip; + thumbnail_toolbar_button.mode = mode; + thumbnail_toolbar_buttons.emplace_back(thumbnail_toolbar_button); + } + if (windows_taskbar_->SetThumbnailToolbar(thumbnail_toolbar_buttons)) { + result->Success(flutter::EncodableValue(nullptr)); + } else { + result->Error("-1", GetErrorString(kSetThumbnailToolbar)); + } + } else if (method_call.method_name().compare(kResetThumbnailToolbar) == 0) { + if (windows_taskbar_->ResetThumbnailToolbar()) { + result->Success(flutter::EncodableValue(nullptr)); + } else { + result->Error("-1", GetErrorString(kResetThumbnailToolbar)); + } + } else if (method_call.method_name().compare(kSetThumbnailTooltip) == 0) { + auto tooltip = + std::get(arguments[flutter::EncodableValue("tooltip")]); + if (windows_taskbar_->SetThumbnailTooltip(tooltip)) { + result->Success(flutter::EncodableValue(nullptr)); + } else { + result->Error("-1", GetErrorString(kSetThumbnailTooltip)); + } + } else if (method_call.method_name().compare(kSetFlashTaskbarAppIcon) == 0) { + auto mode = std::get(arguments[flutter::EncodableValue("mode")]); + auto flash_count = + std::get(arguments[flutter::EncodableValue("flashCount")]); + auto timeout = + std::get(arguments[flutter::EncodableValue("timeout")]); + if (windows_taskbar_->SetFlashTaskbarAppIcon(mode, flash_count, timeout)) { + result->Success(flutter::EncodableValue(nullptr)); + } else { + result->Error("-1", GetErrorString(kSetFlashTaskbarAppIcon)); + } + } else if (method_call.method_name().compare(kResetFlashTaskbarAppIcon) == + 0) { + if (windows_taskbar_->ResetFlashTaskbarAppIcon()) { + result->Success(flutter::EncodableValue(nullptr)); + } else { + result->Error("-1", GetErrorString(kResetFlashTaskbarAppIcon)); + } + } else if (method_call.method_name().compare(kSetOverlayIcon) == 0) { + auto icon = + std::get(arguments[flutter::EncodableValue("icon")]); + auto tooltip = + std::get(arguments[flutter::EncodableValue("tooltip")]); + if (windows_taskbar_->SetOverlayIcon(icon, tooltip)) { + result->Success(flutter::EncodableValue(nullptr)); + } else { + result->Error("-1", GetErrorString(kSetOverlayIcon)); + } + } else if (method_call.method_name().compare(kResetOverlayIcon) == 0) { + if (windows_taskbar_->ResetOverlayIcon()) { + result->Success(flutter::EncodableValue(nullptr)); + } else { + result->Error("-1", GetErrorString(kResetOverlayIcon)); + } + } else { + result->NotImplemented(); + } +} +} // namespace + +std::string WindowsTaskbarPlugin::GetErrorString(std::string method_name) { + return "WindowsTaskbar::" + method_name + " call failed."; +} + +void WindowsTaskbarPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + WindowsTaskbarPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +} diff --git a/windows/windows_taskbar_plugin.cpp b/windows/windows_taskbar_plugin.cpp deleted file mode 100644 index c5296f2..0000000 --- a/windows/windows_taskbar_plugin.cpp +++ /dev/null @@ -1,289 +0,0 @@ -/// This file is a part of windows_taskbar -/// (https://github.com/alexmercerind/windows_taskbar). -/// -/// Copyright (c) 2021 & 2022, Hitesh Kumar Saini . -/// All rights reserved. -/// Use of this source code is governed by MIT license that can be found in the -/// LICENSE file. - -#pragma once - -#ifndef UNICODE -#define UNICODE -#endif - -#include "include/windows_taskbar/windows_taskbar_plugin.h" - -#include -#include -#include -#include -#include -#include - -#pragma comment(lib, "comctl32.lib") -#pragma comment( \ - linker, \ - "\"/manifestdependency:type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") - -namespace { - -// Convert string encoded in UTF-8 to wstring. -// Returns an empty std::wstring on failure. -std::wstring Utf16FromUtf8(std::string string) { - int size_needed = - MultiByteToWideChar(CP_UTF8, 0, string.c_str(), -1, NULL, 0); - if (size_needed == 0) { - return std::wstring(); - } - std::wstring wstrTo(size_needed, 0); - int converted_length = MultiByteToWideChar(CP_UTF8, 0, string.c_str(), -1, - &wstrTo[0], size_needed); - if (converted_length == 0) { - return std::wstring(); - } - return wstrTo; -} - -class WindowsTaskbarPlugin : public flutter::Plugin { - public: - static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); - - WindowsTaskbarPlugin(flutter::PluginRegistrarWindows* registrar); - - virtual ~WindowsTaskbarPlugin(); - - private: - static constexpr UINT kBaseThumbnailToolbarButtonId = 40001; - static constexpr UINT kMaximumButtonCount = 7; - static constexpr auto kSetProgressMode = "SetProgressMode"; - static constexpr auto kSetProgress = "SetProgress"; - static constexpr auto kSetThumbnailToolbar = "SetThumbnailToolbar"; - static constexpr auto kSetThumbnailTooltip = "SetThumbnailTooltip"; - static constexpr auto kSetFlashTaskbar = "SetFlashTaskbar"; - static constexpr auto kSetOverlayIcon = "SetOverlayIcon"; - static constexpr auto kResetOverlayIcon = "ResetOverlayIcon"; - - void HandleMethodCall( - const flutter::MethodCall& method_call, - std::unique_ptr> result); - - flutter::PluginRegistrarWindows* registrar_; - std::unique_ptr> channel_; - bool thumb_buttons_added_ = false; -}; - -void WindowsTaskbarPlugin::RegisterWithRegistrar( - flutter::PluginRegistrarWindows* registrar) { - auto plugin = std::make_unique(registrar); - registrar->AddPlugin(std::move(plugin)); -} - -WindowsTaskbarPlugin::WindowsTaskbarPlugin( - flutter::PluginRegistrarWindows* registrar) - : registrar_(registrar) { - channel_ = std::make_unique>( - registrar_->messenger(), "com.alexmercerind/windows_taskbar", - &flutter::StandardMethodCodec::GetInstance()); - channel_->SetMethodCallHandler([this](const auto& call, auto result) { - HandleMethodCall(call, std::move(result)); - }); - registrar_->RegisterTopLevelWindowProcDelegate( - [=](HWND hwnd, UINT message, WPARAM wparam, - LPARAM lparam) -> std::optional { - { - switch (message) { - case WM_COMMAND: { - int const button_id = LOWORD(wparam); - if (button_id > 40000 && - button_id <= 40000 + kMaximumButtonCount) { - int32_t index = button_id - kBaseThumbnailToolbarButtonId; - channel_->InvokeMethod( - "WM_COMMAND", - std::make_unique(index)); - } - break; - } - default: - break; - } - return std::nullopt; - } - }); -} - -WindowsTaskbarPlugin::~WindowsTaskbarPlugin() {} - -void WindowsTaskbarPlugin::HandleMethodCall( - const flutter::MethodCall& method_call, - std::unique_ptr> result) { - auto arguments = std::get(*method_call.arguments()); - if (method_call.method_name().compare(kSetProgressMode) == 0) { - auto mode = std::get(arguments[flutter::EncodableValue("mode")]); - ITaskbarList3* taskbar_list; - HRESULT hr; - hr = ::CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&taskbar_list)); - taskbar_list->SetProgressState( - ::GetAncestor(registrar_->GetView()->GetNativeWindow(), GA_ROOT), - static_cast(mode)); - taskbar_list->Release(); - result->Success(flutter::EncodableValue(nullptr)); - } else if (method_call.method_name().compare(kSetProgress) == 0) { - auto completed = (ULONGLONG)std::get( - arguments[flutter::EncodableValue("completed")]); - auto total = (ULONGLONG)std::get( - arguments[flutter::EncodableValue("total")]); - ITaskbarList3* taskbar_list; - HRESULT hr; - hr = ::CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&taskbar_list)); - taskbar_list->SetProgressValue( - ::GetAncestor(registrar_->GetView()->GetNativeWindow(), GA_ROOT), - completed, total); - taskbar_list->Release(); - result->Success(flutter::EncodableValue(nullptr)); - } else if (method_call.method_name().compare(kSetThumbnailToolbar) == 0) { - auto buttons = std::get( - arguments[flutter::EncodableValue("buttons")]); - ITaskbarList3* taskbar_list; - HRESULT hr; - hr = ::CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&taskbar_list)); - if (SUCCEEDED(hr)) { - hr = taskbar_list->HrInit(); - if (SUCCEEDED(hr)) { - HIMAGELIST image_list = ::ImageList_Create( - ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CXSMICON), - ILC_MASK | ILC_COLOR32, 0, 0); - for (const auto& button : buttons) { - auto data = std::get(button); - auto icon = - std::get(data[flutter::EncodableValue("icon")]); - ::ImageList_AddIcon( - image_list, - (HICON)LoadImage(0, Utf16FromUtf8(icon).c_str(), IMAGE_ICON, - GetSystemMetrics(SM_CXSMICON), - GetSystemMetrics(SM_CXSMICON), - LR_LOADFROMFILE | LR_LOADTRANSPARENT)); - } - if (image_list) { - hr = taskbar_list->ThumbBarSetImageList( - ::GetAncestor(registrar_->GetView()->GetNativeWindow(), GA_ROOT), - image_list); - THUMBBUTTON thumb_buttons[kMaximumButtonCount]; - if (SUCCEEDED(hr)) { - for (UINT i = 0; i < kMaximumButtonCount; i++) { - if (i < buttons.size()) { - auto button = buttons[i]; - auto data = std::get(button); - auto mode = - std::get(data[flutter::EncodableValue("mode")]); - auto tooltip = std::get( - data[flutter::EncodableValue("tooltip")]); - thumb_buttons[i].dwMask = THB_BITMAP | THB_TOOLTIP | THB_FLAGS; - thumb_buttons[i].dwFlags = - (THUMBBUTTONFLAGS)mode | THBF_ENABLED; - thumb_buttons[i].iId = kBaseThumbnailToolbarButtonId + i; - thumb_buttons[i].iBitmap = i; - ::StringCchCopy(thumb_buttons[i].szTip, - ARRAYSIZE(thumb_buttons[i].szTip), - Utf16FromUtf8(tooltip).c_str()); - } else { - thumb_buttons[i].dwMask = THB_FLAGS; - thumb_buttons[i].dwFlags = THBF_HIDDEN; - thumb_buttons[i].iId = kBaseThumbnailToolbarButtonId + i; - } - } - if (!thumb_buttons_added_) { - taskbar_list->ThumbBarAddButtons( - ::GetAncestor(registrar_->GetView()->GetNativeWindow(), - GA_ROOT), - kMaximumButtonCount, thumb_buttons); - thumb_buttons_added_ = true; - } else { - taskbar_list->ThumbBarUpdateButtons( - ::GetAncestor(registrar_->GetView()->GetNativeWindow(), - GA_ROOT), - kMaximumButtonCount, thumb_buttons); - } - ::ImageList_Destroy(image_list); - } - } - } - } - taskbar_list->Release(); - result->Success(flutter::EncodableValue(nullptr)); - } else if (method_call.method_name().compare(kSetThumbnailTooltip) == 0) { - auto tooltip = - std::get(arguments[flutter::EncodableValue("tooltip")]); - ITaskbarList3* taskbar_list; - HRESULT hr; - hr = ::CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&taskbar_list)); - taskbar_list->SetThumbnailTooltip( - ::GetAncestor(registrar_->GetView()->GetNativeWindow(), GA_ROOT), - Utf16FromUtf8(tooltip).c_str()); - taskbar_list->Release(); - result->Success(flutter::EncodableValue(nullptr)); - } else if (method_call.method_name().compare(kSetFlashTaskbar) == 0) { - auto mode = std::get(arguments[flutter::EncodableValue("mode")]); - auto flash_count = - std::get(arguments[flutter::EncodableValue("flashCount")]); - auto timeout = - std::get(arguments[flutter::EncodableValue("timeout")]); - FLASHWINFO flash_info; - flash_info.cbSize = sizeof(flash_info); - flash_info.dwFlags = mode; - flash_info.dwTimeout = timeout; - flash_info.hwnd = - ::GetAncestor(registrar_->GetView()->GetNativeWindow(), GA_ROOT); - flash_info.uCount = flash_count; - ::FlashWindowEx(&flash_info); - } else if (method_call.method_name().compare(kSetOverlayIcon) == 0) { - auto icon = - std::get(arguments[flutter::EncodableValue("icon")]); - auto altTooltip = - std::get(arguments[flutter::EncodableValue("altTooltip")]); - auto image = (HICON)LoadImage( - 0, Utf16FromUtf8(icon).c_str(), IMAGE_ICON, - GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CXSMICON), - LR_LOADFROMFILE | LR_LOADTRANSPARENT); - ITaskbarList3* taskbar_list; - HRESULT hr; - hr = ::CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&taskbar_list)); - if (SUCCEEDED(hr)) { - taskbar_list->SetOverlayIcon( - ::GetAncestor(registrar_->GetView()->GetNativeWindow(), GA_ROOT), - image, - Utf16FromUtf8(altTooltip).c_str()); - } - taskbar_list->Release(); - DestroyIcon(image); - result->Success(flutter::EncodableValue(nullptr)); - } else if (method_call.method_name().compare(kResetOverlayIcon) == 0) { - ITaskbarList3* taskbar_list; - HRESULT hr; - hr = ::CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&taskbar_list)); - if (SUCCEEDED(hr)) { - taskbar_list->SetOverlayIcon( - ::GetAncestor(registrar_->GetView()->GetNativeWindow(), GA_ROOT), - nullptr, - Utf16FromUtf8("").c_str()); - } - taskbar_list->Release(); - result->Success(flutter::EncodableValue(nullptr)); - } else { - result->NotImplemented(); - } -} -} // namespace - -void WindowsTaskbarPluginRegisterWithRegistrar( - FlutterDesktopPluginRegistrarRef registrar) { - WindowsTaskbarPlugin::RegisterWithRegistrar( - flutter::PluginRegistrarManager::GetInstance() - ->GetRegistrar(registrar)); -}