Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
18 | pmbaty | 1 | #include <SDL.h> |
2 | |||
3 | #include "harness.h" |
||
4 | #include "harness/config.h" |
||
5 | #include "harness/hooks.h" |
||
6 | #include "harness/trace.h" |
||
7 | #include "sdl2_scancode_to_dinput.h" |
||
8 | |||
9 | #include "globvars.h" |
||
10 | #include "grafdata.h" |
||
11 | #include "pd/sys.h" |
||
12 | |||
13 | SDL_Window* window; |
||
14 | SDL_Renderer* renderer; |
||
15 | SDL_Texture* screen_texture; |
||
16 | uint32_t converted_palette[256]; |
||
17 | br_pixelmap* last_screen_src; |
||
18 | int render_width, render_height; |
||
19 | int window_width, window_height; |
||
20 | |||
21 | Uint32 last_frame_time; |
||
22 | |||
23 | uint8_t directinput_key_state[SDL_NUM_SCANCODES]; |
||
24 | |||
25 | static void* create_window_and_renderer(char* title, int x, int y, int width, int height) { |
||
26 | window_width = width; |
||
27 | window_height = height; |
||
28 | render_width = width; |
||
29 | render_height = height; |
||
30 | |||
31 | if (SDL_Init(SDL_INIT_VIDEO) != 0) { |
||
32 | LOG_PANIC("SDL_INIT_VIDEO error: %s", SDL_GetError()); |
||
33 | } |
||
34 | |||
35 | window = SDL_CreateWindow(title, |
||
36 | SDL_WINDOWPOS_CENTERED, |
||
37 | SDL_WINDOWPOS_CENTERED, |
||
38 | width, height, |
||
39 | SDL_WINDOW_RESIZABLE); |
||
40 | |||
41 | if (window == NULL) { |
||
42 | LOG_PANIC("Failed to create window: %s", SDL_GetError()); |
||
43 | } |
||
44 | |||
45 | if (harness_game_config.start_full_screen) { |
||
46 | SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); |
||
47 | } |
||
48 | |||
49 | renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC); |
||
50 | if (renderer == NULL) { |
||
51 | LOG_PANIC("Failed to create renderer: %s", SDL_GetError()); |
||
52 | } |
||
53 | SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); |
||
54 | SDL_RenderSetLogicalSize(renderer, render_width, render_height); |
||
55 | |||
56 | screen_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height); |
||
57 | if (screen_texture == NULL) { |
||
58 | SDL_RendererInfo info; |
||
59 | SDL_GetRendererInfo(renderer, &info); |
||
60 | for (Uint32 i = 0; i < info.num_texture_formats; i++) { |
||
61 | LOG_INFO("%s\n", SDL_GetPixelFormatName(info.texture_formats[i])); |
||
62 | } |
||
63 | LOG_PANIC("Failed to create screen_texture: %s", SDL_GetError()); |
||
64 | } |
||
65 | |||
66 | return window; |
||
67 | } |
||
68 | |||
69 | static int set_window_pos(void* hWnd, int x, int y, int nWidth, int nHeight) { |
||
70 | // SDL_SetWindowPosition(hWnd, x, y); |
||
71 | if (nWidth == 320 && nHeight == 200) { |
||
72 | nWidth = 640; |
||
73 | nHeight = 400; |
||
74 | } |
||
75 | SDL_SetWindowSize(hWnd, nWidth, nHeight); |
||
76 | return 0; |
||
77 | } |
||
78 | |||
79 | static void destroy_window(void* hWnd) { |
||
80 | // SDL_GL_DeleteContext(context); |
||
81 | SDL_DestroyWindow(window); |
||
82 | SDL_Quit(); |
||
83 | window = NULL; |
||
84 | } |
||
85 | |||
86 | // Checks whether the `flag_check` is the only modifier applied. |
||
87 | // e.g. is_only_modifier(event.key.keysym.mod, KMOD_ALT) returns true when only the ALT key was pressed |
||
88 | static int is_only_key_modifier(int modifier_flags, int flag_check) { |
||
89 | return (modifier_flags & flag_check) && (modifier_flags & (KMOD_CTRL | KMOD_SHIFT | KMOD_ALT | KMOD_GUI)) == (modifier_flags & flag_check); |
||
90 | } |
||
91 | |||
92 | static int get_and_handle_message(MSG_* msg) { |
||
93 | SDL_Event event; |
||
94 | int dinput_key; |
||
95 | |||
96 | while (SDL_PollEvent(&event)) { |
||
97 | switch (event.type) { |
||
98 | case SDL_KEYDOWN: |
||
99 | case SDL_KEYUP: |
||
100 | if (event.key.keysym.sym == SDLK_RETURN) { |
||
101 | if (event.key.type == SDL_KEYDOWN) { |
||
102 | if ((event.key.keysym.mod & (KMOD_CTRL | KMOD_SHIFT | KMOD_ALT | KMOD_GUI))) { |
||
103 | // Ignore keydown of RETURN when used together with some modifier |
||
104 | return 0; |
||
105 | } |
||
106 | } else if (event.key.type == SDL_KEYUP) { |
||
107 | if (is_only_key_modifier(event.key.keysym.mod, KMOD_ALT)) { |
||
108 | SDL_SetWindowFullscreen(window, (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP); |
||
109 | } |
||
110 | } |
||
111 | } |
||
112 | |||
113 | // Map incoming SDL scancode to DirectInput DIK_* key code. |
||
114 | // https://github.com/DanielGibson/Snippets/blob/master/sdl2_scancode_to_dinput.h |
||
115 | dinput_key = sdlScanCodeToDirectInputKeyNum[event.key.keysym.scancode]; |
||
116 | if (dinput_key == 0) { |
||
117 | LOG_WARN("unexpected scan code %s (%d)", SDL_GetScancodeName(event.key.keysym.scancode), event.key.keysym.scancode); |
||
118 | return 0; |
||
119 | } |
||
120 | // DInput expects high bit to be set if key is down |
||
121 | // https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ee418261(v=vs.85) |
||
122 | directinput_key_state[dinput_key] = (event.type == SDL_KEYDOWN ? 0x80 : 0); |
||
123 | break; |
||
124 | |||
125 | case SDL_QUIT: |
||
126 | msg->message = WM_QUIT; |
||
127 | return 1; |
||
128 | } |
||
129 | } |
||
130 | return 0; |
||
131 | } |
||
132 | |||
133 | static void get_keyboard_state(unsigned int count, uint8_t* buffer) { |
||
134 | memcpy(buffer, directinput_key_state, count); |
||
135 | } |
||
136 | |||
137 | static int get_mouse_buttons(int* pButton1, int* pButton2) { |
||
138 | int state = SDL_GetMouseState(NULL, NULL); |
||
139 | *pButton1 = state & SDL_BUTTON_LMASK; |
||
140 | *pButton2 = state & SDL_BUTTON_RMASK; |
||
141 | return 0; |
||
142 | } |
||
143 | |||
144 | static int get_mouse_position(int* pX, int* pY) { |
||
145 | SDL_GetMouseState(pX, pY); |
||
146 | |||
147 | #if defined(DETHRACE_FIX_BUGS) |
||
148 | // In hires mode (640x480), the menus are still rendered at (320x240), |
||
149 | // so prescale the cursor coordinates accordingly. |
||
150 | *pX *= gGraf_specs[gGraf_data_index].phys_width; |
||
151 | *pX /= gGraf_specs[gReal_graf_data_index].phys_width; |
||
152 | *pY *= gGraf_specs[gGraf_data_index].phys_height; |
||
153 | *pY /= gGraf_specs[gReal_graf_data_index].phys_height; |
||
154 | #endif |
||
155 | return 0; |
||
156 | } |
||
157 | |||
158 | static void limit_fps(void) { |
||
159 | Uint32 now = SDL_GetTicks(); |
||
160 | if (last_frame_time != 0) { |
||
161 | unsigned int frame_time = now - last_frame_time; |
||
162 | last_frame_time = now; |
||
163 | if (frame_time < 100) { |
||
164 | int sleep_time = (1000 / harness_game_config.fps) - frame_time; |
||
165 | if (sleep_time > 5) { |
||
166 | gHarness_platform.Sleep(sleep_time); |
||
167 | } |
||
168 | } |
||
169 | } |
||
170 | last_frame_time = SDL_GetTicks(); |
||
171 | } |
||
172 | |||
173 | static void present_screen(br_pixelmap* src) { |
||
174 | // fastest way to convert 8 bit indexed to 32 bit |
||
175 | uint8_t* src_pixels = src->pixels; |
||
176 | uint32_t* dest_pixels; |
||
177 | int dest_pitch; |
||
178 | |||
179 | SDL_LockTexture(screen_texture, NULL, (void**)&dest_pixels, &dest_pitch); |
||
180 | for (int i = 0; i < src->height * src->width; i++) { |
||
181 | *dest_pixels = converted_palette[*src_pixels]; |
||
182 | dest_pixels++; |
||
183 | src_pixels++; |
||
184 | } |
||
185 | SDL_UnlockTexture(screen_texture); |
||
186 | SDL_RenderClear(renderer); |
||
187 | SDL_RenderCopy(renderer, screen_texture, NULL, NULL); |
||
188 | SDL_RenderPresent(renderer); |
||
189 | |||
190 | last_screen_src = src; |
||
191 | |||
192 | if (harness_game_config.fps != 0) { |
||
193 | limit_fps(); |
||
194 | } |
||
195 | } |
||
196 | |||
197 | static void set_palette(PALETTEENTRY_* pal) { |
||
198 | for (int i = 0; i < 256; i++) { |
||
199 | converted_palette[i] = (0xff << 24 | pal[i].peRed << 16 | pal[i].peGreen << 8 | pal[i].peBlue); |
||
200 | } |
||
201 | if (last_screen_src != NULL) { |
||
202 | present_screen(last_screen_src); |
||
203 | } |
||
204 | } |
||
205 | |||
206 | int show_error_message(void* window, char* text, char* caption) { |
||
207 | fprintf(stderr, "%s", text); |
||
208 | SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, caption, text, window); |
||
209 | return 0; |
||
210 | } |
||
211 | |||
212 | void Harness_Platform_Init(tHarness_platform* platform) { |
||
213 | platform->ProcessWindowMessages = get_and_handle_message; |
||
214 | platform->Sleep = SDL_Delay; |
||
215 | platform->GetTicks = SDL_GetTicks; |
||
216 | platform->CreateWindowAndRenderer = create_window_and_renderer; |
||
217 | platform->ShowCursor = SDL_ShowCursor; |
||
218 | platform->SetWindowPos = set_window_pos; |
||
219 | platform->DestroyWindow = destroy_window; |
||
220 | platform->GetKeyboardState = get_keyboard_state; |
||
221 | platform->GetMousePosition = get_mouse_position; |
||
222 | platform->GetMouseButtons = get_mouse_buttons; |
||
223 | platform->DestroyWindow = destroy_window; |
||
224 | platform->ShowErrorMessage = show_error_message; |
||
225 | platform->Renderer_SetPalette = set_palette; |
||
226 | platform->Renderer_Present = present_screen; |
||
227 | } |