brewery
notashelf /
48fa1cc8525bbb05a245b13dba7ec49afa48077a

chroma

public

Lightweight wallpaper daemon for Wayland

Star0tarball
clone
ssh://git@git.notashelf.dev:33/notashelf/chroma.git
src/render.cC431 lines12.7 KB
1
#include <math.h>
2
#include <stdio.h>
3
#include <stdlib.h>
4
#include <string.h>
5
6
#include <EGL/egl.h>
7
#include <GLES2/gl2.h>
8
9
#include "../include/chroma.h"
10
11
// Vertex shader for simple texture rendering
12
static const char *vertex_shader_source =
13
    "#version 120\n"
14
    "attribute vec2 position;\n"
15
    "attribute vec2 texcoord;\n"
16
    "varying vec2 v_texcoord;\n"
17
    "void main() {\n"
18
    "    gl_Position = vec4(position, 0.0, 1.0);\n"
19
    "    v_texcoord = texcoord;\n"
20
    "}\n";
21
22
// Fragment shader for simple texture rendering
23
static const char *fragment_shader_source =
24
    "#version 120\n"
25
    "varying vec2 v_texcoord;\n"
26
    "uniform sampler2D texture;\n"
27
    "void main() {\n"
28
    "    gl_FragColor = texture2D(texture, v_texcoord);\n"
29
    "}\n";
