diff options
-rw-r--r-- | LICENSE | 19 | ||||
-rw-r--r-- | meson.build | 15 | ||||
-rw-r--r-- | shaders/frag.frag | 25 | ||||
-rw-r--r-- | shaders/vert.vert | 22 | ||||
-rw-r--r-- | src/client.h | 13 | ||||
-rw-r--r-- | src/egl.c | 4 | ||||
-rw-r--r-- | src/gl.c | 177 | ||||
-rw-r--r-- | src/gl.h | 18 | ||||
-rw-r--r-- | src/main.c | 14 | ||||
-rw-r--r-- | src/png.c | 91 | ||||
-rw-r--r-- | src/png.h | 9 |
11 files changed, 395 insertions, 12 deletions
@@ -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; @@ -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 */ @@ -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 */ |