commonlibsse_ng\skse/
input_map.rs

1// C++ Original code
2// - ref: https://github.com/SARDONYX-forks/CommonLibVR/blob/ng/include/SKSE/InputMap.h
3// - ref: https://github.com/SARDONYX-forks/CommonLibVR/blob/ng/src/SKSE/InputMap.cpp
4// SPDX-FileCopyrightText: (C) 2018 Ryan-rsm-McKenzie
5// SPDX-License-Identifier: MIT
6//
7// SPDX-FileCopyrightText: (C) 2025 SARDONYX
8// SPDX-License-Identifier: Apache-2.0 OR MIT
9
10use std::borrow::Cow;
11
12use windows::Win32::Devices::HumanInterfaceDevice::{
13    DIK_DELETE, DIK_DIVIDE, DIK_DOWNARROW, DIK_END, DIK_HOME, DIK_INSERT, DIK_LEFTARROW,
14    DIK_NUMPADENTER, DIK_PGDN, DIK_PGUP, DIK_RALT, DIK_RCONTROL, DIK_RIGHTARROW, DIK_UPARROW,
15};
16use windows::Win32::UI::Input::KeyboardAndMouse::GetKeyNameTextW;
17use windows::Win32::UI::Input::XboxController::{
18    XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_BUTTON_FLAGS,
19    XINPUT_GAMEPAD_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_LEFT, XINPUT_GAMEPAD_DPAD_RIGHT,
20    XINPUT_GAMEPAD_DPAD_UP, XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_LEFT_THUMB,
21    XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_RIGHT_THUMB, XINPUT_GAMEPAD_START,
22    XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y,
23};
24use windows::core::HSTRING;
25
26pub const MACRO_KEYBOARD_OFFSET: u32 = 0; // Not actually used, just for self-documentation
27pub const MACRO_NUM_KEYBOARD_KEYS: u32 = 256;
28
29pub const MACRO_MOUSE_BUTTON_OFFSET: u32 = MACRO_NUM_KEYBOARD_KEYS; // 256
30pub const MACRO_NUM_MOUSE_BUTTONS: u32 = 8;
31
32pub const MACRO_MOUSE_WHEEL_OFFSET: u32 = MACRO_MOUSE_BUTTON_OFFSET + MACRO_NUM_MOUSE_BUTTONS; // 264
33pub const MACRO_MOUSE_WHEEL_DIRECTIONS: u32 = 2;
34
35pub const MACRO_GAMEPAD_OFFSET: u32 = MACRO_MOUSE_WHEEL_OFFSET + MACRO_MOUSE_WHEEL_DIRECTIONS; // 266
36pub const MACRO_NUM_GAMEPAD_BUTTONS: u32 = 16;
37
38pub const MAX_MACROS: u32 = MACRO_GAMEPAD_OFFSET + MACRO_NUM_GAMEPAD_BUTTONS; // 282
39
40#[repr(u32)]
41#[derive(Debug, Copy, Clone, PartialEq, Eq)]
42pub enum GamepadButtonOffset {
43    DpadUp = MACRO_GAMEPAD_OFFSET,
44    DpadDown,
45    DpadLeft,
46    DpadRight,
47    Start,
48    Back,
49    LeftThumb,
50    RightThumb,
51    LeftShoulder,
52    RightShoulder,
53    A,
54    B,
55    X,
56    Y,
57    Lt,
58    Rt, // 281
59}
60const _: () = assert!(GamepadButtonOffset::Rt as u32 == 281);
61
62impl GamepadButtonOffset {
63    const fn from_xinput(key_mask: XINPUT_GAMEPAD_BUTTON_FLAGS) -> Option<Self> {
64        // if (RE::ControlMap::GetSingleton()->GetGamePadType() == RE::PC_GAMEPAD_TYPE::kOrbis) {
65        // 	keyMask = ScePadOffsetToXInput(keyMask);
66        // }
67        Some(match key_mask {
68            XINPUT_GAMEPAD_DPAD_UP => Self::DpadUp,
69            XINPUT_GAMEPAD_DPAD_DOWN => Self::DpadDown,
70            XINPUT_GAMEPAD_DPAD_LEFT => Self::DpadLeft,
71            XINPUT_GAMEPAD_DPAD_RIGHT => Self::DpadRight,
72            XINPUT_GAMEPAD_START => Self::Start,
73            XINPUT_GAMEPAD_BACK => Self::Back,
74            XINPUT_GAMEPAD_LEFT_THUMB => Self::LeftThumb,
75            XINPUT_GAMEPAD_RIGHT_THUMB => Self::RightThumb,
76            XINPUT_GAMEPAD_LEFT_SHOULDER => Self::LeftShoulder,
77            XINPUT_GAMEPAD_RIGHT_SHOULDER => Self::RightShoulder,
78            XINPUT_GAMEPAD_A => Self::A,
79            XINPUT_GAMEPAD_B => Self::B,
80            XINPUT_GAMEPAD_X => Self::X,
81            XINPUT_GAMEPAD_Y => Self::Y,
82
83            other => {
84                match other.0 {
85                    0x9 => Self::Lt,  // Left Trigger game-defined ID
86                    0xA => Self::Rt,  // Right Trigger game-defined ID
87                    _ => return None, // Invalid return MAX_MACROS
88                }
89            }
90        })
91    }
92
93    pub const fn to_xinput(&self) -> XINPUT_GAMEPAD_BUTTON_FLAGS {
94        match *self {
95            Self::DpadUp => XINPUT_GAMEPAD_DPAD_UP,
96            Self::DpadDown => XINPUT_GAMEPAD_DPAD_DOWN,
97            Self::DpadLeft => XINPUT_GAMEPAD_DPAD_LEFT,
98            Self::DpadRight => XINPUT_GAMEPAD_DPAD_RIGHT,
99            Self::Start => XINPUT_GAMEPAD_START,
100            Self::Back => XINPUT_GAMEPAD_BACK,
101            Self::LeftThumb => XINPUT_GAMEPAD_LEFT_THUMB,
102            Self::RightThumb => XINPUT_GAMEPAD_RIGHT_THUMB,
103            Self::LeftShoulder => XINPUT_GAMEPAD_LEFT_SHOULDER,
104            Self::RightShoulder => XINPUT_GAMEPAD_RIGHT_SHOULDER,
105            Self::A => XINPUT_GAMEPAD_A,
106            Self::B => XINPUT_GAMEPAD_B,
107            Self::X => XINPUT_GAMEPAD_X,
108            Self::Y => XINPUT_GAMEPAD_Y,
109            Self::Lt => XINPUT_GAMEPAD_BUTTON_FLAGS(0x9), // Custom mapping for LT
110            Self::Rt => XINPUT_GAMEPAD_BUTTON_FLAGS(0xA), // Custom mapping for RT
111        }
112
113        // if (RE::ControlMap::GetSingleton()->GetGamePadType() == RE::PC_GAMEPAD_TYPE::kOrbis) {
114        // 	keyMask = XInputToScePadOffset(keyMask);
115        // }
116        // return keyMask;
117    }
118
119    pub const fn to_str(&self) -> &'static str {
120        match self {
121            Self::DpadUp => "Gamepad DPad Up",
122            Self::DpadDown => "Gamepad DPad Down",
123            Self::DpadLeft => "Gamepad DPad Left",
124            Self::DpadRight => "Gamepad DPad Right",
125            Self::Start => "Gamepad Start",
126            Self::Back => "Gamepad Back",
127            Self::LeftThumb => "Gamepad Left Thumb",
128            Self::RightThumb => "Gamepad Right Thumb",
129            Self::LeftShoulder => "Gamepad Left Shoulder",
130            Self::RightShoulder => "Gamepad Right Shoulder",
131            Self::A => "Gamepad A",
132            Self::B => "Gamepad B",
133            Self::X => "Gamepad X",
134            Self::Y => "Gamepad Y",
135            Self::Lt => "Gamepad LT",
136            Self::Rt => "Gamepad RT",
137        }
138    }
139}
140
141/// PS4
142///
143/// Constants for SCE Pad buttons
144#[repr(u32)]
145pub enum ScePadButton {
146    Share = 0x00000001,
147    L3 = 0x00000002,
148    R3 = 0x00000004,
149    Options = 0x00000008,
150    Up = 0x00000010,
151    Right = 0x00000020,
152    Down = 0x00000040,
153    Left = 0x00000080,
154    L2 = 0x00000100,
155    R2 = 0x00000200,
156    L1 = 0x00000400,
157    R1 = 0x00000800,
158    Triangle = 0x00001000,
159    Circle = 0x00002000,
160    Cross = 0x00004000,
161    Square = 0x00008000,
162    Playstation = 0x00010000,
163    TouchPad = 0x00100000,
164    Intercepted = 0x80000000,
165}
166
167impl ScePadButton {
168    pub const fn from_xinput(key_mask: XINPUT_GAMEPAD_BUTTON_FLAGS) -> Option<Self> {
169        Some(match key_mask {
170            XINPUT_GAMEPAD_DPAD_UP => Self::Up,
171            XINPUT_GAMEPAD_DPAD_DOWN => Self::Down,
172            XINPUT_GAMEPAD_DPAD_LEFT => Self::Left,
173            XINPUT_GAMEPAD_DPAD_RIGHT => Self::Right,
174            XINPUT_GAMEPAD_START => Self::Options,
175            XINPUT_GAMEPAD_BACK => Self::TouchPad,
176            XINPUT_GAMEPAD_LEFT_THUMB => Self::L3,
177            XINPUT_GAMEPAD_RIGHT_THUMB => Self::R3,
178            XINPUT_GAMEPAD_LEFT_SHOULDER => Self::L1,
179            XINPUT_GAMEPAD_RIGHT_SHOULDER => Self::R1,
180            XINPUT_GAMEPAD_A => Self::Cross,
181            XINPUT_GAMEPAD_B => Self::Circle,
182            XINPUT_GAMEPAD_X => Self::Square,
183            XINPUT_GAMEPAD_Y => Self::Triangle,
184            _ => return None,
185        })
186    }
187
188    pub fn to_xinput(&self) -> Option<XINPUT_GAMEPAD_BUTTON_FLAGS> {
189        Some(match *self {
190            Self::Up => XINPUT_GAMEPAD_DPAD_UP,
191            Self::Down => XINPUT_GAMEPAD_DPAD_DOWN,
192            Self::Left => XINPUT_GAMEPAD_DPAD_LEFT,
193            Self::Right => XINPUT_GAMEPAD_DPAD_RIGHT,
194            Self::Options => XINPUT_GAMEPAD_START,
195            Self::TouchPad => XINPUT_GAMEPAD_BACK,
196            Self::L3 => XINPUT_GAMEPAD_LEFT_THUMB,
197            Self::R3 => XINPUT_GAMEPAD_RIGHT_THUMB,
198            Self::L1 => XINPUT_GAMEPAD_LEFT_SHOULDER,
199            Self::R1 => XINPUT_GAMEPAD_RIGHT_SHOULDER,
200            Self::Share => todo!(),
201            Self::L2 => todo!(),
202            Self::R2 => todo!(),
203            Self::Triangle => XINPUT_GAMEPAD_Y,
204            Self::Circle => XINPUT_GAMEPAD_B,
205            Self::Cross => XINPUT_GAMEPAD_A,
206            Self::Square => XINPUT_GAMEPAD_X,
207            Self::Playstation | Self::Intercepted => return None,
208        })
209    }
210}
211
212pub fn get_key_name(key_code: u32) -> Cow<'static, str> {
213    if (MACRO_MOUSE_BUTTON_OFFSET..MACRO_GAMEPAD_OFFSET).contains(&key_code) {
214        get_mouse_button_name(key_code).unwrap_or("Unknown").into()
215    } else if (MACRO_GAMEPAD_OFFSET..MAX_MACROS).contains(&key_code) {
216        GamepadButtonOffset::from_xinput(XINPUT_GAMEPAD_BUTTON_FLAGS(key_code as u16))
217            .map_or("Unknown", |input| input.to_str())
218            .into()
219    } else {
220        get_keyboard_key_name(key_code).into()
221    }
222}
223
224fn get_keyboard_key_name(key_code: u32) -> String {
225    let mut scancode = key_code & 0xFF;
226
227    scancode = match scancode {
228        DIK_NUMPADENTER => 0x11C,
229        DIK_RCONTROL => 0x11D,
230        DIK_DIVIDE => 0x135,
231        DIK_RALT => 0x138,
232        DIK_HOME => 0x147,
233        DIK_UPARROW => 0x148,
234        DIK_PGUP => 0x149,
235        DIK_LEFTARROW => 0x14B,
236        DIK_RIGHTARROW => 0x14D,
237        DIK_END => 0x14F,
238        DIK_DOWNARROW => 0x150,
239        DIK_PGDN => 0x151,
240        DIK_INSERT => 0x152,
241        DIK_DELETE => 0x153,
242        _ => scancode,
243    };
244
245    let mut l_param = (scancode << 16) as i32;
246    if scancode == 0x45 {
247        l_param |= 1 << 24;
248    }
249
250    let mut buffer = [0_u16; 256];
251    let length = unsafe { GetKeyNameTextW(l_param, buffer.as_mut_slice()) };
252    if length > 0 {
253        return HSTRING::from_wide(&buffer[..length as usize]).to_string();
254    }
255
256    String::new()
257}
258
259const fn get_mouse_button_name(key_code: u32) -> Option<&'static str> {
260    Some(match key_code {
261        256 => "Left Mouse Button",
262        257 => "Right Mouse Button",
263        258 => "Middle Mouse Button",
264        259 => "Mouse Button 3",
265        260 => "Mouse Button 4",
266        261 => "Mouse Button 5",
267        262 => "Mouse Button 6",
268        263 => "Mouse Button 7",
269        264 => "Mouse Wheel Up",
270        265 => "Mouse Wheel Down",
271        _ => return None,
272    })
273}