summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
7 files changed, 317 insertions, 9 deletions
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 */