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 | } |