OoliteProject / oolite

The main Oolite repository.
https://www.oolite.space
544 stars 70 forks source link

New keyconfig #397

Closed phkb closed 2 years ago

phkb commented 2 years ago

This PR adds the ability to set keyboard configuration within the game itself, rather than relying only on an external plist file. In this PR, the old file plist file, keyconfig.plist, is obsolete. A new version of the file, keyconfig2.plist, is instead in play, along with mapping files for Windows, Linux and Mac machines.

The keyconfig2.plist file follows this format:

{
    "default" = { // name of the definition
        key_roll_left = ({ // name of the key control (in this case 'Roll left'
            key = "arrowLeft"; // name of the key to use for this function
        });
        ...
    };
    ...
}

The key definitions are an array of dictionary items, allowing for multiple keys to define a single function. Each key can also have modifier flags set, being "shift", "mod1" (ie Control) and "mod2" (Alt on PC/Linux, Command on Mac).

Each definition must align with an equivalent set of codes in the "keymappings_windows.plist"/"keymappings_linux.plist"/"keymappings_mac.plist" file. These files map the keyboard scancodes to a unicode value, based on the keyboard layout. Note: scan codes vary between WIndows, Linux and Mac systems.

The keymappings_*.plist files follows this format:

{
    "default" = { // name of the definition, which must match a definition in the keyconfig2.plist file
        description = "Default (US) keyboard";  // description of this layout
        mapping_normal = { // scancode to unicode mapping for unshifted keys
            "41" = 96; // ` key
            "2" = 49; // 1 key
            "3" = 50; // 2 key
            ...
        };
        mapping_shifted = { // scancode to unicode mapping for shifted keys
            "41" = 126; // ~ key
            "2" = 33; // ! key
            "3" = 64; // @ key
            ...
        };

    };
    ...
}

Multiple definitions have been set up in the supplied mapping files, covering US English (default), UK English, German, Italian, Norwegian, French, Danish, Dutch, Greek, Spanish, Swedish, Portuguese and Brazilian. More definitions can be added via OXP.

The keyboard configuration screens in game can be found on the "Game Options" screen, below the "Joystick Configuration" item: image The keyboard layout can be selected at the top of the screen. image Selecting a key function will take the user to a second screen, where primary and secondary key definitions can be set. image

All changes are stored in the .GNUStepDefaults file to be loaded back in when the game is restarted.

AnotherCommander commented 2 years ago

Run a few tests with revision 6c1d9c2 of this PR and I have a few observations:

  1. Shift-restart does not clear the cache anymore. This is not the fault of the PR, it is apparently a side effect of the SDL.dll that fixed the Alt key state issue. I have a quick fix though, will commit to master soon.
  2. Advanced Nav Array keys don't seem to work at all.
  3. In the F6 screen, holding Ctrl while moving the cross will reset its position , rather than speed it up.
  4. (Cosmetic) In the keys setup menu, I have a strong dislike for the word "Astrogation". I would prefer "Navigation" for uniformity reasons, since this is the word used everywhere in Readmes, the reference sheet and in-game.

Edited to add:

  1. The key group headers (like Astrogation Navigation above ) should be entries in descriptions.plist rather than hard-coded.
phkb commented 2 years ago

Also changed "Astrogation" to "Navigation"

AnotherCommander commented 2 years ago

One little thing I noticed: When in flight, normally you can use Ctrl to decrease the rate of roll/pitch/yaw, resulting in more accurate movements when needed. To do this, you typically press Ctrl before any of the r/p/y keys and then the key you want to utilize for movement. In the new_keyconfig branch this does not work in exactly the same way. If I press Ctrl first, then r/p/y keys are blocked. If instead I press r/p/y for a split second and then I press Ctrl, it works, but it also means that for the first millliseconds of movement I am not in increased accuracy mode. Can this be modified to reflect the original behaviour exactly?

AnotherCommander commented 2 years ago

Just realized the the yaw key seems to work fine with Ctrl. It's the pitch and roll ones that demonstrate this behaviour.

AnotherCommander commented 2 years ago

Found one more little inconsistency: F6 screen, start moving the cursor and press Ctrl. It works, the cursor moves faster. Now, while holding Ctrl and whatever key you are using to move the cursor, press whatever key is needed to get the cross to move diagonally. It won't work; the cursor continues to move as if only Ctrl+the original key were held. Doing the same sequence in trunk will work as expected i.e. the cursor will start moving in one direction and then start moving diagonally. Note that in new_keyconfig, if you start by moving diagonally and then press Ctrl it works and the cursor moves diagonally faster.

Edit: Noticed this also when flying. Pressing yaw and Ctrl to start moving slowly and then holding p/r down will not move the ship diagonally. It looks like Ctrl can be used with only one other key simultaneously.

phkb commented 2 years ago

It looks like I'm not being accurate enough with distinguishing between normal arrow keys and number pad arrow keys. Got a bit of a change to make the distinction, and then check on all OS's. Back soon.

AnotherCommander commented 2 years ago

I think I've resolved all of the issues I reported with this patch. Please have a look just in case I've messed up something really badly - hadn't looked at the code of this branch for ages.

