blob: 57b56cea578414c149568f0d8a79638ae79388cd [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;
Kristian Høgsbergffb74062008-11-25 18:10:39 -0500481#if 1
482 double bounce = 0.0;
483 double friction = 1.0;
484 double spring = 0.2;
485#else
486 double bounce = 0.2;
487 double friction = 0.04;
488 double spring = 0.09;
489#endif
Kristian Høgsberg5c1e6ec2008-11-25 13:51:36 -0500490
491 y = ec->overlay_y;
Kristian Høgsbergffb74062008-11-25 18:10:39 -0500492 force = (ec->overlay_target - ec->overlay_y) * spring +
493 (ec->overlay_previous - y) * friction;
Kristian Høgsberg5c1e6ec2008-11-25 13:51:36 -0500494
495 ec->overlay_y = y + (y - ec->overlay_previous) + force;
496 ec->overlay_previous = y;
497
498 top = ec->height - ec->overlay->map.height;
499 bottom = ec->height;
500 if (ec->overlay_y >= bottom) {
501 ec->overlay_y = bottom;
502 ec->overlay_previous = bottom;
503 }
504
505 if (ec->overlay_y <= top) {
Kristian Høgsbergffb74062008-11-25 18:10:39 -0500506 ec->overlay_y = top + bounce * (top - ec->overlay_y);
507 ec->overlay_previous =
508 top + bounce * (top - ec->overlay_previous);
Kristian Høgsberg5c1e6ec2008-11-25 13:51:36 -0500509 }
510
511 ec->overlay->map.y = ec->overlay_y + 0.5;
Kristian Høgsbergffb74062008-11-25 18:10:39 -0500512
Kristian Høgsberg73c30582008-11-25 22:45:46 -0500513 if (fabs(y - ec->overlay_target) > 0.2 ||
514 fabs(ec->overlay_y - ec->overlay_target) > 0.2)
Kristian Høgsberg5c1e6ec2008-11-25 13:51:36 -0500515 schedule_repaint(ec);
516}
517
518static void
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400519repaint(void *data)
520{
521 struct egl_compositor *ec = data;
522 struct wl_surface_iterator *iterator;
523 struct wl_surface *surface;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500524 struct egl_surface *es;
525
526 draw_surface(ec->background);
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400527
528 iterator = wl_surface_iterator_create(ec->wl_display, 0);
529 while (wl_surface_iterator_next(iterator, &surface)) {
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500530 es = wl_surface_get_data(surface);
531 if (es == NULL)
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400532 continue;
533
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500534 draw_surface(es);
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400535 }
536 wl_surface_iterator_destroy(iterator);
537
Kristian Høgsberg54879822008-11-23 17:07:32 -0500538 draw_surface(ec->overlay);
539
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500540 draw_surface(ec->pointer);
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500541
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400542 eglSwapBuffers(ec->display, ec->surface);
Kristian Høgsberg9af92b32008-11-24 01:12:46 -0500543
Kristian Høgsberg5c1e6ec2008-11-25 13:51:36 -0500544 animate_overlay(ec);
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400545}
546
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500547static void
548schedule_repaint(struct egl_compositor *ec)
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400549{
550 struct wl_event_loop *loop;
551
552 loop = wl_display_get_event_loop(ec->wl_display);
553 wl_event_loop_add_idle(loop, repaint, ec);
554}
555
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500556static void
557notify_surface_create(struct wl_compositor *compositor,
558 struct wl_surface *surface)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400559{
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500560 struct egl_surface *es;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400561
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500562 es = malloc(sizeof *es);
563 if (es == NULL)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400564 return;
565
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500566 es->surface = EGL_NO_SURFACE;
567 wl_surface_set_data(surface, es);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400568
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500569 glGenTextures(1, &es->texture);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400570}
571
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500572static void
573notify_surface_destroy(struct wl_compositor *compositor,
574 struct wl_surface *surface)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400575{
576 struct egl_compositor *ec = (struct egl_compositor *) compositor;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500577 struct egl_surface *es;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400578
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500579 es = wl_surface_get_data(surface);
580 if (es == NULL)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400581 return;
Kristian Høgsberg2d9cd1e2008-11-03 08:09:34 -0500582
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500583 egl_surface_destroy(es, ec);
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400584
585 schedule_repaint(ec);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400586}
587
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500588static void
589notify_surface_attach(struct wl_compositor *compositor,
590 struct wl_surface *surface, uint32_t name,
591 uint32_t width, uint32_t height, uint32_t stride)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400592{
593 struct egl_compositor *ec = (struct egl_compositor *) compositor;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500594 struct egl_surface *es;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400595
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500596 es = wl_surface_get_data(surface);
597 if (es == NULL)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400598 return;
599
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500600 if (es->surface != EGL_NO_SURFACE)
601 eglDestroySurface(ec->display, es->surface);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400602
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500603 es->surface = eglCreateSurfaceForName(ec->display, ec->config,
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500604 name, width, height, stride, NULL);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400605
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500606 glBindTexture(GL_TEXTURE_2D, es->texture);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400607 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
608 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT);
609 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
610 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500611 eglBindTexImage(ec->display, es->surface, GL_TEXTURE_2D);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400612
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400613 schedule_repaint(ec);
Kristian Høgsbergf9212892008-10-11 18:40:23 -0400614}
615
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500616static void
617notify_surface_map(struct wl_compositor *compositor,
618 struct wl_surface *surface, struct wl_map *map)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400619{
620 struct egl_compositor *ec = (struct egl_compositor *) compositor;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500621 struct egl_surface *es;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400622
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500623 es = wl_surface_get_data(surface);
624 if (es == NULL)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400625 return;
626
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500627 es->map = *map;
Kristian Høgsberg5ebb3172008-10-11 19:21:35 -0400628
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400629 schedule_repaint(ec);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400630}
631
Kristian Høgsberg7f77bd82008-11-07 08:39:37 -0500632static void
633notify_surface_copy(struct wl_compositor *compositor,
634 struct wl_surface *surface,
635 int32_t dst_x, int32_t dst_y,
636 uint32_t name, uint32_t stride,
637 int32_t x, int32_t y, int32_t width, int32_t height)
638{
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500639 struct egl_compositor *ec = (struct egl_compositor *) compositor;
640 EGLSurface src;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500641 struct egl_surface *es;
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500642
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500643 es = wl_surface_get_data(surface);
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500644
645 /* FIXME: glCopyPixels should work, but then we'll have to
646 * call eglMakeCurrent to set up the src and dest surfaces
647 * first. This seems cheaper, but maybe there's a better way
648 * to accomplish this. */
649
650 src = eglCreateSurfaceForName(ec->display, ec->config,
651 name, x + width, y + height, stride, NULL);
652
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500653 eglCopyNativeBuffers(ec->display, es->surface, GL_FRONT_LEFT, dst_x, dst_y,
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500654 src, GL_FRONT_LEFT, x, y, width, height);
655 schedule_repaint(ec);
Kristian Høgsberg7f77bd82008-11-07 08:39:37 -0500656}
657
658static void
659notify_surface_damage(struct wl_compositor *compositor,
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500660 struct wl_surface *surface,
661 int32_t x, int32_t y, int32_t width, int32_t height)
Kristian Høgsberg7f77bd82008-11-07 08:39:37 -0500662{
663 struct egl_compositor *ec = (struct egl_compositor *) compositor;
664
665 /* FIXME: This need to take a damage region, of course. */
666 schedule_repaint(ec);
667}
668
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500669static void
670notify_pointer_motion(struct wl_compositor *compositor,
671 struct wl_object *source, int x, int y)
672{
673 struct egl_compositor *ec = (struct egl_compositor *) compositor;
Kristian Høgsberg961a04c2008-11-25 22:38:56 -0500674 const int hotspot_x = 16, hotspot_y = 16;
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500675
Kristian Høgsberg961a04c2008-11-25 22:38:56 -0500676 ec->pointer->map.x = x - hotspot_x;
677 ec->pointer->map.y = y - hotspot_y;
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500678 schedule_repaint(ec);
679}
680
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500681static void
682notify_key(struct wl_compositor *compositor,
683 struct wl_object *source, uint32_t key, uint32_t state)
684{
685 struct egl_compositor *ec = (struct egl_compositor *) compositor;
686
Kristian Høgsberg9af92b32008-11-24 01:12:46 -0500687 if (key == KEY_ESC && state == 1) {
688 if (ec->overlay_target == ec->height)
689 ec->overlay_target -= 200;
690 else
691 ec->overlay_target += 200;
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500692 schedule_repaint(ec);
Kristian Høgsberg9af92b32008-11-24 01:12:46 -0500693 }
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500694}
695
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500696static const struct wl_compositor_interface interface = {
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400697 notify_surface_create,
698 notify_surface_destroy,
699 notify_surface_attach,
Kristian Høgsberg7f77bd82008-11-07 08:39:37 -0500700 notify_surface_map,
701 notify_surface_copy,
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500702 notify_surface_damage,
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500703 notify_pointer_motion,
704 notify_key
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400705};
706
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500707static const char pointer_device_file[] =
708 "/dev/input/by-id/usb-Apple__Inc._Apple_Internal_Keyboard_._Trackpad-event-mouse";
709static const char keyboard_device_file[] =
710 "/dev/input/by-id/usb-Apple__Inc._Apple_Internal_Keyboard_._Trackpad-event-kbd";
711
712static void
713create_input_devices(struct wl_display *display)
714{
715 struct wl_object *obj;
716 const char *path;
717
718 path = getenv("WAYLAND_POINTER");
719 if (path == NULL)
720 path = pointer_device_file;
721
722 obj = wl_input_device_create(display, path);
723 if (obj != NULL)
724 wl_display_add_object(display, obj);
725
726 path = getenv("WAYLAND_KEYBOARD");
727 if (path == NULL)
728 path = keyboard_device_file;
729
730 obj = wl_input_device_create(display, path);
731 if (obj != NULL)
732 wl_display_add_object(display, obj);
733}
734
Kristian Høgsbergbf9541f2008-11-25 12:10:09 -0500735static uint32_t
736create_frontbuffer(int fd, int *width, int *height, int *stride)
737{
738 drmModeConnector *connector;
739 drmModeRes *resources;
740 drmModeEncoder *encoder;
741 struct drm_mode_modeinfo *mode;
742 struct drm_i915_gem_create create;
743 struct drm_i915_gem_pin pin;
744 struct drm_gem_flink flink;
745 unsigned int fb_id;
746 int i, ret;
747
748 resources = drmModeGetResources(fd);
749 if (!resources) {
750 fprintf(stderr, "drmModeGetResources failed\n");
751 return 0;
752 }
753
754 for (i = 0; i < resources->count_connectors; i++) {
755 connector = drmModeGetConnector(fd, resources->connectors[i]);
756 if (connector == NULL)
757 continue;
758
759 if (connector->connection == DRM_MODE_CONNECTED &&
760 connector->count_modes > 0)
761 break;
762
763 drmModeFreeConnector(connector);
764 }
765
766 if (i == resources->count_connectors) {
767 fprintf(stderr, "No currently active connector found.\n");
768 return -1;
769 }
770
771 mode = &connector->modes[0];
772
773 for (i = 0; i < resources->count_encoders; i++) {
774 encoder = drmModeGetEncoder(fd, resources->encoders[i]);
775
776 if (encoder == NULL)
777 continue;
778
779 if (encoder->encoder_id == connector->encoder_id)
780 break;
781
782 drmModeFreeEncoder(encoder);
783 }
784
785 /* Mode size at 32 bpp */
786 create.size = mode->hdisplay * mode->vdisplay * 4;
787 if (ioctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create) != 0) {
788 fprintf(stderr, "gem create failed: %m\n");
789 return 0;
790 }
791
792 pin.handle = create.handle;
793 pin.alignment = 4096;
794 if (ioctl(fd, DRM_IOCTL_I915_GEM_PIN, &pin)) {
795 fprintf(stderr, "failed to pin buffer: %m\n");
796 return 0;
797 }
798
799 ret = drmModeAddFB(fd, mode->hdisplay, mode->vdisplay,
800 32, 32, mode->hdisplay * 4, create.handle, &fb_id);
801 if (ret) {
802 fprintf(stderr, "failed to add fb: %m\n");
803 return 0;
804 }
805
806 ret = drmModeSetCrtc(fd, encoder->crtc_id, fb_id, 0, 0,
807 &connector->connector_id, 1, mode);
808 if (ret) {
809 fprintf(stderr, "failed to set mode: %m\n");
810 return 0;
811 }
812
813 flink.handle = create.handle;
814 if (ioctl(fd, DRM_IOCTL_GEM_FLINK, &flink) != 0) {
815 fprintf(stderr, "gem flink failed: %m\n");
816 return 0;
817 }
818
819 *width = mode->hdisplay;
820 *height = mode->vdisplay;
821 *stride = mode->hdisplay * 4;
822
823 return flink.name;
824}
825
Kristian Høgsberg443853c2008-11-25 12:12:05 -0500826static int
827pick_config(struct egl_compositor *ec)
828{
829 EGLConfig configs[100];
830 EGLint value, count;
831 int i;
832
833 if (!eglGetConfigs(ec->display, configs, ARRAY_LENGTH(configs), &count)) {
834 fprintf(stderr, "failed to get configs\n");
835 return -1;
836 }
837
838 ec->config = EGL_NO_CONFIG;
839 for (i = 0; i < count; i++) {
840 eglGetConfigAttrib(ec->display,
841 configs[i],
842 EGL_DEPTH_SIZE,
843 &value);
844 if (value > 0) {
845 fprintf(stderr, "config %d has depth size %d\n", i, value);
846 continue;
847 }
848
849 eglGetConfigAttrib(ec->display,
850 configs[i],
851 EGL_STENCIL_SIZE,
852 &value);
853 if (value > 0) {
854 fprintf(stderr, "config %d has stencil size %d\n", i, value);
855 continue;
856 }
857
858 eglGetConfigAttrib(ec->display,
859 configs[i],
860 EGL_CONFIG_CAVEAT,
861 &value);
862 if (value != EGL_NONE) {
863 fprintf(stderr, "config %d has caveat %d\n", i, value);
864 continue;
865 }
866
867 ec->config = configs[i];
868 break;
869 }
870
871 if (ec->config == EGL_NO_CONFIG) {
872 fprintf(stderr, "found no config without depth or stencil buffers\n");
873 return -1;
874 }
875
876 return 0;
877}
Kristian Høgsbergbf9541f2008-11-25 12:10:09 -0500878
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400879static const char gem_device[] = "/dev/dri/card0";
880
Kristian Høgsbergb7a01922008-11-08 15:39:41 -0500881WL_EXPORT struct wl_compositor *
Kristian Høgsbergf9212892008-10-11 18:40:23 -0400882wl_compositor_create(struct wl_display *display)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400883{
Kristian Høgsberg443853c2008-11-25 12:12:05 -0500884 EGLint major, minor;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400885 struct egl_compositor *ec;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500886 const char *filename;
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -0500887 struct screenshooter *shooter;
Kristian Høgsbergbf9541f2008-11-25 12:10:09 -0500888 uint32_t fb_name;
889 int stride;
890 const static EGLint attribs[] =
891 { EGL_RENDER_BUFFER, EGL_BACK_BUFFER, EGL_NONE };
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400892
893 ec = malloc(sizeof *ec);
894 if (ec == NULL)
895 return NULL;
896
897 ec->base.interface = &interface;
Kristian Høgsbergf9212892008-10-11 18:40:23 -0400898 ec->wl_display = display;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400899
Kristian Høgsbergc508d932008-10-13 22:52:42 -0400900 ec->display = eglCreateDisplayNative(gem_device, "i965");
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400901 if (ec->display == NULL) {
902 fprintf(stderr, "failed to create display\n");
903 return NULL;
904 }
905
906 if (!eglInitialize(ec->display, &major, &minor)) {
907 fprintf(stderr, "failed to initialize display\n");
908 return NULL;
909 }
910
Kristian Høgsberg443853c2008-11-25 12:12:05 -0500911 if (pick_config(ec))
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400912 return NULL;
Kristian Høgsbergbf9541f2008-11-25 12:10:09 -0500913
Kristian Høgsbergbf9541f2008-11-25 12:10:09 -0500914 fb_name = create_frontbuffer(eglGetDisplayFD(ec->display),
915 &ec->width, &ec->height, &stride);
916 ec->surface = eglCreateSurfaceForName(ec->display, ec->config,
917 fb_name, ec->width, ec->height, stride, attribs);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400918 if (ec->surface == NULL) {
919 fprintf(stderr, "failed to create surface\n");
920 return NULL;
921 }
922
Kristian Høgsberg2d9cd1e2008-11-03 08:09:34 -0500923 ec->context = eglCreateContext(ec->display, ec->config, NULL, NULL);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400924 if (ec->context == NULL) {
925 fprintf(stderr, "failed to create context\n");
926 return NULL;
927 }
928
929 if (!eglMakeCurrent(ec->display, ec->surface, ec->surface, ec->context)) {
930 fprintf(stderr, "failed to make context current\n");
931 return NULL;
932 }
933
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500934 glViewport(0, 0, ec->width, ec->height);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400935 glMatrixMode(GL_PROJECTION);
936 glLoadIdentity();
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500937 glOrtho(0, ec->width, ec->height, 0, 0, 1000.0);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400938 glMatrixMode(GL_MODELVIEW);
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500939
940 create_input_devices(display);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400941
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500942 filename = getenv("WAYLAND_BACKGROUND");
943 if (filename == NULL)
944 filename = "background.jpg";
945 ec->background = background_create(filename, 1280, 800);
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500946 ec->pointer = pointer_create(100, 100, 64, 64);
Kristian Høgsberg9af92b32008-11-24 01:12:46 -0500947 ec->overlay = overlay_create(0, ec->height, ec->width, 200);
948 ec->overlay_target = ec->height;
949 ec->overlay_previous = ec->height;
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500950
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400951 ec->gem_fd = open(gem_device, O_RDWR);
952 if (ec->gem_fd < 0) {
953 fprintf(stderr, "failed to open drm device\n");
954 return NULL;
955 }
956
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -0500957 shooter = screenshooter_create(ec);
958 wl_display_add_object(display, &shooter->base);
959 wl_display_add_global(display, &shooter->base);
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500960
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400961 schedule_repaint(ec);
962
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400963 return &ec->base;
964}