diff options
| author | 2023-12-31 20:57:42 -0500 | |
|---|---|---|
| committer | 2023-12-31 20:57:42 -0500 | |
| commit | 5a838b06b3889a0744a2afee2efe8d38726d7f72 (patch) | |
| tree | 54cbba7097e8582c2c67a7baa0c8a8787ecef0de | |
| parent | 5a7b8f02bf073a83ad2227a5a403ad468340c1de (diff) | |
| download | stela-5a838b06b3889a0744a2afee2efe8d38726d7f72.tar.gz stela-5a838b06b3889a0744a2afee2efe8d38726d7f72.tar.bz2 stela-5a838b06b3889a0744a2afee2efe8d38726d7f72.zip | |
Update
- Add x11 backend
- Add option to set a window as a desktop background (wayland only)
- Major overhaul of options
Signed-off-by: Andrew Opalach <andrew@akon.city>
| -rw-r--r-- | meson.build | 138 | ||||
| -rw-r--r-- | meson_options.txt | 5 | ||||
| -rw-r--r-- | src/egl_common.c | 41 | ||||
| -rw-r--r-- | src/egl_common.h | 23 | ||||
| -rw-r--r-- | src/gl_common.c | 46 | ||||
| -rw-r--r-- | src/gl_common.h | 22 | ||||
| -rw-r--r-- | src/gl_versions.h | 2 | ||||
| -rw-r--r-- | src/poll_common.c | 69 | ||||
| -rw-r--r-- | src/poll_common.h | 16 | ||||
| -rw-r--r-- | src/sekihi.c | 13 | ||||
| -rw-r--r-- | src/window.c | 78 | ||||
| -rw-r--r-- | src/window.h | 56 | ||||
| -rw-r--r-- | src/window_glfw.c | 105 | ||||
| -rw-r--r-- | src/window_glfw.h | 2 | ||||
| -rw-r--r-- | src/window_wayland.c | 672 | ||||
| -rw-r--r-- | src/window_wayland.h | 37 | ||||
| -rw-r--r-- | src/window_x11.c | 342 | ||||
| -rw-r--r-- | src/window_x11.h | 23 |
18 files changed, 1333 insertions, 357 deletions
diff --git a/meson.build b/meson.build index a5f7212..09c9fed 100644 --- a/meson.build +++ b/meson.build @@ -2,41 +2,103 @@ project('sekihi', 'c', version: '0.1', default_options: ['warning_level=2', 'c_std=c99']) compiler = meson.get_compiler('c') +cmake = import('cmake') + +is_debug = get_option('buildtype').startswith('debug') + +is_linux = host_machine.system() == 'linux' +is_windows = host_machine.system() == 'windows' +is_macos = host_machine.system() == 'darwin' alabaster = subproject('libalabaster').get_variable('alabaster') -akiyo = subproject('libakiyo').get_variable('akiyo') +akiyo = subproject('libakiyo', default_options: ['event-loop=disabled']).get_variable('akiyo') -sekihi_src = ['src/sekihi.c', 'src/window.c'] +sekihi_src = ['src/sekihi.c', 'src/poll_common.c', 'src/window.c'] +sekihi_inc = [include_directories('include')] sekihi_deps = [alabaster, akiyo] +sekihi_args = [] -glfw3 = dependency('glfw3', required: false, allow_fallback: true) -if glfw3.found() - sekihi_src += ['src/window_glfw.c'] - sekihi_deps += [glfw3] +if get_option('poll') == 'inline' + sekihi_args += ['-DSEKIHI_POLL_INLINE'] +elif get_option('poll') == 'fd' + sekihi_args += ['-DSEKIHI_POLL_FD'] endif -glad = subproject('glad').get_variable('glad') -sekihi_deps += [glad] +if get_option('event-buffer') + sekihi_args += ['-DSEKIHI_EVENT_BUFFER'] +endif -#brcmEGL = compiler.find_library('brcmEGL') -#brcmGLESv2 = compiler.find_library('brcmGLESv2') -#sekihi_deps += [brcmEGL, brcmGLESv2] +if get_option('pause') + sekihi_args += ['-DSEKIHI_PAUSE'] +endif -egl = dependency('egl', required: false) -if egl.found() - sekihi_src += ['src/egl_common.c'] - sekihi_deps += [egl] +if get_option('api') == 'opengl' + use_glad = true + if use_glad + sekihi_deps += [subproject('glad').get_variable('glad')] + sekihi_args += ['-DSEKIHI_USE_GLAD'] + else + sekihi_deps += [dependency('gl')] + endif + sekihi_src += ['src/gl_common.c'] + sekihi_args += ['-DSEKIHI_API_OPENGL'] + if get_option('window') in ['x11', 'wayland'] + egl = dependency('egl') + #brcmEGL = compiler.find_library('brcmEGL') + #brcmGLESv2 = compiler.find_library('brcmGLESv2') + sekihi_src += ['src/egl_common.c'] + sekihi_deps += [egl] + #sekihi_deps += [brcmEGL, brcmGLESv2] + endif +elif get_option('api') == 'vulkan' + vulkan = dependency('vulkan') + vulkan_headers = vulkan.partial_dependency(includes: true, compile_args: true) + sekihi_deps += [vulkan_headers] + sekihi_args += ['-DSEKIHI_API_VULKAN'] endif -wl_client = dependency('wayland-client', required: false) -wl_egl = dependency('wayland-egl', required: false) -if wl_client.found() and wl_egl.found() +if get_option('window') == 'glfw' + glfw3 = dependency('glfw3', required: false, allow_fallback: false) + if not glfw3.found() + glfw_opts = cmake.subproject_options() + glfw_opts.add_cmake_defines({ 'CMAKE_BUILD_TYPE': is_debug ? 'Debug' : 'Release' }) + glfw_opts.add_cmake_defines({ 'GLFW_INSTALL': false }) + glfw_opts.add_cmake_defines({ 'GLFW_BUILD_EXAMPLES': false }) + glfw_opts.add_cmake_defines({ 'GLFW_BUILD_TESTS': false }) + glfw_opts.add_cmake_defines({ 'GLFW_BUILD_DOCS': false }) + #glfw_opts.add_cmake_defines({ 'GLFW_USE_WAYLAND': is_linux }) + glfw_opts.add_cmake_defines({ 'GLFW_BUILD_WAYLAND': is_linux }) + glfw_opts.add_cmake_defines({ 'GLFW_BUILD_X11': is_linux }) + glfw_opts.add_cmake_defines({ 'GLFW_BUILD_WIN32': is_windows }) + glfw_opts.add_cmake_defines({ 'GLFW_BUILD_COCAO': is_macos }) + glfw3 = cmake.subproject('glfw3', options: glfw_opts).dependency('glfw3') + endif + sekihi_src += ['src/window_glfw.c'] + sekihi_deps += [glfw3] + sekihi_args += ['-DSEKIHI_WINDOW_GLFW'] +elif get_option('window') == 'x11' + x11 = dependency('X11', required: false) + if x11.found() + sekihi_src += ['src/window_x11.c'] + sekihi_deps += [x11] + endif + sekihi_args += ['-DSEKIHI_WINDOW_X11'] +elif get_option('window') == 'wayland' + wl_client = dependency('wayland-client') + wl_egl = dependency('wayland-egl') wl_protocols = dependency('wayland-protocols', required: false) if not wl_protocols.found() wl_protocols_datadir = subproject('wayland-protocols').get_variable('wayland_protocols_srcdir') else wl_protocols_datadir = wl_protocols.get_variable(pkgconfig: 'pkgdatadir') endif + wlr_protocols = dependency('wlr-protocols', required: false) + if not wlr_protocols.found() + wlr_protocols_datadir = subproject('wlr-protocols').get_variable('wlr_protocols_srcdir') + else + # Untested + wlr_protocols_datadir = wlr_protocols.get_variable(pkgconfig: 'pkgdatadir') + endif scanner = find_program('wayland-scanner') scanner_private_code = generator(scanner, output: '@BASENAME@-protocol.c', arguments: ['private-code','@INPUT@', '@OUTPUT@']) @@ -47,34 +109,42 @@ if wl_client.found() and wl_egl.found() wl_protocols_datadir + '/stable/xdg-shell/xdg-shell.xml'), scanner_private_code.process( wl_protocols_datadir + '/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml'), -# scanner_private_code.process( -# wl_protocols_datadir + '/stable/presentation-time/presentation-time.xml') + #scanner_private_code.process( + # wl_protocols_datadir + '/unstable/xdg-output/xdg-output-unstable-v1.xml'), + scanner_private_code.process( + wl_protocols_datadir + '/stable/presentation-time/presentation-time.xml'), + scanner_private_code.process( + wlr_protocols_datadir + '/unstable/wlr-layer-shell-unstable-v1.xml'), + #scanner_private_code.process( + # wlr_protocols_datadir + '/unstable/wlr-foreign-toplevel-management-unstable-v1.xml') ] protocols_headers = [ scanner_client_header.process( wl_protocols_datadir + '/stable/xdg-shell/xdg-shell.xml'), scanner_client_header.process( wl_protocols_datadir + '/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml'), -# scanner_client_header.process( -# wl_protocols_datadir + '/stable/presentation-time/presentation-time.xml') + #scanner_client_header.process( + # wl_protocols_datadir + '/unstable/xdg-output/xdg-output-unstable-v1.xml'), + scanner_client_header.process( + wl_protocols_datadir + '/stable/presentation-time/presentation-time.xml'), + scanner_client_header.process( + wlr_protocols_datadir + '/unstable/wlr-layer-shell-unstable-v1.xml'), + #scanner_client_header.process( + # wlr_protocols_datadir + '/unstable/wlr-foreign-toplevel-management-unstable-v1.xml') ] lib_protocols = static_library( 'protocols', protocols_src + protocols_headers, dependencies: wl_client) protocols = declare_dependency(link_with: lib_protocols, sources: protocols_headers) sekihi_src += ['src/window_wayland.c'] sekihi_deps += [wl_egl, protocols] + sekihi_args += ['-DSEKIHI_WINDOW_WAYLAND'] endif -vulkan = dependency('vulkan') -vulkan_headers = vulkan.partial_dependency(includes: true, compile_args: true) -sekihi_deps += [vulkan_headers] +sekihi = declare_dependency(include_directories: sekihi_inc, + dependencies: sekihi_deps, sources: sekihi_src, + compile_args: sekihi_args) -#sekihi_lib = shared_library('sekihi', sekihi_src, -# install: false, -# c_args: ['-DSEKIHI_WINDOW_NULL', '-DSEKIHI_API_NULL', '-DSEKIHI_POLL_INLINE'], -# gnu_symbol_visibility: 'hidden', -# dependencies: sekihi_deps) - -sekihi = declare_dependency(sources: sekihi_src, - include_directories: include_directories('./include'), - dependencies: [sekihi_deps]) +if not meson.is_subproject() + sekihi_lib = shared_library('sekihi', [], + gnu_symbol_visibility: 'hidden', dependencies: sekihi, install: false) +endif diff --git a/meson_options.txt b/meson_options.txt index e69de29..8eed019 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -0,0 +1,5 @@ +option('api', type: 'combo', choices: ['opengl', 'vulkan', 'dx11'], value: 'opengl') +option('window', type: 'combo', choices: ['glfw', 'wayland', 'x11'], value: 'glfw') +option('poll', type: 'combo', choices: ['inline', 'fd'], value: 'inline') +option('event-buffer', type: 'boolean', value: false) +option('pause', type: 'boolean', value: false) 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, ®istry_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, ®istry_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); |