Subversion Repositories Games.Carmageddon

Rev

Blame | Last modification | View Log | Download | RSS feed

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