Atlas - SDL_dosvideo.c

Home / ext / SDL / src / video / dos Lines: 1 | Size: 12780 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// SDL internals 27#include "../../events/SDL_keyboard_c.h" 28#include "../../events/SDL_mouse_c.h" 29#include "../SDL_sysvideo.h" 30 31// DOS declarations 32#include "SDL_dosevents_c.h" 33#include "SDL_dosframebuffer_c.h" 34#include "SDL_dosmodes.h" 35#include "SDL_dosmouse.h" 36#include "SDL_dosvideo.h" 37 38// Apply a display mode for a window: set the hardware mode, store it as 39// the window's requested fullscreen mode, and update window dimensions. 40static void DOSVESA_ApplyModeForWindow(SDL_VideoDisplay *display, SDL_Window *window, SDL_DisplayMode *mode) 41{ 42 SDL_SetDisplayModeForDisplay(display, mode); 43 44 if (mode) { 45 SDL_copyp(&window->requested_fullscreen_mode, mode); 46 window->floating.w = window->windowed.w = window->w = mode->w; 47 window->floating.h = window->windowed.h = window->h = mode->h; 48 } 49} 50 51static bool DOSVESA_CreateWindow(SDL_VideoDevice *device, SDL_Window *window, SDL_PropertiesID create_props) 52{ 53 // Allocate window internal data 54 SDL_WindowData *wdata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData)); 55 if (!wdata) { 56 return false; 57 } 58 59 // Setup driver data for this window 60 window->internal = wdata; 61 62 // One window, it always has focus 63 SDL_SetMouseFocus(window); 64 SDL_SetKeyboardFocus(window); 65 66 { 67 SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window); 68 if (!display) { 69 return true; 70 } 71 72 SDL_DisplayMode *mode = NULL; 73 if (window->requested_fullscreen_mode.internal) { 74 // App explicitly set a fullscreen mode. 75 mode = &window->requested_fullscreen_mode; 76 } else if (window->floating.w > 0 && window->floating.h > 0) { 77 SDL_DisplayMode closest; 78 if (SDL_GetClosestFullscreenDisplayMode(display->id, window->floating.w, window->floating.h, 0.0f, false, &closest)) { 79 mode = &closest; 80 } 81 } 82 if (!mode) { 83 return true; 84 } 85 86 DOSVESA_ApplyModeForWindow(display, window, mode); 87 } 88 89 return true; 90} 91 92static void DOSVESA_SetWindowSize(SDL_VideoDevice *device, SDL_Window *window) 93{ 94 SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window); 95 if (!display) { 96 return; 97 } 98 99 SDL_DisplayMode closest; 100 SDL_DisplayMode *mode = NULL; 101 if (SDL_GetClosestFullscreenDisplayMode(display->id, window->floating.w, window->floating.h, 0.0f, false, &closest)) { 102 mode = &closest; 103 } 104 105 DOSVESA_ApplyModeForWindow(display, window, mode); 106 107 // Invalidate the framebuffer so it gets recreated at the new size. 108 window->surface_valid = false; 109} 110 111// Critical for performance: this function must be implemented and as simple 112// as possible to avoid slowdowns during calls to SDL_UpdateWindowSurface(). 113// A few pointer dereferences here can cost 10% of performance easily. 114static void DOSVESA_GetWindowSizeInPixels(SDL_VideoDevice *device, SDL_Window *window, int *w, int *h) 115{ 116 *w = window->w; 117 *h = window->h; 118} 119 120static void DOSVESA_DestroyWindow(SDL_VideoDevice *device, SDL_Window *window) 121{ 122 SDL_free(window->internal); 123 window->internal = NULL; 124} 125 126static bool DOSVESA_VideoInit(SDL_VideoDevice *device) 127{ 128 SDL_VideoData *data = device->internal; 129 130 // Verify that the "fat DS" nearptr trick is active. Without it, 131 // DOS_PhysicalToLinear() produces garbage pointers and we crash. 132 // SDL_RunApp() enables this automatically; if the app defined 133 // SDL_MAIN_HANDLED it must call __djgpp_nearptr_enable() itself. 134 if (__djgpp_conventional_base == 0) { 135 return SDL_SetError("DOSVESA: __djgpp_nearptr_enable() was not called. " 136 "Did you define SDL_MAIN_HANDLED without enabling the fat DS trick?"); 137 } 138 139 // We are probably in text mode at startup, so we don't have a real "desktop mode" atm. 140 // Pick something _super_ conservative for now. 141 // We'll change to a real video mode after enumerating available modes below. 142 SDL_DisplayMode mode; 143 SDL_zero(mode); 144 mode.format = SDL_PIXELFORMAT_RGB565; 145 mode.w = 320; 146 mode.h = 200; 147 148 SDL_VideoDisplay vdisplay; 149 SDL_zero(vdisplay); 150 SDL_memcpy(&vdisplay.desktop_mode, &mode, sizeof(mode)); 151 vdisplay.name = (char *)DOSVESA_GetGPUName(); 152 SDL_DisplayID display_id = SDL_AddVideoDisplay(&vdisplay, false); 153 if (!display_id) { 154 return false; 155 } 156 157 SDL_zero(data->current_mode); 158 159 SDL_VideoDisplay *display = SDL_GetVideoDisplay(display_id); 160 if (!display || !DOSVESA_GetDisplayModes(device, display)) { 161 return false; 162 } 163 164 // Pick a sensible default desktop mode. This determines the window 165 // size for FULLSCREEN_ONLY. Target 640x480 as a safe default; apps 166 // that want something else should call SDL_SetWindowFullscreenMode. 167 { 168 SDL_DisplayMode closest; 169 if (SDL_GetClosestFullscreenDisplayMode(display_id, 640, 480, 0.0f, false, &closest)) { 170 // Deep-copy the mode into desktop_mode. We need our own 171 // internal allocation because SDL frees desktop_mode.internal 172 // and fullscreen_modes[].internal independently. 173 SDL_DisplayModeData *desktop_internal = (SDL_DisplayModeData *)SDL_malloc(sizeof(*desktop_internal)); 174 if (desktop_internal) { 175 SDL_copyp(desktop_internal, (const SDL_DisplayModeData *)closest.internal); 176 SDL_copyp(&display->desktop_mode, &closest); 177 display->desktop_mode.internal = desktop_internal; 178 } 179 } 180 } 181 182 // Save the current video mode so we can restore it on quit. 183 // The VBE calls (0x4F03, 0x4F04) return 0x004F on success; on cards 184 // without VBE support (or VBE < 1.2) they simply fail and we fall 185 // back to restoring standard text mode 0x03. 186 { 187 __dpmi_regs regs; 188 SDL_zero(regs); 189 regs.x.ax = 0x4F03; // VBE Get Current Mode 190 __dpmi_int(0x10, &regs); 191 if (regs.x.ax == 0x004F) { 192 data->original_vbe_mode = regs.x.bx; 193 } else { 194 data->original_vbe_mode = 0x03; // assume text mode 195 } 196 197 // Save VBE state via VBE function 0x4F04 so we can do a full restore later. 198 // Step 1: query the required buffer size (subfunction 0x00). 199 SDL_zero(regs); 200 regs.x.ax = 0x4F04; 201 regs.x.dx = 0x00; // subfunction 0: get state buffer size 202 regs.x.cx = 0x0F; // save all state: hardware + BIOS data + DAC + SVGA 203 __dpmi_int(0x10, &regs); 204 if (regs.x.ax == 0x004F) { 205 // regs.x.bx contains size in 64-byte blocks. 206 Uint32 state_size = (Uint32)regs.x.bx * 64; 207 _go32_dpmi_seginfo state_seginfo; 208 void *state_buf = DOS_AllocateConventionalMemory(state_size, &state_seginfo); 209 if (state_buf) { 210 // Step 2: save state (subfunction 0x01) into conventional memory buffer. 211 SDL_zero(regs); 212 regs.x.ax = 0x4F04; 213 regs.x.dx = 0x01; // subfunction 1: save state 214 regs.x.cx = 0x0F; // all state 215 regs.x.es = DOS_LinearToPhysical(state_buf) / 16; 216 regs.x.bx = DOS_LinearToPhysical(state_buf) & 0xF; 217 __dpmi_int(0x10, &regs); 218 if (regs.x.ax == 0x004F) { 219 // Copy state from conventional memory to our heap so we 220 // can free the low-memory buffer now. 221 data->vbe_state_buffer = SDL_malloc(state_size); 222 if (data->vbe_state_buffer) { 223 SDL_memcpy(data->vbe_state_buffer, state_buf, state_size); 224 data->vbe_state_buffer_size = state_size; 225 } 226 } 227 DOS_FreeConventionalMemory(&state_seginfo); 228 } 229 } 230 } 231 232 DOSVESA_InitMouse(device); 233 DOSVESA_InitKeyboard(device); 234 235 return true; 236} 237 238static void DOSVESA_VideoQuit(SDL_VideoDevice *device) 239{ 240 SDL_VideoData *data = device->internal; 241 242 if (data->mapping.size) { 243 __dpmi_free_physical_address_mapping(&data->mapping); // dump existing video mapping. 244 SDL_zero(data->mapping); 245 } 246 247 // Restore saved VBE state if available. 248 if (data->vbe_state_buffer && data->vbe_state_buffer_size > 0) { 249 _go32_dpmi_seginfo restore_seginfo; 250 void *restore_buf = DOS_AllocateConventionalMemory(data->vbe_state_buffer_size, &restore_seginfo); 251 if (restore_buf) { 252 SDL_memcpy(restore_buf, data->vbe_state_buffer, data->vbe_state_buffer_size); 253 __dpmi_regs regs; 254 SDL_zero(regs); 255 regs.x.ax = 0x4F04; 256 regs.x.dx = 0x02; // subfunction 2: restore state 257 regs.x.cx = 0x0F; // all state 258 regs.x.es = DOS_LinearToPhysical(restore_buf) / 16; 259 regs.x.bx = DOS_LinearToPhysical(restore_buf) & 0xF; 260 __dpmi_int(0x10, &regs); 261 DOS_FreeConventionalMemory(&restore_seginfo); 262 } 263 SDL_free(data->vbe_state_buffer); 264 data->vbe_state_buffer = NULL; 265 data->vbe_state_buffer_size = 0; 266 } 267 268 // Restore the original video mode. 269 { 270 __dpmi_regs regs; 271 bool restored = false; 272 273 // Try VBE mode restore first (only works on VBE 1.2+). 274 if (data->original_vbe_mode != 0x03) { 275 SDL_zero(regs); 276 regs.x.ax = 0x4F02; 277 regs.x.bx = data->original_vbe_mode; 278 __dpmi_int(0x10, &regs); 279 restored = (regs.x.ax == 0x004F); 280 } 281 282 // Fall back to standard BIOS text mode (works on any VGA). 283 if (!restored) { 284 SDL_zero(regs); 285 regs.x.ax = 0x0003; 286 __dpmi_int(0x10, &regs); 287 } 288 } 289 290 SDL_zero(data->current_mode); 291 292 DOSVESA_QuitMouse(device); 293 DOSVESA_QuitKeyboard(device); 294} 295 296static void DOSVESA_Destroy(SDL_VideoDevice *device) 297{ 298 SDL_VideoData *data = device->internal; 299 SDL_free(data->vbe_state_buffer); 300 SDL_free(device->internal); 301 SDL_free(device); 302 DOSVESA_FreeVESAInfo(); 303} 304 305static SDL_VideoDevice *DOSVESA_CreateDevice(void) 306{ 307 if (!DOSVESA_SupportsVESA()) { 308 return NULL; 309 } 310 311 SDL_VideoDevice *device; 312 SDL_VideoData *phdata; 313 314 // Initialize SDL_VideoDevice structure 315 device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice)); 316 if (!device) { 317 return NULL; 318 } 319 320 // Initialize internal data 321 phdata = (SDL_VideoData *)SDL_calloc(1, sizeof(SDL_VideoData)); 322 if (!phdata) { 323 SDL_free(device); 324 return NULL; 325 } 326 327 device->internal = phdata; 328 device->free = DOSVESA_Destroy; 329 device->VideoInit = DOSVESA_VideoInit; 330 device->VideoQuit = DOSVESA_VideoQuit; 331 device->GetDisplayModes = DOSVESA_GetDisplayModes; 332 device->SetDisplayMode = DOSVESA_SetDisplayMode; 333 device->CreateSDLWindow = DOSVESA_CreateWindow; 334 device->SetWindowSize = DOSVESA_SetWindowSize; 335 device->DestroyWindow = DOSVESA_DestroyWindow; 336 device->CreateWindowFramebuffer = DOSVESA_CreateWindowFramebuffer; 337 device->SetWindowFramebufferVSync = DOSVESA_SetWindowFramebufferVSync; 338 device->GetWindowFramebufferVSync = DOSVESA_GetWindowFramebufferVSync; 339 device->UpdateWindowFramebuffer = DOSVESA_UpdateWindowFramebuffer; 340 device->DestroyWindowFramebuffer = DOSVESA_DestroyWindowFramebuffer; 341 device->GetWindowSizeInPixels = DOSVESA_GetWindowSizeInPixels; 342 device->PumpEvents = DOSVESA_PumpEvents; 343 device->device_caps = VIDEO_DEVICE_CAPS_FULLSCREEN_ONLY; 344 345 return device; 346} 347 348VideoBootStrap DOSVESA_bootstrap = { 349 "vesa", 350 "DOS VESA Video Driver", 351 DOSVESA_CreateDevice, 352 NULL, // no ShowMessageBox implementation 353 false 354}; 355 356#endif // SDL_VIDEO_DRIVER_DOSVESA 357
[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.