Skip to content

Commit 49e6c83

Browse files
committed
✨ glue together keybindings
1 parent 13db165 commit 49e6c83

8 files changed

Lines changed: 275 additions & 30 deletions

File tree

Console/Program.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
using EliteAPI;
22
using EliteAPI.Utils;
3+
using Newtonsoft.Json;
34

45
Log.AddListener(log =>
56
{
6-
if (log.Level < Log.LogLevel.Info)
7-
return;
7+
88

99
ConsoleColor color = log.Level switch
1010
{
11-
Log.LogLevel.Debug => ConsoleColor.Gray,
11+
Log.LogLevel.Debug => ConsoleColor.DarkGray,
1212
Log.LogLevel.Info => ConsoleColor.Blue,
1313
Log.LogLevel.Warning => ConsoleColor.Yellow,
14-
Log.LogLevel.Error => ConsoleColor.Red
14+
Log.LogLevel.Error => ConsoleColor.Red,
15+
_ => ConsoleColor.White
1516
};
1617

1718
var previousColor = Console.ForegroundColor;
@@ -22,14 +23,15 @@
2223

2324
var api = new EliteDangerousApi();
2425

25-
api.OnAll(e =>
26+
api.OnKeybindingsChanged(bindings =>
2627
{
27-
Console.WriteLine($"Event: {e.Event}");
28-
});
28+
foreach (var binding in bindings)
29+
{
30+
if (binding.Name != "LandingGearToggle")
31+
continue;
2932

30-
api.OnJournalChanged(e =>
31-
{
32-
Console.WriteLine($"New journal file being watched: {e.FullName}");
33+
Log.Info(JsonConvert.SerializeObject(binding));
34+
}
3335
});
3436

3537
api.Start();

EliteAPI/Bindings/BindingsUtils.cs

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,177 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.IO;
34

45
public class BindingsUtils
56
{
7+
private static readonly Dictionary<string, string> KeyCodeMap = new(StringComparer.OrdinalIgnoreCase)
8+
{
9+
{ "Key_Backspace", "8" },
10+
{ "Key_Tab", "9" },
11+
{ "Key_Enter", "13" },
12+
{ "Key_Pause", "19" },
13+
{ "Key_CapsLock", "20" },
14+
{ "Key_Kana", "21" },
15+
{ "Key_Kanji", "25" },
16+
{ "Key_Escape", "27" },
17+
{ "Key_Convert", "28" },
18+
{ "Key_NoConvert", "29" },
19+
{ "Key_Space", "32" },
20+
{ "Key_PageUp", "33" },
21+
{ "Key_PageDown", "34" },
22+
{ "Key_End", "35" },
23+
{ "Key_Home", "36" },
24+
{ "Key_LeftArrow", "37" },
25+
{ "Key_UpArrow", "38" },
26+
{ "Key_RightArrow", "39" },
27+
{ "Key_DownArrow", "40" },
28+
{ "Key_SYSRQ", "44" },
29+
{ "Key_Insert", "45" },
30+
{ "Key_Delete", "46" },
31+
{ "Key_0", "48" },
32+
{ "Key_1", "49" },
33+
{ "Key_2", "50" },
34+
{ "Key_3", "51" },
35+
{ "Key_4", "52" },
36+
{ "Key_5", "53" },
37+
{ "Key_6", "54" },
38+
{ "Key_7", "55" },
39+
{ "Key_8", "56" },
40+
{ "Key_9", "57" },
41+
{ "Key_A", "65" },
42+
{ "Key_B", "66" },
43+
{ "Key_C", "67" },
44+
{ "Key_D", "68" },
45+
{ "Key_E", "69" },
46+
{ "Key_F", "70" },
47+
{ "Key_G", "71" },
48+
{ "Key_H", "72" },
49+
{ "Key_I", "73" },
50+
{ "Key_J", "74" },
51+
{ "Key_K", "75" },
52+
{ "Key_L", "76" },
53+
{ "Key_M", "77" },
54+
{ "Key_N", "78" },
55+
{ "Key_O", "79" },
56+
{ "Key_P", "80" },
57+
{ "Key_Q", "81" },
58+
{ "Key_R", "82" },
59+
{ "Key_S", "83" },
60+
{ "Key_T", "84" },
61+
{ "Key_U", "85" },
62+
{ "Key_V", "86" },
63+
{ "Key_W", "87" },
64+
{ "Key_X", "88" },
65+
{ "Key_Y", "89" },
66+
{ "Key_Z", "90" },
67+
{ "Key_LeftWin", "91" },
68+
{ "Key_RightWin", "92" },
69+
{ "Key_Apps", "93" },
70+
{ "Key_Sleep", "95" },
71+
{ "Key_Numpad_0", "96" },
72+
{ "Key_Numpad_1", "97" },
73+
{ "Key_Numpad_2", "98" },
74+
{ "Key_Numpad_3", "99" },
75+
{ "Key_Numpad_4", "100" },
76+
{ "Key_Numpad_5", "101" },
77+
{ "Key_Numpad_6", "102" },
78+
{ "Key_Numpad_7", "103" },
79+
{ "Key_Numpad_8", "104" },
80+
{ "Key_Numpad_9", "105" },
81+
{ "Key_Numpad_Multiply", "106" },
82+
{ "Key_Numpad_Add", "107" },
83+
{ "Key_Numpad_Subtract", "109" },
84+
{ "Key_Numpad_Decimal", "110" },
85+
{ "Key_Numpad_Comma", "110" },
86+
{ "Key_Numpad_Divide", "111" },
87+
{ "Key_Numpad_Enter", "156" },
88+
{ "Key_F1", "112" },
89+
{ "Key_F2", "113" },
90+
{ "Key_F3", "114" },
91+
{ "Key_F4", "115" },
92+
{ "Key_F5", "116" },
93+
{ "Key_F6", "117" },
94+
{ "Key_F7", "118" },
95+
{ "Key_F8", "119" },
96+
{ "Key_F9", "120" },
97+
{ "Key_F10", "121" },
98+
{ "Key_F11", "122" },
99+
{ "Key_F12", "123" },
100+
{ "Key_F13", "124" },
101+
{ "Key_F14", "125" },
102+
{ "Key_F15", "126" },
103+
{ "Key_F16", "127" },
104+
{ "Key_F17", "128" },
105+
{ "Key_F18", "129" },
106+
{ "Key_F19", "130" },
107+
{ "Key_F20", "131" },
108+
{ "Key_F21", "132" },
109+
{ "Key_F22", "133" },
110+
{ "Key_F23", "134" },
111+
{ "Key_F24", "135" },
112+
{ "Key_NumLock", "144" },
113+
{ "Key_ScrollLock", "145" },
114+
{ "Key_LeftShift", "160" },
115+
{ "Key_RightShift", "161" },
116+
{ "Key_LeftControl", "162" },
117+
{ "Key_RightControl", "163" },
118+
{ "Key_LeftAlt", "164" },
119+
{ "Key_RightAlt", "165" },
120+
{ "Key_WebBack", "166" },
121+
{ "Key_WebForward", "167" },
122+
{ "Key_WebRefresh", "168" },
123+
{ "Key_WebStop", "169" },
124+
{ "Key_WebSearch", "170" },
125+
{ "Key_WebFavourites", "171" },
126+
{ "Key_WebHome", "172" },
127+
{ "Key_Mute", "173" },
128+
{ "Key_VolumeDown", "174" },
129+
{ "Key_VolumeUp", "175" },
130+
{ "Key_NextTrack", "176" },
131+
{ "Key_PrevTrack", "177" },
132+
{ "Key_MediaStop", "178" },
133+
{ "Key_Stop", "178" },
134+
{ "Key_PlayPause", "179" },
135+
{ "Key_Mail", "180" },
136+
{ "Key_MediaSelect", "181" },
137+
{ "Key_SemiColon", "186" },
138+
{ "Key_Plus", "187" },
139+
{ "Key_Equals", "187" },
140+
{ "Key_Comma", "188" },
141+
{ "Key_Minus", "189" },
142+
{ "Key_Period", "190" },
143+
{ "Key_Slash", "191" },
144+
{ "Key_Grave", "192" },
145+
{ "Key_LeftBracket", "219" },
146+
{ "Key_BackSlash", "220" },
147+
{ "Key_RightBracket", "221" },
148+
{ "Key_Apostrophe", "222" },
149+
{ "Key_OEM_102", "226" },
150+
{ "Key_ä", "222" },
151+
{ "Key_ö", "192" },
152+
{ "Key_ü", "186" },
153+
{ "Key_ß", "219" },
154+
{ "Key_Acute", "221" },
155+
{ "Key_LessThan", "226" },
156+
{ "Key_Circumflex", "220" },
157+
{ "Key_Hash", "191" },
158+
{ "Key_Colon", "186" },
159+
{ "Key_ABNT_C1", "-1" },
160+
{ "Key_Yen", "-1" },
161+
{ "Key_ABNT_C2", "-1" },
162+
{ "Key_Numpad_Equals", "-1" },
163+
{ "Key_Underline", "-1" },
164+
{ "Key_AX", "-1" },
165+
{ "Key_Unlabeled", "-1" },
166+
{ "Key_Calculator", "-1" },
167+
{ "Key_AT", "-1" },
168+
{ "Key_Power", "-1" },
169+
{ "Key_Wake", "-1" },
170+
{ "Key_MyComputer", "-1" },
171+
{ "Key_GreenModifier", "-1" },
172+
{ "Key_OrangeModifier", "-1" }
173+
};
174+
6175
public static DirectoryInfo GetBindingsDirectory()
7176
{
8177
return new DirectoryInfo(Path.Combine(GetOptionsDirectory().FullName, "Bindings"));
@@ -13,4 +182,14 @@ private static DirectoryInfo GetOptionsDirectory()
13182
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
14183
return new DirectoryInfo(Path.Combine(localAppData, "Frontier Developments", "Elite Dangerous", "Options"));
15184
}
185+
186+
internal static string GetKeyCode(string key)
187+
{
188+
if (key == null)
189+
return "-1";
190+
191+
var keyCode = KeyCodeMap.TryGetValue(key, out var code) ? code : "-1";
192+
193+
return $"[{keyCode}]";
194+
}
16195
}

EliteAPI/EliteDangerousApi.cs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Linq;
5+
using EliteAPI.Bindings;
56
using EliteAPI.Events;
67
using EliteAPI.Journals;
78
using EliteAPI.Json;
@@ -15,6 +16,7 @@ public class EliteDangerousApi
1516
{
1617
private readonly FileWatcher _journalWatcher;
1718
private readonly List<FileWatcher> _statusWatchers;
19+
private readonly FileWatcher _bindingsPresetsWatcher;
1820
private readonly StatusTracker _statusTracker = new();
1921

2022
private readonly List<Action<FileInfo>> _journalChangedHandlers = [];
@@ -27,6 +29,7 @@ public class EliteDangerousApi
2729
.Where(t => typeof(IEvent).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract)
2830
.ToDictionary(t => t.Name.EndsWith("Event") ? t.Name.Substring(0, t.Name.Length - 5) : t.Name, t => t, StringComparer.OrdinalIgnoreCase);
2931
private readonly Dictionary<string, List<Action<(string eventName, string json)>>> _untypedEventHandlers = new(StringComparer.OrdinalIgnoreCase);
32+
private readonly List<Action<IReadOnlyCollection<Control>>> _bindingsHandlers = [];
3033

3134
public EliteDangerousApi()
3235
{
@@ -46,6 +49,8 @@ public EliteDangerousApi()
4649
.ToList();
4750

4851
_journalWatcher = FileWatcher.Create(JournalUtils.GetJournalsDirectory(), "Journal.*.log", FileWatchMode.LineByLine);
52+
53+
_bindingsPresetsWatcher = FileWatcher.Create(BindingsUtils.GetBindingsDirectory(), "StartPreset*", FileWatchMode.EntireFile);
4954
}
5055

5156
public void Start()
@@ -55,16 +60,17 @@ public void Start()
5560
JournalUtils.PrepareLocalisations(json);
5661
Invoke(json);
5762
});
58-
5963
_journalWatcher.OnFileChanged(file =>
6064
{
6165
foreach (var handler in _journalChangedHandlers)
6266
SafeInvoke.Invoke("handling journal switch", handler, file);
6367
});
6468
_statusWatchers.ForEach(w => w.OnContentChanged(Invoke));
69+
_bindingsPresetsWatcher.OnContentChanged(HandleBindingsPreset);
6570

6671
_statusWatchers.ForEach(w => w.StartWatching());
6772
_journalWatcher.StartWatching();
73+
HandleBindingsPreset(_bindingsPresetsWatcher.StartWatching());
6874

6975
}
7076

@@ -76,6 +82,14 @@ public void OnJournalChanged(Action<FileInfo> handler)
7682
_journalChangedHandlers.Add(handler);
7783
}
7884

