1/*
2 * LegacyClonk
3 *
4 * Copyright (c) 2021, The LegacyClonk Team and contributors
5 *
6 * Distributed under the terms of the ISC license; see accompanying file
7 * "COPYING" for details.
8 *
9 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
10 * See accompanying file "TRADEMARK" for details.
11 *
12 * To redistribute this file separately, substitute the full license texts
13 * for the above references.
14 */
15
16#include "C4TextEncoding.h"
17#include "C4ToastLibNotify.h"
18
19#include "C4Language.h"
20#include "C4Log.h"
21#include "C4Version.h"
22
23#include "StdBuf.h"
24
25C4ToastSystemLibNotify::C4ToastSystemLibNotify()
26{
27 if (!notify_init(C4ENGINENAME))
28 {
29 throw std::runtime_error{"notify_init failed"};
30 }
31}
32
33C4ToastSystemLibNotify::~C4ToastSystemLibNotify()
34{
35 notify_uninit();
36}
37
38std::unique_ptr<C4Toast> C4ToastSystemLibNotify::CreateToast()
39{
40 return std::make_unique<C4ToastLibNotify>();
41}
42
43C4ToastLibNotify::C4ToastLibNotify() : C4Toast{}, notification{notify_notification_new(summary: "", body: "", icon: "")}
44{
45 notify_notification_add_action(notification, action: "default", label: "Default", callback: &C4ToastLibNotify::Activated, user_data: this, free_func: nullptr);
46}
47
48C4ToastLibNotify::~C4ToastLibNotify()
49{
50 g_object_unref(G_OBJECT(notification));
51}
52
53void C4ToastLibNotify::AddAction(std::string_view action)
54{
55 const size_t index{actions.size()};
56 actions.emplace_back(args&: action);
57
58 notify_notification_add_action(
59 notification,
60 action: std::format(fmt: "action-{}", args: index).c_str(),
61 label: TextEncodingConverter.ClonkToUtf8<char>(input: action).c_str(),
62 callback: &C4ToastLibNotify::OnAction,
63 user_data: new UserData{.toast: this, .index: index},
64 free_func: operator delete
65 );
66}
67
68void C4ToastLibNotify::SetExpiration(std::uint32_t expiration)
69{
70 notify_notification_set_timeout(notification, timeout: expiration * 1000u);
71}
72
73void C4ToastLibNotify::SetText(std::string_view text)
74{
75 this->text = text;
76}
77
78void C4ToastLibNotify::SetTitle(std::string_view title)
79{
80 this->title = title;
81}
82
83void C4ToastLibNotify::Show()
84{
85 signalClose = g_signal_connect(notification, "closed", G_CALLBACK(&C4ToastLibNotify::Dismissed), this);
86 notify_notification_update(notification, summary: TextEncodingConverter.ClonkToUtf8<char>(input: title).c_str(), body: TextEncodingConverter.ClonkToUtf8<char>(input: text).c_str(), icon: nullptr);
87
88 if (GError *error{nullptr}; !notify_notification_show(notification, error: &error) && eventHandler)
89 {
90 if (error)
91 {
92 LogNTr(level: spdlog::level::err, fmt: "C4ToastLibNotify: Failed to show notification: {}", args: TextEncodingConverter.Utf8ToClonk(input: title).c_str());
93 }
94 eventHandler->Failed();
95 }
96}
97
98void C4ToastLibNotify::Hide()
99{
100 g_signal_handler_disconnect(instance: notification, handler_id: signalClose);
101 if (GError *error{nullptr}; !notify_notification_close(notification, error: &error) && error)
102 {
103 LogNTr(level: spdlog::level::err, fmt: "C4ToastLibNotify: Failed to hide notification: {}", args&: error->message);
104 }
105}
106
107void C4ToastLibNotify::Activated(NotifyNotification *, char *, gpointer userData)
108{
109 if (auto *const toast = reinterpret_cast<C4ToastLibNotify *>(userData); toast->eventHandler)
110 {
111 toast->eventHandler->Activated();
112 }
113}
114
115void C4ToastLibNotify::OnAction(NotifyNotification *, char *, gpointer userData)
116{
117 if (auto [toast, index] = *reinterpret_cast<UserData *>(userData); toast->eventHandler)
118 {
119 toast->eventHandler->OnAction(action: toast->actions.at(n: index));
120 }
121}
122
123void C4ToastLibNotify::Dismissed(NotifyNotification *, gpointer userData)
124{
125 if (auto *const toast = reinterpret_cast<C4ToastLibNotify *>(userData); toast->eventHandler)
126 {
127 toast->eventHandler->Dismissed();
128 }
129}
130