Initial code for using winrt headers

This commit is contained in:
LAGonauta 2021-03-26 08:22:55 -03:00
parent 915dd4c738
commit 95051d304a
19 changed files with 422 additions and 154 deletions

View file

@ -1,7 +1,4 @@
set(GETTEXT_PACKAGE "dino-windows-notifications")
find_package(Gettext)
include(${GETTEXT_USE_FILE})
gettext_compile(${GETTEXT_PACKAGE} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../main/po TARGET_NAME ${GETTEXT_PACKAGE}-translations)
project(windows-notification)
find_packages(WINDOWS_NOTIFICATION_PACKAGES REQUIRED
Gee
@ -15,55 +12,54 @@ vala_precompile(WINDOWS_NOTIFICATION_VALA_C
SOURCES
src/plugin.vala
src/register_plugin.vala
src/win_notification_provider.vala
CUSTOM_VAPIS
${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi
${CMAKE_BINARY_DIR}/exports/dino.vapi
${CMAKE_BINARY_DIR}/exports/qlite.vapi
${CMAKE_CURRENT_SOURCE_DIR}/vapi/DinoWinToastLib.vapi
${CMAKE_CURRENT_SOURCE_DIR}/vapi/DinoWinToastTemplate.vapi
${CMAKE_CURRENT_SOURCE_DIR}/vapi/win32.vapi
${CMAKE_CURRENT_SOURCE_DIR}/vapi/shortcutcreator.vapi
PACKAGES
${WINDOWS_NOTIFICATION_PACKAGES}
)
add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/exports/DinoWinToastLib.h"
COMMAND
cp "${CMAKE_CURRENT_SOURCE_DIR}/src/DinoWinToastLib.h" "${CMAKE_BINARY_DIR}/exports/DinoWinToastLib.h"
DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/src/DinoWinToastLib.h"
COMMENT
Copy header file DinoWinToastLib.h
set(WINDOWS_API_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/api/src/win32.cpp
${CMAKE_CURRENT_SOURCE_DIR}/api/src/converter.cpp
${CMAKE_CURRENT_SOURCE_DIR}/api/src/shortcutcreator.cpp
)
add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/exports/DinoWinToastTemplate.h"
COMMAND
cp "${CMAKE_CURRENT_SOURCE_DIR}/src/DinoWinToastTemplate.h" "${CMAKE_BINARY_DIR}/exports/DinoWinToastTemplate.h"
DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/src/DinoWinToastTemplate.h"
COMMENT
Copy header file DinoWinToastTemplate.h
add_library(windows-notification SHARED ${WINDOWS_NOTIFICATION_VALA_C} ${WINDOWS_API_SOURCES})
target_include_directories(windows-notification
PRIVATE
${PROJECT_SOURCE_DIR}/api/include
)
add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/exports/DinoWinToastDllExport.h"
COMMAND
cp "${CMAKE_CURRENT_SOURCE_DIR}/src/DinoWinToastDllExport.h" "${CMAKE_BINARY_DIR}/exports/DinoWinToastDllExport.h"
DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/src/DinoWinToastDllExport.h"
COMMENT
Copy header file DinoWinToastDllExport.h
)
find_library(shlwapi_LIBRARY shlwapi libshlwapi libshlwapi.a HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
if(NOT shlwapi_LIBRARY)
message(FATAL_ERROR "shlwapi library not found")
endif(NOT shlwapi_LIBRARY)
add_definitions(${VALA_CFLAGS} -DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\" -DLOCALE_INSTALL_DIR=\"${LOCALE_INSTALL_DIR}\")
add_library(windows-notification SHARED ${WINDOWS_NOTIFICATION_VALA_C} ${CMAKE_BINARY_DIR}/exports/DinoWinToastLib.h ${CMAKE_BINARY_DIR}/exports/DinoWinToastTemplate.h ${CMAKE_BINARY_DIR}/exports/DinoWinToastDllExport.h)
add_dependencies(omemo ${GETTEXT_PACKAGE}-translations)
find_library(propsys_LIBRARY propsys libpropsys libpropsys.a HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
if(NOT propsys_LIBRARY)
message(FATAL_ERROR "propsys library not found")
endif(NOT propsys_LIBRARY)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(WINTOASTLIB "${CMAKE_CURRENT_SOURCE_DIR}/src/DinoWinToastLib_AMD64.lib")
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(WINTOASTLIB "${CMAKE_CURRENT_SOURCE_DIR}/src/DinoWinToastLib_x86.lib")
endif()
find_library(ntdll_LIBRARY ntdll libntdll libntdll.a HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
if(NOT ntdll_LIBRARY)
message(FATAL_ERROR "ntdll library not found")
endif(NOT ntdll_LIBRARY)
find_library(mincore_LIBRARY mincore libmincore libmincore.a HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
if(NOT mincore_LIBRARY)
message(FATAL_ERROR "mincore library not found")
endif(NOT mincore_LIBRARY)
target_link_libraries(windows-notification libdino ${shlwapi_LIBRARY} ${propsys_LIBRARY} ${ntdll_LIBRARY} ${mincore_LIBRARY} ${WINDOWS_NOTIFICATION_PACKAGES})
target_compile_features(windows-notification PRIVATE cxx_std_20)
# target_compile_options(windows-notification PRIVATE -municode -DUNICODE -fcoroutines -iquote ../../include/winrt/yolort_impl)
target_compile_options(windows-notification PRIVATE -municode -DUNICODE -fcoroutines)
target_link_libraries(windows-notification libdino ${WINDOWS_NOTIFICATION_PACKAGES} ${WINTOASTLIB})
set_target_properties(windows-notification PROPERTIES PREFIX "")
set_target_properties(windows-notification PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins/)

View file

@ -0,0 +1,18 @@
#pragma once
template<typename T>
struct ComPtr
{
T *p{};
~ComPtr() { if (p != nullptr) p->Release(); }
T &operator*() const { return *p; }
T **operator&() const { return &p; }
T **operator&() { return &p; }
T *operator->() const { return p; }
template<typename U>
HRESULT As( U **const pp ) const
{ return p->QueryInterface(pp); }
};

View file

@ -0,0 +1,7 @@
#pragma once
#include <glib.h>
#include <string>
std::wstring char_to_wstr(const gchar* str);
char* wstr_to_char(const std::wstring& wstr);

View file

@ -0,0 +1,14 @@
#pragma once
#include <glib.h>
#ifdef __cplusplus
extern "C"
{
#endif
gboolean TryCreateShortcut(gchar* aumid);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1 @@
#include "win32.hpp"

View file

@ -0,0 +1,25 @@
#pragma once
#include <glib.h>
#ifdef __cplusplus
#include <string>
#include <array>
#include <optional>
#include <memory>
std::optional<std::wstring> GetCurrentModulePath();
std::optional<std::wstring> GetShortcutPath();
#endif
#ifdef __cplusplus
extern "C"
{
#endif
gboolean SupportsModernNotifications();
gboolean SetAppModelID(gchar* aumid);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,14 @@
#pragma once
#include <glib.h>
#ifdef __cplusplus
extern "C"
{
#endif
gboolean Initialize();
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,53 @@
#include <stringapiset.h>
#include "converter.hpp"
// Convert a wide Unicode string to an UTF8 string
std::string wstr_to_str(const std::wstring& wstr)
{
if(wstr.empty())
{
return std::string();
}
int final_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), nullptr, 0, nullptr, nullptr);
std::string strTo(final_size, 0);
WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), strTo.data(), final_size, nullptr, nullptr);
return strTo;
}
char* wstr_to_char(const std::wstring& wstr)
{
if(wstr.empty())
{
return nullptr;
}
int final_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), nullptr, 0, nullptr, nullptr);
char* strTo = new char[final_size];
WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), strTo, final_size, nullptr, nullptr);
return strTo;
}
// Convert an UTF8 string to a wide Unicode String
std::wstring std_to_wstr(const std::string &str)
{
if(str.empty())
{
return std::wstring();
}
int final_size = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
std::wstring wstrTo(final_size, 0);
MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), wstrTo.data(), final_size);
return wstrTo;
}
std::wstring char_to_wstr(const char* str) // TODO: how to be safe from non-null terminated strings?
{
if(str == nullptr)
{
return std::wstring();
}
int final_size = MultiByteToWideChar(CP_UTF8, 0, str, strlen(str), nullptr, 0);
std::wstring wstrTo(final_size, 0);
MultiByteToWideChar(CP_UTF8, 0, str, strlen(str), wstrTo.data(), final_size);
return wstrTo;
}

