Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1 | pmbaty | 1 | #include <glad/glad.h> |
| 2 | |||
| 3 | // this needs to be included after glad.h |
||
| 4 | #include <SDL.h> |
||
| 5 | #include <SDL_opengl.h> |
||
| 6 | |||
| 7 | #include "../renderers/gl/gl_renderer.h" |
||
| 8 | #include "harness/config.h" |
||
| 9 | #include "harness/hooks.h" |
||
| 10 | #include "harness/trace.h" |
||
| 11 | #include "sdl2_scancode_to_dinput.h" |
||
| 12 | |||
| 13 | #include "globvars.h" |
||
| 14 | #include "grafdata.h" |
||
| 15 | #include "pd/sys.h" |
||
| 16 | |||
| 17 | SDL_Window* window; |
||
| 18 | SDL_GLContext context; |
||
| 19 | uint8_t directinput_key_state[SDL_NUM_SCANCODES]; |
||
| 20 | int render_width, render_height; |
||
| 21 | int window_width, window_height; |
||
| 22 | int vp_x, vp_y, vp_width, vp_height; |
||
| 23 | |||
| 24 | static const int scalefactor = 2; // Pierre-Marie Baty -- scale factor addition |
||
| 25 | |||
| 26 | struct { |
||
| 27 | float x; |
||
| 28 | float y; |
||
| 29 | } sdl_window_scale; |
||
| 30 | |||
| 31 | static void update_viewport(void) { |
||
| 32 | const float target_aspect_ratio = (float)render_width / render_height; |
||
| 33 | const float aspect_ratio = (float)window_width / window_height; |
||
| 34 | |||
| 35 | vp_width = window_width; |
||
| 36 | vp_height = window_height; |
||
| 37 | if (aspect_ratio != target_aspect_ratio) { |
||
| 38 | if (aspect_ratio > target_aspect_ratio) { |
||
| 39 | vp_width = window_height * target_aspect_ratio + .5f; |
||
| 40 | } else { |
||
| 41 | vp_height = window_width / target_aspect_ratio + .5f; |
||
| 42 | } |
||
| 43 | } |
||
| 44 | vp_x = (window_width - vp_width) / 2; |
||
| 45 | vp_y = (window_height - vp_height) / 2; |
||
| 46 | GLRenderer_SetViewport(vp_x, vp_y, vp_width, vp_height); |
||
| 47 | } |
||
| 48 | |||
| 49 | static void* create_window_and_renderer(char* title, int x, int y, int width, int height) { |
||
| 50 | |||
| 51 | window_width = width; |
||
| 52 | window_height = height; |
||
| 53 | render_width = width; |
||
| 54 | render_height = height; |
||
| 55 | |||
| 56 | if (SDL_Init(SDL_INIT_VIDEO) != 0) { |
||
| 57 | LOG_PANIC("SDL_INIT_VIDEO error: %s", SDL_GetError()); |
||
| 58 | } |
||
| 59 | |||
| 60 | if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE) != 0) { |
||
| 61 | LOG_PANIC("Failed to set SDL_GL_CONTEXT_PROFILE_MASK attribute. %s", SDL_GetError()); |
||
| 62 | }; |
||
| 63 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); |
||
| 64 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); |
||
| 65 | |||
| 66 | SDL_GL_SetSwapInterval(1); |
||
| 67 | |||
| 68 | window = SDL_CreateWindow(title, |
||
| 69 | SDL_WINDOWPOS_CENTERED, |
||
| 70 | SDL_WINDOWPOS_CENTERED, |
||
| 71 | scalefactor * width, scalefactor * height, // Pierre-Marie Baty -- scale factor addition |
||
| 72 | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); |
||
| 73 | |||
| 74 | if (window == NULL) { |
||
| 75 | LOG_PANIC("Failed to create window. %s", SDL_GetError()); |
||
| 76 | } |
||
| 77 | |||
| 78 | sdl_window_scale.x = ((float)render_width) / (scalefactor * width); // Pierre-Marie Baty -- scale factor addition |
||
| 79 | sdl_window_scale.y = ((float)render_height) / (scalefactor * height); // Pierre-Marie Baty -- scale factor addition |
||
| 80 | |||
| 81 | if (harness_game_config.start_full_screen) { |
||
| 82 | SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); |
||
| 83 | } |
||
| 84 | |||
| 85 | context = SDL_GL_CreateContext(window); |
||
| 86 | if (!context) { |
||
| 87 | LOG_PANIC("Failed to call SDL_GL_CreateContext. %s", SDL_GetError()); |
||
| 88 | } |
||
| 89 | |||
| 90 | // Load GL extensions using glad |
||
| 91 | if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) { |
||
| 92 | LOG_PANIC("Failed to initialize the OpenGL context with GLAD."); |
||
| 93 | exit(1); |
||
| 94 | } |
||
| 95 | |||
| 96 | GLRenderer_Init(render_width, render_height); |
||
| 97 | update_viewport(); |
||
| 98 | |||
| 99 | return window; |
||
| 100 | } |
||
| 101 | |||
| 102 | static int set_window_pos(void* hWnd, int x, int y, int nWidth, int nHeight) { |
||
| 103 | // SDL_SetWindowPosition(hWnd, x, y); |
||
| 104 | // if (nWidth == 320 && nHeight == 200) { |
||
| 105 | nWidth = scalefactor * nWidth; // Pierre-Marie Baty -- scale factor addition |
||
| 106 | nHeight = scalefactor * nHeight - 1; // Pierre-Marie Baty -- scale factor addition. FIXME: find out why the minus one is necessary. |
||
| 107 | // } |
||
| 108 | SDL_SetWindowSize(hWnd, nWidth, nHeight); |
||
| 109 | return 0; |
||
| 110 | } |
||
| 111 | |||
| 112 | static void destroy_window(void* hWnd) { |
||
| 113 | SDL_GL_DeleteContext(context); |
||
| 114 | SDL_DestroyWindow(window); |
||
| 115 | SDL_Quit(); |
||
| 116 | window = NULL; |
||
| 117 | } |
||
| 118 | |||
| 119 | // Checks whether the `flag_check` is the only modifier applied. |
||
| 120 | // e.g. is_only_modifier(event.key.keysym.mod, KMOD_ALT) returns true when only the ALT key was pressed |
||
| 121 | static int is_only_key_modifier(int modifier_flags, int flag_check) { |
||
| 122 | return (modifier_flags & flag_check) && (modifier_flags & (KMOD_CTRL | KMOD_SHIFT | KMOD_ALT | KMOD_GUI)) == (modifier_flags & flag_check); |
||
| 123 | } |
||
| 124 | |||
| 125 | static int get_and_handle_message(MSG_* msg) { |
||
| 126 | SDL_Event event; |
||
| 127 | int dinput_key; |
||
| 128 | |||
| 129 | while (SDL_PollEvent(&event)) { |
||
| 130 | switch (event.type) { |
||
| 131 | case SDL_KEYDOWN: |
||
| 132 | case SDL_KEYUP: |
||
| 133 | if (event.key.keysym.sym == SDLK_RETURN) { |
||
| 134 | if (event.key.type == SDL_KEYDOWN) { |
||
| 135 | if ((event.key.keysym.mod & (KMOD_CTRL | KMOD_SHIFT | KMOD_ALT | KMOD_GUI))) { |
||
| 136 | // Ignore keydown of RETURN when used together with some modifier |
||
| 137 | return 0; |
||
| 138 | } |
||
| 139 | } else if (event.key.type == SDL_KEYUP) { |
||
| 140 | if (is_only_key_modifier(event.key.keysym.mod, KMOD_ALT)) { |
||
| 141 | SDL_SetWindowFullscreen(window, (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP); |
||
| 142 | } |
||
| 143 | } |
||
| 144 | } |
||
| 145 | |||
| 146 | // Map incoming SDL scancode to DirectInput DIK_* key code. |
||
| 147 | // https://github.com/DanielGibson/Snippets/blob/master/sdl2_scancode_to_dinput.h |
||
| 148 | dinput_key = sdlScanCodeToDirectInputKeyNum[event.key.keysym.scancode]; |
||
| 149 | if (dinput_key == 0) { |
||
| 150 | LOG_WARN("unexpected scan code %s (%d)", SDL_GetScancodeName(event.key.keysym.scancode), event.key.keysym.scancode); |
||
| 151 | return 0; |
||
| 152 | } |
||
| 153 | // DInput expects high bit to be set if key is down |
||
| 154 | // https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ee418261(v=vs.85) |
||
| 155 | directinput_key_state[dinput_key] = (event.type == SDL_KEYDOWN ? 0x80 : 0); |
||
| 156 | break; |
||
| 157 | |||
| 158 | case SDL_WINDOWEVENT: |
||
| 159 | switch (event.window.event) { |
||
| 160 | case SDL_WINDOWEVENT_SIZE_CHANGED: |
||
| 161 | SDL_GetWindowSize(window, &window_width, &window_height); |
||
| 162 | update_viewport(); |
||
| 163 | sdl_window_scale.x = (float)render_width / vp_width; |
||
| 164 | sdl_window_scale.y = (float)render_height / vp_height; |
||
| 165 | break; |
||
| 166 | } |
||
| 167 | break; |
||
| 168 | |||
| 169 | case SDL_QUIT: |
||
| 170 | msg->message = WM_QUIT; |
||
| 171 | return 1; |
||
| 172 | } |
||
| 173 | } |
||
| 174 | return 0; |
||
| 175 | } |
||
| 176 | |||
| 177 | static void swap_window(void) { |
||
| 178 | SDL_GL_SwapWindow(window); |
||
| 179 | } |
||
| 180 | |||
| 181 | static void get_keyboard_state(unsigned int count, uint8_t* buffer) { |
||
| 182 | memcpy(buffer, directinput_key_state, count); |
||
| 183 | } |
||
| 184 | |||
| 185 | static int get_mouse_buttons(int* pButton1, int* pButton2) { |
||
| 186 | int state = SDL_GetMouseState(NULL, NULL); |
||
| 187 | *pButton1 = state & SDL_BUTTON_LMASK; |
||
| 188 | *pButton2 = state & SDL_BUTTON_RMASK; |
||
| 189 | return 0; |
||
| 190 | } |
||
| 191 | |||
| 192 | static int get_mouse_position(int* pX, int* pY) { |
||
| 193 | SDL_GetMouseState(pX, pY); |
||
| 194 | |||
| 195 | if (*pX < vp_x) { |
||
| 196 | *pX = vp_x; |
||
| 197 | } else if (*pX >= vp_x + vp_width) { |
||
| 198 | *pX = vp_x + vp_width - 1; |
||
| 199 | } |
||
| 200 | if (*pY < vp_y) { |
||
| 201 | *pY = vp_y; |
||
| 202 | } else if (*pY >= vp_y + vp_height) { |
||
| 203 | *pY = vp_y + vp_height - 1; |
||
| 204 | } |
||
| 205 | *pX -= vp_x; |
||
| 206 | *pY -= vp_y; |
||
| 207 | *pX *= sdl_window_scale.x; |
||
| 208 | *pY *= sdl_window_scale.y; |
||
| 209 | |||
| 210 | #if defined(DETHRACE_FIX_BUGS) |
||
| 211 | // In hires mode (640x480), the menus are still rendered at (320x240), |
||
| 212 | // so prescale the cursor coordinates accordingly. |
||
| 213 | *pX *= gGraf_specs[gGraf_data_index].phys_width; |
||
| 214 | *pX /= gGraf_specs[gReal_graf_data_index].phys_width; |
||
| 215 | *pY *= gGraf_specs[gGraf_data_index].phys_height; |
||
| 216 | *pY /= gGraf_specs[gReal_graf_data_index].phys_height; |
||
| 217 | #endif |
||
| 218 | return 0; |
||
| 219 | } |
||
| 220 | |||
| 221 | static void set_palette(PALETTEENTRY_* pal) { |
||
| 222 | GLRenderer_SetPalette((uint8_t*)pal); |
||
| 223 | } |
||
| 224 | |||
| 225 | int show_error_message(void* window, char* text, char* caption) { |
||
| 226 | fprintf(stderr, "%s", text); |
||
| 227 | SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, caption, text, window); |
||
| 228 | return 0; |
||
| 229 | } |
||
| 230 | |||
| 231 | void Harness_Platform_Init(tHarness_platform* platform) { |
||
| 232 | platform->ProcessWindowMessages = get_and_handle_message; |
||
| 233 | platform->Sleep = SDL_Delay; |
||
| 234 | platform->GetTicks = SDL_GetTicks; |
||
| 235 | platform->CreateWindowAndRenderer = create_window_and_renderer; |
||
| 236 | platform->ShowCursor = SDL_ShowCursor; |
||
| 237 | platform->SetWindowPos = set_window_pos; |
||
| 238 | platform->SwapWindow = swap_window; |
||
| 239 | platform->DestroyWindow = destroy_window; |
||
| 240 | platform->GetKeyboardState = get_keyboard_state; |
||
| 241 | platform->GetMousePosition = get_mouse_position; |
||
| 242 | platform->GetMouseButtons = get_mouse_buttons; |
||
| 243 | platform->DestroyWindow = destroy_window; |
||
| 244 | platform->ShowErrorMessage = show_error_message; |
||
| 245 | |||
| 246 | platform->Renderer_BufferModel = GLRenderer_BufferModel; |
||
| 247 | platform->Renderer_BufferMaterial = GLRenderer_BufferMaterial; |
||
| 248 | platform->Renderer_BufferTexture = GLRenderer_BufferTexture; |
||
| 249 | platform->Renderer_SetPalette = set_palette; |
||
| 250 | platform->Renderer_FullScreenQuad = GLRenderer_FullScreenQuad; |
||
| 251 | platform->Renderer_Model = GLRenderer_Model; |
||
| 252 | platform->Renderer_ClearBuffers = GLRenderer_ClearBuffers; |
||
| 253 | platform->Renderer_FlushBuffers = GLRenderer_FlushBuffers; |
||
| 254 | platform->Renderer_BeginScene = GLRenderer_BeginScene; |
||
| 255 | platform->Renderer_EndScene = GLRenderer_EndScene; |
||
| 256 | platform->Renderer_SetViewport = GLRenderer_SetViewport; |
||
| 257 | } |