diff --git a/doomsday/client/include/ui/bindcontext.h b/doomsday/client/include/ui/bindcontext.h index 409654653e..4f66fcde9d 100644 --- a/doomsday/client/include/ui/bindcontext.h +++ b/doomsday/client/include/ui/bindcontext.h @@ -22,10 +22,16 @@ #include #include +#include #include "dd_share.h" #include "b_command.h" #include "b_device.h" +/// @todo: Move to public API +typedef int (*FallbackResponderFunc)(event_t *); +typedef int (*DDFallbackResponderFunc)(ddevent_t const *); +// todo ends + struct controlbinding_t { controlbinding_t *next; @@ -36,10 +42,6 @@ struct controlbinding_t dbinding_t deviceBinds[DDMAXPLAYERS]; ///< Separate bindings for each local player. }; -/// @todo: Move to public API -typedef int (*FallbackResponderFunc)(event_t *); -typedef int (*DDFallbackResponderFunc)(ddevent_t const *); - void B_DestroyControlBinding(controlbinding_t *conBin); void B_InitControlBindingList(controlbinding_t *listRoot); @@ -55,6 +57,10 @@ void B_DestroyControlBindingList(controlbinding_t *listRoot); */ class BindContext { +public: + /// Notified when the active state of the context changes. + DENG2_DEFINE_AUDIENCE2(ActiveChange, void bindContextActiveChanged(BindContext &context)) + public: /** * @param name Symbolic name for the context. @@ -63,16 +69,16 @@ class BindContext ~BindContext(); /** - * Returns @c true if the context is @em active, meaning, bindings in the context are - * in effect and their associated action(s) will be executed if triggered. + * Returns @c true if the context is @em active, meaning, bindings in the context + * are in effect and their associated action(s) will be executed if triggered. * * @see activate(), deactivate() */ bool isActive() const; /** - * Returns @c true if the context is @em protected, meaning, it should not be manually - * (de)activated directly, by the end user. + * Returns @c true if the context is @em protected, meaning, it should not be + * manually (de)activated by the end user, directly. * * @see protect() */ @@ -101,11 +107,11 @@ class BindContext void activate(bool yes = true); inline void deactivate(bool yes = true) { activate(!yes); } - void acquireAll(bool yex = true); - void acquireKeyboard(bool yes = true); + void acquire(int deviceId, bool yes = true); + void acquireAll(bool yes = true); + bool willAcquire(int deviceId) const; bool willAcquireAll() const; - bool willAcquireKeyboard() const; void printAllBindings() const; void writeToFile(FILE *file) const; @@ -122,22 +128,21 @@ class BindContext dbinding_t *findDeviceBinding(int device, cbdevtype_t bindType, int id); /** - * Looks through context @a bc and looks for a binding that matches either - * @a match1 or @a match2. + * Looks through the context for a binding that matches either @a match1 or @a match2. */ bool findMatchingBinding(cbinding_t *match1, dbinding_t *match2, cbinding_t **evResult, dbinding_t **dResult); - void deleteMatching(cbinding_t *eventBinding, dbinding_t *deviceBinding); + void deleteMatching(cbinding_t *commandBind, dbinding_t *controlBind); // --- cbinding_t *bindCommand(char const *eventDesc, char const *command); /** - * @param device Use @c < 0 || >= NUM_INPUT_DEVICES for wildcard search. + * @param deviceId Use @c < 0 || >= NUM_INPUT_DEVICES for wildcard search. */ - cbinding_t *findCommandBinding(char const *command, int device) const; + cbinding_t *findCommandBinding(char const *command, int deviceId = -1) const; /** * Iterate through all the evbinding_ts of the context. diff --git a/doomsday/client/src/ui/b_main.cpp b/doomsday/client/src/ui/b_main.cpp index 87db8c1ad8..07d88bbfeb 100644 --- a/doomsday/client/src/ui/b_main.cpp +++ b/doomsday/client/src/ui/b_main.cpp @@ -88,13 +88,13 @@ void B_Init() isys.newContext("menu")->acquireAll(); isys.newContext("gameui"); isys.newContext("shortcut"); - isys.newContext("chat")->acquireKeyboard(); + isys.newContext("chat")->acquire(IDEV_KEYBOARD); isys.newContext("message")->acquireAll(); // Binding context for the console. BindContext *bc = isys.newContext(CONSOLE_BINDING_CONTEXT_NAME); - bc->protect(); // Only we can (de)activate. - bc->acquireKeyboard(); // Console takes over all keyboard events. + bc->protect(); // Only we can (de)activate. + bc->acquire(IDEV_KEYBOARD); // Console takes over all keyboard events. // UI doesn't let anything past it. isys.newContext(UI_BINDING_CONTEXT_NAME)->acquireAll(); diff --git a/doomsday/client/src/ui/bindcontext.cpp b/doomsday/client/src/ui/bindcontext.cpp index 363480cc67..f4d58929b8 100644 --- a/doomsday/client/src/ui/bindcontext.cpp +++ b/doomsday/client/src/ui/bindcontext.cpp @@ -120,8 +120,12 @@ DENG2_PIMPL(BindContext) return eb; } + + DENG2_PIMPL_AUDIENCE(ActiveChange) }; +DENG2_AUDIENCE_METHOD(BindContext, ActiveChange) + BindContext::BindContext(String const &name) : d(new Instance(this)) { setName(name); @@ -159,6 +163,8 @@ void BindContext::setName(String const &newName) void BindContext::activate(bool yes) { + bool const oldActive = d->active; + LOG_INPUT_VERBOSE("%s binding context '%s'") << (yes? "Activating" : "Deactivating") << d->name; @@ -175,12 +181,20 @@ void BindContext::activate(bool yes) return LoopContinue; }); } + + if(oldActive != d->active) + { + // Notify interested parties. + DENG2_FOR_AUDIENCE2(ActiveChange, i) i->bindContextActiveChanged(*this); + } } -void BindContext::acquireKeyboard(bool yes) +void BindContext::acquire(int deviceId, bool yes) { - if(yes) d->acquireDevices.insert(IDEV_KEYBOARD); - else d->acquireDevices.remove(IDEV_KEYBOARD); + DENG2_ASSERT(deviceId >= 0 && deviceId < NUM_INPUT_DEVICES); + + if(yes) d->acquireDevices.insert(deviceId); + else d->acquireDevices.remove(deviceId); inputSys().updateAllDeviceStateAssociations(); } @@ -192,15 +206,16 @@ void BindContext::acquireAll(bool yes) inputSys().updateAllDeviceStateAssociations(); } -bool BindContext::willAcquireAll() const +bool BindContext::willAcquire(int deviceId) const { - return d->acquireAllDevices; + /// @todo: What about acquireAllDevices? (Ambiguous naming/usage). + return d->acquireDevices.contains(deviceId); } -bool BindContext::willAcquireKeyboard() const + +bool BindContext::willAcquireAll() const { - /// @todo: What about acquireAllDevices? (Ambiguous naming/usage). - return d->acquireDevices.contains(IDEV_KEYBOARD); + return d->acquireAllDevices; } void BindContext::setDDFallbackResponder(DDFallbackResponderFunc newResponderFunc) @@ -268,7 +283,7 @@ cbinding_t *BindContext::bindCommand(char const *eventDesc, char const *command) return b; } -cbinding_t *BindContext::findCommandBinding(char const *command, int device) const +cbinding_t *BindContext::findCommandBinding(char const *command, int deviceId) const { if(command && command[0]) { @@ -276,7 +291,7 @@ cbinding_t *BindContext::findCommandBinding(char const *command, int device) con { if(qstricmp(i->command, command)) continue; - if((device < 0 || device >= NUM_INPUT_DEVICES) || i->device == device) + if((deviceId < 0 || deviceId >= NUM_INPUT_DEVICES) || i->device == deviceId) { return i; } diff --git a/doomsday/client/src/ui/inputsystem.cpp b/doomsday/client/src/ui/inputsystem.cpp index a14374efd2..84293144e7 100644 --- a/doomsday/client/src/ui/inputsystem.cpp +++ b/doomsday/client/src/ui/inputsystem.cpp @@ -1272,7 +1272,7 @@ void InputSystem::updateAllDeviceStateAssociations() }); // If the context have made a broad device acquisition, mark all relevant states. - if(bc->willAcquireKeyboard()) + if(bc->willAcquire(IDEV_KEYBOARD)) { InputDevice &keyboard = device(IDEV_KEYBOARD); if(keyboard.isActive()) @@ -1366,12 +1366,14 @@ cbinding_t *InputSystem::bindCommand(char const *eventDesc, char const *command) bool InputSystem::unbindCommand(char const *command) { DENG2_ASSERT(command); + LOG_AS("InputSystem"); + bool didDelete = false; for(BindContext *bc : d->contexts) { - while(cbinding_t *ev = bc->findCommandBinding(command, NUM_INPUT_DEVICES)) + while(cbinding_t *bind = bc->findCommandBinding(command)) { - didDelete |= bc->deleteBinding(ev->bid); + didDelete |= bc->deleteBinding(bind->bid); } }; return didDelete; @@ -1418,36 +1420,36 @@ dbinding_t *InputSystem::bindControl(char const *controlDesc, char const *device LOG_INPUT_VERBOSE("Control '%s' in context '%s' of local player %i to be bound to '%s'") << control->name << bc->name() << localNum << deviceDesc; - controlbinding_t *conBin = bc->findControlBinding(control->id); - bool justCreated = false; - if(!conBin) + controlbinding_t *cbin = bc->findControlBinding(control->id); + bool justCreated = false; + if(!cbin) { - conBin = bc->getControlBinding(control->id); + cbin = bc->getControlBinding(control->id); justCreated = true; } - dbinding_t *devBin = B_NewDeviceBinding(&conBin->deviceBinds[localNum], deviceDesc); - if(!devBin) + dbinding_t *bind = B_NewDeviceBinding(&cbin->deviceBinds[localNum], deviceDesc); + if(!bind) { // Failure in the parsing. if(justCreated) { - B_DestroyControlBinding(conBin); + B_DestroyControlBinding(cbin); } - conBin = nullptr; return nullptr; } /// @todo: In interactive binding mode, should ask the user if the /// replacement is ok. For now, just delete the other binding. - bc->deleteMatching(nullptr, devBin); + bc->deleteMatching(nullptr, bind); updateAllDeviceStateAssociations(); - return devBin; + return bind; } bool InputSystem::removeBinding(int id) { + LOG_AS("InputSystem"); for(BindContext *bc : d->contexts) { if(bool result = bc->deleteBinding(id)) return result; @@ -1458,6 +1460,7 @@ bool InputSystem::removeBinding(int id) void InputSystem::writeAllBindingsTo(FILE *file) { DENG2_ASSERT(file); + LOG_AS("InputSystem"); // Start with a clean slate when restoring the bindings. fprintf(file, "clearbindings\n\n");