85+
/// <summary>
86+
/// Listens for when keybindings have changed
87+
/// </summary>
88+
public void OnKeybindingsChanged(Action<IReadOnlyCollection<Control>> handler)
89+
{
90+
_bindingsHandlers.Add(handler);
91+
}
92+
7993
/// <summary>
8094
/// Listens for a specific event of <typeparamref name="TEvent"/>.
8195
/// </summary>
@@ -223,4 +237,33 @@ internal void Invoke(string json, IEvent? @event)
223237
_statusTracker.UpdateState(paths);
224238
}
225239
}
240+
241+
private void HandleBindingsPreset(string presetContent)
242+
{
243+
var presets = presetContent.Split('\n').Where(line => !string.IsNullOrWhiteSpace(line)).ToList();
244+
245+
// Make sure all presets use the same format
246+
if (presets.Count == 0 || presets.Distinct().Count() > 1)
247+
{
248+
Log.Warning("Multiple different keybindings preset formats detected, skipping keybindings setup");
249+
return;
250+
}
251+
252+
var preset = presets[0];
253+
254+
var file = BindingsUtils.GetBindingsDirectory().GetFiles().FirstOrDefault(f => f.Name.StartsWith(preset));
255+
256+
if (file == null)
257+
{
258+
Log.Warning($"Keybindings preset '{preset}' could not be found, skipping keybindings setup");
259+
return;
260+
}
261+
262+
var bindings = BindingParser.Parse(File.ReadAllText(file.FullName));
263+
264+
Log.Info($"Loaded {bindings.Count} keybindings from preset '{preset}'");
265+
266+
foreach (var handler in _bindingsHandlers)
267+
SafeInvoke.Invoke("handling keybindings change", handler, bindings);
268+
}
226269
}

