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