Atlas - SDL_dosevents.c
Home / ext / SDL / src / video / dos Lines: 1 | Size: 14723 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21 22#include "SDL_internal.h" 23 24#ifdef SDL_VIDEO_DRIVER_DOSVESA 25 26#include "../../events/SDL_events_c.h" 27#include "../../events/SDL_keyboard_c.h" 28#include "../../events/SDL_mouse_c.h" 29#include "../SDL_sysvideo.h" 30#include "SDL_dosvideo.h" 31 32#include "../../core/dos/SDL_dos_scheduler.h" 33#include "SDL_dosevents_c.h" 34 35// PS/2 keyboard controller port 36#define KBD_DATA_PORT 0x60 37 38// Scancode byte structure 39#define SCANCODE_MASK 0x7F // bits 0-6: scancode index 40#define SCANCODE_RELEASE 0x80 // bit 7: key released (break code) 41 42// Multi-byte scancode prefixes 43#define SCANCODE_PREFIX_EXTENDED 0xE0 // extended key prefix 44#define SCANCODE_PREFIX_PAUSE 0xE1 // pause key sequence prefix 45 46// VGA Input Status Register 1 (for vblank detection) 47#define VGA_STATUS_PORT 0x3DA 48#define VGA_STATUS_VBLANK 0x08 // bit 3: vertical retrace active 49 50// Scancode table: https://www.plantation-productions.com/Webster/www.artofasm.com/DOS/pdf/apndxc.pdf 51static const SDL_Scancode DOSVESA_ScancodeMapping[] = { // index is the scancode from the IRQ1 handler bitwise-ANDed against 0x7F. 52 /* 0x00 */ SDL_SCANCODE_UNKNOWN, 53 /* 0x01 */ SDL_SCANCODE_ESCAPE, 54 /* 0x02 */ SDL_SCANCODE_1, 55 /* 0x03 */ SDL_SCANCODE_2, 56 /* 0x04 */ SDL_SCANCODE_3, 57 /* 0x05 */ SDL_SCANCODE_4, 58 /* 0x06 */ SDL_SCANCODE_5, 59 /* 0x07 */ SDL_SCANCODE_6, 60 /* 0x08 */ SDL_SCANCODE_7, 61 /* 0x09 */ SDL_SCANCODE_8, 62 /* 0x0A */ SDL_SCANCODE_9, 63 /* 0x0B */ SDL_SCANCODE_0, 64 /* 0x0C */ SDL_SCANCODE_MINUS, 65 /* 0x0D */ SDL_SCANCODE_EQUALS, 66 /* 0x0E */ SDL_SCANCODE_BACKSPACE, 67 /* 0x0F */ SDL_SCANCODE_TAB, 68 69 /* 0x10 */ SDL_SCANCODE_Q, 70 /* 0x11 */ SDL_SCANCODE_W, 71 /* 0x12 */ SDL_SCANCODE_E, 72 /* 0x13 */ SDL_SCANCODE_R, 73 /* 0x14 */ SDL_SCANCODE_T, 74 /* 0x15 */ SDL_SCANCODE_Y, 75 /* 0x16 */ SDL_SCANCODE_U, 76 /* 0x17 */ SDL_SCANCODE_I, 77 /* 0x18 */ SDL_SCANCODE_O, 78 /* 0x19 */ SDL_SCANCODE_P, 79 /* 0x1A */ SDL_SCANCODE_LEFTBRACKET, 80 /* 0x1B */ SDL_SCANCODE_RIGHTBRACKET, 81 /* 0x1C */ SDL_SCANCODE_RETURN, 82 /* 0x1D */ SDL_SCANCODE_LCTRL, 83 /* 0x1E */ SDL_SCANCODE_A, 84 /* 0x1F */ SDL_SCANCODE_S, 85 86 /* 0x20 */ SDL_SCANCODE_D, 87 /* 0x21 */ SDL_SCANCODE_F, 88 /* 0x22 */ SDL_SCANCODE_G, 89 /* 0x23 */ SDL_SCANCODE_H, 90 /* 0x24 */ SDL_SCANCODE_J, 91 /* 0x25 */ SDL_SCANCODE_K, 92 /* 0x26 */ SDL_SCANCODE_L, 93 /* 0x27 */ SDL_SCANCODE_SEMICOLON, 94 /* 0x28 */ SDL_SCANCODE_APOSTROPHE, 95 /* 0x29 */ SDL_SCANCODE_GRAVE, 96 /* 0x2A */ SDL_SCANCODE_LSHIFT, 97 /* 0x2B */ SDL_SCANCODE_BACKSLASH, 98 /* 0x2C */ SDL_SCANCODE_Z, 99 /* 0x2D */ SDL_SCANCODE_X, 100 /* 0x2E */ SDL_SCANCODE_C, 101 /* 0x2F */ SDL_SCANCODE_V, 102 103 /* 0x30 */ SDL_SCANCODE_B, 104 /* 0x31 */ SDL_SCANCODE_N, 105 /* 0x32 */ SDL_SCANCODE_M, 106 /* 0x33 */ SDL_SCANCODE_COMMA, 107 /* 0x34 */ SDL_SCANCODE_PERIOD, 108 /* 0x35 */ SDL_SCANCODE_SLASH, 109 /* 0x36 */ SDL_SCANCODE_RSHIFT, 110 /* 0x37 */ SDL_SCANCODE_KP_MULTIPLY, 111 /* 0x38 */ SDL_SCANCODE_LALT, 112 /* 0x39 */ SDL_SCANCODE_SPACE, 113 /* 0x3A */ SDL_SCANCODE_CAPSLOCK, 114 /* 0x3B */ SDL_SCANCODE_F1, 115 /* 0x3C */ SDL_SCANCODE_F2, 116 /* 0x3D */ SDL_SCANCODE_F3, 117 /* 0x3E */ SDL_SCANCODE_F4, 118 /* 0x3F */ SDL_SCANCODE_F5, 119 120 /* 0x040 */ SDL_SCANCODE_F6, 121 /* 0x041 */ SDL_SCANCODE_F7, 122 /* 0x042 */ SDL_SCANCODE_F8, 123 /* 0x043 */ SDL_SCANCODE_F9, 124 /* 0x044 */ SDL_SCANCODE_F10, 125 /* 0x045 */ SDL_SCANCODE_NUMLOCKCLEAR, 126 /* 0x046 */ SDL_SCANCODE_SCROLLLOCK, 127 /* 0x047 */ SDL_SCANCODE_KP_7, 128 /* 0x048 */ SDL_SCANCODE_KP_8, 129 /* 0x049 */ SDL_SCANCODE_KP_9, 130 /* 0x04A */ SDL_SCANCODE_KP_MINUS, 131 /* 0x04B */ SDL_SCANCODE_KP_4, 132 /* 0x04C */ SDL_SCANCODE_KP_5, 133 /* 0x04D */ SDL_SCANCODE_KP_6, 134 /* 0x04E */ SDL_SCANCODE_KP_PLUS, 135 /* 0x04F */ SDL_SCANCODE_KP_1, 136 137 /* 0x050 */ SDL_SCANCODE_KP_2, 138 /* 0x051 */ SDL_SCANCODE_KP_3, 139 /* 0x052 */ SDL_SCANCODE_KP_0, 140 /* 0x053 */ SDL_SCANCODE_KP_PERIOD, 141 /* 0x054 */ SDL_SCANCODE_UNKNOWN, 142 /* 0x055 */ SDL_SCANCODE_UNKNOWN, 143 /* 0x056 */ SDL_SCANCODE_UNKNOWN, 144 /* 0x057 */ SDL_SCANCODE_F11, 145 /* 0x058 */ SDL_SCANCODE_F12 146}; 147 148// Extended scancode table for keys prefixed with 0xE0 149static const SDL_Scancode DOSVESA_ExtendedScancodeMapping[] = { // index is the scancode byte following the 0xE0 prefix, masked with 0x7F. 150 /* 0x00 */ SDL_SCANCODE_UNKNOWN, 151 /* 0x01 */ SDL_SCANCODE_UNKNOWN, 152 /* 0x02 */ SDL_SCANCODE_UNKNOWN, 153 /* 0x03 */ SDL_SCANCODE_UNKNOWN, 154 /* 0x04 */ SDL_SCANCODE_UNKNOWN, 155 /* 0x05 */ SDL_SCANCODE_UNKNOWN, 156 /* 0x06 */ SDL_SCANCODE_UNKNOWN, 157 /* 0x07 */ SDL_SCANCODE_UNKNOWN, 158 /* 0x08 */ SDL_SCANCODE_UNKNOWN, 159 /* 0x09 */ SDL_SCANCODE_UNKNOWN, 160 /* 0x0A */ SDL_SCANCODE_UNKNOWN, 161 /* 0x0B */ SDL_SCANCODE_UNKNOWN, 162 /* 0x0C */ SDL_SCANCODE_UNKNOWN, 163 /* 0x0D */ SDL_SCANCODE_UNKNOWN, 164 /* 0x0E */ SDL_SCANCODE_UNKNOWN, 165 /* 0x0F */ SDL_SCANCODE_UNKNOWN, 166 167 /* 0x10 */ SDL_SCANCODE_UNKNOWN, 168 /* 0x11 */ SDL_SCANCODE_UNKNOWN, 169 /* 0x12 */ SDL_SCANCODE_UNKNOWN, 170 /* 0x13 */ SDL_SCANCODE_UNKNOWN, 171 /* 0x14 */ SDL_SCANCODE_UNKNOWN, 172 /* 0x15 */ SDL_SCANCODE_UNKNOWN, 173 /* 0x16 */ SDL_SCANCODE_UNKNOWN, 174 /* 0x17 */ SDL_SCANCODE_UNKNOWN, 175 /* 0x18 */ SDL_SCANCODE_UNKNOWN, 176 /* 0x19 */ SDL_SCANCODE_UNKNOWN, 177 /* 0x1A */ SDL_SCANCODE_UNKNOWN, 178 /* 0x1B */ SDL_SCANCODE_UNKNOWN, 179 /* 0x1C */ SDL_SCANCODE_KP_ENTER, 180 /* 0x1D */ SDL_SCANCODE_RCTRL, 181 /* 0x1E */ SDL_SCANCODE_UNKNOWN, 182 /* 0x1F */ SDL_SCANCODE_UNKNOWN, 183 184 /* 0x20 */ SDL_SCANCODE_UNKNOWN, 185 /* 0x21 */ SDL_SCANCODE_UNKNOWN, 186 /* 0x22 */ SDL_SCANCODE_UNKNOWN, 187 /* 0x23 */ SDL_SCANCODE_UNKNOWN, 188 /* 0x24 */ SDL_SCANCODE_UNKNOWN, 189 /* 0x25 */ SDL_SCANCODE_UNKNOWN, 190 /* 0x26 */ SDL_SCANCODE_UNKNOWN, 191 /* 0x27 */ SDL_SCANCODE_UNKNOWN, 192 /* 0x28 */ SDL_SCANCODE_UNKNOWN, 193 /* 0x29 */ SDL_SCANCODE_UNKNOWN, 194 /* 0x2A */ SDL_SCANCODE_UNKNOWN, // fake left shift, ignore 195 /* 0x2B */ SDL_SCANCODE_UNKNOWN, 196 /* 0x2C */ SDL_SCANCODE_UNKNOWN, 197 /* 0x2D */ SDL_SCANCODE_UNKNOWN, 198 /* 0x2E */ SDL_SCANCODE_UNKNOWN, 199 /* 0x2F */ SDL_SCANCODE_UNKNOWN, 200 201 /* 0x30 */ SDL_SCANCODE_UNKNOWN, 202 /* 0x31 */ SDL_SCANCODE_UNKNOWN, 203 /* 0x32 */ SDL_SCANCODE_UNKNOWN, 204 /* 0x33 */ SDL_SCANCODE_UNKNOWN, 205 /* 0x34 */ SDL_SCANCODE_UNKNOWN, 206 /* 0x35 */ SDL_SCANCODE_KP_DIVIDE, 207 /* 0x36 */ SDL_SCANCODE_UNKNOWN, // fake right shift, ignore 208 /* 0x37 */ SDL_SCANCODE_PRINTSCREEN, 209 /* 0x38 */ SDL_SCANCODE_RALT, 210 /* 0x39 */ SDL_SCANCODE_UNKNOWN, 211 /* 0x3A */ SDL_SCANCODE_UNKNOWN, 212 /* 0x3B */ SDL_SCANCODE_UNKNOWN, 213 /* 0x3C */ SDL_SCANCODE_UNKNOWN, 214 /* 0x3D */ SDL_SCANCODE_UNKNOWN, 215 /* 0x3E */ SDL_SCANCODE_UNKNOWN, 216 /* 0x3F */ SDL_SCANCODE_UNKNOWN, 217 218 /* 0x40 */ SDL_SCANCODE_UNKNOWN, 219 /* 0x41 */ SDL_SCANCODE_UNKNOWN, 220 /* 0x42 */ SDL_SCANCODE_UNKNOWN, 221 /* 0x43 */ SDL_SCANCODE_UNKNOWN, 222 /* 0x44 */ SDL_SCANCODE_UNKNOWN, 223 /* 0x45 */ SDL_SCANCODE_UNKNOWN, 224 /* 0x46 */ SDL_SCANCODE_PAUSE, // Ctrl+Break sends E0 46 E0 C6 225 /* 0x47 */ SDL_SCANCODE_HOME, 226 /* 0x48 */ SDL_SCANCODE_UP, 227 /* 0x49 */ SDL_SCANCODE_PAGEUP, 228 /* 0x4A */ SDL_SCANCODE_UNKNOWN, 229 /* 0x4B */ SDL_SCANCODE_LEFT, 230 /* 0x4C */ SDL_SCANCODE_UNKNOWN, 231 /* 0x4D */ SDL_SCANCODE_RIGHT, 232 /* 0x4E */ SDL_SCANCODE_UNKNOWN, 233 /* 0x4F */ SDL_SCANCODE_END, 234 235 /* 0x50 */ SDL_SCANCODE_DOWN, 236 /* 0x51 */ SDL_SCANCODE_PAGEDOWN, 237 /* 0x52 */ SDL_SCANCODE_INSERT, 238 /* 0x53 */ SDL_SCANCODE_DELETE, 239 /* 0x54 */ SDL_SCANCODE_UNKNOWN, 240 /* 0x55 */ SDL_SCANCODE_UNKNOWN, 241 /* 0x56 */ SDL_SCANCODE_UNKNOWN, 242 /* 0x57 */ SDL_SCANCODE_UNKNOWN, 243 /* 0x58 */ SDL_SCANCODE_UNKNOWN, 244 /* 0x59 */ SDL_SCANCODE_UNKNOWN, 245 /* 0x5A */ SDL_SCANCODE_UNKNOWN, 246 /* 0x5B */ SDL_SCANCODE_LGUI, 247 /* 0x5C */ SDL_SCANCODE_RGUI, 248 /* 0x5D */ SDL_SCANCODE_APPLICATION 249}; 250 251static Uint8 keyevents_ringbuffer[256]; 252static int keyevents_head = 0; 253static int keyevents_tail = 0; 254 255void DOSVESA_PumpEvents(SDL_VideoDevice *device) 256{ 257 /* Give cooperative threads a chance to run. Audio mixing now runs 258 in its own cooperative thread (via SDL's normal audio thread), 259 so it will execute during this yield along with loading threads 260 and anything else that is runnable. */ 261 DOS_Yield(); 262 263 static bool is_extended = false; 264 static int pause_sequence_remaining = 0; 265 266 while (keyevents_head != keyevents_tail) { 267 const Uint8 event = keyevents_ringbuffer[keyevents_tail]; 268 keyevents_tail = (keyevents_tail + 1) & (SDL_arraysize(keyevents_ringbuffer) - 1); 269 270 // Handle remaining bytes of E1 Pause key sequence (E1 1D 45 E1 9D C5). 271 if (pause_sequence_remaining > 0) { 272 pause_sequence_remaining--; 273 continue; 274 } 275 276 // Pause key sends a multi-byte sequence: E1 1D 45 E1 9D C5. Emit PAUSE press+release and consume the rest. 277 if (event == SCANCODE_PREFIX_PAUSE) { 278 pause_sequence_remaining = 5; // skip the next 5 bytes 279 SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_PAUSE, true); 280 SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_PAUSE, false); 281 continue; 282 } 283 284 if (event == SCANCODE_PREFIX_EXTENDED) { 285 is_extended = true; 286 continue; 287 } 288 289 const int scancode = (int)(event & SCANCODE_MASK); 290 const bool pressed = ((event & SCANCODE_RELEASE) == 0); 291 292 SDL_Scancode sc = SDL_SCANCODE_UNKNOWN; 293 294 if (is_extended) { 295 is_extended = false; 296 if (scancode < SDL_arraysize(DOSVESA_ExtendedScancodeMapping)) { 297 sc = DOSVESA_ExtendedScancodeMapping[scancode]; 298 } 299 } else { 300 if (scancode < SDL_arraysize(DOSVESA_ScancodeMapping)) { 301 sc = DOSVESA_ScancodeMapping[scancode]; 302 } 303 } 304 305 if (sc != SDL_SCANCODE_UNKNOWN) { 306 SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, 0, sc, pressed); 307 308 // Generate text input events for key-down on printable characters. 309 // SDL keycodes below SDLK_SCANCODE_MASK are Unicode codepoints. 310 if (pressed) { 311 SDL_Keymod mod = SDL_GetModState(); 312 if (!(mod & (SDL_KMOD_CTRL | SDL_KMOD_ALT))) { 313 SDL_Keycode keycode = SDL_GetKeyFromScancode(sc, mod, false); 314 if (keycode > 0 && keycode < SDLK_SCANCODE_MASK && !SDL_iscntrl((int)keycode)) { 315 char text[5]; 316 char *end = SDL_UCS4ToUTF8((Uint32)keycode, text); 317 *end = '\0'; 318 SDL_SendKeyboardText(text); 319 } 320 } 321 } 322 } 323 } 324 325 SDL_Mouse *mouse = SDL_GetMouse(); 326 if (mouse->internal) { // if non-NULL, there's a mouse detected on the system. 327 __dpmi_regs regs; 328 329 regs.x.ax = 0x3; // read mouse buttons and position. 330 __dpmi_int(0x33, ®s); 331 const Uint16 buttons = (int)(Sint16)regs.x.bx; 332 333 SDL_SendMouseButton(0, mouse->focus, SDL_DEFAULT_MOUSE_ID, SDL_BUTTON_LEFT, (buttons & (1 << 0)) != 0); 334 SDL_SendMouseButton(0, mouse->focus, SDL_DEFAULT_MOUSE_ID, SDL_BUTTON_RIGHT, (buttons & (1 << 1)) != 0); 335 SDL_SendMouseButton(0, mouse->focus, SDL_DEFAULT_MOUSE_ID, SDL_BUTTON_MIDDLE, (buttons & (1 << 2)) != 0); 336 337 if (!mouse->relative_mode) { 338 const int x = (int)(Sint16)regs.x.cx; // part of function 0x3's return value. 339 const int y = (int)(Sint16)regs.x.dx; 340 SDL_SendMouseMotion(0, mouse->focus, SDL_DEFAULT_MOUSE_ID, false, x, y); 341 } else { 342 regs.x.ax = 0xB; // read motion counters 343 __dpmi_int(0x33, ®s); 344 // values returned here are -32768 to 32767 345 const SDL_VideoData *viddata = device->internal; 346 const float MICKEYS_PER_HPIXEL = viddata->mickeys_per_hpixel; 347 const float MICKEYS_PER_VPIXEL = viddata->mickeys_per_vpixel; 348 const int mickeys_x = (int)(Sint16)regs.x.cx; 349 const int mickeys_y = (int)(Sint16)regs.x.dx; 350 SDL_SendMouseMotion(0, mouse->focus, SDL_DEFAULT_MOUSE_ID, true, mickeys_x / MICKEYS_PER_HPIXEL, mickeys_y / MICKEYS_PER_VPIXEL); 351 } 352 } 353} 354 355static void KeyboardIRQHandler(void) // this is wrapped in a thing that handles IRET, etc. 356{ 357 keyevents_ringbuffer[keyevents_head] = inportb(KBD_DATA_PORT); 358 keyevents_head = (keyevents_head + 1) & (SDL_arraysize(keyevents_ringbuffer) - 1); 359 DOS_EndOfInterrupt(1); 360} 361static void KeyboardIRQHandler_End(void) {} // end-of-ISR label for memory locking 362 363void DOSVESA_InitKeyboard(SDL_VideoDevice *device) 364{ 365 SDL_VideoData *data = device->internal; 366 367 // Lock ISR code and data to prevent page faults during interrupts 368 DOS_LockCode(KeyboardIRQHandler, KeyboardIRQHandler_End); 369 DOS_LockVariable(keyevents_ringbuffer); 370 DOS_LockVariable(keyevents_head); 371 DOS_LockVariable(keyevents_tail); 372 373 DOS_HookInterrupt(1, KeyboardIRQHandler, &data->keyboard_interrupt_hook); 374} 375 376void DOSVESA_QuitKeyboard(SDL_VideoDevice *device) 377{ 378 SDL_VideoData *data = device->internal; 379 DOS_UnhookInterrupt(&data->keyboard_interrupt_hook, false); 380 381 // Drain the BIOS keyboard buffer so held keys (like ESC) don't 382 // bleed through to the DOS command line after we exit. 383 { 384 __dpmi_regs regs; 385 for (;;) { 386 regs.h.ah = 0x01; // BIOS: check for keystroke 387 __dpmi_int(0x16, ®s); 388 if (regs.x.flags & 0x40) { // ZF set = buffer empty 389 break; 390 } 391 regs.h.ah = 0x00; // BIOS: read keystroke (removes it) 392 __dpmi_int(0x16, ®s); 393 } 394 } 395} 396 397#endif // SDL_VIDEO_DRIVER_DOSVESA 398[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.