blob: 132fcbd35a56605d3038e5fbb8b62ab3ad394ebc [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øgsberg44f36e32008-11-26 12:57:31 -0500544 wl_display_post_acknowledge(ec->wl_display);
545
Kristian Høgsberg5c1e6ec2008-11-25 13:51:36 -0500546 animate_overlay(ec);
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400547}
548
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500549static void
550schedule_repaint(struct egl_compositor *ec)
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400551{
552 struct wl_event_loop *loop;
553
554 loop = wl_display_get_event_loop(ec->wl_display);
555 wl_event_loop_add_idle(loop, repaint, ec);
556}
557
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500558static void
559notify_surface_create(struct wl_compositor *compositor,
560 struct wl_surface *surface)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400561{
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500562 struct egl_surface *es;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400563
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500564 es = malloc(sizeof *es);
565 if (es == NULL)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400566 return;
567
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500568 es->surface = EGL_NO_SURFACE;
569 wl_surface_set_data(surface, es);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400570
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500571 glGenTextures(1, &es->texture);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400572}
573
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500574static void
575notify_surface_destroy(struct wl_compositor *compositor,
576 struct wl_surface *surface)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400577{
578 struct egl_compositor *ec = (struct egl_compositor *) compositor;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500579 struct egl_surface *es;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400580
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500581 es = wl_surface_get_data(surface);
582 if (es == NULL)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400583 return;
Kristian Høgsberg2d9cd1e2008-11-03 08:09:34 -0500584
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500585 egl_surface_destroy(es, ec);
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400586
587 schedule_repaint(ec);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400588}
589
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500590static void
591notify_surface_attach(struct wl_compositor *compositor,
592 struct wl_surface *surface, uint32_t name,
593 uint32_t width, uint32_t height, uint32_t stride)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400594{
595 struct egl_compositor *ec = (struct egl_compositor *) compositor;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500596 struct egl_surface *es;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400597
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500598 es = wl_surface_get_data(surface);
599 if (es == NULL)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400600 return;
601
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500602 if (es->surface != EGL_NO_SURFACE)
603 eglDestroySurface(ec->display, es->surface);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400604
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500605 es->surface = eglCreateSurfaceForName(ec->display, ec->config,
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500606 name, width, height, stride, NULL);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400607
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500608 glBindTexture(GL_TEXTURE_2D, es->texture);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400609 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
610 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT);
611 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
612 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500613 eglBindTexImage(ec->display, es->surface, GL_TEXTURE_2D);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400614
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400615 schedule_repaint(ec);
Kristian Høgsbergf9212892008-10-11 18:40:23 -0400616}
617
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500618static void
619notify_surface_map(struct wl_compositor *compositor,
620 struct wl_surface *surface, struct wl_map *map)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400621{
622 struct egl_compositor *ec = (struct egl_compositor *) compositor;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500623 struct egl_surface *es;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400624
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500625 es = wl_surface_get_data(surface);
626 if (es == NULL)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400627 return;
628
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500629 es->map = *map;
Kristian Høgsberg5ebb3172008-10-11 19:21:35 -0400630
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400631 schedule_repaint(ec);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400632}
633
Kristian Høgsberg7f77bd82008-11-07 08:39:37 -0500634static void
635notify_surface_copy(struct wl_compositor *compositor,
636 struct wl_surface *surface,
637 int32_t dst_x, int32_t dst_y,
638 uint32_t name, uint32_t stride,
639 int32_t x, int32_t y, int32_t width, int32_t height)
640{
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500641 struct egl_compositor *ec = (struct egl_compositor *) compositor;
642 EGLSurface src;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500643 struct egl_surface *es;
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500644
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500645 es = wl_surface_get_data(surface);
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500646
647 /* FIXME: glCopyPixels should work, but then we'll have to
648 * call eglMakeCurrent to set up the src and dest surfaces
649 * first. This seems cheaper, but maybe there's a better way
650 * to accomplish this. */
651
652 src = eglCreateSurfaceForName(ec->display, ec->config,
653 name, x + width, y + height, stride, NULL);
654
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500655 eglCopyNativeBuffers(ec->display, es->surface, GL_FRONT_LEFT, dst_x, dst_y,
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500656 src, GL_FRONT_LEFT, x, y, width, height);
657 schedule_repaint(ec);
Kristian Høgsberg7f77bd82008-11-07 08:39:37 -0500658}
659
660static void
661notify_surface_damage(struct wl_compositor *compositor,
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500662 struct wl_surface *surface,
663 int32_t x, int32_t y, int32_t width, int32_t height)
Kristian Høgsberg7f77bd82008-11-07 08:39:37 -0500664{
665 struct egl_compositor *ec = (struct egl_compositor *) compositor;
666
667 /* FIXME: This need to take a damage region, of course. */
668 schedule_repaint(ec);
669}
670
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500671static void
672notify_pointer_motion(struct wl_compositor *compositor,
673 struct wl_object *source, int x, int y)
674{
675 struct egl_compositor *ec = (struct egl_compositor *) compositor;
Kristian Høgsberg961a04c2008-11-25 22:38:56 -0500676 const int hotspot_x = 16, hotspot_y = 16;
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500677
Kristian Høgsberg961a04c2008-11-25 22:38:56 -0500678 ec->pointer->map.x = x - hotspot_x;
679 ec->pointer->map.y = y - hotspot_y;
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500680 schedule_repaint(ec);
681}
682
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500683static void
684notify_key(struct wl_compositor *compositor,
685 struct wl_object *source, uint32_t key, uint32_t state)
686{
687 struct egl_compositor *ec = (struct egl_compositor *) compositor;
688
Kristian Høgsberg9af92b32008-11-24 01:12:46 -0500689 if (key == KEY_ESC && state == 1) {
690 if (ec->overlay_target == ec->height)
691 ec->overlay_target -= 200;
692 else
693 ec->overlay_target += 200;
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500694 schedule_repaint(ec);
Kristian Høgsberg9af92b32008-11-24 01:12:46 -0500695 }
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500696}
697
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500698static const struct wl_compositor_interface interface = {
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400699 notify_surface_create,
700 notify_surface_destroy,
701 notify_surface_attach,
Kristian Høgsberg7f77bd82008-11-07 08:39:37 -0500702 notify_surface_map,
703 notify_surface_copy,
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500704 notify_surface_damage,
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500705 notify_pointer_motion,
706 notify_key
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400707};
708
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500709static const char pointer_device_file[] =
710 "/dev/input/by-id/usb-Apple__Inc._Apple_Internal_Keyboard_._Trackpad-event-mouse";
711static const char keyboard_device_file[] =
712 "/dev/input/by-id/usb-Apple__Inc._Apple_Internal_Keyboard_._Trackpad-event-kbd";
713
714static void
715create_input_devices(struct wl_display *display)
716{
717 struct wl_object *obj;
718 const char *path;
719
720 path = getenv("WAYLAND_POINTER");
721 if (path == NULL)
722 path = pointer_device_file;
723
724 obj = wl_input_device_create(display, path);
725 if (obj != NULL)
726 wl_display_add_object(display, obj);
727
728 path = getenv("WAYLAND_KEYBOARD");
729 if (path == NULL)
730 path = keyboard_device_file;
731
732 obj = wl_input_device_create(display, path);
733 if (obj != NULL)
734 wl_display_add_object(display, obj);
735}
736
Kristian Høgsbergbf9541f2008-11-25 12:10:09 -0500737static uint32_t
738create_frontbuffer(int fd, int *width, int *height, int *stride)
739{
740 drmModeConnector *connector;
741 drmModeRes *resources;
742 drmModeEncoder *encoder;
743 struct drm_mode_modeinfo *mode;
744 struct drm_i915_gem_create create;
745 struct drm_i915_gem_pin pin;
746 struct drm_gem_flink flink;
747 unsigned int fb_id;
748 int i, ret;
749
750 resources = drmModeGetResources(fd);
751 if (!resources) {
752 fprintf(stderr, "drmModeGetResources failed\n");
753 return 0;
754 }
755
756 for (i = 0; i < resources->count_connectors; i++) {
757 connector = drmModeGetConnector(fd, resources->connectors[i]);
758 if (connector == NULL)
759 continue;
760
761 if (connector->connection == DRM_MODE_CONNECTED &&
762 connector->count_modes > 0)
763 break;
764
765 drmModeFreeConnector(connector);
766 }
767
768 if (i == resources->count_connectors) {
769 fprintf(stderr, "No currently active connector found.\n");
770 return -1;
771 }
772
773 mode = &connector->modes[0];
774
775 for (i = 0; i < resources->count_encoders; i++) {
776 encoder = drmModeGetEncoder(fd, resources->encoders[i]);
777
778 if (encoder == NULL)
779 continue;
780
781 if (encoder->encoder_id == connector->encoder_id)
782 break;
783
784 drmModeFreeEncoder(encoder);
785 }
786
787 /* Mode size at 32 bpp */
788 create.size = mode->hdisplay * mode->vdisplay * 4;
789 if (ioctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create) != 0) {
790 fprintf(stderr, "gem create failed: %m\n");
791 return 0;
792 }
793
794 pin.handle = create.handle;
795 pin.alignment = 4096;
796 if (ioctl(fd, DRM_IOCTL_I915_GEM_PIN, &pin)) {
797 fprintf(stderr, "failed to pin buffer: %m\n");
798 return 0;
799 }
800
801 ret = drmModeAddFB(fd, mode->hdisplay, mode->vdisplay,
802 32, 32, mode->hdisplay * 4, create.handle, &fb_id);
803 if (ret) {
804 fprintf(stderr, "failed to add fb: %m\n");
805 return 0;
806 }
807
808 ret = drmModeSetCrtc(fd, encoder->crtc_id, fb_id, 0, 0,
809 &connector->connector_id, 1, mode);
810 if (ret) {
811 fprintf(stderr, "failed to set mode: %m\n");
812 return 0;
813 }
814
815 flink.handle = create.handle;
816 if (ioctl(fd, DRM_IOCTL_GEM_FLINK, &flink) != 0) {
817 fprintf(stderr, "gem flink failed: %m\n");
818 return 0;
819 }
820
821 *width = mode->hdisplay;
822 *height = mode->vdisplay;
823 *stride = mode->hdisplay * 4;
824
825 return flink.name;
826}
827
Kristian Høgsberg443853c2008-11-25 12:12:05 -0500828static int
829pick_config(struct egl_compositor *ec)
830{
831 EGLConfig configs[100];
832 EGLint value, count;
833 int i;
834
835 if (!eglGetConfigs(ec->display, configs, ARRAY_LENGTH(configs), &count)) {
836 fprintf(stderr, "failed to get configs\n");
837 return -1;
838 }
839
840 ec->config = EGL_NO_CONFIG;
841 for (i = 0; i < count; i++) {
842 eglGetConfigAttrib(ec->display,
843 configs[i],
844 EGL_DEPTH_SIZE,
845 &value);
846 if (value > 0) {
847 fprintf(stderr, "config %d has depth size %d\n", i, value);
848 continue;
849 }
850
851 eglGetConfigAttrib(ec->display,
852 configs[i],
853 EGL_STENCIL_SIZE,
854 &value);
855 if (value > 0) {
856 fprintf(stderr, "config %d has stencil size %d\n", i, value);
857 continue;
858 }
859
860 eglGetConfigAttrib(ec->display,
861 configs[i],
862 EGL_CONFIG_CAVEAT,
863 &value);
864 if (value != EGL_NONE) {
865 fprintf(stderr, "config %d has caveat %d\n", i, value);
866 continue;
867 }
868
869 ec->config = configs[i];
870 break;
871 }
872
873 if (ec->config == EGL_NO_CONFIG) {
874 fprintf(stderr, "found no config without depth or stencil buffers\n");
875 return -1;
876 }
877
878 return 0;
879}
Kristian Høgsbergbf9541f2008-11-25 12:10:09 -0500880
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400881static const char gem_device[] = "/dev/dri/card0";
882
Kristian Høgsbergb7a01922008-11-08 15:39:41 -0500883WL_EXPORT struct wl_compositor *
Kristian Høgsbergf9212892008-10-11 18:40:23 -0400884wl_compositor_create(struct wl_display *display)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400885{
Kristian Høgsberg443853c2008-11-25 12:12:05 -0500886 EGLint major, minor;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400887 struct egl_compositor *ec;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500888 const char *filename;
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -0500889 struct screenshooter *shooter;
Kristian Høgsbergbf9541f2008-11-25 12:10:09 -0500890 uint32_t fb_name;
891 int stride;
892 const static EGLint attribs[] =
893 { EGL_RENDER_BUFFER, EGL_BACK_BUFFER, EGL_NONE };
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400894
895 ec = malloc(sizeof *ec);
896 if (ec == NULL)
897 return NULL;
898
899 ec->base.interface = &interface;
Kristian Høgsbergf9212892008-10-11 18:40:23 -0400900 ec->wl_display = display;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400901
Kristian Høgsbergc508d932008-10-13 22:52:42 -0400902 ec->display = eglCreateDisplayNative(gem_device, "i965");
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400903 if (ec->display == NULL) {
904 fprintf(stderr, "failed to create display\n");
905 return NULL;
906 }
907
908 if (!eglInitialize(ec->display, &major, &minor)) {
909 fprintf(stderr, "failed to initialize display\n");
910 return NULL;
911 }
912
Kristian Høgsberg443853c2008-11-25 12:12:05 -0500913 if (pick_config(ec))
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400914 return NULL;
Kristian Høgsbergbf9541f2008-11-25 12:10:09 -0500915
Kristian Høgsbergbf9541f2008-11-25 12:10:09 -0500916 fb_name = create_frontbuffer(eglGetDisplayFD(ec->display),
917 &ec->width, &ec->height, &stride);
918 ec->surface = eglCreateSurfaceForName(ec->display, ec->config,
919 fb_name, ec->width, ec->height, stride, attribs);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400920 if (ec->surface == NULL) {
921 fprintf(stderr, "failed to create surface\n");
922 return NULL;
923 }
924
Kristian Høgsberg2d9cd1e2008-11-03 08:09:34 -0500925 ec->context = eglCreateContext(ec->display, ec->config, NULL, NULL);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400926 if (ec->context == NULL) {
927 fprintf(stderr, "failed to create context\n");
928 return NULL;
929 }
930
931 if (!eglMakeCurrent(ec->display, ec->surface, ec->surface, ec->context)) {
932 fprintf(stderr, "failed to make context current\n");
933 return NULL;
934 }
935
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500936 glViewport(0, 0, ec->width, ec->height);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400937 glMatrixMode(GL_PROJECTION);
938 glLoadIdentity();
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500939 glOrtho(0, ec->width, ec->height, 0, 0, 1000.0);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400940 glMatrixMode(GL_MODELVIEW);
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500941
942 create_input_devices(display);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400943
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500944 filename = getenv("WAYLAND_BACKGROUND");
945 if (filename == NULL)
946 filename = "background.jpg";
947 ec->background = background_create(filename, 1280, 800);
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500948 ec->pointer = pointer_create(100, 100, 64, 64);
Kristian Høgsberg9af92b32008-11-24 01:12:46 -0500949 ec->overlay = overlay_create(0, ec->height, ec->width, 200);
950 ec->overlay_target = ec->height;
951 ec->overlay_previous = ec->height;
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500952
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400953 ec->gem_fd = open(gem_device, O_RDWR);
954 if (ec->gem_fd < 0) {
955 fprintf(stderr, "failed to open drm device\n");
956 return NULL;
957 }
958
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -0500959 shooter = screenshooter_create(ec);
960 wl_display_add_object(display, &shooter->base);
961 wl_display_add_global(display, &shooter->base);
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500962
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400963 schedule_repaint(ec);
964
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400965 return &ec->base;
966}