summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/egl_common.c41
-rw-r--r--src/egl_common.h23
-rw-r--r--src/gl_common.c46
-rw-r--r--src/gl_common.h22
-rw-r--r--src/gl_versions.h2
-rw-r--r--src/poll_common.c69
-rw-r--r--src/poll_common.h16
-rw-r--r--src/sekihi.c13
-rw-r--r--src/window.c78
-rw-r--r--src/window.h56
-rw-r--r--src/window_glfw.c105
-rw-r--r--src/window_glfw.h2
-rw-r--r--src/window_wayland.c672
-rw-r--r--src/window_wayland.h37
-rw-r--r--src/window_x11.c342
-rw-r--r--src/window_x11.h23
16 files changed, 1224 insertions, 323 deletions
diff --git a/src/egl_common.c b/src/egl_common.c
index eeaf501..44c2ccb 100644
--- a/src/egl_common.c
+++ b/src/egl_common.c
@@ -62,7 +62,7 @@ out:
return index != -1;
}
-bool skh_load_egl(struct skh_egl *egl)
+bool skh_maybe_load_egl(struct skh_egl *egl)
{
(void)egl;
#ifdef SEKIHI_USE_GLAD
@@ -82,6 +82,7 @@ bool skh_init_egl(struct skh_egl *egl, EGLint visual_id, EGLConfig *config)
al_log_error("egl", "Failed to initialize.");
return false;
}
+ al_log_info("egl", "Initialized EGL version %i.%i.", major, minor);
#ifdef SEKIHI_USE_GLAD
s32 egl_version = gladLoaderLoadEGL(egl->display);
@@ -91,7 +92,7 @@ bool skh_init_egl(struct skh_egl *egl, EGLint visual_id, EGLConfig *config)
}
#endif
- al_log_info("egl", "Initialized version %i.%i.", major, minor);
+ al_log_info("egl", "Using EGL (glad) version %s.", eglQueryString(egl->display, EGL_VERSION));
egl->context = EGL_NO_CONTEXT;
@@ -129,7 +130,7 @@ bool skh_init_egl(struct skh_egl *egl, EGLint visual_id, EGLConfig *config)
return false;
}
- al_log_info("egl", "Trying to make a %s context of version %i.%i (GLSL %i).",
+ al_log_info("egl", "Trying to create a %s context of version %i.%i (GLSL %i).",
skh_gl_vers[i].api == SEKIHI_GL_API ? "GL" : "GLES",
skh_gl_vers[i].major, skh_gl_vers[i].minor, skh_gl_vers[i].glsl_ver);
@@ -165,7 +166,7 @@ void skh_egl_release_current(struct skh_egl *egl)
void skh_egl_swap_interval(struct skh_egl *egl, s32 interval)
{
- eglSwapInterval(&egl->display, interval);
+ eglSwapInterval(egl->display, interval);
}
void skh_egl_swap_buffers(struct skh_egl *egl)
@@ -193,3 +194,35 @@ void skh_egl_terminate(struct skh_egl *egl)
{
eglTerminate(egl->display);
}
+
+#define ERROR_CASE_STR(value) case value: return #value;
+static const char *eglGetErrorString(EGLint error)
+{
+ switch(error) {
+ ERROR_CASE_STR(EGL_SUCCESS)
+ ERROR_CASE_STR(EGL_NOT_INITIALIZED)
+ ERROR_CASE_STR(EGL_BAD_ACCESS)
+ ERROR_CASE_STR(EGL_BAD_ALLOC)
+ ERROR_CASE_STR(EGL_BAD_ATTRIBUTE)
+ ERROR_CASE_STR(EGL_BAD_CONTEXT)
+ ERROR_CASE_STR(EGL_BAD_CONFIG)
+ ERROR_CASE_STR(EGL_BAD_CURRENT_SURFACE)
+ ERROR_CASE_STR(EGL_BAD_DISPLAY)
+ ERROR_CASE_STR(EGL_BAD_SURFACE)
+ ERROR_CASE_STR(EGL_BAD_MATCH)
+ ERROR_CASE_STR(EGL_BAD_PARAMETER)
+ ERROR_CASE_STR(EGL_BAD_NATIVE_PIXMAP)
+ ERROR_CASE_STR(EGL_BAD_NATIVE_WINDOW)
+ ERROR_CASE_STR(EGL_CONTEXT_LOST)
+ default: return "Unknown";
+ }
+}
+#undef ERROR_CASE_STR
+
+void skh_egl_check_error()
+{
+ EGLint error;
+ while ((error = eglGetError()) != EGL_SUCCESS) {
+ al_log_error("egl", "EGL error %s, %d.", eglGetErrorString(error), error);
+ }
+}
diff --git a/src/egl_common.h b/src/egl_common.h
index 4a30000..e021ea6 100644
--- a/src/egl_common.h
+++ b/src/egl_common.h
@@ -1,32 +1,21 @@
#pragma once
-#include <al/types.h>
-
-#define SEKIHI_USE_GLAD
-#define SEKIHI_USE_GLES
+#include "gl_common.h"
#ifdef SEKIHI_USE_GLAD
#include <glad/egl.h>
-#ifdef SEKIHI_USE_GLES
-#include <glad/gles2.h>
-#else
-#include <glad/gl.h>
-#endif
#else
-#ifdef SEKIHI_USE_GLES
-#error "Only GLES is supported in this configuration"
-#endif
#include <EGL/egl.h>
-#include <GLES2/gl2.h>
+#include <EGL/eglext.h>
#endif
struct skh_egl {
- EGLDisplay *display;
- EGLSurface *surface;
+ EGLDisplay display;
+ EGLSurface surface;
EGLContext context;
};
-bool skh_load_egl(struct skh_egl *egl);
+bool skh_maybe_load_egl(struct skh_egl *egl);
bool skh_init_egl(struct skh_egl *egl, EGLint visual_id, EGLConfig *config);
void skh_egl_make_current(struct skh_egl *egl);
void skh_egl_release_current(struct skh_egl *egl);
@@ -36,3 +25,5 @@ bool skh_egl_is_ready(struct skh_egl *egl);
void skh_egl_destroy_surface(struct skh_egl *egl);
void skh_egl_destroy_context(struct skh_egl *egl);
void skh_egl_terminate(struct skh_egl *egl);
+
+void skh_egl_check_error();
diff --git a/src/gl_common.c b/src/gl_common.c
new file mode 100644
index 0000000..2a2fd53
--- /dev/null
+++ b/src/gl_common.c
@@ -0,0 +1,46 @@
+#include <al/lib.h>
+#include <al/log.h>
+
+#include "gl_common.h"
+
+// https://stackoverflow.com/a/49236965
+#define ERROR_CASE_STR(value) case value: return #value;
+static const char *glGetErrorString(GLenum error)
+{
+ switch(error) {
+ ERROR_CASE_STR(GL_INVALID_ENUM)
+ ERROR_CASE_STR(GL_INVALID_VALUE)
+ ERROR_CASE_STR(GL_INVALID_OPERATION)
+ ERROR_CASE_STR(GL_INVALID_FRAMEBUFFER_OPERATION)
+ ERROR_CASE_STR(GL_OUT_OF_MEMORY)
+ default: return "Unknown";
+ }
+}
+#undef ERROR_CASE_STR
+
+void skh_gl_check_error()
+{
+ GLenum error;
+ while ((error = glGetError()) != GL_NO_ERROR) {
+ al_log_error("egl", "GL error %s, %d.", glGetErrorString(error), error);
+ }
+}
+
+bool skh_gl_loader_load(void (*(*get_proc_address)(const char *))(void))
+{
+#ifdef SEKIHI_USE_GLAD
+ al_assert(get_proc_address);
+#ifdef SEKIHI_USE_GLES
+ if (!gladLoadGLES2((GLADloadfunc)get_proc_address)) {
+ return false;
+ }
+#else
+ if (!gladLoadGL((GLADloadfunc)get_proc_address)) {
+ return false;
+ }
+#endif
+#else
+ (void)get_proc_address;
+#endif
+ return true;
+}
diff --git a/src/gl_common.h b/src/gl_common.h
new file mode 100644
index 0000000..cb15a42
--- /dev/null
+++ b/src/gl_common.h
@@ -0,0 +1,22 @@
+//#define SEKIHI_USE_GLAD
+//#define SEKIHI_USE_GLES
+
+#include <al/types.h>
+
+#ifdef SEKIHI_USE_GLAD
+#ifdef SEKIHI_USE_GLES
+#include <glad/gles2.h>
+#else
+#include <glad/gl.h>
+#endif
+#else
+#ifdef SEKIHI_USE_GLES
+#include <GLES2/gl2.h>
+#else
+#include <GL/gl.h>
+#include <GL/glext.h>
+#endif
+#endif
+
+void skh_gl_check_error();
+bool skh_gl_loader_load(void (*(*get_proc_address)(const char *))(void));
diff --git a/src/gl_versions.h b/src/gl_versions.h
index 5b9bfc2..c90f319 100644
--- a/src/gl_versions.h
+++ b/src/gl_versions.h
@@ -2,7 +2,7 @@
#include "egl_common.h"
-// https://github.com/haasn/libplacebo/blob/master/demos/window_glfw.c#L178
+// https://github.com/haasn/libplacebo/blob/795600a44b03fcd52c055981a403ad60ee5d027a/demos/window_glfw.c#L194
#ifdef SEKIHI_USE_GLES
#define PASTE_GL_VERSIONS() static struct { \
s32 api; \
diff --git a/src/poll_common.c b/src/poll_common.c
new file mode 100644
index 0000000..1d18f84
--- /dev/null
+++ b/src/poll_common.c
@@ -0,0 +1,69 @@
+#include <al/log.h>
+#include <aki/common.h>
+
+#include "poll_common.h"
+
+#if defined SEKIHI_POLL_INLINE
+bool skh_poll_common(struct aki_pollfd *fds, bool block)
+{
+ fds[0].revents = 0;
+#if defined SEKIHI_PAUSE
+ fds[1].revents = 0;
+#endif
+
+#if defined SEKIHI_PAUSE
+ if (aki_poll_fds(fds, 2, (block) ? -1 : 0) < 0 && errno != EINTR) {
+#else
+ if (aki_poll_fds(fds, 1, (block) ? -1 : 0) < 0 && errno != EINTR) {
+#endif
+ al_log_error("poll_common", "poll() failed (%s).", errno, aki_strerror(errno));
+ return false;
+ }
+
+ if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { // Fatal error.
+ al_log_error("poll_common", "poll() returned error in revents.");
+ return false;
+ }
+
+ return true;
+}
+#if defined SEKIHI_PAUSE
+void skh_wake_common(struct skh_window *window, struct aki_pollfd *fds)
+{
+ window->pending_refresh = true;
+ // If we are already in poll, don't try to wake again instead
+ // just ensure we refresh.
+ if (!(fds[0].revents & POLLIN || fds[1].revents & POLLIN)) {
+ s64 val = 1;
+ ssize_t ret = aki_write(fds[1].fd, &val, sizeof(s64));
+ al_assert(ret == sizeof(s64));
+ }
+}
+#endif
+#endif
+
+bool skh_process_events_common(struct aki_pollfd *fds)
+{
+#if defined SEKIHI_POLL_INLINE
+#if defined SEKIHI_PAUSE
+ if (fds[1].revents & POLLIN) {
+ s64 val = aki_read(fds[1].fd, &val, sizeof(s64));
+ al_assert(val == sizeof(s64));
+ }
+#endif
+ return (fds[0].revents & POLLIN);
+#elif defined SEKIHI_POLL_FD
+ return true;
+#endif
+}
+
+#if defined SEKIHI_PAUSE
+bool skh_should_refresh_common(struct skh_window *window)
+{
+ if (window->pending_refresh) {
+ window->pending_refresh = false;
+ return true;
+ }
+ return false;
+}
+#endif
diff --git a/src/poll_common.h b/src/poll_common.h
new file mode 100644
index 0000000..69dc536
--- /dev/null
+++ b/src/poll_common.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <aki/socket.h>
+
+#include "window.h"
+
+#if defined SEKIHI_POLL_INLINE
+bool skh_poll_common(struct aki_pollfd *fds, bool block);
+#if defined SEKIHI_PAUSE
+void skh_wake_common(struct skh_window *window, struct aki_pollfd *fds);
+#endif
+#endif
+bool skh_process_events_common(struct aki_pollfd *fds);
+#if defined SEKIHI_PAUSE
+bool skh_should_refresh_common(struct skh_window *window);
+#endif
diff --git a/src/sekihi.c b/src/sekihi.c
index 04e7a89..11f1982 100644
--- a/src/sekihi.c
+++ b/src/sekihi.c
@@ -2,18 +2,23 @@
#include "window.h"
-#if defined SEKIHI_WINDOW_GLFW
-#include "window_glfw.h"
-#elif defined SEKIHI_WINDOW_WAYLAND
+#if defined SEKIHI_WINDOW_WAYLAND
#include "window_wayland.h"
+#elif defined SEKIHI_WINDOW_X11
+#include "window_x11.h"
+#elif defined SEKIHI_WINDOW_GLFW
+#include "window_glfw.h"
#endif
-#if defined SEKIHI_WINDOW_WAYLAND || defined SEKIHI_WINDOW_GLFW
+#if defined SEKIHI_WINDOW_WAYLAND || defined SEKIHI_WINDOW_X11 || defined SEKIHI_WINDOW_GLFW
struct skh_window *skh_window_create(void)
{
#if defined SEKIHI_WINDOW_WAYLAND
al_log_info("sekihi", "Creating wayland (EGL) window.");
return skh_window_wayland_create();
+#elif defined SEKIHI_WINDOW_X11
+ al_log_info("sekihi", "Creating X11 (EGL) window.");
+ return skh_window_x11_create();
#elif defined SEKIHI_WINDOW_GLFW
al_log_info("sekihi", "Creating GLFW3 window.");
return skh_window_glfw_create();
diff --git a/src/window.c b/src/window.c
index 6d164a3..40e7f41 100644
--- a/src/window.c
+++ b/src/window.c
@@ -13,44 +13,53 @@
void skh_window_send_pointer_pos_event(struct skh_window *window, f64 x, f64 y)
{
-#if defined SEKIHI_POLL_CONSERVATIVE
+#if defined SEKIHI_EVENT_BUFFER
GET_EVENT_CHUNK(SEKIHI_EVENT_POINTER_POS);
((f64 *)(&ptr[1]))[0] = x;
((f64 *)(&ptr[9]))[0] = y;
APPEND_EVENT_CHUNK();
#else
if (window->pointer_pos_callback) {
- window->pointer_pos_callback(window->userdata, x, y);
- window->pending_refresh = true;
+ if (window->pointer_pos_callback(window->userdata, x, y)) {
+#if defined SEKIHI_PAUSE
+ window->pending_refresh = true;
+#endif
+ }
}
#endif
}
void skh_window_send_scroll_event(struct skh_window *window, f64 y)
{
-#if defined SEKIHI_POLL_CONSERVATIVE
+#if defined SEKIHI_EVENT_BUFFER
GET_EVENT_CHUNK(SEKIHI_EVENT_SCROLL);
((f64 *)(&ptr[1]))[0] = y;
APPEND_EVENT_CHUNK();
#else
if (window->scroll_callback) {
- window->scroll_callback(window->userdata, y);
- window->pending_refresh = true;
+ if (window->scroll_callback(window->userdata, y)) {
+#if defined SEKIHI_PAUSE
+ window->pending_refresh = true;
+#endif
+ }
}
#endif
}
void skh_window_send_mouse_button_event(struct skh_window *window, u8 state, u8 button)
{
-#if defined SEKIHI_POLL_CONSERVATIVE
+#if defined SEKIHI_EVENT_BUFFER
GET_EVENT_CHUNK(SEKIHI_EVENT_MOUSE_BUTTON);
ptr[1] = state;
ptr[2] = button;
APPEND_EVENT_CHUNK();
#else
if (window->mouse_button_callback) {
- window->mouse_button_callback(window->userdata, state, button);
- window->pending_refresh = true;
+ if (window->mouse_button_callback(window->userdata, state, button)) {
+#if defined SEKIHI_PAUSE
+ window->pending_refresh = true;
+#endif
+ }
}
#endif
}
@@ -60,22 +69,25 @@ void skh_window_send_key_event(struct skh_window *window, u8 state, u8 button)
if (window->key_immediate_callback) {
window->key_immediate_callback(window->userdata, state, button);
}
-#if defined SEKIHI_POLL_CONSERVATIVE
+#if defined SEKIHI_EVENT_BUFFER
GET_EVENT_CHUNK(SEKIHI_EVENT_KEY);
ptr[1] = state;
ptr[2] = button;
APPEND_EVENT_CHUNK();
#else
if (window->key_callback) {
- window->key_callback(window->userdata, state, button);
- window->pending_refresh = true;
+ if (window->key_callback(window->userdata, state, button)) {
+#if defined SEKIHI_PAUSE
+ window->pending_refresh = true;
+#endif
+ }
}
#endif
}
void skh_window_send_resize_event(struct skh_window *window, s32 width, s32 height)
{
-#if defined SEKIHI_POLL_CONSERVATIVE
+#if defined SEKIHI_EVENT_BUFFER
GET_EVENT_CHUNK(SEKIHI_EVENT_RESIZE);
((s32 *)(&ptr[1]))[0] = width;
((s32 *)(&ptr[5]))[0] = height;
@@ -83,8 +95,10 @@ void skh_window_send_resize_event(struct skh_window *window, s32 width, s32 heig
#else
if (window->resize_callback) {
window->resize_callback(window->userdata, width, height);
- window->pending_refresh = true;
}
+#if defined SEKIHI_PAUSE
+ window->pending_refresh = true;
+#endif
#endif
}
@@ -93,25 +107,26 @@ void skh_window_send_refresh_event(struct skh_window *window)
{
if (window->refresh_callback) {
window->refresh_callback(window->userdata);
- window->pending_refresh = true;
}
+#if defined SEKIHI_PAUSE
+ window->pending_refresh = true;
+#endif
}
#endif
void skh_window_send_should_close_event(struct skh_window *window)
{
-#if defined SEKIHI_POLL_CONSERVATIVE
+#if defined SEKIHI_EVENT_BUFFER
GET_EVENT_CHUNK(SEKIHI_EVENT_SHOULD_CLOSE);
APPEND_EVENT_CHUNK();
#else
if (window->should_close_callback) {
window->should_close_callback(window->userdata);
- window->pending_refresh = true;
}
#endif
}
-#if defined SEKIHI_POLL_CONSERVATIVE
+#if defined SEKIHI_EVENT_BUFFER
bool skh_window_read_events(struct skh_window *window)
{
u8 op = SEKIHI_EVENT_NONE;
@@ -125,14 +140,22 @@ bool skh_window_read_events(struct skh_window *window)
f64 x = ((f64 *)(&bc[1]))[0];
f64 y = ((f64 *)(&bc[9]))[0];
if (window->pointer_pos_callback) {
- window->pointer_pos_callback(window->userdata, x, y);
+ if (window->pointer_pos_callback(window->userdata, x, y)) {
+#if defined SEKIHI_PAUSE
+ window->pending_refresh = true;
+#endif
+ }
}
break;
}
case SEKIHI_EVENT_SCROLL: {
f64 y = ((f64 *)(&bc[1]))[0];
if (window->scroll_callback) {
- window->scroll_callback(window->userdata, y);
+ if (window->scroll_callback(window->userdata, y)) {
+#if defined SEKIHI_PAUSE
+ window->pending_refresh = true;
+#endif
+ }
}
break;
}
@@ -140,7 +163,11 @@ bool skh_window_read_events(struct skh_window *window)
u8 state = bc[1];
u8 button = bc[2];
if (window->mouse_button_callback) {
- window->mouse_button_callback(window->userdata, state, button);
+ if (window->mouse_button_callback(window->userdata, state, button)) {
+#if defined SEKIHI_PAUSE
+ window->pending_refresh = true;
+#endif
+ }
}
break;
}
@@ -148,7 +175,11 @@ bool skh_window_read_events(struct skh_window *window)
u8 state = bc[1];
u8 button = bc[2];
if (window->key_callback) {
- window->key_callback(window->userdata, state, button);
+ if (window->key_callback(window->userdata, state, button)) {
+#if defined SEKIHI_PAUSE
+ window->pending_refresh = true;
+#endif
+ }
}
break;
}
@@ -158,6 +189,9 @@ bool skh_window_read_events(struct skh_window *window)
if (window->resize_callback) {
window->resize_callback(window->userdata, width, height);
}
+#if defined SEKIHI_PAUSE
+ window->pending_refresh = true;
+#endif
break;
}
case SEKIHI_EVENT_SHOULD_CLOSE: {
diff --git a/src/window.h b/src/window.h
index 249c43e..e1e5dce 100644
--- a/src/window.h
+++ b/src/window.h
@@ -10,11 +10,12 @@
#error "Conflicting graphics APIs."
#endif
-#if !(defined(SEKIHI_POLL_CONSERVATIVE) || defined(SEKIHI_POLL_INLINE))
+#if !(defined(SEKIHI_POLL_FD) || defined(SEKIHI_POLL_INLINE))
#error "No poll method defined."
#endif
#include <al/types.h>
+#include <al/array.h>
#include <al/lib.h>
#include <al/ring_buffer.h>
@@ -30,26 +31,56 @@
#define SEKIHI_MIN_HEIGHT 16
#endif
+enum {
+ SEKIHI_WINDOW_VSYNC = 1,
+ SEKIHI_WINDOW_WALLPAPER = 1 << 1
+};
+
+#define SEKIHI_MAX_MONITOR_NAME 255u
+
+struct skh_window;
+struct skh_monitor {
+ u32 x;
+ u32 y;
+ u32 width;
+ u32 height;
+ s32 scale;
+ bool vertical;
+ char name[SEKIHI_MAX_MONITOR_NAME];
+ struct skh_window *window;
+};
+
struct skh_window {
s32 width;
s32 height;
bool fullscreen;
-#if defined SEKIHI_POLL_CONSERVATIVE
+ array(struct skh_monitor *) monitors;
+#if defined SEKIHI_EVENT_BUFFER
u8 *data;
// Render thread events.
struct al_ring_buffer events;
-#elif defined SEKIHI_POLL_INLINE
+#endif
+#if defined SEKIHI_PAUSE
bool pending_refresh;
#endif
// Methods.
- bool (*create_window)(struct skh_window *, s32, s32, const char *);
+ bool (*create_context)(struct skh_window *, s32);
+ bool (*create_window)(struct skh_window *, s32, s32, const char *, const char *, s32);
void (*resize)(struct skh_window *, s32, s32);
void (*toggle_fullscreen)(struct skh_window *);
+ void (*prepare_read)(struct skh_window *);
+#if defined SEKIHI_POLL_FD
+ s32 (*get_poll_fd)(struct skh_window *);
+#elif defined SEKIHI_POLL_INLINE
bool (*poll)(struct skh_window *, bool);
#if defined SEKIHI_PAUSE
void (*wake)(struct skh_window *);
#endif
- void (*set_ext_data)(struct skh_window *, void *);
+#endif
+ void (*process_events)(struct skh_window *);
+#if defined SEKIHI_PAUSE
+ bool (*should_refresh)(struct skh_window *);
+#endif
void (*free)(struct skh_window **);
// Context specific.
#if defined SEKIHI_API_VULKAN
@@ -58,18 +89,19 @@ struct skh_window {
const char *const *(*vk_get_extensions)(u32 *);
#elif defined SEKIHI_API_OPENGL
void (*(*get_gl_proc_address)(const char *))(void);
- bool (*gl_load_loader)(void *);
+ bool (*gl_loader_load)(void *);
bool (*gl_make_current)(void *);
void (*gl_release_current)(void *);
void (*gl_swap_buffers)(void *);
#elif defined SEKIHI_API_DX11
#endif
// Callbacks.
- void (*pointer_pos_callback)(void *, f64, f64);
- void (*scroll_callback)(void *, f64);
- void (*mouse_button_callback)(void *, u8, u8);
- void (*key_callback)(void *, u8, u8);
- // Always runs on the main thread.
+ void (*output_discovered_callback)(void *, struct skh_monitor *);
+ bool (*pointer_pos_callback)(void *, f64, f64);
+ bool (*scroll_callback)(void *, f64);
+ bool (*mouse_button_callback)(void *, u8, u8);
+ bool (*key_callback)(void *, u8, u8);
+ // Always runs on the thread that's processing events.
void (*key_immediate_callback)(void *, u8, u8);
void (*resize_callback)(void *, s32, s32);
void (*refresh_callback)(void *);
@@ -112,6 +144,6 @@ void skh_window_send_resize_event(struct skh_window *window, s32 width, s32 heig
void skh_window_send_refresh_event(struct skh_window *window);
#endif
void skh_window_send_should_close_event(struct skh_window *window);
-#if defined SEKIHI_POLL_CONSERVATIVE
+#if defined SEKIHI_EVENT_BUFFER
bool skh_window_read_events(struct skh_window *window);
#endif
diff --git a/src/window_glfw.c b/src/window_glfw.c
index 874e08f..64b8d67 100644
--- a/src/window_glfw.c
+++ b/src/window_glfw.c
@@ -1,8 +1,8 @@
#include <al/lib.h>
#include <al/log.h>
-#include "egl_common.h"
#include "window_glfw.h"
+#include "poll_common.h"
#if defined SEKIHI_API_OPENGL
#define SEKIHI_GL_API GLFW_OPENGL_API
@@ -80,22 +80,20 @@ static void close_callback(GLFWwindow *window)
skh_window_send_should_close_event(&glfw->w);
}
-static bool window_glfw_create_window(struct skh_window *window, s32 width, s32 height, const char *name)
+static bool window_glfw_create_window(struct skh_window *window, s32 width, s32 height,
+ const char *monitor, const char *name, s32 flags)
{
struct skh_window_glfw *glfw = (struct skh_window_glfw *)window;
+ (void)monitor;
if (!glfwInit()) {
al_log_error("window_glfw", "Failed to initialize GLFW.");
return false;
}
- //glfwWindowHint(GLFW_SRGB_CAPABLE, GLFW_TRUE);
- //glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);
-
#if defined SEKIHI_API_VULKAN || defined SEKIHI_API_DX11
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
#elif defined SEKIHI_API_OPENGL
- //glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API);
for (u32 i = 0; i < AL_ARRAY_SIZE(skh_gl_vers); i++) {
glfwWindowHint(GLFW_CLIENT_API, skh_gl_vers[i].api);
@@ -128,7 +126,9 @@ static bool window_glfw_create_window(struct skh_window *window, s32 width, s32
return false;
}
- glfwSwapInterval(1);
+ glfwMakeContextCurrent(glfw->window);
+ glfwSwapInterval((flags & SEKIHI_WINDOW_VSYNC) ? 1 : 0);
+ glfwMakeContextCurrent(NULL);
glfwGetFramebufferSize(glfw->window, &glfw->w.width, &glfw->w.height);
glfwSetWindowSizeLimits(glfw->window, SEKIHI_MIN_WIDTH, SEKIHI_MIN_HEIGHT, GLFW_DONT_CARE, GLFW_DONT_CARE);
@@ -139,7 +139,6 @@ static bool window_glfw_create_window(struct skh_window *window, s32 width, s32
glfwSetCursorPosCallback(glfw->window, cursor_position_callback);
glfwSetMouseButtonCallback(glfw->window, mouse_button_callback);
glfwSetScrollCallback(glfw->window, scroll_callback);
- //glfwSetWindowSizeCallback(glfw->window, resize_callback);
glfwSetFramebufferSizeCallback(glfw->window, resize_callback);
#if defined SEKIHI_POLL_INLINE
glfwSetWindowRefreshCallback(glfw->window, refresh_callback);
@@ -156,7 +155,7 @@ static void window_glfw_resize(struct skh_window *window, s32 width, s32 height)
glfwSetWindowSize(glfw->window, width, height);
}
-// https://stackoverflow.com/questions/21421074/how-to-create-a-full-screen-window-on-the-current-monitor-with-glfw
+// https://stackoverflow.com/a/31526753
static GLFWmonitor *get_current_monitor(GLFWwindow *window)
{
s32 best_overlap = 0;
@@ -215,31 +214,19 @@ static void window_glfw_toggle_fullscreen(struct skh_window *window)
glfw->w.fullscreen = !glfw->w.fullscreen;
}
-static bool window_glfw_poll(struct skh_window *window, bool block)
+#if defined SEKIHI_POLL_FD
+static s32 window_glfw_get_poll_fd(struct skh_window *window)
{
- struct skh_window_glfw *glfw = (struct skh_window_glfw *)window;
-#if defined SEKIHI_POLL_CONSERVATIVE
- (void)block;
- glfwWaitEvents();
+ (void)window;
+ return -1;
+}
#elif defined SEKIHI_POLL_INLINE
-#if defined SEKIHI_PAUSE
- if (block) {
- glfwWaitEvents();
- } else {
- glfwPollEvents();
- }
-#else
- (void)block;
- glfwPollEvents();
-#endif
-#endif
-#if defined SEKIHI_POLL_INLINE
- if (glfw->w.pending_refresh) {
- glfw->w.pending_refresh = false;
- return true;
- }
-#endif
- return !block;
+static bool window_glfw_poll(struct skh_window *window, bool block)
+{
+ (void)window;
+ if (block) glfwWaitEvents();
+ else glfwPollEvents();
+ return true;
}
#if defined SEKIHI_PAUSE
@@ -250,13 +237,20 @@ static void window_glfw_wake(struct skh_window *window)
glfwPostEmptyEvent();
}
#endif
+#endif
-static void window_glfw_set_ext_data(struct skh_window *window, void *ext_data)
+static void window_glfw_process_events(struct skh_window *window)
{
(void)window;
- (void)ext_data;
}
+#if defined SEKIHI_PAUSE
+static bool window_glfw_should_refresh(struct skh_window *window)
+{
+ return skh_should_refresh_common(window);
+}
+#endif
+
static void window_glfw_free(struct skh_window **window)
{
struct skh_window_glfw *glfw = (struct skh_window_glfw *)*window;
@@ -264,9 +258,10 @@ static void window_glfw_free(struct skh_window **window)
glfwDestroyWindow(glfw->window);
glfwTerminate();
}
-#if defined SEKIHI_POLL_CONSERVATIVE
+#if defined SEKIHI_EVENT_BUFFER
al_free(glfw->w.data);
#endif
+ al_array_free(glfw->w.monitors);
al_free(glfw);
*window = NULL;
}
@@ -288,23 +283,12 @@ static const char *const *window_glfw_vk_get_extensions(u32 *num)
return glfwGetRequiredInstanceExtensions(num);
}
#elif defined SEKIHI_API_OPENGL
-static bool window_glfw_gl_load_loader(void *data)
+static bool window_glfw_gl_loader_load(void *data)
{
struct skh_window_glfw *glfw = (struct skh_window_glfw *)data;
-#ifdef SEKIHI_USE_GLAD
- //if (!gladLoadEGL(glfwGetEGLDisplay(), (GLADloadfunc)glfw->w.get_gl_proc_address)) {
- // return false;
- //}
-#ifdef SEKIHI_USE_GLES
- if (!gladLoadGLES2((GLADloadfunc)glfw->w.get_gl_proc_address)) {
- return false;
- }
-#else
- if (!gladLoadGL((GLADloadfunc)glfw->w.get_gl_proc_address)) {
+ if (!skh_gl_loader_load(glfw->w.get_gl_proc_address)) {
return false;
}
-#endif
-#endif
return true;
}
@@ -330,21 +314,32 @@ static void window_glfw_gl_swap_buffers(void *data)
struct skh_window *skh_window_glfw_create(void)
{
- struct skh_window_glfw *glfw = (struct skh_window_glfw *)al_calloc(
- 1, sizeof(struct skh_window_glfw));
-#if defined SEKIHI_POLL_CONSERVATIVE
- glfw->w.data = al_malloc(SEKIHI_WINDOW_EVENTS_SIZE);
- al_ring_buffer_init(&glfw->w.events, glfw->w.data, SEKIHI_WINDOW_EVENTS_SIZE);
+ struct skh_window_glfw *glfw = al_alloc_object(struct skh_window_glfw);
+#if defined SEKIHI_EVENT_BUFFER
+ glfw->w.data = al_malloc(SEKIHI_EVENTS_SIZE);
+ al_ring_buffer_init(&glfw->w.events, glfw->w.data, SEKIHI_EVENTS_SIZE);
+#endif
+#if defined SEKIHI_PAUSE
+ glfw->w.pending_refresh = true;
#endif
glfw->w.fullscreen = false;
+ al_array_init(glfw->w.monitors);
+ glfw->w.create_context = NULL;
glfw->w.create_window = window_glfw_create_window;
glfw->w.resize = window_glfw_resize;
glfw->w.toggle_fullscreen = window_glfw_toggle_fullscreen;
+#if defined SEKIHI_POLL_FD
+ glfw->w.get_poll_fd = window_glfw_get_poll_fd;
+#elif defined SEKIHI_POLL_INLINE
glfw->w.poll = window_glfw_poll;
#if defined SEKIHI_PAUSE
glfw->w.wake = window_glfw_wake;
#endif
- glfw->w.set_ext_data = window_glfw_set_ext_data;
+#endif
+ glfw->w.process_events = window_glfw_process_events;
+#if defined SEKIHI_PAUSE
+ glfw->w.should_refresh = window_glfw_should_refresh;
+#endif
glfw->w.free = window_glfw_free;
#if defined SEKIHI_API_VULKAN
glfw->w.get_vk_proc_address = window_glfw_get_vk_proc_address;
@@ -352,7 +347,7 @@ struct skh_window *skh_window_glfw_create(void)
glfw->w.vk_get_extensions = window_glfw_vk_get_extensions;
#elif defined SEKIHI_API_OPENGL
glfw->w.get_gl_proc_address = glfwGetProcAddress;
- glfw->w.gl_load_loader = window_glfw_gl_load_loader;
+ glfw->w.gl_loader_load = window_glfw_gl_loader_load;
glfw->w.gl_make_current = window_glfw_gl_make_current;
glfw->w.gl_release_current = window_glfw_gl_release_current;
glfw->w.gl_swap_buffers = window_glfw_gl_swap_buffers;
diff --git a/src/window_glfw.h b/src/window_glfw.h
index dc7c4a5..cead8ff 100644
--- a/src/window_glfw.h
+++ b/src/window_glfw.h
@@ -2,6 +2,8 @@
#if defined SEKIHI_API_VULKAN
#define GLFW_INCLUDE_VULKAN
+#elif defined SEKIHI_API_OPENGL
+#include "egl_common.h"
#endif
#include <GLFW/glfw3.h>
#ifndef _WIN32
diff --git a/src/window_wayland.c b/src/window_wayland.c
index 0421254..51d2a7c 100644
--- a/src/window_wayland.c
+++ b/src/window_wayland.c
@@ -1,17 +1,17 @@
-#include <aki/thread.h>
#include <al/log.h>
+#include <al/lib.h>
#include "window_wayland.h"
+#include "poll_common.h"
static void xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, u32 serial)
{
- struct skh_window_wayland *wl = (struct skh_window_wayland *)data;
- (void)wl;
+ (void)data;
xdg_wm_base_pong(shell, serial);
}
static const struct xdg_wm_base_listener xdg_wm_base_listener = {
- xdg_wm_base_ping,
+ .ping = xdg_wm_base_ping
};
static void pointer_enter(void *data, struct wl_pointer *pointer,
@@ -54,15 +54,17 @@ static void pointer_button(void *data, struct wl_pointer *pointer,
{
struct skh_window_wayland *wl = (struct skh_window_wayland *)data;
(void)pointer;
- (void)serial;
(void)time;
#define MOUSE1 0x110
+#define MOUSE2 0x111
if (button == MOUSE1) {
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
skh_window_send_mouse_button_event(&wl->w, SEKIHI_BUTTON_PRESSED, SEKIHI_MOUSE1);
} else if (state == WL_POINTER_BUTTON_STATE_RELEASED) {
skh_window_send_mouse_button_event(&wl->w, SEKIHI_BUTTON_RELEASED, SEKIHI_MOUSE1);
}
+ } else if (button == MOUSE2) {
+ xdg_toplevel_move(wl->xdg_toplevel, wl->seat, serial);
}
}
@@ -109,6 +111,15 @@ static void pointer_axis_discrete(void *data, struct wl_pointer *pointer,
(void)discrete;
}
+static void pointer_axis_value120(void *data, struct wl_pointer *pointer,
+ u32 axis, s32 value120)
+{
+ (void)data;
+ (void)pointer;
+ (void)axis;
+ (void)value120;
+}
+
static const struct wl_pointer_listener pointer_listener = {
.enter = pointer_enter,
.leave = pointer_leave,
@@ -118,7 +129,8 @@ static const struct wl_pointer_listener pointer_listener = {
.frame = pointer_frame,
.axis_source = pointer_axis_source,
.axis_stop = pointer_axis_stop,
- .axis_discrete = pointer_axis_discrete
+ .axis_discrete = pointer_axis_discrete,
+ .axis_value120 = pointer_axis_value120
};
static void keyboard_keymap(void *data, struct wl_keyboard *keyboard,
@@ -235,40 +247,200 @@ static const struct wl_seat_listener seat_listener = {
.name = seat_name
};
+static void handle_output_geometry(void *data, struct wl_output *output,
+ s32 x, s32 y, s32 width_mm, s32 height_mm, s32 subpixel,
+ const char *make, const char *model, s32 transform)
+{
+ struct skh_monitor_wayland *monitor = (struct skh_monitor_wayland *)data;
+ (void)output;
+ (void)x;
+ (void)y;
+ (void)width_mm;
+ (void)height_mm;
+ (void)subpixel;
+ (void)make;
+ (void)model;
+ switch (transform) {
+ case WL_OUTPUT_TRANSFORM_90:
+ case WL_OUTPUT_TRANSFORM_270:
+ monitor->m.vertical = true;
+ break;
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ case WL_OUTPUT_TRANSFORM_180:
+ default:
+ monitor->m.vertical = false;
+ break;
+ }
+}
+
+static void handle_output_mode(void *data, struct wl_output *output,
+ u32 flags, s32 width, s32 height, s32 refresh)
+{
+ struct skh_monitor_wayland *monitor = (struct skh_monitor_wayland *)data;
+ (void)output;
+ (void)flags;
+ (void)refresh;
+ if (monitor->m.vertical) {
+ monitor->m.width = height;
+ monitor->m.height = width;
+ } else {
+ monitor->m.width = width;
+ monitor->m.height = height;
+ }
+}
+
+static void handle_output_done(void *data, struct wl_output *output)
+{
+ struct skh_monitor_wayland *monitor = (struct skh_monitor_wayland *)data;
+ (void)output;
+ if (monitor->m.window->output_discovered_callback) {
+ al_log_info("window_wayland", "Output discovered: %s (%ux%u) (vertical: %s).",
+ monitor->m.name, monitor->m.width, monitor->m.height, monitor->m.vertical ? "true" : "false");
+ monitor->m.window->output_discovered_callback(monitor->m.window->userdata, (struct skh_monitor *)monitor);
+ }
+}
+
+static void handle_output_scale(void *data, struct wl_output *output, s32 factor)
+{
+ struct skh_monitor_wayland *monitor = (struct skh_monitor_wayland *)data;
+ (void)output;
+ monitor->m.scale = factor;
+}
+
+static void handle_output_description(void *data, struct wl_output *output, const char *description)
+{
+ (void)data;
+ (void)output;
+ (void)description;
+}
+
+static void handle_output_name(void *data, struct wl_output *output, const char *name)
+{
+ struct skh_monitor_wayland *monitor = (struct skh_monitor_wayland *)data;
+ (void)output;
+ size_t len = strlen(name) + 1;
+ len = AL_MIN(len, SEKIHI_MAX_MONITOR_NAME);
+ al_memcpy(monitor->m.name, name, len);
+}
+
+static const struct wl_output_listener output_listener = {
+ .geometry = handle_output_geometry,
+ .mode = handle_output_mode,
+ .done = handle_output_done,
+ .scale = handle_output_scale,
+ .description = handle_output_description,
+ .name = handle_output_name
+};
+
/*
+static void handle_xdg_output_logical_position(void *data, struct zxdg_output_v1 *output, s32 x, s32 y)
+{
+ struct skh_monitor_wayland *monitor = (struct skh_monitor_wayland *)data;
+ (void)output;
+ monitor->m.x = x;
+ monitor->m.y = y;
+}
+
+static void handle_xdg_output_logical_size(void *data, struct zxdg_output_v1 *output, s32 width, s32 height)
+{
+ (void)data;
+ (void)output;
+ (void)width;
+ (void)height;
+}
+
+static void handle_xdg_output_done(void *data, struct zxdg_output_v1 *output)
+{
+ (void)data;
+ (void)output;
+}
+
+static void handle_xdg_output_name(void *data, struct zxdg_output_v1 *output, const char *name)
+{
+ (void)data;
+ (void)output;
+ (void)name;
+}
+
+static void handle_xdg_output_description(void *data, struct zxdg_output_v1 *output, const char *description)
+{
+ (void)data;
+ (void)output;
+ (void)description;
+}
+
+static const struct zxdg_output_v1_listener xdg_output_listener = {
+ .logical_position = handle_xdg_output_logical_position,
+ .logical_size = handle_xdg_output_logical_size,
+ .done = handle_xdg_output_done,
+ .name = handle_xdg_output_name,
+ .description = handle_xdg_output_description
+};
+
+static void handle_toplevel(void *data, struct zwlr_foreign_toplevel_manager_v1 *manager,
+ struct zwlr_foreign_toplevel_handle_v1 *toplevel)
+{
+ (void)data;
+ (void)manager;
+ (void)toplevel;
+}
+
+static void handle_finished(void *data, struct zwlr_foreign_toplevel_manager_v1 *manager)
+{
+ (void)data;
+ (void)manager;
+}
+
+static const struct zwlr_foreign_toplevel_manager_v1_listener toplevel_manager_listener = {
+ .toplevel = handle_toplevel,
+ .finished = handle_finished
+};
+*/
+
static void presentation_set_clock_id(void *data, struct wp_presentation *presentation, u32 clock_id)
{
struct skh_window_wayland *wl = (struct skh_window_wayland *)data;
- if (clock_id == CLOCK_MONOTONIC)
- {
- wl->presentation = presentation;
- }
+ al_assert(clock_id == CLOCK_MONOTONIC);
+ wl->presentation = presentation;
}
static const struct wp_presentation_listener presentation_listener = {
- presentation_set_clock_id
+ .clock_id = presentation_set_clock_id
};
-*/
static void global_add(void *data, struct wl_registry *registry, u32 name,
const char *interface, u32 version)
{
struct skh_window_wayland *wl = (struct skh_window_wayland *)data;
- (void)version;
if (strcmp(interface, wl_compositor_interface.name) == 0) {
- wl->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 2);
+ wl->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, AL_MIN(5u, version));
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
- wl->seat = wl_registry_bind(registry, name, &wl_seat_interface, 4);
+ wl->seat = wl_registry_bind(registry, name, &wl_seat_interface, AL_MIN(8u, version));
wl_seat_add_listener(wl->seat, &seat_listener, wl);
+ } else if (strcmp(interface, wl_output_interface.name) == 0) {
+ struct skh_monitor_wayland *monitor = al_alloc_object(struct skh_monitor_wayland);
+ monitor->m.window = (struct skh_window *)wl;
+ al_array_push(wl->w.monitors, (struct skh_monitor *)monitor);
+ monitor->output = wl_registry_bind(registry, name, &wl_output_interface, AL_MIN(4u, version));
+ wl_output_add_listener(monitor->output, &output_listener, monitor);
+ // monitor->xdg_output = zxdg_output_manager_v1_get_xdg_output(wl->zxdg_output_manager, monitor->output);
+ // zxdg_output_v1_add_listener(monitor->xdg_output, &xdg_output_listener, monitor);
+ //} else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
+ // wl->zxdg_output_manager = wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, AL_MIN(3u, version));
+ //} else if (strcmp(interface, zwlr_foreign_toplevel_manager_v1_interface.name) == 0) {
+ // wl->wlr_toplevel_manager = wl_registry_bind(registry, name, &zwlr_foreign_toplevel_manager_v1_interface, AL_MIN(3u, version));
+ // zwlr_foreign_toplevel_manager_v1_add_listener(wl->wlr_toplevel_manager, &toplevel_manager_listener, wl);
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
- wl->xdg_wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 2);
+ wl->xdg_wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, AL_MIN(2u, version));
xdg_wm_base_add_listener(wl->xdg_wm_base, &xdg_wm_base_listener, wl);
} else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) {
- wl->zxdg_decoration_manager = wl_registry_bind(registry, name, &zxdg_decoration_manager_v1_interface, 1);
- }/* else if (strcmp(interface, wp_presentation_interface.name) == 0) {
- wl->presentation = wl_registry_bind(registry, name, &wp_presentation_interface, 1);
+ wl->zxdg_decoration_manager = wl_registry_bind(registry, name, &zxdg_decoration_manager_v1_interface, AL_MIN(1u, version));
+ } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
+ wl->layer_shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, AL_MIN(4u, version));
+ } else if (strcmp(interface, wp_presentation_interface.name) == 0) {
+ wl->presentation = wl_registry_bind(registry, name, &wp_presentation_interface, AL_MIN(1u, version));
wp_presentation_add_listener(wl->presentation, &presentation_listener, wl);
- }*/
+ }
}
static void global_remove(void *data, struct wl_registry *registry, u32 name)
@@ -278,7 +450,7 @@ static void global_remove(void *data, struct wl_registry *registry, u32 name)
(void)name;
}
-struct wl_registry_listener registry_listener = {
+static const struct wl_registry_listener registry_listener = {
.global = global_add,
.global_remove = global_remove
};
@@ -286,8 +458,8 @@ struct wl_registry_listener registry_listener = {
static void handle_surface_enter(void *data, struct wl_surface *surface, struct wl_output *output)
{
(void)data;
+ (void)surface;
(void)output;
- wl_surface_set_buffer_scale(surface, 1);
}
static void handle_surface_leave(void *data, struct wl_surface *surface, struct wl_output *output)
@@ -312,29 +484,43 @@ static void preferred_buffer_transform(void *data, struct wl_surface *surface, u
}
static const struct wl_surface_listener surface_listener = {
- handle_surface_enter,
- handle_surface_leave,
- preferred_buffer_scale,
- preferred_buffer_transform
+ .enter = handle_surface_enter,
+ .leave = handle_surface_leave,
+ .preferred_buffer_scale = preferred_buffer_scale,
+ .preferred_buffer_transform = preferred_buffer_transform
};
+static bool check_new_dimensions(struct skh_window_wayland *wl, s32 width, s32 height)
+{
+ if ((width && height) && (width != wl->w.width || height != wl->w.height)) {
+ wl->w.width = width;
+ wl->w.height = height;
+ return true;
+ }
+ return false;
+}
+
+static void send_resize_internal(struct skh_window_wayland *wl)
+{
+ if (skh_egl_is_ready(&wl->egl)) {
+ wl_egl_window_resize(wl->egl_window, wl->w.width, wl->w.height, 0, 0);
+ }
+ skh_window_send_resize_event(&wl->w, wl->w.width, wl->w.height);
+ wl->pending = false;
+}
+
static void handle_xdg_surface_configure(void *data, struct xdg_surface *surface, u32 serial)
{
struct skh_window_wayland *wl = (struct skh_window_wayland *)data;
- xdg_surface_ack_configure(surface, serial);
- if (!wl->pending) return;
- wl->pending = false;
- s32 width = wl->w.width;
- s32 height = wl->w.height;
- xdg_surface_set_window_geometry(wl->xdg_surface, 0, 0, width, height);
- if (skh_egl_is_ready(&wl->egl)) {
- wl_egl_window_resize(wl->egl_window, width, height, 0, 0);
+ if (wl->pending) {
+ xdg_surface_set_window_geometry(wl->xdg_surface, 0, 0, wl->w.width, wl->w.height);
+ send_resize_internal(wl);
}
- skh_window_send_resize_event(&wl->w, width, height);
+ xdg_surface_ack_configure(surface, serial);
}
static const struct xdg_surface_listener xdg_surface_listener = {
- handle_xdg_surface_configure,
+ .configure = handle_xdg_surface_configure,
};
static void handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *toplevel,
@@ -343,12 +529,9 @@ static void handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *tople
struct skh_window_wayland *wl = (struct skh_window_wayland *)data;
(void)toplevel;
(void)states;
- if ((!width && !height) || (width == wl->w.width && height == wl->w.height)) {
- return;
+ if (check_new_dimensions(wl, width, height)) {
+ wl->pending = true;
}
- wl->w.width = width;
- wl->w.height = height;
- wl->pending = true;
}
static void handle_xdg_toplevel_close(void *data, struct xdg_toplevel *toplevel)
@@ -366,22 +549,53 @@ static void handle_xdg_toplevel_configure_bounds(void *data, struct xdg_toplevel
(void)height;
}
+static void handle_xdg_toplevel_wm_capabilities(void *data, struct xdg_toplevel *toplevel,
+ struct wl_array *capabilities)
+{
+ (void)data;
+ (void)toplevel;
+ (void)capabilities;
+}
+
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
- handle_xdg_toplevel_configure,
- handle_xdg_toplevel_close,
- handle_xdg_toplevel_configure_bounds,
- NULL
+ .configure = handle_xdg_toplevel_configure,
+ .configure_bounds = handle_xdg_toplevel_configure_bounds,
+ .wm_capabilities = handle_xdg_toplevel_wm_capabilities,
+ .close = handle_xdg_toplevel_close
+};
+
+static void handle_layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface,
+ u32 serial, u32 width, u32 height)
+{
+ struct skh_window_wayland *wl = (struct skh_window_wayland *)data;
+ if (check_new_dimensions(wl, width, height)) {
+ send_resize_internal(wl);
+ }
+ zwlr_layer_surface_v1_ack_configure(surface, serial);
+}
+
+static void handle_layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface)
+{
+ struct skh_window_wayland *wl = (struct skh_window_wayland *)data;
+ (void)surface;
+ skh_window_send_should_close_event(&wl->w);
+}
+
+static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
+ .configure = handle_layer_surface_configure,
+ .closed = handle_layer_surface_closed
};
static bool wl_egl_init(struct skh_window_wayland *wl)
{
- if (!skh_load_egl(&wl->egl)) return false;
+ if (!skh_maybe_load_egl(&wl->egl)) return false;
wl->egl_window = wl_egl_window_create(wl->surface, wl->w.width, wl->w.height);
+
wl->egl.display = eglGetDisplay((EGLNativeDisplayType)wl->display);
if (!wl->egl.display) {
- al_log_error("window_wayland", "Failed to get wayland-native EGL display.");
- return false;
+ al_log_error("window_wayland", "Failed to get wayland-native EGL display.");
+ return false;
}
EGLConfig config;
@@ -393,76 +607,137 @@ static bool wl_egl_init(struct skh_window_wayland *wl)
wl->w.get_gl_proc_address = eglGetProcAddress;
#endif
- //wl->egl.surface = eglCreatePlatformWindowSurface(wl->egl.display, config, wl->egl_window, NULL);
- wl->egl.surface = eglCreateWindowSurface(wl->egl.display, config, (EGLNativeWindowType)wl->egl_window, NULL);
+ const EGLint surface_attr[] = {
+ EGL_RENDER_BUFFER, EGL_BACK_BUFFER,
+ EGL_NONE
+ };
+
+ wl->egl.surface = eglCreateWindowSurface(wl->egl.display, config, (EGLNativeWindowType)wl->egl_window, surface_attr);
return true;
}
+static bool create_context_internal(struct skh_window_wayland *wl)
+{
+ wl->display = wl_display_connect(NULL);
+ if (!wl->display) {
+ al_log_error("window_wayland", "Failed to connect to the display.");
+ return false;
+ }
+ wl->fds[0].fd = wl_display_get_fd(wl->display);
+ wl->fds[0].events = POLLIN;
+#if defined SEKIHI_POLL_INLINE && defined SEKIHI_PAUSE
+ wl->fds[1].fd = eventfd(0, 0);
+ wl->fds[1].events = POLLIN;
+#endif
+ wl->registry = wl_display_get_registry(wl->display);
+ wl_registry_add_listener(wl->registry, &registry_listener, wl);
+ wl_display_roundtrip(wl->display);
+ wl_display_roundtrip(wl->display);
+ return true;
+}
+
+static bool window_wayland_create_context(struct skh_window *window, s32 flags)
+{
+ struct skh_window_wayland *wl = (struct skh_window_wayland *)window;
+ wl->flags = flags;
+ if (!create_context_internal(wl)) {
+ wl->w.free((struct skh_window **)&wl);
+ return false;
+ }
+ return true;
+}
+
+static struct skh_monitor_wayland *monitor_from_name(struct skh_window_wayland *wl, const char *name)
+{
+ if (name) {
+ for (u32 i = 0; i < wl->w.monitors.size; i++) {
+ struct skh_monitor_wayland *monitor = (struct skh_monitor_wayland *)al_array_at(wl->w.monitors, i);
+ if (strcmp(monitor->m.name, name) == 0) return monitor;
+ }
+ }
+ return (struct skh_monitor_wayland *)al_array_at(wl->w.monitors, 0);
+}
+
//static const struct wl_callback_listener frame_listener;
-static bool window_wayland_create_window(struct skh_window *window, s32 width, s32 height, const char *name)
+static bool window_wayland_create_window(struct skh_window *window, s32 width, s32 height,
+ const char *monitor, const char *name, s32 flags)
{
struct skh_window_wayland *wl = (struct skh_window_wayland *)window;
+ wl->flags = flags;
wl->w.width = width;
wl->w.height = height;
wl->pending = true;
- wl->display = wl_display_connect(NULL);
- if (!wl->display) { al_assert(false); }
+ if (!create_context_internal(wl)) goto err;
- wl->registry = wl_display_get_registry(wl->display);
- wl_registry_add_listener(wl->registry, &registry_listener, window);
- wl_display_roundtrip(wl->display);
+ wl->monitor = monitor_from_name(wl, monitor);
wl->surface = wl_compositor_create_surface(wl->compositor);
- if (!wl->surface) { al_assert(false); }
- wl_surface_add_listener(wl->surface, &surface_listener, window);
+ if (!wl->surface) goto err;
+ wl_surface_add_listener(wl->surface, &surface_listener, wl);
+ wl_surface_set_buffer_scale(wl->surface, wl->monitor ? wl->monitor->m.scale : 1);
- wl->xdg_surface = xdg_wm_base_get_xdg_surface(wl->xdg_wm_base, wl->surface);
- if (!wl->xdg_surface) { al_assert(false); }
- xdg_surface_add_listener(wl->xdg_surface, &xdg_surface_listener, window);
+ if (!(flags & SEKIHI_WINDOW_WALLPAPER)) {
+ wl->xdg_surface = xdg_wm_base_get_xdg_surface(wl->xdg_wm_base, wl->surface);
+ if (!wl->xdg_surface) goto err;
+ xdg_surface_add_listener(wl->xdg_surface, &xdg_surface_listener, wl);
- wl->xdg_toplevel = xdg_surface_get_toplevel(wl->xdg_surface);
- if (!wl->xdg_toplevel) { al_assert(false); }
- xdg_toplevel_add_listener(wl->xdg_toplevel, &xdg_toplevel_listener, window);
+ wl->xdg_toplevel = xdg_surface_get_toplevel(wl->xdg_surface);
+ if (!wl->xdg_toplevel) goto err;
+ xdg_toplevel_add_listener(wl->xdg_toplevel, &xdg_toplevel_listener, wl);
- xdg_toplevel_set_title(wl->xdg_toplevel, name);
- xdg_toplevel_set_app_id(wl->xdg_toplevel, name);
- xdg_toplevel_set_min_size(wl->xdg_toplevel, SEKIHI_MIN_WIDTH, SEKIHI_MIN_HEIGHT);
+ xdg_toplevel_set_title(wl->xdg_toplevel, name);
+ xdg_toplevel_set_app_id(wl->xdg_toplevel, name);
+ xdg_toplevel_set_min_size(wl->xdg_toplevel, SEKIHI_MIN_WIDTH, SEKIHI_MIN_HEIGHT);
- wl->zxdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
- wl->zxdg_decoration_manager, wl->xdg_toplevel);
- zxdg_toplevel_decoration_v1_set_mode(wl->zxdg_toplevel_decoration,
- ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
-
- wl->fds[0].fd = wl_display_get_fd(wl->display);
- wl->fds[0].events = POLLIN;
-
-#if defined SEKIHI_PAUSE
- wl->fds[1].fd = eventfd(0, 0);
- wl->fds[1].events = POLLIN;
-#endif
+ u32 decor = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
+ wl->zxdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
+ wl->zxdg_decoration_manager, wl->xdg_toplevel);
+ zxdg_toplevel_decoration_v1_set_mode(wl->zxdg_toplevel_decoration, decor);
+ } else {
+ u32 surface = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND;
+ u32 anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
+ | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM
+ | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
+ | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
+ wl->layer_surface = zwlr_layer_shell_v1_get_layer_surface(wl->layer_shell,
+ wl->surface, wl->monitor->output, surface, name);
+ if (!wl->layer_surface) goto err;
+ zwlr_layer_surface_v1_set_margin(wl->layer_surface, 0, 0, 0, 0);
+ if (!wl->w.width) wl->w.width = wl->monitor->m.width;
+ if (!wl->w.height) wl->w.height = wl->monitor->m.height;
+ zwlr_layer_surface_v1_set_size(wl->layer_surface, wl->w.width, wl->w.height);
+ zwlr_layer_surface_v1_set_anchor(wl->layer_surface, anchor);
+ zwlr_layer_surface_v1_add_listener(wl->layer_surface, &layer_surface_listener, wl);
+ }
- //wl->frame_callback = wl_surface_frame(wl->surface);
- //wl_callback_add_listener(wl->frame_callback, &frame_listener, wl);
+ if (!wl_egl_init(wl)) goto err;
wl_surface_commit(wl->surface);
wl_display_roundtrip(wl->display);
- if (!wl_egl_init(wl)) {
- return false;
- }
+ skh_egl_make_current(&wl->egl);
+ skh_egl_swap_interval(&wl->egl, (wl->flags & SEKIHI_WINDOW_VSYNC) ? 1 : 0);
+ skh_egl_release_current(&wl->egl);
- skh_egl_swap_interval(&wl->egl, 1);
+ //wl->frame_done = 0;
+ //wl->frame_callback = wl_surface_frame(wl->surface);
+ //wl_callback_add_listener(wl->frame_callback, &frame_listener, wl);
return true;
+err:
+ al_log_error("window_wayland", "Failed to create window.");
+ wl->w.free((struct skh_window **)&wl);
+ return false;
}
static void window_wayland_resize(struct skh_window *window, s32 width, s32 height)
{
- (void)window;
+ struct skh_window_wayland *wl = (struct skh_window_wayland *)window;
+ (void)wl;
(void)width;
(void)height;
}
@@ -478,104 +753,103 @@ static void window_wayland_toggle_fullscreen(struct skh_window *window)
wl->w.fullscreen = !wl->w.fullscreen;
}
-static bool window_wayland_poll(struct skh_window *window, bool block)
+static void prepare_read_internal(struct skh_window_wayland *wl)
{
- struct skh_window_wayland *wl = (struct skh_window_wayland *)window;
-
- wl->fds[0].revents = 0;
-#if defined SEKIHI_PAUSE
- wl->fds[1].revents = 0;
-#endif
-
- if (wl_display_prepare_read(wl->display) != 0) {
+ while (wl_display_prepare_read(wl->display) != 0) {
wl_display_dispatch_pending(wl->display);
- return false;
}
-
wl_display_flush(wl->display);
+}
-#if defined SEKIHI_POLL_CONSERVATIVE
-#if defined SEKIHI_PAUSE
- (void)block;
- if (poll(wl->fds, 2, -1) <= 0) {
-#else
- (void)block;
- if (poll(wl->fds, 1, -1) <= 0) {
-#endif
+static void window_wayland_prepare_read(struct skh_window *window)
+{
+ struct skh_window_wayland *wl = (struct skh_window_wayland *)window;
+ prepare_read_internal(wl);
+}
+
+#if defined SEKIHI_POLL_FD
+static s32 window_wayland_get_poll_fd(struct skh_window *window)
+{
+ struct skh_window_wayland *wl = (struct skh_window_wayland *)window;
+ return wl->fds[0].fd;
+}
#elif defined SEKIHI_POLL_INLINE
+static bool window_wayland_poll(struct skh_window *window, bool block)
+{
+ struct skh_window_wayland *wl = (struct skh_window_wayland *)window;
+ prepare_read_internal(wl);
+ return skh_poll_common(wl->fds, block);
+}
+
#if defined SEKIHI_PAUSE
- if (poll(wl->fds, 2, (block) ? -1 : 0) <= 0) {
-#else
- (void)block;
- if (poll(wl->fds, 1, 0) <= 0) {
+static void window_wayland_wake(struct skh_window *window)
+{
+ struct skh_window_wayland *wl = (struct skh_window_wayland *)window;
+ skh_wake_common(&wl->w, wl->fds);
+}
#endif
#endif
- wl_display_cancel_read(wl->display);
- return false;
- }
- if (wl->fds[0].revents & POLLIN) {
+static void window_wayland_process_events(struct skh_window *window)
+{
+ struct skh_window_wayland *wl = (struct skh_window_wayland *)window;
+ if (skh_process_events_common(wl->fds)) {
wl_display_read_events(wl->display);
wl_display_dispatch_pending(wl->display);
} else {
wl_display_cancel_read(wl->display);
}
-
-#if defined SEKIHI_PAUSE
- if (wl->fds[1].revents & POLLIN) {
- s64 b;
- s64 ret = read(wl->fds[1].fd, &b, sizeof(s64));
- al_assert(ret == sizeof(s64));
- return true;
- }
-#endif
-#if defined SEKIHI_POLL_INLINE
- if (wl->w.pending_refresh) {
- wl->w.pending_refresh = false;
- return true;
- }
-#endif
- return !block;
}
#if defined SEKIHI_PAUSE
-static void window_wayland_wake(struct skh_window *window)
+static bool window_wayland_should_refresh(struct skh_window *window)
{
- struct skh_window_wayland *wl = (struct skh_window_wayland *)window;
- wl->w.pending_refresh = true;
- s64 b = 1;
- ssize_t ret = write(wl->fds[1].fd, &b, sizeof(s64));
- al_assert(ret == sizeof(s64));
+ return skh_should_refresh_common(window);
}
#endif
-static void window_wayland_set_ext_data(struct skh_window *window, void *ext_data)
+static void destroy_surface_internal(struct skh_window_wayland *wl)
{
- (void)window;
- (void)ext_data;
+ if (wl->xdg_surface) {
+ xdg_surface_destroy(wl->xdg_surface);
+ if (wl->zxdg_toplevel_decoration) zxdg_toplevel_decoration_v1_destroy(wl->zxdg_toplevel_decoration);
+ if (wl->xdg_toplevel) xdg_toplevel_destroy(wl->xdg_toplevel);
+ } else if (wl->layer_surface) {
+ zwlr_layer_surface_v1_destroy(wl->layer_surface);
+ }
+ if (wl->egl_window) {
+ skh_egl_destroy_surface(&wl->egl);
+ wl_egl_window_destroy(wl->egl_window);
+ }
+ if (wl->surface) wl_surface_destroy(wl->surface);
}
-static void window_wayland_free(struct skh_window **window)
+void window_wayland_free(struct skh_window **window)
{
struct skh_window_wayland *wl = (struct skh_window_wayland *)*window;
if (!wl->display) return;
- wl_registry_destroy(wl->registry);
- wl_seat_destroy(wl->seat);
- wl_pointer_destroy(wl->pointer);
- wl_keyboard_destroy(wl->keyboard);
- wl_compositor_destroy(wl->compositor);
- xdg_wm_base_destroy(wl->xdg_wm_base);
- xdg_surface_destroy(wl->xdg_surface);
- zxdg_toplevel_decoration_v1_destroy(wl->zxdg_toplevel_decoration);
- zxdg_decoration_manager_v1_destroy(wl->zxdg_decoration_manager);
- xdg_toplevel_destroy(wl->xdg_toplevel);
- skh_egl_destroy_surface(&wl->egl);
- wl_egl_window_destroy(wl->egl_window);
- wl_surface_destroy(wl->surface);
- skh_egl_destroy_context(&wl->egl);
- skh_egl_terminate(&wl->egl);
+ if (wl->registry) wl_registry_destroy(wl->registry);
+ if (wl->seat) wl_seat_destroy(wl->seat);
+ if (wl->pointer) wl_pointer_destroy(wl->pointer);
+ if (wl->keyboard) wl_keyboard_destroy(wl->keyboard);
+ if (wl->compositor) wl_compositor_destroy(wl->compositor);
+ for (u32 i = 0; i < wl->w.monitors.size; i++) {
+ struct skh_monitor_wayland *monitor = (struct skh_monitor_wayland *)al_array_at(wl->w.monitors, i);
+ if (monitor->output) wl_output_destroy(monitor->output);
+ al_free(monitor);
+ }
+ al_array_free(wl->w.monitors);
+ if (wl->xdg_wm_base) xdg_wm_base_destroy(wl->xdg_wm_base);
+ if (wl->zxdg_decoration_manager) zxdg_decoration_manager_v1_destroy(wl->zxdg_decoration_manager);
+ if (wl->layer_shell) zwlr_layer_shell_v1_destroy(wl->layer_shell);
+ if (wl->presentation) wp_presentation_destroy(wl->presentation);
+ destroy_surface_internal(wl);
+ if (skh_egl_is_ready(&wl->egl)) {
+ skh_egl_destroy_context(&wl->egl);
+ skh_egl_terminate(&wl->egl);
+ }
wl_display_disconnect(wl->display);
-#if defined SEKIHI_POLL_CONSERVATIVE
+#if defined SEKIHI_EVENT_BUFFER
al_free(wl->w.data);
#endif
al_free(wl);
@@ -592,36 +866,31 @@ static void feedback_sync_output(void *data, struct wp_presentation_feedback *fe
}
static void feedback_presented(void *data, struct wp_presentation_feedback *feedback,
- u32 tvSecHi, u32 tvSecLo, u32 tvNsec, u32 refreshNsec, u32 seqHi, u32 seqLo, u32 flags)
+ u32 tv_sec_hi, u32 tv_sec_lo, u32 tv_nsec, u32 refresh_nsec,
+ u32 seq_hi, u32 seq_lo, u32 flags)
{
struct skh_window_wayland *wl = (struct skh_window_wayland *)data;
- (void)refreshNsec;
- (void)seqHi;
- (void)seqLo;
+ (void)refresh_nsec;
+ (void)seq_hi;
+ (void)seq_lo;
(void)flags;
-
- u64 sec = (u64)tvSecLo + ((u64)tvSecHi << 32);
- u64 nsec = sec * 1000000000 + (u64)tvNsec;
- wl->presentation_time = ((f64)(nsec * 1e-9)) - wl->timer_offset;
-
- printf("%f\n", wl->presentation_time);
-
wp_presentation_feedback_destroy(feedback);
+ u64 sec = (u64)tv_sec_lo + ((u64)tv_sec_hi << 32);
+ u64 nsec = sec * 1000000000 + (u64)tv_nsec;
+ wl->presentation_time = ((f64)(nsec * 1e-9));
}
static void feedback_discarded(void *data, struct wp_presentation_feedback *feedback)
{
struct skh_window_wayland *wl = (struct skh_window_wayland *)data;
-
- wl->presentation_time = 0.0;
-
wp_presentation_feedback_destroy(feedback);
+ wl->presentation_time = 0.0;
}
static const struct wp_presentation_feedback_listener feedback_listener = {
- feedback_sync_output,
- feedback_presented,
- feedback_discarded
+ .sync_output = feedback_sync_output,
+ .presented = feedback_presented,
+ .discarded = feedback_discarded
};
static void frame_callback(void *data, struct wl_callback *callback, u32 time)
@@ -634,34 +903,24 @@ static void frame_callback(void *data, struct wl_callback *callback, u32 time)
wl->frame_callback = wl_surface_frame(wl->surface);
wl_callback_add_listener(wl->frame_callback, &frame_listener, wl);
+ wl->frame_done = 1;
+
struct wp_presentation_feedback *feedback = wp_presentation_feedback(wl->presentation, wl->surface);
wp_presentation_feedback_add_listener(feedback, &feedback_listener, wl);
}
static const struct wl_callback_listener frame_listener = {
- frame_callback,
+ .done = frame_callback
};
*/
#if defined SEKIHI_API_OPENGL
-static bool window_wayland_gl_load_loader(void *data)
+static bool window_wayland_gl_loader_load(void *data)
{
struct skh_window_wayland *wl = (struct skh_window_wayland *)data;
-#ifdef SEKIHI_USE_GLAD
- al_assert(wl->w.get_gl_proc_address);
- if (!gladLoadEGL(wl->egl.display, (GLADloadfunc)wl->w.get_gl_proc_address)) {
- return false;
- }
-#ifdef SEKIHI_USE_GLES
- if (!gladLoadGLES2((GLADloadfunc)wl->w.get_gl_proc_address)) {
- return false;
- }
-#else
- if (!gladLoadGL((GLADloadfunc)wl->w.get_gl_proc_address)) {
+ if (!skh_gl_loader_load(wl->w.get_gl_proc_address)) {
return false;
}
-#endif
-#endif
return true;
}
@@ -681,35 +940,54 @@ static void window_wayland_gl_release_current(void *data)
static void window_wayland_gl_swap_buffers(void *data)
{
struct skh_window_wayland *wl = (struct skh_window_wayland *)data;
+ //wl->frame_done = 0;
skh_egl_swap_buffers(&wl->egl);
+ /*
+ // This is not correct and cannot be expected to work anything like vsync or
+ // it will introduce massive input lag.
+ while (!wl->frame_done) {
+#if defined SEKIHI_POLL_INLINE
+ wl->w.poll(&wl->w, true);
+#endif
+ }
+ */
}
#endif
struct skh_window *skh_window_wayland_create(void)
{
- struct skh_window_wayland *wl = (struct skh_window_wayland *)al_calloc(
- 1, sizeof(struct skh_window_wayland));
-#if defined SEKIHI_POLL_CONSERVATIVE
- wl->w.data = al_malloc(SEKIHI_WINDOW_EVENTS_SIZE);
- al_ring_buffer_init(&wl->w.events, wl->w.data, SEKIHI_WINDOW_EVENTS_SIZE);
+ struct skh_window_wayland *wl = al_alloc_object(struct skh_window_wayland);
+#if defined SEKIHI_EVENT_BUFFER
+ wl->w.data = al_malloc(SEKIHI_EVENTS_SIZE);
+ al_ring_buffer_init(&wl->w.events, wl->w.data, SEKIHI_EVENTS_SIZE);
+#endif
+#if defined SEKIHI_PAUSE
+ wl->w.pending_refresh = true;
#endif
wl->w.fullscreen = false;
+ al_array_init(wl->w.monitors);
+ wl->w.create_context = window_wayland_create_context;
wl->w.create_window = window_wayland_create_window;
wl->w.resize = window_wayland_resize;
wl->w.toggle_fullscreen = window_wayland_toggle_fullscreen;
+ wl->w.prepare_read = window_wayland_prepare_read;
+#if defined SEKIHI_POLL_FD
+ wl->w.get_poll_fd = window_wayland_get_poll_fd;
+#elif defined SEKIHI_POLL_INLINE
wl->w.poll = window_wayland_poll;
-#if defined SEKIHI_POLL_INLINE
- wl->w.pending_refresh = true;
-#endif
#if defined SEKIHI_PAUSE
wl->w.wake = window_wayland_wake;
#endif
- wl->w.set_ext_data = window_wayland_set_ext_data;
+#endif
+ wl->w.process_events = window_wayland_process_events;
+#if defined SEKIHI_PAUSE
+ wl->w.should_refresh = window_wayland_should_refresh;
+#endif
wl->w.free = window_wayland_free;
#if defined SEKIHI_API_VULKAN
#elif defined SEKIHI_API_OPENGL
wl->w.get_gl_proc_address = NULL;
- wl->w.gl_load_loader = window_wayland_gl_load_loader;
+ wl->w.gl_loader_load = window_wayland_gl_loader_load;
wl->w.gl_make_current = window_wayland_gl_make_current;
wl->w.gl_release_current = window_wayland_gl_release_current;
wl->w.gl_swap_buffers = window_wayland_gl_swap_buffers;
diff --git a/src/window_wayland.h b/src/window_wayland.h
index cbd9659..af5eac6 100644
--- a/src/window_wayland.h
+++ b/src/window_wayland.h
@@ -1,25 +1,31 @@
#pragma once
#include <al/lib.h>
+#include <aki/socket.h>
-#include <poll.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/eventfd.h>
#include <wayland-client-core.h>
#include <wayland-client-protocol.h>
#include <wayland-egl-core.h>
#include <wayland-egl.h>
-#include <wayland-client.h>
#include <xdg-shell-client-protocol.h>
#include <xdg-decoration-unstable-v1-client-protocol.h>
-//#include <presentation-time-client-protocol.h>
+//#include <xdg-output-unstable-v1-client-protocol.h>
+//#include <wlr-foreign-toplevel-management-unstable-v1-client-protocol.h>
+#include <wlr-layer-shell-unstable-v1-client-protocol.h>
+#include <presentation-time-client-protocol.h>
#include "window.h"
#include "egl_common.h"
+struct skh_monitor_wayland {
+ struct skh_monitor m;
+ struct wl_output *output;
+ struct zxdg_output_v1 *xdg_output;
+};
+
struct skh_window_wayland {
struct skh_window w;
+ s32 flags;
struct wl_display *display;
struct wl_registry *registry;
struct wl_seat *seat;
@@ -27,20 +33,27 @@ struct skh_window_wayland {
struct wl_keyboard *keyboard;
struct wl_compositor *compositor;
struct wl_surface *surface;
- bool pending;
+ //struct zxdg_output_manager_v1 *zxdg_output_manager;
+ //struct zwlr_foreign_toplevel_manager_v1 *wlr_toplevel_manager;
struct xdg_wm_base *xdg_wm_base;
+ bool pending;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
struct zxdg_decoration_manager_v1 *zxdg_decoration_manager;
struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration;
- //struct wl_callback *frame_callback;
- //struct wp_presentation *presentation;
+ struct zwlr_layer_shell_v1 *layer_shell;
+ struct skh_monitor_wayland *monitor;
+ struct zwlr_layer_surface_v1 *layer_surface;
+ struct wl_callback *frame_callback;
+ s32 frame_done;
+ struct wp_presentation *presentation;
+ f64 presentation_time;
struct skh_egl egl;
struct wl_egl_window *egl_window;
-#if defined SEKIHI_PAUSE
- struct pollfd fds[2];
+#if defined SEKIHI_POLL_INLINE && defined SEKIHI_PAUSE
+ struct aki_pollfd fds[2];
#else
- struct pollfd fds[1];
+ struct aki_pollfd fds[1];
#endif
f64 pointer_x;
f64 pointer_y;
diff --git a/src/window_x11.c b/src/window_x11.c
new file mode 100644
index 0000000..6e7d480
--- /dev/null
+++ b/src/window_x11.c
@@ -0,0 +1,342 @@
+#include <al/log.h>
+
+#include "window_x11.h"
+#include "poll_common.h"
+
+static Atom ATOM_WM_DELETE_WINDOW;
+
+#define XWIN_ALL_DESKTOPS 0xFFFFFFFF
+
+#define _NET_WM_STATE_REMOVE 0
+#define _NET_WM_STATE_ADD 1
+#define _NET_WM_STATE_TOGGLE 2
+
+static bool x11_egl_init(struct skh_window_x11 *xw)
+{
+ if (!skh_maybe_load_egl(&xw->egl)) return false;
+
+ xw->egl.display = eglGetDisplay((EGLNativeDisplayType)xw->x11_d);
+ if (!xw->egl.display) {
+ al_log_error("window_x11", "Failed to get x11 EGL display.");
+ }
+
+ EGLConfig config;
+ if (!skh_init_egl(&xw->egl, 0, &config)) {
+ return false;
+ }
+
+#if defined SEKIHI_API_OPENGL
+ xw->w.get_gl_proc_address = eglGetProcAddress;
+#endif
+
+ const EGLint surface_attr[] = {
+ EGL_RENDER_BUFFER, EGL_BACK_BUFFER,
+ EGL_NONE
+ };
+
+ xw->egl.surface = eglCreateWindowSurface(xw->egl.display, config, (EGLNativeWindowType)xw->window, surface_attr);
+
+ return true;
+}
+
+static bool window_x11_create_window(struct skh_window *window, s32 width, s32 height,
+ const char *monitor, const char *name, s32 flags)
+{
+ struct skh_window_x11 *xw = (struct skh_window_x11 *)window;
+ (void)monitor;
+
+ xw->x11_d = XOpenDisplay(NULL);
+ if (!xw->x11_d) {
+ al_log_error("window_x11", "Couldn't open display.");
+ return false;
+ }
+
+ s32 screen = XDefaultScreen(xw->x11_d);
+ Window root = RootWindow(xw->x11_d, screen);
+
+ XSetWindowAttributes swa;
+ swa.event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
+ PointerMotionMask | ExposureMask | StructureNotifyMask |
+ PropertyChangeMask | VisibilityChangeMask;
+
+ xw->window = XCreateWindow(xw->x11_d, root, 0, 0, width, height,
+ 0, CopyFromParent, InputOutput, CopyFromParent, CWEventMask, &swa);
+
+ ATOM_WM_DELETE_WINDOW = XInternAtom(xw->x11_d, "WM_DELETE_WINDOW", False);
+ XSetWMProtocols(xw->x11_d, xw->window, &ATOM_WM_DELETE_WINDOW, 1);
+
+ XStoreName(xw->x11_d, xw->window, name);
+
+ XSetWindowAttributes xattr;
+ xattr.override_redirect = False;
+ XChangeWindowAttributes(xw->x11_d, xw->window, CWOverrideRedirect, &xattr);
+
+ XMapWindow(xw->x11_d, xw->window);
+ XFlush(xw->x11_d);
+
+ XWindowAttributes gwa;
+ XGetWindowAttributes(xw->x11_d, xw->window, &gwa);
+ xw->w.width = gwa.width;
+ xw->w.height = gwa.height;
+
+ if (flags & SEKIHI_WINDOW_WALLPAPER)
+ {
+ /*
+ Atom desktop = XInternAtom(xw->x11_d, "_NET_WM_DESKTOP", False);
+ unsigned long all_desktops = XWIN_ALL_DESKTOPS;
+ XChangeProperty(xw->x11_d, xw->window, desktop,
+ XA_CARDINAL, 32, PropModeReplace, (u8 *)&all_desktops, 1);
+ */
+
+ Atom net_wm_type = XInternAtom(xw->x11_d, "_NET_WM_WINDOW_TYPE", False);
+ Atom desktop_type = XInternAtom(xw->x11_d, "_NET_WM_WINDOW_TYPE_DESKTOP", False);
+ XChangeProperty(xw->x11_d, xw->window, net_wm_type,
+ XA_ATOM, 32, PropModeReplace, (u8 *)&desktop_type, 1);
+
+ /*
+ XEvent event;
+ event.xclient.type = ClientMessage;
+ event.xclient.serial = 0;
+ event.xclient.send_event = True;
+ event.xclient.display = xw->x11_d;
+ event.xclient.window = xw->window;
+ event.xclient.message_type = XInternAtom(xw->x11_d, "_NET_WM_STATE", False);
+ event.xclient.format = 32;
+
+ event.xclient.data.l[0] = _NET_WM_STATE_ADD;
+ event.xclient.data.l[1] = XInternAtom(xw->x11_d, "_NET_WM_STATE_BELOW", False);
+ event.xclient.data.l[2] = 0; //unused.
+ event.xclient.data.l[3] = 0;
+ event.xclient.data.l[4] = 0;
+ XSendEvent(xw->x11_d, root, False, SubstructureRedirectMask | SubstructureNotifyMask, &event);
+
+ event.xclient.data.l[1] = XInternAtom(xw->x11_d, "_NET_WM_STATE_STICKY", False);
+ XSendEvent(xw->x11_d, root, False, SubstructureRedirectMask | SubstructureNotifyMask, &event);
+
+ event.xclient.data.l[1] = XInternAtom(xw->x11_d, "_NET_WM_STATE_FULLSCREEN", False);
+ */
+ }
+
+ if (!x11_egl_init(xw)) return false;
+
+ skh_egl_make_current(&xw->egl);
+ skh_egl_swap_interval(&xw->egl, (flags & SEKIHI_WINDOW_VSYNC) ? 1 : 0);
+ skh_egl_release_current(&xw->egl);
+
+ xw->fds[0].fd = ConnectionNumber(xw->x11_d);
+ xw->fds[0].events = POLLIN;
+
+#if defined SEKIHI_POLL_INLINE && defined SEKIHI_PAUSE
+ xw->fds[1].fd = eventfd(0, 0);
+ xw->fds[1].events = POLLIN;
+#endif
+
+ return true;
+}
+
+static void window_x11_resize(struct skh_window *window, s32 width, s32 height)
+{
+ struct skh_window_x11 *xw = (struct skh_window_x11 *)window;
+ (void)xw;
+ (void)width;
+ (void)height;
+}
+
+static void window_x11_toggle_fullscreen(struct skh_window *window)
+{
+ struct skh_window_x11 *xw = (struct skh_window_x11 *)window;
+ if (!xw->w.fullscreen) {
+ } else {
+ }
+ xw->w.fullscreen = !xw->w.fullscreen;
+}
+
+#if defined SEKIHI_POLL_FD
+static s32 window_x11_get_poll_fd(struct skh_window *window)
+{
+ struct skh_window_x11 *xw = (struct skh_window_x11 *)window;
+ return xw->fds[0].fd;
+}
+#elif defined SEKIHI_POLL_INLINE
+static bool window_x11_poll(struct skh_window *window, bool block)
+{
+ struct skh_window_x11 *xw = (struct skh_window_x11 *)window;
+ return skh_poll_common(xw->fds, block);
+}
+
+#if defined SEKIHI_PAUSE
+static void window_x11_wake(struct skh_window *window)
+{
+ struct skh_window_x11 *xw = (struct skh_window_x11 *)window;
+ skh_wake_common(&xw->w, xw->fds);
+}
+#endif
+#endif
+
+static void handle_next_xevent(struct skh_window_x11 *xw)
+{
+ XEvent xev;
+ XNextEvent(xw->x11_d, &xev);
+ switch (xev.type) {
+ case ClientMessage:
+ if ((Atom)xev.xclient.data.l[0] == ATOM_WM_DELETE_WINDOW) {
+ skh_window_send_should_close_event(&xw->w);
+ }
+ break;
+ case ConfigureNotify:
+ xw->w.width = xev.xconfigure.width;
+ xw->w.height = xev.xconfigure.height;
+ skh_window_send_resize_event(&xw->w, xw->w.width, xw->w.height);
+ break;
+ case KeyPress:
+ skh_window_send_key_event(&xw->w, SEKIHI_BUTTON_PRESSED, xev.xkey.keycode);
+ break;
+ case KeyRelease:
+ skh_window_send_key_event(&xw->w, SEKIHI_BUTTON_RELEASED, xev.xkey.keycode);
+ break;
+ case ButtonPress:
+ if (xev.xbutton.button == Button1) {
+ skh_window_send_mouse_button_event(&xw->w, SEKIHI_BUTTON_PRESSED, SEKIHI_MOUSE1);
+ } else if (xev.xbutton.button == Button2) {
+ skh_window_send_mouse_button_event(&xw->w, SEKIHI_BUTTON_PRESSED, SEKIHI_MOUSE2);
+ } else if (xev.xbutton.button == Button4) {
+ skh_window_send_scroll_event(&xw->w, -1.f);
+ } else if (xev.xbutton.button == Button5) {
+ skh_window_send_scroll_event(&xw->w, 1.f);
+ }
+ break;
+ case ButtonRelease:
+ if (xev.xbutton.button == Button1) {
+ skh_window_send_mouse_button_event(&xw->w, SEKIHI_BUTTON_RELEASED, SEKIHI_MOUSE1);
+ } else if (xev.xbutton.button == Button2) {
+ skh_window_send_mouse_button_event(&xw->w, SEKIHI_BUTTON_RELEASED, SEKIHI_MOUSE2);
+ }
+ break;
+ case MotionNotify:
+ skh_window_send_pointer_pos_event(&xw->w, (f64)xev.xmotion.x, (f64)xev.xmotion.y);
+ break;
+ case MapNotify:
+ break;
+ }
+}
+
+static void window_x11_process_events(struct skh_window *window)
+{
+ struct skh_window_x11 *xw = (struct skh_window_x11 *)window;
+ skh_process_events_common(xw->fds);
+ // It seems for some reason POLLIN doesn't get set even
+ // when there's events.
+ XPending(xw->x11_d);
+ while (QLength(xw->x11_d) > 0) {
+ handle_next_xevent(xw);
+ }
+}
+
+#if defined SEKIHI_PAUSE
+static bool window_x11_should_refresh(struct skh_window *window)
+{
+ return skh_should_refresh_common(window);
+}
+#endif
+
+static void window_x11_free(struct skh_window **window)
+{
+ struct skh_window_x11 *xw = (struct skh_window_x11 *)*window;
+ // TODO: Cleanup x11 stuff.
+#if defined SEKIHI_EVENT_BUFFER
+ al_free(xw->w.data);
+#endif
+ al_array_free(xw->w.monitors);
+ al_free(xw);
+ *window = NULL;
+}
+
+#if defined SEKIHI_API_VULKAN
+static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL window_x11_get_vk_proc_address(VkInstance inst, const char *name)
+{
+ return NULL;
+}
+
+static VkResult window_x11_vk_create_surface(void *data, VkInstance inst, VkSurfaceKHR *surface)
+{
+ struct skh_window_x11 *xw = (struct skh_window_x11 *)data;
+ return VK_SUCCESS;
+}
+
+static const char *const *window_x11_vk_get_extensions(u32 *num)
+{
+ return NULL;
+}
+#elif defined SEKIHI_API_OPENGL
+static bool window_x11_gl_loader_load(void *data)
+{
+ struct skh_window_x11 *xw = (struct skh_window_x11 *)data;
+ if (!skh_gl_loader_load(xw->w.get_gl_proc_address)) {
+ return false;
+ }
+ return true;
+}
+
+static bool window_x11_gl_make_current(void *data)
+{
+ struct skh_window_x11 *xw = (struct skh_window_x11 *)data;
+ skh_egl_make_current(&xw->egl);
+ return true;
+}
+
+static void window_x11_gl_release_current(void *data)
+{
+ struct skh_window_x11 *xw = (struct skh_window_x11 *)data;
+ skh_egl_release_current(&xw->egl);
+}
+
+static void window_x11_gl_swap_buffers(void *data)
+{
+ struct skh_window_x11 *xw = (struct skh_window_x11 *)data;
+ skh_egl_swap_buffers(&xw->egl);
+}
+#endif
+
+struct skh_window *skh_window_x11_create(void)
+{
+ struct skh_window_x11 *xw = al_alloc_object(struct skh_window_x11);
+#if defined SEKIHI_EVENT_BUFFER
+ xw->w.data = al_malloc(SEKIHI_EVENTS_SIZE);
+ al_ring_buffer_init(&xw->w.events, xw->w.data, SEKIHI_EVENTS_SIZE);
+#endif
+#if defined SEKIHI_PAUSE
+ xw->w.pending_refresh = true;
+#endif
+ xw->w.fullscreen = false;
+ al_array_init(xw->w.monitors);
+ xw->w.create_context = NULL;
+ xw->w.create_window = window_x11_create_window;
+ xw->w.resize = window_x11_resize;
+ xw->w.toggle_fullscreen = window_x11_toggle_fullscreen;
+#if defined SEKIHI_POLL_FD
+ xw->w.get_poll_fd = window_x11_get_poll_fd;
+#elif defined SEKIHI_POLL_INLINE
+ xw->w.poll = window_x11_poll;
+#if defined SEKIHI_PAUSE
+ xw->w.wake = window_x11_wake;
+#endif
+#endif
+ xw->w.process_events = window_x11_process_events;
+#if defined SEKIHI_PAUSE
+ xw->w.should_refresh = window_x11_should_refresh;
+#endif
+ xw->w.free = window_x11_free;
+#if defined SEKIHI_API_VULKAN
+ xw->w.get_vk_proc_address = window_x11_get_vk_proc_address;
+ xw->w.vk_create_surface = window_x11_vk_create_surface;
+ xw->w.vk_get_extensions = window_x11_vk_get_extensions;
+#elif defined SEKIHI_API_OPENGL
+ xw->w.get_gl_proc_address = NULL;
+ xw->w.gl_loader_load = window_x11_gl_loader_load;
+ xw->w.gl_make_current = window_x11_gl_make_current;
+ xw->w.gl_release_current = window_x11_gl_release_current;
+ xw->w.gl_swap_buffers = window_x11_gl_swap_buffers;
+#elif defined SEKIHI_API_DX11
+#endif
+ return (struct skh_window *)xw;
+}
diff --git a/src/window_x11.h b/src/window_x11.h
new file mode 100644
index 0000000..452a650
--- /dev/null
+++ b/src/window_x11.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <aki/socket.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+#include "window.h"
+#include "egl_common.h"
+
+struct skh_window_x11 {
+ struct skh_window w;
+ Display *x11_d;
+ Window window;
+ struct skh_egl egl;
+#if defined SEKIHI_PAUSE
+ struct aki_pollfd fds[2];
+#else
+ struct aki_pollfd fds[1];
+#endif
+};
+
+struct skh_window *skh_window_x11_create(void);