diff options
| author | Sebastien Braun | 2010-10-07 22:54:10 +0200 |
|---|---|---|
| committer | Sebastien Braun | 2010-10-07 22:54:10 +0200 |
| commit | e61097b8881bc7e72a5499816cb1199ea274a3ca (patch) | |
| tree | bcfa9f3fe3f43af02845bc21367de395b248600c /src/input/inputEventHandler.h | |
| parent | Make behaviour on lookup failures configurable (diff) | |
| download | pvs-e61097b8881bc7e72a5499816cb1199ea274a3ca.tar.gz pvs-e61097b8881bc7e72a5499816cb1199ea274a3ca.tar.xz pvs-e61097b8881bc7e72a5499816cb1199ea274a3ca.zip | |
Rework template meta-magic
- No more implicit dependency on Boost.MPL
- Better documentation for template magic
- Move input handler policies to handler definitions where they belong
- Separate out event descriptions from handlers
Diffstat (limited to 'src/input/inputEventHandler.h')
| -rw-r--r-- | src/input/inputEventHandler.h | 407 |
1 files changed, 208 insertions, 199 deletions
diff --git a/src/input/inputEventHandler.h b/src/input/inputEventHandler.h index 52e3338..5b03a90 100644 --- a/src/input/inputEventHandler.h +++ b/src/input/inputEventHandler.h @@ -22,274 +22,283 @@ #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 +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 getSenderPid() const = 0; + virtual uid_t getSenderUid() const = 0; + virtual gid_t getSenderGid() const = 0; - InputEvent toEvent() const - { - return InputEvent(evtType, evtCode, evtValue); - } + 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; - } +namespace input_policy +{ - virtual void initialize() +///////////////////////////////////////////////////////////////////////// +// 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: +// 1. If the user is on a local seat, allow. If the user is privileged, +// allow. Else deny. +// 2. Allow everybody. +// Additional security policies can be added by following the example +// set by AllowLocalOrPrivileged +///////////////////////////////////////////////////////////////////////// +template<typename PolicyImpl> +BEGIN_POLICY_CLASS(Security) +{ + bool allow(InputEventContext const* context) { + return PolicyImpl::allow(context); } +} +END_POLICY_CLASS - virtual void handle(InputEvent const& evt, InputEventContext const*) = 0; - - static void describeInto(QList<SpecialInputEventDescription>& description) - { - } +struct AllowLocalOrPrivileged +{ + static bool allow(InputEventContext const*); }; -namespace policy { - -enum SecurityFlags { - SEC_FREE_FOR_ALL, - SEC_PHYSICAL_OR_PRIVILEGED +struct AllowEverybody +{ + static bool allow(InputEventContext const*); }; -bool allowPhysicalSeat(InputEvent const& evt, InputEventContext const* ctx); -bool allowPrivilegedUser(InputEvent const& evt, InputEventContext const* ctx); +typedef Security<AllowEverybody> Unprivileged; -struct SecurityAllowAny +///////////////////////////////////////////////////////////////////////// +// 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. +///////////////////////////////////////////////////////////////////////// +template<typename SystemTrait> +BEGIN_POLICY_CLASS(Require) { - bool allow(InputEvent const& evt, InputEventContext const* ctx) - { - return true; - } + static const bool areSystemRequirementsFulfilled = + NextPolicy::areSystemRequirementsFulfilled && + detail::Matches<SystemTrait, detail::SystemTraits>::value; +} +END_POLICY_CLASS + +///////////////////////////////////////////////////////////////////////// +// 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. +///////////////////////////////////////////////////////////////////////// +enum { + HANDLER_CODE_DONT_CARE = 0xffff, + HANDLER_VALUE_DONT_CARE = 0xffffffff }; -struct SecurityAllowPhysicalOrPrivileged +struct T; + +template<unsigned short EventType, + unsigned short EventCode = HANDLER_CODE_DONT_CARE, + unsigned int EventValue = HANDLER_VALUE_DONT_CARE> +BEGIN_POLICY_CLASS(Match) { - bool allow(InputEvent const& evt, InputEventContext const* ctx) + bool isApplicable(InputEvent const& evt) { - if(allowPhysicalSeat(evt, ctx)) - return true; - else if(allowPrivilegedUser(evt, ctx)) - return true; - return false; + 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 -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; -#else -# error "Porting is needed!" -#endif - -struct SystemEnabled; -struct SystemDisabled; - -template<typename System> -struct RequireSystem -{ - typedef typename boost::mpl::contains<Systems, System>::type enabled_type; - static const bool enabled = enabled_type::value; -}; - -struct RequireNoSystem +namespace detail { - typedef boost::mpl::bool_<true>::type enabled_type; - static const bool enabled = enabled_type::value; -}; -} - -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; -}; - -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> -{ -}; +} +} -template<typename DefaultSecurityPolicy, typename HandlerType> -struct ApplyDefaultSecurityPolicy +///////////////////////////////////////////////////////////////////////// +// We want a nice non-generic base so we can make a list of polymorphic +// handlers. +// +// The actual handler class need to provide doHandle and can override +// allow and isApplicable. +///////////////////////////////////////////////////////////////////////// +/* interface */ class InputEventHandlerBase { - typedef HandlerType type; -}; +public: + enum HandlerStatus + { + HANDLER_MATCHED, + HANDLER_NOT_ALLOWED, + HANDLER_NOT_APPLICABLE + }; -template<typename DefaultSecurityPolicy, typename Delegate, typename SystemPolicy> -struct ApplyDefaultSecurityPolicy<DefaultSecurityPolicy, Handler<Delegate, SystemPolicy, void> > -{ - typedef Handler<Delegate, SystemPolicy, DefaultSecurityPolicy> type; + virtual void initialize() = 0; + HandlerStatus handle(InputEvent const& evt, InputEventContext const* context = 0); + +protected: + virtual bool allow(InputEvent const& event, InputEventContext const* context = 0) = 0; + virtual bool isApplicable(InputEvent const& event, InputEventContext const* context = 0) = 0; + virtual void doHandle(InputEvent const& event, InputEventContext const* context = 0) = 0; }; -template<typename DefaultSecurityPolicy, typename Begin, typename End> -struct InputEventHandlerChainHelper +///////////////////////////////////////////////////////////////////////// +// Now that the machinery is in place, we can finally define what it +// is like to be an input event handler: +///////////////////////////////////////////////////////////////////////// +template<POLICY_PARAMS> +class InputEventHandler : public InputEventHandlerBase { -private: - typedef typename boost::mpl::next<Begin>::type next_iterator_type; - typedef InputEventHandlerChainHelper<DefaultSecurityPolicy, next_iterator_type, End> next_in_chain; - - typedef typename boost::mpl::deref<Begin>::type handler_entry_type; - typedef typename ApplyDefaultSecurityPolicy<DefaultSecurityPolicy, handler_entry_type>::type handler_type; - - handler_type _handler; - next_in_chain _next; +protected: + typedef USE_POLICY(input_policy::detail::InputEventHandlerPolicyBase) policy_type; + policy_type policy; 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: + + typedef InputEventHandler super; - static void describeInto(QList<SpecialInputEventDescription>& list) + // allow and isApplicable are actually provided by Policy. If the + // handler class wishes to override any of them, the policy implementation + // can be called by means of super. + 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> +///////////////////////////////////////////////////////////////////////// +// And we can chain input handlers together: +///////////////////////////////////////////////////////////////////////// +class InputEventHandlerChain { - void handle(InputEvent const&, InputEventContext const* context = 0) { - // do nothing - } +private: + QList<InputEventHandlerBase*> handlers; + + ///////////////////////////////////////////////////////////////////////// + // 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 Applicable, typename HandlerType> + struct ConditionallyAppend + { + 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*) + { + } + }; - static void describeInto(QList<SpecialInputEventDescription>&) +public: + // Add an event handler to the chain. The argument is only for + // compilers which cannot handle template member functions + // correctly. + template<typename HandlerType> + InputEventHandlerChain& add(HandlerType const* = 0) { - // do nothing + ConditionallyAppend<HandlerType::areSystemRequirementsFulfilled, HandlerType>::doIt(this); + return *this; } - static QList<SpecialInputEventDescription> describe() + 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> -{ + 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: + break; + case InputEventHandlerBase::HANDLER_NOT_ALLOWED: + context->hasBeenDenied = true; + case InputEventHandlerBase::HANDLER_NOT_APPLICABLE: + continue; + } + } + } }; #endif /* INPUTEVENTHANDLER_H_ */ |
