summary refs log tree commit diff
path: root/src/_WindowBase.h
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/_WindowBase.h
initial commit
Diffstat (limited to 'src/_WindowBase.h')
-rw-r--r--src/_WindowBase.h215
1 files changed, 215 insertions, 0 deletions
diff --git a/src/_WindowBase.h b/src/_WindowBase.h
new file mode 100644
index 0000000..a4cee52
--- /dev/null
+++ b/src/_WindowBase.h
@@ -0,0 +1,215 @@
+#include "Window.h"
+#include "Input.h"
+#include "Event.h"
+#include "Logger.h"
+#include "Platform.h"
+
+struct _DisplayData DisplayInfo;
+struct _WindowData WindowInfo;
+
+#define Display_CentreX(width)  (DisplayInfo.x + (DisplayInfo.Width  - width)  / 2)
+#define Display_CentreY(height) (DisplayInfo.y + (DisplayInfo.Height - height) / 2)
+
+static int cursorPrevX, cursorPrevY;
+static cc_bool cursorVisible = true;
+/* Gets the position of the cursor in screen or window coordinates */
+static void Cursor_GetRawPos(int* x, int* y);
+/* Sets whether the cursor is visible when over this window */
+/* NOTE: You MUST BE VERY CAREFUL with this! OS typically uses a counter for visibility, */
+/*  so setting invisible multiple times means you must then set visible multiple times. */
+static void Cursor_DoSetVisible(cc_bool visible);
+
+/* Sets whether the cursor is visible when over this window */
+static void Cursor_SetVisible(cc_bool visible) {
+	if (cursorVisible == visible) return;
+	cursorVisible = visible;
+	Cursor_DoSetVisible(visible);
+}
+
+static void MoveRawUsingCursorDelta(void) {
+	int x, y;
+	Cursor_GetRawPos(&x, &y);
+	Event_RaiseRawMove(&PointerEvents.RawMoved, x - cursorPrevX, y - cursorPrevY);
+}
+
+static void CentreMousePosition(void) {
+	Cursor_SetPosition(Window_Main.Width / 2, Window_Main.Height / 2);
+	/* Fixes issues with large DPI displays on Windows >= 8.0. */
+	Cursor_GetRawPos(&cursorPrevX, &cursorPrevY);
+}
+
+static void RegrabMouse(void) {
+	if (!Window_Main.Focused || !Window_Main.Exists) return;
+	CentreMousePosition();
+}
+
+static CC_INLINE void DefaultEnableRawMouse(void) {
+	Input.RawMode = true;
+	RegrabMouse();
+	Cursor_SetVisible(false);
+}
+
+static CC_INLINE void DefaultUpdateRawMouse(void) {
+	MoveRawUsingCursorDelta();
+	CentreMousePosition();
+}
+
+static CC_INLINE void DefaultDisableRawMouse(void) {
+	Input.RawMode = false;
+	RegrabMouse();
+	Cursor_SetVisible(true);
+}
+
+/* The actual windowing system specific method to display a message box */
+static void ShowDialogCore(const char* title, const char* msg);
+void Window_ShowDialog(const char* title, const char* msg) {
+	/* Ensure cursor is usable while showing message box */
+	cc_bool rawMode = Input.RawMode;
+
+	if (rawMode) Window_DisableRawMouse();
+	ShowDialogCore(title, msg);
+	if (rawMode) Window_EnableRawMouse();
+}
+
+
+struct GraphicsMode { int R, G, B, A; };
+/* Creates a GraphicsMode compatible with the default display device */
+static CC_INLINE void InitGraphicsMode(struct GraphicsMode* m) {
+	int bpp = DisplayInfo.Depth;
+	m->A = 0;
+
+	switch (bpp) {
+	case 32:
+		m->R =  8; m->G =  8; m->B =  8; m->A = 8; break;
+	case 30:
+		m->R = 10; m->G = 10; m->B = 10; m->A = 2; break;
+	case 24:
+		m->R =  8; m->G =  8; m->B =  8; break;
+	case 16:
+		m->R =  5; m->G =  6; m->B =  5; break;
+	case 15:
+		m->R =  5; m->G =  5; m->B =  5; break;
+	case 8:
+		m->R =  3; m->G =  3; m->B =  2; break;
+	case 4:
+		m->R =  2; m->G =  2; m->B =  1; break;
+	default:
+		/* mode->R = 0; mode->G = 0; mode->B = 0; */
+		Logger_Abort2(bpp, "Unsupported bits per pixel"); break;
+	}
+}
+
+/* EGL is window system agnostic, other OpenGL context backends are tied to one windowing system */
+#if (CC_GFX_BACKEND & CC_GFX_BACKEND_GL_MASK) && defined CC_BUILD_EGL
+/*########################################################################################################################*
+*-------------------------------------------------------EGL OpenGL--------------------------------------------------------*
+*#########################################################################################################################*/
+#include <EGL/egl.h>
+static EGLDisplay ctx_display;
+static EGLContext ctx_context;
+static EGLSurface ctx_surface;
+static EGLConfig ctx_config;
+static EGLint ctx_numConfig;
+
+#ifdef CC_BUILD_SWITCH
+static void GLContext_InitSurface(void); // replacement in Window_Switch.c for handheld/docked resolution fix
+#else
+static void GLContext_InitSurface(void) {
+	void* window = Window_Main.Handle;
+	if (!window) return; /* window not created or lost */
+	ctx_surface = eglCreateWindowSurface(ctx_display, ctx_config, window, NULL);
+
+	if (!ctx_surface) return;
+	eglMakeCurrent(ctx_display, ctx_surface, ctx_surface, ctx_context);
+}
+#endif
+
+static void GLContext_FreeSurface(void) {
+	if (!ctx_surface) return;
+	eglMakeCurrent(ctx_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+	eglDestroySurface(ctx_display, ctx_surface);
+	ctx_surface = NULL;
+}
+
+void GLContext_Create(void) {
+#if CC_GFX_BACKEND == CC_GFX_BACKEND_GL2
+	static EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+#else
+	static EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 1, EGL_NONE };
+#endif
+	static EGLint attribs[] = {
+		EGL_RED_SIZE,  0, EGL_GREEN_SIZE,  0,
+		EGL_BLUE_SIZE, 0, EGL_ALPHA_SIZE,  0,
+		EGL_DEPTH_SIZE,        GLCONTEXT_DEFAULT_DEPTH,
+		EGL_STENCIL_SIZE,      0,
+		EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,
+		EGL_SURFACE_TYPE,      EGL_WINDOW_BIT,
+
+#if defined CC_BUILD_GLES && (CC_GFX_BACKEND == CC_GFX_BACKEND_GL2)
+		EGL_RENDERABLE_TYPE,   EGL_OPENGL_ES2_BIT,
+#elif defined CC_BUILD_GLES
+		EGL_RENDERABLE_TYPE,   EGL_OPENGL_ES_BIT,
+#else
+		EGL_RENDERABLE_TYPE,   EGL_OPENGL_BIT,
+#endif
+		EGL_NONE
+	};
+
+	struct GraphicsMode mode;
+	InitGraphicsMode(&mode);
+	attribs[1] = mode.R; attribs[3] = mode.G;
+	attribs[5] = mode.B; attribs[7] = mode.A;
+
+	ctx_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+	eglInitialize(ctx_display, NULL, NULL);
+	eglBindAPI(EGL_OPENGL_ES_API);
+
+	eglChooseConfig(ctx_display, attribs, &ctx_config, 1, &ctx_numConfig);
+	if (!ctx_config) {
+		attribs[9] = 16; // some older devices only support 16 bit depth buffer
+		eglChooseConfig(ctx_display, attribs, &ctx_config, 1, &ctx_numConfig);
+	}
+	if (!ctx_config) Window_ShowDialog("Warning", "Failed to choose EGL config, ClassiCube may be unable to start");
+
+	ctx_context = eglCreateContext(ctx_display, ctx_config, EGL_NO_CONTEXT, context_attribs);
+	if (!ctx_context) Logger_Abort2(eglGetError(), "Failed to create EGL context");
+	GLContext_InitSurface();
+}
+
+void GLContext_Update(void) {
+	GLContext_FreeSurface();
+	GLContext_InitSurface();
+}
+
+cc_bool GLContext_TryRestore(void) {
+	GLContext_FreeSurface();
+	GLContext_InitSurface();
+	return ctx_surface != NULL;
+}
+
+void GLContext_Free(void) {
+	GLContext_FreeSurface();
+	eglDestroyContext(ctx_display, ctx_context);
+	eglTerminate(ctx_display);
+}
+
+void* GLContext_GetAddress(const char* function) {
+	return eglGetProcAddress(function);
+}
+
+cc_bool GLContext_SwapBuffers(void) {
+	EGLint err;
+	if (!ctx_surface) return false;
+	if (eglSwapBuffers(ctx_display, ctx_surface)) return true;
+
+	err = eglGetError();
+	/* TODO: figure out what errors need to be handled here */
+	Logger_Abort2(err, "Failed to swap buffers");
+	return false;
+}
+
+void GLContext_SetFpsLimit(cc_bool vsync, float minFrameMs) {
+	eglSwapInterval(ctx_display, vsync);
+}
+void GLContext_GetApiInfo(cc_string* info) { }
+#endif