1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
|
/**
* Copyright (C) 2021-2024 Saturneric <eric@bktus.com>
*
* This file is part of GpgFrontend.
*
* GpgFrontend 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.
*
* GpgFrontend 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 GpgFrontend. If not, see <https://www.gnu.org/licenses/>.
*
* The initial version of the source code is inherited from
* the gpg4usb project, which is under GPL-3.0-or-later.
*
* All the source code of GpgFrontend was modified and released by
* Saturneric <eric@bktus.com> starting on May 12, 2021.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#pragma once
#include <GFSDKBasic.h>
#include <GFSDKModule.h>
#include <QMap>
#include <QString>
#include <functional>
#include "GFModuleConvert.h"
#include "GFModuleMemory.h"
/**
* @file GFModuleEvent.h
* @brief Subscribing to events, handling them, and answering them.
*
* A module says what it wants to hear with LISTEN(), declares a handler per
* event with REGISTER_EVENT_HANDLER(), and answers with CB_SUCC() or CB_ERR().
* The dispatch table in between is generated by
* DEFINE_EXECUTE_API_USING_STANDARD_EVEN_HANDLE_MODEL, which the bootstrap
* macro expands for you -- a module does not normally write it out.
*
* Every one of these expands GFGetModuleID(), which is why that accessor is
* the one piece of generated identity that still exists.
*/
/**
* @brief This module's identifier.
*
* Declared here because this header's macros are what call it. It is *defined*
* by GF_MODULE_BOOTSTRAP() in exactly one translation unit per module, from
* the identifier in module.json -- so a second copy cannot exist to disagree
* with the signed manifest the host checks it against.
*/
auto GFGetModuleID() -> const char*;
/**
* @brief The lifecycle functions every module defines.
*
* Written out rather than produced by a macro. `GF_MODULE_API_DECLARE` used to
* expand to eleven of these, six of which described entry points the host
* stopped resolving when the bootstrap table replaced them -- so the
* declarations outlived the functions and nothing noticed, because a
* declaration nobody defines costs nothing until somebody calls it.
*
* GF_MODULE_BOOTSTRAP() adapts these onto the table the host actually uses.
*/
extern "C" {
auto GFRegisterModule() -> int;
auto GFActiveModule() -> int;
auto GFExecuteModule(GFModuleEvent*) -> int;
auto GFDeactivateModule() -> int;
auto GFUnregisterModule() -> int;
}
/// Subscribe to an event by id.
///
/// No DUP: SDK arguments are borrowed, so pre-allocating here would leak.
#define LISTEN(event) GFModuleListenEvent(GFGetModuleID(), (event))
// ------------------------------------------------------------- conversions
inline auto ConvertEventParamsToMap(GFModuleEventParam* params)
-> QMap<QString, QString> {
QMap<QString, QString> param_map;
GFModuleEventParam* current = params;
GFModuleEventParam* last;
while (current != nullptr) {
const auto name = UDUP(current->name);
const auto value = USECDUP(current->value);
if (!name.isEmpty()) param_map[name] = value;
last = current;
current = current->next;
GFFreeMemory(last);
}
return param_map;
}
inline auto ConvertMapToParams(const QMap<QString, QString>& param_map)
-> GFModuleEventParam* {
GFModuleEventParam* head = nullptr;
GFModuleEventParam* prev = nullptr;
#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
for (const auto& [key, value] : param_map.asKeyValueRange()) {
auto* param = static_cast<GFModuleEventParam*>(
GFAllocateMemory(sizeof(GFModuleEventParam)));
param->name = DUP(key.toUtf8());
param->value = SECDUP(value.toUtf8());
param->next = nullptr;
if (prev == nullptr) {
head = param;
} else {
prev->next = param;
}
prev = param;
}
#else
for (auto it = param_map.keyValueBegin(); it != param_map.keyValueEnd();
++it) {
auto* param = static_cast<GFModuleEventParam*>(
GFAllocateMemory(sizeof(GFModuleEventParam)));
param->name = DUP(it->first.toUtf8());
param->value = SECDUP(it->second.toUtf8());
param->next = nullptr;
if (prev == nullptr) {
head = param;
} else {
prev->next = param;
}
prev = param;
}
#endif
return head;
}
inline auto ConvertEventToMap(GFModuleEvent* event) -> QMap<QString, QString> {
QMap<QString, QString> event_map;
event_map["event_id"] = UDUP(event->id);
event_map["trigger_id"] = UDUP(event->trigger_id);
event_map.insert(ConvertEventParamsToMap(event->params));
GFFreeMemory(event);
return event_map;
}
inline auto ConvertMapToEvent(QMap<QString, QString> event_map)
-> GFModuleEvent* {
auto* event =
static_cast<GFModuleEvent*>(GFAllocateMemory(sizeof(GFModuleEvent)));
event->id = DUP(event_map["event_id"].toUtf8());
event->trigger_id = DUP(event_map["trigger_id"].toUtf8());
event->params = nullptr;
return event;
}
// ---------------------------------------------------------------- answering
inline void CB(const QMap<QString, QString>& event, const char* module,
const QMap<QString, QString>& params) {
GFModuleTriggerModuleEventCallback(ConvertMapToEvent(event), module,
ConvertMapToParams(params));
}
#undef CB_SUCC
/// Answer an event with success, and return from the handler.
#define CB_SUCC(event) \
{ \
CB(event, GFGetModuleID(), {{"ret", "0"}}); \
return 0; \
}
#undef CB_ERR
/// Answer an event with a failure, and return that code from the handler.
#define CB_ERR(event, ret, err) \
{ \
CB(event, GFGetModuleID(), \
{{"ret", QString::number(ret)}, {"err", QString(err)}}); \
return ret; \
}
#undef CB_ERR_NO_RET
/// The same, without returning -- for a handler with cleanup still to do.
#define CB_ERR_NO_RET(event, ret, err) \
{ \
CB(event, GFGetModuleID(), \
{{"ret", QString::number(ret)}, {"err", QString(err)}}); \
}
// ----------------------------------------------------------------- dispatch
#define EXECUTE_MODULE() \
auto GFExecuteModule(GFModuleEvent* p_event)->int { \
auto event = ConvertEventToMap(p_event);
#define END_EXECUTE_MODULE() }
#define DEFINE_EXECUTE_API_USING_STANDARD_EVEN_HANDLE_MODEL \
EXECUTE_MODULE() { \
auto event_id = event["event_id"]; \
auto it = _gr_module_event_handlers.find(event_id); \
if (it != _gr_module_event_handlers.end()) { \
return it.value()(event); \
} \
CB_ERR(event, -1, QString("unsupported event id: %1").arg(event_id)); \
} \
END_EXECUTE_MODULE()
/// Register a handler for one event id, at static-initialisation time.
#define REGISTER_EVENT_HANDLER(event_id, handler) \
namespace { \
auto _gv_register_event_handler_by_id_##event_id = []() -> const bool { \
_gr_module_event_handlers[#event_id] = handler; \
return true; \
}(); \
}
|