summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--meson.build3
-rw-r--r--src/background.h9
-rw-r--r--src/client.h6
-rw-r--r--src/color.c48
-rw-r--r--src/color.h (renamed from src/util.h)10
-rw-r--r--src/egl.c42
-rw-r--r--src/egl.h6
-rw-r--r--src/entry.c201
-rw-r--r--src/entry.h25
-rw-r--r--src/gl.c179
-rw-r--r--src/gl.h8
-rw-r--r--src/image.c (renamed from src/background.c)25
-rw-r--r--src/image.h17
-rw-r--r--src/log.c5
-rw-r--r--src/main.c755
-rw-r--r--src/surface.c16
-rw-r--r--src/surface.h10
17 files changed, 1054 insertions, 311 deletions
diff --git a/meson.build b/meson.build
index a8f7cfb..27c8144 100644
--- a/meson.build
+++ b/meson.build
@@ -45,10 +45,11 @@ add_project_arguments(
sources = files(
'src/main.c',
- 'src/background.c',
+ 'src/color.c',
'src/egl.c',
'src/entry.c',
'src/gl.c',
+ 'src/image.c',
'src/log.c',
'src/surface.c',
'src/xdg-shell-protocol.c',
diff --git a/src/background.h b/src/background.h
deleted file mode 100644
index 68b432a..0000000
--- a/src/background.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef PNG_H
-#define PNG_H
-
-#include <stdint.h>
-#include "client.h"
-
-void load_background(struct client_state *state, const char *filename);
-
-#endif /* PNG_H */
diff --git a/src/client.h b/src/client.h
index 934d4d3..d356094 100644
--- a/src/client.h
+++ b/src/client.h
@@ -3,9 +3,10 @@
#include <stdbool.h>
#include <stdint.h>
+#include "color.h"
#include "entry.h"
+#include "image.h"
#include "surface.h"
-#include "util.h"
struct client_state {
/* Globals */
@@ -37,7 +38,10 @@ struct client_state {
struct image background_image;
struct color background_color;
struct entry entry;
+ int32_t width;
+ int32_t height;
uint32_t scale;
+ bool resize;
} window;
/* Keyboard state */
diff --git a/src/color.c b/src/color.c
new file mode 100644
index 0000000..f6b3275
--- /dev/null
+++ b/src/color.c
@@ -0,0 +1,48 @@
+#include <stdlib.h>
+#include <string.h>
+#include "color.h"
+#include "log.h"
+
+struct color hex_to_color(const char *hex)
+{
+ if (hex[0] == '#') {
+ hex++;
+ }
+
+ uint32_t val = 0;
+ size_t len = strlen(hex);
+
+ if (len == 3) {
+ char str[] = {
+ hex[0], hex[0],
+ hex[1], hex[1],
+ hex[2], hex[2],
+ '\0'};
+ val = strtol(str, NULL, 16);
+ val <<= 8;
+ val |= 0xFFu;
+ } else if (len == 4) {
+ char str[] = {
+ hex[0], hex[0],
+ hex[1], hex[1],
+ hex[2], hex[2],
+ hex[3], hex[3],
+ '\0'};
+ val = strtol(str, NULL, 16);
+ } else if (len == 6) {
+ val = strtol(hex, NULL, 16);
+ val <<= 8;
+ val |= 0xFFu;
+ } else if (len == 8) {
+ val = strtol(hex, NULL, 16);
+ } else {
+ log_error("Invalid hex color %s\n", hex);
+ }
+
+ return (struct color) {
+ .r = ((val & 0xFF000000u) >> 24) / 255.0f,
+ .g = ((val & 0x00FF0000u) >> 16) / 255.0f,
+ .b = ((val & 0x0000FF00u) >> 8) / 255.0f,
+ .a = ((val & 0x000000FFu) >> 0) / 255.0f,
+ };
+}
diff --git a/src/util.h b/src/color.h
index 02e2c6a..505ba9a 100644
--- a/src/util.h
+++ b/src/color.h
@@ -4,14 +4,6 @@
#include <stdbool.h>
#include <stdint.h>
-struct image {
- uint8_t *buffer;
- uint32_t width;
- uint32_t height;
- bool swizzle;
- bool redraw;
-};
-
struct color {
float r;
float g;
@@ -19,4 +11,6 @@ struct color {
float a;
};
+struct color hex_to_color(const char *hex);
+
#endif /* UTIL_H */
diff --git a/src/egl.c b/src/egl.c
index 2e6b7a7..59f297b 100644
--- a/src/egl.c
+++ b/src/egl.c
@@ -6,12 +6,13 @@
static const char *egl_error_string();
-void egl_create_window(struct egl *egl, struct wl_surface *wl_surface, uint32_t width, uint32_t height)
+void egl_create_window(
+ struct egl *egl,
+ struct wl_surface *wl_surface,
+ uint32_t width,
+ uint32_t height)
{
- egl->window = wl_egl_window_create(
- wl_surface,
- width,
- height);
+ egl->window = wl_egl_window_create(wl_surface, width, height);
if (egl->window == EGL_NO_SURFACE) {
egl_log_error("Couldn't create EGL window");
@@ -49,13 +50,22 @@ void egl_create_context(struct egl *egl, struct wl_display *wl_display)
return;
}
- result = eglChooseConfig(egl->display, config_attribs, &config, 1, &num_configs);
+ result = eglChooseConfig(
+ egl->display,
+ config_attribs,
+ &config,
+ 1,
+ &num_configs);
if ((result != EGL_TRUE) || (num_configs != 1)) {
egl_log_error("Failed to choose EGL config");
return;
}
- egl->surface = eglCreateWindowSurface(egl->display, config, egl->window, NULL);
+ egl->surface = eglCreateWindowSurface(
+ egl->display,
+ config,
+ egl->window,
+ NULL);
if (egl->surface == EGL_NO_SURFACE) {
egl_log_error("Couldn't create EGL window surface");
return;
@@ -66,13 +76,21 @@ void egl_create_context(struct egl *egl, struct wl_display *wl_display)
EGL_NONE
};
- egl->context = eglCreateContext(egl->display, config, EGL_NO_CONTEXT, context_attribs);
+ egl->context = eglCreateContext(
+ egl->display,
+ config,
+ EGL_NO_CONTEXT,
+ context_attribs);
if (egl->context == EGL_NO_CONTEXT) {
egl_log_error("Couldn't create EGL context");
return;
}
- result = eglMakeCurrent(egl->display, egl->surface, egl->surface, egl->context);
+ result = eglMakeCurrent(
+ egl->display,
+ egl->surface,
+ egl->surface,
+ egl->context);
if (!result) {
egl_log_error("Couldn't make EGL context current");
return;
@@ -84,7 +102,11 @@ void egl_log_error(const char *msg) {
}
void egl_make_current(struct egl *egl) {
- bool result = eglMakeCurrent(egl->display, egl->surface, egl->surface, egl->context);
+ bool result = eglMakeCurrent(
+ egl->display,
+ egl->surface,
+ egl->surface,
+ egl->context);
if (!result) {
egl_log_error("Couldn't make EGL context current");
return;
diff --git a/src/egl.h b/src/egl.h
index a1856b2..a705538 100644
--- a/src/egl.h
+++ b/src/egl.h
@@ -11,7 +11,11 @@ struct egl {
EGLSurface surface;
};
-void egl_create_window(struct egl *egl, struct wl_surface *wl_surface, uint32_t width, uint32_t height);
+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);
diff --git a/src/entry.c b/src/entry.c
index 61b0211..e6338de 100644
--- a/src/entry.c
+++ b/src/entry.c
@@ -9,62 +9,109 @@
#include "log.h"
#include "nelem.h"
-static void calculate_font_extents(struct entry *entry);
+static void calculate_font_extents(struct entry *entry, uint32_t scale);
-void entry_init(struct entry *entry)
+void entry_init(struct entry *entry, uint32_t scale)
{
- calculate_font_extents(entry);
+ calculate_font_extents(entry, scale);
+ struct color color;
- /*
- * Cairo uses native 32 bit integers for pixels, so if this computer is
- * little endian we have to tell OpenGL to swizzle the texture.
+ /*
+ * Cairo uses native 32 bit integers for pixels, so if this processor
+ * is little endian we have to tell OpenGL to swizzle the texture.
*/
if (htonl(0xFFu) != 0xFFu) {
entry->image.swizzle = true;
}
+ /*
+ * Create the cairo surface and context we'll be using.
+ */
cairo_surface_t *surface = cairo_image_surface_create(
CAIRO_FORMAT_ARGB32,
entry->surface.width,
entry->surface.height
);
+ cairo_surface_set_device_scale(surface, scale, scale);
cairo_t *cr = cairo_create(surface);
- cairo_set_source_rgb(cr, 0.031, 0.031, 0);
+
+ /* Running size of current drawing area. */
+ int32_t width = entry->surface.width / scale;
+ int32_t height = entry->surface.height / scale;
+
+ /* Draw the outer outline */
+ color = entry->border.outline_color;
+ cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
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
- );
+ /* Move and clip following draws to be within this outline */
+ cairo_translate(
+ cr,
+ entry->border.outline_width,
+ entry->border.outline_width);
+ width -= 2 * entry->border.outline_width;
+ height -= 2 * entry->border.outline_width;
+ cairo_rectangle(cr, 0, 0, width, height);
cairo_clip(cr);
- cairo_set_source_rgb(cr, 0.976, 0.149, 0.447);
+
+ /* Draw the border */
+ color = entry->border.color;
+ cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
cairo_paint(cr);
+ /* Move and clip following draws to be within the border */
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)
- );
+ width -= 2 * entry->border.width;
+ height -= 2 * entry->border.width;
+ cairo_rectangle(cr, 0, 0, width, height);
+ cairo_clip(cr);
+
+ /* Draw the inner outline */
+ color = entry->border.outline_color;
+ cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
+ cairo_paint(cr);
+
+ /* Move and clip following draws to be within this outline */
+ cairo_translate(
+ cr,
+ entry->border.outline_width,
+ entry->border.outline_width);
+ width -= 2 * entry->border.outline_width;
+ height -= 2 * entry->border.outline_width;
+ cairo_rectangle(cr, 0, 0, width, height);
cairo_clip(cr);
- cairo_set_source_rgb(cr, 0.106, 0.114, 0.118);
+
+ /* Draw the entry background */
+ color = entry->background_color;
+ cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
cairo_paint(cr);
- PangoLayout *layout = pango_cairo_create_layout(cr);
+ /* Move and clip following draws to be within the specified padding */
+ cairo_translate(cr, entry->padding, entry->padding);
+ width -= 2 * entry->padding;
+ height -= 2 * entry->padding;
+ cairo_rectangle(cr, 0, 0, width, height);
+ cairo_clip(cr);
+
+ /*
+ * Move the cursor back up, so that Pango draws in the correct place if
+ * we're doing a tight layout.
+ */
+ cairo_translate(cr, -entry->text_bounds.x, -entry->text_bounds.y);
+
+ /* Setup Pango. */
+ PangoContext *context = pango_cairo_create_context(cr);
+ PangoLayout *layout = pango_layout_new(context);
pango_layout_set_text(layout, "", -1);
- PangoFontDescription *font_description = pango_font_description_from_string("Rubik Bold 48");
+ PangoFontDescription *font_description =
+ pango_font_description_from_string(entry->font_name);
+ pango_font_description_set_size(
+ font_description,
+ entry->font_size * PANGO_SCALE);
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;
@@ -77,49 +124,95 @@ 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);
+
+ /* Redraw the background. */
+ struct color color = entry->background_color;
+ cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
cairo_paint(cr);
- cairo_set_source_rgb(cr, 0.973, 0.973, 0.941);
+
+ /* Draw our text with Pango. */
+ color = entry->foreground_color;
+ cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
//const wchar_t *src = entry->password;
//wcsrtombs(entry->password_mb, &src, N_ELEM(entry->password_mb), NULL);
+ size_t len = 0;
for (unsigned int i = 0; i < entry->password_length; i++) {
- entry->password_mb[2 * i] = '\xC2';
- entry->password_mb[2 * i + 1] = '\xB7';
+ len += wcrtomb(entry->password_mb + len, entry->password_character, NULL);
}
- entry->password_mb[2 * entry->password_length] = '\0';
+ entry->password_mb[len] = '\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)
+void entry_set_scale(struct entry *entry, uint32_t scale)
{
+ cairo_surface_set_device_scale(entry->pangocairo.surface, scale, scale);
+}
+
+void calculate_font_extents(struct entry *entry, uint32_t scale)
+{
+ /*
+ * To calculate the size of the password box, we do the following:
+ * 1. Load the font we're going to use.
+ * 2. Create a string of the desired length using the specified
+ * password character, e.g. "·······".
+ * 3. Render the string with Pango in some abstract layout.
+ * 4. Measure the bounding box of the layout.
+ * 5. Add on the size of the border / outline.
+ */
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));
+
+ PangoFontDescription *font_description =
+ pango_font_description_from_string(entry->font_name);
+ pango_font_description_set_size(
+ font_description,
+ entry->font_size * PANGO_SCALE);
+ pango_layout_set_font_description(layout, font_description);
+ PangoFont *font =
+ pango_font_map_load_font(font_map, context, font_description);
+ pango_font_description_free(font_description);
+
+ font_description = pango_font_describe(font);
+ log_info("Using font: %s\n",
+ pango_font_description_to_string(font_description));
+ g_object_unref(font);
+
+ char *buf = calloc(MAX_PASSWORD_LENGTH, 4);
+ size_t len = 0;
+ for (unsigned int i = 0; i < entry->num_characters; i++) {
+ len += wcrtomb(buf + len, entry->password_character, NULL);
+ }
+ buf[len] = '\0';
+ pango_layout_set_text(layout,buf, -1);
+ free(buf);
+
+ PangoRectangle rect;
+ if (entry->tight_layout) {
+ pango_layout_get_pixel_extents(layout, &rect, NULL);
+ } else {
+ pango_layout_get_pixel_extents(layout, NULL, &rect);
}
- 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;
+ int width = rect.width;
+ int height = rect.height;
+ width += 2 * (
+ entry->border.width
+ + 2 * entry->border.outline_width
+ + entry->padding
+ );
+ height += 2 * (
+ entry->border.width
+ + 2 * entry->border.outline_width
+ + entry->padding
+ );
+ entry->surface.width = width * scale;
+ entry->surface.height = height * scale;
+ entry->text_bounds = rect;
g_object_unref(layout);
g_object_unref(context);
diff --git a/src/entry.h b/src/entry.h
index 18276a1..b1b9670 100644
--- a/src/entry.h
+++ b/src/entry.h
@@ -2,7 +2,8 @@
#define ENTRY_H
#include <pango/pangocairo.h>
-#include "util.h"
+#include "color.h"
+#include "image.h"
#include "surface.h"
#define MAX_PASSWORD_LENGTH 256
@@ -16,18 +17,32 @@ struct entry {
cairo_surface_t *surface;
cairo_t *cr;
} pangocairo;
+ PangoRectangle text_bounds;
+
+ wchar_t password[MAX_PASSWORD_LENGTH];
+ /* Assume maximum of 4 bytes per wchar_t (for UTF-8) */
+ char password_mb[4*MAX_PASSWORD_LENGTH];
+ uint32_t password_length;
+
+ /* Options */
+ uint32_t font_size;
+ const char *font_name;
+ uint32_t padding;
+ bool tight_layout;
+ wchar_t password_character;
+ uint32_t num_characters;
+ struct color foreground_color;
+ struct color background_color;
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_init(struct entry *entry, uint32_t scale);
void entry_update(struct entry *entry);
+void entry_set_scale(struct entry *entry, uint32_t scale);
#endif /* ENTRY_H */
diff --git a/src/gl.c b/src/gl.c
index 94262b4..74e7605 100644
--- a/src/gl.c
+++ b/src/gl.c
@@ -11,9 +11,26 @@
static void load_shader(GLuint shader, const char *filename);
static GLuint create_shader_program(const char *vert, const char *frag);
+static void GLAPIENTRY MessageCallback(
+ GLenum source,
+ GLenum type,
+ GLuint id,
+ GLenum severity,
+ GLsizei length,
+ const GLchar* message,
+ const void* userParam);
+static const char *gl_debug_source_string(GLenum type);
+static const char *gl_debug_type_string(GLenum type);
+static const char *gl_debug_severity_string(GLenum type);
+
void gl_initialise(struct gl *gl, struct image *texture)
{
+#ifdef DEBUG
+ glEnable(GL_DEBUG_OUTPUT);
+ glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
+ glDebugMessageCallback(MessageCallback, 0);
+#endif
if (texture == NULL) {
return;
}
@@ -37,24 +54,44 @@ void gl_initialise(struct gl *gl, struct image *texture)
glGenBuffers(1, &gl->vbo);
glBindBuffer(GL_ARRAY_BUFFER, gl->vbo);
- glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
+ glBufferData(
+ GL_ARRAY_BUFFER,
+ sizeof(vertices),
+ vertices,
+ GL_STATIC_DRAW);
- /* Create a vertex array and enable vertex attributes for the shaders. */
+ /*
+ * Create a vertex array and enable vertex attributes for the shaders.
+ */
glGenVertexArrays(1, &gl->vao);
glBindVertexArray(gl->vao);
GLint posAttrib = glGetAttribLocation(gl->shader, "position");
- glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), 0);
+ glVertexAttribPointer(
+ posAttrib,
+ 2,
+ GL_FLOAT,
+ GL_FALSE,
+ 4*sizeof(float),
+ 0);
glEnableVertexAttribArray(posAttrib);
GLint texAttrib = glGetAttribLocation(gl->shader, "texcoord");
- glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), (void *)(2*sizeof(float)));
+ glVertexAttribPointer(
+ texAttrib,
+ 2,
+ GL_FLOAT,
+ GL_FALSE,
+ 4*sizeof(float),
+ (void *)(2*sizeof(float)));
glEnableVertexAttribArray(texAttrib);
glBindVertexArray(0);
- /* Create the element buffer object that will actually be drawn via
- * glDrawElements(). */
+ /*
+ * Create the element buffer object that will actually be drawn via
+ * glDrawElements().
+ */
GLuint elements[] = {
0, 1, 2,
2, 3, 0
@@ -62,20 +99,36 @@ void gl_initialise(struct gl *gl, struct image *texture)
glGenBuffers(1, &gl->ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl->ebo);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
+ glBufferData(
+ GL_ELEMENT_ARRAY_BUFFER,
+ sizeof(elements),
+ elements,
+ GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
/* Create the texture we'll draw to */
glGenTextures(1, &gl->texture);
glBindTexture(GL_TEXTURE_2D, gl->texture);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->width, texture->height, 0, GL_RGBA,
- GL_UNSIGNED_BYTE, (GLvoid *)texture->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);
+ /*
+ * On little-endian processors, textures from Cairo have to have their
+ * red and blue channels swapped.
+ */
if (texture->swizzle) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_GREEN);
@@ -94,11 +147,26 @@ void gl_clear(struct gl *gl, struct color *color)
glClear(GL_COLOR_BUFFER_BIT);
}
-void gl_draw_texture(struct gl *gl, struct image *texture, int32_t x, int32_t y, int32_t width, int32_t height)
+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);
+ glTexSubImage2D(
+ GL_TEXTURE_2D,
+ 0,
+ 0,
+ 0,
+ texture->width,
+ texture->height,
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ (GLvoid *)texture->buffer);
+ glGetError();
texture->redraw = false;
}
@@ -111,17 +179,20 @@ void load_shader(GLuint shader, const char *filename)
errno = 0;
FILE *fp = fopen(filename, "rb");
if (!fp) {
- log_error("Failed to load shader %s: %s.\n", filename, strerror(errno));
+ log_error("Failed to load shader %s: %s.\n",
+ filename, strerror(errno));
exit(EXIT_FAILURE);
}
if (fseek(fp, 0, SEEK_END) != 0) {
- log_error("Failed to load shader %s: %s.\n", filename, strerror(errno));
+ log_error("Failed to load shader %s: %s.\n",
+ filename, strerror(errno));
fclose(fp);
exit(EXIT_FAILURE);
}
long size = ftell(fp);
if (size <= 0) {
- log_error("Failed to load shader %s: %s.\n", filename, strerror(errno));
+ log_error("Failed to load shader %s: %s.\n",
+ filename, strerror(errno));
fclose(fp);
exit(EXIT_FAILURE);
}
@@ -129,7 +200,8 @@ void load_shader(GLuint shader, const char *filename)
GLchar *source = malloc(usize + 1);
rewind(fp);
if (fread(source, 1, usize, fp) != usize) {
- log_error("Failed to load shader %s: %s.\n", filename, strerror(errno));
+ log_error("Failed to load shader %s: %s.\n",
+ filename, strerror(errno));
fclose(fp);
exit(EXIT_FAILURE);
}
@@ -171,3 +243,78 @@ GLuint create_shader_program(const char *vert, const char *frag)
glLinkProgram(shader);
return shader;
}
+
+void GLAPIENTRY MessageCallback(
+ GLenum source,
+ GLenum type,
+ GLuint id,
+ GLenum severity,
+ GLsizei length,
+ const GLchar* message,
+ const void* userParam)
+{
+ log_debug("Message from OpenGL:\n");
+ log_debug("\tSource: %s\n", gl_debug_source_string(source));
+ log_debug("\tType: %s\n", gl_debug_type_string(type));
+ log_debug("\tSeverity: %s\n", gl_debug_severity_string(severity));
+ log_debug("\tMessage: %s\n", message);
+}
+
+const char *gl_debug_source_string(GLenum type)
+{
+ switch(type) {
+ case GL_DEBUG_SOURCE_API:
+ return "API";
+ case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
+ return "Window system";
+ case GL_DEBUG_SOURCE_SHADER_COMPILER:
+ return "Shader compiler";
+ case GL_DEBUG_SOURCE_THIRD_PARTY:
+ return "Third party";
+ case GL_DEBUG_SOURCE_APPLICATION:
+ return "Application";
+ case GL_DEBUG_SOURCE_OTHER:
+ return "Other";
+ }
+ return "unknown";
+}
+
+const char *gl_debug_type_string(GLenum type)
+{
+ switch(type) {
+ case GL_DEBUG_TYPE_ERROR:
+ return "Error";
+ case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
+ return "Deprecated behavior";
+ case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
+ return "Undefined behavior";
+ case GL_DEBUG_TYPE_PORTABILITY:
+ return "Portability";
+ case GL_DEBUG_TYPE_PERFORMANCE:
+ return "Performance";
+ case GL_DEBUG_TYPE_MARKER:
+ return "Marker";
+ case GL_DEBUG_TYPE_PUSH_GROUP:
+ return "Push group";
+ case GL_DEBUG_TYPE_POP_GROUP:
+ return "Pop group";
+ case GL_DEBUG_TYPE_OTHER:
+ return "Other";
+ }
+ return "Unknown";
+}
+
+const char *gl_debug_severity_string(GLenum type)
+{
+ switch(type) {
+ case GL_DEBUG_SEVERITY_HIGH:
+ return "High";
+ case GL_DEBUG_SEVERITY_MEDIUM:
+ return "Medium";
+ case GL_DEBUG_SEVERITY_LOW:
+ return "Low";
+ case GL_DEBUG_SEVERITY_NOTIFICATION:
+ return "Notification";
+ }
+ return "Unknown";
+}
diff --git a/src/gl.h b/src/gl.h
index 8a4ff73..9f48bb8 100644
--- a/src/gl.h
+++ b/src/gl.h
@@ -16,6 +16,12 @@ struct gl {
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);
+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/background.c b/src/image.c
index 68beb5e..e05353c 100644
--- a/src/background.c
+++ b/src/image.c
@@ -1,28 +1,31 @@
-#include <stdlib.h>
+#include <errno.h>
#include <stdint.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <png.h>
-#include "client.h"
#include "log.h"
-#include "background.h"
+#include "image.h"
#define HEADER_BYTES 8
-void load_background(struct client_state *state, const char *filename)
+void image_load(struct image *image, const char *filename)
{
FILE *fp = fopen(filename, "rb");
uint8_t header[HEADER_BYTES];
if (!fp) {
- log_error("Couldn't open %s\n", filename);
+ log_error("Couldn't open '%s': %s.\n",
+ filename, strerror(errno));
return;
}
if (fread(header, 1, HEADER_BYTES, fp) != HEADER_BYTES) {
- log_error("Failed to read camera data: %s\n", filename);
+ log_error("Failed to read '%s': %s.\n",
+ filename, strerror(errno));
fclose(fp);
return;
}
if (png_sig_cmp(header, 0, HEADER_BYTES)) {
- log_error("Not a PNG file: %s\n", filename);
+ log_error("'%s' isn't a PNG file.\n", filename);
fclose(fp);
return;
}
@@ -63,7 +66,7 @@ void load_background(struct client_state *state, const char *filename)
png_set_expand(png_ptr);
png_set_gray_to_rgb(png_ptr);
png_set_filler(png_ptr, 0xFFu, PNG_FILLER_AFTER);
-
+
png_read_update_info(png_ptr, info_ptr);
uint32_t row_bytes = png_get_rowbytes(png_ptr, info_ptr);
@@ -85,7 +88,7 @@ void load_background(struct client_state *state, const char *filename)
free(row_pointers);
fclose(fp);
- state->window.background_image.width = width;
- state->window.background_image.height = height;
- state->window.background_image.buffer = buffer;
+ image->width = width;
+ image->height = height;
+ image->buffer = buffer;
}
diff --git a/src/image.h b/src/image.h
new file mode 100644
index 0000000..d5bd346
--- /dev/null
+++ b/src/image.h
@@ -0,0 +1,17 @@
+#ifndef IMAGE_H
+#define IMAGE_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+struct image {
+ uint8_t *buffer;
+ uint32_t width;
+ uint32_t height;
+ bool swizzle;
+ bool redraw;
+};
+
+void image_load(struct image *image, const char *filename);
+
+#endif /* IMAGE_H */
diff --git a/src/log.c b/src/log.c
index eb4d165..a27bc6c 100644
--- a/src/log.c
+++ b/src/log.c
@@ -1,4 +1,5 @@
#include <stdio.h>
+#include <time.h>
void log_error(const char *const fmt, ...)
{
@@ -23,9 +24,11 @@ void log_debug(const char *const fmt, ...)
#ifndef DEBUG
return;
#endif
+ struct timespec t;
+ clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t);
va_list args;
va_start(args, fmt);
- printf("[DEBUG]: ");
+ fprintf(stderr, "[%ld.%03ld][DEBUG]: ", t.tv_sec, t.tv_nsec / 1000000);
vprintf(fmt, args);
va_end(args);
}
diff --git a/src/main.c b/src/main.c
index 5b59b68..1385647 100644
--- a/src/main.c
+++ b/src/main.c
@@ -2,6 +2,7 @@
#include <wayland-egl.h>
#include <epoxy/gl.h>
#include <errno.h>
+#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -10,57 +11,89 @@
#include <unistd.h>
#include <wayland-client.h>
#include <wctype.h>
+#include <wchar.h>
#include <xkbcommon/xkbcommon.h>
#include <locale.h>
-#include "background.h"
#include "client.h"
#include "egl.h"
#include "entry.h"
+#include "image.h"
#include "gl.h"
+#include "log.h"
#include "nelem.h"
#include "xdg-shell-client-protocol.h"
#undef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
-void draw_frame(struct surface *surface)
+static void resize(struct client_state *state)
{
- surface->redraw = true;
+ struct surface *surface = &state->window.surface;
+ struct surface *entry_surface = &state->window.entry.surface;
+
+ /*
+ * Resize the main window.
+ * EGL wants actual pixel width / height, so we have to scale the
+ * values provided by Wayland.
+ */
+ surface->width = state->window.width * state->window.scale;
+ surface->height = state->window.height * state->window.scale;
+ wl_egl_window_resize(
+ surface->egl.window,
+ surface->width,
+ surface->height,
+ 0,
+ 0);
+
+ /*
+ * Need to redraw the background at the new size. This entails a
+ * wl_surface_commit, so no need to do so explicitly here.
+ */
+ state->window.surface.redraw = true;
+
+ /*
+ * Center the password entry.
+ * Wayland wants "surface-local" width / height, so we have to divide
+ * the entry's pixel size by the scale factor.
+ */
+ int32_t x = (
+ state->window.width
+ - entry_surface->width / state->window.scale
+ ) / 2;
+ int32_t y = (
+ state->window.height
+ - entry_surface->height / state->window.scale
+ ) / 2;
+ wl_subsurface_set_position( state->window.entry.wl_subsurface, x, y);
+ wl_surface_commit(state->window.entry.surface.wl_surface);
}
-static void
-xdg_toplevel_configure(void *data,
- struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height,
- struct wl_array *states)
+static void xdg_toplevel_configure(
+ void *data,
+ struct xdg_toplevel *xdg_toplevel,
+ int32_t width,
+ int32_t height,
+ struct wl_array *states)
{
struct client_state *state = data;
if (width == 0 || height == 0) {
- /* Compositor is deferring to us */
+ /* Compositor is deferring to us, so don't do anything. */
+ log_debug("XDG toplevel configure with no width or height.\n");
return;
}
-
- 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);
-
- 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);
+ log_debug("XDG toplevel configure, %d x %d.\n", width, height);
+ if (width != state->window.width || height != state->window.height) {
+ state->window.width = width;
+ state->window.height = height;
+ state->window.resize = true;
}
}
-static void
-xdg_toplevel_close(void *data, struct xdg_toplevel *toplevel)
+static void xdg_toplevel_close(void *data, struct xdg_toplevel *toplevel)
{
struct client_state *state = data;
state->closed = true;
+ log_debug("XDG toplevel close.\n");
}
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
@@ -68,172 +101,239 @@ static const struct xdg_toplevel_listener xdg_toplevel_listener = {
.close = xdg_toplevel_close
};
-static void
-xdg_surface_configure(void *data,
- struct xdg_surface *xdg_surface, uint32_t serial)
+static void xdg_surface_configure(
+ void *data,
+ struct xdg_surface *xdg_surface,
+ uint32_t serial)
{
- xdg_surface_ack_configure(xdg_surface, serial);
+ xdg_surface_ack_configure(xdg_surface, serial);
+ log_debug("XDG surface configured.\n");
}
static const struct xdg_surface_listener xdg_surface_listener = {
- .configure = xdg_surface_configure,
+ .configure = xdg_surface_configure,
};
-static void
-wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
- uint32_t format, int32_t fd, uint32_t size)
+static void wl_keyboard_keymap(
+ void *data,
+ struct wl_keyboard *wl_keyboard,
+ uint32_t format,
+ int32_t fd,
+ uint32_t size)
{
- struct client_state *client_state = data;
- assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1);
-
- char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
- assert(map_shm != MAP_FAILED);
-
- struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_string(
- client_state->xkb_context, map_shm,
- XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
- munmap(map_shm, size);
- close(fd);
-
- struct xkb_state *xkb_state = xkb_state_new(xkb_keymap);
- xkb_keymap_unref(client_state->xkb_keymap);
- xkb_state_unref(client_state->xkb_state);
- client_state->xkb_keymap = xkb_keymap;
- client_state->xkb_state = xkb_state;
+ struct client_state *client_state = data;
+ assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1);
+
+ char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+ assert(map_shm != MAP_FAILED);
+
+ struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_string(
+ client_state->xkb_context,
+ map_shm,
+ XKB_KEYMAP_FORMAT_TEXT_V1,
+ XKB_KEYMAP_COMPILE_NO_FLAGS);
+ munmap(map_shm, size);
+ close(fd);
+
+ struct xkb_state *xkb_state = xkb_state_new(xkb_keymap);
+ xkb_keymap_unref(client_state->xkb_keymap);
+ xkb_state_unref(client_state->xkb_state);
+ client_state->xkb_keymap = xkb_keymap;
+ client_state->xkb_state = xkb_state;
+ log_debug("Keyboard configured.\n");
}
-static void
-wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard,
- uint32_t serial, struct wl_surface *surface,
- struct wl_array *keys)
+static void wl_keyboard_enter(
+ void *data,
+ struct wl_keyboard *wl_keyboard,
+ uint32_t serial,
+ struct wl_surface *surface,
+ struct wl_array *keys)
{
+ /* Deliberately left blank */
}
-static void
-wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard,
- uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
+static void wl_keyboard_leave(
+ void *data,
+ struct wl_keyboard *wl_keyboard,
+ uint32_t serial,
+ struct wl_surface *surface)
{
- 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_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)) {
- 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);
- }
- }
+ /* Deliberately left blank */
}
-static void
-wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard,
- uint32_t serial, struct wl_surface *surface)
+static void wl_keyboard_key(
+ void *data,
+ struct wl_keyboard *wl_keyboard,
+ uint32_t serial,
+ uint32_t time,
+ uint32_t key,
+ uint32_t state)
{
+ 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_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)) {
+ if (entry->password_length < N_ELEM(entry->password) - 1) {
+ entry->password[entry->password_length] = ch;
+ entry->password_length++;
+ entry->password[entry->password_length] = L'\0';
+ }
+ fprintf(stderr, "%ls\n", entry->password);
+ } else if (entry->password_length > 0 && sym == XKB_KEY_BackSpace) {
+ entry->password[entry->password_length - 1] = '\0';
+ entry->password_length--;
+ } else if (sym == XKB_KEY_Escape) {
+ entry->password[0] = '\0';
+ entry->password_length = 0;
+ } else if (entry->password_length > 0
+ && (sym == XKB_KEY_Return
+ || sym == XKB_KEY_KP_Enter)) {
+ entry->password[0] = '\0';
+ entry->password_length = 0;
+ }
+ entry_update(&client_state->window.entry);
+ client_state->window.entry.surface.redraw = true;
+ }
}
-static void
-wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
- uint32_t serial, uint32_t mods_depressed,
- uint32_t mods_latched, uint32_t mods_locked,
- uint32_t group)
+static void wl_keyboard_modifiers(
+ void *data,
+ struct wl_keyboard *wl_keyboard,
+ uint32_t serial,
+ uint32_t mods_depressed,
+ uint32_t mods_latched,
+ uint32_t mods_locked,
+ uint32_t group)
{
- struct client_state *client_state = data;
- xkb_state_update_mask(client_state->xkb_state,
- mods_depressed, mods_latched, mods_locked, 0, 0, group);
+ struct client_state *client_state = data;
+ xkb_state_update_mask(
+ client_state->xkb_state,
+ mods_depressed,
+ mods_latched,
+ mods_locked,
+ 0,
+ 0,
+ group);
}
-static void
-wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
- int32_t rate, int32_t delay)
+static void wl_keyboard_repeat_info(
+ void *data,
+ struct wl_keyboard *wl_keyboard,
+ int32_t rate,
+ int32_t delay)
{
+ /* Deliberately left blank */
}
static const struct wl_keyboard_listener wl_keyboard_listener = {
- .keymap = wl_keyboard_keymap,
- .enter = wl_keyboard_enter,
- .leave = wl_keyboard_leave,
- .key = wl_keyboard_key,
- .modifiers = wl_keyboard_modifiers,
- .repeat_info = wl_keyboard_repeat_info,
+ .keymap = wl_keyboard_keymap,
+ .enter = wl_keyboard_enter,
+ .leave = wl_keyboard_leave,
+ .key = wl_keyboard_key,
+ .modifiers = wl_keyboard_modifiers,
+ .repeat_info = wl_keyboard_repeat_info,
};
-static void
-wl_seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities)
+static void wl_seat_capabilities(
+ void *data,
+ struct wl_seat *wl_seat,
+ uint32_t capabilities)
{
- struct client_state *state = data;
-
- bool have_keyboard = capabilities & WL_SEAT_CAPABILITY_KEYBOARD;
-
- if (have_keyboard && state->wl_keyboard == NULL) {
- state->wl_keyboard = wl_seat_get_keyboard(state->wl_seat);
- wl_keyboard_add_listener(state->wl_keyboard,
- &wl_keyboard_listener, state);
- } else if (!have_keyboard && state->wl_keyboard != NULL) {
- wl_keyboard_release(state->wl_keyboard);
- state->wl_keyboard = NULL;
- }
+ struct client_state *state = data;
+
+ bool have_keyboard = capabilities & WL_SEAT_CAPABILITY_KEYBOARD;
+
+ if (have_keyboard && state->wl_keyboard == NULL) {
+ state->wl_keyboard = wl_seat_get_keyboard(state->wl_seat);
+ wl_keyboard_add_listener(
+ state->wl_keyboard,
+ &wl_keyboard_listener,
+ state);
+ log_debug("Got keyboard from seat.\n");
+ } else if (!have_keyboard && state->wl_keyboard != NULL) {
+ wl_keyboard_release(state->wl_keyboard);
+ state->wl_keyboard = NULL;
+ log_debug("Released keyboard.\n");
+ }
}
-static void
-wl_seat_name(void *data, struct wl_seat *wl_seat, const char *name)
+static void wl_seat_name(void *data, struct wl_seat *wl_seat, const char *name)
{
/* Deliberately left blank */
}
static const struct wl_seat_listener wl_seat_listener = {
- .capabilities = wl_seat_capabilities,
- .name = wl_seat_name,
+ .capabilities = wl_seat_capabilities,
+ .name = wl_seat_name,
};
-static void
-xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial)
+static void xdg_wm_base_ping(
+ void *data,
+ struct xdg_wm_base *xdg_wm_base,
+ uint32_t serial)
{
- xdg_wm_base_pong(xdg_wm_base, serial);
+ xdg_wm_base_pong(xdg_wm_base, serial);
}
static const struct xdg_wm_base_listener xdg_wm_base_listener = {
- .ping = xdg_wm_base_ping,
+ .ping = xdg_wm_base_ping,
};
-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)
+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 void output_mode(void *data, struct wl_output *wl_output,
- uint32_t flags, int32_t width, int32_t height, int32_t refresh)
+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)
+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);
+ log_debug("Output scale factor is %d.\n", factor);
}
static void output_done(void *data, struct wl_output *wl_output)
{
+ //struct client_state *state = data;
/* TODO */
+ log_debug("Output configuration done.\n");
}
static const struct wl_output_listener wl_output_listener = {
@@ -243,53 +343,90 @@ static const struct wl_output_listener wl_output_listener = {
.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;
- //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);
+ wl_registry,
+ name,
+ &wl_compositor_interface,
+ 4);
+ log_debug("Bound to compositor %u.\n", name);
} else if (strcmp(interface, wl_subcompositor_interface.name) == 0) {
state->wl_subcompositor = wl_registry_bind(
- wl_registry, name, &wl_subcompositor_interface, 1);
+ wl_registry,
+ name,
+ &wl_subcompositor_interface,
+ 1);
+ log_debug("Bound to subcompositor %u.\n", name);
} 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);
+ wl_registry,
+ name,
+ &wl_seat_interface,
+ 7);
+ wl_seat_add_listener(
+ state->wl_seat,
+ &wl_seat_listener,
+ state);
+ log_debug("Bound to seat %u.\n", name);
} 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);
+ wl_registry,
+ name,
+ &wl_output_interface,
+ 3);
+ wl_output_add_listener(
+ state->wl_output,
+ &wl_output_listener,
+ state);
+ log_debug("Bound to output %u.\n", name);
} 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);
+ wl_registry,
+ name,
+ &xdg_wm_base_interface,
+ 1);
+ xdg_wm_base_add_listener(
+ state->xdg_wm_base,
+ &xdg_wm_base_listener,
+ state);
+ log_debug("Bound to xdg_wm_base %u.\n", name);
}
}
-static void registry_global_remove(void *data, struct wl_registry *wl_registry,
+static void registry_global_remove(
+ void *data,
+ struct wl_registry *wl_registry,
uint32_t name)
{
/* Deliberately left blank */
}
static const struct wl_registry_listener wl_registry_listener = {
- .global = registry_global,
- .global_remove = registry_global_remove,
+ .global = registry_global,
+ .global_remove = registry_global_remove,
};
-static void surface_enter(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output)
+static void surface_enter(
+ void *data,
+ struct wl_surface *wl_surface,
+ struct wl_output *wl_output)
{
/* TODO */
fprintf(stderr, "TODO: enter\n");
}
-static void surface_leave(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output)
+static void surface_leave(
+ void *data,
+ struct wl_surface *wl_surface,
+ struct wl_output *wl_output)
{
/* TODO */
fprintf(stderr, "TODO: leave\n");
@@ -303,67 +440,309 @@ static const struct wl_surface_listener wl_surface_listener = {
int main(int argc, char *argv[])
{
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.window.entry.border.width = 12;
- state.window.entry.border.outline_width = 3;
+ struct client_state state = {
+ .window = {
+ .background_color = {0.89, 0.8, 0.824, 1.0},
+ .scale = 1,
+ .surface = { .width = 640, .height = 480 },
+ .entry = {
+ .border = {
+ .width = 6,
+ .outline_width = 2,
+ .color = {0.976, 0.149, 0.447, 1.0},
+ .outline_color = {0.031, 0.031, 0.0, 1.0},
+ },
+ .font_name = "Sans Bold",
+ .font_size = 24,
+ .padding = 8,
+ .tight_layout = true,
+ .password_character = L'·',
+ .num_characters = 12,
+ .background_color = {0.106, 0.114, 0.118, 1.0},
+ .foreground_color = {1.0, 1.0, 1.0, 1.0}
+ }
+ }
+ };
+
+
+ struct option long_options[] = {
+ {"background_image", required_argument, NULL, 'b'},
+ {"background_color", required_argument, NULL, 'B'},
+ {"border_width", required_argument, NULL, 'r'},
+ {"border_color", required_argument, NULL, 'R'},
+ {"outline_width", required_argument, NULL, 'o'},
+ {"outline_color", required_argument, NULL, 'O'},
+ {"entry_padding", required_argument, NULL, 'e'},
+ {"entry_color", required_argument, NULL, 'E'},
+ {"text_color", required_argument, NULL, 'T'},
+ {"font_name", required_argument, NULL, 'f'},
+ {"font_size", required_argument, NULL, 'F'},
+ {"password_character", required_argument, NULL, 'c'},
+ {"width_characters", required_argument, NULL, 'n'},
+ {"wide_layout", no_argument, NULL, 'w'},
+ {NULL, 0, NULL, 0}
+ };
+ const char *short_options = "b:B:e:E:f:F:r:R:n:o:O:c:T:w";
+
+ int opt = getopt_long(argc, argv, short_options, long_options, NULL);
+ while (opt != -1) {
+ switch (opt) {
+ case 'b':
+ image_load(
+ &state.window.background_image,
+ optarg);
+ break;
+ case 'B':
+ state.window.background_color =
+ hex_to_color(optarg);
+ break;
+ case 'r':
+ state.window.entry.border.width =
+ strtol(optarg, NULL, 0);
+ break;
+ case 'R':
+ state.window.entry.border.color =
+ hex_to_color(optarg);
+ break;
+ case 'o':
+ state.window.entry.border.outline_width =
+ strtol(optarg, NULL, 0);
+ break;
+ case 'O':
+ state.window.entry.border.outline_color =
+ hex_to_color(optarg);
+ break;
+ case 'e':
+ state.window.entry.padding =
+ strtol(optarg, NULL, 0);
+ break;
+ case 'E':
+ state.window.entry.background_color =
+ hex_to_color(optarg);
+ break;
+ case 'T':
+ state.window.entry.foreground_color =
+ hex_to_color(optarg);
+ break;
+ case 'f':
+ state.window.entry.font_name = optarg;
+ break;
+ case 'F':
+ state.window.entry.font_size =
+ strtol(optarg, NULL, 0);
+ break;
+ case 'c':
+ mbrtowc(
+ &state.window.entry.password_character,
+ optarg,
+ 4,
+ NULL);
+ break;
+ case 'n':
+ state.window.entry.num_characters =
+ strtol(optarg, NULL, 0);
+ case 'w':
+ state.window.entry.tight_layout = false;
+ break;
+ case '?':
+ break;
+ }
+ opt = getopt_long(argc, argv, short_options, long_options, NULL);
+ }
+
+
+ /*
+ * Initial Wayland & XKB setup.
+ * The first thing to do is connect a listener to the global registry,
+ * so that we can bind to the various global objects and start talking
+ * to Wayland.
+ */
state.wl_display = wl_display_connect(NULL);
+ if (state.wl_display == NULL) {
+ log_error("Couldn't connect to Wayland display.\n");
+ exit(EXIT_FAILURE);
+ }
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);
+ if (state.xkb_context == NULL) {
+ log_error("Couldn't create an XKB context.\n");
+ exit(EXIT_FAILURE);
+ }
+ wl_registry_add_listener(
+ state.wl_registry,
+ &wl_registry_listener,
+ &state);
+
+ /*
+ * After this first roundtrip, the only thing that should have happened
+ * is our registry_global() function being called and setting up the
+ * various global object bindings.
+ */
+ log_debug("First roundtrip start.\n");
wl_display_roundtrip(state.wl_display);
+ log_debug("First roundtrip done.\n");
+
+ /*
+ * The next roundtrip causes the listeners we set up in
+ * registry_global() to be called. Notably, the output should be
+ * configured, telling us the scale factor.
+ */
+ log_debug("Second roundtrip start.\n");
+ wl_display_roundtrip(state.wl_display);
+ log_debug("Second roundtrip done.\n");
+
+ /*
+ * Next, we create the Wayland surfaces that we need - one for
+ * the whole window, and another for the password entry box.
+ */
+ /*
+ * The main window surface takes on the xdg_surface and xdg_toplevel
+ * roles, in order to receive configure events to change its size.
+ */
+ log_debug("Creating main window surface.\n");
+ state.window.surface.wl_surface =
+ wl_compositor_create_surface(state.wl_compositor);
+ wl_surface_add_listener(
+ state.window.surface.wl_surface,
+ &wl_surface_listener,
+ &state);
+ wl_surface_set_buffer_scale(
+ state.window.surface.wl_surface,
+ state.window.scale);
+
+ 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");
- 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);
- 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);
+ /*
+ * The password entry surface takes on a subsurface role and is set to
+ * be desynchronised, so that we can update it independently from the
+ * main window.
+ */
+ log_debug("Creating password entry surface.\n");
+ state.window.entry.surface.wl_surface =
+ wl_compositor_create_surface(state.wl_compositor);
+ wl_surface_set_buffer_scale(
+ state.window.entry.surface.wl_surface,
+ state.window.scale);
+
+ 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);
- 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_surface_commit(state.window.entry.surface.wl_surface);
+ /*
+ * Initialise the Pango & Cairo structures for rendering the password
+ * entry. Cairo needs to know the size of the surface it's creating,
+ * and there's no way to resize it aside from tearing everything down
+ * and starting again, so we make sure to do this after we've
+ * determined our output's scale factor. This stops us being able to
+ * change the scale factor after startup, but this is just a greeter,
+ * which shouldn't be moving between outputs while running.
+ */
+ log_debug("Initialising Pango / Cairo.\n");
+ entry_init(&state.window.entry, state.window.scale);
+ log_debug("Pango / Cairo initialised.\n");
+
+ /*
+ * Tell the compositor not to make our window smaller than the password
+ * entry.
+ */
+ xdg_toplevel_set_min_size(
+ state.window.xdg_toplevel,
+ state.window.entry.surface.width / state.window.scale,
+ state.window.entry.surface.height / state.window.scale);
+
+ /*
+ * Now that we've done all our Wayland-related setup, we do another
+ * roundtrip. This should cause the XDG toplevel window to be
+ * configured, after which we're ready to start drawing to the screen.
+ */
+ log_debug("Third roundtrip start.\n");
wl_display_roundtrip(state.wl_display);
- egl_make_current(&state.window.surface.egl);
- egl_swap_buffers(&state.window.surface.egl);
- egl_make_current(&state.window.entry.surface.egl);
- egl_swap_buffers(&state.window.entry.surface.egl);
- draw_frame(&state.window.surface);
- draw_frame(&state.window.entry.surface);
+ log_debug("Third roundtrip done.\n");
+
+ /*
+ * Create the various EGL and GL structures for each surface, and
+ * perform an initial render of everything.
+ */
+ log_debug("Initialising main window surface.\n");
+ surface_initialise(
+ &state.window.surface,
+ state.wl_display,
+ &state.window.background_image);
+ surface_draw(
+ &state.window.surface,
+ &state.window.background_color,
+ &state.window.background_image);
+
+ log_debug("Initialising entry window surface.\n");
+ surface_initialise(
+ &state.window.entry.surface,
+ state.wl_display,
+ &state.window.entry.image);
+ surface_draw(
+ &state.window.entry.surface,
+ &state.window.background_color,
+ &state.window.entry.image);
+
+ /* Call resize() just to center the password entry properly. */
+ resize(&state);
+
+ /*
+ * We've just rendered everything and resized, so we don't need to do
+ * it again right now.
+ */
+ state.window.resize = false;
+ state.window.surface.redraw = false;
+ state.window.entry.surface.redraw = false;
+
while (wl_display_dispatch(state.wl_display) != -1) {
if (state.closed) {
break;
}
+ if (state.window.resize) {
+ resize(&state);
+ state.window.resize = false;
+ }
if (state.window.surface.redraw) {
- surface_draw(&state.window.surface, &state.window.background_color, &state.window.background_image);
+ surface_draw(
+ &state.window.surface,
+ &state.window.background_color,
+ &state.window.background_image);
state.window.surface.redraw = false;
}
if (state.window.entry.surface.redraw) {
- surface_draw(&state.window.entry.surface, &state.window.background_color, &state.window.entry.image);
+ surface_draw(
+ &state.window.entry.surface,
+ &state.window.background_color,
+ &state.window.entry.image);
state.window.entry.surface.redraw = false;
}
}
+ log_info("Window closed, performing cleanup.\n");
wl_display_disconnect(state.wl_display);
+ log_info("Finished, exiting.\n");
return 0;
}
diff --git a/src/surface.c b/src/surface.c
index 6253801..76abec9 100644
--- a/src/surface.c
+++ b/src/surface.c
@@ -5,14 +5,24 @@
#undef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
-void surface_initialise(struct surface *surface, struct wl_display *wl_display, struct image *texture)
+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_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)
+void surface_draw(
+ struct surface *surface,
+ struct color *color,
+ struct image *texture)
{
egl_make_current(&surface->egl);
gl_clear(&surface->gl, color);
diff --git a/src/surface.h b/src/surface.h
index 2b2910a..bd79a80 100644
--- a/src/surface.h
+++ b/src/surface.h
@@ -15,7 +15,13 @@ struct surface {
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);
+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 */