View file

@ -0,0 +1,160 @@
#include <shlobj.h>
#include <memory>
#include "shortcutcreator.h"
#include "win32.hpp"
#include "converter.hpp"
#ifdef UNICODE
#define _UNICODE
#endif
#include <tchar.h> // magic
#include <objbase.h> // COM stuff
#include <shlobj.h> // IShellLink
#include <propvarutil.h> // InitPropVariantFromString
#include <propkey.h> // PKEY_AppUserModel_ID
#include <cstdio> // 'cause iostreams are bloat
#include <filesystem>
#include "ComPtr.hpp"
// Not available in MINGW headers for some reason
PSSTDAPI PropVariantToString(_In_ REFPROPVARIANT propvar, _Out_writes_(cch) PWSTR psz, _In_ UINT cch);
int32_t InstallShortcut(const std::wstring& exe_path, const std::wstring& aumid, const std::wstring& shortcut_path)
{
ComPtr<IShellLink> shellLink;
auto hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink));
if (SUCCEEDED(hr))
{
hr = shellLink->SetPath(exe_path.c_str());
if (SUCCEEDED(hr))
{
hr = shellLink->SetArguments(TEXT(""));
if (SUCCEEDED(hr))
{
hr = shellLink->SetWorkingDirectory(exe_path.c_str());
if (SUCCEEDED(hr))
{
ComPtr<IPropertyStore> propertyStore;
hr = shellLink.As(&propertyStore);
if (SUCCEEDED(hr))
{
PROPVARIANT appIdPropVar;
hr = InitPropVariantFromString(aumid.c_str(), &appIdPropVar);
if (SUCCEEDED(hr))
{
hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar);
if (SUCCEEDED(hr))
{
hr = propertyStore->Commit();
if (SUCCEEDED(hr))
{
ComPtr<IPersistFile> persistFile;
hr = shellLink.As(&persistFile);
if (SUCCEEDED(hr))
{
hr = persistFile->Save(shortcut_path.c_str(), TRUE);
}
}
}
PropVariantClear(&appIdPropVar);
}
}
}
}
}
}
return hr;
}
int32_t ValidateShortcut(const std::wstring& shortcut_path, const std::wstring& currentAumid)
{
bool wasChanged = false;
ComPtr<IShellLink> shellLink;
auto hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink));
if (SUCCEEDED(hr))
{
ComPtr<IPersistFile> persistFile;
hr = shellLink.As(&persistFile);
if (SUCCEEDED(hr))
{
hr = persistFile->Load(shortcut_path.c_str(), STGM_READWRITE);
if (SUCCEEDED(hr))
{
ComPtr<IPropertyStore> propertyStore;
hr = shellLink.As(&propertyStore);
if (SUCCEEDED(hr))
{
PROPVARIANT appIdPropVar;
hr = propertyStore->GetValue(PKEY_AppUserModel_ID, &appIdPropVar);
if (SUCCEEDED(hr))
{
std::array<wchar_t, MAX_PATH> AUMI;
hr = PropVariantToString(appIdPropVar, AUMI.data(), AUMI.size());
if (FAILED(hr) || currentAumid != std::wstring(AUMI.data()))
{
// AUMI Changed for the same app, let's update the current value! =)
wasChanged = true;
PropVariantClear(&appIdPropVar);
hr = InitPropVariantFromString(currentAumid.c_str(), &appIdPropVar);
if (SUCCEEDED(hr))
{
hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar);
if (SUCCEEDED(hr))
{
hr = propertyStore->Commit();
if (SUCCEEDED(hr) && SUCCEEDED(persistFile->IsDirty()))
{
hr = persistFile->Save(shortcut_path.c_str(), TRUE);
}
}
}
}
PropVariantClear(&appIdPropVar);
}
}
}
}
}
return hr;
}
int32_t TryCreateShortcutInternal(const std::wstring& aumid)
{
auto exePath = GetCurrentModulePath();
auto shortcutPath = GetShortcutPath();
if (shortcutPath && exePath)
{
auto path = shortcutPath.value() + LR"(\Microsoft\Windows\Start Menu\Programs\Testando.lnk)";
if (!std::filesystem::exists(path))
{
return InstallShortcut(exePath.value(), aumid, path);
}
else
{
return ValidateShortcut(path, aumid);
}
return S_OK;
}
return S_FALSE;
}
extern "C"
{
gboolean TryCreateShortcut(gchar* aumid)
{
auto result = char_to_wstr(aumid);
if (result.empty())
{
return FALSE;
}
return SUCCEEDED(TryCreateShortcutInternal(result));
}
}

