Skip to content

Commit

Permalink
Fixed bug 2849414: Shoots after loading/saving from menu.
Browse files Browse the repository at this point in the history
There already was a mechanism that kept track which binding context
each individual input device (a particular key/axis/hat) was
associated with. However, when this association changes, the old
mechanism did not react in any way. Now I've added a logic wherein
the state of a particular device key/axis/hat is flagged as "expired"
when the context association changes while the device is not in its
default state.

In practice, we consider the state of a device unknown when it changes
its binding context until we can be sure that it has really returned
to its default state.
  • Loading branch information
skyjake committed Apr 5, 2010
1 parent 2fffd26 commit b1cf387
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 41 deletions.
18 changes: 15 additions & 3 deletions doomsday/engine/portable/include/dd_input.h
Expand Up @@ -101,6 +101,18 @@ typedef struct ddevent_s {
#define IS_MOUSE_UP(evp) (evp->device == IDEV_MOUSE && IS_TOGGLE_UP(evp))
#define IS_MOUSE_MOTION(evp) (evp->device == IDEV_MOUSE && evp->type == E_AXIS)

// Binding association. How the device axis/key/etc. relates to binding contexts.
typedef struct inputdevassoc_s {
struct bcontext_s* bContext;
struct bcontext_s* prevBContext;
int flags;
} inputdevassoc_t;

// Association flags.
#define IDAF_EXPIRED 0x1 // The state has expired. The device is considered to remain
// in default state until the flag gets cleared (which happens when
// the real device state returns to its default).

// Input device axis types.
enum
{
Expand All @@ -123,20 +135,20 @@ typedef struct inputdevaxis_s {
int filter; // Filter grade.
float accumulation; // Position accumulator for the filter.
uint time; // Timestamp for the latest update that changed the position.
struct bcontext_s* bContext;
inputdevassoc_t assoc; // Binding association.
} inputdevaxis_t;

typedef struct inputdevkey_s {
char isDown; // True/False for each key.
uint time;
struct bcontext_s* bContext;
inputdevassoc_t assoc; // Binding association.
const char* name; // Symbolic name.
} inputdevkey_t;

typedef struct inputdevhat_s {
int pos; // Position of each hat, -1 if centered.
uint time; // Timestamp for each hat for the latest change.
struct bcontext_s* bContext;
inputdevassoc_t assoc; // Binding association.
} inputdevhat_t;

// Input device flags.
Expand Down
6 changes: 3 additions & 3 deletions doomsday/engine/portable/src/b_command.c
Expand Up @@ -424,7 +424,7 @@ boolean B_TryCommandBinding(evbinding_t* eb, ddevent_t* event, struct bcontext_s
case E_TOGGLE:
if(eb->id != event->toggle.id)
return false;
if(eventClass && dev->keys[eb->id].bContext != eventClass)
if(eventClass && dev->keys[eb->id].assoc.bContext != eventClass)
return false; // Shadowed by a more important active class.

// Is the state as required?
Expand Down Expand Up @@ -462,7 +462,7 @@ boolean B_TryCommandBinding(evbinding_t* eb, ddevent_t* event, struct bcontext_s
case E_AXIS:
if(eb->id != event->axis.id)
return false;
if(eventClass && dev->axes[eb->id].bContext != eventClass)
if(eventClass && dev->axes[eb->id].assoc.bContext != eventClass)
return false; // Shadowed by a more important active class.

// Is the position as required?
Expand All @@ -475,7 +475,7 @@ boolean B_TryCommandBinding(evbinding_t* eb, ddevent_t* event, struct bcontext_s
case E_ANGLE:
if(eb->id != event->angle.id)
return false;
if(eventClass && dev->hats[eb->id].bContext != eventClass)
if(eventClass && dev->hats[eb->id].assoc.bContext != eventClass)
return false; // Shadowed by a more important active class.
// Is the position as required?
if(event->angle.pos != eb->pos)
Expand Down
86 changes: 63 additions & 23 deletions doomsday/engine/portable/src/b_context.c
Expand Up @@ -94,7 +94,7 @@ void B_DestroyAllContexts(void)
*/
void B_UpdateDeviceStateAssociations(void)
{
int i;
int i, j;
uint k;
bcontext_t* bc;
evbinding_t* eb;
Expand All @@ -119,18 +119,18 @@ void B_UpdateDeviceStateAssociations(void)
switch(eb->type)
{
case E_TOGGLE:
if(!dev->keys[eb->id].bContext)
dev->keys[eb->id].bContext = bc;
if(!dev->keys[eb->id].assoc.bContext)
dev->keys[eb->id].assoc.bContext = bc;
break;

case E_AXIS:
if(!dev->axes[eb->id].bContext)
dev->axes[eb->id].bContext = bc;
if(!dev->axes[eb->id].assoc.bContext)
dev->axes[eb->id].assoc.bContext = bc;
break;

case E_ANGLE:
if(!dev->hats[eb->id].bContext)
dev->hats[eb->id].bContext = bc;
if(!dev->hats[eb->id].assoc.bContext)
dev->hats[eb->id].assoc.bContext = bc;
break;

case E_SYMBOLIC:
Expand All @@ -153,23 +153,23 @@ void B_UpdateDeviceStateAssociations(void)
for(db = conBin->deviceBinds[k].next; db != &conBin->deviceBinds[k];
db = db->next)
{
inputdev_t* dev = I_GetDevice(db->device, false);
inputdev_t* dev = I_GetDevice(db->device, false);

switch(db->type)
{
case CBD_TOGGLE:
if(!dev->keys[db->id].bContext)
dev->keys[db->id].bContext = bc;
if(!dev->keys[db->id].assoc.bContext)
dev->keys[db->id].assoc.bContext = bc;
break;

case CBD_AXIS:
if(!dev->axes[db->id].bContext)
dev->axes[db->id].bContext = bc;
if(!dev->axes[db->id].assoc.bContext)
dev->axes[db->id].assoc.bContext = bc;
break;

case CBD_ANGLE:
if(!dev->hats[db->id].bContext)
dev->hats[db->id].bContext = bc;
if(!dev->hats[db->id].assoc.bContext)
dev->hats[db->id].assoc.bContext = bc;
break;

default:
Expand All @@ -189,8 +189,8 @@ void B_UpdateDeviceStateAssociations(void)

for(k = 0; k < dev->numKeys; ++k)
{
if(!dev->keys[k].bContext)
dev->keys[k].bContext = bc;
if(!dev->keys[k].assoc.bContext)
dev->keys[k].assoc.bContext = bc;
}
}

Expand All @@ -199,29 +199,69 @@ void B_UpdateDeviceStateAssociations(void)
int j;
for(j = 0; j < NUM_INPUT_DEVICES; ++j)
{
inputdev_t* dev = I_GetDevice(j, true);
inputdev_t* dev = I_GetDevice(j, true);

if(!dev)
continue;

for(k = 0; k < dev->numKeys; ++k)
{
if(!dev->keys[k].bContext)
dev->keys[k].bContext = bc;
if(!dev->keys[k].assoc.bContext)
dev->keys[k].assoc.bContext = bc;
}
for(k = 0; k < dev->numAxes; ++k)
{
if(!dev->axes[k].bContext)
dev->axes[k].bContext = bc;
if(!dev->axes[k].assoc.bContext)
dev->axes[k].assoc.bContext = bc;
}
for(k = 0; k < dev->numHats; ++k)
{
if(!dev->hats[k].bContext)
dev->hats[k].bContext = bc;
if(!dev->hats[k].assoc.bContext)
dev->hats[k].assoc.bContext = bc;
}
}
}
}

// 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.
for(i = 0; i < NUM_INPUT_DEVICES; ++i)
{
inputdev_t* dev = I_GetDevice(i, false);

// Keys.
for(j = 0; j < dev->numKeys; ++j)
{
if(dev->keys[j].assoc.bContext != dev->keys[j].assoc.prevBContext &&
dev->keys[j].isDown)
{
// No longer valid.
dev->keys[j].assoc.flags |= IDAF_EXPIRED;
}
}

// Axes.
for(j = 0; j < dev->numAxes; ++j)
{
if(dev->axes[j].assoc.bContext != dev->axes[j].assoc.prevBContext &&
dev->axes[j].position != 0)
{
// No longer valid.
dev->axes[j].assoc.flags |= IDAF_EXPIRED;
}
}

// Hats.
for(j = 0; j < dev->numHats; ++j)
{
if(dev->hats[j].assoc.bContext != dev->hats[j].assoc.prevBContext &&
dev->hats[j].pos >= 0)
{
// No longer valid.
dev->hats[j].assoc.flags |= IDAF_EXPIRED;
}
}
}
}

static void B_SetContextCount(int count)
Expand Down
19 changes: 15 additions & 4 deletions doomsday/engine/portable/src/b_device.c
Expand Up @@ -321,18 +321,22 @@ void B_EvaluateDeviceBindingList(int localNum, dbinding_t* listRoot, float* pos,
switch(cb->type)
{
case CBD_TOGGLE:
if(controlClass && dev->keys[cb->id].bContext != controlClass)
if(controlClass && dev->keys[cb->id].assoc.bContext != controlClass)
continue; // Shadowed by a more important active class.

// Expired?
if(dev->keys[cb->id].assoc.flags & IDAF_EXPIRED)
break;

devicePos = (dev->keys[cb->id].isDown? 1.0f : 0.0f);
deviceTime = dev->keys[cb->id].time;
break;

case CBD_AXIS:
axis = &dev->axes[cb->id];
if(controlClass && axis->bContext != controlClass)
if(controlClass && axis->assoc.bContext != controlClass)
{
if(!B_FindDeviceBinding(axis->bContext, cb->device, CBD_AXIS, cb->id))
if(!B_FindDeviceBinding(axis->assoc.bContext, cb->device, CBD_AXIS, cb->id))
{
// The overriding context doesn't bind to the axis, though.
if(axis->type == IDAT_POINTER)
Expand All @@ -344,6 +348,10 @@ void B_EvaluateDeviceBindingList(int localNum, dbinding_t* listRoot, float* pos,
continue; // Shadowed by a more important active class.
}

// Expired?
if(axis->assoc.flags & IDAF_EXPIRED)
break;

if(axis->type == IDAT_POINTER)
{
deviceOffset = axis->position;
Expand All @@ -357,9 +365,12 @@ void B_EvaluateDeviceBindingList(int localNum, dbinding_t* listRoot, float* pos,
break;

case CBD_ANGLE:
if(controlClass && dev->hats[cb->id].bContext != controlClass)
if(controlClass && dev->hats[cb->id].assoc.bContext != controlClass)
continue; // Shadowed by a more important active class.

if(dev->hats[cb->id].assoc.flags & IDAF_EXPIRED)
break;

devicePos = (dev->hats[cb->id].pos == cb->angle? 1.0f : 0.0f);
deviceTime = dev->hats[cb->id].time;
break;
Expand Down
48 changes: 40 additions & 8 deletions doomsday/engine/portable/src/dd_input.c
Expand Up @@ -536,6 +536,12 @@ static void I_UpdateAxis(inputdev_t *dev, uint axis, float pos, timespan_t ticLe
else // Cumulative.
a->position += pos; //a->realPosition;

// We can clear the expiration when it returns to default state.
if(!a->position)
{
a->assoc.flags &= ~IDAF_EXPIRED;
}

/* if(verbose > 3)
{
Con_Message("I_UpdateAxis: device=%s axis=%i pos=%f\n",
Expand Down Expand Up @@ -579,19 +585,36 @@ void I_TrackInput(ddevent_t *ev, timespan_t ticLength)
}
else if(ev->type == E_TOGGLE)
{
dev->keys[ev->toggle.id].isDown =
(ev->toggle.state == ETOG_DOWN || ev->toggle.state == ETOG_REPEAT);
inputdevkey_t* key = &dev->keys[ev->toggle.id];

key->isDown = (ev->toggle.state == ETOG_DOWN || ev->toggle.state == ETOG_REPEAT);

// Mark down the time when the change occurs.
if(ev->toggle.state == ETOG_DOWN || ev->toggle.state == ETOG_UP)
dev->keys[ev->toggle.id].time = Sys_GetRealTime();
{
key->time = Sys_GetRealTime();
}

// We can clear the expiration when the key is released.
if(!key->isDown)
{
key->assoc.flags &= ~IDAF_EXPIRED;
}
}
else if(ev->type == E_ANGLE)
{
dev->hats[ev->angle.id].pos = ev->angle.pos;
inputdevhat_t* hat = &dev->hats[ev->angle.id];

hat->pos = ev->angle.pos;

// Mark down the time when the change occurs.
dev->hats[ev->angle.id].time = Sys_GetRealTime();
hat->time = Sys_GetRealTime();

// We can clear the expiration when the hat is centered.
if(hat->pos < 0)
{
hat->assoc.flags &= ~IDAF_EXPIRED;
}
}
}

Expand All @@ -606,13 +629,22 @@ void I_ClearDeviceContextAssociations(void)

// Keys.
for(j = 0; j < dev->numKeys; ++j)
dev->keys[j].bContext = NULL;
{
dev->keys[j].assoc.prevBContext = dev->keys[j].assoc.bContext;
dev->keys[j].assoc.bContext = NULL;
}
// Axes.
for(j = 0; j < dev->numAxes; ++j)
dev->axes[j].bContext = NULL;
{
dev->axes[j].assoc.prevBContext = dev->axes[j].assoc.bContext;
dev->axes[j].assoc.bContext = NULL;
}
// Hats.
for(j = 0; j < dev->numHats; ++j)
dev->hats[j].bContext = NULL;
{
dev->hats[j].assoc.prevBContext = dev->hats[j].assoc.bContext;
dev->hats[j].assoc.bContext = NULL;
}
}
}

Expand Down

0 comments on commit b1cf387

Please sign in to comment.