summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE19
-rw-r--r--meson.build15
-rw-r--r--shaders/frag.frag25
-rw-r--r--shaders/vert.vert22
-rw-r--r--src/client.h13
-rw-r--r--src/egl.c4
-rw-r--r--src/gl.c177
-rw-r--r--src/gl.h18
-rw-r--r--src/main.c14
-rw-r--r--src/png.c91
-rw-r--r--src/png.h9
11 files changed, 395 insertions, 12 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..dc297d9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright (C) 2021 Philip Jones
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/meson.build b/meson.build
index cdffbdd..5439ee2 100644
--- a/meson.build
+++ b/meson.build
@@ -25,6 +25,12 @@ data_location = join_paths(
'greetd-mini-wl-greeter'
)
+shader_location = join_paths(
+ debug ? '' : data_location,
+ 'shaders',
+ ''
+)
+
add_project_arguments(
[
'-pedantic',
@@ -32,20 +38,23 @@ add_project_arguments(
'-Wshadow',
'-Wno-unused-parameter',
'-D_POSIX_C_SOURCE=200809L',
+ '-DSHADER_PATH="@0@"'.format(shader_location),
],
language: 'c'
)
sources = files(
'src/main.c',
- 'src/log.c',
'src/egl.c',
+ 'src/gl.c',
+ 'src/log.c',
+ 'src/png.c',
'src/xdg-shell-protocol.c',
)
cc = meson.get_compiler('c')
epoxy = dependency('epoxy')
-rt = cc.find_library('rt')
+png = dependency('libpng')
wayland_client = dependency('wayland-client')
wayland_egl = dependency('wayland-egl')
xkbcommon = dependency('xkbcommon')
@@ -53,7 +62,7 @@ xkbcommon = dependency('xkbcommon')
executable(
'greetd-mini-wl-greeter',
sources,
- dependencies: [epoxy, rt, wayland_client, wayland_egl, xkbcommon],
+ dependencies: [epoxy, png, wayland_client, wayland_egl, xkbcommon],
install: true
)
diff --git a/shaders/frag.frag b/shaders/frag.frag
new file mode 100644
index 0000000..c6bdbde
--- /dev/null
+++ b/shaders/frag.frag
@@ -0,0 +1,25 @@
+#version 300 es
+
+/*
+ * Copyright (C) 2021 Philip Jones
+ *
+ * Licensed under the MIT License.
+ * See either the LICENSE file, or:
+ *
+ * https://opensource.org/licenses/MIT
+ *
+ * I don't think you can really copyright this shader though :)
+ */
+
+precision highp float;
+
+in vec2 Texcoord;
+
+out vec4 FragColor;
+
+uniform sampler2D tex;
+
+void main()
+{
+ FragColor = texture(tex, Texcoord);
+}
diff --git a/shaders/vert.vert b/shaders/vert.vert
new file mode 100644
index 0000000..9d62b85
--- /dev/null
+++ b/shaders/vert.vert
@@ -0,0 +1,22 @@
+#version 300 es
+
+/*
+ * Copyright (C) 2017-2020 Philip Jones
+ *
+ * Licensed under the MIT License.
+ * See either the LICENSE file, or:
+ *
+ * https://opensource.org/licenses/MIT
+ *
+ */
+
+in vec2 position;
+in vec2 texcoord;
+
+out vec2 Texcoord;
+
+void main()
+{
+ Texcoord = texcoord;
+ gl_Position = vec4(position, 0.0, 1.0);
+}
diff --git a/src/client.h b/src/client.h
index 04d78ac..32670af 100644
--- a/src/client.h
+++ b/src/client.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stdint.h>
#include "egl.h"
+#include "gl.h"
struct client_state {
/* Globals */
@@ -25,6 +26,18 @@ struct client_state {
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;
/* Keyboard state */
struct xkb_state *xkb_state;
struct xkb_context *xkb_context;
diff --git a/src/egl.c b/src/egl.c
index bd45413..363ecb8 100644
--- a/src/egl.c
+++ b/src/egl.c
@@ -38,7 +38,7 @@ void egl_create_context(struct client_state *state)
EGLint num_configs;
static const EGLint config_attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
@@ -64,7 +64,7 @@ void egl_create_context(struct client_state *state)
}
static const EGLint context_attribs[] = {
- EGL_CONTEXT_MAJOR_VERSION, 2,
+ EGL_CONTEXT_MAJOR_VERSION, 3,
EGL_NONE
};
diff --git a/src/gl.c b/src/gl.c
new file mode 100644
index 0000000..8187438
--- /dev/null
+++ b/src/gl.c
@@ -0,0 +1,177 @@
+#include <epoxy/gl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "client.h"
+#include "gl.h"
+#include "log.h"
+
+#define max(a, b) ((a) > (b) ? (a) : (b))
+
+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)
+{
+ if (state->background_image.buffer == NULL) {
+ return;
+ }
+
+ struct gl *gl = &state->gl;
+
+ /* Compile and link the shader programs */
+ gl->shader = create_shader_program(
+ SHADER_PATH "vert.vert",
+ SHADER_PATH "frag.frag"
+ );
+ glUseProgram(gl->shader);
+ glUniform1i(glGetUniformLocation(gl->shader, "tex"), 0);
+
+ /* Create a vertex buffer for a quad filling the screen */
+ float vertices[] = {
+ // Position Texcoords
+ -1.0f, 1.0f, 0.0f, 0.0f, // Top-left
+ 1.0f, 1.0f, 1.0f, 0.0f, // Top-right
+ 1.0f, -1.0f, 1.0f, 1.0f, // Bottom-right
+ -1.0f, -1.0f, 0.0f, 1.0f // Bottom-left
+ };
+
+ glGenBuffers(1, &gl->vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, gl->vbo);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
+
+ /* 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);
+ glEnableVertexAttribArray(posAttrib);
+
+ GLint texAttrib = glGetAttribLocation(gl->shader, "texcoord");
+ 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(). */
+ GLuint elements[] = {
+ 0, 1, 2,
+ 2, 3, 0
+ };
+
+ glGenBuffers(1, &gl->ebo);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl->ebo);
+ 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, state->background_image.width, state->background_image.height, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, (GLvoid *)state->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);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ /* 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)
+{
+ glClearColor(
+ state->background_color.r,
+ state->background_color.g,
+ state->background_color.b,
+ state->background_color.a
+ );
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ if (state->background_image.buffer == NULL) {
+ return;
+ }
+
+ double scale = max(
+ (double)state->width / state->background_image.width,
+ (double)state->height / state->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);
+ glViewport(x, y, width, height);
+ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
+}
+
+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));
+ exit(EXIT_FAILURE);
+ }
+ if (fseek(fp, 0, SEEK_END) != 0) {
+ 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));
+ fclose(fp);
+ exit(EXIT_FAILURE);
+ }
+ unsigned long usize = (unsigned long) size;
+ 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));
+ fclose(fp);
+ exit(EXIT_FAILURE);
+ }
+ fclose(fp);
+ source[usize] = '\0';
+ glShaderSource(shader, 1, (const GLchar *const *)&source, NULL);
+ free(source);
+
+ glCompileShader(shader);
+
+ GLint status;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+ if (status != GL_TRUE) {
+ log_error("Failed to compile shader %s!\n", filename);
+
+ GLint info_length = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_length);
+ if (info_length > 1) {
+ char *log = malloc((unsigned)info_length * sizeof(*log));
+ glGetShaderInfoLog(shader, info_length, NULL, log);
+ log_error("\t%s\n", log);
+ free(log);
+ }
+ exit(EXIT_FAILURE);
+ }
+}
+
+GLuint create_shader_program(const char *vert, const char *frag)
+{
+ GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+ load_shader(vertex_shader, vert);
+
+ GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+ load_shader(fragment_shader, frag);
+
+ GLuint shader = glCreateProgram();
+ glAttachShader(shader, vertex_shader);
+ glAttachShader(shader, fragment_shader);
+ glLinkProgram(shader);
+ return shader;
+}
diff --git a/src/gl.h b/src/gl.h
new file mode 100644
index 0000000..d963f0f
--- /dev/null
+++ b/src/gl.h
@@ -0,0 +1,18 @@
+#ifndef GL_H
+#define GL_H
+
+#include <epoxy/gl.h>
+
+struct client_state;
+struct gl {
+ GLuint vbo;
+ GLuint vao;
+ GLuint ebo;
+ GLuint texture;
+ GLuint shader;
+};
+
+void gl_initialise(struct client_state *state);
+void gl_draw(struct client_state *state);
+
+#endif /* GL_H */
diff --git a/src/main.c b/src/main.c
index 8d44a58..07a4fbd 100644
--- a/src/main.c
+++ b/src/main.c
@@ -3,26 +3,25 @@
#include <epoxy/egl.h>
#include <epoxy/gl.h>
#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
-#include <time.h>
#include <unistd.h>
#include <wayland-client.h>
#include <xkbcommon/xkbcommon.h>
#include "client.h"
#include "egl.h"
+#include "gl.h"
+#include "png.h"
#include "xdg-shell-client-protocol.h"
void draw_frame(struct client_state *state)
{
- glClearColor(rand() / (double)RAND_MAX, rand() / (double)RAND_MAX, rand() / (double)RAND_MAX, 1.0);
- glClear(GL_COLOR_BUFFER_BIT);
+ gl_draw(state);
eglSwapBuffers(state->egl.display, state->egl.surface);
+ wl_surface_damage_buffer(state->wl_surface, 0, 0, 50, 50);
}
static void
@@ -39,7 +38,6 @@ xdg_toplevel_configure(void *data,
if (width != state->width || height != state->height) {
state->width = width;
state->height = height;
- printf("Resize: %d x %d\n", width, height);
wl_egl_window_resize(state->egl.window, width, height, 0, 0);
draw_frame(state);
}
@@ -224,7 +222,6 @@ wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time)
}
/* Submit a frame for this event */
- wl_surface_damage_buffer(state->wl_surface, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_commit(state->wl_surface);
state->last_frame = time;
@@ -273,6 +270,8 @@ static const struct wl_registry_listener wl_registry_listener = {
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);
@@ -294,6 +293,7 @@ int main(int argc, char *argv[])
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);
diff --git a/src/png.c b/src/png.c
new file mode 100644
index 0000000..af96c8f
--- /dev/null
+++ b/src/png.c
@@ -0,0 +1,91 @@
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <png.h>
+#include "client.h"
+#include "log.h"
+#include "png.h"
+
+#define HEADER_BYTES 8
+
+void load_background(struct client_state *state, const char *filename)
+{
+ FILE *fp = fopen(filename, "rb");
+ uint8_t header[HEADER_BYTES];
+ if (!fp) {
+ log_error("Couldn't open %s\n", filename);
+ return;
+ }
+ if (fread(header, 1, HEADER_BYTES, fp) != HEADER_BYTES) {
+ log_error("Failed to read camera data: %s\n", filename);
+ fclose(fp);
+ return;
+ }
+ if (png_sig_cmp(header, 0, HEADER_BYTES)) {
+ log_error("Not a PNG file: %s\n", filename);
+ fclose(fp);
+ return;
+ }
+
+ png_structp png_ptr = png_create_read_struct(
+ PNG_LIBPNG_VER_STRING,
+ NULL, NULL, NULL);
+ if (!png_ptr) {
+ log_error("Couldn't create PNG read struct.\n");
+ fclose(fp);
+ return;
+ }
+
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
+ fclose(fp);
+ log_error("Couldn't create PNG info struct.\n");
+ return;
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr)) != 0) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ fclose(fp);
+ log_error("Couldn't setjmp for libpng.\n");
+ return;
+ }
+
+ png_init_io(png_ptr, fp);
+ png_set_sig_bytes(png_ptr, HEADER_BYTES);
+ png_read_info(png_ptr, info_ptr);
+
+ uint32_t width = png_get_image_width(png_ptr, info_ptr);
+ uint32_t height = png_get_image_height(png_ptr, info_ptr);
+ //uint8_t color_type = png_get_color_type(png_ptr, info_ptr);
+ //uint8_t bit_depth = png_get_bit_depth(png_ptr, info_ptr);
+
+ 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);
+ /* Guard against integer overflow */
+ if (height > PNG_SIZE_MAX / row_bytes) {
+ png_error(png_ptr, "image_data buffer would be too large");
+ }
+
+ png_bytep buffer = malloc(height * row_bytes);
+ png_bytepp row_pointers = calloc(height, sizeof(png_bytep));
+ for (uint32_t y = 0; y < height; y++) {
+ row_pointers[y] = &buffer[y * row_bytes];
+ }
+
+ png_read_image(png_ptr, row_pointers);
+ png_read_end(png_ptr, NULL);
+
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ free(row_pointers);
+ fclose(fp);
+
+ state->background_image.width = width;
+ state->background_image.height = height;
+ state->background_image.buffer = buffer;
+}
diff --git a/src/png.h b/src/png.h
new file mode 100644
index 0000000..68b432a
--- /dev/null
+++ b/src/png.h
@@ -0,0 +1,9 @@
+#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 */