View file

@ -0,0 +1,60 @@
#include <windows.h>
#include <shlobj.h>
#include "win32.hpp"
#include "converter.hpp"
std::optional<std::wstring> GetCurrentModulePath()
{
std::array<wchar_t, MAX_PATH> exePath;
auto charWritten = GetModuleFileName(nullptr, exePath.data(), exePath.size());
if (charWritten > 0)
{
return std::wstring(exePath.data());
}
return std::nullopt;
}
std::optional<std::wstring> GetShortcutPath()
{
std::array<wchar_t, MAX_PATH> shortcutPath;
auto charWritten = GetEnvironmentVariable(L"APPDATA", shortcutPath.data(), shortcutPath.size());
if (charWritten > 0)
{
return std::wstring(shortcutPath.data());
}
return std::nullopt;
}
bool SetAppModelIDInternal(const std::wstring& aumid)
{
return SUCCEEDED(SetCurrentProcessExplicitAppUserModelID(aumid.c_str()));
}
extern "C"
{
// Not available in mingw headers, but linking works.
NTSTATUS NTAPI RtlGetVersion(PRTL_OSVERSIONINFOW);
gboolean SupportsModernNotifications()
{
RTL_OSVERSIONINFOW rovi = { 0 };
rovi.dwOSVersionInfoSize = sizeof(rovi);
if (S_OK == RtlGetVersion(&rovi))
{
return rovi.dwMajorVersion > 6;
}
return FALSE;
}
gboolean SetAppModelID(gchar* aumid)
{
auto result = char_to_wstr(aumid);
if (result.empty())
{
return FALSE;
}
return SetAppModelIDInternal(result);
}
}

