summaryrefslogblamecommitdiffstats
path: root/src/input/inputEventHandler.h
blob: 783640f78f6180d414ff1aaba065c7abe159060b (plain) (tree)



















                                                                               
                  


                           
                                 

                                
 



                                                                        
                        
 
                           
         
                                      

         


                                                                               
 
                                                                 
                                   

  

                      
 











                                                                         














                                                                       



                                                    
         
                                                  
         

                
 


                                    


                                                    

  


                             


                                                    

  


                                        
                                              
 













                                                                    
                                          
      
                           
 
                                                          



                                                                                                               


                
                       


                                            
  















                                                                          



                                                                  
 
                                                
         






                                                                                      

         
                
 
                       
                
 
 



                                                                         
 

                                                                 
 
                                                    
         
                                                                 
         
 


                                                                
 


                                                                         


                                                           


                             


                                                                     
                         



         


                         
 
 









                                                                    
                           
 


                          


                                                                                                             
          
 



                                                                                          



                                                                              
                                                                                              




                                                                           
                                                                                                     





                                                                                 
                                                                                                 

  





                                       
                       
      
                                                      
 
                       
          
                                  

                                                                                          
      
 
       


                                    

         
                                                                                          



                                                                                                       
                                                                           
         
                                             

         
                                                                                
         
                                                  


         






                                                               
                            
 


                                               
                                                




                                                                                 
                                                       

                                  





                                                                                         




                                                                
 






                                                         
                         
 
       










                                                                        
                                      
                                                                          
         

                                                                                                          

         



                                                               
                         
         




                                                                  
         
 





                                                                     







                                                                                  
                                       






                                                                           


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