Added full Windows support (#8)
* Windows compatibility Tweaks
* Add experimental windows installer
This nsis script should create a windows installer.
Although the installer worked for the first tests
you should handle it with care and consider it
highly experimental
* Prepare signing
Collected some infos regarding signing
a windows build.
* Revert "Prepare signing"
I copied the files into the wrong folder…
… it's late, sorry.
This reverts commit 7d6b9e7f4c
.
* Prepare signing
Collected some infos regarding signing
a windows build.
* Fix typo in Dino slogan
* Add license to windows installer
* Add startmenu folder with several items
Added a startmenu folder with the following items:
* Dino launcher
* License
* Link to Dino website
* Uninstaller
* Prevent duplicated DLLs
* Add dino logo again
The dino logo for the startmenu was accidentally
no longer included since the last commit.
* Simplify installer script
The current build script already places the files in the right
folder structure so the installer doesn't have to do it itself
* Add german language.
* Add option to install without OpenPGP plugin
* Removed compenent section
This section was only introduced to be able to
disable the OpenPGP plugin as Dino often crashed
on Windows if OpenPGP was not installed but the
plugin enabled.
This is no more necessary as the OpenPGP plugin
is now disabled by default.
* Remove installation type "OpenPGP" support
This is no longer needed (see previous commit)
but was forgotten to remove in the previous
commit.
* Add compression to achieve smaller installer size.
* Add AppID (untested).
* Fix syntax error for setting AppID.
* Windows compatibility Tweaks
* fix build on newest MSYS2
* Do not search for the built-in libraries when compiling with MINGW
* Added _WIN32 define to VALAC on Windows
* Add missing _WIN32
* Add support for OpenPGP on Windows
* Use ShellExecute instead of AppInfo to open files on Windows
* Use slight larger font on Windows so it matches Linux style
Also fixes some fuzzy fonts.
* Fixed some Windows not appearing when opening file
* Set alternate file stream for downloaded files.
* Added information and Dino icon to Windows executable
* Set Windows executable version from PROJECT_VERSION
* Add WIN32 fonts as a plugin
* Every call to CoInitialize() must be balanced
with a call to CoUninitialize()
* Add --export-all-symbols to Windows compilation
* Add implicit link directories to package HINT path on MingW
Instead of blacklisting those libraries
* Do not hardcode GPG path on Windows
* Export all plugin symbols on Windows
* Use Dino.Util.get_content_type also on preview
* Allow 32-bit linking
Win32 apis are __stdcall
* Use last_index_of instead of index_of
* Initial notification support
* Refactor windows-notification plugin
* Clean up
* Use code from Dino.Ui.Util
* Convert C code to Vala
* Add callback support
* Allow null image_path
* Use dynamic linking instead of runtime loading
Also made me notice that the signature of the function with the callback was wrong. Oops.
* Added 32-bit wintoast linker library
* Use VAPI and generate template in-app
* Initial plugin using new notification provider
* Add support for custom actions on notification
* Add notification retraction
* Use list with all notifications
* Rename field
* Fix muc invite and voide request not working
* Do not use GLib to open links in messages
Use ShellExecute
* Add MIT licensed winrt headers
* Initial code for using winrt headers
* Initial callback support
* Initial GObject wrapper for WinRT notifications
Still missing a lot of stuff
* Initial code to allow buttons and text
* Use string_view
* Increase ref on event token
* Add toastnotifier
* Fix string conversion
* Actions can stack
* Remove unity compilation unit
* No need to enable coroutines
* Fields must be created in the private struct
Also change unordered_map to list, we do not need hashing and stuff.
* Add failed and dimissed actions
* Cleanup dismissed actions on toast notification finalizer
* Add template type enum
* Rename enums to better match what Vala expects
* Rename plugin vala file
* Add template getter
* Initial experiments with notification XML building
* Anitial builder
* Initial notification provider using WinRT
Crashes when activating actions, might be related to threads.
* Delegate `activate_action` to UI thread
* Fixed crash with multiple notifications
Sometimes an invalid function pointer was called with an invalid context
* Add comment to builder
* Use async
* Use g_new0 and g_free to generate raw strings
* Valac think that getters are always owned by the struct
* introduce try_invoke -- a logging exception catcher
* stop exceptions from crossing ABI boundary in a few places
* mark exception-safe C entry points as such
* clarify some entry points' names
* make GetCurrentModulePath and GetShortcutPath throw win32 errors
* clarify GetCurrentModulePath's name
* generalize GetShortcutPath into GetEnv
* make GetEnv more robust and not limit length of variables
* change some local functions' signatures
* constify all the things
* rewrite shortcut management code with RAII, error logging and exceptions
It actually works now.
* add restoration of shortcut's target path
* switch to runtime loading of PropVariantToStringAlloc
Now it really should work.
* Add ginvoke to CMakeLists
* Removed unused library on linker
It is loaded dynamically
* Add README.md to Windows notification plugin
* Fix notifications not hiding
* unimplement accidentally implemented wide string overloads of describe_argument
* work around GetEnvironmentVariable not resetting last error
* handle exe paths longer than 259 chars
* move some whitespace around
* use lower-case 0x prefix for hresult code formatting everywhere
* remove an unused include
* make meta-error messages more precise
* handle empty hresult_error message specially
* handle theoretical future failures of wsview_to_char
* fix UB in glib::describe_arguments called with no arguments
Makes failure logging of nullary invokables non-crashy.
* make glib::impl::varstring less explosive
* fiddle with punctuation
* add nullary version of g_try_invoke macro
* generalize glib::try_invoke to any return-by-value type and void
* protect GetTemplateContent callers from exceptions
* rewrite InitApartment and protect callers from (the rest of the) exceptions
Initializing COM by calling `winrt::init_apartment()` would always cause
stack unwinding *in practice*, which is suboptimal at best, and even using
`apartment_type::single_threaded` still would require exception filtering
*just in case*.
* handle empty menu-relative shortcut paths
* move module loading functions out of shortcutcreator.cpp
* work around a (pedantic) format specifier warning
* silence enum stringification warnings by first casting to underlying types
* fix / work around uninitialized fields warnings
* don't use FALSE as a null pointer constant
* replace C-style concurrent initialization of statics
C++ statics are thread-safe as is and are usually implemented more
efficiently. Besides, `volatile` is likely misused here anyway.
* reflow/respace
* stop checking for empty AUMIDs
The downstream code handles them just fine.
* log SetCurrentProcessExplicitAppUserModelID errors
* remove the no-longer-needed -municode compile option
* replace lists with vectors
* init `Callback` completely always
The `token` pointer was left dangerously uninitialized after construction.
* comment out unused arguments [-Wunused-parameter]
* Add support for adaptive Windows 10 notifications
* Add support for inline images to notification
* Allow null header, body, applogo, and image on notification builder
* DelegateToUi must be an owned function
* Prefer primary DirectSound device on Windows
It automatically selects the default device for use,
there is no book keeping necessary and things just work
The primary DirectSound device has a (NULL) guid, making
it wasy to be found.
* Do not allow selection of WASAPI devices
Dino would have to resample it own audio, do more book keeping and
somehow find out manually which is the default device.
* Add initial call notifications
* Use correct generic type for ArrayList
Nullable crashes Dino
* Allow devices with properties and use has_classes
* Remove YoloRT from tree
* Build YoloRT on project build
* Do not generate WinRT headers, just download them on build
* Fix compilation on gcc 11
* define _POSIX_C_SOURCE=1 on windows
Fixes "undefined reference to `localtime_r`" in, e.g., Vala's GLib.Time.local
when building on mingw-w64.
* fix call notifications buttons not working
* no need to ignore wasapi
* Ignore wasapi devices as they do not work well yet
* Removed version from Dino executable
We need a better way to get the version number
* Automatically set PANGOCAIRO_BACKEND to fontconfig on win32
* Fixed using GTK3 instead of GTK4
* Check YoloRT checksum before building
* Fix GPGME
* Added build script for windows
Signed-off-by: Maxim Logaev <maxlogaev@proton.me>
* Added README-WIN64.md
Signed-off-by: Maxim Logaev <maxlogaev@proton.me>
* Fixed dist-install dir
Signed-off-by: Maxim Logaev <maxlogaev@proton.me>
* Removed unnecessary installer files
Signed-off-by: Maxim Logaev <maxlogaev@proton.me>
* Added build-installer target to build-win64.sh
Signed-off-by: Maxim Logaev <maxlogaev@proton.me>
* Fixed build dependencies
Signed-off-by: Maxim Logaev <maxlogaev@proton.me>
* Move download yolort headers logic into prepare stage, delete yolort download script
* Added CI for MSYS2 (MINGW64) (#2)
- Use quotes in windows build script;
- Added missing gstreamer, webrtc-audio-processing and git;
- Added CI for Windows.
---------
Signed-off-by: Maxim Logaev <maxlogaev@proton.me>
---------
Signed-off-by: Maxim Logaev <maxlogaev@proton.me>
Co-authored-by: LAGonauta <lagonauta@gmail.com>
Co-authored-by: Martin Dosch <spam@mdosch.de>
Co-authored-by: Martin Dosch <martin@mdosch.de>
Co-authored-by: mjk <yuubi-san@users.noreply.github.com>
Co-authored-by: Daniel Reuther <daniel.reuther@liferay.com>
Co-authored-by: Felipe <LAGonauta@users.noreply.github.com>
Co-authored-by: Psayker <kirill970528@yandex.ru>
This commit is contained in:
parent
bf9f401743
commit
6fd8f9a45c
26
.github/workflows/build-win64.yml
vendored
Normal file
26
.github/workflows/build-win64.yml
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
name: Build for Windows
|
||||||
|
on: [pull_request, push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: msys2/setup-msys2@v2
|
||||||
|
with:
|
||||||
|
msystem: MINGW64
|
||||||
|
update: true
|
||||||
|
install: git
|
||||||
|
- run: git config --global core.autocrlf input
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Build Dino
|
||||||
|
run: |
|
||||||
|
msys2 -c './build-win64.sh --prepare'
|
||||||
|
msys2 -c './build-win64.sh'
|
||||||
|
- name: Build Dino Installer
|
||||||
|
run: |
|
||||||
|
msys2 -c './build-win64.sh --build-installer'
|
||||||
|
- name: Upload Dino Installer
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: dino-installer
|
||||||
|
path: windows-installer/dino-installer.exe
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -6,3 +6,6 @@ Makefile
|
||||||
.idea
|
.idea
|
||||||
.sqlite3
|
.sqlite3
|
||||||
gschemas.compiled
|
gschemas.compiled
|
||||||
|
windows-installer/win64-dist/
|
||||||
|
*.exe
|
||||||
|
*.dll
|
||||||
|
|
|
@ -12,6 +12,9 @@ endif ()
|
||||||
|
|
||||||
# Prepare Plugins
|
# Prepare Plugins
|
||||||
set(DEFAULT_PLUGINS omemo;openpgp;http-files;ice;rtp)
|
set(DEFAULT_PLUGINS omemo;openpgp;http-files;ice;rtp)
|
||||||
|
if (WIN32)
|
||||||
|
set(DEFAULT_PLUGINS ${DEFAULT_PLUGINS};win32-fonts;windows-notification)
|
||||||
|
endif (WIN32)
|
||||||
foreach (plugin ${DEFAULT_PLUGINS})
|
foreach (plugin ${DEFAULT_PLUGINS})
|
||||||
if ("$CACHE{DINO_PLUGIN_ENABLED_${plugin}}" STREQUAL "")
|
if ("$CACHE{DINO_PLUGIN_ENABLED_${plugin}}" STREQUAL "")
|
||||||
if (NOT DEFINED DINO_PLUGIN_ENABLED_${plugin}})
|
if (NOT DEFINED DINO_PLUGIN_ENABLED_${plugin}})
|
||||||
|
@ -176,6 +179,11 @@ if (NOT NO_DEBUG)
|
||||||
set(CMAKE_VALA_FLAGS "${CMAKE_VALA_FLAGS} -g")
|
set(CMAKE_VALA_FLAGS "${CMAKE_VALA_FLAGS} -g")
|
||||||
endif (NOT NO_DEBUG)
|
endif (NOT NO_DEBUG)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_POSIX_C_SOURCE=1")
|
||||||
|
set(CMAKE_VALA_FLAGS "${CMAKE_VALA_FLAGS} --define=_WIN32")
|
||||||
|
endif(WIN32)
|
||||||
|
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
|
|
||||||
|
|
56
README-WIN64.md
Normal file
56
README-WIN64.md
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
![Dino (WIN64)](https://dino.im/img/readme_header.svg)
|
||||||
|
=======
|
||||||
|
|
||||||
|
![screenshots](https://dino.im/img/screenshot-main.png)
|
||||||
|
|
||||||
|
Build on Windows (x86_64)
|
||||||
|
------------
|
||||||
|
- Install and configure the [MSYS2](https://www.msys2.org/) package;
|
||||||
|
- Go to `MINGW64` environment;
|
||||||
|
- Clone project:
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/mxlgv/dino && cd dino
|
||||||
|
```
|
||||||
|
- Run the script to install dependencies:
|
||||||
|
```sh
|
||||||
|
./build-win64.sh --prepare
|
||||||
|
```
|
||||||
|
- Start the build (the builded distribution is available in the `windows-installer/dist-win64` folder):
|
||||||
|
```sh
|
||||||
|
./build-win64.sh
|
||||||
|
```
|
||||||
|
Note: the build script has some other options, their description can be found using the `--help`.
|
||||||
|
|
||||||
|
Build Windows Installer (NSIS)
|
||||||
|
------------
|
||||||
|
Before this, you must build the project according to the instructions above. It's worth making sure that `windows-installer/dist-win64` is not empty.
|
||||||
|
Now you should run:
|
||||||
|
```sh
|
||||||
|
./build-win64.sh --build-installer
|
||||||
|
```
|
||||||
|
|
||||||
|
The builded installer will be available in the directory `windows-installer/dino-installer.exe`.
|
||||||
|
|
||||||
|
Resources
|
||||||
|
---------
|
||||||
|
- Check out the [Dino website](https://dino.im).
|
||||||
|
- Join our XMPP channel at `chat@dino.im`.
|
||||||
|
- The [wiki](https://github.com/dino/dino/wiki) provides additional information.
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
Dino - Modern Jabber/XMPP Client using GTK+/Vala
|
||||||
|
Copyright (C) 2016-2023 Dino contributors
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
182
build-win64.sh
Normal file
182
build-win64.sh
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
DIST_DIR="$PWD/windows-installer/win64-dist"
|
||||||
|
JOBS=$NUMBER_OF_PROCESSORS
|
||||||
|
|
||||||
|
msg()
|
||||||
|
{
|
||||||
|
echo -e "\e[32m$1\e[0m"
|
||||||
|
}
|
||||||
|
|
||||||
|
fatal()
|
||||||
|
{
|
||||||
|
echo -e "\e[31m$1\e[0m"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
download_yolort()
|
||||||
|
{
|
||||||
|
file_name=cppwinrt-2.0.210122.3+windows-10.0.19041+yolort-835cd4e.zip
|
||||||
|
yolort_dir="$PWD/plugins/windows-notification/yolort"
|
||||||
|
|
||||||
|
rm -rf "$yolort_dir"
|
||||||
|
mkdir "$yolort_dir"
|
||||||
|
curl -L -o "$file_name" "https://github.com/LAGonauta/YoloRT/releases/download/v1.0.0/$file_name"
|
||||||
|
echo "675a6d943c97b4acdbfaa473f68d3241d1798b31a67b5529c8d29fc0176a1707 $file_name" | sha256sum --check --status
|
||||||
|
unzip -o "$file_name" -d "$yolort_dir"
|
||||||
|
rm -f "$file_name"
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare()
|
||||||
|
{
|
||||||
|
msg "Installing MINGW64 build dependencies"
|
||||||
|
|
||||||
|
pacman -S --needed --noconfirm \
|
||||||
|
mingw64/mingw-w64-x86_64-gcc \
|
||||||
|
mingw64/mingw-w64-x86_64-cmake \
|
||||||
|
mingw64/mingw-w64-x86_64-ninja \
|
||||||
|
mingw64/mingw-w64-x86_64-gtk4 \
|
||||||
|
mingw64/mingw-w64-x86_64-libadwaita \
|
||||||
|
mingw64/mingw-w64-x86_64-sqlite3 \
|
||||||
|
mingw64/mingw-w64-x86_64-openssl \
|
||||||
|
mingw64/mingw-w64-x86_64-libgcrypt \
|
||||||
|
mingw64/mingw-w64-x86_64-libgee \
|
||||||
|
mingw64/mingw-w64-x86_64-vala \
|
||||||
|
mingw64/mingw-w64-x86_64-gsettings-desktop-schemas \
|
||||||
|
mingw64/mingw-w64-x86_64-qrencode \
|
||||||
|
mingw64/mingw-w64-x86_64-ntldd-git \
|
||||||
|
mingw64/mingw-w64-x86_64-gpgme \
|
||||||
|
mingw64/mingw-w64-x86_64-fontconfig \
|
||||||
|
mingw64/mingw-w64-x86_64-iso-codes \
|
||||||
|
mingw64/mingw-w64-x86_64-gstreamer \
|
||||||
|
mingw64/mingw-w64-x86_64-gst-plugins-bad \
|
||||||
|
mingw64/mingw-w64-x86_64-gst-plugins-good \
|
||||||
|
mingw64/mingw-w64-x86_64-gst-plugins-base \
|
||||||
|
mingw64/mingw-w64-x86_64-gst-plugins-ugly \
|
||||||
|
mingw64/mingw-w64-x86_64-nsis \
|
||||||
|
mingw64/mingw-w64-x86_64-libsignal-protocol-c \
|
||||||
|
mingw64/mingw-w64-x86_64-icu \
|
||||||
|
mingw64/mingw-w64-x86_64-webrtc-audio-processing \
|
||||||
|
git \
|
||||||
|
make \
|
||||||
|
unzip \
|
||||||
|
curl
|
||||||
|
|
||||||
|
msg "Successfully installed!"
|
||||||
|
|
||||||
|
msg "Download YoloRT headers"
|
||||||
|
download_yolort
|
||||||
|
msg "Successfully downloaded!"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
configure()
|
||||||
|
{
|
||||||
|
msg "Running configuration for Windows"
|
||||||
|
./configure --program-prefix="$DIST_DIR" --no-debug --release --disable-fast-vapi --with-libsoup3
|
||||||
|
msg "Configured!"
|
||||||
|
}
|
||||||
|
|
||||||
|
build()
|
||||||
|
{
|
||||||
|
msg "Started building on $JOBS threads"
|
||||||
|
make -j"$JOBS"
|
||||||
|
msg "Successfully builded!"
|
||||||
|
}
|
||||||
|
|
||||||
|
dist_install()
|
||||||
|
{
|
||||||
|
msg "Installing Dino in '$DIST_DIR'!"
|
||||||
|
make install
|
||||||
|
|
||||||
|
msg "Copying MINGW64 dependencies"
|
||||||
|
cp /mingw64/bin/gdbus.exe "$DIST_DIR/bin"
|
||||||
|
cp /mingw64/bin/gspawn-win64-helper.exe "$DIST_DIR/bin"
|
||||||
|
|
||||||
|
cp /mingw64/bin/libcrypto-*-x64.dll "$DIST_DIR/bin/"
|
||||||
|
cp -r /mingw64/lib/gstreamer-1.0 "$DIST_DIR/lib"
|
||||||
|
mkdir -p "$DIST_DIR/lib/gdk-pixbuf-2.0/" && cp -r /mingw64/lib/gdk-pixbuf-2.0 "$DIST_DIR/lib/"
|
||||||
|
mkdir -p "$DIST_DIR/lib/gio/" && cp -r /mingw64/lib/gio "$DIST_DIR/lib/"
|
||||||
|
|
||||||
|
list=`find "$DIST_DIR" -type f \( -name "*.exe" -o -name "*.dll" \) -exec \
|
||||||
|
ntldd -R {} + | \
|
||||||
|
grep "mingw64" | \
|
||||||
|
cut -f1 -d "=" | sort | uniq`
|
||||||
|
for a in $list; do
|
||||||
|
cp -fv "/mingw64/bin/$a" "$DIST_DIR/bin/"
|
||||||
|
done
|
||||||
|
|
||||||
|
msg "Removing debug information from all EXE and DLL files"
|
||||||
|
find "$DIST_DIR" -iname "*.exe" -exec strip -s {} +
|
||||||
|
find "$DIST_DIR" -iname "*.dll" -exec strip -s {} +
|
||||||
|
|
||||||
|
find "$DIST_DIR" -iname "*.a" -exec rm {} +
|
||||||
|
|
||||||
|
msg "Removing redudant header files"
|
||||||
|
rm -rf "$DIST_DIR/include"
|
||||||
|
|
||||||
|
msg "Copy LICENSE"
|
||||||
|
cp -f "$PWD/LICENSE" "$DIST_DIR/LICENSE"
|
||||||
|
|
||||||
|
msg "Copy icons, themes, locales and fonts"
|
||||||
|
cp -f "$PWD/main/dino.ico" "$DIST_DIR/dino.ico"
|
||||||
|
cp -rf "/mingw64/share/xml" "$DIST_DIR/share"
|
||||||
|
mkdir -p "$DIST_DIR/etc/fonts" && cp -r /mingw64/etc/fonts "$DIST_DIR/etc/"
|
||||||
|
mkdir -p "$DIST_DIR/share/icons" && cp -r /mingw64/share/icons "$DIST_DIR/share/"
|
||||||
|
mkdir -p "$DIST_DIR/share/glib-2.0/schemas" && cp -rf /mingw64/share/glib-2.0/schemas "$DIST_DIR/share/glib-2.0/"
|
||||||
|
|
||||||
|
msg "Successfully installed!"
|
||||||
|
}
|
||||||
|
|
||||||
|
build_installer()
|
||||||
|
{
|
||||||
|
msg "Building an installer for Windows using NSIS"
|
||||||
|
cd windows-installer
|
||||||
|
makensis dino.nsi
|
||||||
|
msg "Installer successfully builded!"
|
||||||
|
cd ..
|
||||||
|
}
|
||||||
|
|
||||||
|
clean()
|
||||||
|
{
|
||||||
|
rm -rf build "$DIST_DIR"
|
||||||
|
msg "Build artifacts removed successfull!"
|
||||||
|
}
|
||||||
|
|
||||||
|
help()
|
||||||
|
{
|
||||||
|
cat << EOF
|
||||||
|
usage: $0 [OPTION]
|
||||||
|
--prepare install build dependencies
|
||||||
|
--configure configure the project
|
||||||
|
--build build the project
|
||||||
|
--dist-install install the builded project
|
||||||
|
--build-installer build installer (using NSIS)
|
||||||
|
--clean remove build artifacts
|
||||||
|
--help show this help
|
||||||
|
|
||||||
|
Running without parameters is equivalent to running:
|
||||||
|
'--configure', '--build' and '--dist-install'
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "$(uname)" != "MINGW64_NT"* ]]; then
|
||||||
|
fatal "This is not a MINGW64 environment!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
"--prepare" ) prepare ;;
|
||||||
|
"--configure" ) configure ;;
|
||||||
|
"--build" ) build ;;
|
||||||
|
"--dist-install" ) dist_install ;;
|
||||||
|
"--build-installer") build_installer ;;
|
||||||
|
"--clean" ) clean ;;
|
||||||
|
"--help" ) help ;;
|
||||||
|
"" )
|
||||||
|
configure
|
||||||
|
build
|
||||||
|
dist_install
|
||||||
|
;;
|
||||||
|
*) fatal "Unknown argument!"
|
||||||
|
esac
|
|
@ -13,11 +13,16 @@ function(find_pkg_config_with_fallback name)
|
||||||
# Found via pkg-config, using its result values
|
# Found via pkg-config, using its result values
|
||||||
set(${name}_FOUND ${${name}_PKG_CONFIG_FOUND})
|
set(${name}_FOUND ${${name}_PKG_CONFIG_FOUND})
|
||||||
|
|
||||||
|
if(MINGW)
|
||||||
|
set(MINGWLIBPATH ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
|
||||||
|
endif(MINGW)
|
||||||
|
|
||||||
# Try to find real file name of libraries
|
# Try to find real file name of libraries
|
||||||
foreach(lib ${${name}_PKG_CONFIG_LIBRARIES})
|
foreach(lib ${${name}_PKG_CONFIG_LIBRARIES})
|
||||||
find_library(${name}_${lib}_LIBRARY ${lib} HINTS ${${name}_PKG_CONFIG_LIBRARY_DIRS})
|
find_library(${name}_${lib}_LIBRARY ${lib} HINTS ${${name}_PKG_CONFIG_LIBRARY_DIRS} ${MINGWLIBPATH})
|
||||||
mark_as_advanced(${name}_${lib}_LIBRARY)
|
mark_as_advanced(${name}_${lib}_LIBRARY)
|
||||||
if(NOT ${name}_${lib}_LIBRARY)
|
if(NOT ${name}_${lib}_LIBRARY)
|
||||||
|
message(${name} ": " ${lib} " library not found")
|
||||||
unset(${name}_FOUND)
|
unset(${name}_FOUND)
|
||||||
endif(NOT ${name}_${lib}_LIBRARY)
|
endif(NOT ${name}_${lib}_LIBRARY)
|
||||||
endforeach(lib)
|
endforeach(lib)
|
||||||
|
|
|
@ -13,11 +13,16 @@ function(find_pkg_config_with_fallback_on_config_script name)
|
||||||
# Found via pkg-config, using it's result values
|
# Found via pkg-config, using it's result values
|
||||||
set(${name}_FOUND ${${name}_PKG_CONFIG_FOUND})
|
set(${name}_FOUND ${${name}_PKG_CONFIG_FOUND})
|
||||||
|
|
||||||
|
if(MINGW)
|
||||||
|
set(MINGWLIBPATH ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
|
||||||
|
endif(MINGW)
|
||||||
|
|
||||||
# Try to find real file name of libraries
|
# Try to find real file name of libraries
|
||||||
foreach(lib ${${name}_PKG_CONFIG_LIBRARIES})
|
foreach(lib ${${name}_PKG_CONFIG_LIBRARIES})
|
||||||
find_library(${name}_${lib}_LIBRARY ${lib} HINTS ${${name}_PKG_CONFIG_LIBRARY_DIRS})
|
find_library(${name}_${lib}_LIBRARY ${lib} HINTS ${${name}_PKG_CONFIG_LIBRARY_DIRS} ${MINGWLIBPATH})
|
||||||
mark_as_advanced(${name}_${lib}_LIBRARY)
|
mark_as_advanced(${name}_${lib}_LIBRARY)
|
||||||
if(NOT ${name}_${lib}_LIBRARY)
|
if(NOT ${name}_${lib}_LIBRARY)
|
||||||
|
message(${name} ": " ${lib} " library not found")
|
||||||
unset(${name}_FOUND)
|
unset(${name}_FOUND)
|
||||||
endif(NOT ${name}_${lib}_LIBRARY)
|
endif(NOT ${name}_${lib}_LIBRARY)
|
||||||
endforeach(lib)
|
endforeach(lib)
|
||||||
|
|
|
@ -304,6 +304,24 @@ public class AvatarManager : StreamInteractionModule, Object {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string? get_avatar_filepath(Account account, Jid jid_) {
|
||||||
|
Jid jid = jid_;
|
||||||
|
if (!stream_interactor.get_module(MucManager.IDENTITY).is_groupchat_occupant(jid_, account)) {
|
||||||
|
jid = jid_.bare_jid;
|
||||||
|
}
|
||||||
|
|
||||||
|
string? hash = null;
|
||||||
|
if (user_avatars.has_key(jid)) {
|
||||||
|
hash = user_avatars[jid];
|
||||||
|
} else if (vcard_avatars.has_key(jid)) {
|
||||||
|
hash = vcard_avatars[jid];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hash == null) return null;
|
||||||
|
|
||||||
|
return Path.build_filename(folder, hash);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ public class FileManager : StreamInteractionModule, Object {
|
||||||
try {
|
try {
|
||||||
FileInfo file_info = file.query_info("*", FileQueryInfoFlags.NONE);
|
FileInfo file_info = file.query_info("*", FileQueryInfoFlags.NONE);
|
||||||
file_transfer.file_name = file_info.get_display_name();
|
file_transfer.file_name = file_info.get_display_name();
|
||||||
file_transfer.mime_type = file_info.get_content_type();
|
file_transfer.mime_type = Util.get_content_type(file_info);
|
||||||
file_transfer.size = (int)file_info.get_size();
|
file_transfer.size = (int)file_info.get_size();
|
||||||
file_transfer.input_stream = yield file.read_async();
|
file_transfer.input_stream = yield file.read_async();
|
||||||
|
|
||||||
|
@ -259,9 +259,16 @@ public class FileManager : StreamInteractionModule, Object {
|
||||||
file_transfer.input_stream = yield file.read_async();
|
file_transfer.input_stream = yield file.read_async();
|
||||||
|
|
||||||
FileInfo file_info = file_transfer.get_file().query_info("*", FileQueryInfoFlags.NONE);
|
FileInfo file_info = file_transfer.get_file().query_info("*", FileQueryInfoFlags.NONE);
|
||||||
file_transfer.mime_type = file_info.get_content_type();
|
file_transfer.mime_type = Util.get_content_type(file_info);
|
||||||
|
|
||||||
file_transfer.state = FileTransfer.State.COMPLETE;
|
file_transfer.state = FileTransfer.State.COMPLETE;
|
||||||
|
|
||||||
|
#if _WIN32 // Add Zone.Identifier so Windows knows this file was downloaded from the internet
|
||||||
|
var file_alternate_stream = File.new_for_path(Path.build_filename(get_storage_dir(), filename + ":Zone.Identifier"));
|
||||||
|
var os_alternate_stream = file_alternate_stream.create(FileCreateFlags.REPLACE_DESTINATION);
|
||||||
|
os_alternate_stream.write("[ZoneTransfer]\r\nZoneId=3".data);
|
||||||
|
#endif
|
||||||
|
|
||||||
} catch (Error e) {
|
} catch (Error e) {
|
||||||
warning("Error downloading file: %s", e.message);
|
warning("Error downloading file: %s", e.message);
|
||||||
file_transfer.state = FileTransfer.State.FAILED;
|
file_transfer.state = FileTransfer.State.FAILED;
|
||||||
|
|
|
@ -4,6 +4,25 @@ using Qlite;
|
||||||
namespace Dino {
|
namespace Dino {
|
||||||
|
|
||||||
public class Util {
|
public class Util {
|
||||||
|
#if _WIN32
|
||||||
|
[CCode (cname = "ShellExecuteA", cheader_filename = "windows.h")]
|
||||||
|
private static extern int ShellExecuteA(int* hwnd, string operation, string file, string parameters, string directory, int showCmd);
|
||||||
|
|
||||||
|
[CCode (cname = "CoInitialize", cheader_filename = "windows.h")]
|
||||||
|
private static extern int CoInitialize(void* reserved);
|
||||||
|
|
||||||
|
[CCode (cname = "CoUninitialize", cheader_filename = "windows.h")]
|
||||||
|
private static extern void CoUninitialize();
|
||||||
|
|
||||||
|
private static int ShellExecute(string operation, string file) {
|
||||||
|
CoInitialize(null);
|
||||||
|
var result = ShellExecuteA(null, operation, file, null, null, 1);
|
||||||
|
CoUninitialize();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
public static Message.Type get_message_type_for_conversation(Conversation conversation) {
|
public static Message.Type get_message_type_for_conversation(Conversation conversation) {
|
||||||
switch (conversation.type_) {
|
switch (conversation.type_) {
|
||||||
case Conversation.Type.CHAT:
|
case Conversation.Type.CHAT:
|
||||||
|
@ -29,6 +48,33 @@ public class Util {
|
||||||
assert_not_reached();
|
assert_not_reached();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
public static void launch_default_for_uri(string file_uri)
|
||||||
|
{
|
||||||
|
#if _WIN32
|
||||||
|
ShellExecute("open", file_uri);
|
||||||
|
#else
|
||||||
|
AppInfo.launch_default_for_uri(file_uri, null);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string get_content_type(FileInfo fileInfo)
|
||||||
|
{
|
||||||
|
#if _WIN32
|
||||||
|
string fileName = fileInfo.get_name();
|
||||||
|
int fileNameLength = fileName.length;
|
||||||
|
int extIndex = fileName.last_index_of(".");
|
||||||
|
if (extIndex < fileNameLength)
|
||||||
|
{
|
||||||
|
string extension = fileName.substring(extIndex, fileNameLength - extIndex);
|
||||||
|
string mime_type = ContentType.get_mime_type(extension);
|
||||||
|
if (mime_type != null && mime_type.length != 0)
|
||||||
|
{
|
||||||
|
return mime_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return fileInfo.get_content_type();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -256,7 +256,13 @@ OPTIONS
|
||||||
)
|
)
|
||||||
|
|
||||||
add_definitions(${VALA_CFLAGS} -DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\" -DLOCALE_INSTALL_DIR=\"${LOCALE_INSTALL_DIR}\" -DG_LOG_DOMAIN="dino")
|
add_definitions(${VALA_CFLAGS} -DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\" -DLOCALE_INSTALL_DIR=\"${LOCALE_INSTALL_DIR}\" -DG_LOG_DOMAIN="dino")
|
||||||
add_executable(dino ${MAIN_VALA_C} ${MAIN_GRESOURCES_TARGET})
|
if(WIN32)
|
||||||
|
add_link_options("-Wl,--export-all-symbols")
|
||||||
|
set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> --use-temp-file -O coff <DEFINES> -i <SOURCE> -o <OBJECT>")
|
||||||
|
add_executable(dino ${MAIN_VALA_C} ${MAIN_GRESOURCES_TARGET} dino-info.rc)
|
||||||
|
else(WIN32)
|
||||||
|
add_executable(dino ${MAIN_VALA_C} ${MAIN_GRESOURCES_TARGET})
|
||||||
|
endif(WIN32)
|
||||||
add_dependencies(dino ${GETTEXT_PACKAGE}-translations)
|
add_dependencies(dino ${GETTEXT_PACKAGE}-translations)
|
||||||
target_include_directories(dino PRIVATE src)
|
target_include_directories(dino PRIVATE src)
|
||||||
target_link_libraries(dino libdino ${MAIN_PACKAGES})
|
target_link_libraries(dino libdino ${MAIN_PACKAGES})
|
||||||
|
|
21
main/dino-info.rc
Normal file
21
main/dino-info.rc
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
1 VERSIONINFO
|
||||||
|
BEGIN
|
||||||
|
BLOCK "StringFileInfo"
|
||||||
|
BEGIN
|
||||||
|
BLOCK "080904E4"
|
||||||
|
BEGIN
|
||||||
|
VALUE "CompanyName", "Dino"
|
||||||
|
VALUE "FileDescription", "Dino - Modern XMPP (""Jabber"") Chat Client"
|
||||||
|
VALUE "InternalName", "dino"
|
||||||
|
VALUE "LegalCopyright", "Dino"
|
||||||
|
VALUE "OriginalFilename", "dino.exe"
|
||||||
|
VALUE "ProductName", "Dino"
|
||||||
|
END
|
||||||
|
END
|
||||||
|
BLOCK "VarFileInfo"
|
||||||
|
BEGIN
|
||||||
|
VALUE "Translation", 0x809, 1252
|
||||||
|
END
|
||||||
|
END
|
||||||
|
|
||||||
|
DINO_ICO ICON "./dino.ico"
|
BIN
main/dino.ico
Normal file
BIN
main/dino.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 98 KiB |
|
@ -9,6 +9,12 @@ namespace Dino {
|
||||||
void main(string[] args) {
|
void main(string[] args) {
|
||||||
|
|
||||||
try{
|
try{
|
||||||
|
#if _WIN32
|
||||||
|
var pangocairoResult = Environment.set_variable("PANGOCAIRO_BACKEND", "fontconfig", false);
|
||||||
|
if (!pangocairoResult) {
|
||||||
|
warning("Unable to set PANGOCAIRO_BACKEND environment variable to fontconfig");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
string? exec_path = args.length > 0 ? args[0] : null;
|
string? exec_path = args.length > 0 ? args[0] : null;
|
||||||
SearchPathGenerator search_path_generator = new SearchPathGenerator(exec_path);
|
SearchPathGenerator search_path_generator = new SearchPathGenerator(exec_path);
|
||||||
Intl.textdomain(GETTEXT_PACKAGE);
|
Intl.textdomain(GETTEXT_PACKAGE);
|
||||||
|
|
|
@ -176,7 +176,7 @@ public class FileWidgetController : Object {
|
||||||
|
|
||||||
private void open_file() {
|
private void open_file() {
|
||||||
try{
|
try{
|
||||||
AppInfo.launch_default_for_uri(file_transfer.get_file().get_uri(), null);
|
Dino.Util.launch_default_for_uri(file_transfer.get_file().get_uri());
|
||||||
} catch (Error err) {
|
} catch (Error err) {
|
||||||
warning("Failed to open %s - %s", file_transfer.get_file().get_uri(), err.message);
|
warning("Failed to open %s - %s", file_transfer.get_file().get_uri(), err.message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -242,7 +242,15 @@ public class MessageMetaItem : ContentMetaItem {
|
||||||
|
|
||||||
public static bool on_label_activate_link(string uri) {
|
public static bool on_label_activate_link(string uri) {
|
||||||
// Always handle xmpp URIs with Dino
|
// Always handle xmpp URIs with Dino
|
||||||
if (!uri.has_prefix("xmpp:")) return false;
|
if (!uri.has_prefix("xmpp:")) {
|
||||||
|
#if _WIN32
|
||||||
|
Dino.Util.launch_default_for_uri(uri);
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
File file = File.new_for_uri(uri);
|
File file = File.new_for_uri(uri);
|
||||||
Dino.Application.get_default().open(new File[]{file}, "");
|
Dino.Application.get_default().open(new File[]{file}, "");
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class FileSendOverlay {
|
||||||
|
|
||||||
private async void load_file_widget(File file, FileInfo file_info) {
|
private async void load_file_widget(File file, FileInfo file_info) {
|
||||||
string file_name = file_info.get_display_name();
|
string file_name = file_info.get_display_name();
|
||||||
string mime_type = file_info.get_content_type();
|
string mime_type = Dino.Util.get_content_type(file_info);
|
||||||
|
|
||||||
bool is_image = false;
|
bool is_image = false;
|
||||||
|
|
||||||
|
|
|
@ -373,7 +373,7 @@ public class AddAccountDialog : Gtk.Dialog {
|
||||||
// Button is opening a registration website
|
// Button is opening a registration website
|
||||||
if (form.oob != null) {
|
if (form.oob != null) {
|
||||||
try {
|
try {
|
||||||
AppInfo.launch_default_for_uri(form.oob, null);
|
Dino.Util.launch_default_for_uri(form.oob);
|
||||||
} catch (Error e) { }
|
} catch (Error e) { }
|
||||||
show_sign_in_jid();
|
show_sign_in_jid();
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
if(WIN32)
|
||||||
|
add_link_options("-Wl,--export-all-symbols")
|
||||||
|
endif(WIN32)
|
||||||
|
|
||||||
foreach(plugin ${PLUGINS})
|
foreach(plugin ${PLUGINS})
|
||||||
add_subdirectory(${plugin})
|
add_subdirectory(${plugin})
|
||||||
endforeach(plugin)
|
endforeach(plugin)
|
||||||
|
|
|
@ -177,6 +177,13 @@ private static uint8[] get_uint8_from_data(Data data) {
|
||||||
|
|
||||||
private static void initialize() {
|
private static void initialize() {
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
|
#if _WIN32
|
||||||
|
string gpg = GLib.Environment.find_program_in_path("gpg.exe");
|
||||||
|
if (gpg != null && gpg.length > 0)
|
||||||
|
{
|
||||||
|
set_global_flag("w32-inst-dir", GLib.Path.get_dirname(gpg));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
check_version();
|
check_version();
|
||||||
initialized = true;
|
initialized = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -665,6 +665,9 @@ namespace GPG {
|
||||||
[CCode (cname = "gpgme_strerror")]
|
[CCode (cname = "gpgme_strerror")]
|
||||||
public unowned string strerror(GPGError.Error err);
|
public unowned string strerror(GPGError.Error err);
|
||||||
|
|
||||||
|
[CCode (cname = "gpgme_set_global_flag")]
|
||||||
|
public int set_global_flag(string name, string value);
|
||||||
|
|
||||||
private void throw_if_error(GPGError.Error error) throws GLib.Error {
|
private void throw_if_error(GPGError.Error error) throws GLib.Error {
|
||||||
if (error.code != GPGError.ErrorCode.NO_ERROR) {
|
if (error.code != GPGError.ErrorCode.NO_ERROR) {
|
||||||
throw new GLib.Error(-1, error.code, "%s", error.to_string());
|
throw new GLib.Error(-1, error.code, "%s", error.to_string());
|
||||||
|
|
|
@ -49,6 +49,8 @@ public class Dino.Plugins.Rtp.Plugin : RootInterface, VideoCallPlugin, Object {
|
||||||
device_monitor.get_bus().add_watch(Priority.DEFAULT, on_device_monitor_message);
|
device_monitor.get_bus().add_watch(Priority.DEFAULT, on_device_monitor_message);
|
||||||
device_monitor.start();
|
device_monitor.start();
|
||||||
foreach (Gst.Device device in device_monitor.get_devices()) {
|
foreach (Gst.Device device in device_monitor.get_devices()) {
|
||||||
|
if (device.properties == null) continue;
|
||||||
|
if (device.properties.get_string("device.api") == "wasapi") continue;
|
||||||
if (device.properties.has_name("pipewire-proplist") && device.has_classes("Audio")) continue;
|
if (device.properties.has_name("pipewire-proplist") && device.has_classes("Audio")) continue;
|
||||||
if (device.properties.get_string("device.class") == "monitor") continue;
|
if (device.properties.get_string("device.class") == "monitor") continue;
|
||||||
if (devices.any_match((it) => it.matches(device))) continue;
|
if (devices.any_match((it) => it.matches(device))) continue;
|
||||||
|
|
43
plugins/win32-fonts/CMakeLists.txt
Normal file
43
plugins/win32-fonts/CMakeLists.txt
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
find_packages(WIN32_FONTS_PACKAGES REQUIRED
|
||||||
|
Gee
|
||||||
|
GLib
|
||||||
|
GModule
|
||||||
|
GObject
|
||||||
|
GTK4
|
||||||
|
)
|
||||||
|
|
||||||
|
set(RESOURCE_LIST
|
||||||
|
larger.css
|
||||||
|
)
|
||||||
|
|
||||||
|
compile_gresources(
|
||||||
|
WIN32_FONTS_GRESOURCES_TARGET
|
||||||
|
WIN32_FONTS_GRESOURCES_XML
|
||||||
|
TARGET ${CMAKE_CURRENT_BINARY_DIR}/resources/resources.c
|
||||||
|
TYPE EMBED_C
|
||||||
|
RESOURCES ${RESOURCE_LIST}
|
||||||
|
PREFIX /im/dino/Dino/win32-fonts
|
||||||
|
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data
|
||||||
|
)
|
||||||
|
|
||||||
|
vala_precompile(WIN32_FONTS_VALA_C
|
||||||
|
SOURCES
|
||||||
|
src/plugin.vala
|
||||||
|
src/register_plugin.vala
|
||||||
|
CUSTOM_VAPIS
|
||||||
|
${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi
|
||||||
|
${CMAKE_BINARY_DIR}/exports/dino.vapi
|
||||||
|
${CMAKE_BINARY_DIR}/exports/qlite.vapi
|
||||||
|
PACKAGES
|
||||||
|
${WIN32_FONTS_PACKAGES}
|
||||||
|
GRESOURCES
|
||||||
|
${WIN32_FONTS_GRESOURCES_XML}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_definitions(${VALA_CFLAGS})
|
||||||
|
add_library(win32-fonts SHARED ${WIN32_FONTS_VALA_C} ${WIN32_FONTS_GRESOURCES_TARGET})
|
||||||
|
target_link_libraries(win32-fonts libdino ${WIN32_FONTS_PACKAGES})
|
||||||
|
set_target_properties(win32-fonts PROPERTIES PREFIX "")
|
||||||
|
set_target_properties(win32-fonts PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins/)
|
||||||
|
|
||||||
|
install(TARGETS win32-fonts ${PLUGIN_INSTALL})
|
3
plugins/win32-fonts/data/larger.css
Normal file
3
plugins/win32-fonts/data/larger.css
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
* {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
}
|
16
plugins/win32-fonts/src/plugin.vala
Normal file
16
plugins/win32-fonts/src/plugin.vala
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
using Gtk;
|
||||||
|
|
||||||
|
namespace Dino.Plugins.Win32Fonts {
|
||||||
|
|
||||||
|
public class Plugin : RootInterface, Object {
|
||||||
|
|
||||||
|
public void registered(Dino.Application app) {
|
||||||
|
CssProvider larger_fonts = new CssProvider();
|
||||||
|
larger_fonts.load_from_resource("/im/dino/Dino/win32-fonts/larger.css");
|
||||||
|
StyleContext.add_provider_for_display(Gdk.Display.get_default(), larger_fonts, STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
3
plugins/win32-fonts/src/register_plugin.vala
Normal file
3
plugins/win32-fonts/src/register_plugin.vala
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
public Type register_plugin(Module module) {
|
||||||
|
return typeof (Dino.Plugins.Win32Fonts.Plugin);
|
||||||
|
}
|
1
plugins/windows-notification/.gitignore
vendored
Normal file
1
plugins/windows-notification/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/yolort
|
77
plugins/windows-notification/CMakeLists.txt
Normal file
77
plugins/windows-notification/CMakeLists.txt
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
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
|
||||||
|
GLib
|
||||||
|
GModule
|
||||||
|
GObject
|
||||||
|
GTK4
|
||||||
|
)
|
||||||
|
|
||||||
|
vala_precompile(WINDOWS_NOTIFICATION_VALA_C
|
||||||
|
SOURCES
|
||||||
|
src/windows_notifications_plugin.vala
|
||||||
|
src/windows_notifications_register_plugin.vala
|
||||||
|
src/toast_notification_builder.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/win32.vapi
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/vapi/winrt.vapi
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/vapi/shortcutcreator.vapi
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/vapi/enums.vapi
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/vapi/winrt_windows_ui_notifications.vapi
|
||||||
|
PACKAGES
|
||||||
|
${WINDOWS_NOTIFICATION_PACKAGES}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(WINDOWS_API_SOURCES
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/api/src/gobject/winrt-enums.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/api/src/gobject/winrt-event-token.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/api/src/gobject/winrt-toast-notification.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/api/src/gobject/winrt-toast-notifier.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/api/src/gobject/winrt.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/api/src/win32.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/api/src/converter.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/api/src/dyn_mod.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/api/src/ginvoke.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/api/src/shortcutcreator.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_definitions(${VALA_CFLAGS} -DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\" -DLOCALE_INSTALL_DIR=\"${LOCALE_INSTALL_DIR}\")
|
||||||
|
add_library(windows-notification SHARED ${WINDOWS_NOTIFICATION_VALA_C} ${WINDOWS_API_SOURCES})
|
||||||
|
add_dependencies(windows-notification ${GETTEXT_PACKAGE}-translations)
|
||||||
|
|
||||||
|
target_include_directories(windows-notification
|
||||||
|
PRIVATE
|
||||||
|
${PROJECT_SOURCE_DIR}/api/include
|
||||||
|
${PROJECT_SOURCE_DIR}/api/include/gobject
|
||||||
|
${PROJECT_SOURCE_DIR}/yolort/include
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
target_link_libraries(windows-notification libdino ${shlwapi_LIBRARY} ${ntdll_LIBRARY} ${WINDOWS_NOTIFICATION_PACKAGES})
|
||||||
|
target_compile_features(windows-notification PRIVATE cxx_std_17)
|
||||||
|
target_compile_definitions(windows-notification PRIVATE WINRT_GLIB_H_INSIDE)
|
||||||
|
target_compile_options(windows-notification PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-iquote ${PROJECT_SOURCE_DIR}/yolort/include/winrt/yolort_impl>)
|
||||||
|
|
||||||
|
set_target_properties(windows-notification PROPERTIES PREFIX "")
|
||||||
|
set_target_properties(windows-notification PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins/)
|
||||||
|
|
||||||
|
install(TARGETS windows-notification ${PLUGIN_INSTALL})
|
10
plugins/windows-notification/README.md
Normal file
10
plugins/windows-notification/README.md
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# Windows Notification plugin for Dino
|
||||||
|
Plugin that allows native notifications on Dino on both Windows 8.1 and Windows 10.
|
||||||
|
Currently supports:
|
||||||
|
* Actionable notification (with action button on Windows 10)
|
||||||
|
* Shows avatar image
|
||||||
|
|
||||||
|
# Special thanks
|
||||||
|
- GObject wrapper based on https://github.com/endlessm/xapian-glib/
|
||||||
|
- YoloRT which allows compilation on GCC https://github.com/Yuubi-san/YoloRT
|
||||||
|
- Notification builder inspired by https://gitlab.gnome.org/Philipp/glib/-/commits/windows_toast_notification_backend
|
8
plugins/windows-notification/api/include/converter.hpp
Normal file
8
plugins/windows-notification/api/include/converter.hpp
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
std::wstring sview_to_wstr(const std::string_view str);
|
||||||
|
gchar* wsview_to_char(const std::wstring_view wstr);
|
29
plugins/windows-notification/api/include/dyn_mod.hpp
Normal file
29
plugins/windows-notification/api/include/dyn_mod.hpp
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
|
||||||
|
#ifndef DYN_MOD_HPP
|
||||||
|
#define DYN_MOD_HPP
|
||||||
|
|
||||||
|
namespace dyn_mod
|
||||||
|
{
|
||||||
|
using punny_func = void();
|
||||||
|
|
||||||
|
punny_func &load_symbol(
|
||||||
|
const wchar_t *mod_path,
|
||||||
|
const char *mod_dbgnym,
|
||||||
|
const char *symbol);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline T &load_symbol(
|
||||||
|
const wchar_t *const mod_path, const char *const mod_dbgnym,
|
||||||
|
const char *const symbol)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<T &>(load_symbol(mod_path, mod_dbgnym, symbol));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define dyn_load_symbol_ns(mod_name, namespace, symbol) \
|
||||||
|
::dyn_mod::load_symbol<decltype(namespace::symbol)>( \
|
||||||
|
L ## mod_name, mod_name, #symbol)
|
||||||
|
|
||||||
|
#define dyn_load_symbol(mod_name, symbol) dyn_load_symbol_ns(mod_name, ,symbol)
|
||||||
|
|
||||||
|
#endif
|
172
plugins/windows-notification/api/include/ginvoke.hpp
Normal file
172
plugins/windows-notification/api/include/ginvoke.hpp
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
|
||||||
|
#ifndef GINVOKE_HPP
|
||||||
|
#define GINVOKE_HPP
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <utility>
|
||||||
|
#include <optional>
|
||||||
|
#include <variant>
|
||||||
|
#include <iterator>
|
||||||
|
#include <functional>
|
||||||
|
#include <exception>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cinttypes>
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#include "overload.hpp"
|
||||||
|
#include "make_array.hpp"
|
||||||
|
#include "hexify.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace glib {
|
||||||
|
|
||||||
|
namespace impl
|
||||||
|
{
|
||||||
|
using static_c_str = const char *;
|
||||||
|
using varstring_t = std::variant<std::string, static_c_str>;
|
||||||
|
struct varstring : varstring_t
|
||||||
|
{
|
||||||
|
varstring(std::string &&s) noexcept : varstring_t{std::move(s)} {}
|
||||||
|
varstring(static_c_str &&s) noexcept : varstring_t{std::move(s)} {}
|
||||||
|
varstring(std::nullptr_t) = delete;
|
||||||
|
varstring(const varstring &) = delete;
|
||||||
|
varstring(varstring &&) = default;
|
||||||
|
|
||||||
|
const char* c_str() const && = delete;
|
||||||
|
const char* c_str() const &
|
||||||
|
{
|
||||||
|
return std::visit(overload{
|
||||||
|
[](const std::string &s){ return s.c_str(); },
|
||||||
|
[](const static_c_str s){ return s; }
|
||||||
|
}, static_cast<const varstring_t &>(*this));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hresult
|
||||||
|
{
|
||||||
|
std::int32_t code;
|
||||||
|
varstring message;
|
||||||
|
};
|
||||||
|
std::optional<hresult> get_if_hresult_error(std::exception_ptr) noexcept;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename OStream, typename T,
|
||||||
|
std::enable_if_t<!std::is_enum_v<T>,int> = 0>
|
||||||
|
inline auto &describe_argument(OStream &s, const T &a)
|
||||||
|
{ return s << a; }
|
||||||
|
template<typename OStream, typename T,
|
||||||
|
std::enable_if_t< std::is_enum_v<T>,int> = 0>
|
||||||
|
inline auto &describe_argument(OStream &s, const T &a)
|
||||||
|
{ return s << static_cast<std::underlying_type_t<T>>(a); }
|
||||||
|
|
||||||
|
template<typename OStream>
|
||||||
|
inline auto &describe_argument(OStream &s, std::string_view const a)
|
||||||
|
{ return s << std::quoted(a); }
|
||||||
|
template<typename OStream>
|
||||||
|
inline auto &describe_argument(OStream &s, const std::string & a)
|
||||||
|
{ return s << std::quoted(a); }
|
||||||
|
template<typename OStream>
|
||||||
|
inline auto &describe_argument(OStream &s, const char * const a)
|
||||||
|
{ return s << std::quoted(a); }
|
||||||
|
// TODO: overload for const GString *
|
||||||
|
|
||||||
|
// not implemented (TODO maybe):
|
||||||
|
template<typename OStream>
|
||||||
|
inline auto &describe_argument(OStream &s, std::wstring_view const a) = delete;
|
||||||
|
template<typename OStream>
|
||||||
|
inline auto &describe_argument(OStream &s, const std::wstring & a) = delete;
|
||||||
|
template<typename OStream>
|
||||||
|
inline auto &describe_argument(OStream &s, const wchar_t * const a) = delete;
|
||||||
|
|
||||||
|
inline impl::varstring describe_arguments() noexcept { return {""}; }
|
||||||
|
|
||||||
|
template<typename... Arg>
|
||||||
|
inline impl::varstring describe_arguments(const Arg &... a) noexcept try
|
||||||
|
{
|
||||||
|
std::ostringstream ss;
|
||||||
|
((describe_argument(ss,a) << ','), ...);
|
||||||
|
auto s = std::move(ss).str();
|
||||||
|
s.pop_back();
|
||||||
|
return {std::move(s)};
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
return {"<failed to stringify arguments>"};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define FORMAT "%s(%s) failed: %s"
|
||||||
|
template<typename... Arg>
|
||||||
|
inline void log_invocation_failure(const char *e,
|
||||||
|
const char *func_name, const Arg &... a) noexcept
|
||||||
|
{
|
||||||
|
const auto args = describe_arguments(a...);
|
||||||
|
g_warning(FORMAT, func_name, args.c_str(), e);
|
||||||
|
}
|
||||||
|
template<typename... Arg>
|
||||||
|
inline void log_invocation_failure_desc(const char *e, const char *e_desc,
|
||||||
|
const char *func_name, const Arg &... a) noexcept
|
||||||
|
{
|
||||||
|
const auto args = describe_arguments(a...);
|
||||||
|
g_warning(FORMAT": %s", func_name, args.c_str(), e, e_desc);
|
||||||
|
}
|
||||||
|
#undef FORMAT
|
||||||
|
|
||||||
|
struct regular_void {};
|
||||||
|
|
||||||
|
template<typename Invokable, typename... Arg>
|
||||||
|
inline auto invoke(Invokable &&i, const Arg &... a)
|
||||||
|
{
|
||||||
|
using R = decltype(std::invoke(std::forward<Invokable>(i), a...));
|
||||||
|
if constexpr (std::is_void_v<R>)
|
||||||
|
{
|
||||||
|
std::invoke(std::forward<Invokable>(i), a...);
|
||||||
|
return regular_void{};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return std::invoke(std::forward<Invokable>(i), a...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Invokable, typename... Arg>
|
||||||
|
inline auto try_invoke(
|
||||||
|
const char *func_name, Invokable &&i, const Arg &... a) noexcept
|
||||||
|
-> std::optional<decltype(invoke(std::forward<Invokable>(i), a...))>
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return invoke(std::forward<Invokable>(i), a...);
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
log_invocation_failure(e.what(), func_name, a...);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
if (const auto e = impl::get_if_hresult_error(std::current_exception()))
|
||||||
|
{
|
||||||
|
auto hr = make_array("hresult 0x01234567\0");
|
||||||
|
hexify32(static_cast<std::uint32_t>(e->code), std::end(hr)-1);
|
||||||
|
log_invocation_failure_desc(
|
||||||
|
std::begin(hr), e->message.c_str(), func_name, a...);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
log_invocation_failure("unknown error", func_name, a...);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace glib
|
||||||
|
|
||||||
|
|
||||||
|
#define g_try_invoke(invokable, ...) \
|
||||||
|
glib::try_invoke(#invokable, invokable, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define g_try_invoke0(invokable) \
|
||||||
|
glib::try_invoke(#invokable, invokable)
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,61 @@
|
||||||
|
#ifndef __WINRT_ENUMS_H__
|
||||||
|
#define __WINRT_ENUMS_H__
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define WINRT_TYPE_WINDOWS_UI_NOTIFICATIONS_TOAST_DISMISSAL_REASON \
|
||||||
|
(winrtWindowsUINotificationsToastDismissalReason_get_type())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* winrt_Windows_UI_Notifications_Toast_Dismissal_Reason:
|
||||||
|
* @WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_DISMISSAL_REASON_Activated: Notification was activated, clicked or through
|
||||||
|
* a button
|
||||||
|
* @WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_DISMISSAL_REASON_ApplicationHidden: Application was hidden
|
||||||
|
* @WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_DISMISSAL_REASON_TimedOut: Notification timed out
|
||||||
|
*
|
||||||
|
* Reasons for a notification dismissal
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_DISMISSAL_REASON_Activated,
|
||||||
|
WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_DISMISSAL_REASON_ApplicationHidden,
|
||||||
|
WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_DISMISSAL_REASON_TimedOut,
|
||||||
|
} winrtWindowsUINotificationsToastDismissalReason;
|
||||||
|
|
||||||
|
GType winrt_windows_ui_notifications_toast_dismissal_reason_get_type();
|
||||||
|
|
||||||
|
#define WINRT_TYPE_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE \
|
||||||
|
(winrt_windows_ui_notifications_toast_template_type_get_type())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* winrtWindowsUINotificationsToastTemplateType:
|
||||||
|
* @WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastImageAndText01
|
||||||
|
* @WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastImageAndText02
|
||||||
|
* @WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastImageAndText03
|
||||||
|
* @WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastImageAndText04
|
||||||
|
* @WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastText01
|
||||||
|
* @WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastText02
|
||||||
|
* @WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastText03
|
||||||
|
* @WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastText04
|
||||||
|
*
|
||||||
|
* Basic templates for a toast notification.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastImageAndText01,
|
||||||
|
WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastImageAndText02,
|
||||||
|
WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastImageAndText03,
|
||||||
|
WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastImageAndText04,
|
||||||
|
WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastText01,
|
||||||
|
WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastText02,
|
||||||
|
WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastText03,
|
||||||
|
WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastText04,
|
||||||
|
} winrtWindowsUINotificationsToastTemplateType;
|
||||||
|
|
||||||
|
GType winrt_windows_ui_notifications_toast_template_type_get_type();
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __WINRT_ENUMS_H__ */
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef __WINRT_GLIB_EVENTTOKEN_PRIVATE_H__
|
||||||
|
#define __WINRT_GLIB_EVENTTOKEN_PRIVATE_H__
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include "winrt-headers.h"
|
||||||
|
|
||||||
|
#include "winrt-event-token.h"
|
||||||
|
|
||||||
|
winrtEventToken* winrt_event_token_new_from_token(winrt::event_token* token);
|
||||||
|
winrt::event_token* winrt_event_token_get_internal(winrtEventToken* self);
|
||||||
|
|
||||||
|
#endif /* __WINRT_GLIB_EVENTTOKEN_PRIVATE_H__ */
|
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef __WINRT_GLIB_EVENTTOKEN_H__
|
||||||
|
#define __WINRT_GLIB_EVENTTOKEN_H__
|
||||||
|
|
||||||
|
#if !defined(WINRT_GLIB_H_INSIDE) && !defined(WINRT_GLIB_COMPILATION)
|
||||||
|
#error "Only <winrt-glib.h> can be included directly."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "winrt-glib-types.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define WINRT_TYPE_EVENT_TOKEN (winrt_event_token_get_type())
|
||||||
|
|
||||||
|
G_DECLARE_DERIVABLE_TYPE (winrtEventToken, winrt_event_token, WINRT, EVENT_TOKEN, GObject)
|
||||||
|
|
||||||
|
struct _winrtEventTokenClass
|
||||||
|
{
|
||||||
|
/*< private >*/
|
||||||
|
GObjectClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
gint64 winrt_event_token_get_value(winrtEventToken* self);
|
||||||
|
gboolean winrt_event_token_operator_bool(winrtEventToken* self);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __WINRT_GLIB_EVENTTOKEN_H__ */
|
|
@ -0,0 +1,7 @@
|
||||||
|
#ifndef __WINRT_GLIB_TYPES_H__
|
||||||
|
#define __WINRT_GLIB_TYPES_H__
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include "winrt-enums.h"
|
||||||
|
|
||||||
|
#endif /* __WINRT_GLIB_TYPES_H__ */
|
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef __WINRT_GLIB_H__
|
||||||
|
#define __WINRT_GLIB_H__
|
||||||
|
|
||||||
|
#ifndef WINRT_GLIB_H_INSIDE
|
||||||
|
#define WINRT_GLIB_H_INSIDE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "winrt-enums.h"
|
||||||
|
#include "winrt-glib-types.h"
|
||||||
|
|
||||||
|
#include "winrt.h"
|
||||||
|
#include "winrt-toast-notification.h"
|
||||||
|
#include "winrt-event-token.h"
|
||||||
|
#include "winrt-toast-notifier.h"
|
||||||
|
|
||||||
|
#undef WINRT_GLIB_H_INSIDE
|
||||||
|
|
||||||
|
#endif /* __`WINRT_GLIB_H__ */
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef __WINRT_HEADERS_H__
|
||||||
|
#define __WINRT_HEADERS_H__
|
||||||
|
|
||||||
|
#include <winrt/Windows.UI.Notifications.h>
|
||||||
|
#include <winrt/Windows.Data.Xml.Dom.h>
|
||||||
|
#include <winrt/Windows.Foundation.h>
|
||||||
|
#include <winrt/Windows.Foundation.Collections.h>
|
||||||
|
|
||||||
|
#endif /* __WINRT_HEADERS_H__ */
|
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef __WINRT_GLIB_H_PRIVATE__
|
||||||
|
#define __WINRT_GLIB_H_PRIVATE__
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include "winrt.h"
|
||||||
|
#include "gobject/winrt-headers.h"
|
||||||
|
|
||||||
|
#endif // __WINRT_GLIB_H_PRIVATE__
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef __WINRT_GLIB_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION_PRIVATE_H__
|
||||||
|
#define __WINRT_GLIB_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION_PRIVATE_H__
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include "winrt-headers.h"
|
||||||
|
|
||||||
|
#include "winrt-toast-notification.h"
|
||||||
|
|
||||||
|
winrt::Windows::UI::Notifications::ToastNotification* winrt_windows_ui_notifications_toast_notification_get_internal(winrtWindowsUINotificationsToastNotification* self);
|
||||||
|
void winrt_windows_ui_notifications_toast_notification_set_internal(winrtWindowsUINotificationsToastNotification *self, winrt::Windows::UI::Notifications::ToastNotification notification);
|
||||||
|
|
||||||
|
#endif /* __WINRT_GLIB_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION_PRIVATE_H__ */
|
|
@ -0,0 +1,58 @@
|
||||||
|
#ifndef __WINRT_GLIB_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION_H__
|
||||||
|
#define __WINRT_GLIB_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION_H__
|
||||||
|
|
||||||
|
#if !defined(WINRT_GLIB_H_INSIDE) && !defined(WINRT_GLIB_COMPILATION)
|
||||||
|
#error "Only <winrt-glib.h> can be included directly."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "winrt-glib-types.h"
|
||||||
|
#include "winrt-event-token.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define WINRT_TYPE_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION (winrt_windows_ui_notifications_toast_notification_get_type())
|
||||||
|
|
||||||
|
G_DECLARE_DERIVABLE_TYPE (winrtWindowsUINotificationsToastNotification, winrt_windows_ui_notifications_toast_notification, WINRT, WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION, GObject)
|
||||||
|
|
||||||
|
struct _winrtWindowsUINotificationsToastNotificationClass
|
||||||
|
{
|
||||||
|
/*< private >*/
|
||||||
|
GObjectClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef void(*NotificationCallbackFailed)(void* userdata);
|
||||||
|
typedef void(*NotificationCallbackActivated)(const gchar* arguments, gchar** userInput, gint count, void* userdata);
|
||||||
|
typedef void(*NotificationCallbackDismissed)(winrtWindowsUINotificationsToastDismissalReason reason, void* userdata);
|
||||||
|
|
||||||
|
winrtWindowsUINotificationsToastNotification* winrt_windows_ui_notifications_toast_notification_new(const gchar* doc);
|
||||||
|
|
||||||
|
void winrt_windows_ui_notifications_toast_notification_set_ExpiresOnReboot(winrtWindowsUINotificationsToastNotification* self, gboolean value);
|
||||||
|
gboolean winrt_windows_ui_notifications_toast_notification_get_ExpiresOnReboot(winrtWindowsUINotificationsToastNotification* self);
|
||||||
|
|
||||||
|
void winrt_windows_ui_notifications_toast_notification_SetTag(winrtWindowsUINotificationsToastNotification* self, const gchar* value);
|
||||||
|
gchar* winrt_windows_ui_notifications_toast_notification_GetTag(winrtWindowsUINotificationsToastNotification* self);
|
||||||
|
|
||||||
|
void winrt_windows_ui_notifications_toast_notification_SetGroup(winrtWindowsUINotificationsToastNotification* self, const gchar* value);
|
||||||
|
gchar* winrt_windows_ui_notifications_toast_notification_GetGroup(winrtWindowsUINotificationsToastNotification* self);
|
||||||
|
|
||||||
|
winrtEventToken* winrt_windows_ui_notifications_toast_notification_Activated(winrtWindowsUINotificationsToastNotification* self, NotificationCallbackActivated callback, void* context, void(*free)(void*));
|
||||||
|
void winrt_windows_ui_notifications_toast_notification_RemoveActivated(winrtWindowsUINotificationsToastNotification* self, winrtEventToken* token);
|
||||||
|
|
||||||
|
winrtEventToken* winrt_windows_ui_notifications_toast_notification_Failed(winrtWindowsUINotificationsToastNotification* self, NotificationCallbackFailed callback, void* context, void(*free)(void*));
|
||||||
|
void winrt_windows_ui_notifications_toast_notification_RemoveFailed(winrtWindowsUINotificationsToastNotification* self, winrtEventToken* token);
|
||||||
|
|
||||||
|
winrtEventToken* winrt_windows_ui_notifications_toast_notification_Dismissed(winrtWindowsUINotificationsToastNotification* self, NotificationCallbackDismissed callback, void* context, void(*free)(void*));
|
||||||
|
void winrt_windows_ui_notifications_toast_notification_RemoveDismissed(winrtWindowsUINotificationsToastNotification* self, winrtEventToken* token);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __WINRT_GLIB_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION_H__ */
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef __WINRT_GLIB_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFIER_PRIVATE_H__
|
||||||
|
#define __WINRT_GLIB_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFIER_PRIVATE_H__
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include "winrt-headers.h"
|
||||||
|
|
||||||
|
#include "winrt-toast-notifier.h"
|
||||||
|
|
||||||
|
winrt::Windows::UI::Notifications::ToastNotifier* winrt_windows_ui_notifications_toast_notifier_get_internal(winrtWindowsUINotificationsToastNotifier* self);
|
||||||
|
void winrt_windows_ui_notifications_toast_notifier_set_internal(winrtWindowsUINotificationsToastNotifier *self, winrt::Windows::UI::Notifications::ToastNotifier notification);
|
||||||
|
|
||||||
|
#endif /* __WINRT_GLIB_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFIER_PRIVATE_H__ */
|
|
@ -0,0 +1,39 @@
|
||||||
|
#ifndef __WINRT_GLIB_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFIER_H__
|
||||||
|
#define __WINRT_GLIB_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFIER_H__
|
||||||
|
|
||||||
|
#if !defined(WINRT_GLIB_H_INSIDE) && !defined(WINRT_GLIB_COMPILATION)
|
||||||
|
#error "Only <winrt-glib.h> can be included directly."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "winrt-glib-types.h"
|
||||||
|
#include "winrt-toast-notification.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define WINRT_TYPE_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFIER (winrt_windows_ui_notifications_toast_notifier_get_type())
|
||||||
|
|
||||||
|
G_DECLARE_DERIVABLE_TYPE (winrtWindowsUINotificationsToastNotifier, winrt_windows_ui_notifications_toast_notifier, WINRT, WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFIER, GObject)
|
||||||
|
|
||||||
|
struct _winrtWindowsUINotificationsToastNotifierClass
|
||||||
|
{
|
||||||
|
/*< private >*/
|
||||||
|
GObjectClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
winrtWindowsUINotificationsToastNotifier* winrt_windows_ui_notifications_toast_notifier_new(const gchar* aumid);
|
||||||
|
|
||||||
|
void winrt_windows_ui_notifications_toast_notifier_Show(winrtWindowsUINotificationsToastNotifier* self, winrtWindowsUINotificationsToastNotification* toast_notification);
|
||||||
|
void winrt_windows_ui_notifications_toast_notifier_Hide(winrtWindowsUINotificationsToastNotifier* self, winrtWindowsUINotificationsToastNotification* toast_notification);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __WINRT_GLIB_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFIER_H__ */
|
24
plugins/windows-notification/api/include/gobject/winrt.h
Normal file
24
plugins/windows-notification/api/include/gobject/winrt.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef __WINRT_GLIB_2_H__
|
||||||
|
#define __WINRT_GLIB_2_H__
|
||||||
|
|
||||||
|
#if !defined(WINRT_GLIB_H_INSIDE) && !defined(WINRT_GLIB_COMPILATION)
|
||||||
|
#error "Only <winrt-glib.h> can be included directly."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "winrt-enums.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define EXTERN extern "C"
|
||||||
|
#define NOEXCEPT noexcept
|
||||||
|
#else
|
||||||
|
#define EXTERN
|
||||||
|
#define NOEXCEPT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
EXTERN gboolean winrt_InitApartment() NOEXCEPT;
|
||||||
|
EXTERN char* winrt_windows_ui_notifications_toast_notification_manager_GetTemplateContent(winrtWindowsUINotificationsToastTemplateType type) NOEXCEPT;
|
||||||
|
|
||||||
|
#undef EXTERN
|
||||||
|
#undef NOEXCEPT
|
||||||
|
|
||||||
|
#endif // __WINRT_GLIB_2_H__
|
12
plugins/windows-notification/api/include/hexify.hpp
Normal file
12
plugins/windows-notification/api/include/hexify.hpp
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
#ifndef HEXIFY_HPP
|
||||||
|
#define HEXIFY_HPP
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
constexpr void hexify32(std::uint32_t val, char *const end) noexcept
|
||||||
|
{
|
||||||
|
auto p = end-1;
|
||||||
|
for (auto i = 0; i < 32/4; ++i, --p, val >>= 4)
|
||||||
|
*p = "0123456789ABCDEF"[val & ((1u<<4)-1u)];
|
||||||
|
}
|
||||||
|
#endif
|
15
plugins/windows-notification/api/include/make_array.hpp
Normal file
15
plugins/windows-notification/api/include/make_array.hpp
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
#ifndef MAKE_ARRAY_HPP
|
||||||
|
#define MAKE_ARRAY_HPP
|
||||||
|
#include <array>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
template<std::size_t N>
|
||||||
|
inline auto make_array(const char (&from_literal)[N]) noexcept
|
||||||
|
{
|
||||||
|
static_assert(N);
|
||||||
|
std::array<char,N-1> a;
|
||||||
|
std::copy(+from_literal, from_literal+a.size(), a.begin());
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "typedefinitions.h"
|
||||||
|
|
||||||
|
class NotificationHandler {
|
||||||
|
private:
|
||||||
|
dinoWinToastLib_Notification_Callbacks callbacks{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
WinToastHandler(dinoWinToastLib_Notification_Callbacks callbacks);
|
||||||
|
~WinToastHandler();
|
||||||
|
|
||||||
|
// Public interfaces
|
||||||
|
void toastActivated() const;
|
||||||
|
void toastActivated(int actionIndex) const;
|
||||||
|
void toastDismissed(WinToastLib::IWinToastHandler::WinToastDismissalReason state) const;
|
||||||
|
void toastFailed() const;
|
||||||
|
};
|
10
plugins/windows-notification/api/include/overload.hpp
Normal file
10
plugins/windows-notification/api/include/overload.hpp
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
#ifndef OVERLOAD_HPP
|
||||||
|
#define OVERLOAD_HPP
|
||||||
|
template<typename... Callable>
|
||||||
|
struct overload : Callable...
|
||||||
|
{
|
||||||
|
overload(Callable &&... c) : Callable{std::move(c)}... {}
|
||||||
|
using Callable::operator()...;
|
||||||
|
};
|
||||||
|
#endif
|
16
plugins/windows-notification/api/include/shortcutcreator.h
Normal file
16
plugins/windows-notification/api/include/shortcutcreator.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define EXTERN extern "C"
|
||||||
|
#define NOEXCEPT noexcept
|
||||||
|
#else
|
||||||
|
#define EXTERN
|
||||||
|
#define NOEXCEPT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
EXTERN gboolean EnsureAumiddedShortcutExists(const gchar* aumid) NOEXCEPT;
|
||||||
|
|
||||||
|
#undef EXTERN
|
||||||
|
#undef NOEXCEPT
|
1
plugins/windows-notification/api/include/win32.h
Normal file
1
plugins/windows-notification/api/include/win32.h
Normal file
|
@ -0,0 +1 @@
|
||||||
|
#include "win32.hpp"
|
47
plugins/windows-notification/api/include/win32.hpp
Normal file
47
plugins/windows-notification/api/include/win32.hpp
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#include <string>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <exception>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
|
#include "make_array.hpp"
|
||||||
|
#include "hexify.hpp"
|
||||||
|
|
||||||
|
struct win32_error : std::exception
|
||||||
|
{
|
||||||
|
std::uint32_t code;
|
||||||
|
explicit win32_error() noexcept; // initializes with GetLastError()
|
||||||
|
explicit win32_error(const std::uint32_t code) noexcept
|
||||||
|
: code{code}
|
||||||
|
{}
|
||||||
|
const char *what() const noexcept override
|
||||||
|
{
|
||||||
|
// NOTE: thread-unsafe
|
||||||
|
// TODO: decimal representation seems to be more usual for win32 errors
|
||||||
|
msg = make_array("win32 error 0x01234567\0");
|
||||||
|
hexify32(code, std::end(msg)-1);
|
||||||
|
return std::data(msg);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
mutable std::array<char,22+1> msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::wstring GetExePath();
|
||||||
|
std::wstring GetEnv(const wchar_t *variable_name);
|
||||||
|
|
||||||
|
#define EXTERN extern "C"
|
||||||
|
#define NOEXCEPT noexcept
|
||||||
|
#else
|
||||||
|
#define EXTERN
|
||||||
|
#define NOEXCEPT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
EXTERN gboolean IsWindows10() NOEXCEPT;
|
||||||
|
EXTERN gboolean SetProcessAumid(const gchar* aumid) NOEXCEPT;
|
||||||
|
|
||||||
|
#undef EXTERN
|
||||||
|
#undef NOEXCEPT
|
29
plugins/windows-notification/api/src/converter.cpp
Normal file
29
plugins/windows-notification/api/src/converter.cpp
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#include <stringapiset.h>
|
||||||
|
|
||||||
|
#include "converter.hpp"
|
||||||
|
|
||||||
|
// Convert a wide Unicode string to an UTF8 string
|
||||||
|
char* wsview_to_char(const std::wstring_view wstr)
|
||||||
|
{
|
||||||
|
if(wstr.empty())
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
int final_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), nullptr, 0, nullptr, nullptr);
|
||||||
|
gchar* strTo = g_new0(gchar, final_size + 1);
|
||||||
|
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 sview_to_wstr(const std::string_view 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;
|
||||||
|
}
|
32
plugins/windows-notification/api/src/dyn_mod.cpp
Normal file
32
plugins/windows-notification/api/src/dyn_mod.cpp
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
|
||||||
|
#include "dyn_mod.hpp"
|
||||||
|
#include "win32.hpp"
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
namespace dyn_mod
|
||||||
|
{
|
||||||
|
auto load_module(const wchar_t *const path, const char *const dbgnym)
|
||||||
|
{
|
||||||
|
const auto mod = ::LoadLibraryW(path);
|
||||||
|
if (mod)
|
||||||
|
return mod;
|
||||||
|
const win32_error e{};
|
||||||
|
g_warning("failed to load %s", dbgnym);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
punny_func &load_symbol(
|
||||||
|
const wchar_t *const mod_path, const char *const mod_dbgnym,
|
||||||
|
const char *const symbol)
|
||||||
|
{
|
||||||
|
const auto p = reinterpret_cast<punny_func *>(
|
||||||
|
::GetProcAddress(load_module(mod_path, mod_dbgnym), symbol));
|
||||||
|
if (p)
|
||||||
|
return *p;
|
||||||
|
const win32_error e{};
|
||||||
|
g_warning("couldn't find %s in %s", symbol, mod_dbgnym);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
45
plugins/windows-notification/api/src/ginvoke.cpp
Normal file
45
plugins/windows-notification/api/src/ginvoke.cpp
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
|
||||||
|
#include "ginvoke.hpp"
|
||||||
|
#include "converter.hpp"
|
||||||
|
|
||||||
|
#include <winrt/base.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace glib::impl
|
||||||
|
{
|
||||||
|
std::optional<hresult> get_if_hresult_error(
|
||||||
|
const std::exception_ptr p) noexcept try
|
||||||
|
{
|
||||||
|
std::rethrow_exception(p);
|
||||||
|
}
|
||||||
|
catch (const winrt::hresult_error &e)
|
||||||
|
{
|
||||||
|
const char *ptr = nullptr;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto wmsg = std::wstring_view{e.message()};
|
||||||
|
if (not wmsg.empty())
|
||||||
|
{
|
||||||
|
ptr = wsview_to_char(wmsg);
|
||||||
|
if (not ptr)
|
||||||
|
throw 42;
|
||||||
|
std::string msg{ptr};
|
||||||
|
g_free(const_cast<char *>(ptr));
|
||||||
|
// ^ WTF? Deletion is not modification! ^
|
||||||
|
return {{ e.code(), std::move(msg) }};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return {{ e.code(), "<no error description>" }};
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
g_free(const_cast<char *>(ptr));
|
||||||
|
return {{ e.code(), "<failed to stringify error>" }};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
// This is not the exception you are looking for.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
31
plugins/windows-notification/api/src/gobject/winrt-enums.cpp
Normal file
31
plugins/windows-notification/api/src/gobject/winrt-enums.cpp
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#include "winrt-enums.h"
|
||||||
|
|
||||||
|
#define WINRT_GLIB_DEFINE_ENUM_VALUE(value, nick) \
|
||||||
|
{ value, #value, nick },
|
||||||
|
|
||||||
|
#define WINRT_GLIB_DEFINE_ENUM_TYPE(TypeName, type_name, values) \
|
||||||
|
GType type_name##_get_type() \
|
||||||
|
{ \
|
||||||
|
static constexpr GEnumValue v[] = { \
|
||||||
|
values \
|
||||||
|
{ 0, NULL, NULL }, \
|
||||||
|
}; \
|
||||||
|
static const auto enum_type_id = \
|
||||||
|
g_enum_register_static(g_intern_static_string(#TypeName), v); \
|
||||||
|
return enum_type_id; \
|
||||||
|
}
|
||||||
|
|
||||||
|
WINRT_GLIB_DEFINE_ENUM_TYPE (winrtWindowsUINotificationsToastDismissalReason, winrt_windows_ui_notifications_toast_dismissal_reason,
|
||||||
|
WINRT_GLIB_DEFINE_ENUM_VALUE (WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_DISMISSAL_REASON_Activated, "activated")
|
||||||
|
WINRT_GLIB_DEFINE_ENUM_VALUE (WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_DISMISSAL_REASON_ApplicationHidden, "application-hidden")
|
||||||
|
WINRT_GLIB_DEFINE_ENUM_VALUE (WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_DISMISSAL_REASON_TimedOut, "timed-out"))
|
||||||
|
|
||||||
|
WINRT_GLIB_DEFINE_ENUM_TYPE (winrtWindowsUINotificationsToastTemplateType, winrt_windows_ui_notifications_toast_template_type,
|
||||||
|
WINRT_GLIB_DEFINE_ENUM_VALUE (WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastImageAndText01, "toast-image-and-text01")
|
||||||
|
WINRT_GLIB_DEFINE_ENUM_VALUE (WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastImageAndText02, "toast-image-and-text02")
|
||||||
|
WINRT_GLIB_DEFINE_ENUM_VALUE (WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastImageAndText03, "toast-image-and-text03")
|
||||||
|
WINRT_GLIB_DEFINE_ENUM_VALUE (WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastImageAndText04, "toast-image-and-text04")
|
||||||
|
WINRT_GLIB_DEFINE_ENUM_VALUE (WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastText01, "toast-text01")
|
||||||
|
WINRT_GLIB_DEFINE_ENUM_VALUE (WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastText02, "toast-text02")
|
||||||
|
WINRT_GLIB_DEFINE_ENUM_VALUE (WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastText03, "toast-text03")
|
||||||
|
WINRT_GLIB_DEFINE_ENUM_VALUE (WINRT_WINDOWS_UI_NOTIFICATIONS_TOAST_TEMPLATE_TYPE_ToastText04, "toast-text04"))
|
|
@ -0,0 +1,76 @@
|
||||||
|
#include "winrt-event-token-private.h"
|
||||||
|
|
||||||
|
#define WINRT_EVENT_TOKEN_GET_PRIVATE(obj) \
|
||||||
|
((winrtEventTokenPrivate*) winrt_event_token_get_instance_private ((winrtEventToken*) (obj)))
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
winrt::event_token* token;
|
||||||
|
} winrtEventTokenPrivate;
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_WITH_PRIVATE (winrtEventToken, winrt_event_token, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
static void winrt_event_token_finalize(GObject* self)
|
||||||
|
{
|
||||||
|
winrtEventTokenPrivate* priv = WINRT_EVENT_TOKEN_GET_PRIVATE (self);
|
||||||
|
|
||||||
|
delete priv->token;
|
||||||
|
|
||||||
|
G_OBJECT_CLASS(winrt_event_token_parent_class)->dispose(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void winrt_event_token_class_init (winrtEventTokenClass* klass)
|
||||||
|
{
|
||||||
|
GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
|
||||||
|
|
||||||
|
gobject_class->finalize = winrt_event_token_finalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void winrt_event_token_init (winrtEventToken */*self*/)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*< private >
|
||||||
|
* winrt_event_token_get_internal:
|
||||||
|
* @self: a #winrtEventToken
|
||||||
|
*
|
||||||
|
* Retrieves the `winrt::Windows::UI::Notifications::ToastNotification` object used by @self.
|
||||||
|
*
|
||||||
|
* Returns: (transfer none): a pointer to the internal toast notification instance
|
||||||
|
*/
|
||||||
|
winrt::event_token* winrt_event_token_get_internal(winrtEventToken *self)
|
||||||
|
{
|
||||||
|
winrtEventTokenPrivate *priv = WINRT_EVENT_TOKEN_GET_PRIVATE(self);
|
||||||
|
|
||||||
|
return priv->token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*< private >
|
||||||
|
* winrt_event_token_new:
|
||||||
|
* @doc: the document to be shown
|
||||||
|
*
|
||||||
|
* Creates a new toast notification with a document already set.
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): the newly created #winrtEventToken instance
|
||||||
|
*/
|
||||||
|
winrtEventToken* winrt_event_token_new_from_token(winrt::event_token* token)
|
||||||
|
{
|
||||||
|
auto ret = static_cast<winrtEventToken*>(g_object_new (WINRT_TYPE_EVENT_TOKEN, NULL));
|
||||||
|
winrtEventTokenPrivate* priv = WINRT_EVENT_TOKEN_GET_PRIVATE(ret);
|
||||||
|
priv->token = new winrt::event_token(*token);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean winrt_event_token_operator_bool(winrtEventToken* self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(WINRT_IS_EVENT_TOKEN(self), FALSE);
|
||||||
|
|
||||||
|
return winrt_event_token_get_internal(self)->operator bool();
|
||||||
|
}
|
||||||
|
|
||||||
|
gint64 winrt_event_token_get_value(winrtEventToken* self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (WINRT_IS_EVENT_TOKEN (self), 0);
|
||||||
|
|
||||||
|
return winrt_event_token_get_internal(self)->value;
|
||||||
|
}
|
|
@ -0,0 +1,383 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
#include <tuple>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "winrt-toast-notification-private.h"
|
||||||
|
#include "winrt-event-token-private.h"
|
||||||
|
#include "converter.hpp"
|
||||||
|
|
||||||
|
template<typename Cont, typename Pred>
|
||||||
|
inline void erase_if(Cont &c, Pred p)
|
||||||
|
{
|
||||||
|
c.erase(std::remove_if(c.begin(), c.end(), std::move(p)), c.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
#define WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE(obj) \
|
||||||
|
((winrtWindowsUINotificationsToastNotificationPrivate*) winrt_windows_ui_notifications_toast_notification_get_instance_private ((winrtWindowsUINotificationsToastNotification*) (obj)))
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct Callback {
|
||||||
|
private:
|
||||||
|
winrtEventToken* token;
|
||||||
|
|
||||||
|
public:
|
||||||
|
T callback;
|
||||||
|
void* context;
|
||||||
|
void(*free)(void*);
|
||||||
|
|
||||||
|
|
||||||
|
Callback(T callback, void* context, void(*free)(void*))
|
||||||
|
: token {}
|
||||||
|
, callback{callback}
|
||||||
|
, context {context}
|
||||||
|
, free {free}
|
||||||
|
{}
|
||||||
|
|
||||||
|
~Callback()
|
||||||
|
{
|
||||||
|
if (this->callback && this->context && this->free)
|
||||||
|
{
|
||||||
|
this->free(this->context);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->token) {
|
||||||
|
g_object_unref(this->token);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->callback = nullptr;
|
||||||
|
this->context = nullptr;
|
||||||
|
this->free = nullptr;
|
||||||
|
this->token = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetToken(winrtEventToken* token) {
|
||||||
|
this->token = token;
|
||||||
|
g_object_ref(this->token);
|
||||||
|
}
|
||||||
|
|
||||||
|
winrtEventToken* GetToken() {
|
||||||
|
return this->token;
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete copy
|
||||||
|
Callback(const Callback&) = delete;
|
||||||
|
Callback& operator=(const Callback&) = delete;
|
||||||
|
|
||||||
|
// delete move
|
||||||
|
Callback(Callback&&) = delete;
|
||||||
|
Callback& operator=(Callback&&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _winrtWindowsUINotificationsToastNotificationPrivate
|
||||||
|
{
|
||||||
|
winrt::Windows::UI::Notifications::ToastNotification data;
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Callback<NotificationCallbackActivated>>> activated{};
|
||||||
|
std::vector<std::shared_ptr<Callback<NotificationCallbackFailed>>> failed{};
|
||||||
|
std::vector<std::shared_ptr<Callback<NotificationCallbackDismissed>>> dismissed{};
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
_winrtWindowsUINotificationsToastNotificationPrivate* notification;
|
||||||
|
} winrtWindowsUINotificationsToastNotificationPrivate;
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_WITH_PRIVATE (winrtWindowsUINotificationsToastNotification, winrt_windows_ui_notifications_toast_notification, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
static void winrt_windows_ui_notifications_toast_notification_finalize(GObject* self)
|
||||||
|
{
|
||||||
|
winrtWindowsUINotificationsToastNotificationPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE (self);
|
||||||
|
|
||||||
|
for (const auto& item : priv->notification->activated)
|
||||||
|
{
|
||||||
|
auto token = item->GetToken();
|
||||||
|
if (winrt_event_token_operator_bool(token))
|
||||||
|
{
|
||||||
|
priv->notification->data.Activated(*winrt_event_token_get_internal(token));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& item : priv->notification->failed)
|
||||||
|
{
|
||||||
|
auto token = item->GetToken();
|
||||||
|
if (winrt_event_token_operator_bool(token))
|
||||||
|
{
|
||||||
|
priv->notification->data.Failed(*winrt_event_token_get_internal(token));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& item : priv->notification->dismissed)
|
||||||
|
{
|
||||||
|
auto token = item->GetToken();
|
||||||
|
if (winrt_event_token_operator_bool(token))
|
||||||
|
{
|
||||||
|
priv->notification->data.Dismissed(*winrt_event_token_get_internal(token));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete priv->notification;
|
||||||
|
|
||||||
|
G_OBJECT_CLASS(winrt_windows_ui_notifications_toast_notification_parent_class)->dispose(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void winrt_windows_ui_notifications_toast_notification_class_init (winrtWindowsUINotificationsToastNotificationClass* klass)
|
||||||
|
{
|
||||||
|
GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
|
||||||
|
|
||||||
|
gobject_class->finalize = winrt_windows_ui_notifications_toast_notification_finalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void winrt_windows_ui_notifications_toast_notification_init (winrtWindowsUINotificationsToastNotification */*self*/)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*< private >
|
||||||
|
* winrt_windows_ui_notifications_toast_notification_get_internal:
|
||||||
|
* @self: a #winrtWindowsUINotificationsToastNotification
|
||||||
|
*
|
||||||
|
* Retrieves the `winrt::Windows::UI::Notifications::ToastNotification` object used by @self.
|
||||||
|
*
|
||||||
|
* Returns: (transfer none): a pointer to the internal toast notification instance
|
||||||
|
*/
|
||||||
|
winrt::Windows::UI::Notifications::ToastNotification* winrt_windows_ui_notifications_toast_notification_get_internal(winrtWindowsUINotificationsToastNotification *self)
|
||||||
|
{
|
||||||
|
winrtWindowsUINotificationsToastNotificationPrivate *priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE (self);
|
||||||
|
|
||||||
|
return &priv->notification->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*< private >
|
||||||
|
* winrt_windows_ui_notifications_toast_notification_set_internal:
|
||||||
|
* @self: a #winrtWindowsUINotificationsToastNotification
|
||||||
|
* @notification: a `winrt::Windows::UI::Notifications::ToastNotification` instance
|
||||||
|
*
|
||||||
|
* Sets the internal database instance wrapped by @self, clearing
|
||||||
|
* any existing instance if needed.
|
||||||
|
*/
|
||||||
|
void winrt_windows_ui_notifications_toast_notification_set_internal(winrtWindowsUINotificationsToastNotification* self, winrt::Windows::UI::Notifications::ToastNotification notification)
|
||||||
|
{
|
||||||
|
winrtWindowsUINotificationsToastNotificationPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE(self);
|
||||||
|
|
||||||
|
delete priv->notification;
|
||||||
|
|
||||||
|
priv->notification = new _winrtWindowsUINotificationsToastNotificationPrivate { notification };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* winrt_windows_ui_notifications_toast_notification_new:
|
||||||
|
* @doc: the document to be shown
|
||||||
|
*
|
||||||
|
* Creates a new toast notification with a document already set.
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): the newly created #winrtWindowsUINotificationsToastNotification instance
|
||||||
|
*/
|
||||||
|
winrtWindowsUINotificationsToastNotification* winrt_windows_ui_notifications_toast_notification_new(const char* doc)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (doc != NULL, NULL);
|
||||||
|
|
||||||
|
auto ret = static_cast<winrtWindowsUINotificationsToastNotification*>(g_object_new (WINRT_TYPE_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION, NULL));
|
||||||
|
winrt::Windows::Data::Xml::Dom::XmlDocument xmlDoc;
|
||||||
|
xmlDoc.LoadXml(sview_to_wstr(doc));
|
||||||
|
winrt_windows_ui_notifications_toast_notification_set_internal(ret, winrt::Windows::UI::Notifications::ToastNotification{ xmlDoc });
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void winrt_windows_ui_notifications_toast_notification_set_ExpiresOnReboot(winrtWindowsUINotificationsToastNotification* self, gboolean value)
|
||||||
|
{
|
||||||
|
g_return_if_fail (WINRT_IS_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION (self));
|
||||||
|
|
||||||
|
winrt_windows_ui_notifications_toast_notification_get_internal(self)->ExpiresOnReboot(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean winrt_windows_ui_notifications_toast_notification_get_ExpiresOnReboot(winrtWindowsUINotificationsToastNotification* self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (WINRT_IS_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION (self), FALSE);
|
||||||
|
|
||||||
|
return winrt_windows_ui_notifications_toast_notification_get_internal(self)->ExpiresOnReboot();
|
||||||
|
}
|
||||||
|
|
||||||
|
void winrt_windows_ui_notifications_toast_notification_SetTag(winrtWindowsUINotificationsToastNotification* self, const char* value)
|
||||||
|
{
|
||||||
|
g_return_if_fail (WINRT_IS_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION (self));
|
||||||
|
|
||||||
|
winrt_windows_ui_notifications_toast_notification_get_internal(self)->Tag(sview_to_wstr(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* winrt_windows_ui_notifications_toast_notification_GetTag:
|
||||||
|
* @manager: a #winrtWindowsUINotificationsToastNotification
|
||||||
|
*
|
||||||
|
* Returns the value of the tag
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): the value
|
||||||
|
*/
|
||||||
|
char* winrt_windows_ui_notifications_toast_notification_GetTag(winrtWindowsUINotificationsToastNotification* self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (WINRT_IS_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION (self), nullptr);
|
||||||
|
|
||||||
|
return wsview_to_char(winrt_windows_ui_notifications_toast_notification_get_internal(self)->Tag());
|
||||||
|
}
|
||||||
|
|
||||||
|
void winrt_windows_ui_notifications_toast_notification_SetGroup(winrtWindowsUINotificationsToastNotification* self, const char* value)
|
||||||
|
{
|
||||||
|
g_return_if_fail (WINRT_IS_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION (self));
|
||||||
|
|
||||||
|
winrt_windows_ui_notifications_toast_notification_get_internal(self)->Group(sview_to_wstr(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* winrt_windows_ui_notifications_toast_notification_GetGroup:
|
||||||
|
* @manager: a #winrtWindowsUINotificationsToastNotification
|
||||||
|
*
|
||||||
|
* Returns the value of the group
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): the value
|
||||||
|
*/
|
||||||
|
char* winrt_windows_ui_notifications_toast_notification_GetGroup(winrtWindowsUINotificationsToastNotification* self)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (WINRT_IS_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION (self), nullptr);
|
||||||
|
|
||||||
|
return wsview_to_char(winrt_windows_ui_notifications_toast_notification_get_internal(self)->Group());
|
||||||
|
}
|
||||||
|
|
||||||
|
winrtEventToken* winrt_windows_ui_notifications_toast_notification_Activated(winrtWindowsUINotificationsToastNotification* self, NotificationCallbackActivated callback, void* context, void(*free)(void*))
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (WINRT_IS_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION (self), NULL);
|
||||||
|
g_return_val_if_fail (callback != nullptr && context != nullptr && free != nullptr, NULL);
|
||||||
|
|
||||||
|
winrtWindowsUINotificationsToastNotificationPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE(self);
|
||||||
|
|
||||||
|
auto callback_data = std::make_shared<Callback<NotificationCallbackActivated>>(callback, context, free);
|
||||||
|
auto token = priv->notification->data.Activated([=](auto /*sender*/, winrt::Windows::Foundation::IInspectable inspectable)
|
||||||
|
{
|
||||||
|
std::wstring arguments;
|
||||||
|
std::vector<std::tuple<std::wstring, std::wstring>> user_input;
|
||||||
|
{
|
||||||
|
auto args = inspectable.try_as<winrt::Windows::UI::Notifications::IToastActivatedEventArgs>();
|
||||||
|
if (args != nullptr)
|
||||||
|
{
|
||||||
|
arguments = std::wstring(args.Arguments());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto args = inspectable.try_as<winrt::Windows::UI::Notifications::IToastActivatedEventArgs2>();
|
||||||
|
if (args != nullptr)
|
||||||
|
{
|
||||||
|
for (const auto& item : args.UserInput())
|
||||||
|
{
|
||||||
|
auto value = winrt::unbox_value_or<winrt::hstring>(item.Value(), winrt::hstring());
|
||||||
|
user_input.emplace_back(std::make_tuple(std::wstring(item.Key()), std::wstring(value)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto args = wsview_to_char(arguments);
|
||||||
|
callback_data->callback(args, nullptr /* user_input */ , 0 /* user_input.size() */, callback_data->context);
|
||||||
|
g_free(args);
|
||||||
|
});
|
||||||
|
callback_data->SetToken(winrt_event_token_new_from_token(&token));
|
||||||
|
|
||||||
|
priv->notification->activated.push_back(callback_data);
|
||||||
|
return callback_data->GetToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
winrtEventToken* winrt_windows_ui_notifications_toast_notification_Failed(winrtWindowsUINotificationsToastNotification* self, NotificationCallbackFailed callback, void* context, void(*free)(void*))
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (WINRT_IS_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION (self), NULL);
|
||||||
|
g_return_val_if_fail (callback != nullptr && context != nullptr && free != nullptr, NULL);
|
||||||
|
|
||||||
|
winrtWindowsUINotificationsToastNotificationPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE(self);
|
||||||
|
|
||||||
|
auto callback_data = std::make_shared<Callback<NotificationCallbackFailed>>(callback, context, free);
|
||||||
|
auto token = priv->notification->data.Failed([=](auto /*sender*/, auto /*toastFailedEventArgs*/)
|
||||||
|
{
|
||||||
|
callback_data->callback(callback_data->context);
|
||||||
|
});
|
||||||
|
|
||||||
|
callback_data->SetToken(winrt_event_token_new_from_token(&token));
|
||||||
|
|
||||||
|
priv->notification->failed.push_back(callback_data);
|
||||||
|
return callback_data->GetToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
winrtEventToken* winrt_windows_ui_notifications_toast_notification_Dismissed(winrtWindowsUINotificationsToastNotification* self, NotificationCallbackDismissed callback, void* context, void(*free)(void*))
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (WINRT_IS_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION (self), NULL);
|
||||||
|
g_return_val_if_fail (callback != nullptr && context != nullptr && free != nullptr, NULL);
|
||||||
|
|
||||||
|
winrtWindowsUINotificationsToastNotificationPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE(self);
|
||||||
|
|
||||||
|
auto callback_data = std::make_shared<Callback<NotificationCallbackDismissed>>(callback, context, free);
|
||||||
|
auto token = priv->notification->data.Dismissed([=](auto /*sender*/, winrt::Windows::UI::Notifications::ToastDismissedEventArgs dismissed)
|
||||||
|
{
|
||||||
|
auto reason = dismissed.Reason();
|
||||||
|
callback_data->callback(static_cast<winrtWindowsUINotificationsToastDismissalReason>(reason), callback_data->context);
|
||||||
|
});
|
||||||
|
|
||||||
|
callback_data->SetToken(winrt_event_token_new_from_token(&token));
|
||||||
|
|
||||||
|
priv->notification->dismissed.push_back(callback_data);
|
||||||
|
return callback_data->GetToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: refactor `Remove{Activated,Failed,Dismissed}` methods into one to deduplicate code
|
||||||
|
void winrt_windows_ui_notifications_toast_notification_RemoveActivated(winrtWindowsUINotificationsToastNotification* self, winrtEventToken* token)
|
||||||
|
{
|
||||||
|
g_return_if_fail (WINRT_IS_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION (self));
|
||||||
|
|
||||||
|
winrtWindowsUINotificationsToastNotificationPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE(self);
|
||||||
|
|
||||||
|
erase_if(priv->notification->activated, [&](const auto& callback) {
|
||||||
|
if (winrt_event_token_get_value(token) == winrt_event_token_get_value(callback->GetToken()))
|
||||||
|
{
|
||||||
|
if (winrt_event_token_operator_bool(callback->GetToken()))
|
||||||
|
{
|
||||||
|
priv->notification->data.Activated(*winrt_event_token_get_internal(callback->GetToken()));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void winrt_windows_ui_notifications_toast_notification_RemoveFailed(winrtWindowsUINotificationsToastNotification* self, winrtEventToken* token)
|
||||||
|
{
|
||||||
|
g_return_if_fail (WINRT_IS_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION (self));
|
||||||
|
|
||||||
|
winrtWindowsUINotificationsToastNotificationPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE(self);
|
||||||
|
|
||||||
|
erase_if(priv->notification->failed, [&](const auto& callback) {
|
||||||
|
if (winrt_event_token_get_value(token) == winrt_event_token_get_value(callback->GetToken()))
|
||||||
|
{
|
||||||
|
if (winrt_event_token_operator_bool(callback->GetToken()))
|
||||||
|
{
|
||||||
|
priv->notification->data.Failed(*winrt_event_token_get_internal(callback->GetToken()));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void winrt_windows_ui_notifications_toast_notification_RemoveDismissed(winrtWindowsUINotificationsToastNotification* self, winrtEventToken* token)
|
||||||
|
{
|
||||||
|
g_return_if_fail (WINRT_IS_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION (self));
|
||||||
|
|
||||||
|
winrtWindowsUINotificationsToastNotificationPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFICATION_GET_PRIVATE(self);
|
||||||
|
|
||||||
|
erase_if(priv->notification->dismissed, [&](const auto& callback) {
|
||||||
|
if (winrt_event_token_get_value(token) == winrt_event_token_get_value(callback->GetToken()))
|
||||||
|
{
|
||||||
|
if (winrt_event_token_operator_bool(callback->GetToken()))
|
||||||
|
{
|
||||||
|
priv->notification->data.Dismissed(*winrt_event_token_get_internal(callback->GetToken()));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include "winrt-toast-notifier-private.h"
|
||||||
|
#include "winrt-toast-notification-private.h"
|
||||||
|
#include "converter.hpp"
|
||||||
|
|
||||||
|
#define WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFIER_GET_PRIVATE(obj) \
|
||||||
|
((winrtWindowsUINotificationsToastNotifierPrivate*) winrt_windows_ui_notifications_toast_notifier_get_instance_private ((winrtWindowsUINotificationsToastNotifier*) (obj)))
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
winrt::Windows::UI::Notifications::ToastNotifier data;
|
||||||
|
} _winrtWindowsUINotificationsToastNotifierPrivate;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
_winrtWindowsUINotificationsToastNotifierPrivate* notifier;
|
||||||
|
} winrtWindowsUINotificationsToastNotifierPrivate;
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_WITH_PRIVATE (winrtWindowsUINotificationsToastNotifier, winrt_windows_ui_notifications_toast_notifier, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
static void winrt_windows_ui_notifications_toast_notifier_finalize(GObject* self)
|
||||||
|
{
|
||||||
|
winrtWindowsUINotificationsToastNotifierPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFIER_GET_PRIVATE (self);
|
||||||
|
|
||||||
|
delete priv->notifier;
|
||||||
|
|
||||||
|
G_OBJECT_CLASS(winrt_windows_ui_notifications_toast_notifier_parent_class)->dispose(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void winrt_windows_ui_notifications_toast_notifier_class_init (winrtWindowsUINotificationsToastNotifierClass* klass)
|
||||||
|
{
|
||||||
|
GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
|
||||||
|
|
||||||
|
gobject_class->finalize = winrt_windows_ui_notifications_toast_notifier_finalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void winrt_windows_ui_notifications_toast_notifier_init (winrtWindowsUINotificationsToastNotifier */*self*/)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*< private >
|
||||||
|
* winrt_windows_ui_notifications_toast_notifier_get_internal:
|
||||||
|
* @self: a #winrtWindowsUINotificationsToastNotifier
|
||||||
|
*
|
||||||
|
* Retrieves the `winrt::Windows::UI::Notifications::ToastNotifier` object used by @self.
|
||||||
|
*
|
||||||
|
* Returns: (transfer none): a pointer to the internal toast notification instance
|
||||||
|
*/
|
||||||
|
winrt::Windows::UI::Notifications::ToastNotifier* winrt_windows_ui_notifications_toast_notifier_get_internal(winrtWindowsUINotificationsToastNotifier *self)
|
||||||
|
{
|
||||||
|
winrtWindowsUINotificationsToastNotifierPrivate *priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFIER_GET_PRIVATE (self);
|
||||||
|
|
||||||
|
return &priv->notifier->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*< private >
|
||||||
|
* winrt_windows_ui_notifications_toast_notifier_set_internal:
|
||||||
|
* @self: a #winrtWindowsUINotificationsToastNotifier
|
||||||
|
* @notification: a `winrt::Windows::UI::Notifications::ToastNotifier` instance
|
||||||
|
*
|
||||||
|
* Sets the internal database instance wrapped by @self, clearing
|
||||||
|
* any existing instance if needed.
|
||||||
|
*/
|
||||||
|
void winrt_windows_ui_notifications_toast_notifier_set_internal(winrtWindowsUINotificationsToastNotifier* self, winrt::Windows::UI::Notifications::ToastNotifier notifier)
|
||||||
|
{
|
||||||
|
winrtWindowsUINotificationsToastNotifierPrivate* priv = WINRT_WINDOWS_UI_NOTIFICATION_TOAST_NOTIFIER_GET_PRIVATE(self);
|
||||||
|
|
||||||
|
delete priv->notifier;
|
||||||
|
|
||||||
|
priv->notifier = new _winrtWindowsUINotificationsToastNotifierPrivate { notifier };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* winrt_windows_ui_notifications_toast_notifier_new:
|
||||||
|
* @doc: the document to be shown
|
||||||
|
*
|
||||||
|
* Creates a new toast notifier instance with its aumid set
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): the newly created #winrtWindowsUINotificationsToastNotifier instance
|
||||||
|
*/
|
||||||
|
winrtWindowsUINotificationsToastNotifier* winrt_windows_ui_notifications_toast_notifier_new(const gchar* aumid)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (aumid != NULL, NULL);
|
||||||
|
|
||||||
|
auto ret = static_cast<winrtWindowsUINotificationsToastNotifier*>(g_object_new (WINRT_TYPE_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFIER, NULL));
|
||||||
|
auto notifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(sview_to_wstr(aumid));
|
||||||
|
winrt_windows_ui_notifications_toast_notifier_set_internal(ret, notifier);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void winrt_windows_ui_notifications_toast_notifier_Show(winrtWindowsUINotificationsToastNotifier* self, winrtWindowsUINotificationsToastNotification* toast_notification)
|
||||||
|
{
|
||||||
|
g_return_if_fail (WINRT_IS_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFIER (self));
|
||||||
|
g_return_if_fail (WINRT_IS_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION (toast_notification));
|
||||||
|
|
||||||
|
winrt_windows_ui_notifications_toast_notifier_get_internal(self)->Show(*winrt_windows_ui_notifications_toast_notification_get_internal(toast_notification));
|
||||||
|
}
|
||||||
|
|
||||||
|
void winrt_windows_ui_notifications_toast_notifier_Hide(winrtWindowsUINotificationsToastNotifier* self, winrtWindowsUINotificationsToastNotification* toast_notification)
|
||||||
|
{
|
||||||
|
g_return_if_fail (WINRT_IS_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFIER (self));
|
||||||
|
g_return_if_fail (WINRT_IS_WINDOWS_UI_NOTIFICATIONS_TOAST_NOTIFICATION (toast_notification));
|
||||||
|
|
||||||
|
winrt_windows_ui_notifications_toast_notifier_get_internal(self)->Hide(*winrt_windows_ui_notifications_toast_notification_get_internal(toast_notification));
|
||||||
|
}
|
35
plugins/windows-notification/api/src/gobject/winrt.cpp
Normal file
35
plugins/windows-notification/api/src/gobject/winrt.cpp
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
|
||||||
|
#include "gobject/winrt-private.h"
|
||||||
|
#include "converter.hpp"
|
||||||
|
#include "ginvoke.hpp"
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
static void ImplInitApartment()
|
||||||
|
{
|
||||||
|
const auto res = ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
||||||
|
if (FAILED(res))
|
||||||
|
{
|
||||||
|
if (res == RPC_E_CHANGED_MODE) // seems harmless
|
||||||
|
g_info("attempted to change COM apartment mode of thread %" PRIu32,
|
||||||
|
std::uint32_t{::GetCurrentThreadId()});
|
||||||
|
else
|
||||||
|
winrt::throw_hresult(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean winrt_InitApartment() noexcept
|
||||||
|
{
|
||||||
|
return g_try_invoke0(ImplInitApartment).has_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* ImplGetTemplateContent(winrtWindowsUINotificationsToastTemplateType type)
|
||||||
|
{
|
||||||
|
using namespace winrt::Windows::UI::Notifications;
|
||||||
|
return wsview_to_char(ToastNotificationManager::GetTemplateContent(static_cast<ToastTemplateType>(type)).GetXml());
|
||||||
|
}
|
||||||
|
|
||||||
|
char* winrt_windows_ui_notifications_toast_notification_manager_GetTemplateContent(winrtWindowsUINotificationsToastTemplateType type) noexcept
|
||||||
|
{
|
||||||
|
return g_try_invoke(ImplGetTemplateContent, type).value_or(nullptr);
|
||||||
|
}
|
130
plugins/windows-notification/api/src/shortcutcreator.cpp
Normal file
130
plugins/windows-notification/api/src/shortcutcreator.cpp
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
|
||||||
|
#include "shortcutcreator.h"
|
||||||
|
#include "win32.hpp"
|
||||||
|
#include "converter.hpp"
|
||||||
|
#include "ginvoke.hpp"
|
||||||
|
#include "dyn_mod.hpp"
|
||||||
|
|
||||||
|
#include <objbase.h> // COM stuff
|
||||||
|
#include <shlobj.h> // IShellLink
|
||||||
|
#include <propvarutil.h> // InitPropVariantFromString
|
||||||
|
#include <propkey.h> // PKEY_AppUserModel_ID
|
||||||
|
#include <winrt/base.h> // At least one COM header must have been previously
|
||||||
|
// included, for `winrt::create_instance` to work with the `GUID` type.
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace dyn
|
||||||
|
{
|
||||||
|
// PropVariantToString is a pain to use, and
|
||||||
|
// MinGW 6.0.0 doesn't have libpropsys.a in the first place;
|
||||||
|
// MinGW 9.0.0 doesn't have PropVariantToStringAlloc in its libpropsys.a.
|
||||||
|
// So...
|
||||||
|
constexpr auto PropVariantToStringAlloc = [](const auto &... arg)
|
||||||
|
{
|
||||||
|
static const auto &f =
|
||||||
|
dyn_load_symbol("propsys.dll", PropVariantToStringAlloc);
|
||||||
|
return f(arg...);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#define checked(func, args) \
|
||||||
|
if (const auto hr = ((func)args); FAILED(hr)) \
|
||||||
|
{ \
|
||||||
|
g_warning("%s%s failed: hresult 0x%08" PRIX32, \
|
||||||
|
#func, #args, static_cast<std::uint32_t>(hr)); \
|
||||||
|
winrt::throw_hresult(hr); \
|
||||||
|
}
|
||||||
|
|
||||||
|
struct property
|
||||||
|
{
|
||||||
|
property() noexcept : var{} {}
|
||||||
|
|
||||||
|
explicit property(const std::wstring &value)
|
||||||
|
{
|
||||||
|
checked(::InitPropVariantFromString,(value.c_str(), &var));
|
||||||
|
}
|
||||||
|
|
||||||
|
~property()
|
||||||
|
{
|
||||||
|
if (const auto hr = ::PropVariantClear(&var); FAILED(hr))
|
||||||
|
g_critical("PropVariantClear failed: hresult 0x%08" PRIX32,
|
||||||
|
static_cast<std::uint32_t>(hr));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto str() const
|
||||||
|
{
|
||||||
|
wchar_t *str;
|
||||||
|
checked(dyn::PropVariantToStringAlloc,(var, &str));
|
||||||
|
return std::unique_ptr
|
||||||
|
<wchar_t, decltype(&::CoTaskMemFree)>
|
||||||
|
{ str, &::CoTaskMemFree };
|
||||||
|
}
|
||||||
|
|
||||||
|
operator const PROPVARIANT &() const noexcept { return var; }
|
||||||
|
operator PROPVARIANT *() noexcept { return &var; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
PROPVARIANT var;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ImplEnsureAumiddedShortcutExists(
|
||||||
|
const std::string_view menu_rel_path, const std::string_view narrow_aumid)
|
||||||
|
{
|
||||||
|
if (menu_rel_path.empty())
|
||||||
|
throw std::runtime_error{"empty menu-relative shortcut path"};
|
||||||
|
|
||||||
|
const auto aumid = sview_to_wstr(narrow_aumid);
|
||||||
|
|
||||||
|
const auto exe_path = GetExePath();
|
||||||
|
const auto shortcut_path = GetEnv(L"APPDATA")
|
||||||
|
+ LR"(\Microsoft\Windows\Start Menu\)"
|
||||||
|
+ sview_to_wstr(menu_rel_path) + L".lnk";
|
||||||
|
|
||||||
|
const auto lnk = winrt::create_instance<IShellLinkW>(CLSID_ShellLink);
|
||||||
|
const auto file = lnk.as<IPersistFile>();
|
||||||
|
const auto store = lnk.as<IPropertyStore>();
|
||||||
|
|
||||||
|
if (SUCCEEDED(file->Load(shortcut_path.c_str(), STGM_READWRITE)))
|
||||||
|
{
|
||||||
|
property aumid_prop;
|
||||||
|
checked(store->GetValue,(PKEY_AppUserModel_ID, aumid_prop));
|
||||||
|
if (aumid_prop.str().get() != aumid)
|
||||||
|
checked(store->SetValue,(PKEY_AppUserModel_ID, property{aumid}));
|
||||||
|
|
||||||
|
std::array<wchar_t, MAX_PATH+1> targ_path;
|
||||||
|
checked(lnk->GetPath,(targ_path.data(), targ_path.size(), nullptr, 0));
|
||||||
|
if (targ_path.data() != exe_path)
|
||||||
|
checked(lnk->SetPath,(exe_path.c_str()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
checked(store->SetValue,(PKEY_AppUserModel_ID, property{aumid}));
|
||||||
|
checked(lnk->SetPath,(exe_path.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
checked(store->Commit,());
|
||||||
|
|
||||||
|
if (file->IsDirty() != S_FALSE) // not the same as `== S_OK`
|
||||||
|
{
|
||||||
|
constexpr auto set_file_as_current = TRUE;
|
||||||
|
checked(file->Save,(shortcut_path.c_str(), set_file_as_current));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef checked
|
||||||
|
|
||||||
|
} // nameless namespace
|
||||||
|
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
gboolean EnsureAumiddedShortcutExists(const gchar *const aumid) noexcept
|
||||||
|
{
|
||||||
|
return g_try_invoke(
|
||||||
|
ImplEnsureAumiddedShortcutExists, R"(Programs\Dino)", aumid)
|
||||||
|
.has_value();
|
||||||
|
}
|
||||||
|
}
|
104
plugins/windows-notification/api/src/win32.cpp
Normal file
104
plugins/windows-notification/api/src/win32.cpp
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
#include <windows.h>
|
||||||
|
#include <shlobj.h>
|
||||||
|
|
||||||
|
#include "win32.hpp"
|
||||||
|
#include "converter.hpp"
|
||||||
|
#include "ginvoke.hpp"
|
||||||
|
|
||||||
|
#include <winrt/base.h>
|
||||||
|
|
||||||
|
win32_error::win32_error() noexcept
|
||||||
|
: win32_error{::GetLastError()}
|
||||||
|
{}
|
||||||
|
|
||||||
|
constexpr auto noncharacter = L'\uFFFF';
|
||||||
|
|
||||||
|
template<DWORD InitialGuess, typename Oracle>
|
||||||
|
static std::wstring GetStringOfGuessableLength(const Oracle &take_a_guess)
|
||||||
|
{
|
||||||
|
constexpr auto grow = [](const std::size_t s) { return s + s/2; };
|
||||||
|
static_assert(
|
||||||
|
grow(InitialGuess) != InitialGuess, "imminent infinite loop");
|
||||||
|
|
||||||
|
std::wstring buf(InitialGuess, noncharacter);
|
||||||
|
auto maybe_len = take_a_guess(buf.data(), static_cast<DWORD>(buf.size()));
|
||||||
|
|
||||||
|
if (not maybe_len) do
|
||||||
|
{
|
||||||
|
constexpr auto dw_max = std::size_t{std::numeric_limits<DWORD>::max()};
|
||||||
|
if (buf.size() == dw_max)
|
||||||
|
throw std::runtime_error{"wat, string too long for DWORD?"};
|
||||||
|
buf.resize(std::min(grow(buf.size()), dw_max));
|
||||||
|
maybe_len = take_a_guess(buf.data(), static_cast<DWORD>(buf.size()));
|
||||||
|
}
|
||||||
|
while (not maybe_len);
|
||||||
|
|
||||||
|
buf.resize(*maybe_len);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring GetExePath()
|
||||||
|
{
|
||||||
|
const auto try_get_exe_path = [](
|
||||||
|
const auto buf, const auto bufsize) -> std::optional<std::size_t>
|
||||||
|
{
|
||||||
|
constexpr HMODULE exe_module = nullptr;
|
||||||
|
::SetLastError(0); // just in case
|
||||||
|
const auto res = ::GetModuleFileNameW(exe_module, buf, bufsize);
|
||||||
|
if (const auto e = ::GetLastError();
|
||||||
|
e == ERROR_INSUFFICIENT_BUFFER or res == bufsize)
|
||||||
|
return {};
|
||||||
|
else if (not e)
|
||||||
|
return res;
|
||||||
|
else
|
||||||
|
throw win32_error{e};
|
||||||
|
};
|
||||||
|
|
||||||
|
return GetStringOfGuessableLength<MAX_PATH+1>(try_get_exe_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring GetEnv(const wchar_t *const variable_name)
|
||||||
|
{
|
||||||
|
const auto bufsize = ::GetEnvironmentVariableW(variable_name, nullptr, 0);
|
||||||
|
if (not bufsize)
|
||||||
|
throw win32_error{};
|
||||||
|
std::wstring buf(bufsize, noncharacter);
|
||||||
|
::SetLastError(0);
|
||||||
|
const auto res =
|
||||||
|
::GetEnvironmentVariableW(variable_name, buf.data(), bufsize);
|
||||||
|
if (const auto e = ::GetLastError())
|
||||||
|
throw win32_error{e};
|
||||||
|
if (not res or res >= bufsize) // not entirely sure this isn't just paranoia
|
||||||
|
throw std::runtime_error{"GetEnvironmentVariableW misbehaved"};
|
||||||
|
buf.resize(res);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ImplSetProcessAumid(const std::string_view aumid)
|
||||||
|
{
|
||||||
|
winrt::check_hresult(::SetCurrentProcessExplicitAppUserModelID(
|
||||||
|
sview_to_wstr(aumid).c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
// Not available in mingw headers, but linking works.
|
||||||
|
NTSTATUS NTAPI RtlGetVersion(PRTL_OSVERSIONINFOW);
|
||||||
|
|
||||||
|
gboolean IsWindows10() noexcept
|
||||||
|
{
|
||||||
|
RTL_OSVERSIONINFOW rovi = {};
|
||||||
|
rovi.dwOSVersionInfoSize = sizeof(rovi);
|
||||||
|
if (S_OK == RtlGetVersion(&rovi))
|
||||||
|
{
|
||||||
|
return rovi.dwMajorVersion > 6;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean SetProcessAumid(const gchar *const aumid) noexcept
|
||||||
|
{
|
||||||
|
return g_try_invoke(ImplSetProcessAumid, aumid).has_value();
|
||||||
|
}
|
||||||
|
}
|
240
plugins/windows-notification/src/toast_notification_builder.vala
Normal file
240
plugins/windows-notification/src/toast_notification_builder.vala
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
using Dino.Entities;
|
||||||
|
using Dino.Plugins.WindowsNotification.Vapi;
|
||||||
|
using winrt.Windows.UI.Notifications;
|
||||||
|
using Dino.Plugins.WindowsNotification.Vapi.Win32Api;
|
||||||
|
using Xmpp;
|
||||||
|
|
||||||
|
namespace Dino.Plugins.WindowsNotification {
|
||||||
|
private delegate void NodeFunction(StanzaNode node);
|
||||||
|
|
||||||
|
public enum ActivationType {
|
||||||
|
Foreground,
|
||||||
|
Background
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Scenario {
|
||||||
|
Basic,
|
||||||
|
IncomingCall
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Button {
|
||||||
|
public string title;
|
||||||
|
public string arguments;
|
||||||
|
public string imageUri;
|
||||||
|
public ActivationType activationType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ToastNotificationBuilder {
|
||||||
|
private static bool _supportsModernFeatures = IsWindows10();
|
||||||
|
private Gee.List<Button> _buttons = new Gee.ArrayList<Button>();
|
||||||
|
private string _header = null;
|
||||||
|
private string _body = null;
|
||||||
|
private string _appLogo = null;
|
||||||
|
private string _inlineImage = null;
|
||||||
|
private Scenario _scenario = Scenario.Basic;
|
||||||
|
|
||||||
|
public ToastNotificationBuilder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ToastNotificationBuilder AddButton(string title, string arguments, string? imageUri = null, ActivationType activationType = ActivationType.Foreground) {
|
||||||
|
_buttons.add(new Button() { title = title, arguments = arguments, imageUri = imageUri, activationType = activationType });
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ToastNotificationBuilder SetHeader(string? header) {
|
||||||
|
_header = header;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ToastNotificationBuilder SetBody(string? body) {
|
||||||
|
_body = body;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ToastNotificationBuilder SetAppLogo(string? applogo_path) {
|
||||||
|
_appLogo = applogo_path;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ToastNotificationBuilder SetInlineImage(string? image_path) {
|
||||||
|
_inlineImage = image_path;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ToastNotificationBuilder SetScenario(Scenario scenario) {
|
||||||
|
_scenario = scenario;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ToastNotification Build() {
|
||||||
|
if (!_supportsModernFeatures) {
|
||||||
|
return yield BuildFromLegacyTemplate();
|
||||||
|
}
|
||||||
|
return BuildWithToastGeneric();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async StanzaNode BuildStanzaFromXml(string xml) {
|
||||||
|
var reader = new Xmpp.StanzaReader.for_string(xml);
|
||||||
|
|
||||||
|
StanzaNode root_node = yield reader.read_node();
|
||||||
|
|
||||||
|
ExecuteOnAllSubNodes(root_node, (node) => {
|
||||||
|
node.ns_uri = "";
|
||||||
|
foreach (var attr in node.attributes){
|
||||||
|
attr.ns_uri = "";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return root_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Gee.ArrayList<StanzaNode> FindRecursive(StanzaNode node, string tag_name, Gee.List<StanzaAttribute>? attributes) {
|
||||||
|
var ret = new Gee.ArrayList<StanzaNode>();
|
||||||
|
FindRecursiveInternal(node, tag_name, attributes, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FindRecursiveInternal(StanzaNode root_node, string tag_name, Gee.List<StanzaAttribute>? attributes, Gee.List<StanzaNode> list) {
|
||||||
|
if (root_node.name == tag_name) {
|
||||||
|
if (attributes != null) {
|
||||||
|
foreach (var attr in attributes) {
|
||||||
|
var node_attr = root_node.get_attribute_raw(attr.name, attr.ns_uri);
|
||||||
|
if (node_attr != null && node_attr.equals(attr)) {
|
||||||
|
list.add(root_node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
list.add(root_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (var node in root_node.get_all_subnodes()) {
|
||||||
|
FindRecursiveInternal(node, tag_name, attributes, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ToXml(StanzaNode node) {
|
||||||
|
var namespace_state = new NamespaceState();
|
||||||
|
namespace_state.set_current("");
|
||||||
|
return node.to_xml(namespace_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteOnAllSubNodes(StanzaNode root_node, NodeFunction func) {
|
||||||
|
func(root_node);
|
||||||
|
foreach (var node in root_node.get_all_subnodes()) {
|
||||||
|
ExecuteOnAllSubNodes(node, func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy templates, works on both Windows 8.1 and Windows 10:
|
||||||
|
// https://docs.microsoft.com/en-us/previous-versions/windows/apps/hh761494(v=win.10)
|
||||||
|
private async ToastNotification BuildFromLegacyTemplate() {
|
||||||
|
ToastTemplateType templateType = _header != null ? ToastTemplateType.ToastText02 : ToastTemplateType.ToastText01;
|
||||||
|
if (_appLogo != null) {
|
||||||
|
if (templateType == ToastTemplateType.ToastText02) {
|
||||||
|
templateType = ToastTemplateType.ToastImageAndText02;
|
||||||
|
} else {
|
||||||
|
templateType = ToastTemplateType.ToastImageAndText01;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var template = yield BuildStanzaFromXml(ToastNotificationManager.GetTemplateContent(templateType));
|
||||||
|
{ // add header and body
|
||||||
|
var attributes = new Gee.ArrayList<StanzaAttribute>();
|
||||||
|
attributes.add(new StanzaAttribute.build("", "id", "1"));
|
||||||
|
attributes.add(new StanzaAttribute.build("", "id", "2"));
|
||||||
|
|
||||||
|
var nodes = FindRecursive(template, "text", attributes);
|
||||||
|
foreach (var node in nodes) {
|
||||||
|
var attr = node.get_attribute_raw("id", "");
|
||||||
|
if (attr != null) {
|
||||||
|
if (attr.val == "1") {
|
||||||
|
if (templateType == ToastTemplateType.ToastText02 || templateType == ToastTemplateType.ToastImageAndText02) {
|
||||||
|
node.put_node(new StanzaNode.text(_header));
|
||||||
|
} else {
|
||||||
|
node.put_node(new StanzaNode.text(_body));
|
||||||
|
}
|
||||||
|
} else if (attr.val == "2") {
|
||||||
|
node.put_node(new StanzaNode.text(_body));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // add image
|
||||||
|
var nodes = FindRecursive(template, "image", null);
|
||||||
|
foreach (var node in nodes) {
|
||||||
|
var attr = node.get_attribute_raw("src", "");
|
||||||
|
if (attr != null) {
|
||||||
|
attr.val = _appLogo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var xml = ToXml(template);
|
||||||
|
return new ToastNotification(xml);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modern adaptive templates for Windows 10:
|
||||||
|
// https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/adaptive-interactive-toasts?tabs=builder-syntax
|
||||||
|
private ToastNotification BuildWithToastGeneric() {
|
||||||
|
var toast = new StanzaNode.build("toast", "");
|
||||||
|
if (_scenario == Scenario.IncomingCall) {
|
||||||
|
toast.put_attribute("scenario", "incomingCall");
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // add content
|
||||||
|
var visual = new StanzaNode.build("visual", "");
|
||||||
|
{
|
||||||
|
var binding = new StanzaNode.build("binding", "");
|
||||||
|
binding.put_attribute("template", "ToastGeneric");
|
||||||
|
|
||||||
|
if (_header != null) {
|
||||||
|
var header = new StanzaNode.build("text", "");
|
||||||
|
header.put_node(new StanzaNode.text(_header));
|
||||||
|
binding.put_node(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_body != null) {
|
||||||
|
var body = new StanzaNode.build("text", "");
|
||||||
|
body.put_node(new StanzaNode.text(_body));
|
||||||
|
binding.put_node(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_appLogo != null) {
|
||||||
|
var appLogo = new StanzaNode.build("image", "");
|
||||||
|
appLogo.put_attribute("placement", "appLogoOverride");
|
||||||
|
appLogo.put_attribute("src", _appLogo);
|
||||||
|
binding.put_node(appLogo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_inlineImage != null) {
|
||||||
|
var inlineImage = new StanzaNode.build("image", "");
|
||||||
|
inlineImage.put_attribute("src", _inlineImage);
|
||||||
|
binding.put_node(inlineImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
visual.put_node(binding);
|
||||||
|
}
|
||||||
|
toast.put_node(visual);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_buttons.size > 0) { // add actions
|
||||||
|
var actions = new StanzaNode.build("actions", "");
|
||||||
|
foreach (var button in _buttons) {
|
||||||
|
var action = new StanzaNode.build("action", "");
|
||||||
|
action.put_attribute("content", button.title);
|
||||||
|
action.put_attribute("arguments", button.arguments);
|
||||||
|
if (button.activationType == ActivationType.Background) {
|
||||||
|
action.put_attribute("activationType", "background");
|
||||||
|
}
|
||||||
|
actions.put_node(action);
|
||||||
|
}
|
||||||
|
toast.put_node(actions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ToastNotification(ToXml(toast));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
328
plugins/windows-notification/src/win_notification_provider.vala
Normal file
328
plugins/windows-notification/src/win_notification_provider.vala
Normal file
|
@ -0,0 +1,328 @@
|
||||||
|
using Dino;
|
||||||
|
using Dino.Entities;
|
||||||
|
using winrt.Windows.UI.Notifications;
|
||||||
|
using Xmpp;
|
||||||
|
using Gee;
|
||||||
|
|
||||||
|
namespace Dino.Plugins.WindowsNotification {
|
||||||
|
public class WindowsNotificationProvider : NotificationProvider, Object {
|
||||||
|
|
||||||
|
private delegate void DelegateToUi();
|
||||||
|
|
||||||
|
private static uint32 notification_counter = 0;
|
||||||
|
private ToastNotifier notifier;
|
||||||
|
private StreamInteractor stream_interactor;
|
||||||
|
private Dino.Application app;
|
||||||
|
|
||||||
|
private Gee.List<uint32> marked_for_removal;
|
||||||
|
|
||||||
|
// we must keep a reference to the notification itself or else their actions are disabled
|
||||||
|
private HashMap<uint, ToastNotification> notifications;
|
||||||
|
private Gee.List<uint32> content_notifications;
|
||||||
|
private HashMap<Conversation, Gee.List<uint32>> conversation_notifications;
|
||||||
|
private HashMap<Call, uint32> call_notifications;
|
||||||
|
|
||||||
|
public WindowsNotificationProvider(Dino.Application app, ToastNotifier notifier) {
|
||||||
|
this.notifier = notifier;
|
||||||
|
this.stream_interactor = app.stream_interactor;
|
||||||
|
this.app = app;
|
||||||
|
this.marked_for_removal = new Gee.ArrayList<uint32>();
|
||||||
|
this.content_notifications = new Gee.ArrayList<uint32>();
|
||||||
|
this.conversation_notifications = new HashMap<Conversation, Gee.List<uint32>>(Conversation.hash_func, Conversation.equals_func);
|
||||||
|
this.call_notifications = new HashMap<Call, uint32>(Call.hash_func, Call.equals_func);
|
||||||
|
this.notifications = new HashMap<uint, ToastNotification>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public double get_priority() {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void notify_message(Message message, Conversation conversation, string conversation_display_name, string? participant_display_name) {
|
||||||
|
yield notify_content_item(conversation, conversation_display_name, participant_display_name, message.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void notify_file(FileTransfer file_transfer, Conversation conversation, bool is_image, string conversation_display_name, string? participant_display_name) {
|
||||||
|
string text = "";
|
||||||
|
if (file_transfer.direction == Message.DIRECTION_SENT) {
|
||||||
|
text = is_image ? _("Image sent") : _("File sent");
|
||||||
|
} else {
|
||||||
|
text = is_image ? _("Image received") : _("File received");
|
||||||
|
}
|
||||||
|
|
||||||
|
string? inlineImagePath = null;
|
||||||
|
if (file_transfer.state == FileTransfer.State.COMPLETE) {
|
||||||
|
inlineImagePath = file_transfer.get_file().get_path();
|
||||||
|
}
|
||||||
|
|
||||||
|
yield notify_content_item(conversation, conversation_display_name, participant_display_name, text, inlineImagePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void notify_subscription_request(Conversation conversation) {
|
||||||
|
string summary = _("Subscription request");
|
||||||
|
string body = Markup.escape_text(conversation.counterpart.to_string());
|
||||||
|
|
||||||
|
var image_path = get_avatar(conversation);
|
||||||
|
var notification = yield new ToastNotificationBuilder()
|
||||||
|
.SetHeader(summary)
|
||||||
|
.SetBody(body)
|
||||||
|
.SetAppLogo(image_path)
|
||||||
|
.AddButton(_("Accept"), "accept-subscription")
|
||||||
|
.AddButton(_("Deny"), "deny-subscription")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var notification_id = generate_id();
|
||||||
|
notification.Activated((argument, user_input) => {
|
||||||
|
run_on_ui(() => {
|
||||||
|
if (argument != null) {
|
||||||
|
app.activate_action(argument, conversation.id);
|
||||||
|
} else {
|
||||||
|
app.activate_action("open-conversation", conversation.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
marked_for_removal.add(notification_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
notification.Dismissed((reason) => marked_for_removal.add(notification_id));
|
||||||
|
|
||||||
|
notification.Failed(() => marked_for_removal.add(notification_id));
|
||||||
|
|
||||||
|
notifications[notification_id] = notification;
|
||||||
|
|
||||||
|
if (!conversation_notifications.has_key(conversation)) {
|
||||||
|
conversation_notifications[conversation] = new ArrayList<uint32>();
|
||||||
|
}
|
||||||
|
conversation_notifications[conversation].add(notification_id);
|
||||||
|
|
||||||
|
notifier.Show(notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void notify_connection_error(Account account, ConnectionManager.ConnectionError error) {
|
||||||
|
string summary = _("Could not connect to %s").printf(account.bare_jid.domainpart);
|
||||||
|
string body = "";
|
||||||
|
switch (error.source) {
|
||||||
|
case ConnectionManager.ConnectionError.Source.SASL:
|
||||||
|
body = _("Wrong password");
|
||||||
|
break;
|
||||||
|
case ConnectionManager.ConnectionError.Source.TLS:
|
||||||
|
body = _("Invalid TLS certificate");
|
||||||
|
break;
|
||||||
|
case ConnectionManager.ConnectionError.Source.STREAM_ERROR:
|
||||||
|
body = "Stream Error";
|
||||||
|
break;
|
||||||
|
case ConnectionManager.ConnectionError.Source.CONNECTION:
|
||||||
|
body = "Connection";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var notification = yield new ToastNotificationBuilder()
|
||||||
|
.SetHeader(summary)
|
||||||
|
.SetBody(body)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var notification_id = generate_id();
|
||||||
|
notification.Activated((argument, user_input) => marked_for_removal.add(notification_id));
|
||||||
|
notification.Dismissed((reason) => marked_for_removal.add(notification_id));
|
||||||
|
notification.Failed(() => marked_for_removal.add(notification_id));
|
||||||
|
|
||||||
|
notifications[notification_id] = notification;
|
||||||
|
notifier.Show(notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void notify_muc_invite(Account account, Jid room_jid, Jid from_jid, string inviter_display_name) {
|
||||||
|
Conversation direct_conversation = new Conversation(from_jid, account, Conversation.Type.CHAT);
|
||||||
|
|
||||||
|
string display_room = room_jid.bare_jid.to_string();
|
||||||
|
string summary = _("Invitation to %s").printf(display_room);
|
||||||
|
string body = _("%s invited you to %s").printf(inviter_display_name, display_room);
|
||||||
|
|
||||||
|
var image_path = get_avatar(direct_conversation);
|
||||||
|
var notification = yield new ToastNotificationBuilder()
|
||||||
|
.SetHeader(summary)
|
||||||
|
.SetBody(body)
|
||||||
|
.SetAppLogo(image_path)
|
||||||
|
.AddButton(_("Accept"), "open-muc-join")
|
||||||
|
.AddButton(_("Deny"), "deny-invite")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var notification_id = generate_id();
|
||||||
|
var group_conversation_id = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(room_jid, account, Conversation.Type.GROUPCHAT).id;
|
||||||
|
notification.Activated((argument, user_input) => {
|
||||||
|
run_on_ui(() => {
|
||||||
|
if (argument != null) {
|
||||||
|
app.activate_action(argument, group_conversation_id);
|
||||||
|
} else {
|
||||||
|
app.activate_action("open-muc-join", group_conversation_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
marked_for_removal.add(notification_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
notification.Dismissed((reason) => marked_for_removal.add(notification_id));
|
||||||
|
|
||||||
|
notification.Failed(() => marked_for_removal.add(notification_id));
|
||||||
|
|
||||||
|
notifications[notification_id] = notification;
|
||||||
|
notifier.Show(notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void notify_voice_request(Conversation conversation, Jid from_jid) {
|
||||||
|
string display_name = Dino.get_participant_display_name(stream_interactor, conversation, from_jid);
|
||||||
|
string display_room = Dino.get_conversation_display_name(stream_interactor, conversation, _("%s from %s"));
|
||||||
|
string summary = _("Permission request");
|
||||||
|
string body = _("%s requests the permission to write in %s").printf(display_name, display_room);
|
||||||
|
|
||||||
|
var image_path = get_avatar(conversation);
|
||||||
|
var notification = yield new ToastNotificationBuilder()
|
||||||
|
.SetHeader(summary)
|
||||||
|
.SetBody(body)
|
||||||
|
.SetAppLogo(image_path)
|
||||||
|
.AddButton(_("Accept"), "accept-voice-request")
|
||||||
|
.AddButton(_("Deny"), "deny-voice-request")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var notification_id = generate_id();
|
||||||
|
notification.Activated((argument, user_input) => {
|
||||||
|
if (argument != null) {
|
||||||
|
run_on_ui(() => app.activate_action(argument, conversation.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
marked_for_removal.add(notification_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
notification.Dismissed((reason) => marked_for_removal.add(notification_id));
|
||||||
|
|
||||||
|
notification.Failed(() => marked_for_removal.add(notification_id));
|
||||||
|
|
||||||
|
notifications[notification_id] = notification;
|
||||||
|
notifier.Show(notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void notify_content_item(Conversation conversation, string conversation_display_name, string? participant_display_name, string body_, string? inlineImagePath = null) {
|
||||||
|
clear_marked();
|
||||||
|
|
||||||
|
string body = body_;
|
||||||
|
if (participant_display_name != null) {
|
||||||
|
body = @"$participant_display_name: $body";
|
||||||
|
}
|
||||||
|
|
||||||
|
var image_path = get_avatar(conversation);
|
||||||
|
var notification = yield new ToastNotificationBuilder()
|
||||||
|
.SetHeader(conversation_display_name)
|
||||||
|
.SetBody(body)
|
||||||
|
.SetAppLogo(image_path)
|
||||||
|
.SetInlineImage(inlineImagePath)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var notification_id = generate_id();
|
||||||
|
notification.Activated((argument, user_input) => {
|
||||||
|
run_on_ui(() => app.activate_action("open-conversation", conversation.id));
|
||||||
|
marked_for_removal.add(notification_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
notification.Dismissed((reason) => marked_for_removal.add(notification_id));
|
||||||
|
|
||||||
|
notification.Failed(() => marked_for_removal.add(notification_id));
|
||||||
|
|
||||||
|
notifications[notification_id] = notification;
|
||||||
|
notifier.Show(notification);
|
||||||
|
|
||||||
|
content_notifications.add(notification_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? get_avatar(Conversation conversation) {
|
||||||
|
var avatar_manager = app.stream_interactor.get_module(AvatarManager.IDENTITY);
|
||||||
|
return avatar_manager.get_avatar_filepath(conversation.account, conversation.counterpart);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void retract_content_item_notifications() {
|
||||||
|
foreach (uint id in content_notifications) {
|
||||||
|
remove_notification(id);
|
||||||
|
}
|
||||||
|
content_notifications.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void retract_conversation_notifications(Conversation conversation) {
|
||||||
|
if (conversation_notifications.has_key(conversation)) {
|
||||||
|
var conversation_items = conversation_notifications[conversation];
|
||||||
|
foreach (uint id in conversation_items) {
|
||||||
|
remove_notification(id);
|
||||||
|
}
|
||||||
|
conversation_items.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void notify_call(Call call, Conversation conversation, bool video, bool multiparty, string conversation_display_name) {
|
||||||
|
string summary = Markup.escape_text(conversation_display_name);
|
||||||
|
string body = video ? _("Incoming video call") : _("Incoming call");
|
||||||
|
if (multiparty) {
|
||||||
|
body = video ? _("Incoming video group call") : _("Incoming group call");
|
||||||
|
}
|
||||||
|
|
||||||
|
var image_path = get_avatar(conversation);
|
||||||
|
var notification = yield new ToastNotificationBuilder()
|
||||||
|
.SetHeader(summary)
|
||||||
|
.SetBody(body)
|
||||||
|
.SetAppLogo(image_path)
|
||||||
|
.AddButton(_("Accept"), "accept-call")
|
||||||
|
.AddButton(_("Deny"), "reject-call", null, ActivationType.Background)
|
||||||
|
.SetScenario(Scenario.IncomingCall)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var notification_id = generate_id();
|
||||||
|
notification.Activated((argument, user_input) => {
|
||||||
|
run_on_ui(() => {
|
||||||
|
if (argument != null) {
|
||||||
|
app.activate_action(argument, new Variant.tuple(new Variant[] { new Variant.int32(conversation.id), new Variant.int32(call.id) }));
|
||||||
|
} else {
|
||||||
|
app.activate_action("open-conversation", conversation.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
marked_for_removal.add(notification_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
notification.Dismissed((reason) => marked_for_removal.add(notification_id));
|
||||||
|
|
||||||
|
notification.Failed(() => marked_for_removal.add(notification_id));
|
||||||
|
|
||||||
|
notifications[notification_id] = notification;
|
||||||
|
|
||||||
|
call_notifications[call] = notification_id;
|
||||||
|
|
||||||
|
notifier.Show(notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void retract_call_notification(Call call, Conversation conversation) {
|
||||||
|
if (call_notifications.has_key(call)) {
|
||||||
|
var notification_id = call_notifications[call];
|
||||||
|
remove_notification(notification_id);
|
||||||
|
call_notifications.unset(call);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clear_marked() {
|
||||||
|
foreach (var id in marked_for_removal) {
|
||||||
|
remove_notification(id);
|
||||||
|
}
|
||||||
|
marked_for_removal.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void remove_notification(uint id) {
|
||||||
|
ToastNotification notification = null;
|
||||||
|
notifications.unset(id, out notification);
|
||||||
|
if (notification != null) {
|
||||||
|
notifier.Hide(notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private uint32 generate_id() {
|
||||||
|
return AtomicUint.add(ref notification_counter, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void run_on_ui(owned DelegateToUi func) {
|
||||||
|
Idle.add(() => { func(); return false; }, GLib.Priority.HIGH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
using Dino.Entities;
|
||||||
|
using Dino.Plugins.WindowsNotification.Vapi;
|
||||||
|
using winrt.Windows.UI.Notifications;
|
||||||
|
using Xmpp;
|
||||||
|
|
||||||
|
namespace Dino.Plugins.WindowsNotification {
|
||||||
|
public class Plugin : RootInterface, Object {
|
||||||
|
|
||||||
|
private static string AUMID = "org.dino.Dino";
|
||||||
|
|
||||||
|
public void registered(Dino.Application app) {
|
||||||
|
|
||||||
|
if (!winrt.InitApartment())
|
||||||
|
{
|
||||||
|
// log error, return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Win32Api.SetProcessAumid(AUMID))
|
||||||
|
{
|
||||||
|
// log error, return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ShortcutCreator.EnsureAumiddedShortcutExists(AUMID))
|
||||||
|
{
|
||||||
|
// log error, return
|
||||||
|
}
|
||||||
|
|
||||||
|
app.stream_interactor.get_module(NotificationEvents.IDENTITY)
|
||||||
|
.register_notification_provider(new WindowsNotificationProvider(app, new ToastNotifier(AUMID)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
public Type register_plugin(Module module) {
|
||||||
|
return typeof (Dino.Plugins.WindowsNotification.Plugin);
|
||||||
|
}
|
9
plugins/windows-notification/vapi/enums.vapi
Normal file
9
plugins/windows-notification/vapi/enums.vapi
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[CCode (cheader_filename = "enums.h")]
|
||||||
|
namespace Dino.Plugins.WindowsNotification.Vapi.Enums {
|
||||||
|
[CCode (cname = "Dismissed_Reason", cprefix = "Dismissed_Reason_")]
|
||||||
|
public enum DismissedReason {
|
||||||
|
Activated,
|
||||||
|
ApplicationHidden,
|
||||||
|
TimedOut
|
||||||
|
}
|
||||||
|
}
|
5
plugins/windows-notification/vapi/shortcutcreator.vapi
Normal file
5
plugins/windows-notification/vapi/shortcutcreator.vapi
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[CCode (cheader_filename = "shortcutcreator.h")]
|
||||||
|
namespace Dino.Plugins.WindowsNotification.Vapi.ShortcutCreator {
|
||||||
|
[CCode (cname = "EnsureAumiddedShortcutExists")]
|
||||||
|
public bool EnsureAumiddedShortcutExists(string aumid);
|
||||||
|
}
|
9
plugins/windows-notification/vapi/win32.vapi
Normal file
9
plugins/windows-notification/vapi/win32.vapi
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[CCode (cheader_filename = "win32.h")]
|
||||||
|
namespace Dino.Plugins.WindowsNotification.Vapi.Win32Api {
|
||||||
|
[CCode (cname = "IsWindows10")]
|
||||||
|
public bool IsWindows10();
|
||||||
|
|
||||||
|
[CCode (cname = "SetProcessAumid")]
|
||||||
|
public bool SetProcessAumid(string aumid);
|
||||||
|
}
|
||||||
|
|
13
plugins/windows-notification/vapi/winrt.vapi
Normal file
13
plugins/windows-notification/vapi/winrt.vapi
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[CCode (cheader_filename = "gobject/winrt-glib.h")]
|
||||||
|
namespace winrt {
|
||||||
|
public bool InitApartment();
|
||||||
|
|
||||||
|
[CCode (type_id = "winrt_event_token_get_type ()")]
|
||||||
|
public class EventToken : GLib.Object {
|
||||||
|
[CCode (has_construct_function = false)]
|
||||||
|
public EventToken();
|
||||||
|
public int64 value { get; }
|
||||||
|
[CCode(cname = "winrt_event_token_operator_bool")]
|
||||||
|
public bool IsValid();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
using winrt;
|
||||||
|
|
||||||
|
[CCode (cheader_filename = "gobject/winrt-glib.h")]
|
||||||
|
namespace winrt.Windows.UI.Notifications {
|
||||||
|
|
||||||
|
[CCode (type_id = "winrt_windows_ui_notifications_toast_dismissal_reason_get_type ()")]
|
||||||
|
public enum ToastDismissalReason
|
||||||
|
{
|
||||||
|
Activated,
|
||||||
|
ApplicationHidden,
|
||||||
|
TimedOut
|
||||||
|
}
|
||||||
|
|
||||||
|
[CCode (type_id = "winrt_windows_ui_notifications_toast_template_type_get_type ()")]
|
||||||
|
public enum ToastTemplateType
|
||||||
|
{
|
||||||
|
ToastImageAndText01,
|
||||||
|
ToastImageAndText02,
|
||||||
|
ToastImageAndText03,
|
||||||
|
ToastImageAndText04,
|
||||||
|
ToastText01,
|
||||||
|
ToastText02,
|
||||||
|
ToastText03,
|
||||||
|
ToastText04
|
||||||
|
}
|
||||||
|
|
||||||
|
[CCode (cname = "NotificationCallbackFailed", has_target = true)]
|
||||||
|
public delegate void NotificationCallbackFailed();
|
||||||
|
|
||||||
|
[CCode (cname = "NotificationCallbackActivated", has_target = true)]
|
||||||
|
public delegate void NotificationCallbackActivated(string? arguments, string[]? userInput);
|
||||||
|
|
||||||
|
[CCode (cname = "NotificationCallbackDismissed", has_target = true)]
|
||||||
|
public delegate void NotificationCallbackDismissed(ToastDismissalReason reason);
|
||||||
|
|
||||||
|
[CCode (type_id = "winrt_windows_ui_notifications_toast_notification_get_type ()")]
|
||||||
|
public class ToastNotification : GLib.Object {
|
||||||
|
public ToastNotification(string doc);
|
||||||
|
public bool ExpiresOnReboot { get; set; }
|
||||||
|
|
||||||
|
public EventToken Activated(owned NotificationCallbackActivated handler);
|
||||||
|
public void RemoveActivated(EventToken token);
|
||||||
|
|
||||||
|
public EventToken Failed(owned NotificationCallbackFailed handler);
|
||||||
|
public void RemoveFailed(EventToken token);
|
||||||
|
|
||||||
|
public EventToken Dismissed(owned NotificationCallbackDismissed handler);
|
||||||
|
public void RemoveDismissed(EventToken token);
|
||||||
|
|
||||||
|
public string GetTag();
|
||||||
|
public void SetTag(string tag);
|
||||||
|
public string GetGroup();
|
||||||
|
public void SetGroup(string group);
|
||||||
|
}
|
||||||
|
|
||||||
|
[CCode (type_id = "winrt_windows_ui_notifications_toast_notifier_get_type ()")]
|
||||||
|
public class ToastNotifier : GLib.Object {
|
||||||
|
public ToastNotifier(string aumid);
|
||||||
|
public void Show(ToastNotification notification);
|
||||||
|
public void Hide(ToastNotification notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Compact]
|
||||||
|
[CCode]
|
||||||
|
public class ToastNotificationManager {
|
||||||
|
public static string GetTemplateContent(ToastTemplateType type);
|
||||||
|
}
|
||||||
|
}
|
15
windows-installer/LICENSE_SHORT
Normal file
15
windows-installer/LICENSE_SHORT
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
Dino - Modern Jabber/XMPP Client using GTK+/Vala
|
||||||
|
Copyright (C) 2016-2020 Dino contributors
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
68
windows-installer/dino.nsi
Normal file
68
windows-installer/dino.nsi
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
Unicode True
|
||||||
|
|
||||||
|
RequestExecutionLevel user
|
||||||
|
SetCompressor /SOLID lzma
|
||||||
|
|
||||||
|
!define MUI_PRODUCT "Dino"
|
||||||
|
!define MUI_PRODUCT_NAME ${MUI_PRODUCT}
|
||||||
|
!define MUI_BRANDINGTEXT ${MUI_PRODUCT}
|
||||||
|
!define PRODUCT_WEBSITE "https://dino.im"
|
||||||
|
!define MUI_ICON "win64-dist/dino.ico"
|
||||||
|
!define ICON "win64-dist/dino.ico"
|
||||||
|
!define MUI_COMPONENTSPAGE_NODESC
|
||||||
|
|
||||||
|
# Modern Interface
|
||||||
|
!include "MUI2.nsh"
|
||||||
|
!insertmacro MUI_PAGE_LICENSE "LICENSE_SHORT"
|
||||||
|
!insertmacro MUI_PAGE_INSTFILES
|
||||||
|
!include "english.nsh"
|
||||||
|
|
||||||
|
Name ${MUI_PRODUCT}
|
||||||
|
BrandingText "Communicating happiness"
|
||||||
|
|
||||||
|
# define installer name
|
||||||
|
OutFile "dino-installer.exe"
|
||||||
|
|
||||||
|
# set install directory
|
||||||
|
InstallDir $APPDATA\Dino
|
||||||
|
|
||||||
|
Section
|
||||||
|
|
||||||
|
# Install all files
|
||||||
|
SetOutPath $INSTDIR
|
||||||
|
File /r win64-dist\*.*
|
||||||
|
|
||||||
|
# define uninstaller name
|
||||||
|
WriteUninstaller $INSTDIR\uninstaller.exe
|
||||||
|
|
||||||
|
# Create a shortcut for startmenu
|
||||||
|
CreateDirectory "$SMPROGRAMS\Dino"
|
||||||
|
CreateShortcut "$SMPROGRAMS\Dino\Dino.lnk" "$INSTDIR\bin\dino.exe" "" "$INSTDIR\dino.ico"
|
||||||
|
CreateShortcut "$SMPROGRAMS\Dino\Uninstaller.lnk" "$INSTDIR\uninstaller.exe"
|
||||||
|
CreateShortcut "$SMPROGRAMS\Dino\License.lnk" "notepad.exe" "$INSTDIR\LICENSE"
|
||||||
|
CreateShortcut "$SMPROGRAMS\Dino\Dino website.lnk" "https://dino.im" "" "$INSTDIR\dino.ico"
|
||||||
|
|
||||||
|
# Create a shortcut for desktop
|
||||||
|
CreateShortCut "$DESKTOP\Dino.lnk" "$INSTDIR\bin\dino.exe" "" "$INSTDIR\dino.ico"
|
||||||
|
|
||||||
|
# set application ID
|
||||||
|
# No "ApplicationID" plugin for NSIS MINGW64
|
||||||
|
# ApplicationID::Set "$SMPROGRAMS\Dino\Dino.lnk" "Dino" "true"
|
||||||
|
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
# Uninstaller section
|
||||||
|
Section "Uninstall"
|
||||||
|
|
||||||
|
# Delete startmenu folder
|
||||||
|
RMDir /r "$SMPROGRAMS\Dino"
|
||||||
|
|
||||||
|
# Always delete uninstaller first
|
||||||
|
Delete $INSTDIR\uninstaller.exe
|
||||||
|
|
||||||
|
# now delete installed file
|
||||||
|
Delete $INSTDIR\*
|
||||||
|
|
||||||
|
# Delete the directory
|
||||||
|
RMDir /r $INSTDIR
|
||||||
|
SectionEnd
|
3
windows-installer/english.nsh
Normal file
3
windows-installer/english.nsh
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
!define MUI_TEXT_LICENSE_TITLE "Dino License"
|
||||||
|
!define MUI_TEXT_LICENSE_SUBTITLE "Please read the license short summary carefully"
|
||||||
|
!insertmacro MUI_LANGUAGE "English"
|
Loading…
Reference in a new issue