Hope and change

So, as I suggested in my prior blog post, I’ve been working on totally redesigning the Common::Action and Common::Keymap structures.

I’ve been at work on this for a few days now, but because I’m totally redesigning the way they interact with each other, and interface with the rest of the code at large, I’m not done yet, and the code won’t compile until I finish, so I can’t commit it yet.

So, to show what I’ve been working at, I’m posting what I’ve gotten done here.

So far, all the actual rewritten code has been going into the Action struct and related enums.

enum ActionType {

//Emulate a hardware event in the engine
kSingleKeyPressActionType,
kMultiKeyPressType,
kMouseClickType,
kMouseWheelType,

//Interface directly with the VM
kSaveActionType,
kMenuActionType,
kQuitActionType,
kVirtualKeyboardActionType,
kKeyRemapActionType,
kVolumeUpActionType,
kVolumeDownActionType,

//This is the total number of types currently defined in this enum
kActionTypeMax

};

enum ClickType {

kLeftClickType,
kMiddleClickType,
kRightClickType

};

enum KeyType {

//Direction keys
kDirUpKeyType,
kDirDownKeyType,
kDirLeftKeyType,
kDirRightKeyType,

//Keyboard keys
kTextKeyType, //Letters, numbers, symbols, whitespace
kModifierKeyType, //Ctrl, Alt, Shift

//Gamepad buttons
kFaceButtonKeyType, //A, B, C, X, Y, Z, and the like
kShoulderButtonKeyType, //Separated from FaceButtons because they can be used as pseudo-modifier keys.

//System key types
kPauseKeyType, //Start, Pause, etc..
kMenuKeyType, //Select, Escape, etc…
kSystemActionKeyType, //F1-F12, volume sliders, and so forth

//This is the total number of types currently defined in this enum
kKeyTypeMax

};

struct Action {

char id[ACTION_ID_SIZE];

ActionType type;
KeyType preferredKey;

private:

List<Event> _events;
HardwareKey *_mappedKey;

public:

void mapKey (const HardwareKey *key);

void addKeyPressEvent(KeyCode code, byte modifiers) {

KeyState key = KeyState(code);
key.flags = modifiers;

Event keyPress;
keyPress.type = EVENT_KEYDOWN;
keyPress.kbd = key;

_events.push_back(keyPress);

}

void addMouseClickEvent(ClickType t) {

Event mouseClick;
if (t == kLeftClickType)

mouseClick.type = EVENT_LBUTTONDOWN;

else if (t == kRightClickType)

mouseClick.type = EVENT_RBUTTONDOWN;

else

mouseClick.type = EVENT_MBUTTONDOWN;

_events.push_back(mouseClick);

}

template <ActonType t>;
Action<kSingleKeyPressActionType> (char *i, KeyType k, KeyCode c, byte m) {

memcpy(id,i,ACTION_ID_SIZE);

type = t;
preferredKey = k;

addKeyPressEvent(c,m);

}

Action<kMultiKeyPressType> (char *i, KeyType k, List<KeyCode> cs, byte m) {

memcpy(id,i,ACTION_ID_SIZE);

type = t;
preferredKey = k;

List<KeyCode>::iterator it;
for (it = cs.begin(); it != cs.end(); it++) {

KeyCode c = *it;
addKeyPressEvent(c,m);

}

}

Action<kMouseClickType> (char *i, bool l = true, bool m = false, bool r = false) {

memcpy(id,i,ACTION_ID_SIZE);

type = t;
preferredKey = k;

if (l)

addMouseClickEvent(kLeftClickType);

if (m)

addMouseClickEvent(kMiddleClickType);

if (r)

addMouseClickEvent(kRightClickType);

}

Action<kMouseWheelType> (char *i, bool up = false) {

memcpy(id,i,ACTION_ID_SIZE);

Event evt;
if (up)

evt.type = EVENT_WHEELUP;

else

evt.type = EVENT_WHEELDOWN;

_events.push_back(evt);

}

Action (char *i) {

memcpy(id,i,ACTION_ID_SIZE);
type = t;

}

};

There’s more about how this is going to work which is still in my head, but a lot of initialization stuff that was previously done by Action methods is going to be moved into the Keymap, so there will no longer be any need for an Action to know anything about the set that it is in, except to have a HardwareKey pointer provided to it (and even that will only be so that the Keymap can do effective data-management when an action gets remapped.)

Radical changes required?

I’m at a sticking point in designing the keymapper API, or planning/progressing on the similar tasks, which stems from the complexity of the existing code.

I wonder if it might be faster, easier, and better overall to redesign some parts of the keymapper from scratch, so I can focus on building something clean and simple, instead of trying to figure out what parts of the existing structure can be cut out without compromising the function of its design.

Up until this point, I’ve been working under the assumption that the prior student’s design is fundamentally sound, and it is only the details of implementation that needed reworking, but if that is not the case, then the approach I’ve been taking will require more time and effort to be successful than a complete redesign of the component would.

I’m going to consider, over the course of the next day, how I would do this if I had to implement it from scratch, and see if I can find a simpler method than the one that was used.

Over the wire and through the woods.

