summaryrefslogtreecommitdiffstats
path: root/src/input/inputEventHandler.h
blob: 783640f78f6180d414ff1aaba065c7abe159060b (plain) (blame)
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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
/*
 # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
 #
 # This program is free software distributed under the GPL version 2.
 # See http://openslx.org/COPYING
 #
 # If you have any feedback please consult http://openslx.org/feedback and
 # send your suggestions, praise, or complaints to feedback@openslx.org
 #
 # General information about OpenSLX can be found at http://openslx.org/
 # --------------------------------------------------------------------------
 # inputEventHandler.h:
 #  - Common definitions for input event handlers
 # --------------------------------------------------------------------------
 */

#ifndef INPUTEVENTHANDLER_H_
#define INPUTEVENTHANDLER_H_

#include <QtGlobal>
#include <QtDebug>
#include <QList>
#include <QString>
#include <QCoreApplication>
#include <src/input/inputEvent.h>
#include "detail/policyChain.h"
#include "detail/systemTraits.h"

/**
 * For handling access control, this specifies who sent the input event.
 * This only really makes sense in the privileged input handler chain.
 */
struct InputEventContext
{
	InputEventContext()
	{
		hasBeenDenied = false;
	}

	virtual pid_t senderPid() const = 0; /**< PID of the sending process */
	virtual uid_t senderUid() const = 0; /**< UID of the sending process */
	virtual gid_t senderGid() const = 0; /**< GID of the sending process */

	/** Support the generation of meaningful log messages. */
	mutable bool hasBeenDenied;
};

namespace input_policy
{

/////////////////////////////////////////////////////////////////////////
// Policy:
//  Modifies the behaviour of an input handler class.
//
//  There are several kinds of policy:
//   - Security Policy (When to allow a certain action)
//   - System Requirements (When to enable a certain handler)
//   - Applicability (When to consider a certain handler)
//
//  Policies are tied together using the detail::PolicyChain class.
/////////////////////////////////////////////////////////////////////////

/**
 * Security Policy.
 * At the moment there are two security policies:
 * -* If the user is on a local seat, allow. If the user is privileged,
 *    allow. Else deny.
 * -* Allow everybody.
 *
 * Additional security policies can be added by following the example
 * set by \ref AllowLocalOrPrivileged
 *
 * \param PolicyImpl The implementation class that security decisions
 *   should be delegated to.
 * \see {AllowLocalOrPrivileged}
 * \see {AllowEverybody}
 */
template<typename PolicyImpl>
BEGIN_POLICY_CLASS(Security)
{
	bool allow(InputEventContext const* context)
	{
		return PolicyImpl::allow(context);
	}
}
END_POLICY_CLASS

/**
 * Check the default security model.
 */
struct AllowLocalOrPrivileged
{
	static bool allow(InputEventContext const*);
};

/**
 * Do not restrict execution.
 */
struct AllowEverybody
{
	static bool allow(InputEventContext const*);
};

/**
 * Shorthand for unrestricted execution.
 */
typedef Security<AllowEverybody> Unprivileged;

/**
 * System Requirements.
 *
 * At the moment, this is trivial, as PVS only runs on Linux. But,
 * as porting efforts are already underway, we include the necessary
 * machinery anyway.
 *
 * \param Trait... a list of system traits that need to be
 *   present in order for the handler that this policy is applied to
 *   to run.
 */
#ifdef DOXYGEN_RUNNING
template<typename Trait...>
#else
template<IMPLICIT_TYPE_LIST_PARAMS(Trait)>
#endif
BEGIN_POLICY_CLASS(Require)
{
	static const bool areSystemRequirementsFulfilled =
			(NextPolicy::areSystemRequirementsFulfilled
				&& !NextPolicy::areSystemRequirementsVacuouslyFulfilled)
			|| detail::Matches<AllOf<IMPLICIT_TYPE_LIST_ARGS(Trait)>, detail::SystemTraits>::value;
	static const bool areSystemRequirementsVacuouslyFulfilled = false;
}
END_POLICY_CLASS

#ifndef DOXYGEN_RUNNING
enum {
	HANDLER_CODE_DONT_CARE = 0xffff,
	HANDLER_VALUE_DONT_CARE = 0xffffffff
};
#endif

/**
 * Event selection.
 *
 * This policy makes the handler it is applied to applicable if the
 * template parameters \c EventType, \c EventCode and \c EventValue
 * match the corresponding fields of the incoming \ref InputEvent.
 *
 * Can be applied multiple times, and will be combined by logical
 * OR.
 *
 * \param EventType Match the \ref InputEvent::type() field.
 * \param EventCode (optional:) Match the \ref InputEvent::code() field.
 * \param EventValue (optional:) Match the \ref InputEvent::value() field.
 */
template<unsigned short EventType,
		unsigned short EventCode = HANDLER_CODE_DONT_CARE,
		unsigned int EventValue = HANDLER_VALUE_DONT_CARE>
BEGIN_POLICY_CLASS(Match)
{
	bool isApplicable(InputEvent const& evt)
	{
		if(evt.type() != EventType)
			return NextPolicy::isApplicable(evt);
		if(EventCode != HANDLER_CODE_DONT_CARE && evt.code() != EventCode)
			return NextPolicy::isApplicable(evt);
		if(EventValue != HANDLER_VALUE_DONT_CARE && evt.value() != EventValue)
			return NextPolicy::isApplicable(evt);
		return true;
	}
};
END_POLICY_CLASS

#ifndef DOXYGEN_RUNNING
namespace detail
{

/////////////////////////////////////////////////////////////////////////
// Base case: If no policies are given:
/////////////////////////////////////////////////////////////////////////
struct InputEventHandlerPolicyBase
{
	// The default security policy applies
	typedef AllowLocalOrPrivileged DefaultSecurityPolicyImpl;

