summary refs log tree commit diff
path: root/src/Window_SDL.c
diff options
context:
space:
mode:
authorWlodekM <[email protected]>2024-06-16 10:35:45 +0300
committerWlodekM <[email protected]>2024-06-16 10:35:45 +0300
commitabef6da56913f1c55528103e60a50451a39628b1 (patch)
treeb3c8092471ecbb73e568cd0d336efa0e7871ee8d /src/Window_SDL.c
initial commit
Diffstat (limited to 'src/Window_SDL.c')
-rw-r--r--src/Window_SDL.c509
1 files changed, 509 insertions, 0 deletions
diff --git a/src/Window_SDL.c b/src/Window_SDL.c
new file mode 100644
index 0000000..eacd4ac
--- /dev/null
+++ b/src/Window_SDL.c
@@ -0,0 +1,509 @@
+#include "Core.h"
+#if CC_WIN_BACKEND == CC_WIN_BACKEND_SDL2
+#include "_WindowBase.h"
+#include "Graphics.h"
+#include "String.h"
+#include "Funcs.h"
+#include "Bitmap.h"
+#include "Errors.h"
+#include <SDL2/SDL.h>
+
+static SDL_Window* win_handle;
+#warning "Some features are missing from the SDL backend. If possible, it is recommended that you use a native windowing backend instead"
+
+#ifdef CC_BUILD_OS2
+#define INCL_PM
+#include <os2.h>
+// Internal OS/2 driver data
+typedef struct _WINDATA {
+    SDL_Window     *window;
+    void					 *pOutput; /* Video output routines */
+    HWND            hwndFrame;
+    HWND            hwnd;
+    PFNWP           fnUserWndProc;
+    PFNWP           fnWndFrameProc;
+
+    void         		*pVOData; /* Video output data */
+
+    HRGN            hrgnShape;
+    HPOINTER        hptrIcon;
+    RECTL           rectlBeforeFS;
+
+    LONG            lSkipWMSize;
+    LONG            lSkipWMMove;
+    LONG            lSkipWMMouseMove;
+    LONG            lSkipWMVRNEnabled;
+    LONG            lSkipWMAdjustFramePos;
+} WINDATA;
+#endif
+
+
+static void RefreshWindowBounds(void) {
+	SDL_GetWindowSize(win_handle, &Window_Main.Width, &Window_Main.Height);
+}
+
+static void Window_SDLFail(const char* place) {
+	char strBuffer[256];
+	cc_string str;
+	String_InitArray_NT(str, strBuffer);
+
+	String_Format2(&str, "Error when %c: %c", place, SDL_GetError());
+	str.buffer[str.length] = '\0';
+	Logger_Abort(str.buffer);
+}
+
+void Window_PreInit(void) {
+	SDL_Init(SDL_INIT_VIDEO);
+}
+
+void Window_Init(void) {
+	SDL_DisplayMode mode = { 0 };
+	SDL_GetDesktopDisplayMode(0, &mode);
+	Input.Sources = INPUT_SOURCE_NORMAL;
+
+	DisplayInfo.Width  = mode.w;
+	DisplayInfo.Height = mode.h;
+	DisplayInfo.Depth  = SDL_BITSPERPIXEL(mode.format);
+	DisplayInfo.ScaleX = 1;
+	DisplayInfo.ScaleY = 1;
+}
+
+void Window_Free(void) { }
+
+
+#ifdef CC_BUILD_ICON
+/* See misc/sdl/sdl_icon_gen.cs for how to generate this file */
+#include "../misc/sdl/CCIcon_SDL.h"
+
+static void ApplyIcon(void) {
+	SDL_Surface* surface = SDL_CreateRGBSurfaceFrom(CCIcon_Data, CCIcon_Width, CCIcon_Height, 32, CCIcon_Pitch,
+													0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
+	SDL_SetWindowIcon(win_handle, surface);
+}
+#else
+static void ApplyIcon(void) { }
+#endif
+
+static void DoCreateWindow(int width, int height, int flags) {
+	win_handle = SDL_CreateWindow(NULL, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, 
+					flags | SDL_WINDOW_RESIZABLE);
+	if (!win_handle) Window_SDLFail("creating window");
+
+	RefreshWindowBounds();
+	Window_Main.Exists = true;
+	Window_Main.Handle = win_handle;
+	ApplyIcon();
+	/* TODO grab using SDL_SetWindowGrab? seems to be unnecessary on Linux at least */
+}
+
+void Window_Create2D(int width, int height) { DoCreateWindow(width, height, 0); }
+#if (CC_GFX_BACKEND & CC_GFX_BACKEND_GL_MASK)
+void Window_Create3D(int width, int height) { DoCreateWindow(width, height, SDL_WINDOW_OPENGL); }
+#else
+void Window_Create3D(int width, int height) { DoCreateWindow(width, height, 0); }
+#endif
+
+void Window_SetTitle(const cc_string* title) {
+	char str[NATIVE_STR_LEN];
+	String_EncodeUtf8(str, title);
+	SDL_SetWindowTitle(win_handle, str);
+}
+
+void Clipboard_GetText(cc_string* value) {
+	char* ptr = SDL_GetClipboardText();
+	if (!ptr) return;
+
+	int len = String_Length(ptr);
+	String_AppendUtf8(value, ptr, len);
+	SDL_free(ptr);
+}
+
+void Clipboard_SetText(const cc_string* value) {
+	char str[NATIVE_STR_LEN];
+	String_EncodeUtf8(str, value);
+	SDL_SetClipboardText(str);
+}
+
+int Window_GetWindowState(void) {
+	Uint32 flags = SDL_GetWindowFlags(win_handle);
+
+	if (flags & SDL_WINDOW_MINIMIZED)          return WINDOW_STATE_MINIMISED;
+	if (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) return WINDOW_STATE_FULLSCREEN;
+	return WINDOW_STATE_NORMAL;
+}
+
+cc_result Window_EnterFullscreen(void) {
+	return SDL_SetWindowFullscreen(win_handle, SDL_WINDOW_FULLSCREEN_DESKTOP);
+}
+cc_result Window_ExitFullscreen(void) { SDL_RestoreWindow(win_handle); return 0; }
+
+int Window_IsObscured(void) { return 0; }
+
+void Window_Show(void) { SDL_ShowWindow(win_handle); }
+
+void Window_SetSize(int width, int height) {
+	SDL_SetWindowSize(win_handle, width, height);
+}
+
+void Window_RequestClose(void) {
+	SDL_Event e;
+	e.type = SDL_QUIT;
+	SDL_PushEvent(&e);
+}
+
+static int MapNativeKey(SDL_Keycode k) {
+	if (k >= SDLK_0   && k <= SDLK_9)   { return '0'     + (k - SDLK_0); }
+	if (k >= SDLK_a   && k <= SDLK_z)   { return 'A'     + (k - SDLK_a); }
+	if (k >= SDLK_F1  && k <= SDLK_F12) { return CCKEY_F1  + (k - SDLK_F1); }
+	if (k >= SDLK_F13 && k <= SDLK_F24) { return CCKEY_F13 + (k - SDLK_F13); }
+	/* SDLK_KP_0 isn't before SDLK_KP_1 */
+	if (k >= SDLK_KP_1 && k <= SDLK_KP_9) { return CCKEY_KP1 + (k - SDLK_KP_1); }
+
+	switch (k) {
+		case SDLK_RETURN: return CCKEY_ENTER;
+		case SDLK_ESCAPE: return CCKEY_ESCAPE;
+		case SDLK_BACKSPACE: return CCKEY_BACKSPACE;
+		case SDLK_TAB:    return CCKEY_TAB;
+		case SDLK_SPACE:  return CCKEY_SPACE;
+		case SDLK_QUOTE:  return CCKEY_QUOTE;
+		case SDLK_EQUALS: return CCKEY_EQUALS;
+		case SDLK_COMMA:  return CCKEY_COMMA;
+		case SDLK_MINUS:  return CCKEY_MINUS;
+		case SDLK_PERIOD: return CCKEY_PERIOD;
+		case SDLK_SLASH:  return CCKEY_SLASH;
+		case SDLK_SEMICOLON:    return CCKEY_SEMICOLON;
+		case SDLK_LEFTBRACKET:  return CCKEY_LBRACKET;
+		case SDLK_BACKSLASH:    return CCKEY_BACKSLASH;
+		case SDLK_RIGHTBRACKET: return CCKEY_RBRACKET;
+		case SDLK_BACKQUOTE:    return CCKEY_TILDE;
+		case SDLK_CAPSLOCK:     return CCKEY_CAPSLOCK;
+		case SDLK_PRINTSCREEN: return CCKEY_PRINTSCREEN;
+		case SDLK_SCROLLLOCK:  return CCKEY_SCROLLLOCK;
+		case SDLK_PAUSE:       return CCKEY_PAUSE;
+		case SDLK_INSERT:   return CCKEY_INSERT;
+		case SDLK_HOME:     return CCKEY_HOME;
+		case SDLK_PAGEUP:   return CCKEY_PAGEUP;
+		case SDLK_DELETE:   return CCKEY_DELETE;
+		case SDLK_END:      return CCKEY_END;
+		case SDLK_PAGEDOWN: return CCKEY_PAGEDOWN;
+		case SDLK_RIGHT: return CCKEY_RIGHT;
+		case SDLK_LEFT:  return CCKEY_LEFT;
+		case SDLK_DOWN:  return CCKEY_DOWN;
+		case SDLK_UP:    return CCKEY_UP;
+
+		case SDLK_NUMLOCKCLEAR: return CCKEY_NUMLOCK;
+		case SDLK_KP_DIVIDE: return CCKEY_KP_DIVIDE;
+		case SDLK_KP_MULTIPLY: return CCKEY_KP_MULTIPLY;
+		case SDLK_KP_MINUS: return CCKEY_KP_MINUS;
+		case SDLK_KP_PLUS: return CCKEY_KP_PLUS;
+		case SDLK_KP_ENTER: return CCKEY_KP_ENTER;
+		case SDLK_KP_0: return CCKEY_KP0;
+		case SDLK_KP_PERIOD: return CCKEY_KP_DECIMAL;
+
+		case SDLK_LCTRL: return CCKEY_LCTRL;
+		case SDLK_LSHIFT: return CCKEY_LSHIFT;
+		case SDLK_LALT: return CCKEY_LALT;
+		case SDLK_LGUI: return CCKEY_LWIN;
+		case SDLK_RCTRL: return CCKEY_RCTRL;
+		case SDLK_RSHIFT: return CCKEY_RSHIFT;
+		case SDLK_RALT: return CCKEY_RALT;
+		case SDLK_RGUI: return CCKEY_RWIN;
+		
+		case SDLK_AUDIONEXT: return CCKEY_MEDIA_NEXT;
+		case SDLK_AUDIOPREV: return CCKEY_MEDIA_PREV;
+		case SDLK_AUDIOPLAY: return CCKEY_MEDIA_PLAY;
+		case SDLK_AUDIOSTOP: return CCKEY_MEDIA_STOP;
+		
+		case SDLK_AUDIOMUTE:  return CCKEY_VOLUME_MUTE;
+		case SDLK_VOLUMEDOWN: return CCKEY_VOLUME_DOWN;
+		case SDLK_VOLUMEUP:   return CCKEY_VOLUME_UP;
+	}
+	return INPUT_NONE;
+}
+
+static void OnKeyEvent(const SDL_Event* e) {
+	cc_bool pressed = e->key.state == SDL_PRESSED;
+	int key = MapNativeKey(e->key.keysym.sym);
+	if (key) Input_Set(key, pressed);
+}
+
+static void OnMouseEvent(const SDL_Event* e) {
+	cc_bool pressed = e->button.state == SDL_PRESSED;
+	int btn;
+	switch (e->button.button) {
+		case SDL_BUTTON_LEFT:   btn = CCMOUSE_L; break;
+		case SDL_BUTTON_MIDDLE: btn = CCMOUSE_M; break;
+		case SDL_BUTTON_RIGHT:  btn = CCMOUSE_R; break;
+		case SDL_BUTTON_X1:     btn = CCMOUSE_X1; break;
+		case SDL_BUTTON_X2:     btn = CCMOUSE_X2; break;
+		default: return;
+	}
+	Input_Set(btn, pressed);
+}
+
+static void OnTextEvent(const SDL_Event* e) {
+	cc_codepoint cp;
+	const char* src;
+	int i, len;
+
+	src = e->text.text;
+	len = String_CalcLen(src, SDL_TEXTINPUTEVENT_TEXT_SIZE);
+
+	while (len > 0) {
+		i = Convert_Utf8ToCodepoint(&cp, src, len);
+		if (!i) break;
+
+		Event_RaiseInt(&InputEvents.Press, cp);
+		src += i; len -= i;
+	}
+}
+
+static void OnWindowEvent(const SDL_Event* e) {
+	switch (e->window.event) {
+		case SDL_WINDOWEVENT_EXPOSED:
+			Event_RaiseVoid(&WindowEvents.RedrawNeeded);
+			break;
+		case SDL_WINDOWEVENT_SIZE_CHANGED:
+			RefreshWindowBounds();
+			Event_RaiseVoid(&WindowEvents.Resized);
+			break;
+		case SDL_WINDOWEVENT_MINIMIZED:
+		case SDL_WINDOWEVENT_MAXIMIZED:
+		case SDL_WINDOWEVENT_RESTORED:
+			Event_RaiseVoid(&WindowEvents.StateChanged);
+			break;
+		case SDL_WINDOWEVENT_FOCUS_GAINED:
+			Window_Main.Focused = true;
+			Event_RaiseVoid(&WindowEvents.FocusChanged);
+			break;
+		case SDL_WINDOWEVENT_FOCUS_LOST:
+			Window_Main.Focused = false;
+			Event_RaiseVoid(&WindowEvents.FocusChanged);
+			break;
+		case SDL_WINDOWEVENT_CLOSE:
+			Window_RequestClose();
+			break;
+		}
+}
+
+void Window_ProcessEvents(float delta) {
+	SDL_Event e;
+	while (SDL_PollEvent(&e)) {
+		switch (e.type) {
+
+		case SDL_KEYDOWN:
+		case SDL_KEYUP:
+			OnKeyEvent(&e); break;
+		case SDL_MOUSEBUTTONDOWN:
+		case SDL_MOUSEBUTTONUP:
+			OnMouseEvent(&e); break;
+		case SDL_MOUSEWHEEL:
+			Mouse_ScrollHWheel(e.wheel.x);
+			Mouse_ScrollVWheel(e.wheel.y);
+			break;
+		case SDL_MOUSEMOTION:
+			Pointer_SetPosition(0, e.motion.x, e.motion.y);
+			if (Input.RawMode) Event_RaiseRawMove(&PointerEvents.RawMoved, e.motion.xrel, e.motion.yrel);
+			break;
+		case SDL_TEXTINPUT:
+			OnTextEvent(&e); break;
+		case SDL_WINDOWEVENT:
+			OnWindowEvent(&e); break;
+
+		case SDL_QUIT:
+			Window_Main.Exists = false;
+			Event_RaiseVoid(&WindowEvents.Closing);
+			SDL_DestroyWindow(win_handle);
+			break;
+
+		case SDL_RENDER_DEVICE_RESET:
+			Gfx_LoseContext("SDL device reset event");
+			Gfx_RecreateContext();
+			break;
+		}
+	}
+}
+
+void Window_ProcessGamepads(float delta) { }
+
+static void Cursor_GetRawPos(int* x, int* y) {
+	SDL_GetMouseState(x, y);
+}
+void Cursor_SetPosition(int x, int y) {
+	SDL_WarpMouseInWindow(win_handle, x, y);
+}
+
+static void Cursor_DoSetVisible(cc_bool visible) {
+	SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE);
+}
+
+static void ShowDialogCore(const char* title, const char* msg) {
+	SDL_ShowSimpleMessageBox(0, title, msg, win_handle);
+}
+
+cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args) {
+#if defined CC_BUILD_OS2
+	FILEDLG fileDialog;
+	HWND hDialog;
+
+	memset(&fileDialog, 0, sizeof(FILEDLG));
+	fileDialog.cbSize = sizeof(FILEDLG);
+	fileDialog.fl = FDS_HELPBUTTON | FDS_CENTER | FDS_PRELOAD_VOLINFO | FDS_OPEN_DIALOG;
+	fileDialog.pszTitle = args->description;
+	fileDialog.pszOKButton = NULL;
+	fileDialog.pfnDlgProc = WinDefFileDlgProc;
+
+	Mem_Copy(fileDialog.szFullFile, *args->filters, CCHMAXPATH);
+	hDialog = WinFileDlg(HWND_DESKTOP, 0, &fileDialog);
+	if (fileDialog.lReturn == DID_OK) {
+		cc_string temp = String_FromRaw(fileDialog.szFullFile, CCHMAXPATH); 
+		args->Callback(&temp);
+	}
+	
+	return 0;
+#else
+	return ERR_NOT_SUPPORTED;
+#endif
+}
+
+cc_result Window_SaveFileDialog(const struct SaveFileDialogArgs* args) {
+#if defined CC_BUILD_OS2
+	FILEDLG fileDialog;
+	HWND hDialog;
+
+	memset(&fileDialog, 0, sizeof(FILEDLG));
+	fileDialog.cbSize = sizeof(FILEDLG);
+	fileDialog.fl = FDS_HELPBUTTON | FDS_CENTER | FDS_PRELOAD_VOLINFO | FDS_SAVEAS_DIALOG;
+	fileDialog.pszTitle = args->titles;
+	fileDialog.pszOKButton = NULL;
+	fileDialog.pfnDlgProc = WinDefFileDlgProc;
+
+	Mem_Copy(fileDialog.szFullFile, *args->filters, CCHMAXPATH);
+	hDialog = WinFileDlg(HWND_DESKTOP, 0, &fileDialog);
+	if (fileDialog.lReturn == DID_OK) {
+		cc_string temp = String_FromRaw(fileDialog.szFullFile, CCHMAXPATH);
+		args->Callback(&temp);
+	}
+	
+	return 0;
+#else
+	return ERR_NOT_SUPPORTED;
+#endif
+}
+
+static SDL_Surface* win_surface;
+static SDL_Surface* blit_surface;
+
+void Window_AllocFramebuffer(struct Bitmap* bmp, int width, int height) {
+	SDL_PixelFormat* fmt;
+	win_surface = SDL_GetWindowSurface(win_handle);
+	if (!win_surface) Window_SDLFail("getting window surface");
+
+	fmt = win_surface->format;
+	if (fmt->BitsPerPixel != 32) {
+		/* Slow path: e.g. 15 or 16 bit pixels */
+		Platform_Log1("Slow color depth: %b bpp", &fmt->BitsPerPixel);
+		blit_surface = SDL_CreateRGBSurface(0, win_surface->w, win_surface->h, 32, 0, 0, 0, 0);
+		if (!blit_surface) Window_SDLFail("creating blit surface");
+
+		SDL_SetSurfaceBlendMode(blit_surface, SDL_BLENDMODE_NONE);
+		bmp->scan0 = blit_surface->pixels;
+	} else {
+		/* Fast path: 32 bit pixels */
+		if (SDL_MUSTLOCK(win_surface)) {
+			int ret = SDL_LockSurface(win_surface);
+			if (ret < 0) Window_SDLFail("locking window surface");
+		}
+		bmp->scan0 = win_surface->pixels;
+	}
+	/* TODO proper stride */
+	bmp->width  = width;
+	bmp->height = height;
+}
+
+void Window_DrawFramebuffer(Rect2D r, struct Bitmap* bmp) {
+	SDL_Rect rect;
+	rect.x = r.x; rect.w = r.width;
+	rect.y = r.y; rect.h = r.height;
+
+	if (blit_surface) SDL_BlitSurface(blit_surface, &rect, win_surface, &rect);
+	SDL_UpdateWindowSurfaceRects(win_handle, &rect, 1);
+}
+
+void Window_FreeFramebuffer(struct Bitmap* bmp) {
+	if (blit_surface) SDL_FreeSurface(blit_surface);
+	blit_surface = NULL;
+
+	/* SDL docs explicitly say to NOT free window surface */
+	/* https://wiki.libsdl.org/SDL_GetWindowSurface */
+	/* TODO: Do we still need to unlock it though? */
+}
+
+void OnscreenKeyboard_Open(struct OpenKeyboardArgs* args) { SDL_StartTextInput(); }
+void OnscreenKeyboard_SetText(const cc_string* text) { }
+void OnscreenKeyboard_Draw2D(Rect2D* r, struct Bitmap* bmp) { }
+void OnscreenKeyboard_Draw3D(void) { }
+void OnscreenKeyboard_Close(void) { SDL_StopTextInput(); }
+
+void Window_EnableRawMouse(void) {
+	RegrabMouse();
+	SDL_SetRelativeMouseMode(true);
+	Input.RawMode = true;
+}
+void Window_UpdateRawMouse(void) { CentreMousePosition(); }
+
+void Window_DisableRawMouse(void) {
+	RegrabMouse();
+	SDL_SetRelativeMouseMode(false);
+	Input.RawMode = false;
+}
+
+
+/*########################################################################################################################*
+*-----------------------------------------------------OpenGL context------------------------------------------------------*
+*#########################################################################################################################*/
+#if (CC_GFX_BACKEND & CC_GFX_BACKEND_GL_MASK) && !defined CC_BUILD_EGL
+static SDL_GLContext win_ctx;
+
+void GLContext_Create(void) {
+	struct GraphicsMode mode;
+	InitGraphicsMode(&mode);
+	SDL_GL_SetAttribute(SDL_GL_RED_SIZE,   mode.R);
+	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, mode.G);
+	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,  mode.B);
+	SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, mode.A);
+
+	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,   GLCONTEXT_DEFAULT_DEPTH);
+	SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);
+	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, true);
+#ifdef CC_BUILD_GLES
+	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
+#endif
+
+	win_ctx = SDL_GL_CreateContext(win_handle);
+	if (!win_ctx) Window_SDLFail("creating OpenGL context");
+}
+
+void GLContext_Update(void) { }
+cc_bool GLContext_TryRestore(void) { return true; }
+void GLContext_Free(void) {
+	SDL_GL_DeleteContext(win_ctx);
+	win_ctx = NULL;
+}
+
+void* GLContext_GetAddress(const char* function) {
+	return SDL_GL_GetProcAddress(function);
+}
+
+cc_bool GLContext_SwapBuffers(void) {
+	SDL_GL_SwapWindow(win_handle);
+	return true;
+}
+
+void GLContext_SetFpsLimit(cc_bool vsync, float minFrameMs) {
+	SDL_GL_SetSwapInterval(vsync);
+}
+void GLContext_GetApiInfo(cc_string* info) { }
+
+#endif
+#endif