30
31
// Vertices for a fullscreen quad
32
static const float vertices[] = {
33
    // Position  Texcoord
34
    -1.0f, -1.0f, 0.0f, 1.0f, // Bottom left
35
    1.0f,  -1.0f, 1.0f, 1.0f, // Bottom right
36
    1.0f,  1.0f,  1.0f, 0.0f, // Top right
37
    -1.0f, 1.0f,  0.0f, 0.0f, // Top left
38
};
39
40
static const unsigned int indices[] = {
41
    0, 1, 2, // First triangle
42
    2, 3, 0  // Second triangle
43
};
44
45
// Shader compilation helper
46
static GLuint compile_shader(GLenum type, const char *source) {
47
  GLuint shader = glCreateShader(type);
48
  if (!shader) {
49
    chroma_log("ERROR", "Failed to create shader");
50
    return 0;
51
  }
52
53
  glShaderSource(shader, 1, &source, NULL);
54
  glCompileShader(shader);
55
56
  GLint status;
57
  glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
58
  if (status != GL_TRUE) {
59
    char log[512];
60
    glGetShaderInfoLog(shader, sizeof(log), NULL, log);
61
    chroma_log("ERROR", "Shader compilation failed: %s", log);
62
    glDeleteShader(shader);
63
    return 0;
64
  }
65
66
  return shader;
67
}
68
69
// Shader program creation helper
70
static GLuint create_shader_program(void) {
71
  GLuint vertex_shader = compile_shader(GL_VERTEX_SHADER, vertex_shader_source);
72
  if (!vertex_shader) {
73
    return 0;
74
  }
75
76
  GLuint fragment_shader =
77
      compile_shader(GL_FRAGMENT_SHADER, fragment_shader_source);
78
  if (!fragment_shader) {
79
    glDeleteShader(vertex_shader);
80
    return 0;
81
  }
82
83
  GLuint program = glCreateProgram();
84
  if (!program) {
85
    chroma_log("ERROR", "Failed to create shader program");
86
    glDeleteShader(vertex_shader);
87
    glDeleteShader(fragment_shader);
88
    return 0;
89
  }
90
91
  glAttachShader(program, vertex_shader);
92
  glAttachShader(program, fragment_shader);
93
  glLinkProgram(program);
94
95
  GLint status;
96
  glGetProgramiv(program, GL_LINK_STATUS, &status);
97
  if (status != GL_TRUE) {
98
    char log[512];
99
    glGetProgramInfoLog(program, sizeof(log), NULL, log);
100
    chroma_log("ERROR", "Shader program linking failed: %s", log);
101
    glDeleteProgram(program);
102
    program = 0;
103
  }
104
105
  glDeleteShader(vertex_shader);
106
  glDeleteShader(fragment_shader);
107
108
  return program;
109
}
110
111
// EGL configuration selection
112
static int choose_egl_config(EGLDisplay display, EGLConfig *config) {
113
  EGLint attributes[] = {EGL_SURFACE_TYPE,
114
                         EGL_WINDOW_BIT,
115
                         EGL_RED_SIZE,
116
                         8,
117
                         EGL_GREEN_SIZE,
118
                         8,
119
                         EGL_BLUE_SIZE,
120
                         8,
121
                         EGL_ALPHA_SIZE,
122
                         8,
123
                         EGL_RENDERABLE_TYPE,
124
                         EGL_OPENGL_BIT,
125
                         EGL_NONE};
126
127
  EGLint num_configs;
128
  if (!eglChooseConfig(display, attributes, config, 1, &num_configs)) {
129
    chroma_log("ERROR", "Failed to choose EGL config: 0x%04x", eglGetError());
130
    return -1;
131
  }
132
133
  if (num_configs == 0) {
134
    chroma_log("ERROR", "No suitable EGL configs found");
135
    return -1;
136
  }
137
138
  return 0;
139
}
140
141
// EGL initialization
142
int chroma_egl_init(chroma_state_t *state) {
143
  if (!state || !state->display) {
144
    return CHROMA_ERROR_INIT;
145
  }
146
147
  // Get EGL display
148
  state->egl_display = eglGetDisplay((EGLNativeDisplayType)state->display);
149
  if (state->egl_display == EGL_NO_DISPLAY) {
150
    chroma_log("ERROR", "Failed to get EGL display: 0x%04x", eglGetError());
151
    return CHROMA_ERROR_EGL;
152
  }
153
154
  // Initialize EGL
155
  EGLint major, minor;
156
  if (!eglInitialize(state->egl_display, &major, &minor)) {
157
    chroma_log("ERROR", "Failed to initialize EGL: 0x%04x", eglGetError());
158
    return CHROMA_ERROR_EGL;
159
  }
160
161
  chroma_log("INFO", "EGL initialized: version %d.%d", major, minor);
162
163
  // Bind OpenGL API
164
  if (!eglBindAPI(EGL_OPENGL_API)) {
165
    chroma_log("ERROR", "Failed to bind OpenGL API: 0x%04x", eglGetError());
166
    chroma_egl_cleanup(state);
167
    return CHROMA_ERROR_EGL;
168
  }
169
170
  // Choose EGL config
171
  if (choose_egl_config(state->egl_display, &state->egl_config) != 0) {
172
    chroma_egl_cleanup(state);
173
    return CHROMA_ERROR_EGL;
174
  }
175
176
  // Create EGL context
177
  EGLint context_attributes[] = {EGL_CONTEXT_MAJOR_VERSION, 2,
178
                                 EGL_CONTEXT_MINOR_VERSION, 1, EGL_NONE};
179
180
  state->egl_context = eglCreateContext(state->egl_display, state->egl_config,
181
                                        EGL_NO_CONTEXT, context_attributes);
182
  if (state->egl_context == EGL_NO_CONTEXT) {
183
    chroma_log("ERROR", "Failed to create EGL context: 0x%04x", eglGetError());
184
    chroma_egl_cleanup(state);
185
    return CHROMA_ERROR_EGL;
186
  }
187
188
  chroma_log("INFO", "EGL context created successfully");
189
  return CHROMA_OK;
190
}
191
192
// EGL cleanup
193
void chroma_egl_cleanup(chroma_state_t *state) {
194
  if (!state) {
195
    return;
196
  }
197
198
  if (state->egl_display != EGL_NO_DISPLAY) {
199
    eglMakeCurrent(state->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
200
                   EGL_NO_CONTEXT);
201
202
    if (state->egl_context != EGL_NO_CONTEXT) {
203
      eglDestroyContext(state->egl_display, state->egl_context);
204
      state->egl_context = EGL_NO_CONTEXT;
205
    }
206
207
    eglTerminate(state->egl_display);
208
    state->egl_display = EGL_NO_DISPLAY;
209
  }
210
211
  chroma_log("INFO", "EGL cleaned up");
212
}
213
214
// Create surface for output
215
int chroma_surface_create(chroma_state_t *state, chroma_output_t *output) {
216
  if (!state || !output || !state->compositor || !state->layer_shell) {
217
    return CHROMA_ERROR_INIT;
218
  }
219
220
  // Create Wayland surface
221
  output->surface = wl_compositor_create_surface(state->compositor);
222
  if (!output->surface) {
223
    chroma_log("ERROR", "Failed to create Wayland surface for output %u",
224
               output->id);
225
    return CHROMA_ERROR_WAYLAND;
226
  }
227
228
  // Create layer surface for wallpaper
229
  output->layer_surface = zwlr_layer_shell_v1_get_layer_surface(
230
      state->layer_shell, output->surface, output->wl_output,
231
      ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "chroma-wallpaper");
232
233
  if (!output->layer_surface) {
234
    chroma_log("ERROR", "Failed to create layer surface for output %u",
235
               output->id);
236
    chroma_surface_destroy(output);
237
    return CHROMA_ERROR_WAYLAND;
238
  }
239
240
  // Configure layer surface
241
  zwlr_layer_surface_v1_set_size(output->layer_surface, output->width,
242
                                 output->height);
243
  zwlr_layer_surface_v1_set_anchor(output->layer_surface,
244
                                   ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
245
                                       ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
246
                                       ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
247
                                       ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
248
  zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, -1);
249
  zwlr_layer_surface_v1_set_keyboard_interactivity(
250
      output->layer_surface, ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE);
251
252
  // Add layer surface listener
253
  zwlr_layer_surface_v1_add_listener(
254
      output->layer_surface, &chroma_layer_surface_listener_impl, output);
255
256
  // Commit surface to trigger configure event
257
  wl_surface_commit(output->surface);
258
259
  // Wait for configure event
260
  wl_display_roundtrip(state->display);
261
262
  // Create EGL window
263
  output->egl_window =
264
      wl_egl_window_create(output->surface, output->width, output->height);
265
  if (!output->egl_window) {
266
    chroma_log("ERROR", "Failed to create EGL window for output %u",
267
               output->id);
268
    chroma_surface_destroy(output);
269
    return CHROMA_ERROR_EGL;
270
  }
271
272
  // Create EGL surface
273
  output->egl_surface =
274
      eglCreateWindowSurface(state->egl_display, state->egl_config,
275
                             (EGLNativeWindowType)output->egl_window, NULL);
276
  if (output->egl_surface == EGL_NO_SURFACE) {
277
    chroma_log("ERROR", "Failed to create EGL surface for output %u: 0x%04x",
278
               output->id, eglGetError());
279
    chroma_surface_destroy(output);
280
    return CHROMA_ERROR_EGL;
281
  }
282
283
  chroma_log("INFO", "Created surface for output %u (%dx%d)", output->id,
284
             output->width, output->height);
285
286
  return CHROMA_OK;
287
}
288
289
// Destroy surface
290
void chroma_surface_destroy(chroma_output_t *output) {
291
  if (!output) {
292
    return;
293
  }
294
295
  if (output->egl_surface != EGL_NO_SURFACE) {
296
    eglDestroySurface(eglGetCurrentDisplay(), output->egl_surface);
297
    output->egl_surface = EGL_NO_SURFACE;
298
  }
299
300
  if (output->egl_window) {
301
    wl_egl_window_destroy(output->egl_window);
302
    output->egl_window = NULL;
303
  }
304
305
  if (output->layer_surface) {
306
    zwlr_layer_surface_v1_destroy(output->layer_surface);
307
    output->layer_surface = NULL;
308
  }
309
310
  if (output->surface) {
311
    wl_surface_destroy(output->surface);
312
    output->surface = NULL;
313
  }
314
315
  chroma_log("DEBUG", "Destroyed surface for output %u", output->id);
316
}
317
318
// Create texture from image data
319
static GLuint create_texture_from_image(chroma_image_t *image) {
320
  if (!image || !image->loaded || !image->data) {
321
    return 0;
322
  }
323
324
  GLuint texture;
325
  glGenTextures(1, &texture);
326
  glBindTexture(GL_TEXTURE_2D, texture);
327
328
  // Set texture parameters
329
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
330
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
331
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
332
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
333
334
  // Upload texture data
335
  GLenum format = (image->channels == 4) ? GL_RGBA : GL_RGB;
336
  glTexImage2D(GL_TEXTURE_2D, 0, format, image->width, image->height, 0, format,
337
               GL_UNSIGNED_BYTE, image->data);
338
339
  glBindTexture(GL_TEXTURE_2D, 0);
340
341
  return texture;
342
}
343
344
// Render wallpaper to output
345
int chroma_render_wallpaper(chroma_state_t *state, chroma_output_t *output) {
346
  if (!state || !output || !output->image || !output->image->loaded) {
347
    return CHROMA_ERROR_INIT;
348
  }
349
350
  // Make context current
351
  if (!eglMakeCurrent(state->egl_display, output->egl_surface,
352
                      output->egl_surface, state->egl_context)) {
353
    chroma_log("ERROR", "Failed to make EGL context current: 0x%04x",
354
               eglGetError());
355
    return CHROMA_ERROR_EGL;
356
  }
357
358
  // Set viewport
359
  glViewport(0, 0, output->width, output->height);
360
361
  // Clear screen
362
  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
363
  glClear(GL_COLOR_BUFFER_BIT);
364
365
  // Create shader program (should be cached in real implementation)
366
  GLuint program = create_shader_program();
367
  if (!program) {
368
    return CHROMA_ERROR_EGL;
369
  }
370
371
  // Use shader program
372
  glUseProgram(program);
373
374
  // Create and bind texture
375
  GLuint texture = create_texture_from_image(output->image);
376
  if (!texture) {
377
    chroma_log("ERROR", "Failed to create texture for output %u", output->id);
378
    glDeleteProgram(program);
379
    return CHROMA_ERROR_EGL;
380
  }
381
382
  glActiveTexture(GL_TEXTURE0);
383
  glBindTexture(GL_TEXTURE_2D, texture);
384
  glUniform1i(glGetUniformLocation(program, "texture"), 0);
385
386
  // Create vertex buffer objects (should be cached in real implementation)
387
  GLuint vbo, ebo;
388
  glGenBuffers(1, &vbo);
389
  glGenBuffers(1, &ebo);
390
391
  glBindBuffer(GL_ARRAY_BUFFER, vbo);
392
  glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
393
394
  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
395
  glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices,
396
               GL_STATIC_DRAW);