diff --git a/deps/Windows-deps b/deps/Windows-deps
index a40abccf..36fd5e6d 160000
--- a/deps/Windows-deps
+++ b/deps/Windows-deps
@@ -1 +1 @@
-Subproject commit a40abccfb0480650fb513ea79d5e81972e9c086e
+Subproject commit 36fd5e6d0f4823e320dd60aa0d23e2baeab78f99-dirty
diff --git a/src/SDL/MyOpenGLView.m b/src/SDL/MyOpenGLView.m
index 2811ae02..95908f5c 100644
--- a/src/SDL/MyOpenGLView.m
+++ b/src/SDL/MyOpenGLView.m
@@ -1697,6 +1697,7 @@ static NSString * kOOLogKeyDown               = @"input.keyMapping.keyPress.keyDown";
    NSTimeInterval          timeNow = [NSDate timeIntervalSinceReferenceDate];
    Uint16                  key_id;
    int                     scan_code;
+   static BOOL             modifier_pressed = NO;

    while (SDL_PollEvent(&event))
    {
@@ -1867,7 +1868,6 @@ static NSString * kOOLogKeyDown               = @"input.keyMapping.keyPress.keyDown";

                //char *keychar = SDL_GetKeyName(kbd_event->keysym.sym);
                // deal with modifiers first
-               BOOL modifier_pressed = NO;
                BOOL special_key = NO;

                // translate scancode to unicode equiv
@@ -1955,22 +1955,22 @@ static NSString * kOOLogKeyDown             = @"input.keyMapping.keyPress.keyDown";
                    {
                        key_id = 0; // reset the value here to force a lookup from the keymappings data
                    }
-   
-                   // if we get here and we still don't have a key id, grab the unicode value from our keymappings dict
-                   if (key_id == 0) 
+               }
+               
+               // if we get here and we still don't have a key id, grab the unicode value from our keymappings dict
+               if (key_id == 0) 
+               {
+                   // get unicode value for keycode from keymappings files
+                   // this handles all the non-functional keys. the function keys are handled in the switch below
+                   if (!shift)
                    {
-                       // get unicode value for keycode from keymappings files
-                       // this handles all the non-functional keys. the function keys are handled in the switch below
-                       if (!shift)
-                       {
-                           NSString *keyNormal = [keyMappings_normal objectForKey:[NSString stringWithFormat:@"%d", scan_code]];
-                           if (keyNormal) key_id = [keyNormal integerValue];
-                       }
-                       else
-                       {
-                           NSString *keyShifted = [keyMappings_shifted objectForKey:[NSString stringWithFormat:@"%d", scan_code]];
-                           if (keyShifted) key_id = [keyShifted integerValue];
-                       }
+                       NSString *keyNormal = [keyMappings_normal objectForKey:[NSString stringWithFormat:@"%d", scan_code]];
+                       if (keyNormal) key_id = [keyNormal integerValue];
+                   }
+                   else
+                   {
+                       NSString *keyShifted = [keyMappings_shifted objectForKey:[NSString stringWithFormat:@"%d", scan_code]];
+                       if (keyShifted) key_id = [keyShifted integerValue];
                    }
                }
                // if we've got the unicode value, we can store it in our array now
@@ -2008,16 +2008,19 @@ static NSString * kOOLogKeyDown             = @"input.keyMapping.keyPress.keyDown";
                    case SDLK_LSHIFT:
                    case SDLK_RSHIFT:
                        shift = NO;
+                       modifier_pressed = NO;
                        break;

                    case SDLK_LCTRL:
                    case SDLK_RCTRL:
                        ctrl = NO;
+                       modifier_pressed = NO;
                        break;

                    case SDLK_LALT:
                    case SDLK_RALT:
                        opt = NO;
+                       modifier_pressed = NO;
                        break;
                    default:
                        ;
phkb commented 2 years ago

I've incorporated some of your diff (modifier_pressed is only used in the keydown event to skip any secondary lookups), and also refined the separation of number pad keys. So it should be possible to use all the number pad keys independently of any normal key equivalence (eg the +, -, / * and . keys). Hopefully this rectifies all the issues with holding Ctrl down with the pitch, roll and yaw keys, and on the F6 screen as well.

AnotherCommander commented 2 years ago

Seems good here. In the meantime, I've picked up a couple more inconveniences, but I think they should be somewhat simpler fixes:

  1. While in flight, pause game, F2 and Keyboard Configuration. Edit any key and attempt to assign numpad2 to it. You will find that certain undesired things happen on the way: the hud vanishes and upon hitting 2 you get transferred immediately back to options. Effectively, you are unable to assing that particular key. This appears to be because game input is still being received as you type. The game should not be receiving gameplay input while typing key assignments.
  2. . Cosmetic and up to you if you want to change this or not. In the Select Keyboard Layout screen the word "keyboard" is unnecessarily repeated on every single row. Just the country and code page info where applicable should be fine and the page would appear much less cluttered IMO.
phkb commented 2 years ago

I think I found all the instances where inflight controls were getting confused. And agree: too many "keyboards" on that screen.

phkb commented 2 years ago

If nothing further has turned up, I'll merge this tonight.