	bool allow(InputEventContext const* context)
	{
		return DefaultSecurityPolicyImpl::allow(context);
	}

	// A handler that does not specify requirements works
	// everywhere
	static const bool areSystemRequirementsFulfilled = true;

	// We need this to implement proper logical OR
	static const bool areSystemRequirementsVacuouslyFulfilled = true;

	// Generate an error when no match policy is given.
	bool isApplicable(InputEvent const&)
	{
		return false;
	}

	// If any policy implementation needs an initialization hook:
	// Don't forget to call NextPolicy::initialize() in your
	// implementation!
	void initialize()
	{
	}
};

} // namespace detail
#endif // DOXYGEN_RUNNING

}

/**
 * Base class without template parameters to enable making a list of
 * polymorphic event handlers.
 *
 * The actual handler class needs to provide \ref doHandle and can
 * override \ref allow and \ref isApplicable.
 *
 * \note Do not derive from InputEventHandlerBase to implement
 *   new event handlers! Derive from InputEventHandler instead.
 */
class InputEventHandlerBase
{
public:
	enum HandlerStatus
	{
		HANDLER_MATCHED,		/**< The handler matched the input event and was executed. */
		HANDLER_NOT_ALLOWED,	/**< Execution of the handler was prevented by security policy. */
		HANDLER_NOT_APPLICABLE	/**< The handler did not match the input event. */
	};

	virtual void initialize() = 0;
	HandlerStatus handle(InputEvent const& evt, InputEventContext const* context = 0);

protected:
	/**
	 * Check security preconditions for the execution of this handler.
	 * This is normally handled by the \ref input_policy::Security policy.
	 */
	virtual bool allow(InputEvent const& event, InputEventContext const* context = 0) = 0;

	/**
	 * Check if this handler can handle the incoming input event.
	 * This is normally handled by the \ref input_policy::Match policy.
	 */
	virtual bool isApplicable(InputEvent const& event, InputEventContext const* context = 0) = 0;

	/**
	 * Actually handle the incoming event.
	 * It is assumed that all preconditions have been checked and the handler
	 * has been initialized at the point where this method is called.
	 */
	virtual void doHandle(InputEvent const& event, InputEventContext const* context = 0) = 0;
};

