Skip to content

Commit

Permalink
InputSystem|Bindings|Client: BindContext (de)activation is now observ…
Browse files Browse the repository at this point in the history
…able
  • Loading branch information
danij-deng committed Nov 4, 2014
1 parent d552ba6 commit 7cc0f59
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 42 deletions.
37 changes: 21 additions & 16 deletions doomsday/client/include/ui/bindcontext.h
Expand Up @@ -22,10 +22,16 @@

#include <functional>
#include <de/Action>
#include <de/Observers>
#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;
Expand All @@ -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);
Expand All @@ -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.
Expand All @@ -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()
*/
Expand Down Expand Up @@ -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;
Expand All @@ -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.
Expand Down
6 changes: 3 additions & 3 deletions doomsday/client/src/ui/b_main.cpp
Expand Up @@ -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();
Expand Down
35 changes: 25 additions & 10 deletions doomsday/client/src/ui/bindcontext.cpp
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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();
}
Expand All @@ -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)
Expand Down Expand Up @@ -268,15 +283,15 @@ 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])
{
for(cbinding_t *i = d->commandBinds.next; i != &d->commandBinds; i = i->next)
{
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;
}
Expand Down
29 changes: 16 additions & 13 deletions doomsday/client/src/ui/inputsystem.cpp
Expand Up @@ -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())
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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");
Expand Down

0 comments on commit 7cc0f59

Please sign in to comment.