397
398
  // Set vertex attributes
399
  GLint position_attr = glGetAttribLocation(program, "position");
400
  GLint texcoord_attr = glGetAttribLocation(program, "texcoord");
401
402
  glEnableVertexAttribArray(position_attr);
403
  glVertexAttribPointer(position_attr, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float),
404
                        (void *)0);
405
406
  glEnableVertexAttribArray(texcoord_attr);
407
  glVertexAttribPointer(texcoord_attr, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float),
408
                        (void *)(2 * sizeof(float)));
409
410
  // Draw
411
  glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
412
413
  // Cleanup
414
  glDeleteBuffers(1, &vbo);
415
  glDeleteBuffers(1, &ebo);
416
  glDeleteTextures(1, &texture);
417
  glDeleteProgram(program);
418
419
  // Swap buffers
420
  if (!eglSwapBuffers(state->egl_display, output->egl_surface)) {
421
    chroma_log("ERROR", "Failed to swap buffers for output %u: 0x%04x",
422
               output->id, eglGetError());
423
    return CHROMA_ERROR_EGL;
424
  }
425
426
  // Commit surface
427
  wl_surface_commit(output->surface);
428
429
  chroma_log("DEBUG", "Rendered wallpaper to output %u", output->id);
430
  return CHROMA_OK;
431
}