View file

@ -0,0 +1,2 @@
#include "winrt.h"

View file

@ -1,14 +1,29 @@
using Gee;
using Dino.Entities;
using Win32Api;
using ShortcutCreator;
namespace Dino.Plugins.WindowsNotification {
public class Plugin : RootInterface, Object {
public void registered(Dino.Application app) {
var provider = WindowsNotificationProvider.try_create(app);
if (provider != null) {
app.stream_interactor.get_module(NotificationEvents.IDENTITY).register_notification_provider(provider);
var created = ShortcutCreator.TryCreateShortcut("org.dino.Dino");
if (!created)
{
// log somewhere, return
}
var initialized =
if (!Win32Api.SupportsModernNotifications())
{
// limit types of notifications on template builder
}
// var provider = WindowsNotificationProvider.try_create(app);
// if (provider != null) {
// app.stream_interactor.get_module(NotificationEvents.IDENTITY).register_notification_provider(provider);
// }
}
public void shutdown() {

View file

@ -1,48 +0,0 @@
[CCode (cheader_filename = "DinoWinToastLib.h")]
namespace DinoWinToast {
[CCode (cname = "dinoWinToastLib_Notification_Reason", cprefix = "Reason_")]
public enum Reason {
Activated,
ApplicationHidden,
TimedOut
}
[CCode (cname = "dinoWinToastLib_Notification_Callback_Simple", has_target = true)]
public delegate void NotificationCallbackSimple();
[CCode (cname = "dinoWinToastLib_Notification_Callback_ActivatedWithActionIndex", has_target = true)]
public delegate void NotificationCallbackWithActionIndex(int actionId);
[CCode (cname = "dinoWinToastLib_Notification_Callback_Dismissed", has_target = true)]
public delegate void NotificationCallbackDismissed(Reason reason);
[CCode (cname = "dinoWinToastLib_Notification_Callbacks", free_function = "dinoWinToastLib_DestroyCallbacks")]
[Compact]
public class Callbacks {
[CCode (delegate_target_cname = "activated_context", destroy_notify_cname = "activated_free")]
public NotificationCallbackSimple activated;
[CCode (delegate_target_cname = "activatedWithIndex_context", destroy_notify_cname = "activatedWithIndex_free")]
public NotificationCallbackWithActionIndex activatedWithIndex;
[CCode (delegate_target_cname = "dismissed_context", destroy_notify_cname = "dismissed_free")]
public NotificationCallbackDismissed dismissed;
[CCode (delegate_target_cname = "failed_context", destroy_notify_cname = "failed_free")]
public NotificationCallbackSimple failed;
[CCode (cname = "dinoWinToastLib_NewCallbacks")]
public Callbacks();
}
[CCode (cname = "dinoWinToastLib_Init")]
public int Init();
[CCode (cname = "dinoWinToastLib_ShowMessage")]
public int64 ShowMessage(DinoWinToastTemplate templ, Callbacks callbacks);
[CCode (cname = "dinoWinToastLib_RemoveNotification")]
public bool RemoveNotification(int64 notification_id);
}

View file

@ -1,65 +0,0 @@
[CCode (cheader_filename = "DinoWinToastTemplate.h")]
namespace DinoWinToast {
[CCode (cname = "dino_wintoasttemplate_duration", cprefix = "Duration_")]
public enum Duration {
System,
Short,
Long
}
[CCode (cname = "dino_wintoasttemplate_audiooption", cprefix = "AudioOption_")]
public enum AudioOption {
Default,
Silent,
Loop
}
[CCode (cname = "dino_wintoasttemplate_textfield", cprefix = "TextField_")]
public enum TextField {
FirstLine,
SecondLine,
ThirdLine
}
[CCode (cname = "dino_wintoasttemplate_wintoasttemplatetype", cprefix = "TemplateType_")]
public enum TemplateType {
ImageAndText01,
ImageAndText02,
ImageAndText03,
ImageAndText04,
Text01,
Text02,
Text03,
Text04,
WinToastTemplateTypeCount
}
[CCode (cname="dino_wintoasttemplate", free_function = "dino_wintoasttemplate_destroy")]
[Compact]
public class DinoWinToastTemplate {
[CCode (cname = "dino_wintoasttemplate_new")]
public DinoWinToastTemplate(TemplateType type = TemplateType.ImageAndText02);
[CCode (cname = "dino_wintoasttemplate_setTextField")]
public void setTextField(char* txt, TextField pos);
[CCode (cname = "dino_wintoasttemplate_setImagePath")]
public void setImagePath(char* imgPath);
[CCode (cname = "dino_wintoasttemplate_setAttributionText")]
public void setAttributionText(char* attributionText);
[CCode (cname = "dino_wintoasttemplate_addAction")]
public void addAction(char* label);
[CCode (cname = "dino_wintoasttemplate_setAudioOption")]
public void setAudioOption(AudioOption option);
[CCode (cname = "dino_wintoasttemplate_setDuration")]
public void setDuration(Duration duration);
[CCode (cname = "dino_wintoasttemplate_setExpiration")]
public void setExpiration(int64 millisecondsFromNow);
}
}

View file

@ -0,0 +1,5 @@
[CCode (cheader_filename = "shortcutcreator.h")]
namespace ShortcutCreator {
[CCode (cname = "TryCreateShortcut")]
public bool TryCreateShortcut(string aumid);
}

View file

@ -0,0 +1,6 @@
[CCode (cheader_filename = "win32.h")]
namespace Win32Api {
[CCode (cname = "SupportsModernNotifications")]
public bool SupportsModernNotifications();
}

View file

@ -0,0 +1,5 @@
[CCode (cheader_filename = "winrt.h")]
namespace WinRTApi {
[CCode (cname = "Initialize")]
public bool Initialize();
}