1 /***************************************************************************
2 * Copyright (C) 2005-2020 by the Quassel Project *
3 * devel@quassel-irc.org *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
20
21 #include "eventmanager.h"
22
23 #include <QCoreApplication>
24 #include <QDebug>
25 #include <QEvent>
26
27 #include "event.h"
28 #include "ircevent.h"
29
30 // ============================================================
31 // QueuedEvent
32 // ============================================================
33 class QueuedQuasselEvent : public QEvent
34 {
35 public:
QueuedQuasselEvent(Event * event)36 QueuedQuasselEvent(Event* event)
37 : QEvent(QEvent::User)
38 , event(event)
39 {}
40 Event* event;
41 };
42
43 // ============================================================
44 // EventManager
45 // ============================================================
EventManager(QObject * parent)46 EventManager::EventManager(QObject* parent)
47 : QObject(parent)
48 {}
49
eventEnum()50 QMetaEnum EventManager::eventEnum()
51 {
52 if (!_enum.isValid()) {
53 int eventEnumIndex = staticMetaObject.indexOfEnumerator("EventType");
54 Q_ASSERT(eventEnumIndex >= 0);
55 _enum = staticMetaObject.enumerator(eventEnumIndex);
56 Q_ASSERT(_enum.isValid());
57 }
58 return _enum;
59 }
60
eventTypeByName(const QString & name)61 EventManager::EventType EventManager::eventTypeByName(const QString& name)
62 {
63 int val = eventEnum().keyToValue(name.toLatin1());
64 return (val == -1) ? Invalid : static_cast<EventType>(val);
65 }
66
eventGroupByName(const QString & name)67 EventManager::EventType EventManager::eventGroupByName(const QString& name)
68 {
69 EventType type = eventTypeByName(name);
70 return type == Invalid ? Invalid : static_cast<EventType>(type & EventGroupMask);
71 }
72
enumName(EventType type)73 QString EventManager::enumName(EventType type)
74 {
75 return eventEnum().valueToKey(type);
76 }
77
enumName(int type)78 QString EventManager::enumName(int type)
79 {
80 return eventEnum().valueToKey(type);
81 }
82
createEvent(const QVariantMap & map)83 Event* EventManager::createEvent(const QVariantMap& map)
84 {
85 QVariantMap m = map;
86
87 Network* net = networkById(m.take("network").toInt());
88 return Event::fromVariantMap(m, net);
89 }
90
91 /* NOTE:
92 Registering and calling handlers works fine even if they specify a subclass of Event as their parameter.
93 However, this most probably is a result from a reinterpret_cast somewhere deep inside Qt, so there is *no*
94 type safety. If the event sent is of the wrong class type, you'll get a neat segfault!
95 Thus, we need to make sure that events are of the correct class type when sending!
96
97 We might add a registration-time check later, which will require matching the enum base name (e.g. "IrcEvent") with
98 the type the handler claims to support. This still won't protect us from someone sending an IrcEvent object
99 with an enum type "NetworkIncoming", for example.
100
101 Another way would be to add a check into the various Event subclasses, such that the ctor matches the given event type
102 with the actual class. Possibly (optionally) using rtti...
103 */
104
findEventType(const QString & methodSignature_,const QString & methodPrefix) const105 int EventManager::findEventType(const QString& methodSignature_, const QString& methodPrefix) const
106 {
107 if (!methodSignature_.startsWith(methodPrefix))
108 return -1;
109
110 QString methodSignature = methodSignature_;
111
112 methodSignature = methodSignature.section('(', 0, 0); // chop the attribute list
113 methodSignature = methodSignature.mid(methodPrefix.length()); // strip prefix
114
115 int eventType = -1;
116
117 // special handling for numeric IrcEvents: IrcEvent042 gets mapped to IrcEventNumeric + 42
118 if (methodSignature.length() == 8 + 3 && methodSignature.startsWith("IrcEvent")) {
119 int num = methodSignature.right(3).toUInt();
120 if (num > 0) {
121 QString numericSig = methodSignature.left(methodSignature.length() - 3) + "Numeric";
122 eventType = eventEnum().keyToValue(numericSig.toLatin1());
123 if (eventType < 0) {
124 qWarning() << Q_FUNC_INFO << "Could not find EventType" << numericSig << "for handling" << methodSignature;
125 return -1;
126 }
127 eventType += num;
128 }
129 }
130
131 if (eventType < 0)
132 eventType = eventEnum().keyToValue(methodSignature.toLatin1());
133 if (eventType < 0) {
134 qWarning() << Q_FUNC_INFO << "Could not find EventType" << methodSignature;
135 return -1;
136 }
137 return eventType;
138 }
139
registerObject(QObject * object,Priority priority,const QString & methodPrefix,const QString & filterPrefix)140 void EventManager::registerObject(QObject* object, Priority priority, const QString& methodPrefix, const QString& filterPrefix)
141 {
142 for (int i = object->metaObject()->methodOffset(); i < object->metaObject()->methodCount(); i++) {
143 QString methodSignature = object->metaObject()->method(i).methodSignature();
144 int eventType = findEventType(methodSignature, methodPrefix);
145 if (eventType > 0) {
146 Handler handler(object, i, priority);
147 registeredHandlers()[eventType].append(handler);
148 // qDebug() << "Registered event handler for" << methodSignature << "in" << object;
149 }
150 eventType = findEventType(methodSignature, filterPrefix);
151 if (eventType > 0) {
152 Handler handler(object, i, priority);
153 registeredFilters()[eventType].append(handler);
154 // qDebug() << "Registered event filterer for" << methodSignature << "in" << object;
155 }
156 }
157 }
158
registerEventFilter(EventType event,QObject * object,const char * slot)159 void EventManager::registerEventFilter(EventType event, QObject* object, const char* slot)
160 {
161 registerEventHandler(QList<EventType>() << event, object, slot, NormalPriority, true);
162 }
163
registerEventFilter(QList<EventType> events,QObject * object,const char * slot)164 void EventManager::registerEventFilter(QList<EventType> events, QObject* object, const char* slot)
165 {
166 registerEventHandler(events, object, slot, NormalPriority, true);
167 }
168
registerEventHandler(EventType event,QObject * object,const char * slot,Priority priority,bool isFilter)169 void EventManager::registerEventHandler(EventType event, QObject* object, const char* slot, Priority priority, bool isFilter)
170 {
171 registerEventHandler(QList<EventType>() << event, object, slot, priority, isFilter);
172 }
173
registerEventHandler(QList<EventType> events,QObject * object,const char * slot,Priority priority,bool isFilter)174 void EventManager::registerEventHandler(QList<EventType> events, QObject* object, const char* slot, Priority priority, bool isFilter)
175 {
176 int methodIndex = object->metaObject()->indexOfMethod(slot);
177 if (methodIndex < 0) {
178 qWarning() << Q_FUNC_INFO << QString("Slot %1 not found in object %2").arg(slot).arg(object->objectName());
179 return;
180 }
181 Handler handler(object, methodIndex, priority);
182 foreach (EventType event, events) {
183 if (isFilter) {
184 registeredFilters()[event].append(handler);
185 qDebug() << "Registered event filter for" << event << "in" << object;
186 }
187 else {
188 registeredHandlers()[event].append(handler);
189 qDebug() << "Registered event handler for" << event << "in" << object;
190 }
191 }
192 }
193
postEvent(Event * event)194 void EventManager::postEvent(Event* event)
195 {
196 if (sender() && sender()->thread() != this->thread()) {
197 auto* queuedEvent = new QueuedQuasselEvent(event);
198 QCoreApplication::postEvent(this, queuedEvent);
199 }
200 else {
201 if (_eventQueue.isEmpty())
202 // we're currently not processing events
203 processEvent(event);
204 else
205 _eventQueue.append(event);
206 }
207 }
208
customEvent(QEvent * event)209 void EventManager::customEvent(QEvent* event)
210 {
211 if (event->type() == QEvent::User) {
212 auto* queuedEvent = static_cast<QueuedQuasselEvent*>(event);
213 processEvent(queuedEvent->event);
214 event->accept();
215 }
216 }
217
processEvent(Event * event)218 void EventManager::processEvent(Event* event)
219 {
220 Q_ASSERT(_eventQueue.isEmpty());
221 dispatchEvent(event);
222 // dispatching the event might cause new events to be generated. we process those afterwards.
223 while (!_eventQueue.isEmpty()) {
224 dispatchEvent(_eventQueue.first());
225 _eventQueue.removeFirst();
226 }
227 }
228
dispatchEvent(Event * event)229 void EventManager::dispatchEvent(Event* event)
230 {
231 // qDebug() << "Dispatching" << event;
232
233 // we try handlers from specialized to generic by masking the enum
234
235 // build a list sorted by priorities that contains all eligible handlers
236 QList<Handler> handlers;
237 QHash<QObject*, Handler> filters;
238 QSet<QObject*> ignored;
239 uint type = event->type();
240
241 bool checkDupes = false;
242
243 // special handling for numeric IrcEvents
244 if ((type & ~IrcEventNumericMask) == IrcEventNumeric) {
245 auto* numEvent = static_cast<::IrcEventNumeric*>(event);
246 if (!numEvent)
247 qWarning() << "Invalid event type for IrcEventNumeric!";
248 else {
249 int num = numEvent->number();
250 if (num > 0) {
251 insertHandlers(registeredHandlers().value(type + num), handlers, false);
252 insertFilters(registeredFilters().value(type + num), filters);
253 checkDupes = true;
254 }
255 }
256 }
257
258 // exact type
259 insertHandlers(registeredHandlers().value(type), handlers, checkDupes);
260 insertFilters(registeredFilters().value(type), filters);
261
262 // check if we have a generic handler for the event group
263 if ((type & EventGroupMask) != type) {
264 insertHandlers(registeredHandlers().value(type & EventGroupMask), handlers, true);
265 insertFilters(registeredFilters().value(type & EventGroupMask), filters);
266 }
267
268 // now dispatch the event
269 QList<Handler>::const_iterator it;
270 for (it = handlers.begin(); it != handlers.end() && !event->isStopped(); ++it) {
271 QObject* obj = it->object;
272
273 if (ignored.contains(obj)) // object has filtered the event
274 continue;
275
276 if (filters.contains(obj)) { // we have a filter, so let's check if we want to deliver the event
277 Handler filter = filters.value(obj);
278 bool result = false;
279 void* param[] = {Q_RETURN_ARG(bool, result).data(), Q_ARG(Event*, event).data()};
280 obj->qt_metacall(QMetaObject::InvokeMetaMethod, filter.methodIndex, param);
281 if (!result) {
282 ignored.insert(obj);
283 continue; // mmmh, event filter told us to not accept
284 }
285 }
286
287 // finally, deliverance!
288 void* param[] = {nullptr, Q_ARG(Event*, event).data()};
289 obj->qt_metacall(QMetaObject::InvokeMetaMethod, it->methodIndex, param);
290 }
291
292 // that's it
293 delete event;
294 }
295
insertHandlers(const QList<Handler> & newHandlers,QList<Handler> & existing,bool checkDupes)296 void EventManager::insertHandlers(const QList<Handler>& newHandlers, QList<Handler>& existing, bool checkDupes)
297 {
298 foreach (const Handler& handler, newHandlers) {
299 if (existing.isEmpty())
300 existing.append(handler);
301 else {
302 // need to insert it at the proper position, but only if we don't yet have a handler for this event and object!
303 bool insert = true;
304 QList<Handler>::iterator insertpos = existing.end();
305 QList<Handler>::iterator it = existing.begin();
306 while (it != existing.end()) {
307 if (checkDupes && handler.object == it->object) {
308 insert = false;
309 break;
310 }
311 if (insertpos == existing.end() && handler.priority > it->priority)
312 insertpos = it;
313
314 ++it;
315 }
316 if (insert)
317 existing.insert(it, handler);
318 }
319 }
320 }
321
322 // priority is ignored, and only the first (should be most specialized) filter is being used
323 // fun things could happen if you used the registerEventFilter() methods in the wrong order though
insertFilters(const QList<Handler> & newFilters,QHash<QObject *,Handler> & existing)324 void EventManager::insertFilters(const QList<Handler>& newFilters, QHash<QObject*, Handler>& existing)
325 {
326 foreach (const Handler& filter, newFilters) {
327 if (!existing.contains(filter.object))
328 existing[filter.object] = filter;
329 }
330 }
331
332 QMetaEnum EventManager::_enum;
333