diff options
author | Phil Jones <philj56@gmail.com> | 2021-10-28 13:07:25 +0100 |
---|---|---|
committer | Phil Jones <philj56@gmail.com> | 2021-10-28 13:07:47 +0100 |
commit | 3bf156c9d9598acbee38c49d17cdb7d77e79d520 (patch) | |
tree | 1566c551e1aa6bdb3a96a19c42c632d98dac8410 | |
parent | 42679f7e53364e8de8de665ac7ba9b7e2dd1970b (diff) |
Add basic HiDPI scaling and a subsurface.
-rw-r--r-- | meson.build | 2 | ||||
-rw-r--r-- | src/background.c (renamed from src/png.c) | 8 | ||||
-rw-r--r-- | src/background.h (renamed from src/png.h) | 0 | ||||
-rw-r--r-- | src/client.h | 67 | ||||
-rw-r--r-- | src/egl.c | 32 | ||||
-rw-r--r-- | src/egl.h | 9 | ||||
-rw-r--r-- | src/gl.c | 31 | ||||
-rw-r--r-- | src/main.c | 294 |
8 files changed, 277 insertions, 166 deletions
diff --git a/meson.build b/meson.build index 5439ee2..7f1439e 100644 --- a/meson.build +++ b/meson.build @@ -45,10 +45,10 @@ add_project_arguments( sources = files( 'src/main.c', + 'src/background.c', 'src/egl.c', 'src/gl.c', 'src/log.c', - 'src/png.c', 'src/xdg-shell-protocol.c', ) diff --git a/src/png.c b/src/background.c index af96c8f..68beb5e 100644 --- a/src/png.c +++ b/src/background.c @@ -4,7 +4,7 @@ #include <png.h> #include "client.h" #include "log.h" -#include "png.h" +#include "background.h" #define HEADER_BYTES 8 @@ -85,7 +85,7 @@ void load_background(struct client_state *state, const char *filename) free(row_pointers); fclose(fp); - state->background_image.width = width; - state->background_image.height = height; - state->background_image.buffer = buffer; + state->window.background_image.width = width; + state->window.background_image.height = height; + state->window.background_image.buffer = buffer; } diff --git a/src/png.h b/src/background.h index 68b432a..68b432a 100644 --- a/src/png.h +++ b/src/background.h diff --git a/src/client.h b/src/client.h index 32670af..4814fdb 100644 --- a/src/client.h +++ b/src/client.h @@ -6,38 +6,65 @@ #include "egl.h" #include "gl.h" +struct surface { + struct egl egl; + struct gl gl; + struct wl_surface *wl_surface; + int32_t width; + int32_t height; + bool redraw; +}; + +struct image { + uint8_t *buffer; + uint32_t width; + uint32_t height; +}; + +struct color { + float r; + float g; + float b; + float a; +}; + struct client_state { /* Globals */ struct wl_display *wl_display; struct wl_registry *wl_registry; - struct wl_shm *wl_shm; struct wl_compositor *wl_compositor; + struct wl_subcompositor *wl_subcompositor; struct wl_seat *wl_seat; + struct wl_output *wl_output; struct xdg_wm_base *xdg_wm_base; + + uint32_t wl_display_name; + uint32_t wl_registry_name; + uint32_t wl_compositor_name; + uint32_t wl_subcompositor_name; + uint32_t wl_seat_name; + uint32_t wl_output_name; + uint32_t xdg_wm_base_name; + /* Objects */ - struct wl_surface *wl_surface; struct wl_keyboard *wl_keyboard; - struct xdg_surface *xdg_surface; - struct xdg_toplevel *xdg_toplevel; + /* State */ - float offset; - uint32_t last_frame; - int width; - int height; bool closed; - struct egl egl; - struct gl gl; struct { - uint8_t *buffer; - uint32_t width; - uint32_t height; - } background_image; - struct color { - float r; - float g; - float b; - float a; - } background_color; + struct surface surface; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + struct image background_image; + struct color background_color; + struct { + struct surface surface; + struct wl_subsurface *wl_subsurface; + struct image image; + } entry; + uint32_t scale; + } window; + /* Keyboard state */ struct xkb_state *xkb_state; struct xkb_context *xkb_context; @@ -1,28 +1,26 @@ #include <assert.h> #include <string.h> #include <wayland-egl.h> -#include "client.h" +#include "egl.h" #include "log.h" -static void egl_log_error(); static const char *egl_error_string(); -void egl_create_window(struct client_state *state) +void egl_create_window(struct egl *egl, struct wl_surface *wl_surface, uint32_t width, uint32_t height) { - state->egl.window = wl_egl_window_create( - state->wl_surface, - state->width, - state->height); + egl->window = wl_egl_window_create( + wl_surface, + width, + height); - if (state->egl.window == EGL_NO_SURFACE) { + if (egl->window == EGL_NO_SURFACE) { egl_log_error("Couldn't create EGL window"); } } -void egl_create_context(struct client_state *state) +void egl_create_context(struct egl *egl, struct wl_display *wl_display) { - struct egl *egl = &state->egl; - egl->display = eglGetDisplay(state->wl_display); + egl->display = eglGetDisplay(wl_display); if (egl->display == EGL_NO_DISPLAY) { egl_log_error("Couldn't get EGL display"); return; @@ -85,6 +83,18 @@ void egl_log_error(const char *msg) { log_error("%s: %s\n", msg, egl_error_string()); } +void egl_make_current(struct egl *egl) { + bool result = eglMakeCurrent(egl->display, egl->surface, egl->surface, egl->context); + if (!result) { + egl_log_error("Couldn't make EGL context current"); + return; + } +} + +void egl_swap_buffers(struct egl *egl) { + eglSwapBuffers(egl->display, egl->surface); +} + const char *egl_error_string() { switch(eglGetError()) { case EGL_SUCCESS: @@ -1,9 +1,9 @@ #ifndef EGL_H #define EGL_H +#include <wayland-egl.h> #include <epoxy/egl.h> -struct client_state; struct egl { EGLNativeWindowType window; EGLDisplay display; @@ -11,7 +11,10 @@ struct egl { EGLSurface surface; }; -void egl_create_window(struct client_state *state); -void egl_create_context(struct client_state *state); +void egl_create_window(struct egl *egl, struct wl_surface *wl_surface, uint32_t width, uint32_t height); +void egl_create_context(struct egl *egl, struct wl_display *wl_display); +void egl_log_error(const char *msg); +void egl_make_current(struct egl *egl); +void egl_swap_buffers(struct egl *egl); #endif /* EGL_H */ @@ -14,11 +14,11 @@ static GLuint create_shader_program(const char *vert, const char *frag); void gl_initialise(struct client_state *state) { - if (state->background_image.buffer == NULL) { + if (state->window.background_image.buffer == NULL) { return; } - struct gl *gl = &state->gl; + struct gl *gl = &state->window.surface.gl; /* Compile and link the shader programs */ gl->shader = create_shader_program( @@ -71,8 +71,8 @@ void gl_initialise(struct client_state *state) /* Create the texture we'll draw to */ glGenTextures(1, &gl->texture); glBindTexture(GL_TEXTURE_2D, gl->texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, state->background_image.width, state->background_image.height, 0, GL_RGBA, - GL_UNSIGNED_BYTE, (GLvoid *)state->background_image.buffer); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, state->window.background_image.width, state->window.background_image.height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, (GLvoid *)state->window.background_image.buffer); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -86,26 +86,25 @@ void gl_initialise(struct client_state *state) void gl_draw(struct client_state *state) { glClearColor( - state->background_color.r, - state->background_color.g, - state->background_color.b, - state->background_color.a + state->window.background_color.r, + state->window.background_color.g, + state->window.background_color.b, + state->window.background_color.a ); glClear(GL_COLOR_BUFFER_BIT); - if (state->background_image.buffer == NULL) { + if (state->window.background_image.buffer == NULL) { return; } double scale = max( - (double)state->width / state->background_image.width, - (double)state->height / state->background_image.height + (double)state->window.surface.width / state->window.background_image.width, + (double)state->window.surface.height / state->window.background_image.height ); - uint32_t width = (uint32_t)(scale * state->background_image.width); - uint32_t height = (uint32_t)(scale * state->background_image.height); - int32_t x = -((int32_t)width - (int32_t)state->width) / 2; - int32_t y = -((int32_t)height - (int32_t)state->height) / 2; - printf("%d, %d\n", x, y); + uint32_t width = (uint32_t)(scale * state->window.background_image.width); + uint32_t height = (uint32_t)(scale * state->window.background_image.height); + int32_t x = -((int32_t)width - (int32_t)state->window.surface.width) / 2; + int32_t y = -((int32_t)height - (int32_t)state->window.surface.height) / 2; glViewport(x, y, width, height); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); } @@ -10,18 +10,26 @@ #include <sys/mman.h> #include <unistd.h> #include <wayland-client.h> +#include <wctype.h> #include <xkbcommon/xkbcommon.h> +#include <locale.h> #include "client.h" #include "egl.h" #include "gl.h" -#include "png.h" +#include "background.h" #include "xdg-shell-client-protocol.h" -void draw_frame(struct client_state *state) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +static void wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time); +static const struct wl_callback_listener wl_surface_frame_listener = { + .done = wl_surface_frame_done, +}; + +void draw_frame(struct surface *surface) { - gl_draw(state); - eglSwapBuffers(state->egl.display, state->egl.surface); - wl_surface_damage_buffer(state->wl_surface, 0, 0, 50, 50); + printf("DRAW\n"); + surface->redraw = true; } static void @@ -35,11 +43,24 @@ xdg_toplevel_configure(void *data, return; } - if (width != state->width || height != state->height) { - state->width = width; - state->height = height; - wl_egl_window_resize(state->egl.window, width, height, 0, 0); - draw_frame(state); + int32_t scaled_width = width * state->window.scale; + int32_t scaled_height = height * state->window.scale; + + if (scaled_width != state->window.surface.width || scaled_height != state->window.surface.height) { + state->window.surface.width = scaled_width; + state->window.surface.height = scaled_height; + wl_egl_window_resize(state->window.surface.egl.window, scaled_width, scaled_height, 0, 0); + + printf("%d, %d\n", + (width - state->window.entry.surface.width / state->window.scale) / 2, + (height - state->window.entry.surface.height / state->window.scale) / 2 + ); + wl_subsurface_set_position(state->window.entry.wl_subsurface, + (width - state->window.entry.surface.width / state->window.scale) / 2, + (height - state->window.entry.surface.height / state->window.scale) / 2 + ); + wl_surface_commit(state->window.entry.surface.wl_surface); + draw_frame(&state->window.surface); } } @@ -94,19 +115,6 @@ wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { - struct client_state *client_state = data; - fprintf(stderr, "keyboard enter; keys pressed are:\n"); - uint32_t *key; - wl_array_for_each(key, keys) { - char buf[128]; - xkb_keysym_t sym = xkb_state_key_get_one_sym( - client_state->xkb_state, *key + 8); - xkb_keysym_get_name(sym, buf, sizeof(buf)); - fprintf(stderr, "sym: %-12s (%d), ", buf, sym); - xkb_state_key_get_utf8(client_state->xkb_state, - *key + 8, buf, sizeof(buf)); - fprintf(stderr, "utf8: '%s'\n", buf); - } } static void @@ -116,17 +124,15 @@ wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard, struct client_state *client_state = data; char buf[128]; uint32_t keycode = key + 8; - xkb_keysym_t sym = xkb_state_key_get_one_sym( - client_state->xkb_state, keycode); + xkb_keysym_t sym = xkb_state_key_get_one_sym(client_state->xkb_state, keycode); xkb_keysym_get_name(sym, buf, sizeof(buf)); - const char *action = - state == WL_KEYBOARD_KEY_STATE_PRESSED ? "press" : "release"; - fprintf(stderr, "key %s: sym: %-12s (%d), ", action, buf, sym); - xkb_state_key_get_utf8(client_state->xkb_state, keycode, - buf, sizeof(buf)); - fprintf(stderr, "utf8: '%s'\n", buf); if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { - draw_frame(client_state); + int len = xkb_state_key_get_utf8(client_state->xkb_state, keycode, buf, sizeof(buf)); + wchar_t ch; + mbtowc(&ch, buf, sizeof(buf)); + if (len > 0 && iswprint(ch)) { + fprintf(stderr, "%s\n", buf); + } } } @@ -134,7 +140,6 @@ static void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) { - fprintf(stderr, "keyboard leave\n"); } static void @@ -152,7 +157,6 @@ static void wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) { - /* Left as an exercise for the reader */ } static const struct wl_keyboard_listener wl_keyboard_listener = { @@ -184,7 +188,7 @@ wl_seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) static void wl_seat_name(void *data, struct wl_seat *wl_seat, const char *name) { - fprintf(stderr, "seat name: %s\n", name); + fprintf(stderr, "seat name: %s\n", name); } static const struct wl_seat_listener wl_seat_listener = { @@ -202,64 +206,88 @@ static const struct xdg_wm_base_listener xdg_wm_base_listener = { .ping = xdg_wm_base_ping, }; -static const struct wl_callback_listener wl_surface_frame_listener; - static void wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) { /* Destroy this callback */ wl_callback_destroy(cb); + fprintf(stderr, "callback\n"); /* Request another frame */ struct client_state *state = data; - cb = wl_surface_frame(state->wl_surface); + cb = wl_surface_frame(state->window.surface.wl_surface); wl_callback_add_listener(cb, &wl_surface_frame_listener, state); - /* Update scroll amount at 24 pixels per second */ - if (state->last_frame != 0) { - int elapsed = time - state->last_frame; - state->offset += elapsed / 1000.0 * 24; - } - /* Submit a frame for this event */ - wl_surface_commit(state->wl_surface); + wl_surface_commit(state->window.surface.wl_surface); +} - state->last_frame = time; +static void output_geometry(void *data, struct wl_output *wl_output, + int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, + int32_t subpixel, const char *make, const char *model, int32_t transform) +{ + /* Deliberately left blank */ } -static const struct wl_callback_listener wl_surface_frame_listener = { - .done = wl_surface_frame_done, +static void output_mode(void *data, struct wl_output *wl_output, + uint32_t flags, int32_t width, int32_t height, int32_t refresh) +{ + /* Deliberately left blank */ +} + +static void output_scale(void *data, struct wl_output *wl_output, int32_t factor) +{ + struct client_state *state = data; + state->window.scale = MAX(factor, (int32_t)state->window.scale); + wl_surface_set_buffer_scale(state->window.surface.wl_surface, state->window.scale); + wl_surface_set_buffer_scale(state->window.entry.surface.wl_surface, state->window.scale); +} + +static void output_done(void *data, struct wl_output *wl_output) +{ + /* TODO */ +} + +static const struct wl_output_listener wl_output_listener = { + .geometry = output_geometry, + .mode = output_mode, + .done = output_done, + .scale = output_scale, }; -static void -registry_global(void *data, struct wl_registry *wl_registry, - uint32_t name, const char *interface, uint32_t version) +static void registry_global(void *data, struct wl_registry *wl_registry, + uint32_t name, const char *interface, uint32_t version) { - struct client_state *state = data; - if (strcmp(interface, wl_shm_interface.name) == 0) { - state->wl_shm = wl_registry_bind( - wl_registry, name, &wl_shm_interface, 1); - } else if (strcmp(interface, wl_compositor_interface.name) == 0) { - state->wl_compositor = wl_registry_bind( - wl_registry, name, &wl_compositor_interface, 4); - } else if (strcmp(interface, wl_seat_interface.name) == 0) { - state->wl_seat = wl_registry_bind( - wl_registry, name, &wl_seat_interface, 7); - wl_seat_add_listener(state->wl_seat, - &wl_seat_listener, state); - } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { - state->xdg_wm_base = wl_registry_bind( - wl_registry, name, &xdg_wm_base_interface, 1); - xdg_wm_base_add_listener(state->xdg_wm_base, - &xdg_wm_base_listener, state); - } + struct client_state *state = data; + //fprintf(stderr, "%s\n", interface); + if (strcmp(interface, wl_compositor_interface.name) == 0) { + state->wl_compositor = wl_registry_bind( + wl_registry, name, &wl_compositor_interface, 4); + } else if (strcmp(interface, wl_subcompositor_interface.name) == 0) { + state->wl_subcompositor = wl_registry_bind( + wl_registry, name, &wl_subcompositor_interface, 1); + } else if (strcmp(interface, wl_seat_interface.name) == 0) { + state->wl_seat = wl_registry_bind( + wl_registry, name, &wl_seat_interface, 7); + wl_seat_add_listener(state->wl_seat, + &wl_seat_listener, state); + } else if (strcmp(interface, wl_output_interface.name) == 0) { + state->wl_output = wl_registry_bind( + wl_registry, name, &wl_output_interface, 3); + wl_output_add_listener(state->wl_output, + &wl_output_listener, state); + } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { + state->xdg_wm_base = wl_registry_bind( + wl_registry, name, &xdg_wm_base_interface, 1); + xdg_wm_base_add_listener(state->xdg_wm_base, + &xdg_wm_base_listener, state); + } } -static void -registry_global_remove(void *data, - struct wl_registry *wl_registry, uint32_t name) +static void registry_global_remove(void *data, struct wl_registry *wl_registry, + uint32_t name) { - /* This space deliberately left blank */ + /* Deliberately left blank */ } static const struct wl_registry_listener wl_registry_listener = { @@ -267,44 +295,88 @@ static const struct wl_registry_listener wl_registry_listener = { .global_remove = registry_global_remove, }; +static void surface_enter(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output) +{ + fprintf(stderr, "enter\n"); +} + +static void surface_leave(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output) +{ + fprintf(stderr, "leave\n"); +} + +static const struct wl_surface_listener wl_surface_listener = { + .enter = surface_enter, + .leave = surface_leave +}; + int main(int argc, char *argv[]) { - struct client_state state = { 0 }; - load_background(&state, "Night-1800.png"); - state.background_color = (struct color){ 0.2, 0.8, 0.8, 1.0 }; - state.width = 640; - state.height = 480; - state.wl_display = wl_display_connect(NULL); - state.wl_registry = wl_display_get_registry(state.wl_display); - state.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - wl_registry_add_listener(state.wl_registry, &wl_registry_listener, &state); - wl_display_roundtrip(state.wl_display); - - state.wl_surface = wl_compositor_create_surface(state.wl_compositor); - state.xdg_surface = xdg_wm_base_get_xdg_surface( - state.xdg_wm_base, state.wl_surface); - xdg_surface_add_listener(state.xdg_surface, &xdg_surface_listener, &state); - state.xdg_toplevel = xdg_surface_get_toplevel(state.xdg_surface); - xdg_toplevel_add_listener(state.xdg_toplevel, - &xdg_toplevel_listener, &state); - xdg_toplevel_set_title(state.xdg_toplevel, "Greetd mini wayland greeter"); - - wl_surface_commit(state.wl_surface); - - egl_create_window(&state); - egl_create_context(&state); - gl_initialise(&state); - - struct wl_callback *cb = wl_surface_frame(state.wl_surface); - wl_callback_add_listener(cb, &wl_surface_frame_listener, &state); - - wl_display_roundtrip(state.wl_display); - draw_frame(&state); - while (!state.closed) { - wl_display_dispatch(state.wl_display); - } - - wl_display_disconnect(state.wl_display); - - return 0; + setlocale(LC_ALL, ""); + struct client_state state = { 0 }; + load_background(&state, "Night-1800.png"); + state.window.background_color = (struct color){ 0.2, 0.8, 0.8, 1.0 }; + state.window.surface.width = 640; + state.window.surface.height = 480; + state.window.entry.surface.width = 80; + state.window.entry.surface.height = 40; + state.wl_display = wl_display_connect(NULL); + state.wl_registry = wl_display_get_registry(state.wl_display); + state.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + wl_registry_add_listener(state.wl_registry, &wl_registry_listener, &state); + wl_display_roundtrip(state.wl_display); + + state.window.surface.wl_surface = wl_compositor_create_surface(state.wl_compositor); + state.window.scale = 1; + wl_surface_add_listener(state.window.surface.wl_surface, &wl_surface_listener, &state); + state.window.xdg_surface = xdg_wm_base_get_xdg_surface(state.xdg_wm_base, state.window.surface.wl_surface); + xdg_surface_add_listener(state.window.xdg_surface, &xdg_surface_listener, &state); + state.window.xdg_toplevel = xdg_surface_get_toplevel(state.window.xdg_surface); + xdg_toplevel_add_listener(state.window.xdg_toplevel, + &xdg_toplevel_listener, &state); + xdg_toplevel_set_title(state.window.xdg_toplevel, "Greetd mini wayland greeter"); + wl_surface_commit(state.window.surface.wl_surface); + + state.window.entry.surface.wl_surface = wl_compositor_create_surface(state.wl_compositor); + state.window.entry.wl_subsurface = wl_subcompositor_get_subsurface(state.wl_subcompositor, state.window.entry.surface.wl_surface, state.window.surface.wl_surface); + wl_subsurface_set_desync(state.window.entry.wl_subsurface); + wl_surface_commit(state.window.entry.surface.wl_surface); + + + struct surface *surface = &state.window.surface; + egl_create_window(&surface->egl, surface->wl_surface, surface->width, surface->height); + egl_create_context(&surface->egl, state.wl_display); + gl_initialise(&state); + + surface = &state.window.entry.surface; + egl_create_window(&surface->egl, surface->wl_surface, surface->width, surface->height); + egl_create_context(&surface->egl, state.wl_display); + + + wl_display_roundtrip(state.wl_display); + egl_make_current(&state.window.surface.egl); + eglSwapBuffers(state.window.surface.egl.display, state.window.surface.egl.surface); + egl_make_current(&state.window.entry.surface.egl); + eglSwapBuffers(state.window.entry.surface.egl.display, state.window.entry.surface.egl.surface); + draw_frame(&state.window.surface); + while (wl_display_dispatch(state.wl_display) != -1) { + if (state.closed) { + break; + } + if (state.window.surface.redraw) { + egl_make_current(&state.window.surface.egl); + gl_draw(&state); + egl_swap_buffers(&state.window.surface.egl); + state.window.surface.redraw = false; + } + if (state.window.entry.surface.redraw) { + egl_make_current(&state.window.entry.surface.egl); + egl_swap_buffers(&state.window.entry.surface.egl); + state.window.entry.surface.redraw = false; + } + } + + wl_display_disconnect(state.wl_display); + + return 0; } |