summaryrefslogtreecommitdiffstats
path: root/src/input/inputEventHandler.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/input/inputEventHandler.h')
-rw-r--r--src/input/inputEventHandler.h499
1 files changed, 301 insertions, 198 deletions
diff --git a/src/input/inputEventHandler.h b/src/input/inputEventHandler.h
index 52e3338..783640f 100644
--- a/src/input/inputEventHandler.h
+++ b/src/input/inputEventHandler.h
@@ -22,274 +22,377 @@
#include <QList>
#include <QString>
#include <QCoreApplication>
-#include <boost/mpl/contains.hpp>
-#include <boost/mpl/if.hpp>
-#include <boost/mpl/vector.hpp>
#include <src/input/inputEvent.h>
+#include "detail/policyChain.h"
+#include "detail/systemTraits.h"
-#define HANDLER_TYPE_DONT_CARE 0xffff
-#define HANDLER_CODE_DONT_CARE 0xffff
-#define HANDLER_VALUE_DONT_CARE 0xffffffff
-
-class InputEventContext
-{
-public:
- virtual pid_t getSenderPid() const = 0;
- virtual uid_t getSenderUid() const = 0;
- virtual gid_t getSenderGid() const = 0;
-};
-
-struct SpecialInputEventDescription
+/**
+ * For handling access control, this specifies who sent the input event.
+ * This only really makes sense in the privileged input handler chain.
+ */
+struct InputEventContext
{
- SpecialInputEventDescription(QString const& d, quint16 t, quint16 c, quint32 v = 0)
- : descriptionString(d), evtType(t), evtCode(c), evtValue(v)
+ InputEventContext()
{
+ hasBeenDenied = false;
}
- QString descriptionString;
- quint16 evtType;
- quint16 evtCode;
- quint32 evtValue;
+ 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 */
- InputEvent toEvent() const
- {
- return InputEvent(evtType, evtCode, evtValue);
- }
+ /** Support the generation of meaningful log messages. */
+ mutable bool hasBeenDenied;
};
-template<quint16 Type = HANDLER_TYPE_DONT_CARE,
- quint16 Code = HANDLER_CODE_DONT_CARE,
- quint32 Value = HANDLER_VALUE_DONT_CARE>
-class DefaultInputEventHandler {
-protected:
- static QString tr(char const* string)
- {
- return QCoreApplication::translate("InputEventHandler", string);
- }
-
-public:
- virtual bool matches(InputEvent const& evt, InputEventContext const*) {
- if(Type != HANDLER_TYPE_DONT_CARE) {
- if(evt.type() != Type)
- return false;
- }
- if(Code != HANDLER_CODE_DONT_CARE) {
- if(evt.code() != Code)
- return false;
- }
- if(Value != HANDLER_VALUE_DONT_CARE) {
- if(evt.value() != Value)
- return false;
- }
- return true;
- }
-
- virtual void initialize()
- {
- }
-
- virtual void handle(InputEvent const& evt, InputEventContext const*) = 0;
+namespace input_policy
+{
- static void describeInto(QList<SpecialInputEventDescription>& description)
+/////////////////////////////////////////////////////////////////////////
+// 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);
}
-};
-
-namespace policy {
-
-enum SecurityFlags {
- SEC_FREE_FOR_ALL,
- SEC_PHYSICAL_OR_PRIVILEGED
-};
-
-bool allowPhysicalSeat(InputEvent const& evt, InputEventContext const* ctx);
-bool allowPrivilegedUser(InputEvent const& evt, InputEventContext const* ctx);
+}
+END_POLICY_CLASS
-struct SecurityAllowAny
+/**
+ * Check the default security model.
+ */
+struct AllowLocalOrPrivileged
{
- bool allow(InputEvent const& evt, InputEventContext const* ctx)
- {
- return true;
- }
+ static bool allow(InputEventContext const*);
};
-struct SecurityAllowPhysicalOrPrivileged
+/**
+ * Do not restrict execution.
+ */
+struct AllowEverybody
{
- bool allow(InputEvent const& evt, InputEventContext const* ctx)
- {
- if(allowPhysicalSeat(evt, ctx))
- return true;
- else if(allowPrivilegedUser(evt, ctx))
- return true;
- return false;
- }
+ static bool allow(InputEventContext const*);
};
-struct UnixLike;
-struct Linux;
-struct Windows;
-
-#if defined(__linux)
-typedef boost::mpl::vector2<UnixLike,Linux>::type Systems;
-#elif defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__)
-typedef boost::mpl::vector1<Windows>::type Systems;
+/**
+ * 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
-# error "Porting is needed!"
+template<IMPLICIT_TYPE_LIST_PARAMS(Trait)>
#endif
-
-struct SystemEnabled;
-struct SystemDisabled;
-
-template<typename System>
-struct RequireSystem
+BEGIN_POLICY_CLASS(Require)
{
- typedef typename boost::mpl::contains<Systems, System>::type enabled_type;
- static const bool enabled = enabled_type::value;
+ 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
-struct RequireNoSystem
+/**
+ * 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)
{
- typedef boost::mpl::bool_<true>::type enabled_type;
- static const bool enabled = enabled_type::value;
+ 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
+{
-template<bool Enabled, typename Delegate, typename SecurityPolicy>
-class HandlerHelper
+/////////////////////////////////////////////////////////////////////////
+// Base case: If no policies are given:
+/////////////////////////////////////////////////////////////////////////
+struct InputEventHandlerPolicyBase
{
-public:
- bool handle(InputEvent const& evt, InputEventContext const* context = 0) {
- if(!securityPolicy.allow(evt, context))
- {
- std::string evtStr = evt.toString();
- qWarning("Input Event %s has been denied by security policy", evtStr.c_str());
- return true;
- }
- if(delegate.matches(evt, context)) {
- delegate.handle(evt, context);
- return true;
- } else {
- return false;
- }
- }
+ // The default security policy applies
+ typedef AllowLocalOrPrivileged DefaultSecurityPolicyImpl;
- void initialize()
+ bool allow(InputEventContext const* context)
{
- delegate.initialize();
+ return DefaultSecurityPolicyImpl::allow(context);
}
- static void describeInto(QList<SpecialInputEventDescription>& list)
- {
- Delegate::describeInto(list);
- }
+ // A handler that does not specify requirements works
+ // everywhere
+ static const bool areSystemRequirementsFulfilled = true;
-private:
- Delegate delegate;
- SecurityPolicy securityPolicy;
-};
+ // We need this to implement proper logical OR
+ static const bool areSystemRequirementsVacuouslyFulfilled = true;
-template<typename Delegate, typename SecurityPolicy>
-class HandlerHelper<false, Delegate, SecurityPolicy>
-{
-public:
- bool handle(InputEvent const& evt, InputEventContext const* context = 0) {
+ // 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()
{
}
-
- static void describeInto(QList<SpecialInputEventDescription>&)
- {
- }
};
-template<typename Delegate, typename SystemPolicy = policy::RequireNoSystem, typename SecurityPolicy = void>
-struct Handler : public HandlerHelper<SystemPolicy::enabled, Delegate, SecurityPolicy>
-{
-};
+} // namespace detail
+#endif // DOXYGEN_RUNNING
-template<typename DefaultSecurityPolicy, typename HandlerType>
-struct ApplyDefaultSecurityPolicy
-{
- typedef HandlerType type;
-};
+}
-template<typename DefaultSecurityPolicy, typename Delegate, typename SystemPolicy>
-struct ApplyDefaultSecurityPolicy<DefaultSecurityPolicy, Handler<Delegate, SystemPolicy, void> >
+/**
+ * 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
{
- typedef Handler<Delegate, SystemPolicy, DefaultSecurityPolicy> type;
-};
+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. */
+ };
-template<typename DefaultSecurityPolicy, typename Begin, typename End>
-struct InputEventHandlerChainHelper
-{
-private:
- typedef typename boost::mpl::next<Begin>::type next_iterator_type;
- typedef InputEventHandlerChainHelper<DefaultSecurityPolicy, next_iterator_type, End> next_in_chain;
+ virtual void initialize() = 0;
+ HandlerStatus handle(InputEvent const& evt, InputEventContext const* context = 0);
- typedef typename boost::mpl::deref<Begin>::type handler_entry_type;
- typedef typename ApplyDefaultSecurityPolicy<DefaultSecurityPolicy, handler_entry_type>::type handler_type;
+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;
+};
- handler_type _handler;
- next_in_chain _next;
+/**
+ * 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 handle(InputEvent const& evt, InputEventContext const* context = 0) {
- if(!_handler.handle(evt, context)) {
- _next.handle(evt, context);
- }
+ void initialize()
+ {
+ policy.initialize();
}
- void initialize() {
- _handler.initialize();
- _next.initialize();
- }
+ /** Export this so the handler chain can decide whether to include this handler */
+ static const bool areSystemRequirementsFulfilled = policy_type::areSystemRequirementsFulfilled;
+
+protected:
- static void describeInto(QList<SpecialInputEventDescription>& list)
+ bool allow(InputEvent const&, InputEventContext const* context = 0)
{
- handler_type::describeInto(list);
- next_in_chain::describeInto(list);
+ return policy.allow(context);
}
- static QList<SpecialInputEventDescription> describe()
+ bool isApplicable(InputEvent const& event, InputEventContext const* = 0)
{
- QList<SpecialInputEventDescription> list;
- describeInto(list);
- return list;
+ return policy.isApplicable(event);
}
};
-template<typename DefaultSecurityPolicy, typename End>
-struct InputEventHandlerChainHelper<DefaultSecurityPolicy, End, End>
+/**
+ * Chain \ref InputEventHandler instances together.
+ * The chain is also responsible for the creation of instances.
+ *
+ * \see privilegedInputEventHandlerChain.cpp
+ * \see unprivilegedInputEventHandlerChain.cpp
+ */
+class InputEventHandlerChain
{
- void handle(InputEvent const&, InputEventContext const* context = 0) {
- // do nothing
- }
+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);
+ }
+ };
- void initialize() {
- // do nothing
- }
+ template<typename HandlerType>
+ struct ConditionallyAppend<false, HandlerType>
+ {
+ static void doIt(InputEventHandlerChain*)
+ {
+ }
+ };
+#endif // DOXYGEN_RUNNING
- static void describeInto(QList<SpecialInputEventDescription>&)
+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)
{
- // do nothing
+ ConditionallyAppend<HandlerType::areSystemRequirementsFulfilled, HandlerType>::doIt(this);
+ return *this;
}
- static QList<SpecialInputEventDescription> describe()
+ /**
+ * Call \ref InputEventHandlerBase::initialize() on all
+ * handlers in the chain.
+ */
+ void initialize()
{
- return QList<SpecialInputEventDescription>();
+ QListIterator<InputEventHandlerBase*> i(handlers);
+ while(i.hasNext())
+ {
+ i.next()->initialize();
+ }
}
-};
-template<typename DefaultSecurityPolicy, typename Collection>
-struct InputEventHandlerChain :
- public InputEventHandlerChainHelper<DefaultSecurityPolicy,
- typename boost::mpl::begin<Collection>::type,
- typename boost::mpl::end<Collection>::type>
-{
+ /**
+ * 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_ */