blob: 580dcbf2db690708adf7ca5f5e96a1087219350b [file] [log] [blame]
Kristian Høgsberg16eb6752008-10-08 22:51:32 -04001#include <stdio.h>
2#include <string.h>
3#include <stdlib.h>
4#include <stdint.h>
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -05005#include <stdarg.h>
Kristian Høgsberg16eb6752008-10-08 22:51:32 -04006#include <i915_drm.h>
7#include <sys/ioctl.h>
8#include <sys/mman.h>
9#include <fcntl.h>
10#include <unistd.h>
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -050011#include <signal.h>
12#include <png.h>
Kristian Høgsberg16eb6752008-10-08 22:51:32 -040013
14#include "wayland.h"
15
16#include <GL/gl.h>
17#include <eagle.h>
18
19#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
20
21struct egl_compositor {
22 struct wl_compositor base;
23 EGLDisplay display;
24 EGLSurface surface;
25 EGLContext context;
Kristian Høgsberg2d9cd1e2008-11-03 08:09:34 -050026 EGLConfig config;
Kristian Høgsbergf9212892008-10-11 18:40:23 -040027 struct wl_display *wl_display;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -040028 int gem_fd;
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -050029 int width, height;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -040030};
31
32struct surface_data {
Kristian Høgsberg16eb6752008-10-08 22:51:32 -040033 GLuint texture;
Kristian Høgsbergf9212892008-10-11 18:40:23 -040034 struct wl_map map;
Kristian Høgsberg2d9cd1e2008-11-03 08:09:34 -050035 EGLSurface surface;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -040036};
37
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -050038static int do_screenshot;
39
40static void
41handle_sigusr1(int s)
42{
43 do_screenshot = 1;
44}
45
46static void
47die(const char *msg, ...)
48{
49 va_list ap;
50
51 va_start (ap, msg);
52 vfprintf(stderr, msg, ap);
53 va_end (ap);
54
55 exit(EXIT_FAILURE);
56}
57
58static void
59stdio_write_func (png_structp png, png_bytep data, png_size_t size)
60{
61 FILE *fp;
62 size_t ret;
63
64 fp = png_get_io_ptr (png);
65 while (size) {
66 ret = fwrite (data, 1, size, fp);
67 size -= ret;
68 data += ret;
69 if (size && ferror (fp))
70 die("write: %m\n");
71 }
72}
73
74static void
75png_simple_output_flush_fn (png_structp png_ptr)
76{
77}
78
79static void
80png_simple_error_callback (png_structp png,
81 png_const_charp error_msg)
82{
83 die("png error: %s\n", error_msg);
84}
85
86static void
87png_simple_warning_callback (png_structp png,
88 png_const_charp error_msg)
89{
90 fprintf(stderr, "png warning: %s\n", error_msg);
91}
92
93static void
94convert_pixels(png_structp png, png_row_infop row_info, png_bytep data)
95{
96 unsigned int i;
97
98 for (i = 0; i < row_info->rowbytes; i += 4) {
99 uint8_t *b = &data[i];
100 uint32_t pixel;
101
102 memcpy (&pixel, b, sizeof (uint32_t));
103 b[0] = (pixel & 0xff0000) >> 16;
104 b[1] = (pixel & 0x00ff00) >> 8;
105 b[2] = (pixel & 0x0000ff) >> 0;
106 b[3] = 0;
107 }
108}
109
110static void
111screenshot(struct egl_compositor *ec)
112{
113 png_struct *png;
114 png_info *info;
115 png_byte **volatile rows = NULL;
116 png_color_16 white;
117 int depth, i;
118 FILE *fp;
119 uint8_t *data;
120 GLuint stride;
121 static const char filename[] = "wayland-screenshot.png";
122
123 data = eglReadBuffer(ec->display, ec->surface, GL_FRONT_LEFT, &stride);
124 if (data == NULL)
125 die("eglReadBuffer failed\n");
126 rows = malloc(ec->height * sizeof rows[0]);
127 if (rows == NULL)
128 die("malloc failed\n");
129
130 for (i = 0; i < ec->height; i++)
131 rows[i] = (png_byte *) data + i * stride;
132
133 png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
134 png_simple_error_callback,
135 png_simple_warning_callback);
136 if (png == NULL)
137 die("png_create_write_struct failed\n");
138
139 info = png_create_info_struct(png);
140 if (info == NULL)
141 die("png_create_info_struct failed\n");
142
143 fp = fopen(filename, "w");
144 if (fp == NULL)
145 die("fopen failed: %m\n");
146
147 png_set_write_fn(png, fp, stdio_write_func, png_simple_output_flush_fn);
148
149 depth = 8;
150 png_set_IHDR(png, info,
151 ec->width,
152 ec->height, depth,
153 PNG_COLOR_TYPE_RGB,
154 PNG_INTERLACE_NONE,
155 PNG_COMPRESSION_TYPE_DEFAULT,
156 PNG_FILTER_TYPE_DEFAULT);
157
158 white.gray = (1 << depth) - 1;
159 white.red = white.blue = white.green = white.gray;
160 png_set_bKGD(png, info, &white);
161 png_write_info (png, info);
162 png_set_write_user_transform_fn(png, convert_pixels);
163
164 png_set_filler(png, 0, PNG_FILLER_AFTER);
165 png_write_image(png, rows);
166 png_write_end(png, info);
167
168 png_destroy_write_struct(&png, &info);
169 fclose(fp);
170 free(rows);
171 free(data);
172}
173
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400174static void
175repaint(void *data)
176{
177 struct egl_compositor *ec = data;
178 struct wl_surface_iterator *iterator;
179 struct wl_surface *surface;
180 struct surface_data *sd;
181 GLint vertices[12];
Kristian Høgsberg5a27f3e2008-11-02 10:55:25 -0500182 GLint tex_coords[12] = { 0, 0, 0, 1, 1, 0, 1, 1 };
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400183 GLuint indices[4] = { 0, 1, 2, 3 };
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400184
185 iterator = wl_surface_iterator_create(ec->wl_display, 0);
186 while (wl_surface_iterator_next(iterator, &surface)) {
187 sd = wl_surface_get_data(surface);
188 if (sd == NULL)
189 continue;
190
191 vertices[0] = sd->map.x;
192 vertices[1] = sd->map.y;
193 vertices[2] = 0;
194
195 vertices[3] = sd->map.x;
196 vertices[4] = sd->map.y + sd->map.height;
197 vertices[5] = 0;
198
199 vertices[6] = sd->map.x + sd->map.width;
Kristian Høgsberg48a33ba2008-10-12 12:56:11 -0400200 vertices[7] = sd->map.y;
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400201 vertices[8] = 0;
202
203 vertices[9] = sd->map.x + sd->map.width;
Kristian Høgsberg48a33ba2008-10-12 12:56:11 -0400204 vertices[10] = sd->map.y + sd->map.height;
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400205 vertices[11] = 0;
206
207 glBindTexture(GL_TEXTURE_2D, sd->texture);
208 glEnable(GL_TEXTURE_2D);
209 glEnable(GL_BLEND);
Kristian Høgsberg3f59e822008-11-03 06:35:46 -0500210 /* Assume pre-multiplied alpha for now, this probably
211 * needs to be a wayland visual type of thing. */
212 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400213
214 glEnableClientState(GL_VERTEX_ARRAY);
215 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
216 glVertexPointer(3, GL_INT, 0, vertices);
217 glTexCoordPointer(2, GL_INT, 0, tex_coords);
Kristian Høgsberg48a33ba2008-10-12 12:56:11 -0400218 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, indices);
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400219 }
220 wl_surface_iterator_destroy(iterator);
221
222 glFlush();
223
224 eglSwapBuffers(ec->display, ec->surface);
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500225
226 if (do_screenshot) {
227 glFinish();
228 /* FIXME: There's a bug somewhere so that glFinish()
229 * doesn't actually wait for all rendering to finish.
230 * I *think* it's fixed in upstream drm, but for my
231 * kernel I need this sleep now... */
232 sleep(1);
233 screenshot(ec);
234 do_screenshot = 0;
235 }
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400236}
237
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500238static void
239schedule_repaint(struct egl_compositor *ec)
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400240{
241 struct wl_event_loop *loop;
242
243 loop = wl_display_get_event_loop(ec->wl_display);
244 wl_event_loop_add_idle(loop, repaint, ec);
245}
246
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500247static void
248notify_surface_create(struct wl_compositor *compositor,
249 struct wl_surface *surface)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400250{
251 struct surface_data *sd;
252
253 sd = malloc(sizeof *sd);
254 if (sd == NULL)
255 return;
256
Kristian Høgsberg2d9cd1e2008-11-03 08:09:34 -0500257 sd->surface = EGL_NO_SURFACE;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400258 wl_surface_set_data(surface, sd);
259
260 glGenTextures(1, &sd->texture);
261}
262
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500263static void
264notify_surface_destroy(struct wl_compositor *compositor,
265 struct wl_surface *surface)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400266{
267 struct egl_compositor *ec = (struct egl_compositor *) compositor;
268 struct surface_data *sd;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400269
270 sd = wl_surface_get_data(surface);
Kristian Høgsberg2d9cd1e2008-11-03 08:09:34 -0500271 if (sd == NULL)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400272 return;
Kristian Høgsberg2d9cd1e2008-11-03 08:09:34 -0500273
274 if (sd->surface != EGL_NO_SURFACE)
275 eglDestroySurface(ec->display, sd->surface);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400276
277 glDeleteTextures(1, &sd->texture);
278
279 free(sd);
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400280
281 schedule_repaint(ec);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400282}
283
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500284static void
285notify_surface_attach(struct wl_compositor *compositor,
286 struct wl_surface *surface, uint32_t name,
287 uint32_t width, uint32_t height, uint32_t stride)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400288{
289 struct egl_compositor *ec = (struct egl_compositor *) compositor;
290 struct surface_data *sd;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400291
292 sd = wl_surface_get_data(surface);
293 if (sd == NULL)
294 return;
295
Kristian Høgsberg2d9cd1e2008-11-03 08:09:34 -0500296 if (sd->surface != EGL_NO_SURFACE)
297 eglDestroySurface(ec->display, sd->surface);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400298
Kristian Høgsberg56f3c712008-11-05 07:55:45 -0500299 /* FIXME: We need to use a single buffer config without depth
300 * or stencil buffers here to keep egl from creating auxillary
301 * buffers for the pixmap here. */
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500302 sd->surface = eglCreateSurfaceForName(ec->display, ec->config,
303 name, width, height, stride, NULL);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400304
305 glBindTexture(GL_TEXTURE_2D, sd->texture);
306 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
307 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT);
308 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
309 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Kristian Høgsberg2d9cd1e2008-11-03 08:09:34 -0500310 eglBindTexImage(ec->display, sd->surface, GL_TEXTURE_2D);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400311
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400312 schedule_repaint(ec);
Kristian Høgsbergf9212892008-10-11 18:40:23 -0400313}
314
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500315static void
316notify_surface_map(struct wl_compositor *compositor,
317 struct wl_surface *surface, struct wl_map *map)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400318{
319 struct egl_compositor *ec = (struct egl_compositor *) compositor;
320 struct surface_data *sd;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400321
322 sd = wl_surface_get_data(surface);
323 if (sd == NULL)
324 return;
325
Kristian Høgsbergf9212892008-10-11 18:40:23 -0400326 sd->map = *map;
Kristian Høgsberg5ebb3172008-10-11 19:21:35 -0400327
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400328 schedule_repaint(ec);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400329}
330
Kristian Høgsberg7f77bd82008-11-07 08:39:37 -0500331static void
332notify_surface_copy(struct wl_compositor *compositor,
333 struct wl_surface *surface,
334 int32_t dst_x, int32_t dst_y,
335 uint32_t name, uint32_t stride,
336 int32_t x, int32_t y, int32_t width, int32_t height)
337{
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500338 struct egl_compositor *ec = (struct egl_compositor *) compositor;
339 EGLSurface src;
340 struct surface_data *sd;
341
342 sd = wl_surface_get_data(surface);
343
344 /* FIXME: glCopyPixels should work, but then we'll have to
345 * call eglMakeCurrent to set up the src and dest surfaces
346 * first. This seems cheaper, but maybe there's a better way
347 * to accomplish this. */
348
349 src = eglCreateSurfaceForName(ec->display, ec->config,
350 name, x + width, y + height, stride, NULL);
351
352 eglCopyNativeBuffers(ec->display, sd->surface, GL_FRONT_LEFT, dst_x, dst_y,
353 src, GL_FRONT_LEFT, x, y, width, height);
354 schedule_repaint(ec);
Kristian Høgsberg7f77bd82008-11-07 08:39:37 -0500355}
356
357static void
358notify_surface_damage(struct wl_compositor *compositor,
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500359 struct wl_surface *surface,
360 int32_t x, int32_t y, int32_t width, int32_t height)
Kristian Høgsberg7f77bd82008-11-07 08:39:37 -0500361{
362 struct egl_compositor *ec = (struct egl_compositor *) compositor;
363
364 /* FIXME: This need to take a damage region, of course. */
365 schedule_repaint(ec);
366}
367
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500368static const struct wl_compositor_interface interface = {
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400369 notify_surface_create,
370 notify_surface_destroy,
371 notify_surface_attach,
Kristian Høgsberg7f77bd82008-11-07 08:39:37 -0500372 notify_surface_map,
373 notify_surface_copy,
374 notify_surface_damage
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400375};
376
377static const char gem_device[] = "/dev/dri/card0";
378
Kristian Høgsbergb7a01922008-11-08 15:39:41 -0500379WL_EXPORT struct wl_compositor *
Kristian Høgsbergf9212892008-10-11 18:40:23 -0400380wl_compositor_create(struct wl_display *display)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400381{
382 EGLConfig configs[64];
383 EGLint major, minor, count;
384 struct egl_compositor *ec;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400385
386 ec = malloc(sizeof *ec);
387 if (ec == NULL)
388 return NULL;
389
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500390 ec->width = 1280;
391 ec->height = 800;
392
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400393 ec->base.interface = &interface;
Kristian Høgsbergf9212892008-10-11 18:40:23 -0400394 ec->wl_display = display;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400395
Kristian Høgsbergc508d932008-10-13 22:52:42 -0400396 ec->display = eglCreateDisplayNative(gem_device, "i965");
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400397 if (ec->display == NULL) {
398 fprintf(stderr, "failed to create display\n");
399 return NULL;
400 }
401
402 if (!eglInitialize(ec->display, &major, &minor)) {
403 fprintf(stderr, "failed to initialize display\n");
404 return NULL;
405 }
406
407 if (!eglGetConfigs(ec->display, configs, ARRAY_LENGTH(configs), &count)) {
408 fprintf(stderr, "failed to get configs\n");
409 return NULL;
410 }
411
Kristian Høgsberg2d9cd1e2008-11-03 08:09:34 -0500412 ec->config = configs[24];
413 ec->surface = eglCreateSurfaceNative(ec->display, ec->config,
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500414 0, 0, ec->width, ec->height);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400415 if (ec->surface == NULL) {
416 fprintf(stderr, "failed to create surface\n");
417 return NULL;
418 }
419
Kristian Høgsberg2d9cd1e2008-11-03 08:09:34 -0500420 ec->context = eglCreateContext(ec->display, ec->config, NULL, NULL);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400421 if (ec->context == NULL) {
422 fprintf(stderr, "failed to create context\n");
423 return NULL;
424 }
425
426 if (!eglMakeCurrent(ec->display, ec->surface, ec->surface, ec->context)) {
427 fprintf(stderr, "failed to make context current\n");
428 return NULL;
429 }
430
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500431 glViewport(0, 0, ec->width, ec->height);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400432 glMatrixMode(GL_PROJECTION);
433 glLoadIdentity();
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500434 glOrtho(0, ec->width, ec->height, 0, 0, 1000.0);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400435 glMatrixMode(GL_MODELVIEW);
Kristian Høgsberga234e702008-10-11 22:13:51 -0400436 glClearColor(0.0, 0.05, 0.2, 0.0);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400437
438 ec->gem_fd = open(gem_device, O_RDWR);
439 if (ec->gem_fd < 0) {
440 fprintf(stderr, "failed to open drm device\n");
441 return NULL;
442 }
443
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500444 signal(SIGUSR1, handle_sigusr1);
445
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400446 schedule_repaint(ec);
447
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400448 return &ec->base;
449}