From c691b8e48c572e2d5f1c7c16c8f42babd7d706d5 Mon Sep 17 00:00:00 2001 From: Phil Jones Date: Fri, 29 Oct 2021 13:23:44 +0100 Subject: Basic text entry working. --- meson.build | 6 ++- src/client.h | 33 ++------------- src/entry.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/entry.h | 33 +++++++++++++++ src/gl.c | 43 ++++++++++---------- src/gl.h | 9 +++-- src/main.c | 85 +++++++++++++++++---------------------- src/nelem.h | 13 ++++++ src/surface.c | 34 ++++++++++++++++ src/surface.h | 21 ++++++++++ src/util.h | 22 ++++++++++ 11 files changed, 320 insertions(+), 105 deletions(-) create mode 100644 src/entry.c create mode 100644 src/entry.h create mode 100644 src/nelem.h create mode 100644 src/surface.c create mode 100644 src/surface.h create mode 100644 src/util.h diff --git a/meson.build b/meson.build index 7f1439e..a8f7cfb 100644 --- a/meson.build +++ b/meson.build @@ -47,13 +47,17 @@ sources = files( 'src/main.c', 'src/background.c', 'src/egl.c', + 'src/entry.c', 'src/gl.c', 'src/log.c', + 'src/surface.c', 'src/xdg-shell-protocol.c', ) cc = meson.get_compiler('c') epoxy = dependency('epoxy') +glib = dependency('glib-2.0') +pangocairo = dependency('pangocairo') png = dependency('libpng') wayland_client = dependency('wayland-client') wayland_egl = dependency('wayland-egl') @@ -62,7 +66,7 @@ xkbcommon = dependency('xkbcommon') executable( 'greetd-mini-wl-greeter', sources, - dependencies: [epoxy, png, wayland_client, wayland_egl, xkbcommon], + dependencies: [epoxy, glib, pangocairo, png, wayland_client, wayland_egl, xkbcommon], install: true ) diff --git a/src/client.h b/src/client.h index 4814fdb..934d4d3 100644 --- a/src/client.h +++ b/src/client.h @@ -3,30 +3,9 @@ #include #include -#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; -}; +#include "entry.h" +#include "surface.h" +#include "util.h" struct client_state { /* Globals */ @@ -57,11 +36,7 @@ struct client_state { 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; + struct entry entry; uint32_t scale; } window; diff --git a/src/entry.c b/src/entry.c new file mode 100644 index 0000000..61b0211 --- /dev/null +++ b/src/entry.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include +#include +#include "client.h" +#include "entry.h" +#include "log.h" +#include "nelem.h" + +static void calculate_font_extents(struct entry *entry); + +void entry_init(struct entry *entry) +{ + calculate_font_extents(entry); + + /* + * Cairo uses native 32 bit integers for pixels, so if this computer is + * little endian we have to tell OpenGL to swizzle the texture. + */ + if (htonl(0xFFu) != 0xFFu) { + entry->image.swizzle = true; + } + + cairo_surface_t *surface = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, + entry->surface.width, + entry->surface.height + ); + cairo_t *cr = cairo_create(surface); + cairo_set_source_rgb(cr, 0.031, 0.031, 0); + cairo_paint(cr); + + cairo_translate(cr, entry->border.outline_width, entry->border.outline_width); + cairo_rectangle(cr, + 0, + 0, + entry->surface.width - 2*entry->border.outline_width, + entry->surface.height - 2*entry->border.outline_width + ); + cairo_clip(cr); + cairo_set_source_rgb(cr, 0.976, 0.149, 0.447); + cairo_paint(cr); + + cairo_translate(cr, entry->border.width, entry->border.width); + cairo_rectangle(cr, + 0, + 0, + entry->surface.width - 2*(entry->border.outline_width + entry->border.width), + entry->surface.height - 2*(entry->border.outline_width + entry->border.width) + ); + cairo_clip(cr); + cairo_set_source_rgb(cr, 0.106, 0.114, 0.118); + cairo_paint(cr); + + PangoLayout *layout = pango_cairo_create_layout(cr); + pango_layout_set_text(layout, "", -1); + + PangoFontDescription *font_description = pango_font_description_from_string("Rubik Bold 48"); + pango_layout_set_font_description(layout, font_description); + pango_font_description_free(font_description); + + cairo_set_source_rgb(cr, 0.973, 0.973, 0.941); + pango_cairo_update_layout(cr, layout); + pango_cairo_show_layout(cr, layout); + + entry->pangocairo.surface = surface; + entry->pangocairo.cr = cr; + entry->pangocairo.layout = layout; + entry->image.width = entry->surface.width; + entry->image.height = entry->surface.height; + entry->image.buffer = cairo_image_surface_get_data(surface); +} + +void entry_update(struct entry *entry) +{ + cairo_t *cr = entry->pangocairo.cr; + PangoLayout *layout = entry->pangocairo.layout; + cairo_set_source_rgb(cr, 0.106, 0.114, 0.118); + cairo_paint(cr); + cairo_set_source_rgb(cr, 0.973, 0.973, 0.941); + //const wchar_t *src = entry->password; + //wcsrtombs(entry->password_mb, &src, N_ELEM(entry->password_mb), NULL); + for (unsigned int i = 0; i < entry->password_length; i++) { + entry->password_mb[2 * i] = '\xC2'; + entry->password_mb[2 * i + 1] = '\xB7'; + } + entry->password_mb[2 * entry->password_length] = '\0'; + fprintf(stderr, "%s\n", entry->password_mb); + pango_layout_set_text(layout, entry->password_mb, -1); + pango_cairo_update_layout(cr, layout); + pango_cairo_show_layout(cr, layout); + entry->image.redraw = true; +} + +void calculate_font_extents(struct entry *entry) +{ + PangoFontMap *font_map = pango_cairo_font_map_get_default(); + PangoContext *context = pango_font_map_create_context(font_map); + PangoLayout *layout = pango_layout_new(context); + { + PangoFontDescription *font_description = pango_font_description_from_string("Rubik Bold 48"); + pango_layout_set_font_description(layout, font_description); + PangoFont *font = pango_font_map_load_font(font_map, context, font_description); + g_object_unref(font); + pango_font_description_free(font_description); + + font_description = pango_context_get_font_description(context); + log_info("Using family: %s\n", pango_font_description_get_family(font_description)); + } + pango_layout_set_text(layout, "············", -1); + + int width; + int height; + pango_layout_get_pixel_size(layout, &width, &height); + fprintf(stderr, "%d x %d\n", width, height); + fprintf(stderr, "%d, %d\n", entry->border.width, entry->border.outline_width); + width += 2 * (entry->border.width + entry->border.outline_width); + height += 2 * (entry->border.width + entry->border.outline_width); + entry->surface.width = width; + entry->surface.height = height; + + g_object_unref(layout); + g_object_unref(context); +} diff --git a/src/entry.h b/src/entry.h new file mode 100644 index 0000000..18276a1 --- /dev/null +++ b/src/entry.h @@ -0,0 +1,33 @@ +#ifndef ENTRY_H +#define ENTRY_H + +#include +#include "util.h" +#include "surface.h" + +#define MAX_PASSWORD_LENGTH 256 + +struct entry { + struct surface surface; + struct wl_subsurface *wl_subsurface; + struct image image; + struct { + PangoLayout *layout; + cairo_surface_t *surface; + cairo_t *cr; + } pangocairo; + struct { + struct color color; + struct color outline_color; + int32_t width; + int32_t outline_width; + } border; + wchar_t password[MAX_PASSWORD_LENGTH]; + char password_mb[4*MAX_PASSWORD_LENGTH]; + uint32_t password_length; +}; + +void entry_init(struct entry *entry); +void entry_update(struct entry *entry); + +#endif /* ENTRY_H */ diff --git a/src/gl.c b/src/gl.c index f9ff059..94262b4 100644 --- a/src/gl.c +++ b/src/gl.c @@ -12,14 +12,12 @@ static void load_shader(GLuint shader, const char *filename); static GLuint create_shader_program(const char *vert, const char *frag); -void gl_initialise(struct client_state *state) +void gl_initialise(struct gl *gl, struct image *texture) { - if (state->window.background_image.buffer == NULL) { + if (texture == NULL) { return; } - struct gl *gl = &state->window.surface.gl; - /* Compile and link the shader programs */ gl->shader = create_shader_program( SHADER_PATH "vert.vert", @@ -71,40 +69,39 @@ 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->window.background_image.width, state->window.background_image.height, 0, GL_RGBA, - GL_UNSIGNED_BYTE, (GLvoid *)state->window.background_image.buffer); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->width, texture->height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, (GLvoid *)texture->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); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + if (texture->swizzle) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_GREEN); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ALPHA); + } + /* Bind the actual bits we'll be using to render */ glBindVertexArray(gl->vao); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl->ebo); } -void gl_draw(struct client_state *state) +void gl_clear(struct gl *gl, struct color *color) { - glClearColor( - state->window.background_color.r, - state->window.background_color.g, - state->window.background_color.b, - state->window.background_color.a - ); + glClearColor(color->r, color->g, color->b, color->a); glClear(GL_COLOR_BUFFER_BIT); +} - if (state->window.background_image.buffer == NULL) { - return; +void gl_draw_texture(struct gl *gl, struct image *texture, int32_t x, int32_t y, int32_t width, int32_t height) +{ + if (texture->redraw) { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture->width, texture->height, GL_RGBA, + GL_UNSIGNED_BYTE, (GLvoid *)texture->buffer); + texture->redraw = false; } - double scale = max( - (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->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); } diff --git a/src/gl.h b/src/gl.h index d963f0f..8a4ff73 100644 --- a/src/gl.h +++ b/src/gl.h @@ -3,7 +3,9 @@ #include -struct client_state; +struct color; +struct image; + struct gl { GLuint vbo; GLuint vao; @@ -12,7 +14,8 @@ struct gl { GLuint shader; }; -void gl_initialise(struct client_state *state); -void gl_draw(struct client_state *state); +void gl_initialise(struct gl *gl, struct image *texture); +void gl_clear(struct gl *gl, struct color *color); +void gl_draw_texture(struct gl *gl, struct image *texture, int32_t x, int32_t y, int32_t width, int32_t height); #endif /* GL_H */ diff --git a/src/main.c b/src/main.c index cacc82e..5b59b68 100644 --- a/src/main.c +++ b/src/main.c @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -13,22 +12,19 @@ #include #include #include +#include "background.h" #include "client.h" #include "egl.h" +#include "entry.h" #include "gl.h" -#include "background.h" +#include "nelem.h" #include "xdg-shell-client-protocol.h" +#undef MAX #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) { - printf("DRAW\n"); surface->redraw = true; } @@ -51,10 +47,6 @@ xdg_toplevel_configure(void *data, 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 @@ -127,11 +119,23 @@ wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard, xkb_keysym_t sym = xkb_state_key_get_one_sym(client_state->xkb_state, keycode); xkb_keysym_get_name(sym, buf, sizeof(buf)); if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { + struct entry *entry = &client_state->window.entry; 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); + if (entry->password_length < N_ELEM(entry->password) - 1) { + entry->password[entry->password_length] = ch; + entry->password_length++; + } + fprintf(stderr, "%ls\n", entry->password); + entry_update(&client_state->window.entry); + draw_frame(&client_state->window.entry.surface); + } else if (entry->password_length > 0 && sym == XKB_KEY_BackSpace) { + entry->password[entry->password_length - 1] = '\0'; + entry->password_length--; + entry_update(&client_state->window.entry); + draw_frame(&client_state->window.entry.surface); } } } @@ -188,7 +192,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); + /* Deliberately left blank */ } static const struct wl_seat_listener wl_seat_listener = { @@ -206,22 +210,6 @@ static const struct xdg_wm_base_listener xdg_wm_base_listener = { .ping = xdg_wm_base_ping, }; -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->window.surface.wl_surface); - wl_callback_add_listener(cb, &wl_surface_frame_listener, state); - - /* Submit a frame for this event */ - wl_surface_commit(state->window.surface.wl_surface); -} - 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) @@ -297,12 +285,14 @@ static const struct wl_registry_listener wl_registry_listener = { static void surface_enter(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output) { - fprintf(stderr, "enter\n"); + /* TODO */ + fprintf(stderr, "TODO: enter\n"); } static void surface_leave(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output) { - fprintf(stderr, "leave\n"); + /* TODO */ + fprintf(stderr, "TODO: leave\n"); } static const struct wl_surface_listener wl_surface_listener = { @@ -320,6 +310,8 @@ int main(int argc, char *argv[]) state.window.surface.height = 480; state.window.entry.surface.width = 80; state.window.entry.surface.height = 40; + state.window.entry.border.width = 12; + state.window.entry.border.outline_width = 3; 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); @@ -337,41 +329,36 @@ int main(int argc, char *argv[]) xdg_toplevel_set_title(state.window.xdg_toplevel, "Greetd mini wayland greeter"); wl_surface_commit(state.window.surface.wl_surface); + entry_init(&state.window.entry); + xdg_toplevel_set_min_size(state.window.xdg_toplevel, + state.window.entry.surface.width, + state.window.entry.surface.height); + 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); - + surface_initialise(&state.window.surface, state.wl_display, &state.window.background_image); + surface_initialise(&state.window.entry.surface, state.wl_display, &state.window.entry.image); 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_swap_buffers(&state.window.surface.egl); egl_make_current(&state.window.entry.surface.egl); - eglSwapBuffers(state.window.entry.surface.egl.display, state.window.entry.surface.egl.surface); + egl_swap_buffers(&state.window.entry.surface.egl); draw_frame(&state.window.surface); + draw_frame(&state.window.entry.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); + surface_draw(&state.window.surface, &state.window.background_color, &state.window.background_image); 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); + surface_draw(&state.window.entry.surface, &state.window.background_color, &state.window.entry.image); state.window.entry.surface.redraw = false; } } diff --git a/src/nelem.h b/src/nelem.h new file mode 100644 index 0000000..61bf6ce --- /dev/null +++ b/src/nelem.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2021 Philip Jones + * + * Licensed under the MIT License. + * See either the LICENSE file, or: + * + * https://opensource.org/licenses/MIT + * + */ + +#ifndef N_ELEM +#define N_ELEM(x) (sizeof(x) / sizeof(*(x))) +#endif diff --git a/src/surface.c b/src/surface.c new file mode 100644 index 0000000..6253801 --- /dev/null +++ b/src/surface.c @@ -0,0 +1,34 @@ +#include "client.h" +#include "gl.h" +#include "surface.h" + +#undef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +void surface_initialise(struct surface *surface, struct wl_display *wl_display, struct image *texture) +{ + egl_create_window(&surface->egl, surface->wl_surface, surface->width, surface->height); + egl_create_context(&surface->egl, wl_display); + gl_initialise(&surface->gl, texture); +} + +void surface_draw(struct surface *surface, struct color *color, struct image *texture) +{ + egl_make_current(&surface->egl); + gl_clear(&surface->gl, color); + + if (texture != NULL && texture->buffer != NULL) { + double scale = MAX( + (double)surface->width / texture->width, + (double)surface->height / texture->height + ); + uint32_t width = (uint32_t)(scale * texture->width); + uint32_t height = (uint32_t)(scale * texture->height); + int32_t x = -((int32_t)width - (int32_t)surface->width) / 2; + int32_t y = -((int32_t)height - (int32_t)surface->height) / 2; + + gl_draw_texture(&surface->gl, texture, x, y, width, height); + } + + egl_swap_buffers(&surface->egl); +} diff --git a/src/surface.h b/src/surface.h new file mode 100644 index 0000000..2b2910a --- /dev/null +++ b/src/surface.h @@ -0,0 +1,21 @@ +#ifndef SURFACE_H +#define SURFACE_H + +#include +#include +#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; +}; + +void surface_initialise(struct surface *surface, struct wl_display *wl_display, struct image *texture); +void surface_draw(struct surface *surface, struct color *color, struct image *texture); + +#endif /* SURFACE_H */ diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..02e2c6a --- /dev/null +++ b/src/util.h @@ -0,0 +1,22 @@ +#ifndef UTIL_H +#define UTIL_H + +#include +#include + +struct image { + uint8_t *buffer; + uint32_t width; + uint32_t height; + bool swizzle; + bool redraw; +}; + +struct color { + float r; + float g; + float b; + float a; +}; + +#endif /* UTIL_H */ -- cgit v1.2.3