/**
 * Base class for input event handlers.
 */
#ifdef DOXYGEN_RUNNING
template<typename Policy...>
#else
template<POLICY_PARAMS>
#endif
class InputEventHandler : public InputEventHandlerBase
{
#ifndef DOXYGEN_RUNNING
protected:
	// instantiate our policy:
	typedef USE_POLICY(input_policy::detail::InputEventHandlerPolicyBase) policy_type;
	policy_type policy;
#endif

public:
	void initialize()
	{
		policy.initialize();
	}

	/** Export this so the handler chain can decide whether to include this handler */
	static const bool areSystemRequirementsFulfilled = policy_type::areSystemRequirementsFulfilled;

protected:

	bool allow(InputEvent const&, InputEventContext const* context = 0)
	{
		return policy.allow(context);
	}

	bool isApplicable(InputEvent const& event, InputEventContext const* = 0)
	{
		return policy.isApplicable(event);
	}
};

/**
 * Chain \ref InputEventHandler instances together.
 * The chain is also responsible for the creation of instances.
 *
 * \see privilegedInputEventHandlerChain.cpp
 * \see unprivilegedInputEventHandlerChain.cpp
 */
class InputEventHandlerChain
{
private:
	QList<InputEventHandlerBase*> handlers;

#ifndef DOXYGEN_RUNNING // Implementation detail
	/////////////////////////////////////////////////////////////////////////
	// We need to statically dispatch on a static member of HandlerType.
	// Unfortunately, we cannot specialize member functions of a template.
	// So, we need to make this a class with a static non-template member.
	/////////////////////////////////////////////////////////////////////////
	template<bool Compatible, typename HandlerType>
	struct ConditionallyAppend
	{
		/////////////////////////////////////////////////////////////////////////
		// This method will never be instantiated for handlers that are
		// not Compatible, thus generating no reference to HandlerType
		// and permitting compilation to proceed without
		// tedious nested preprocessor conditionals.
		/////////////////////////////////////////////////////////////////////////
		static void doIt(InputEventHandlerChain* chain)
		{
			chain->handlers.append(new HandlerType);
		}
	};

	template<typename HandlerType>
	struct ConditionallyAppend<false, HandlerType>
	{
		static void doIt(InputEventHandlerChain*)
		{
		}
	};
#endif // DOXYGEN_RUNNING

public:
	/**
	 * Add an event handler to the chain.
	 *
	 * \param fake_parameter The parameter is only for
	 *   compilers which cannot handle template member functions
	 *   correctly.
	 * \return A reference to the receiver, for invocation chaining.
	 *
	 * \note Do not pass a value to this function. The function
	 *   handles creation of an instance itself.
	 */
	template<typename HandlerType>
	InputEventHandlerChain& add(HandlerType const* fake_parameter = 0)
	{
		ConditionallyAppend<HandlerType::areSystemRequirementsFulfilled, HandlerType>::doIt(this);
		return *this;
	}

	/**
	 * Call \ref InputEventHandlerBase::initialize() on all
	 * handlers in the chain.
	 */
	void initialize()
	{
		QListIterator<InputEventHandlerBase*> i(handlers);
		while(i.hasNext())
		{
			i.next()->initialize();
		}
	}

	/**
	 * Handle an input event. All handlers in the chain are tried
	 * in the order they were added, until one handler's
	 * implementation of InputEventHandlerBase::handle() returns
	 * InputEventHandlerBase::HANDLER_MATCHED.
	 */
	void handle(InputEvent const& event, InputEventContext const* context = 0)
	{
		QListIterator<InputEventHandlerBase*> i(handlers);
		while(i.hasNext())
		{
			switch(i.next()->handle(event, context))
			{
			case InputEventHandlerBase::HANDLER_MATCHED:
				return;
			case InputEventHandlerBase::HANDLER_NOT_ALLOWED:
				context->hasBeenDenied = true;
			case InputEventHandlerBase::HANDLER_NOT_APPLICABLE:
				continue;
			}
		}
	}
};

#endif /* INPUTEVENTHANDLER_H_ */