EliteAPI/Utils/Logger.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,6 @@ public static void Write(LogLevel level, string message)
122122
var timestamp = DateTime.Now;
123123
var logEntry = $"[{timestamp:yyyy-MM-dd HH:mm:ss.fff}] [{level}] {message}";
124124

125-
// Write to console for immediate feedback
126-
Console.WriteLine(logEntry);
127-
128125
// Write to file in a thread-safe manner
129126
lock (_fileLock)
130127
{

EliteAPI/bindings/Binding.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1+
using System.Linq;
2+
13
namespace EliteAPI.Bindings;
24

3-
public readonly struct Binding : IBinding
5+
public readonly struct Binding
46
{
57
public string Device { get; init; }
68

79
public string Key { get; init; }
810

9-
public IBinding[]? Modifiers { get; init; }
11+
public string KeyCode => $"{string.Join("", Modifiers.Select(m => m.KeyCode))}{BindingsUtils.GetKeyCode(Key)}";
12+
13+
public Binding[] Modifiers { get; init; }
1014

11-
public Binding(string device, string key, IBinding[]? modifiers = null)
15+
public Binding(string device, string key, Binding[]? modifiers = null)
1216
{
1317
Device = device;
1418
Key = key;
15-
Modifiers = modifiers;
16-
19+
Modifiers = modifiers ?? [];
1720
}
1821
}

EliteAPI/bindings/BindingParser.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ private static Control ParseElement(XElement element)
7171
{
7272
var mDevice = GetAttribute(m, "Device") ?? string.Empty;
7373
var mKey = GetAttribute(m, "Key") ?? string.Empty;
74-
return (IBinding)new Binding(mDevice, mKey);
74+
return new Binding(mDevice, mKey);
7575
})
7676
.ToArray();
7777

EliteAPI/bindings/IBinding.cs

Lines changed: 0 additions & 9 deletions
This file was deleted.

0 commit comments

Comments
 (0)