blob: 8fd73242d2d14eb85fd2605fe73c5cbc862bb6d7 [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>
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -050012#include <cairo.h>
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -050013#include <gdk-pixbuf/gdk-pixbuf.h>
14#include <glib.h>
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -050015#include <png.h>
Kristian Høgsberg54879822008-11-23 17:07:32 -050016#include <math.h>
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -050017#include <linux/input.h>
Kristian Høgsbergbf9541f2008-11-25 12:10:09 -050018#include <xf86drmMode.h>
Kristian Høgsberg16eb6752008-10-08 22:51:32 -040019
20#include "wayland.h"
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -050021#include "cairo-util.h"
Kristian Høgsberg16eb6752008-10-08 22:51:32 -040022
23#include <GL/gl.h>
24#include <eagle.h>
25
26#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
27
28struct egl_compositor {
29 struct wl_compositor base;
30 EGLDisplay display;
31 EGLSurface surface;
32 EGLContext context;
Kristian Høgsberg2d9cd1e2008-11-03 08:09:34 -050033 EGLConfig config;
Kristian Høgsbergf9212892008-10-11 18:40:23 -040034 struct wl_display *wl_display;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -040035 int gem_fd;
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -050036 int width, height;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -050037 struct egl_surface *pointer;
38 struct egl_surface *background;
Kristian Høgsberg54879822008-11-23 17:07:32 -050039 struct egl_surface *overlay;
Kristian Høgsberg5c1e6ec2008-11-25 13:51:36 -050040 double overlay_y, overlay_target, overlay_previous;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -040041};
42
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -050043struct egl_surface {
Kristian Høgsberg16eb6752008-10-08 22:51:32 -040044 GLuint texture;
Kristian Høgsbergf9212892008-10-11 18:40:23 -040045 struct wl_map map;
Kristian Høgsberg2d9cd1e2008-11-03 08:09:34 -050046 EGLSurface surface;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -040047};
48
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -050049static void
50die(const char *msg, ...)
51{
52 va_list ap;
53
54 va_start (ap, msg);
55 vfprintf(stderr, msg, ap);
56 va_end (ap);
57
58 exit(EXIT_FAILURE);
59}
60
61static void
62stdio_write_func (png_structp png, png_bytep data, png_size_t size)
63{
64 FILE *fp;
65 size_t ret;
66
67 fp = png_get_io_ptr (png);
68 while (size) {
69 ret = fwrite (data, 1, size, fp);
70 size -= ret;
71 data += ret;
72 if (size && ferror (fp))
73 die("write: %m\n");
74 }
75}
76
77static void
78png_simple_output_flush_fn (png_structp png_ptr)
79{
80}
81
82static void
83png_simple_error_callback (png_structp png,
84 png_const_charp error_msg)
85{
86 die("png error: %s\n", error_msg);
87}
88
89static void
90png_simple_warning_callback (png_structp png,
91 png_const_charp error_msg)
92{
93 fprintf(stderr, "png warning: %s\n", error_msg);
94}
95
96static void
97convert_pixels(png_structp png, png_row_infop row_info, png_bytep data)
98{
99 unsigned int i;
100
101 for (i = 0; i < row_info->rowbytes; i += 4) {
102 uint8_t *b = &data[i];
103 uint32_t pixel;
104
105 memcpy (&pixel, b, sizeof (uint32_t));
106 b[0] = (pixel & 0xff0000) >> 16;
107 b[1] = (pixel & 0x00ff00) >> 8;
108 b[2] = (pixel & 0x0000ff) >> 0;
109 b[3] = 0;
110 }
111}
112
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -0500113struct screenshooter {
114 struct wl_object base;
115 struct egl_compositor *ec;
116};
117
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500118static void
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -0500119screenshooter_shoot(struct wl_client *client, struct screenshooter *shooter)
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500120{
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -0500121 struct egl_compositor *ec = shooter->ec;
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500122 png_struct *png;
123 png_info *info;
124 png_byte **volatile rows = NULL;
125 png_color_16 white;
126 int depth, i;
127 FILE *fp;
128 uint8_t *data;
129 GLuint stride;
130 static const char filename[] = "wayland-screenshot.png";
131
132 data = eglReadBuffer(ec->display, ec->surface, GL_FRONT_LEFT, &stride);
133 if (data == NULL)
134 die("eglReadBuffer failed\n");
135 rows = malloc(ec->height * sizeof rows[0]);
136 if (rows == NULL)
137 die("malloc failed\n");
138
139 for (i = 0; i < ec->height; i++)
140 rows[i] = (png_byte *) data + i * stride;
141
142 png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
143 png_simple_error_callback,
144 png_simple_warning_callback);
145 if (png == NULL)
146 die("png_create_write_struct failed\n");
147
148 info = png_create_info_struct(png);
149 if (info == NULL)
150 die("png_create_info_struct failed\n");
151
152 fp = fopen(filename, "w");
153 if (fp == NULL)
154 die("fopen failed: %m\n");
155
156 png_set_write_fn(png, fp, stdio_write_func, png_simple_output_flush_fn);
157
158 depth = 8;
159 png_set_IHDR(png, info,
160 ec->width,
161 ec->height, depth,
162 PNG_COLOR_TYPE_RGB,
163 PNG_INTERLACE_NONE,
164 PNG_COMPRESSION_TYPE_DEFAULT,
165 PNG_FILTER_TYPE_DEFAULT);
166
167 white.gray = (1 << depth) - 1;
168 white.red = white.blue = white.green = white.gray;
169 png_set_bKGD(png, info, &white);
170 png_write_info (png, info);
171 png_set_write_user_transform_fn(png, convert_pixels);
172
173 png_set_filler(png, 0, PNG_FILLER_AFTER);
174 png_write_image(png, rows);
175 png_write_end(png, info);
176
177 png_destroy_write_struct(&png, &info);
178 fclose(fp);
179 free(rows);
180 free(data);
181}
182
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -0500183static const struct wl_method screenshooter_methods[] = {
184 { "shoot", screenshooter_shoot, 0, NULL }
185};
186
187static const struct wl_interface screenshooter_interface = {
188 "screenshooter", 1,
189 ARRAY_LENGTH(screenshooter_methods),
190 screenshooter_methods,
191};
192
193static struct screenshooter *
194screenshooter_create(struct egl_compositor *ec)
195{
196 struct screenshooter *shooter;
197
198 shooter = malloc(sizeof *shooter);
199 if (shooter == NULL)
200 return NULL;
201
202 shooter->base.interface = &screenshooter_interface;
203 shooter->ec = ec;
204
205 return shooter;
206};
207
Kristian Høgsberg54879822008-11-23 17:07:32 -0500208static struct egl_surface *
209egl_surface_create_from_cairo_surface(cairo_surface_t *surface,
210 int x, int y, int width, int height)
211{
212 struct egl_surface *es;
213 int stride;
214 void *data;
215
216 stride = cairo_image_surface_get_stride(surface);
217 data = cairo_image_surface_get_data(surface);
218
219 es = malloc(sizeof *es);
220 if (es == NULL)
221 return NULL;
222
223 glGenTextures(1, &es->texture);
224 glBindTexture(GL_TEXTURE_2D, es->texture);
225 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
226 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT);
227 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
228 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
229 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
230 GL_BGRA, GL_UNSIGNED_BYTE, data);
231
232 es->map.x = x;
233 es->map.y = y;
234 es->map.width = width;
235 es->map.height = height;
236 es->surface = EGL_NO_SURFACE;
237
238 return es;
239}
240
241static void
242egl_surface_destroy(struct egl_surface *es, struct egl_compositor *ec)
243{
244 glDeleteTextures(1, &es->texture);
245 if (es->surface != EGL_NO_SURFACE)
246 eglDestroySurface(ec->display, es->surface);
247 free(es);
248}
249
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400250static void
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500251pointer_path(cairo_t *cr, int x, int y)
252{
253 const int end = 3, tx = 4, ty = 12, dx = 5, dy = 10;
254 const int width = 16, height = 16;
255
256 cairo_move_to(cr, x, y);
257 cairo_line_to(cr, x + tx, y + ty);
258 cairo_line_to(cr, x + dx, y + dy);
259 cairo_line_to(cr, x + width - end, y + height);
260 cairo_line_to(cr, x + width, y + height - end);
261 cairo_line_to(cr, x + dy, y + dx);
262 cairo_line_to(cr, x + ty, y + tx);
263 cairo_close_path(cr);
264}
265
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500266static struct egl_surface *
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500267pointer_create(int x, int y, int width, int height)
268{
Kristian Høgsberg54879822008-11-23 17:07:32 -0500269 struct egl_surface *es;
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500270 const int hotspot_x = 16, hotspot_y = 16;
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500271 cairo_surface_t *surface;
272 cairo_t *cr;
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500273
274 surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
275 width, height);
276
277 cr = cairo_create(surface);
278 pointer_path(cr, hotspot_x + 5, hotspot_y + 4);
279 cairo_set_line_width (cr, 2);
280 cairo_set_source_rgb(cr, 0, 0, 0);
281 cairo_stroke_preserve(cr);
282 cairo_fill(cr);
283 blur_surface(surface, width);
284
285 pointer_path(cr, hotspot_x, hotspot_y);
286 cairo_stroke_preserve(cr);
287 cairo_set_source_rgb(cr, 1, 1, 1);
288 cairo_fill(cr);
289 cairo_destroy(cr);
290
Kristian Høgsberg54879822008-11-23 17:07:32 -0500291 es = egl_surface_create_from_cairo_surface(surface, x, y, width, height);
292
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500293 cairo_surface_destroy(surface);
294
Kristian Høgsberg54879822008-11-23 17:07:32 -0500295 return es;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500296}
297
298static struct egl_surface *
299background_create(const char *filename, int width, int height)
300{
301 struct egl_surface *background;
302 GdkPixbuf *pixbuf;
303 GError *error = NULL;
304 int pixbuf_width, pixbuf_height;
305 void *data;
306
307 background = malloc(sizeof *background);
308 if (background == NULL)
309 return NULL;
310
311 g_type_init();
312
313 pixbuf = gdk_pixbuf_new_from_file(filename, &error);
314 if (error != NULL) {
315 free(background);
316 return NULL;
317 }
318
319 pixbuf_width = gdk_pixbuf_get_width(pixbuf);
320 pixbuf_height = gdk_pixbuf_get_height(pixbuf);
321 data = gdk_pixbuf_get_pixels(pixbuf);
322
323 glGenTextures(1, &background->texture);
324 glBindTexture(GL_TEXTURE_2D, background->texture);
325 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
326 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT);
327 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
328 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
329 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, pixbuf_width, pixbuf_height, 0,
330 GL_BGR, GL_UNSIGNED_BYTE, data);
331
332 background->map.x = 0;
333 background->map.y = 0;
334 background->map.width = width;
335 background->map.height = height;
336 background->surface = EGL_NO_SURFACE;
337
338 return background;
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500339}
340
341static void
Kristian Høgsberg54879822008-11-23 17:07:32 -0500342rounded_rect(cairo_t *cr, int x0, int y0, int x1, int y1, int radius)
343{
344 cairo_move_to(cr, x0, y0 + radius);
345 cairo_arc(cr, x0 + radius, y0 + radius, radius, M_PI, 3 * M_PI / 2);
346 cairo_line_to(cr, x1 - radius, y0);
347 cairo_arc(cr, x1 - radius, y0 + radius, radius, 3 * M_PI / 2, 2 * M_PI);
348 cairo_line_to(cr, x1, y1 - radius);
349 cairo_arc(cr, x1 - radius, y1 - radius, radius, 0, M_PI / 2);
350 cairo_line_to(cr, x0 + radius, y1);
351 cairo_arc(cr, x0 + radius, y1 - radius, radius, M_PI / 2, M_PI);
352 cairo_close_path(cr);
353}
354
355static void
356draw_button(cairo_t *cr, int x, int y, int width, int height, const char *text)
357{
358 cairo_pattern_t *gradient;
359 cairo_text_extents_t extents;
360 double bright = 0.15, dim = 0.02;
361 int radius = 10;
362
363 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
364 cairo_set_line_width (cr, 2);
365 rounded_rect(cr, x, y, x + width, y + height, radius);
366 cairo_set_source_rgb(cr, dim, dim, dim);
367 cairo_stroke(cr);
368 rounded_rect(cr, x + 2, y + 2, x + width, y + height, radius);
369 cairo_set_source_rgb(cr, 0.1, 0.1, 0.1);
370 cairo_stroke(cr);
371
372 rounded_rect(cr, x + 1, y + 1, x + width - 1, y + height - 1, radius - 1);
373 cairo_set_source_rgb(cr, bright, bright, bright);
374 cairo_stroke(cr);
375 rounded_rect(cr, x + 3, y + 3, x + width - 1, y + height - 1, radius - 1);
376 cairo_set_source_rgb(cr, dim, dim, dim);
377 cairo_stroke(cr);
378
379 rounded_rect(cr, x + 1, y + 1, x + width - 1, y + height - 1, radius - 1);
380 gradient = cairo_pattern_create_linear (0, y, 0, y + height);
381 cairo_pattern_add_color_stop_rgb(gradient, 0, 0.15, 0.15, 0.15);
382 cairo_pattern_add_color_stop_rgb(gradient, 0.5, 0.08, 0.08, 0.08);
383 cairo_pattern_add_color_stop_rgb(gradient, 0.5, 0.07, 0.07, 0.07);
384 cairo_pattern_add_color_stop_rgb(gradient, 1, 0.1, 0.1, 0.1);
385 cairo_set_source(cr, gradient);
386 cairo_fill(cr);
387
388 cairo_set_font_size(cr, 16);
389 cairo_text_extents(cr, text, &extents);
390 cairo_move_to(cr, x + (width - extents.width) / 2, y + (height - extents.height) / 2 - extents.y_bearing);
391 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
392 cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
393 cairo_set_line_width (cr, 4);
394 cairo_text_path(cr, text);
395 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
396 cairo_stroke_preserve(cr);
397 cairo_set_source_rgb(cr, 1, 1, 1);
398 cairo_fill(cr);
399}
400
401static struct egl_surface *
402overlay_create(int x, int y, int width, int height)
403{
404 struct egl_surface *es;
405 cairo_surface_t *surface;
406 cairo_t *cr;
407 int total_width, button_x, button_y;
408 const int button_width = 150;
409 const int button_height = 40;
410 const int spacing = 50;
411
412 surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
413 width, height);
414
415 cr = cairo_create(surface);
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -0500416 cairo_set_source_rgba(cr, 0.1, 0.1, 0.1, 0.8);
Kristian Høgsberg54879822008-11-23 17:07:32 -0500417 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
418 cairo_paint(cr);
419
420 total_width = button_width * 2 + spacing;
421 button_x = (width - total_width) / 2;
422 button_y = height - button_height - 20;
423 draw_button(cr, button_x, button_y, button_width, button_height, "Previous");
424 button_x += button_width + spacing;
425 draw_button(cr, button_x, button_y, button_width, button_height, "Next");
426
427 cairo_destroy(cr);
428
429 es = egl_surface_create_from_cairo_surface(surface, x, y, width, height);
430
431 cairo_surface_destroy(surface);
432
433 return es;
434}
435
436static void
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500437draw_surface(struct egl_surface *es)
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500438{
439 GLint vertices[12];
440 GLint tex_coords[12] = { 0, 0, 0, 1, 1, 0, 1, 1 };
441 GLuint indices[4] = { 0, 1, 2, 3 };
442
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500443 vertices[0] = es->map.x;
444 vertices[1] = es->map.y;
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500445 vertices[2] = 0;
446
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500447 vertices[3] = es->map.x;
448 vertices[4] = es->map.y + es->map.height;
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500449 vertices[5] = 0;
450
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500451 vertices[6] = es->map.x + es->map.width;
452 vertices[7] = es->map.y;
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500453 vertices[8] = 0;
454
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500455 vertices[9] = es->map.x + es->map.width;
456 vertices[10] = es->map.y + es->map.height;
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500457 vertices[11] = 0;
458
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500459 glBindTexture(GL_TEXTURE_2D, es->texture);
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500460 glEnable(GL_TEXTURE_2D);
461 glEnable(GL_BLEND);
462 /* Assume pre-multiplied alpha for now, this probably
463 * needs to be a wayland visual type of thing. */
464 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
465
466 glEnableClientState(GL_VERTEX_ARRAY);
467 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
468 glVertexPointer(3, GL_INT, 0, vertices);
469 glTexCoordPointer(2, GL_INT, 0, tex_coords);
470 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, indices);
471}
472
473static void
Kristian Høgsberg9af92b32008-11-24 01:12:46 -0500474schedule_repaint(struct egl_compositor *ec);
475
476static void
Kristian Høgsberg5c1e6ec2008-11-25 13:51:36 -0500477animate_overlay(struct egl_compositor *ec)
478{
479 double force, y;
480 int32_t top, bottom;
481
482 y = ec->overlay_y;
483 force = (ec->overlay_target - ec->overlay_y) / 5.0 +
484 (ec->overlay_previous - y);
485
486 ec->overlay_y = y + (y - ec->overlay_previous) + force;
487 ec->overlay_previous = y;
488
489 top = ec->height - ec->overlay->map.height;
490 bottom = ec->height;
491 if (ec->overlay_y >= bottom) {
492 ec->overlay_y = bottom;
493 ec->overlay_previous = bottom;
494 }
495
496 if (ec->overlay_y <= top) {
497 ec->overlay_y = top;
498 ec->overlay_previous = top;
499 }
500
501 ec->overlay->map.y = ec->overlay_y + 0.5;
502 if ((int) (y + 0.5) != ec->overlay_target)
503 schedule_repaint(ec);
504}
505
506static void
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400507repaint(void *data)
508{
509 struct egl_compositor *ec = data;
510 struct wl_surface_iterator *iterator;
511 struct wl_surface *surface;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500512 struct egl_surface *es;
513
514 draw_surface(ec->background);
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400515
516 iterator = wl_surface_iterator_create(ec->wl_display, 0);
517 while (wl_surface_iterator_next(iterator, &surface)) {
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500518 es = wl_surface_get_data(surface);
519 if (es == NULL)
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400520 continue;
521
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500522 draw_surface(es);
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400523 }
524 wl_surface_iterator_destroy(iterator);
525
Kristian Høgsberg54879822008-11-23 17:07:32 -0500526 draw_surface(ec->overlay);
527
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500528 draw_surface(ec->pointer);
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500529
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400530 eglSwapBuffers(ec->display, ec->surface);
Kristian Høgsberg9af92b32008-11-24 01:12:46 -0500531
Kristian Høgsberg5c1e6ec2008-11-25 13:51:36 -0500532 animate_overlay(ec);
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400533}
534
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500535static void
536schedule_repaint(struct egl_compositor *ec)
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400537{
538 struct wl_event_loop *loop;
539
540 loop = wl_display_get_event_loop(ec->wl_display);
541 wl_event_loop_add_idle(loop, repaint, ec);
542}
543
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500544static void
545notify_surface_create(struct wl_compositor *compositor,
546 struct wl_surface *surface)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400547{
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500548 struct egl_surface *es;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400549
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500550 es = malloc(sizeof *es);
551 if (es == NULL)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400552 return;
553
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500554 es->surface = EGL_NO_SURFACE;
555 wl_surface_set_data(surface, es);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400556
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500557 glGenTextures(1, &es->texture);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400558}
559
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500560static void
561notify_surface_destroy(struct wl_compositor *compositor,
562 struct wl_surface *surface)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400563{
564 struct egl_compositor *ec = (struct egl_compositor *) compositor;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500565 struct egl_surface *es;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400566
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500567 es = wl_surface_get_data(surface);
568 if (es == NULL)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400569 return;
Kristian Høgsberg2d9cd1e2008-11-03 08:09:34 -0500570
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500571 egl_surface_destroy(es, ec);
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400572
573 schedule_repaint(ec);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400574}
575
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500576static void
577notify_surface_attach(struct wl_compositor *compositor,
578 struct wl_surface *surface, uint32_t name,
579 uint32_t width, uint32_t height, uint32_t stride)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400580{
581 struct egl_compositor *ec = (struct egl_compositor *) compositor;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500582 struct egl_surface *es;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400583
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500584 es = wl_surface_get_data(surface);
585 if (es == NULL)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400586 return;
587
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500588 if (es->surface != EGL_NO_SURFACE)
589 eglDestroySurface(ec->display, es->surface);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400590
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500591 es->surface = eglCreateSurfaceForName(ec->display, ec->config,
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500592 name, width, height, stride, NULL);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400593
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500594 glBindTexture(GL_TEXTURE_2D, es->texture);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400595 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
596 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT);
597 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
598 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500599 eglBindTexImage(ec->display, es->surface, GL_TEXTURE_2D);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400600
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400601 schedule_repaint(ec);
Kristian Høgsbergf9212892008-10-11 18:40:23 -0400602}
603
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500604static void
605notify_surface_map(struct wl_compositor *compositor,
606 struct wl_surface *surface, struct wl_map *map)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400607{
608 struct egl_compositor *ec = (struct egl_compositor *) compositor;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500609 struct egl_surface *es;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400610
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500611 es = wl_surface_get_data(surface);
612 if (es == NULL)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400613 return;
614
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500615 es->map = *map;
Kristian Høgsberg5ebb3172008-10-11 19:21:35 -0400616
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400617 schedule_repaint(ec);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400618}
619
Kristian Høgsberg7f77bd82008-11-07 08:39:37 -0500620static void
621notify_surface_copy(struct wl_compositor *compositor,
622 struct wl_surface *surface,
623 int32_t dst_x, int32_t dst_y,
624 uint32_t name, uint32_t stride,
625 int32_t x, int32_t y, int32_t width, int32_t height)
626{
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500627 struct egl_compositor *ec = (struct egl_compositor *) compositor;
628 EGLSurface src;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500629 struct egl_surface *es;
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500630
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500631 es = wl_surface_get_data(surface);
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500632
633 /* FIXME: glCopyPixels should work, but then we'll have to
634 * call eglMakeCurrent to set up the src and dest surfaces
635 * first. This seems cheaper, but maybe there's a better way
636 * to accomplish this. */
637
638 src = eglCreateSurfaceForName(ec->display, ec->config,
639 name, x + width, y + height, stride, NULL);
640
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500641 eglCopyNativeBuffers(ec->display, es->surface, GL_FRONT_LEFT, dst_x, dst_y,
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500642 src, GL_FRONT_LEFT, x, y, width, height);
643 schedule_repaint(ec);
Kristian Høgsberg7f77bd82008-11-07 08:39:37 -0500644}
645
646static void
647notify_surface_damage(struct wl_compositor *compositor,
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500648 struct wl_surface *surface,
649 int32_t x, int32_t y, int32_t width, int32_t height)
Kristian Høgsberg7f77bd82008-11-07 08:39:37 -0500650{
651 struct egl_compositor *ec = (struct egl_compositor *) compositor;
652
653 /* FIXME: This need to take a damage region, of course. */
654 schedule_repaint(ec);
655}
656
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500657static void
658notify_pointer_motion(struct wl_compositor *compositor,
659 struct wl_object *source, int x, int y)
660{
661 struct egl_compositor *ec = (struct egl_compositor *) compositor;
662
663 ec->pointer->map.x = x;
664 ec->pointer->map.y = y;
665 schedule_repaint(ec);
666}
667
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500668static void
669notify_key(struct wl_compositor *compositor,
670 struct wl_object *source, uint32_t key, uint32_t state)
671{
672 struct egl_compositor *ec = (struct egl_compositor *) compositor;
673
Kristian Høgsberg9af92b32008-11-24 01:12:46 -0500674 if (key == KEY_ESC && state == 1) {
675 if (ec->overlay_target == ec->height)
676 ec->overlay_target -= 200;
677 else
678 ec->overlay_target += 200;
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500679 schedule_repaint(ec);
Kristian Høgsberg9af92b32008-11-24 01:12:46 -0500680 }
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500681}
682
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500683static const struct wl_compositor_interface interface = {
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400684 notify_surface_create,
685 notify_surface_destroy,
686 notify_surface_attach,
Kristian Høgsberg7f77bd82008-11-07 08:39:37 -0500687 notify_surface_map,
688 notify_surface_copy,
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500689 notify_surface_damage,
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500690 notify_pointer_motion,
691 notify_key
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400692};
693
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500694static const char pointer_device_file[] =
695 "/dev/input/by-id/usb-Apple__Inc._Apple_Internal_Keyboard_._Trackpad-event-mouse";
696static const char keyboard_device_file[] =
697 "/dev/input/by-id/usb-Apple__Inc._Apple_Internal_Keyboard_._Trackpad-event-kbd";
698
699static void
700create_input_devices(struct wl_display *display)
701{
702 struct wl_object *obj;
703 const char *path;
704
705 path = getenv("WAYLAND_POINTER");
706 if (path == NULL)
707 path = pointer_device_file;
708
709 obj = wl_input_device_create(display, path);
710 if (obj != NULL)
711 wl_display_add_object(display, obj);
712
713 path = getenv("WAYLAND_KEYBOARD");
714 if (path == NULL)
715 path = keyboard_device_file;
716
717 obj = wl_input_device_create(display, path);
718 if (obj != NULL)
719 wl_display_add_object(display, obj);
720}
721
Kristian Høgsbergbf9541f2008-11-25 12:10:09 -0500722static uint32_t
723create_frontbuffer(int fd, int *width, int *height, int *stride)
724{
725 drmModeConnector *connector;
726 drmModeRes *resources;
727 drmModeEncoder *encoder;
728 struct drm_mode_modeinfo *mode;
729 struct drm_i915_gem_create create;
730 struct drm_i915_gem_pin pin;
731 struct drm_gem_flink flink;
732 unsigned int fb_id;
733 int i, ret;
734
735 resources = drmModeGetResources(fd);
736 if (!resources) {
737 fprintf(stderr, "drmModeGetResources failed\n");
738 return 0;
739 }
740
741 for (i = 0; i < resources->count_connectors; i++) {
742 connector = drmModeGetConnector(fd, resources->connectors[i]);
743 if (connector == NULL)
744 continue;
745
746 if (connector->connection == DRM_MODE_CONNECTED &&
747 connector->count_modes > 0)
748 break;
749
750 drmModeFreeConnector(connector);
751 }
752
753 if (i == resources->count_connectors) {
754 fprintf(stderr, "No currently active connector found.\n");
755 return -1;
756 }
757
758 mode = &connector->modes[0];
759
760 for (i = 0; i < resources->count_encoders; i++) {
761 encoder = drmModeGetEncoder(fd, resources->encoders[i]);
762
763 if (encoder == NULL)
764 continue;
765
766 if (encoder->encoder_id == connector->encoder_id)
767 break;
768
769 drmModeFreeEncoder(encoder);
770 }
771
772 /* Mode size at 32 bpp */
773 create.size = mode->hdisplay * mode->vdisplay * 4;
774 if (ioctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create) != 0) {
775 fprintf(stderr, "gem create failed: %m\n");
776 return 0;
777 }
778
779 pin.handle = create.handle;
780 pin.alignment = 4096;
781 if (ioctl(fd, DRM_IOCTL_I915_GEM_PIN, &pin)) {
782 fprintf(stderr, "failed to pin buffer: %m\n");
783 return 0;
784 }
785
786 ret = drmModeAddFB(fd, mode->hdisplay, mode->vdisplay,
787 32, 32, mode->hdisplay * 4, create.handle, &fb_id);
788 if (ret) {
789 fprintf(stderr, "failed to add fb: %m\n");
790 return 0;
791 }
792
793 ret = drmModeSetCrtc(fd, encoder->crtc_id, fb_id, 0, 0,
794 &connector->connector_id, 1, mode);
795 if (ret) {
796 fprintf(stderr, "failed to set mode: %m\n");
797 return 0;
798 }
799
800 flink.handle = create.handle;
801 if (ioctl(fd, DRM_IOCTL_GEM_FLINK, &flink) != 0) {
802 fprintf(stderr, "gem flink failed: %m\n");
803 return 0;
804 }
805
806 *width = mode->hdisplay;
807 *height = mode->vdisplay;
808 *stride = mode->hdisplay * 4;
809
810 return flink.name;
811}
812
Kristian Høgsberg443853c2008-11-25 12:12:05 -0500813static int
814pick_config(struct egl_compositor *ec)
815{
816 EGLConfig configs[100];
817 EGLint value, count;
818 int i;
819
820 if (!eglGetConfigs(ec->display, configs, ARRAY_LENGTH(configs), &count)) {
821 fprintf(stderr, "failed to get configs\n");
822 return -1;
823 }
824
825 ec->config = EGL_NO_CONFIG;
826 for (i = 0; i < count; i++) {
827 eglGetConfigAttrib(ec->display,
828 configs[i],
829 EGL_DEPTH_SIZE,
830 &value);
831 if (value > 0) {
832 fprintf(stderr, "config %d has depth size %d\n", i, value);
833 continue;
834 }
835
836 eglGetConfigAttrib(ec->display,
837 configs[i],
838 EGL_STENCIL_SIZE,
839 &value);
840 if (value > 0) {
841 fprintf(stderr, "config %d has stencil size %d\n", i, value);
842 continue;
843 }
844
845 eglGetConfigAttrib(ec->display,
846 configs[i],
847 EGL_CONFIG_CAVEAT,
848 &value);
849 if (value != EGL_NONE) {
850 fprintf(stderr, "config %d has caveat %d\n", i, value);
851 continue;
852 }
853
854 ec->config = configs[i];
855 break;
856 }
857
858 if (ec->config == EGL_NO_CONFIG) {
859 fprintf(stderr, "found no config without depth or stencil buffers\n");
860 return -1;
861 }
862
863 return 0;
864}
Kristian Høgsbergbf9541f2008-11-25 12:10:09 -0500865
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400866static const char gem_device[] = "/dev/dri/card0";
867
Kristian Høgsbergb7a01922008-11-08 15:39:41 -0500868WL_EXPORT struct wl_compositor *
Kristian Høgsbergf9212892008-10-11 18:40:23 -0400869wl_compositor_create(struct wl_display *display)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400870{
Kristian Høgsberg443853c2008-11-25 12:12:05 -0500871 EGLint major, minor;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400872 struct egl_compositor *ec;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500873 const char *filename;
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -0500874 struct screenshooter *shooter;
Kristian Høgsbergbf9541f2008-11-25 12:10:09 -0500875 uint32_t fb_name;
876 int stride;
877 const static EGLint attribs[] =
878 { EGL_RENDER_BUFFER, EGL_BACK_BUFFER, EGL_NONE };
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400879
880 ec = malloc(sizeof *ec);
881 if (ec == NULL)
882 return NULL;
883
884 ec->base.interface = &interface;
Kristian Høgsbergf9212892008-10-11 18:40:23 -0400885 ec->wl_display = display;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400886
Kristian Høgsbergc508d932008-10-13 22:52:42 -0400887 ec->display = eglCreateDisplayNative(gem_device, "i965");
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400888 if (ec->display == NULL) {
889 fprintf(stderr, "failed to create display\n");
890 return NULL;
891 }
892
893 if (!eglInitialize(ec->display, &major, &minor)) {
894 fprintf(stderr, "failed to initialize display\n");
895 return NULL;
896 }
897
Kristian Høgsberg443853c2008-11-25 12:12:05 -0500898 if (pick_config(ec))
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400899 return NULL;
Kristian Høgsbergbf9541f2008-11-25 12:10:09 -0500900
Kristian Høgsbergbf9541f2008-11-25 12:10:09 -0500901 fb_name = create_frontbuffer(eglGetDisplayFD(ec->display),
902 &ec->width, &ec->height, &stride);
903 ec->surface = eglCreateSurfaceForName(ec->display, ec->config,
904 fb_name, ec->width, ec->height, stride, attribs);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400905 if (ec->surface == NULL) {
906 fprintf(stderr, "failed to create surface\n");
907 return NULL;
908 }
909
Kristian Høgsberg2d9cd1e2008-11-03 08:09:34 -0500910 ec->context = eglCreateContext(ec->display, ec->config, NULL, NULL);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400911 if (ec->context == NULL) {
912 fprintf(stderr, "failed to create context\n");
913 return NULL;
914 }
915
916 if (!eglMakeCurrent(ec->display, ec->surface, ec->surface, ec->context)) {
917 fprintf(stderr, "failed to make context current\n");
918 return NULL;
919 }
920
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500921 glViewport(0, 0, ec->width, ec->height);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400922 glMatrixMode(GL_PROJECTION);
923 glLoadIdentity();
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500924 glOrtho(0, ec->width, ec->height, 0, 0, 1000.0);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400925 glMatrixMode(GL_MODELVIEW);
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500926
927 create_input_devices(display);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400928
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500929 filename = getenv("WAYLAND_BACKGROUND");
930 if (filename == NULL)
931 filename = "background.jpg";
932 ec->background = background_create(filename, 1280, 800);
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500933 ec->pointer = pointer_create(100, 100, 64, 64);
Kristian Høgsberg9af92b32008-11-24 01:12:46 -0500934 ec->overlay = overlay_create(0, ec->height, ec->width, 200);
935 ec->overlay_target = ec->height;
936 ec->overlay_previous = ec->height;
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500937
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400938 ec->gem_fd = open(gem_device, O_RDWR);
939 if (ec->gem_fd < 0) {
940 fprintf(stderr, "failed to open drm device\n");
941 return NULL;
942 }
943
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -0500944 shooter = screenshooter_create(ec);
945 wl_display_add_object(display, &shooter->base);
946 wl_display_add_global(display, &shooter->base);
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500947
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400948 schedule_repaint(ec);
949
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400950 return &ec->base;
951}