From ea99c83ad90b8554606a0bfc947d2f778535621c Mon Sep 17 00:00:00 2001 From: danij Date: Tue, 4 Nov 2014 10:50:28 +0000 Subject: [PATCH] Client|InputSystem|Bindings: Cleanup --- doomsday/client/include/ui/b_context.h | 62 +- doomsday/client/src/ui/b_context.cpp | 567 ++++++++---------- doomsday/client/src/ui/b_main.cpp | 102 ++-- .../client/src/ui/infine/infinesystem.cpp | 10 +- doomsday/client/src/ui/widgetactions.cpp | 9 +- .../src/ui/widgets/inputbindingwidget.cpp | 7 +- 6 files changed, 349 insertions(+), 408 deletions(-) diff --git a/doomsday/client/include/ui/b_context.h b/doomsday/client/include/ui/b_context.h index d55617fb58..64368c7e9d 100644 --- a/doomsday/client/include/ui/b_context.h +++ b/doomsday/client/include/ui/b_context.h @@ -25,7 +25,8 @@ #include "b_command.h" #include "b_device.h" -struct controlbinding_t { +struct controlbinding_t +{ controlbinding_t *next; controlbinding_t *prev; int bid; ///< Unique identifier. @@ -133,21 +134,15 @@ class BindContext DENG2_PRIVATE(d) }; -/** - * Marks all device states with the highest-priority binding context to which they have - * a connection via device bindings. This ensures that if a high-priority context is - * using a particular device state, lower-priority contexts will not be using the same - * state for their own controls. - * - * Called automatically whenever a context is activated or deactivated. - */ -void B_UpdateAllDeviceStateAssociations(); +// ------------------------------------------------------------------------------ -/** - * Creates a new binding context. The new context has the highest priority - * of all existing contexts, and is inactive. - */ -BindContext *B_NewContext(char const *name); +void B_DestroyControlBinding(controlbinding_t *conBin); + +void B_InitControlBindingList(controlbinding_t *listRoot); + +void B_DestroyControlBindingList(controlbinding_t *listRoot); + +// ------------------------------------------------------------------------------ /** * Destroy all binding contexts and the bindings within the contexts. @@ -155,25 +150,23 @@ BindContext *B_NewContext(char const *name); */ void B_DestroyAllContexts(); -void B_SetContextFallbackForDDEvents(char const *name, int (*ddResponderFunc)(ddevent_t const *)); - -BindContext *B_ContextByPos(int pos); - -BindContext *B_ContextByName(de::String const &name); - int B_ContextCount(); -int B_GetContextPos(BindContext *bc); +bool B_HasContext(de::String const &name); -void B_ReorderContext(BindContext *bc, int pos); +BindContext &B_Context(de::String const &name); -void B_DestroyContext(BindContext *bc); +BindContext *B_ContextPtr(de::String const &name); -void B_DestroyControlBinding(controlbinding_t *conBin); +BindContext &B_ContextAt(int position); -void B_InitControlBindingList(controlbinding_t *listRoot); +int B_ContextPositionOf(BindContext *bc); -void B_DestroyControlBindingList(controlbinding_t *listRoot); +/** + * Creates a new binding context. The new context has the highest priority + * of all existing contexts, and is inactive. + */ +BindContext *B_NewContext(de::String const &name); /** * Finds the action bound to a given event, iterating through all enabled @@ -185,8 +178,19 @@ void B_DestroyControlBindingList(controlbinding_t *listRoot); */ de::Action *B_ActionForEvent(ddevent_t const *event); -void B_PrintContexts(); +/** + * Marks all device states with the highest-priority binding context to which they have + * a connection via device bindings. This ensures that if a high-priority context is + * using a particular device state, lower-priority contexts will not be using the same + * state for their own controls. + * + * Called automatically whenever a context is activated or deactivated. + */ +void B_UpdateAllDeviceStateAssociations(); -void B_PrintAllBindings(); +/** + * Iterate through all the BindContexts from highest to lowest priority. + */ +de::LoopResult B_ForAllContexts(std::function func); #endif // CLIENT_INPUTSYSTEM_BINDCONTEXT_H diff --git a/doomsday/client/src/ui/b_context.cpp b/doomsday/client/src/ui/b_context.cpp index 62ff0b60d6..57ea470da8 100644 --- a/doomsday/client/src/ui/b_context.cpp +++ b/doomsday/client/src/ui/b_context.cpp @@ -23,6 +23,8 @@ #include "ui/b_context.h" #include +#include +#include #include #include #include "clientapp.h" @@ -38,228 +40,11 @@ using namespace de; -static int bindContextCount; -static BindContext **bindContexts; - static inline InputSystem &inputSys() { return ClientApp::inputSystem(); } -void B_DestroyAllContexts() -{ - if(bindContexts) - { - // Do not use the global bindContextCount to control the loop; it is - // changed as a sideeffect of B_DestroyContext() and also, the - // bindContexts array itself is shifted back to idx 0 afterwards. - int const numBindClasses = bindContextCount; - for(int i = 0; i < numBindClasses; ++i) - { - B_DestroyContext(bindContexts[0]); - } - M_Free(bindContexts); - } - bindContexts = nullptr; - bindContextCount = 0; -} - -/// @todo: Most of this logic belongs in BindContext. -void B_UpdateAllDeviceStateAssociations() -{ - // Clear all existing associations. - inputSys().forAllDevices([] (InputDevice &device) - { - device.forAllControls([] (InputDeviceControl &control) - { - control.clearBindContextAssociation(); - return LoopContinue; - }); - return LoopContinue; - }); - - // We need to iterate through all the device bindings in all contexts. - for(int i = 0; i < bindContextCount; ++i) - { - BindContext *bc = bindContexts[i]; - - // Skip inactive contexts. - if(!bc->isActive()) - continue; - - // Mark all event bindings in the context. - bc->forAllCommandBindings([&bc] (evbinding_t &cb) - { - InputDevice &device = inputSys().device(cb.device); - - switch(cb.type) - { - case E_TOGGLE: { - InputDeviceControl &ctrl = device.button(cb.id); - if(!ctrl.hasBindContext()) - { - ctrl.setBindContext(bc); - } - break; } - - case E_AXIS: { - InputDeviceControl &ctrl = device.axis(cb.id); - if(!ctrl.hasBindContext()) - { - ctrl.setBindContext(bc); - } - break; } - - case E_ANGLE: { - InputDeviceControl &ctrl = device.hat(cb.id); - if(!ctrl.hasBindContext()) - { - ctrl.setBindContext(bc); - } - break; } - - case E_SYMBOLIC: - break; - - default: - DENG2_ASSERT(!"B_UpdateAllDeviceStateAssociations: Invalid eb.type"); - break; - } - return LoopContinue; - }); - - // All controls in the context. - bc->forAllControlBindings([&bc] (controlbinding_t &cb) - { - // Associate all the device bindings. - for(int k = 0; k < DDMAXPLAYERS; ++k) - for(dbinding_t *db = cb.deviceBinds[k].next; db != &cb.deviceBinds[k]; db = db->next) - { - InputDevice &device = inputSys().device(db->device); - - switch(db->type) - { - case CBD_TOGGLE: { - InputDeviceControl &ctrl = device.button(db->id); - if(!ctrl.hasBindContext()) - { - ctrl.setBindContext(bc); - } - break; } - - case CBD_AXIS: { - InputDeviceControl &ctrl = device.axis(db->id); - if(!ctrl.hasBindContext()) - { - ctrl.setBindContext(bc); - } - break; } - - case CBD_ANGLE: { - InputDeviceControl &ctrl = device.hat(db->id); - if(!ctrl.hasBindContext()) - { - ctrl.setBindContext(bc); - } - break; } - - default: - DENG2_ASSERT(!"B_UpdateAllDeviceStateAssociations: Invalid db->type"); - break; - } - } - return LoopContinue; - }); - - // If the context have made a broad device acquisition, mark all - // relevant states. - if(bc->willAcquireKeyboard()) - { - InputDevice &device = inputSys().device(IDEV_KEYBOARD); - if(device.isActive()) - { - device.forAllControls([&bc] (InputDeviceControl &control) - { - if(!control.hasBindContext()) - { - control.setBindContext(bc); - } - return LoopContinue; - }); - } - } - - if(bc->willAcquireAll()) - { - inputSys().forAllDevices([&bc] (InputDevice &device) - { - if(device.isActive()) - { - device.forAllControls([&bc] (InputDeviceControl &control) - { - if(!control.hasBindContext()) - { - control.setBindContext(bc); - } - return LoopContinue; - }); - } - return LoopContinue; - }); - } - } - - // Now that we know what are the updated context associations, let's check - // the devices and see if any of the states need to be expired. - inputSys().forAllDevices([] (InputDevice &device) - { - device.forAllControls([] (InputDeviceControl &control) - { - if(!control.inDefaultState()) - { - control.expireBindContextAssociationIfChanged(); - } - return LoopContinue; - }); - return LoopContinue; - }); -} - -static void B_SetContextCount(int count) -{ - bindContexts = (BindContext **) M_Realloc(bindContexts, sizeof(*bindContexts) * count); - bindContextCount = count; -} - -static void B_InsertContext(BindContext *bc, int contextIdx) -{ - DENG2_ASSERT(bc); - - B_SetContextCount(bindContextCount + 1); - if(contextIdx < bindContextCount - 1) - { - // We need to make room for this new binding context. - std::memmove(&bindContexts[contextIdx + 1], &bindContexts[contextIdx], - sizeof(BindContext *) * (bindContextCount - 1 - contextIdx)); - } - bindContexts[contextIdx] = bc; -} - -static void B_RemoveContext(BindContext *bc) -{ - if(!bc) return; - - int contextIdx = B_GetContextPos(bc); - - if(contextIdx >= 0) - { - std::memmove(&bindContexts[contextIdx], &bindContexts[contextIdx + 1], - sizeof(BindContext *) * (bindContextCount - 1 - contextIdx)); - - B_SetContextCount(bindContextCount - 1); - } -} - // Binding Context Flags: #define BCF_ACTIVE 0x01 ///< Context is only used when it is active. #define BCF_PROTECTED 0x02 ///< Context cannot be (de)activated by plugins. @@ -538,13 +323,6 @@ dbinding_t *BindContext::findDeviceBinding(int device, cbdevtype_t bindType, int return nullptr; } -BindContext *B_NewContext(char const *name) -{ - BindContext *bc = new BindContext(name); - B_InsertContext(bc, 0); - return bc; -} - bool BindContext::deleteBinding(int bid) { // Check if it is one of the command bindings. @@ -798,109 +576,106 @@ void BindContext::writeToFile(FILE *file) const // ------------------------------------------------------------------------------ -void B_DestroyContext(BindContext *bc) +void B_InitControlBindingList(controlbinding_t *listRoot) { - if(!bc) return; - B_RemoveContext(bc); - delete bc; + DENG2_ASSERT(listRoot); + de::zapPtr(listRoot); + listRoot->next = listRoot->prev = listRoot; } -void B_SetContextFallbackForDDEvents(char const *name, int (*ddResponderFunc)(ddevent_t const *)) +void B_DestroyControlBindingList(controlbinding_t *listRoot) { - if(BindContext *ctx = B_ContextByName(name)) + DENG2_ASSERT(listRoot); + while(listRoot->next != listRoot) { - ctx->setDDFallbackResponder(ddResponderFunc); + B_DestroyControlBinding(listRoot->next); } } -void B_SetContextFallback(char const *name, int (*responderFunc)(event_t *)) +void B_DestroyControlBinding(controlbinding_t *conBin) { - if(BindContext *ctx = B_ContextByName(name)) + if(!conBin) return; + + DENG2_ASSERT(conBin->bid != 0); + + // Unlink first, if linked. + if(conBin->prev) { - ctx->setFallbackResponder(responderFunc); + conBin->prev->next = conBin->next; + conBin->next->prev = conBin->prev; } -} -BindContext *B_ContextByName(String const &name) -{ - for(int i = 0; i < bindContextCount; ++i) + for(int i = 0; i < DDMAXPLAYERS; ++i) { - BindContext const *context = bindContexts[i]; - if(!context->name().compareWithoutCase(name)) - { - return const_cast(context); - } + B_DestroyDeviceBindingList(&conBin->deviceBinds[i]); } - return nullptr; + M_Free(conBin); } -BindContext *B_ContextByPos(int pos) +// ------------------------------------------------------------------------------ + +typedef QList BindContexts; +static BindContexts bindContexts; ///< Ordered from highest to lowest priority. + +void B_DestroyAllContexts() { - if(pos >= 0 && pos < bindContextCount) - { - return bindContexts[pos]; - } - return nullptr; + if(bindContexts.isEmpty()) return; + + qDeleteAll(bindContexts); + bindContexts.clear(); } int B_ContextCount() { - return bindContextCount; + return bindContexts.count(); } -int B_GetContextPos(BindContext *bc) +bool B_HasContext(String const &name) { - if(bc) - { - for(int i = 0; i < bindContextCount; ++i) - { - if(bindContexts[i] == bc) - return i; - } - } - return -1; + return B_ContextPtr(name) != nullptr; } -void B_ReorderContext(BindContext *bc, int pos) +BindContext &B_Context(String const &name) { - B_RemoveContext(bc); - B_InsertContext(bc, pos); + if(BindContext *bc = B_ContextPtr(name)) + { + return *bc; + } + throw Error("B_Context", "Unknown binding context name:" + name); } -void B_DestroyControlBinding(controlbinding_t *conBin) +/// @todo: Optimize O(n) search... +BindContext *B_ContextPtr(de::String const &name) { - if(!conBin) return; - - DENG2_ASSERT(conBin->bid != 0); - - // Unlink first, if linked. - if(conBin->prev) + for(BindContext const *bc : bindContexts) { - conBin->prev->next = conBin->next; - conBin->next->prev = conBin->prev; + if(!bc->name().compareWithoutCase(name)) + { + return const_cast(bc); + } } + return nullptr; +} - for(int i = 0; i < DDMAXPLAYERS; ++i) +BindContext &B_ContextAt(int position) +{ + if(position >= 0 && position < bindContexts.count()) { - B_DestroyDeviceBindingList(&conBin->deviceBinds[i]); + return *bindContexts.at(position); } - M_Free(conBin); + throw Error("B_ContextAt", "Invalid position:" + String::number(position)); } -void B_InitControlBindingList(controlbinding_t *listRoot) +int B_ContextPositionOf(BindContext *bc) { - DENG2_ASSERT(listRoot); - de::zapPtr(listRoot); - listRoot->next = listRoot->prev = listRoot; + return (bc? bindContexts.indexOf(bc) : -1); } -void B_DestroyControlBindingList(controlbinding_t *listRoot) +BindContext *B_NewContext(String const &name) { - DENG2_ASSERT(listRoot); - while(listRoot->next != listRoot) - { - B_DestroyControlBinding(listRoot->next); - } + BindContext *bc = new BindContext(name); + bindContexts.prepend(bc); + return bc; } Action *B_ActionForEvent(ddevent_t const *event) @@ -908,11 +683,9 @@ Action *B_ActionForEvent(ddevent_t const *event) event_t ev; bool validGameEvent = InputSystem::convertEvent(event, &ev); - for(int i = 0; i < bindContextCount; ++i) + for(BindContext *bc : bindContexts) { - BindContext *bc = bindContexts[i]; - if(!bc->isActive()) - continue; + if(!bc->isActive()) continue; if(Action *act = bc->actionForEvent(event)) { @@ -927,22 +700,24 @@ Action *B_ActionForEvent(ddevent_t const *event) // Try the fallback responders. if(bc->tryFallbackResponders(*event, ev, validGameEvent)) + { return nullptr; // fallback responder executed something + } } return nullptr; // Nobody had a binding for this event. } +/// @todo: Don't format a string - just collect pointers. int B_BindingsForCommand(char const *cmd, char *buf, size_t bufSize) { DENG2_ASSERT(cmd && buf); - ddstring_t result; Str_Init(&result); - ddstring_t str; Str_Init(&str); + AutoStr *result = AutoStr_NewStd(); + AutoStr *str = AutoStr_NewStd(); int numFound = 0; - for(int i = 0; i < bindContextCount; ++i) + for(BindContext const *bc : bindContexts) { - BindContext *bc = bindContexts[i]; bc->forAllCommandBindings([&bc, &numFound, &str, &cmd, &result] (evbinding_t &eb) { if(strcmp(eb.command, cmd)) @@ -951,24 +726,23 @@ int B_BindingsForCommand(char const *cmd, char *buf, size_t bufSize) // It's here! if(numFound) { - Str_Append(&result, " "); + Str_Append(result, " "); } numFound++; - B_EventBindingToString(&eb, &str); - Str_Appendf(&result, "%i@%s:%s", eb.bid, bc->name().toUtf8().constData(), Str_Text(&str)); + B_EventBindingToString(&eb, str); + Str_Appendf(result, "%i@%s:%s", eb.bid, bc->name().toUtf8().constData(), Str_Text(str)); return LoopContinue; }); } // Copy the result to the return buffer. std::memset(buf, 0, bufSize); - strncpy(buf, Str_Text(&result), bufSize - 1); + strncpy(buf, Str_Text(result), bufSize - 1); - Str_Free(&result); - Str_Free(&str); return numFound; } +/// @todo: Don't format a string - just collect pointers. int B_BindingsForControl(int localPlayer, char const *controlName, int inverse, char *buf, size_t bufSize) { @@ -977,13 +751,12 @@ int B_BindingsForControl(int localPlayer, char const *controlName, int inverse, if(localPlayer < 0 || localPlayer >= DDMAXPLAYERS) return 0; - ddstring_t result; Str_Init(&result); - ddstring_t str; Str_Init(&str); + AutoStr *result = AutoStr_NewStd(); + AutoStr *str = AutoStr_NewStd(); int numFound = 0; - for(int i = 0; i < bindContextCount; ++i) + for(BindContext *bc : bindContexts) { - BindContext *bc = bindContexts[i]; bc->forAllControlBindings([&bc, &controlName, &inverse, &localPlayer, &numFound, &result, &str] (controlbinding_t &cb) { char const *name = P_PlayerControlById(cb.control)->name; @@ -1000,12 +773,12 @@ int B_BindingsForControl(int localPlayer, char const *controlName, int inverse, // It's here! if(numFound) { - Str_Append(&result, " "); + Str_Append(result, " "); } numFound++; - B_DeviceBindingToString(db, &str); - Str_Appendf(&result, "%i@%s:%s", db->bid, bc->name().toUtf8().constData(), Str_Text(&str)); + B_DeviceBindingToString(db, str); + Str_Appendf(result, "%i@%s:%s", db->bid, bc->name().toUtf8().constData(), Str_Text(str)); } } return LoopContinue; @@ -1014,36 +787,182 @@ int B_BindingsForControl(int localPlayer, char const *controlName, int inverse, // Copy the result to the return buffer. std::memset(buf, 0, bufSize); - strncpy(buf, Str_Text(&result), bufSize - 1); + strncpy(buf, Str_Text(result), bufSize - 1); - Str_Free(&result); - Str_Free(&str); return numFound; } -void B_PrintContexts() +/// @todo: Most of this logic belongs in BindContext. +void B_UpdateAllDeviceStateAssociations() { - LOG_INPUT_MSG("%i binding contexts defined:") << bindContextCount; + // Clear all existing associations. + inputSys().forAllDevices([] (InputDevice &device) + { + device.forAllControls([] (InputDeviceControl &control) + { + control.clearBindContextAssociation(); + return LoopContinue; + }); + return LoopContinue; + }); - for(int i = 0; i < bindContextCount; ++i) + // Mark all bindings in all contexts. + for(BindContext *bc : bindContexts) { - BindContext *bc = bindContexts[i]; - LOG_INPUT_MSG("[%3i] %s\"%s\"" _E(.) " (%s)") - << i - << (bc->isActive()? _E(b) : _E(w)) - << bc->name() - << (bc->isActive()? "active" : "inactive"); + // Skip inactive contexts. + if(!bc->isActive()) + continue; + + bc->forAllCommandBindings([&bc] (evbinding_t &cb) + { + InputDevice &device = inputSys().device(cb.device); + + switch(cb.type) + { + case E_TOGGLE: { + InputDeviceControl &ctrl = device.button(cb.id); + if(!ctrl.hasBindContext()) + { + ctrl.setBindContext(bc); + } + break; } + + case E_AXIS: { + InputDeviceControl &ctrl = device.axis(cb.id); + if(!ctrl.hasBindContext()) + { + ctrl.setBindContext(bc); + } + break; } + + case E_ANGLE: { + InputDeviceControl &ctrl = device.hat(cb.id); + if(!ctrl.hasBindContext()) + { + ctrl.setBindContext(bc); + } + break; } + + case E_SYMBOLIC: + break; + + default: + DENG2_ASSERT(!"B_UpdateAllDeviceStateAssociations: Invalid eb.type"); + break; + } + return LoopContinue; + }); + + bc->forAllControlBindings([&bc] (controlbinding_t &cb) + { + // Associate all the device bindings. + for(int k = 0; k < DDMAXPLAYERS; ++k) + for(dbinding_t *db = cb.deviceBinds[k].next; db != &cb.deviceBinds[k]; db = db->next) + { + InputDevice &device = inputSys().device(db->device); + + switch(db->type) + { + case CBD_TOGGLE: { + InputDeviceControl &ctrl = device.button(db->id); + if(!ctrl.hasBindContext()) + { + ctrl.setBindContext(bc); + } + break; } + + case CBD_AXIS: { + InputDeviceControl &ctrl = device.axis(db->id); + if(!ctrl.hasBindContext()) + { + ctrl.setBindContext(bc); + } + break; } + + case CBD_ANGLE: { + InputDeviceControl &ctrl = device.hat(db->id); + if(!ctrl.hasBindContext()) + { + ctrl.setBindContext(bc); + } + break; } + + default: + DENG2_ASSERT(!"B_UpdateAllDeviceStateAssociations: Invalid db->type"); + break; + } + } + return LoopContinue; + }); + + // If the context have made a broad device acquisition, mark all relevant states. + if(bc->willAcquireKeyboard()) + { + InputDevice &device = inputSys().device(IDEV_KEYBOARD); + if(device.isActive()) + { + device.forAllControls([&bc] (InputDeviceControl &control) + { + if(!control.hasBindContext()) + { + control.setBindContext(bc); + } + return LoopContinue; + }); + } + } + + if(bc->willAcquireAll()) + { + inputSys().forAllDevices([&bc] (InputDevice &device) + { + if(device.isActive()) + { + device.forAllControls([&bc] (InputDeviceControl &control) + { + if(!control.hasBindContext()) + { + control.setBindContext(bc); + } + return LoopContinue; + }); + } + return LoopContinue; + }); + } } + + // Now that we know what are the updated context associations, let's check the devices + // and see if any of the states need to be expired. + inputSys().forAllDevices([] (InputDevice &device) + { + device.forAllControls([] (InputDeviceControl &control) + { + if(!control.inDefaultState()) + { + control.expireBindContextAssociationIfChanged(); + } + return LoopContinue; + }); + return LoopContinue; + }); } -void B_PrintAllBindings() +LoopResult B_ForAllContexts(std::function func) { - LOG_INPUT_MSG("%i binding contexts defined") << bindContextCount; + for(BindContext *bc : bindContexts) + { + if(auto result = func(*bc)) return result; + } + return LoopContinue; +} - for(int i = 0; i < bindContextCount; ++i) +#undef B_SetContextFallback +void B_SetContextFallback(char const *name, int (*responderFunc)(event_t *)) +{ + if(B_HasContext(name)) { - BindContext *bc = bindContexts[i]; - bc->printAllBindings(); + B_Context(name).setFallbackResponder(responderFunc); } } diff --git a/doomsday/client/src/ui/b_main.cpp b/doomsday/client/src/ui/b_main.cpp index ae9e73ab83..2f34ae1b49 100644 --- a/doomsday/client/src/ui/b_main.cpp +++ b/doomsday/client/src/ui/b_main.cpp @@ -229,21 +229,21 @@ void B_Init() void B_InitialContextActivations() { - // Disable all contexts. - for(int i = 0; i < B_ContextCount(); ++i) + // Deactivate all contexts. + B_ForAllContexts([] (BindContext &bc) { - BindContext *context = B_ContextByPos(i); - context->deactivate(); - } + bc.deactivate(); + return LoopContinue; + }); // These are the contexts active by default. - B_ContextByName(GLOBAL_BINDING_CONTEXT_NAME)->activate(); - B_ContextByName(DEFAULT_BINDING_CONTEXT_NAME)->activate(); + B_Context(GLOBAL_BINDING_CONTEXT_NAME).activate(); + B_Context(DEFAULT_BINDING_CONTEXT_NAME).activate(); /* if(Con_IsActive()) { - B_ContextByName(CONSOLE_BINDING_CONTEXT_NAME)->activate(); + B_Context(CONSOLE_BINDING_CONTEXT_NAME).activate(); } */ } @@ -295,7 +295,7 @@ char const *B_ParseContext(char const *desc, BindContext **bc) AutoStr *str = AutoStr_NewStd(); desc = Str_CopyDelim(str, desc, ':'); - *bc = B_ContextByName(Str_Text(str)); + *bc = B_ContextPtr(Str_Text(str)); return desc; } @@ -309,7 +309,7 @@ evbinding_t *B_BindCommand(char const *eventDesc, char const *command) eventDesc = B_ParseContext(eventDesc, &bc); if(!bc) { - bc = B_ContextByName(DEFAULT_BINDING_CONTEXT_NAME); + bc = B_ContextPtr(DEFAULT_BINDING_CONTEXT_NAME); } return bc->bindCommand(eventDesc, command); @@ -346,11 +346,12 @@ dbinding_t *B_BindControl(char const *controlDesc, char const *device) return nullptr; } - BindContext *bc = B_ContextByName(control->bindContextName); + BindContext *bc = B_ContextPtr(control->bindContextName); if(!bc) { - bc = B_ContextByName(DEFAULT_BINDING_CONTEXT_NAME); + bc = B_ContextPtr(DEFAULT_BINDING_CONTEXT_NAME); } + DENG2_ASSERT(bc); LOG_INPUT_VERBOSE("Control '%s' in context '%s' of local player %i to be bound to '%s'") << control->name << bc->name() << localNum << device; @@ -389,7 +390,7 @@ dbinding_t *B_GetControlDeviceBindings(int localNum, int control, BindContext ** return nullptr; playercontrol_t *pc = P_PlayerControlById(control); - BindContext *bc = B_ContextByName(pc->bindContextName); + BindContext *bc = B_ContextPtr(pc->bindContextName); if(bContext) *bContext = bc; @@ -403,12 +404,10 @@ dbinding_t *B_GetControlDeviceBindings(int localNum, int control, BindContext ** dd_bool B_Delete(int bid) { - for(int i = 0; i < B_ContextCount(); ++i) + B_ForAllContexts([&bid] (BindContext &bc) { - BindContext *context = B_ContextByPos(i); - if(context->deleteBinding(bid)) - return true; - } + return bc.deleteBinding(bid); + }); return false; } @@ -494,28 +493,30 @@ int B_KeyForShortName(char const *key) void B_WriteToFile(FILE *file) { + DENG2_ASSERT(file); + // Start with a clean slate when restoring the bindings. fprintf(file, "clearbindings\n\n"); - for(int i = 0; i < B_ContextCount(); ++i) + B_ForAllContexts([&file] (BindContext &bc) { - BindContext *context = B_ContextByPos(i); - context->writeToFile(file); - } + bc.writeToFile(file); + return LoopContinue; + }); } bool B_UnbindCommand(char const *command) { - bool deleted = false; - for(int i = 0; i < B_ContextCount(); ++i) + bool didDelete = false; + B_ForAllContexts([&command, &didDelete] (BindContext &bc) { - BindContext *context = B_ContextByPos(i); - while(evbinding_t *ev = context->findCommandBinding(command, NUM_INPUT_DEVICES)) + while(evbinding_t *ev = bc.findCommandBinding(command, NUM_INPUT_DEVICES)) { - deleted |= context->deleteBinding(ev->bid); + didDelete |= bc.deleteBinding(ev->bid); } - } - return deleted; + return LoopContinue; + }); + return didDelete; } #undef DD_GetKeyCode @@ -551,14 +552,28 @@ D_CMD(BindControlToDevice) D_CMD(ListBindingContexts) { DENG2_UNUSED3(src, argc, argv); - B_PrintContexts(); + LOG_INPUT_MSG("%i binding contexts defined:") << B_ContextCount(); + int idx = 0; + B_ForAllContexts([&idx] (BindContext &bc) + { + LOG_INPUT_MSG("[%3i] %s\"%s\"" _E(.) " (%s)") + << (idx++) << (bc.isActive()? _E(b) : _E(w)) + << bc.name() + << (bc.isActive()? "active" : "inactive"); + return LoopContinue; + }); return true; } D_CMD(ListBindings) { DENG2_UNUSED3(src, argc, argv); - B_PrintAllBindings(); + LOG_INPUT_MSG("%i binding contexts defined") << B_ContextCount(); + B_ForAllContexts([] (BindContext &bc) + { + bc.printAllBindings(); + return LoopContinue; + }); return true; } @@ -574,12 +589,12 @@ D_CMD(ClearBindings) { DENG2_UNUSED3(src, argc, argv); - for(int i = 0; i < B_ContextCount(); ++i) + B_ForAllContexts([] (BindContext &bc) { - BindContext *context = B_ContextByPos(i); - LOG_INPUT_VERBOSE("Clearing binding context '%s'") << context->name(); - context->clearAllBindings(); - } + LOG_INPUT_VERBOSE("Clearing binding context '%s'") << bc.name(); + bc.clearAllBindings(); + return LoopContinue; + }); // We can restart the id counter, all the old bindings were destroyed. bindingIdCounter = 0; @@ -617,23 +632,24 @@ D_CMD(ActivateBindingContext) { DENG2_UNUSED2(src, argc); - bool doActivate = !stricmp(argv[0], "activatebcontext"); - BindContext *bc = B_ContextByName(argv[1]); + bool const doActivate = !stricmp(argv[0], "activatebcontext"); + String const context = argv[1]; - if(!bc) + if(!B_HasContext(context)) { - LOG_INPUT_WARNING("Binding context '%s' does not exist") << argv[1]; + LOG_INPUT_WARNING("Binding context '%s' does not exist") << context; return false; } - if(bc->isProtected()) + BindContext &bc = B_Context(context); + if(bc.isProtected()) { LOG_INPUT_ERROR("Binding context '%s' is protected and cannot be manually %s") - << bc->name() << (doActivate? "activated" : "deactivated"); + << bc.name() << (doActivate? "activated" : "deactivated"); return false; } - bc->activate(doActivate); + bc.activate(doActivate); return true; } diff --git a/doomsday/client/src/ui/infine/infinesystem.cpp b/doomsday/client/src/ui/infine/infinesystem.cpp index fcbbfd5f0e..b140b41e66 100644 --- a/doomsday/client/src/ui/infine/infinesystem.cpp +++ b/doomsday/client/src/ui/infine/infinesystem.cpp @@ -146,8 +146,9 @@ void InFineSystem::initBindingContext() // static if(inited) return; inited = true; - B_SetContextFallbackForDDEvents("finale", de::function_cast(gx.FinaleResponder)); - B_ContextByName("finale")->activate(); // always on + BindContext &context = B_Context("finale"); + context.setDDFallbackResponder(de::function_cast(gx.FinaleResponder)); + context.activate(); // always on } void InFineSystem::deinitBindingContext() // static @@ -155,8 +156,9 @@ void InFineSystem::deinitBindingContext() // static // Not yet initialized? if(!inited) return; - B_SetContextFallbackForDDEvents("finale", nullptr); - B_ContextByName("finale")->deactivate(); + BindContext &context = B_Context("finale"); + context.setDDFallbackResponder(nullptr); + context.deactivate(); inited = false; } #endif // __CLIENT__ diff --git a/doomsday/client/src/ui/widgetactions.cpp b/doomsday/client/src/ui/widgetactions.cpp index 82d3b7d506..cd99524192 100644 --- a/doomsday/client/src/ui/widgetactions.cpp +++ b/doomsday/client/src/ui/widgetactions.cpp @@ -43,6 +43,7 @@ bool WidgetActions::tryEvent(Event const &event, String const &context) { ddevent_t ddev; InputSystem::convertEvent(event, &ddev); + if(context.isEmpty()) { // Check all enabled contexts. @@ -50,9 +51,9 @@ bool WidgetActions::tryEvent(Event const &event, String const &context) } // Check a specific binding context for an action (regardless of its activation status). - if(BindContext *bc = B_ContextByName(context)) + if(B_HasContext(context)) { - AutoRef act(bc->actionForEvent(&ddev, false)); + AutoRef act(B_Context(context).actionForEvent(&ddev, false)); if(act.get()) { act->trigger(); @@ -83,8 +84,8 @@ void WidgetActions::trackInput(Event const &event) void WidgetActions::activateContext(String const &context, bool yes) { - if(BindContext *bc = B_ContextByName(context)) + if(B_HasContext(context)) { - bc->activate(yes); + B_Context(context).activate(yes); } } diff --git a/doomsday/client/src/ui/widgets/inputbindingwidget.cpp b/doomsday/client/src/ui/widgets/inputbindingwidget.cpp index f94db17bc6..e107d98d01 100644 --- a/doomsday/client/src/ui/widgets/inputbindingwidget.cpp +++ b/doomsday/client/src/ui/widgets/inputbindingwidget.cpp @@ -94,14 +94,13 @@ DENG_GUI_PIMPL(InputBindingWidget) // Check all the contexts associated with this widget. foreach(QString bcName, contexts) { - BindContext const *bc = B_ContextByName(bcName); - if(!bc) continue; + if(!B_HasContext(bcName)) continue; - if(evbinding_t const *com = bc->findCommandBinding(command.toLatin1(), device)) + if(evbinding_t const *cb = B_Context(bcName).findCommandBinding(command.toLatin1(), device)) { // This'll do. AutoStr *str = AutoStr_New(); - B_EventBindingToString(com, str); + B_EventBindingToString(cb, str); text = prettyKey(Str_Text(str)); break; }