Compare commits
183 commits
master
...
master-win
Author | SHA1 | Date | |
---|---|---|---|
089f956ca5 | |||
22984621fb | |||
c826ea18a6 | |||
40fa2fc335 | |||
89d84edd8e | |||
ca1f97e243 | |||
9c15a81b40 | |||
3848cf3129 | |||
43502806bc | |||
b886e2a2f2 | |||
4674e90d16 | |||
a3171e12df | |||
5f500b4d42 | |||
f9fd66dba5 | |||
1c156e8c9f | |||
f6fe383ce8 | |||
49f331627d | |||
7603990740 | |||
7872d21f03 | |||
86182a7db9 | |||
82e6937f21 | |||
3d61f175a6 | |||
471f8c5f4a | |||
fb05c83c8e | |||
ef41c27cb2 | |||
e638ef0694 | |||
a7f7a6d388 | |||
d507808e40 | |||
2d82a4c195 | |||
c29032bcc6 | |||
90bd9df891 | |||
d10badd690 | |||
11828cc2cf | |||
a583a44994 | |||
dd71e943c3 | |||
b8b74817f7 | |||
43fd04ce41 | |||
733d612b18 | |||
5a335f89d3 | |||
c57fc768aa | |||
88a376c1c2 | |||
97753bd5c4 | |||
d7118c1b93 | |||
96fbbdd8bb | |||
4b0c3b8ef0 | |||
ee11a2dbcf | |||
064541dd2b | |||
854818133e | |||
878bf9ac43 | |||
e6a521ada9 | |||
359dcbf70c | |||
c40775b7da | |||
934b492357 | |||
0258980977 | |||
8034a6f344 | |||
c89671af04 | |||
822b91f40c | |||
332216e63c | |||
7b6f3ea9f6 | |||
8988b77d57 | |||
987eb672d5 | |||
1698d15f7d | |||
17c1172d84 | |||
8f8dfa2e70 | |||
e8d84d2eda | |||
f1bcb6604f | |||
c855d5e7cb | |||
ed7c23c8d2 | |||
b1cb64d716 | |||
da424fb73b | |||
39b51b2d4c | |||
7cb6b3f4b3 | |||
15abe70fc6 | |||
ab6ce05aec | |||
c44b1daaf7 | |||
5e50d1d2d7 | |||
d3214188d5 | |||
229ff697c4 | |||
c3624f8490 | |||
597061aed1 | |||
fd035084de | |||
426f997f9b | |||
c60e0a828f | |||
cc0e206c27 | |||
60791cb3f0 | |||
f9a3b28624 | |||
cb3c6874bc | |||
eeda464ca9 | |||
f193948f4e | |||
401c4a1bb1 | |||
34519e96bd | |||
fb754b0d43 | |||
2eb1eea06e | |||
5b40d166d2 | |||
2ad659f777 | |||
b21066c89e | |||
1bd1376cea | |||
f1b5633ce6 | |||
3d9dcbcf87 | |||
8856fcbceb | |||
94944a9e2a | |||
7de1c01fe8 | |||
0f555da7a9 | |||
f2c689fa12 | |||
839d2a5316 | |||
be0e1841b8 | |||
2476b5e04b | |||
bc9b9b95e0 | |||
9d9b9e8e42 | |||
0153953b5d | |||
363b200272 | |||
c6a96fc025 | |||
898470ed23 | |||
6cdaad315a | |||
91d61843f2 | |||
c4f526329d | |||
40bf3d2fd4 | |||
7fd918f32d | |||
1d1b00222f | |||
8df226ec29 | |||
13e0a5c0c4 | |||
ca0cab0e36 | |||
ae9671716f | |||
ce0deed0dc | |||
95051d304a | |||
915dd4c738 | |||
e3356bb3aa | |||
03774a3756 | |||
2d63c8ae77 | |||
96f96ead7e | |||
5f2e636868 | |||
d8bb9897a4 | |||
08f2391acc | |||
eae628758c | |||
931f09504d | |||
5702b323c9 | |||
198bce4a84 | |||
8553a7cd86 | |||
642ed6ab1b | |||
d36de2b9ea | |||
49978edebf | |||
65a26bce53 | |||
1d4bb774a5 | |||
3dd19fad71 | |||
4a3306b479 | |||
0b9bebd97e | |||
24890ca38a | |||
50b2a0dc0a | |||
a6515ed826 | |||
e7fd7f4726 | |||
9022ac596b | |||
823df3ed70 | |||
6d5ee03e9e | |||
1673ec3481 | |||
16d0cc6fd9 | |||
03f17b8f39 | |||
25673ab145 | |||
8cb15fc325 | |||
281a84a14c | |||
e2fd821bb4 | |||
0d384f83ae | |||
1841b348f1 | |||
5f5b8fdfa0 | |||
eb7cf3297c | |||
5d551360df | |||
a94acccd1d | |||
d0d676e72d | |||
81721307ca | |||
2d73f97d34 | |||
e24aa9aabc | |||
26a85ac109 | |||
8adf6731e7 | |||
9154edaee1 | |||
fd916bd6e6 | |||
48619ee84d | |||
70900cd73e | |||
64260ee8c0 | |||
246000ce6a | |||
9779abe701 | |||
3ffec591a9 | |||
7d6b9e7f4c | |||
43f118bd4e | |||
057a72cd9f |
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")
|
||||||
|
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})
|
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