I’ve just been informed by LordHoto that the 1900UTC August 17th line is not, in fact, a deadline for students, but the end of their obligation to perform, and I can continue up until the 24th, to submit code which may be evaluated.

So, I’ll be continuing to work on the API in the hopes of doing just that.

Down to the wire

As I am writing this, the GSoC Coding period ends in just under 19 hours. Shortly after my last blog post, I began to make some headway on the task, and I’ve kept up a reasonable pace since then, and I’ve now got modifiers working in a way that I’m satisfied with, so I’m finally starting on the real meat of the task — the engine<->keymapper interface. Unfortunately, I haven’t managed to take a whole lot of time yet to sit and think about the details of how this is going to work, so I’m going to be flying by the seat of my pants a bit.

I hope to have a more fully-fleshed proposal for an API, and some work on an initial implementation done, before the 19:00 UTC deadline.

Wish me good luck, I will need it.

EDIT:

With 6 and a half hours remaining in the GSoC coding period, and badly in need of sleep, it looks like I won’t be able to get the API proposal finished before the pencils down deadline.

In fact, what I’ve managed to get done towards it is embarassingly small, but I will post it here for posterity, anyway:

How this is going to work:

  • simple method to create simple common actions such as mouse clicks and key presses
    • Autogenerate keypress and mouseclick action IDs and descriptions
      • What is the bare minimum required for an Action structure to be functional?
      • What aspects of an Action can be defaulted or autogenerated in the majority of cases?
        • preferredkey
        • events
        • id?
        • description?
        • Priority??
          • How does priority work? (is 0 the highest priority, or the lowest?)
          • What is the point of having a “parent-child” keymap hierarchy?
  • model Keymapper interface after CursorManager
    • implement a stack for keymaps
      • implement pushKeyMap
        • What input should it take?
          • cursor manager takes the components for a cursor, and handles cursor assembly internally
          • But maybe if there’s a streamlined method for keymap production, the engine could handle that and push the completed map
      • replaceKeyMap
      • popKeyMap
      • Surely there are other distinguishing features to the CursorManager

slow going

As anyone who’s been following the commit logs can tell, I’m having a lot of difficulty keeping up a good pace of work on the keymapper. The reason for this is that, unlike with my prior project, nearly all the progress I make on this task comes at the expense of code that someone else wrote before, and because I did not get a lot of time to look over it before I started working (which is mostly my own fault), I don’t really understand how it all works together well enough to be certain that the code I’m replacing it with will be better.

As a result, I’m being very hesitant about any changes I make because I always want to be certain I understand what the existing code is doing (and how) before I destroy it to make room for my own.

However, I am still working on the keymapper improvements, and I have no intent of giving up before I’ve finished them, no matter how long I have to keep working on this past the end of this year’s GSoC.

Keymapper API proposal for engine to define actions

Note: I’m staying up late to get this proposal written before the deadline my mentor has given, so my words in this post might not make a lot of sense.

It seems to me that the problem with the currently implemented approach is that the Action structure is too complicated to require engine authors to create and initialize a full structure for every single action that they wish to be mapped to a key. Additionally, it requires engine authors to give each individual action a universally unique 4 character ID, so that actions and their keymappings can be saved to the config without any other game that ScummVM can run ever overwriting it with a different key. However, some of the more complicated features of the Action structure may be necessary for some games to fully function in a reduced-key environment.

Because of these factors, it seems to me that there are two steps which should be taken to make the keymapper work better for engine authors:

  1. The Common::Action structure needs to be closely examined, and unnecessary components removed.
  2. One or more constructors should be provided to allow for easy mass production of more common types of Actions, especially ones that simply need to map one key press to some other button.

I fear that this API proposal is grossly insufficient, because the task of properly implementing modifiers in the keymapper has been much more interesting and less straightforward than I expected, and has left me with very little time before to properly examine the other aspects of the keymapper component before now.

Gaining momentum

After a long break from coding (longer than what I’d wanted by half), I’ve started working on properly implementing modifiers for the keymapper, instead of feeding every key in an additional time for each possible combination of modifiers that exists.

I’ve got it halfway working in a temporary way — it only feeds the keys in once, but the remap dialog can recognize modified keys and tell that they are different from unmodified keys (and that ctrl-a is different from shift-a), but events mapped to modified keys aren’t triggering at all for some reason.

The more important thing, though, is that I’m back in the swing of coding and getting work done again. Hopefully I should be back up to full speed in another day or two.

Back in the saddle

Alright, I’m finally back from vacation, and well rested, and shaking off my cold, so it’s time to get back to work on this.

From this point, I’m actually going to be working on the keymapper, so I’m making this blog post to organize my thoughts into a plan of action.

  1. Become familiar with the current working of the keymapper.
  2. Create a standardized framework for backends to describe keys and define keymaps.
  3. Make a stack for keymaps to be pushed onto and popped from as needed.
  4. Make use of the framework to allow the keymapper to automatically generate keymaps.
  5. Figure out some way to make the keymaps easily userconfigurable.

This is only the most general of outlines at the moment, I expect to add subtasks, and rearrange the task list as I become more familiar with the keymapper as it currently exists.

Note: I wrote this post on July 30, but somehow wordpress never published it, thankfully it was saved to drafts, at least.