/*
# 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_ */