#include "materialsystem/igamewindow.h" #include "tier0/lib.h" #include "tier0/platform.h" #include "tier1/interface.h" #include "tier1/utlvector.h" #define SDL_MAIN_HANDLED #include "SDL3/SDL.h" #include "SDL3/SDL_init.h" #include "SDL3/SDL_video.h" #include "SDL3/SDL_vulkan.h" #include "SDL3/SDL_events.h" class CSDLGameWindow: public IGameWindow { public: virtual void Init() override; virtual void Shutdown() override; virtual void Frame( float fDelta ) override; virtual uint32_t GetRenderWidth() override; virtual uint32_t GetRenderHeight() override; virtual bool BRenderSizeUpdated() override; virtual void SetOutputImage( IImage *pImage ) override; virtual IImage *GetOutputImage() override; virtual void SetKeyCallback( KeyCallbackFn fn ) override; virtual void SetAxisCallback( AxisCallbackFn fn ) override; virtual void *CreateVulkanSurface( void *pInstance ) override; virtual void DestroyVulkanSurface( void *pInstance ) override; SDL_WindowID WindowID(); KeyCallbackFn m_fnKeyCallback = NULL; AxisCallbackFn m_fnAxisCallback = NULL; bool m_bWindowSizeUpdated; uint32_t m_uRenderWidth; uint32_t m_uRenderHeight; private: VkSurfaceKHR m_hSurface; SDL_Window *m_pWindow; IImage *m_pOutputImage; }; void CSDLGameWindow::Init() { m_pWindow = SDL_CreateWindow("funnygame", 1280, 720, SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE); if (!m_pWindow) Plat_FatalErrorFunc("SDL_CreateWindow: %s\n", SDL_GetError()); m_uRenderWidth = 1280; m_uRenderHeight = 720; SDL_SetWindowRelativeMouseMode(m_pWindow, true); } void CSDLGameWindow::Shutdown() { } void CSDLGameWindow::Frame( float fDelta ) { } uint32_t CSDLGameWindow::GetRenderWidth() { return m_uRenderWidth; } uint32_t CSDLGameWindow::GetRenderHeight() { return m_uRenderHeight; } bool CSDLGameWindow::BRenderSizeUpdated() { return m_bWindowSizeUpdated; } void CSDLGameWindow::SetOutputImage( IImage *pImage ) { m_pOutputImage = pImage; } IImage *CSDLGameWindow::GetOutputImage() { return m_pOutputImage; } void CSDLGameWindow::SetKeyCallback( KeyCallbackFn fn ) { m_fnKeyCallback = fn; } void CSDLGameWindow::SetAxisCallback( AxisCallbackFn fn ) { m_fnAxisCallback = fn; } void *CSDLGameWindow::CreateVulkanSurface( void *pInstance ) { SDL_Vulkan_CreateSurface(m_pWindow, (VkInstance)pInstance, NULL, (VkSurfaceKHR*)&m_hSurface); return (void*)m_hSurface; } void CSDLGameWindow::DestroyVulkanSurface( void *pInstance ) { SDL_Vulkan_DestroySurface((VkInstance)pInstance, (VkSurfaceKHR)m_hSurface, NULL); } SDL_WindowID CSDLGameWindow::WindowID() { return SDL_GetWindowID(m_pWindow); } class CSDLGameWindowManager: public IGameWindowManager { public: virtual void Init() override; virtual void Frame( float fDelta ) override; virtual void Shutdown() override; virtual IGameWindow *CreateWindow() override; virtual void DestroyWindow( IGameWindow* pWindow ) override; virtual int GetVulkanInstanceExtensionCount() override; virtual const char **GetVulkanInstanceExtensions() override; private: CUtlVector m_pWindows; }; IGameWindowManager *GameWindowManager() { static CSDLGameWindowManager mgr; return &mgr; } EXPOSE_INTERFACE_FN(GameWindowManager, IGameWindowManager, GAME_WINDOW_MANAGER_INTERFACE_VERSION) void CSDLGameWindowManager::Init() { if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_GAMEPAD)) Plat_FatalErrorFunc("SDL_Init: %s\n", SDL_GetError()); } static EInputButton GetKeyButton( SDL_Keycode eCode ) { switch(eCode) { case SDLK_ESCAPE: return k_EInputButton_ESCAPE; case SDLK_TAB: return k_EInputButton_TAB; case SDLK_RETURN: return k_EInputButton_ENTER; case SDLK_RCTRL: return k_EInputButton_CONTROL; case SDLK_LCTRL: return k_EInputButton_CONTROL; case SDLK_RSHIFT: return k_EInputButton_SHIFT; case SDLK_LSHIFT: return k_EInputButton_SHIFT; case SDLK_RALT: return k_EInputButton_ALT; case SDLK_LALT: return k_EInputButton_ALT; case SDLK_SPACE: return k_EInputButton_SPACE; case SDLK_GRAVE: return k_EInputButton_TILDE; case SDLK_F1: return k_EInputButton_F1; case SDLK_F2: return k_EInputButton_F2; case SDLK_F3: return k_EInputButton_F3; case SDLK_F4: return k_EInputButton_F4; case SDLK_F5: return k_EInputButton_F5; case SDLK_F6: return k_EInputButton_F6; case SDLK_F7: return k_EInputButton_F7; case SDLK_F8: return k_EInputButton_F8; case SDLK_F9: return k_EInputButton_F9; case SDLK_F10: return k_EInputButton_F10; case SDLK_F11: return k_EInputButton_F11; case SDLK_F12: return k_EInputButton_F12; case SDLK_1: return k_EInputButton_1; case SDLK_2: return k_EInputButton_2; case SDLK_3: return k_EInputButton_3; case SDLK_4: return k_EInputButton_4; case SDLK_5: return k_EInputButton_5; case SDLK_6: return k_EInputButton_6; case SDLK_7: return k_EInputButton_7; case SDLK_8: return k_EInputButton_8; case SDLK_9: return k_EInputButton_9; case SDLK_0: return k_EInputButton_0; case SDLK_A: return k_EInputButton_A; case SDLK_B: return k_EInputButton_B; case SDLK_C: return k_EInputButton_C; case SDLK_D: return k_EInputButton_D; case SDLK_E: return k_EInputButton_E; case SDLK_F: return k_EInputButton_F; case SDLK_G: return k_EInputButton_G; case SDLK_H: return k_EInputButton_H; case SDLK_I: return k_EInputButton_I; case SDLK_J: return k_EInputButton_J; case SDLK_K: return k_EInputButton_K; case SDLK_L: return k_EInputButton_L; case SDLK_M: return k_EInputButton_M; case SDLK_N: return k_EInputButton_N; case SDLK_O: return k_EInputButton_O; case SDLK_P: return k_EInputButton_P; case SDLK_Q: return k_EInputButton_Q; case SDLK_R: return k_EInputButton_R; case SDLK_S: return k_EInputButton_S; case SDLK_T: return k_EInputButton_T; case SDLK_U: return k_EInputButton_U; case SDLK_V: return k_EInputButton_V; case SDLK_W: return k_EInputButton_W; case SDLK_X: return k_EInputButton_X; case SDLK_Y: return k_EInputButton_Y; case SDLK_Z: return k_EInputButton_Z; } return k_EInputButton_NONE; } void CSDLGameWindowManager::Frame( float fDelta ) { SDL_Event event; CSDLGameWindow *pWindow; pWindow = NULL; for (auto a: m_pWindows) { a->m_bWindowSizeUpdated = false; } while (SDL_PollEvent(&event)) { for (auto a: m_pWindows) { if (a->WindowID() != event.window.windowID) break; pWindow = a; break; } switch (event.type) { case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: pWindow->m_bWindowSizeUpdated = true; pWindow->m_uRenderWidth = event.window.data1; pWindow->m_uRenderHeight = event.window.data2; break; case SDL_EVENT_KEY_UP: if (pWindow->m_fnKeyCallback) pWindow->m_fnKeyCallback(k_EInputDevice_Keyboard, GetKeyButton(event.key.key), false); break; case SDL_EVENT_KEY_DOWN: if (pWindow->m_fnKeyCallback) pWindow->m_fnKeyCallback(k_EInputDevice_Keyboard, GetKeyButton(event.key.key), true); break; case SDL_EVENT_MOUSE_MOTION: if (pWindow->m_fnAxisCallback) { pWindow->m_fnAxisCallback(k_EInputDevice_Mouse, k_EInputAxis_MouseX, event.motion.xrel); pWindow->m_fnAxisCallback(k_EInputDevice_Mouse, k_EInputAxis_MouseY, event.motion.yrel); } break; case SDL_EVENT_QUIT: Plat_Exit(0); break; default: break; } } } void CSDLGameWindowManager::Shutdown() { } IGameWindow *CSDLGameWindowManager::CreateWindow() { CSDLGameWindow *pWindow = new CSDLGameWindow; m_pWindows.AppendTail(pWindow); return pWindow; } void CSDLGameWindowManager::DestroyWindow( IGameWindow* pWindow ) { delete (CSDLGameWindow*)pWindow; } int CSDLGameWindowManager::GetVulkanInstanceExtensionCount() { uint32_t nCount; SDL_Vulkan_GetInstanceExtensions(&nCount); return nCount; } const char **CSDLGameWindowManager::GetVulkanInstanceExtensions() { uint32_t nCount; return (const char **)SDL_Vulkan_GetInstanceExtensions(&nCount); }