From 0499a7564be96e6b757bf506af1f0e85c3a3bd18 Mon Sep 17 00:00:00 2001 From: danij Date: Mon, 10 Nov 2014 15:15:14 +0000 Subject: [PATCH] InputSystem|Client: Cleanup --- doomsday/client/include/ui/b_main.h | 4 - doomsday/client/include/ui/b_util.h | 11 +- doomsday/client/include/ui/inputsystem.h | 32 ++- doomsday/client/src/dd_main.cpp | 4 +- doomsday/client/src/ui/b_main.cpp | 26 +-- doomsday/client/src/ui/b_util.cpp | 133 +++++------ doomsday/client/src/ui/bindcontext.cpp | 16 +- doomsday/client/src/ui/inputsystem.cpp | 280 ++++++++++++++--------- 8 files changed, 264 insertions(+), 242 deletions(-) diff --git a/doomsday/client/include/ui/b_main.h b/doomsday/client/include/ui/b_main.h index 104ec09013..cabf5f59d7 100644 --- a/doomsday/client/include/ui/b_main.h +++ b/doomsday/client/include/ui/b_main.h @@ -22,8 +22,4 @@ void B_Init(); -void B_BindDefaults(); - -void B_BindGameDefaults(); - #endif // CLIENT_INPUTSYSTEM_BINDINGS_H diff --git a/doomsday/client/include/ui/b_util.h b/doomsday/client/include/ui/b_util.h index cc4fae7af0..32d161abe3 100644 --- a/doomsday/client/include/ui/b_util.h +++ b/doomsday/client/include/ui/b_util.h @@ -28,7 +28,7 @@ class BindContext; class InputDevice; struct ImpulseBinding; -// Event Binding Toggle State +// Event Binding State enum ebstate_t { EBTOG_UNDEFINED, @@ -81,7 +81,7 @@ bool B_ParseStateCondition(statecondition_t *cond, char const *desc); // --------------------------------------------------------------------------------- -de::String B_ControlDescToString(InputDevice const &device, ddeventtype_t type, int id); +de::String B_ControlDescToString(int deviceId, ddeventtype_t type, int id); de::String B_ToggleStateToString(ebstate_t state); @@ -115,12 +115,5 @@ char const *B_ShortNameForKey(int ddKey, bool forceLowercase = true); int B_KeyForShortName(char const *key); -/** - * @return Never returns zero, as that is reserved for list roots. - */ -int B_NewIdentifier(); - -void B_ResetIdentifiers(); - #endif // CLIENT_INPUTSYSTEM_BINDING_UTILITIES_H diff --git a/doomsday/client/include/ui/inputsystem.h b/doomsday/client/include/ui/inputsystem.h index ab9839da92..9f9b4d94a8 100644 --- a/doomsday/client/include/ui/inputsystem.h +++ b/doomsday/client/include/ui/inputsystem.h @@ -68,7 +68,12 @@ class InputSystem : public de::System // System. void timeChanged(de::Clock const &); - /** +public: // Input devices ----------------------------------------------------- + + /// Required/referenced input device is missing. @ingroup errors + DENG2_ERROR(MissingDeviceError); + + /* * Lookup an InputDevice by it's unique @a id. */ InputDevice &device(int id) const; @@ -144,7 +149,7 @@ class InputSystem : public de::System public: static bool convertEvent(ddevent_t const &from, event_t &to); - static void convertEvent(de::Event const &from, ddevent_t &to); + static bool convertEvent(de::Event const &from, ddevent_t &to); /** * Updates virtual input device state. @@ -169,6 +174,10 @@ class InputSystem : public de::System /// Required/referenced binding context is missing. @ingroup errors DENG2_ERROR(MissingContextError); + void bindDefaults(); + + void bindGameDefaults(); + /** * Try to make a new (console) command binding. * @@ -201,6 +210,11 @@ class InputSystem : public de::System */ bool removeBinding(int id); + /** + * Remove all bindings in all contexts. + */ + void removeAllBindings(); + // --- /** @@ -257,17 +271,17 @@ class InputSystem : public de::System * * eventparams{+cond}* * - * @param binding Command binding to configure. - * @param eventDesc Descriptor for event information and any additional conditions. - * @param command Console command to execute when triggered, if any. - * @param newId @c true= assign a new unique identifier. + * @param binding Command binding to configure. + * @param eventDesc Descriptor for event information and any additional conditions. + * @param command Console command to execute when triggered, if any. + * @param assignNewId @c true= assign a new unique identifier. * * @throws ConfigureError on failure. At which point @a binding should be considered * to be in an undefined state. The caller may choose to clear and then reconfigure * it using another descriptor. */ void configure(CommandBinding &binding, char const *eventDesc, - char const *command = nullptr, bool newId = false); + char const *command = nullptr, bool assignNewId = true); /** * Parse a device-control => player impulse trigger descriptor and configure the given @@ -277,14 +291,14 @@ class InputSystem : public de::System * @param ctrlDesc Descriptor for control information and any additional conditions. * @param impulseId Identifier of the player impulse to execute when triggered, if any. * @param localPlayer Local player number to execute the impulse for when triggered. - * @param newId @c true= assign a new unique identifier. + * @param assignNewId @c true= assign a new unique identifier. * * @throws ConfigureError on failure. At which point @a binding should be considered * to be in an undefined state. The caller may choose to clear and then reconfigure * it using another descriptor. */ void configure(ImpulseBinding &binding, char const *ctrlDesc, - int impulseId, int localPlayer, bool newId = false); + int impulseId, int localPlayer, bool assignNewId = true); /** * Does the opposite of the B_Parse* methods for event descriptor, including the diff --git a/doomsday/client/src/dd_main.cpp b/doomsday/client/src/dd_main.cpp index 670f702535..a6859b9401 100644 --- a/doomsday/client/src/dd_main.cpp +++ b/doomsday/client/src/dd_main.cpp @@ -1230,7 +1230,7 @@ static int DD_ActivateGameWorker(void *context) #ifdef __CLIENT__ // Apply default control bindings for this game. - B_BindGameDefaults(); + ClientApp::inputSystem().bindGameDefaults(); // Read bindings for this game and merge with the working set. Con_ParseCommands(App_CurrentGame().bindingConfig(), CPCF_ALLOW_SAVE_BINDINGS); @@ -1516,7 +1516,7 @@ bool App_ChangeGame(Game &game, bool allowReload) P_ImpulseShutdown(); Con_Execute(CMDS_DDAY, "clearbindings", true, false); - B_BindDefaults(); + ClientApp::inputSystem().bindDefaults(); ClientApp::inputSystem().initialContextActivations(); #endif // Reset the world back to it's initial state (unload the map, reset players, etc...). diff --git a/doomsday/client/src/ui/b_main.cpp b/doomsday/client/src/ui/b_main.cpp index f288c4b7af..32e6559450 100644 --- a/doomsday/client/src/ui/b_main.cpp +++ b/doomsday/client/src/ui/b_main.cpp @@ -21,9 +21,6 @@ #include "ui/b_main.h" -#include -#include -#include #include "dd_main.h" // App_GameLoaded #include "dd_def.h" #include "clientapp.h" @@ -125,28 +122,7 @@ void B_Init() */ // Bind all the defaults for the engine only. - B_BindDefaults(); + isys.bindDefaults(); isys.initialContextActivations(); } - -void B_BindDefaults() -{ - InputSystem &isys = ClientApp::inputSystem(); - - // Engine's highest priority context: opening control panel, opening the console. - isys.bindCommand("global:key-f11-down + key-alt-down", "releasemouse"); - isys.bindCommand("global:key-f11-down", "togglefullscreen"); - isys.bindCommand("global:key-tilde-down + key-shift-up", "taskbar"); - - // Console bindings (when open). - isys.bindCommand("console:key-tilde-down + key-shift-up", "taskbar"); // without this, key would be entered into command line - - // Bias editor. -} - -void B_BindGameDefaults() -{ - if(!App_GameLoaded()) return; - Con_Executef(CMDS_DDAY, false, "defaultgamebindings"); -} diff --git a/doomsday/client/src/ui/b_util.cpp b/doomsday/client/src/ui/b_util.cpp index 887524ceee..89e9ff2856 100644 --- a/doomsday/client/src/ui/b_util.cpp +++ b/doomsday/client/src/ui/b_util.cpp @@ -17,18 +17,11 @@ * http://www.gnu.org/licenses */ -#include - -#include "de_platform.h" -#include "de_console.h" -#include "dd_main.h" -#include "de_misc.h" -#include "clientapp.h" - #include "ui/b_util.h" +#include +#include "clientapp.h" #include "BindContext" -#include "ui/b_main.h" #include "ui/inputdevice.h" #include "ui/inputdeviceaxiscontrol.h" #include "ui/inputdevicebuttoncontrol.h" @@ -51,27 +44,27 @@ bool B_ParseToggleState(char const *toggleName, ebstate_t *state) { DENG2_ASSERT(toggleName && state); - if(!strlen(toggleName) || !strcasecmp(toggleName, "down")) + if(!qstrlen(toggleName) || !qstricmp(toggleName, "down")) { *state = EBTOG_DOWN; // this is the default, if omitted return true; } - if(!strcasecmp(toggleName, "undefined")) + if(!qstricmp(toggleName, "undefined")) { *state = EBTOG_UNDEFINED; return true; } - if(!strcasecmp(toggleName, "repeat")) + if(!qstricmp(toggleName, "repeat")) { *state = EBTOG_REPEAT; return true; } - if(!strcasecmp(toggleName, "press")) + if(!qstricmp(toggleName, "press")) { *state = EBTOG_PRESS; return true; } - if(!strcasecmp(toggleName, "up")) + if(!qstricmp(toggleName, "up")) { *state = EBTOG_UP; return true; @@ -85,28 +78,28 @@ bool B_ParseAxisPosition(char const *desc, ebstate_t *state, float *pos) { DENG2_ASSERT(desc && state && pos); - if(!strncasecmp(desc, "within", 6) && strlen(desc) > 6) + if(!qstrnicmp(desc, "within", 6) && qstrlen(desc) > 6) { *state = EBAXIS_WITHIN; - *pos = strtod(desc + 6, nullptr); + *pos = String((desc + 6)).toFloat(); return true; } - if(!strncasecmp(desc, "beyond", 6) && strlen(desc) > 6) + if(!qstrnicmp(desc, "beyond", 6) && qstrlen(desc) > 6) { *state = EBAXIS_BEYOND; - *pos = strtod(desc + 6, nullptr); + *pos = String((desc + 6)).toFloat(); return true; } - if(!strncasecmp(desc, "pos", 3) && strlen(desc) > 3) + if(!qstrnicmp(desc, "pos", 3) && qstrlen(desc) > 3) { *state = EBAXIS_BEYOND_POSITIVE; - *pos = strtod(desc + 3, nullptr); + *pos = String((desc + 3)).toFloat(); return true; } - if(!strncasecmp(desc, "neg", 3) && strlen(desc) > 3) + if(!qstrnicmp(desc, "neg", 3) && qstrlen(desc) > 3) { *state = EBAXIS_BEYOND_NEGATIVE; - *pos = -strtod(desc + 3, nullptr); + *pos = -String((desc + 3)).toFloat(); return true; } @@ -117,7 +110,7 @@ bool B_ParseAxisPosition(char const *desc, ebstate_t *state, float *pos) dd_bool B_ParseModifierId(char const *desc, int *id) { DENG2_ASSERT(desc && id); - *id = strtoul(desc, nullptr, 10) - 1 + CTL_MODIFIER_1; + *id = String(desc).toInt() - 1 + CTL_MODIFIER_1; return (*id >= CTL_MODIFIER_1 && *id <= CTL_MODIFIER_4); } @@ -127,17 +120,17 @@ bool B_ParseKeyId(char const *desc, int *id) LOG_AS("B_ParseKeyId"); // The possibilies: symbolic key name, or "codeNNN". - if(!strncasecmp(desc, "code", 4) && strlen(desc) == 7) + if(!qstrnicmp(desc, "code", 4) && qstrlen(desc) == 7) { // Hexadecimal? if(desc[4] == 'x' || desc[4] == 'X') { - *id = strtoul(desc + 5, nullptr, 16); + *id = String((desc + 5)).toInt(nullptr, 16); return true; } // Decimal. - *id = strtoul(desc + 4, nullptr, 10); + *id = String((desc + 4)).toInt(); if(*id > 0 && *id <= 255) return true; LOGDEV_INPUT_WARNING("Key code %i out of range") << *id; @@ -155,9 +148,10 @@ bool B_ParseKeyId(char const *desc, int *id) bool B_ParseMouseTypeAndId(char const *desc, ddeventtype_t *type, int *id) { DENG2_ASSERT(desc && type && id); + InputDevice const &mouse = inputSys().device(IDEV_MOUSE); // Maybe it's one of the named buttons? - *id = inputSys().device(IDEV_MOUSE).toButtonId(desc); + *id = mouse.toButtonId(desc); if(*id >= 0) { *type = E_TOGGLE; @@ -165,23 +159,23 @@ bool B_ParseMouseTypeAndId(char const *desc, ddeventtype_t *type, int *id) } // Perhaps a generic button? - if(!strncasecmp(desc, "button", 6) && strlen(desc) > 6) + if(!qstrnicmp(desc, "button", 6) && qstrlen(desc) > 6) { *type = E_TOGGLE; - *id = strtoul(desc + 6, nullptr, 10) - 1; - if(inputSys().device(IDEV_MOUSE).hasButton(*id)) + *id = String((desc + 6)).toInt() - 1; + if(mouse.hasButton(*id)) return true; - LOG_INPUT_WARNING("Mouse button %i does not exist") << *id; + LOG_INPUT_WARNING("\"%s\" button %i does not exist") << mouse.title() << *id; return false; } // Must be an axis, then. *type = E_AXIS; - *id = inputSys().device(IDEV_MOUSE).toAxisId(desc); + *id = mouse.toAxisId(desc); if(*id >= 0) return true; - LOG_INPUT_WARNING("Mouse axis \"%s\" does not exist") << desc; + LOG_INPUT_WARNING("\"%s\" axis \"%s\" does not exist") << mouse.title() << desc; return false; } @@ -199,27 +193,27 @@ dd_bool B_ParseDeviceAxisTypeAndId(InputDevice const &device, char const *desc, bool B_ParseJoystickTypeAndId(InputDevice const &device, char const *desc, ddeventtype_t *type, int *id) { - if(!strncasecmp(desc, "button", 6) && strlen(desc) > 6) + if(!qstrnicmp(desc, "button", 6) && qstrlen(desc) > 6) { *type = E_TOGGLE; - *id = strtoul(desc + 6, nullptr, 10) - 1; + *id = String((desc + 6)).toInt() - 1; if(device.hasButton(*id)) return true; - LOG_INPUT_WARNING("Joystick button %i does not exist") << *id; + LOG_INPUT_WARNING("\"%s\" button %i does not exist") << device.title() << *id; return false; } - if(!strncasecmp(desc, "hat", 3) && strlen(desc) > 3) + if(!qstrnicmp(desc, "hat", 3) && qstrlen(desc) > 3) { *type = E_ANGLE; - *id = strtoul(desc + 3, nullptr, 10) - 1; + *id = String((desc + 3)).toInt() - 1; if(device.hasHat(*id)) return true; - LOG_INPUT_WARNING("Joystick hat %i does not exist") << *id; + LOG_INPUT_WARNING("\"%s\" hat %i does not exist") << device.title() << *id; return false; } - if(!strcasecmp(desc, "hat")) + if(!qstricmp(desc, "hat")) { *type = E_ANGLE; *id = 0; @@ -233,14 +227,14 @@ bool B_ParseJoystickTypeAndId(InputDevice const &device, char const *desc, ddeve bool B_ParseAnglePosition(char const *desc, float *pos) { DENG2_ASSERT(desc && pos); - if(!strcasecmp(desc, "center")) + if(!qstricmp(desc, "center")) { *pos = -1; return true; } - if(!strncasecmp(desc, "angle", 5) && strlen(desc) > 5) + if(!qstrnicmp(desc, "angle", 5) && qstrlen(desc) > 5) { - *pos = strtod(desc + 5, nullptr); + *pos = String((desc + 5)).toFloat(); return true; } LOG_INPUT_WARNING("Angle position \"%s\" is invalid") << desc; @@ -249,9 +243,10 @@ bool B_ParseAnglePosition(char const *desc, float *pos) bool B_ParseStateCondition(statecondition_t *cond, char const *desc) { - AutoStr *str = AutoStr_NewStd(); + DENG2_ASSERT(cond && desc); // First, we expect to encounter a device name. + AutoStr *str = AutoStr_NewStd(); desc = Str_CopyDelim(str, desc, '-'); if(!Str_CompareIgnoreCase(str, "multiplayer")) @@ -635,24 +630,27 @@ void B_EvaluateImpulseBindings(BindContext *context, int localNum, int impulseId *pos = de::clamp(-1.0f, *pos, 1.0f); } -String B_ControlDescToString(InputDevice const &device, ddeventtype_t type, int id) +String B_ControlDescToString(int deviceId, ddeventtype_t type, int id) { + InputDevice *device = nullptr; String str; - if(type != E_SYMBOLIC) { + device = &inputSys().device(deviceId); // Name of the device. - str += device.name() + "-"; + str += device->name() + "-"; } switch(type) { - case E_TOGGLE: - if(!device.button(id).name().isEmpty()) + case E_TOGGLE: { + DENG2_ASSERT(device); + InputDeviceButtonControl &button = device->button(id); + if(!button.name().isEmpty()) { - str += device.button(id).name(); + str += button.name(); } - else if(&device == inputSys().devicePtr(IDEV_KEYBOARD)) + else if(device == inputSys().devicePtr(IDEV_KEYBOARD)) { char const *name = B_ShortNameForKey(id); if(name) @@ -668,13 +666,17 @@ String B_ControlDescToString(InputDevice const &device, ddeventtype_t type, int { str += "button" + String::number(id + 1); } + break; } + + case E_AXIS: + DENG2_ASSERT(device); + str += device->axis(id).name(); break; - case E_AXIS: str += device.axis(id).name(); break; case E_ANGLE: str += "hat" + String::number(id + 1); break; case E_SYMBOLIC: str += "sym"; break; - default: DENG2_ASSERT(!"B_DeviceDescToString: Invalid event type"); break; + default: DENG2_ASSERT(!"B_ControlDescToString: Invalid event type"); break; } return str; @@ -728,7 +730,7 @@ String B_StateConditionToString(statecondition_t const &cond) } else { - str += B_ControlDescToString(inputSys().device(cond.device), + str += B_ControlDescToString(cond.device, ( cond.type == SCT_TOGGLE_STATE? E_TOGGLE : cond.type == SCT_AXIS_BEYOND ? E_AXIS : E_ANGLE), cond.id); @@ -758,7 +760,7 @@ String B_StateConditionToString(statecondition_t const &cond) String B_EventToString(ddevent_t const &ev) { - String str = B_ControlDescToString(inputSys().device(ev.device), ev.type, + String str = B_ControlDescToString(ev.device, ev.type, ( ev.type == E_TOGGLE ? ev.toggle.id : ev.type == E_AXIS ? ev.axis.id : ev.type == E_ANGLE ? ev.angle.id @@ -787,23 +789,6 @@ String B_EventToString(ddevent_t const &ev) return str; } -static int bindingIdCounter; - -int B_NewIdentifier() -{ - int id = 0; - while(!id) - { - id = ++bindingIdCounter; - } - return id; -} - -void B_ResetIdentifiers() -{ - bindingIdCounter = 0; -} - struct keyname_t { int key; ///< DDKEY @@ -915,11 +900,11 @@ int B_KeyForShortName(char const *key) for(uint idx = 0; keyNames[idx].key; ++idx) { - if(!stricmp(key, keyNames[idx].name)) + if(!qstricmp(key, keyNames[idx].name)) return keyNames[idx].key; } - if(strlen(key) == 1 && isalnum(key[0])) + if(qstrlen(key) == 1 && isalnum(key[0])) { // ASCII char. return tolower(key[0]); diff --git a/doomsday/client/src/ui/bindcontext.cpp b/doomsday/client/src/ui/bindcontext.cpp index 39f9068819..728a425641 100644 --- a/doomsday/client/src/ui/bindcontext.cpp +++ b/doomsday/client/src/ui/bindcontext.cpp @@ -54,7 +54,7 @@ DENG2_PIMPL(BindContext) // Acquired device states, unless higher-priority contexts override. typedef QSet DeviceIds; DeviceIds acquireDevices; - bool acquireAllDevices = false; ///< @c true= will ignore @var acquireDevices. + bool acquireAllDevices = false; ///< @c true= will ignore @var acquireDevices. typedef QList CommandBindings; CommandBindings commandBinds; @@ -208,7 +208,7 @@ CommandBinding *BindContext::bindCommand(char const *eventDesc, char const *comm try { std::unique_ptr newBind(new CommandBinding); - inputSys().configure(*newBind, eventDesc, command, true/*assign an ID*/); + inputSys().configure(*newBind, eventDesc, command); CommandBinding *bind = newBind.get(); d->commandBinds.prepend(newBind.release()); @@ -240,7 +240,7 @@ ImpulseBinding *BindContext::bindImpulse(char const *ctrlDesc, try { std::unique_ptr newBind(new ImpulseBinding); - inputSys().configure(*newBind, ctrlDesc, impulse.id(), localPlayer, true/*assign an ID*/); + inputSys().configure(*newBind, ctrlDesc, impulse.id(), localPlayer); ImpulseBinding *bind = newBind.get(); d->impulseBinds[localPlayer].append(newBind.release()); @@ -267,13 +267,13 @@ CommandBinding *BindContext::findCommandBinding(char const *command, int deviceI { if(command && command[0]) { - for(CommandBinding *bind : d->commandBinds) + for(CommandBinding const *bind : d->commandBinds) { if(bind->command.compareWithoutCase(command)) continue; if((deviceId < 0 || deviceId >= NUM_INPUT_DEVICES) || bind->deviceId == deviceId) { - return bind; + return const_cast(bind); } } } @@ -283,11 +283,11 @@ CommandBinding *BindContext::findCommandBinding(char const *command, int deviceI ImpulseBinding *BindContext::findImpulseBinding(int deviceId, ibcontroltype_t bindType, int controlId) const { for(int i = 0; i < DDMAXPLAYERS; ++i) - for(ImpulseBinding *bind : d->impulseBinds[i]) + for(ImpulseBinding const *bind : d->impulseBinds[i]) { if(bind->deviceId == deviceId && bind->type == bindType && bind->controlId == controlId) { - return bind; + return const_cast(bind); } } return nullptr; @@ -517,7 +517,7 @@ bool BindContext::tryEvent(ddevent_t const &event, bool respectHigherContexts) c if(!isActive()) return false; // Is this event bindable to an action? - if(event.type != EV_FOCUS) + if(event.type != E_FOCUS) { // See if the command bindings will have it. for(CommandBinding const *bind : d->commandBinds) diff --git a/doomsday/client/src/ui/inputsystem.cpp b/doomsday/client/src/ui/inputsystem.cpp index ac74b7c04c..2870595921 100644 --- a/doomsday/client/src/ui/inputsystem.cpp +++ b/doomsday/client/src/ui/inputsystem.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include "clientapp.h" #include "dd_def.h" // MAXEVENTS, ASSERT_NOT_64BIT @@ -291,10 +292,12 @@ DENG2_PIMPL(InputSystem) , DENG2_OBSERVES(BindContext, AcquireDeviceChange) , DENG2_OBSERVES(BindContext, BindingAddition) { - Binder binder; + bool ignoreInput = false; + SettingsRegister settings; + + Binder binder; Record *scriptBindings = nullptr; - bool ignoreInput = false; typedef QList Devices; Devices devices; @@ -302,6 +305,8 @@ DENG2_PIMPL(InputSystem) typedef QList BindContexts; BindContexts contexts; ///< Ordered from highest to lowest priority. + int bindingIdCounter = 0; + Instance(Public *i) : Base(i) { // Initialize settings. @@ -327,9 +332,27 @@ DENG2_PIMPL(InputSystem) { self.clearAllContexts(); clearAllDevices(); + + // Shutdown system APIs. I_ShutdownInterfaces(); } + void resetIdentifiers() + { + bindingIdCounter = 0; + } + + /// @return Never returns zero, as that is reserved for list roots. + int newIdentifier() + { + int id = 0; + while(!id) + { + id = ++bindingIdCounter; + } + return id; + } + void clearAllDevices() { qDeleteAll(devices); @@ -402,7 +425,7 @@ DENG2_PIMPL(InputSystem) /** * Send all the events of the given timestamp down the responder chain. */ - void dispatchEvents(eventqueue_t *q, timespan_t ticLength, bool updateAxes) + void dispatchEvents(eventqueue_t *q, timespan_t ticLength, bool updateAxes = true) { bool const callGameResponders = App_GameLoaded(); @@ -781,14 +804,12 @@ DENG2_PIMPL(InputSystem) context->forAllCommandBindings([&_self, &context] (CommandBinding &bind) { - InputDevice &dev = _self.device(bind.deviceId); - InputDeviceControl *ctrl = nullptr; switch(bind.type) { - case E_TOGGLE: ctrl = &dev.button(bind.controlId); break; - case E_AXIS: ctrl = &dev.axis(bind.controlId); break; - case E_ANGLE: ctrl = &dev.hat(bind.controlId); break; + case E_AXIS: ctrl = &_self.device(bind.deviceId).axis(bind.controlId); break; + case E_TOGGLE: ctrl = &_self.device(bind.deviceId).button(bind.controlId); break; + case E_ANGLE: ctrl = &_self.device(bind.deviceId).hat(bind.controlId); break; case E_SYMBOLIC: break; @@ -813,8 +834,8 @@ DENG2_PIMPL(InputSystem) InputDeviceControl *ctrl = nullptr; switch(bind.type) { - case IBD_TOGGLE: ctrl = &dev.button(bind.controlId); break; case IBD_AXIS: ctrl = &dev.axis(bind.controlId); break; + case IBD_TOGGLE: ctrl = &dev.button(bind.controlId); break; case IBD_ANGLE: ctrl = &dev.hat(bind.controlId); break; default: @@ -915,7 +936,8 @@ InputDevice &InputSystem::device(int id) const { return *d->devices.at(id); } - throw Error("InputSystem::device", "Unknown id:" + String::number(id)); + /// @throw MissingDeviceError Given id is not valid. + throw MissingDeviceError("InputSystem::device", "Unknown id:" + String::number(id)); } InputDevice *InputSystem::devicePtr(int id) const @@ -983,7 +1005,6 @@ void InputSystem::clearEvents() { clearQueue(&queue); clearQueue(&sharpQueue); - clearEventStrings(); } @@ -1034,7 +1055,7 @@ void InputSystem::processSharpEvents(timespan_t ticLength) // Sharp ticks may have some events queued on the side. if(DD_IsSharpTick() || !DD_IsFrameTimeAdvancing()) { - d->dispatchEvents(&sharpQueue, DD_IsFrameTimeAdvancing()? SECONDSPERTIC : ticLength, true); + d->dispatchEvents(&sharpQueue, DD_IsFrameTimeAdvancing()? SECONDSPERTIC : ticLength); } } @@ -1043,7 +1064,7 @@ bool InputSystem::tryEvent(ddevent_t const &event, String const &namedContext) if(namedContext.isEmpty()) { // Try all active contexts in order. - for(BindContext *context : d->contexts) + for(BindContext const *context : d->contexts) { if(context->tryEvent(event)) return true; } @@ -1080,9 +1101,13 @@ void InputSystem::trackEvent(ddevent_t const &event) if(event.toggle.id == DDKEY_RSHIFT) { if(event.toggle.state == ETOG_DOWN) + { ::shiftDown = true; + } else if(event.toggle.state == ETOG_UP) + { ::shiftDown = false; + } } else if(event.toggle.id == DDKEY_RALT) { @@ -1100,17 +1125,21 @@ void InputSystem::trackEvent(ddevent_t const &event) } // Update the state table. - if(event.type == E_AXIS) + switch(event.type) { + case E_AXIS: dev->axis(event.axis.id).applyRealPosition(event.axis.pos); - } - else if(event.type == E_TOGGLE) - { + break; + + case E_TOGGLE: dev->button(event.toggle.id).setDown(event.toggle.state == ETOG_DOWN || event.toggle.state == ETOG_REPEAT); - } - else if(event.type == E_ANGLE) - { + break; + + case E_ANGLE: dev->hat(event.angle.id).setPosition(event.angle.pos); + break; + + default: break; } } @@ -1121,7 +1150,7 @@ void InputSystem::trackEvent(Event const &event) trackEvent(ddev); } -void InputSystem::convertEvent(Event const &from, ddevent_t &to) // static +bool InputSystem::convertEvent(Event const &from, ddevent_t &to) // static { de::zap(to); switch(from.type()) @@ -1134,11 +1163,12 @@ void InputSystem::convertEvent(Event const &from, ddevent_t &to) // static to.type = E_TOGGLE; to.toggle.id = kev.ddKey(); to.toggle.state = (kev.state() == KeyEvent::Pressed? ETOG_DOWN : ETOG_UP); - strcpy(to.toggle.text, kev.text().toLatin1()); + qstrcpy(to.toggle.text, kev.text().toLatin1()); break; } default: break; } + return true; } bool InputSystem::convertEvent(ddevent_t const &from, event_t &to) // static @@ -1155,7 +1185,7 @@ bool InputSystem::convertEvent(ddevent_t const &from, event_t &to) // static #ifdef __64BIT__ ASSERT_64BIT(from.symbolic.name); ev->data1 = (int)(((uint64_t) from.symbolic.name) & 0xffffffff); // low dword - ev->data2 = (int)(((uint64_t) from.symbolic.name) >> 32); // high dword + ev->data2 = (int)(((uint64_t) from.symbolic.name) >> 32); // high dword #else ASSERT_NOT_64BIT(from.symbolic.name); to.data1 = (int) from.symbolic.name; @@ -1254,13 +1284,6 @@ void InputSystem::initialContextActivations() // These are the contexts active by default. context(GLOBAL_BINDING_CONTEXT_NAME ).activate(); context(DEFAULT_BINDING_CONTEXT_NAME).activate(); - - /* - if(Con_IsActive()) - { - context(CONSOLE_BINDING_CONTEXT_NAME).activate(); - } - */ } void InputSystem::clearAllContexts() @@ -1269,6 +1292,9 @@ void InputSystem::clearAllContexts() qDeleteAll(d->contexts); d->contexts.clear(); + + // We can restart the id counter, all the old bindings were removed. + d->resetIdentifiers(); } int InputSystem::contextCount() const @@ -1340,9 +1366,11 @@ LoopResult InputSystem::forAllContexts(std::function return LoopContinue; } -static char const *parseContext(char const *desc, String &context) +static char const *parseContext(String &context, char const *desc) { - if(!desc || !strchr(desc, ':')) + DENG2_ASSERT(desc); + + if(!strchr(desc, ':')) { // No context defined. context = ""; @@ -1358,6 +1386,25 @@ static char const *parseContext(char const *desc, String &context) // --------------------------------------------------------------------------- +void InputSystem::bindDefaults() +{ + // Engine's highest priority context: opening control panel, opening the console. + bindCommand("global:key-f11-down + key-alt-down", "releasemouse"); + bindCommand("global:key-f11-down", "togglefullscreen"); + bindCommand("global:key-tilde-down + key-shift-up", "taskbar"); + + // Console bindings (when open). + bindCommand("console:key-tilde-down + key-shift-up", "taskbar"); // without this, key would be entered into command line + + // Bias editor. +} + +void InputSystem::bindGameDefaults() +{ + if(!App_GameLoaded()) return; + Con_Executef(CMDS_DDAY, false, "defaultgamebindings"); +} + CommandBinding *InputSystem::bindCommand(char const *eventDesc, char const *command) { DENG2_ASSERT(eventDesc && command); @@ -1365,7 +1412,7 @@ CommandBinding *InputSystem::bindCommand(char const *eventDesc, char const *comm // The event descriptor may begin with a symbolic binding context name. String contextName; - eventDesc = parseContext(eventDesc, contextName); + eventDesc = parseContext(contextName, eventDesc); if(BindContext *context = contextPtr(contextName.isEmpty()? DEFAULT_BINDING_CONTEXT_NAME : contextName)) { @@ -1380,15 +1427,15 @@ ImpulseBinding *InputSystem::bindImpulse(char const *ctrlDesc, char const *impul LOG_AS("InputSystem"); // The impulse descriptor may begin with the local player number. - int localNum = 0; + int localPlayer = 0; AutoStr *str = AutoStr_NewStd(); char const *ptr = Str_CopyDelim(str, impulseDesc, '-'); - if(!strncasecmp(Str_Text(str), "local", 5) && Str_Length(str) > 5) + if(!qstrnicmp(Str_Text(str), "local", 5) && Str_Length(str) > 5) { - localNum = strtoul(Str_Text(str) + 5, nullptr, 10) - 1; - if(localNum < 0 || localNum >= DDMAXPLAYERS) + localPlayer = String((Str_Text(str) + 5)).toInt() - 1; + if(localPlayer < 0 || localPlayer >= DDMAXPLAYERS) { - LOG_INPUT_WARNING("Local player number %i is invalid") << localNum; + LOG_INPUT_WARNING("Local player number %i is invalid") << localPlayer; return nullptr; } @@ -1406,26 +1453,26 @@ ImpulseBinding *InputSystem::bindImpulse(char const *ctrlDesc, char const *impul } BindContext *context = contextPtr(impulse->bindContextName()); - DENG2_ASSERT(context); // Should be known by now? if(!context) { context = contextPtr(DEFAULT_BINDING_CONTEXT_NAME); } DENG2_ASSERT(context); - return context->bindImpulse(ctrlDesc, *impulse, localNum); + return context->bindImpulse(ctrlDesc, *impulse, localPlayer); } /** * Parse the main part of the event descriptor, with no conditions included. */ -static bool configureCommandBinding(CommandBinding &b, char const *eventDesc, char const *command) +static bool configureCommandBinding(CommandBinding &bind, char const *eventDesc, + char const *command) { DENG2_ASSERT(eventDesc); InputSystem &isys = ClientApp::inputSystem(); - // Take a copy of the commands string. - b.command = command; + // Take a copy of the command string. + bind.command = command; // Parse the event descriptor. AutoStr *str = AutoStr_NewStd(); @@ -1434,93 +1481,102 @@ static bool configureCommandBinding(CommandBinding &b, char const *eventDesc, ch eventDesc = Str_CopyDelim(str, eventDesc, '-'); if(!Str_CompareIgnoreCase(str, "key")) { - b.deviceId = IDEV_KEYBOARD; - b.type = E_TOGGLE; // Keyboards only have toggles (as far as we know). + bind.deviceId = IDEV_KEYBOARD; + bind.type = E_TOGGLE; // Keyboards only have toggles (as far as we know). // Parse the key. eventDesc = Str_CopyDelim(str, eventDesc, '-'); - if(!B_ParseKeyId(Str_Text(str), &b.controlId)) + if(!B_ParseKeyId(Str_Text(str), &bind.controlId)) { return false; } // The final part of a key event is the state of the key toggle. eventDesc = Str_CopyDelim(str, eventDesc, '-'); - if(!B_ParseToggleState(Str_Text(str), &b.state)) + if(!B_ParseToggleState(Str_Text(str), &bind.state)) { return false; } } else if(!Str_CompareIgnoreCase(str, "mouse")) { - b.deviceId = IDEV_MOUSE; + bind.deviceId = IDEV_MOUSE; // Next comes a button or axis name. eventDesc = Str_CopyDelim(str, eventDesc, '-'); - if(!B_ParseMouseTypeAndId(Str_Text(str), &b.type, &b.controlId)) + if(!B_ParseMouseTypeAndId(Str_Text(str), &bind.type, &bind.controlId)) { return false; } // The last part determines the toggle state or the axis position. eventDesc = Str_CopyDelim(str, eventDesc, '-'); - if(b.type == E_TOGGLE) + switch(bind.type) { - if(!B_ParseToggleState(Str_Text(str), &b.state)) + case E_TOGGLE: + if(!B_ParseToggleState(Str_Text(str), &bind.state)) { return false; } - } - else // Axis position. - { - if(!B_ParseAxisPosition(Str_Text(str), &b.state, &b.pos)) + break; + + case E_AXIS: + if(!B_ParseAxisPosition(Str_Text(str), &bind.state, &bind.pos)) { return false; } + break; + + default: DENG2_ASSERT(!"configureCommandBinding: Invalid bind.type"); break; } } else if(!Str_CompareIgnoreCase(str, "joy") || !Str_CompareIgnoreCase(str, "head")) { - b.deviceId = (!Str_CompareIgnoreCase(str, "joy")? IDEV_JOY1 : IDEV_HEAD_TRACKER); + bind.deviceId = (!Str_CompareIgnoreCase(str, "joy")? IDEV_JOY1 : IDEV_HEAD_TRACKER); // Next part defined button, axis, or hat. eventDesc = Str_CopyDelim(str, eventDesc, '-'); - if(!B_ParseJoystickTypeAndId(isys.device(b.deviceId), Str_Text(str), &b.type, &b.controlId)) + if(!B_ParseJoystickTypeAndId(isys.device(bind.deviceId), Str_Text(str), &bind.type, &bind.controlId)) { return false; } // What is the state of the toggle, axis, or hat? eventDesc = Str_CopyDelim(str, eventDesc, '-'); - if(b.type == E_TOGGLE) + switch(bind.type) { - if(!B_ParseToggleState(Str_Text(str), &b.state)) + case E_TOGGLE: + if(!B_ParseToggleState(Str_Text(str), &bind.state)) { return false; } - } - else if(b.type == E_AXIS) - { - if(!B_ParseAxisPosition(Str_Text(str), &b.state, &b.pos)) + break; + + case E_AXIS: + if(!B_ParseAxisPosition(Str_Text(str), &bind.state, &bind.pos)) { return false; } - } - else // Angle. - { - if(!B_ParseAnglePosition(Str_Text(str), &b.pos)) + break; + + case E_ANGLE: + if(!B_ParseAnglePosition(Str_Text(str), &bind.pos)) { return false; } + break; + + default: DENG2_ASSERT(!"configureCommandBinding: Invalid bind.type") break; } } else if(!Str_CompareIgnoreCase(str, "sym")) { // A symbolic event. - b.type = E_SYMBOLIC; - b.deviceId = 0; - b.symbolicName = strdup(eventDesc); + bind.type = E_SYMBOLIC; + bind.deviceId = uint(-1); + bind.symbolicName = eventDesc; + eventDesc = nullptr; } else @@ -1540,14 +1596,14 @@ static bool configureCommandBinding(CommandBinding &b, char const *eventDesc, ch return true; } -static bool configureImpulseBinding(ImpulseBinding &b, char const *ctrlDesc, +static bool configureImpulseBinding(ImpulseBinding &bind, char const *ctrlDesc, int impulseId, int localPlayer) { DENG2_ASSERT(ctrlDesc); InputSystem &isys = ClientApp::inputSystem(); - b.impulseId = impulseId; - b.localPlayer = localPlayer; + bind.impulseId = impulseId; + bind.localPlayer = localPlayer; // Parse the control descriptor. AutoStr *str = AutoStr_NewStd(); @@ -1556,47 +1612,47 @@ static bool configureImpulseBinding(ImpulseBinding &b, char const *ctrlDesc, ctrlDesc = Str_CopyDelim(str, ctrlDesc, '-'); if(!Str_CompareIgnoreCase(str, "key")) { - b.deviceId = IDEV_KEYBOARD; - b.type = IBD_TOGGLE; + bind.deviceId = IDEV_KEYBOARD; + bind.type = IBD_TOGGLE; // Parse the key. ctrlDesc = Str_CopyDelim(str, ctrlDesc, '-'); - if(!B_ParseKeyId(Str_Text(str), &b.controlId)) + if(!B_ParseKeyId(Str_Text(str), &bind.controlId)) { return false; } } else if(!Str_CompareIgnoreCase(str, "mouse")) { - b.deviceId = IDEV_MOUSE; + bind.deviceId = IDEV_MOUSE; ctrlDesc = Str_CopyDelim(str, ctrlDesc, '-'); ddeventtype_t type; - if(!B_ParseMouseTypeAndId(Str_Text(str), &type, &b.controlId)) + if(!B_ParseMouseTypeAndId(Str_Text(str), &type, &bind.controlId)) { return false; } - b.type = EVTYPE_TO_IBDTYPE(type); + bind.type = EVTYPE_TO_IBDTYPE(type); } else if(!Str_CompareIgnoreCase(str, "joy") || !Str_CompareIgnoreCase(str, "head")) { - b.deviceId = (!Str_CompareIgnoreCase(str, "joy")? IDEV_JOY1 : IDEV_HEAD_TRACKER); + bind.deviceId = (!Str_CompareIgnoreCase(str, "joy")? IDEV_JOY1 : IDEV_HEAD_TRACKER); // Next part defined button, axis, or hat. ctrlDesc = Str_CopyDelim(str, ctrlDesc, '-'); ddeventtype_t type; - if(!B_ParseJoystickTypeAndId(isys.device(b.deviceId), Str_Text(str), &type, &b.controlId)) + if(!B_ParseJoystickTypeAndId(isys.device(bind.deviceId), Str_Text(str), &type, &bind.controlId)) { return false; } - b.type = EVTYPE_TO_IBDTYPE(type); + bind.type = EVTYPE_TO_IBDTYPE(type); // Hats include the angle. if(type == E_ANGLE) { ctrlDesc = Str_CopyDelim(str, ctrlDesc, '-'); - if(!B_ParseAnglePosition(Str_Text(str), &b.angle)) + if(!B_ParseAnglePosition(Str_Text(str), &bind.angle)) { return false; } @@ -1609,11 +1665,11 @@ static bool configureImpulseBinding(ImpulseBinding &b, char const *ctrlDesc, ctrlDesc = Str_CopyDelim(str, ctrlDesc, '-'); if(!Str_CompareIgnoreCase(str, "inverse")) { - b.flags |= IBDF_INVERSE; + bind.flags |= IBDF_INVERSE; } else if(!Str_CompareIgnoreCase(str, "staged")) { - b.flags |= IBDF_TIME_STAGED; + bind.flags |= IBDF_TIME_STAGED; } else { @@ -1626,7 +1682,7 @@ static bool configureImpulseBinding(ImpulseBinding &b, char const *ctrlDesc, } void InputSystem::configure(CommandBinding &bind, char const *eventDesc, - char const *command, bool newId) + char const *command, bool assignNewId) { DENG2_ASSERT(eventDesc); LOG_AS("InputSystem"); @@ -1641,6 +1697,7 @@ void InputSystem::configure(CommandBinding &bind, char const *eventDesc, } // Any conditions? + bind.conditions.clear(); while(eventDesc) { // A new condition. @@ -1654,12 +1711,11 @@ void InputSystem::configure(CommandBinding &bind, char const *eventDesc, } } - // Assign a new binding ID? - if(newId) bind.id = B_NewIdentifier(); + if(assignNewId) bind.id = d->newIdentifier(); } void InputSystem::configure(ImpulseBinding &bind, char const *ctrlDesc, int impulseId, - int localPlayer, bool newId) + int localPlayer, bool assignNewId) { DENG2_ASSERT(ctrlDesc); DENG2_ASSERT(localPlayer >= 0 && localPlayer < DDMAXPLAYERS); @@ -1675,6 +1731,7 @@ void InputSystem::configure(ImpulseBinding &bind, char const *ctrlDesc, int impu } // Any conditions? + bind.conditions.clear(); while(ctrlDesc) { // A new condition. @@ -1688,14 +1745,13 @@ void InputSystem::configure(ImpulseBinding &bind, char const *ctrlDesc, int impu } } - // Assign a new binding ID? - if(newId) bind.id = B_NewIdentifier(); + if(assignNewId) bind.id = d->newIdentifier(); } String InputSystem::composeBindsFor(CommandBinding const &bind) { LOG_AS("InputSystem"); - String str = B_ControlDescToString(device(bind.deviceId), bind.type, bind.controlId); + String str = B_ControlDescToString(bind.deviceId, bind.type, bind.controlId); switch(bind.type) { @@ -1719,7 +1775,7 @@ String InputSystem::composeBindsFor(CommandBinding const &bind) String InputSystem::composeBindsFor(ImpulseBinding const &bind) { LOG_AS("InputSystem"); - String str = B_ControlDescToString(device(bind.deviceId), IBDTYPE_TO_EVTYPE(bind.type), bind.controlId); + String str = B_ControlDescToString(bind.deviceId, IBDTYPE_TO_EVTYPE(bind.type), bind.controlId); if(bind.type == IBD_ANGLE) { @@ -1755,6 +1811,17 @@ bool InputSystem::removeBinding(int id) return false; } +void InputSystem::removeAllBindings() +{ + for(BindContext *context : d->contexts) + { + context->clearAllBindings(); + }; + + // We can restart the id counter, all the old bindings were removed. + d->resetIdentifiers(); +} + D_CMD(ListDevices) { DENG2_UNUSED3(src, argc, argv); @@ -1811,7 +1878,7 @@ D_CMD(ActivateContext) DENG2_UNUSED2(src, argc); InputSystem &isys = ClientApp::inputSystem(); - bool const doActivate = !stricmp(argv[0], "activatebcontext"); + bool const doActivate = !String(argv[0]).compareWithoutCase("activatebcontext"); String const name = argv[1]; if(!isys.hasContext(name)) @@ -1912,15 +1979,7 @@ D_CMD(ListBindings) D_CMD(ClearBindings) { DENG2_UNUSED3(src, argc, argv); - - ClientApp::inputSystem().forAllContexts([] (BindContext &context) - { - context.clearAllBindings(); - return LoopContinue; - }); - - // We can restart the id counter, all the old bindings were destroyed. - B_ResetIdentifiers(); + ClientApp::inputSystem().removeAllBindings(); return true; } @@ -1928,7 +1987,7 @@ D_CMD(RemoveBinding) { DENG2_UNUSED2(src, argc); - int id = strtoul(argv[1], nullptr, 10); + int const id = String(argv[1]).toInt(); if(ClientApp::inputSystem().removeBinding(id)) { LOG_INPUT_MSG("Binding " _E(b) "%i" _E(.) " deleted") << id; @@ -1944,10 +2003,9 @@ D_CMD(RemoveBinding) D_CMD(DefaultBindings) { DENG2_UNUSED3(src, argc, argv); - - B_BindDefaults(); - B_BindGameDefaults(); - + InputSystem &isys = ClientApp::inputSystem(); + isys.bindDefaults(); + isys.bindGameDefaults(); return true; } @@ -2024,7 +2082,7 @@ DENG_EXTERN_C int B_BindingsForCommand(char const *commandCString, char *outBuf, // Copy the result to the return buffer. std::memset(outBuf, 0, outBufSize); - strncpy(outBuf, out.toUtf8().constData(), outBufSize - 1); + qstrncpy(outBuf, out.toUtf8().constData(), outBufSize - 1); return numFound; } @@ -2073,7 +2131,7 @@ DENG_EXTERN_C int B_BindingsForControl(int localPlayer, char const *impulseNameC // Copy the result to the return buffer. std::memset(outBuf, 0, outBufSize); - strncpy(outBuf, out.toUtf8().constData(), outBufSize - 1); + qstrncpy(outBuf, out.toUtf8().constData(), outBufSize - 1); return numFound; } @@ -2082,7 +2140,7 @@ DENG_EXTERN_C int DD_GetKeyCode(char const *key) { DENG2_ASSERT(key); int code = B_KeyForShortName(key); - return (code ? code : key[0]); + return (code? code : key[0]); } DENG_DECLARE_API(B) =