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();
}
|