File: controllers.hh

package info (click to toggle)
performous 0.7.0%2Bgit20140715-2
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 7,900 kB
  • ctags: 3,470
  • sloc: cpp: 16,647; sh: 2,495; ansic: 2,015; python: 431; xml: 407; objc: 245; makefile: 12
file content (139 lines) | stat: -rw-r--r-- 5,809 bytes parent folder | download | duplicates (2)
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
#pragma once

#include <climits>
#include <deque>
#include <vector>
#include <iostream>
#include <stdexcept>
#include <boost/noncopyable.hpp>
#include <boost/smart_ptr/scoped_ptr.hpp>

#include "SDL_events.h"

#include "util.hh"
#include "xtime.hh"
#include "configuration.hh"

namespace input {
	enum SourceType { SOURCETYPE_NONE, SOURCETYPE_JOYSTICK, SOURCETYPE_MIDI, SOURCETYPE_KEYBOARD, SOURCETYPE_N };
	enum DevType { DEVTYPE_GENERIC, DEVTYPE_VOCALS, DEVTYPE_GUITAR, DEVTYPE_DRUMS, DEVTYPE_KEYTAR, DEVTYPE_PIANO, DEVTYPE_DANCEPAD, DEVTYPE_N };
	/// Generalized mapping of navigation actions
	enum NavButton {
		NAV_NONE /* No NavEvent emitted */, NAV_SOME /* Major gameplay button with no direct nav function, used for joining instruments */,
		NAV_START, NAV_CANCEL, NAV_PAUSE,
		NAV_REPEAT = 0x80 /* Anything after this is auto-repeating */,
		NAV_UP, NAV_DOWN, NAV_LEFT, NAV_RIGHT, NAV_MOREUP, NAV_MOREDOWN, NAV_VOLUME_UP, NAV_VOLUME_DOWN
	};
	/// Alternative orientation-agnostic mapping where A axis is the one that is easiest to access (e.g. guitar pick) and B might not be available on all devices
	enum NavMenu { NAVMENU_NONE, NAVMENU_A_PREV, NAVMENU_A_NEXT, NAVMENU_B_PREV, NAVMENU_B_NEXT };

	enum ButtonId: unsigned {
		// Button constants for each DevType
		#define DEFINE_BUTTON(devtype, button, num, nav) devtype##_##button = num,
		#include "controllers-buttons.ii"
	};

	struct Button {
		ButtonId id;
		Button(ButtonId id = GENERIC_UNASSIGNED): id(id) {}
		Button(unsigned layer, unsigned num): id(ButtonId(layer << 8 | num)) {}
		operator ButtonId() const { return id; }
		unsigned layer() const { return id >> 8; }
		unsigned num() const { return id & 0xFF; }
		bool generic() const { return layer() == 0x100; }
	};

	typedef unsigned HWButton;
	static const MinMax<HWButton> hwIsAxis(0x10000000u, 0x1000FFFFu);
	static const MinMax<HWButton> hwIsHat(0x11000000u, 0x1100FFFFu);
	
	/// Each controller has unique SourceId that can be used for telling players apart etc.
	struct SourceId {
		SourceId(SourceType type = SOURCETYPE_NONE, unsigned device = 0, unsigned channel = 0): type(type), device(device), channel(channel) {
		}
		SourceType type;
		unsigned device, channel;  ///< Device number and channel (0..1023)
		/// Provide numeric conversion for comparison and ordered containers
		operator unsigned() const { return unsigned(type)<<20 | device<<10 | channel; }
		bool isKeyboard() const { return type == SOURCETYPE_KEYBOARD; }  ///< This is so common test that a helper is provided
	};
	
	struct Event {
		SourceId source; ///< Where did it originate from
		HWButton hw; ///< Hardware button number (for internal use and debugging only)
		Button button; ///< Mapped button id
		NavButton nav; ///< Navigational button interpretation
		double value; ///< Zero for button release, up to 1.0 for press (e.g. velocity value), or axis value (-1.0 .. 1.0)
		boost::xtime time; ///< When did the event occur
		DevType devType; ///< Device type
		Event(): source(), hw(), nav(NAV_NONE), value(), time(), devType() {}
		bool pressed() const { return value != 0.0; }
	};

	/// NavEvent is a menu navigation event, generalized for all controller type so that the user doesn't need to know about controllers.
	struct NavEvent {
		SourceId source;
		DevType devType;
		NavButton button;
		NavMenu menu;
		boost::xtime time;
		unsigned repeat;  ///< Zero for hardware event, increased by one for each auto-repeat
		NavEvent(): source(), devType(), button(), menu(), time(), repeat() {}
		explicit NavEvent(Event const& ev): source(ev.source), devType(ev.devType), button(ev.nav), menu(), time(ev.time), repeat() {}
	};
	
	/// A handle for receiving device events
	class Device: boost::noncopyable {
		typedef std::deque<Event> Events;
		Events m_events;
	public:
		const SourceId source;
		const DevType type;
		Device(SourceId const& source, DevType type): source(source), type(type) {}
		bool getEvent(Event&);
		void pushEvent(Event const&);
	};
	typedef boost::shared_ptr<Device> DevicePtr;

	/// The main controller class that contains everything
	class Controllers: boost::noncopyable {
	public:
		Controllers();
		~Controllers();
		/// Return true and a nav event if there are any in queue. Otherwise return false.
		bool getNav(NavEvent& ev);
		/// Enable or disable event processing (pending events will be cleared).
		void enableEvents(bool state);
		/// Adopt a specific orphan device (for receiving Events).
		DevicePtr registerDevice(SourceId const& source);
		/// Internally poll for new events. The current time is passed for reference.
		void process(boost::xtime const& now);
		/// Push an SDL event for processing. Returns true if the event was taken (recognized and accepted).
		bool pushEvent(SDL_Event const& sdlEv, boost::xtime const& now);
	private:
		struct Impl;
		boost::scoped_ptr<Impl> self;
	};

	/// Base class for different types of hardware backends.
	class Hardware: boost::noncopyable {
	public:
		static bool midiEnabled();
		static void enableKeyboardInstruments(bool state);
		virtual ~Hardware() {}
		/// Get the name of a specific device of this type
		virtual std::string getName(unsigned) const { return std::string(); }
		/// Return an Event and true if any are available. The Event is pre-initialized with current time.
		virtual bool process(Event&) { return false; }
		/// Convert an SDL event into Event. The Event is pre-initialized with event's time. Returns false if SDL_Event was not handled.
		virtual bool process(Event&, SDL_Event const&) { return false; }
		// Note: process functions are expected to return Event with source, hw and value set and possibly with time adjusted.
		typedef boost::shared_ptr<Hardware> ptr;
	};
	
	Hardware::ptr constructKeyboard();
	Hardware::ptr constructJoysticks();
	Hardware::ptr constructMidi();
}