blob: 0cf77f699752024cd0bd6984b000e656eab6c966 [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øgsberg9af92b32008-11-24 01:12:46 -050040 int32_t 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øgsbergef7a9ca2008-10-11 21:21:39 -0400477repaint(void *data)
478{
479 struct egl_compositor *ec = data;
480 struct wl_surface_iterator *iterator;
481 struct wl_surface *surface;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500482 struct egl_surface *es;
Kristian Høgsberg9af92b32008-11-24 01:12:46 -0500483 double force;
484 int32_t y;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500485
486 draw_surface(ec->background);
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400487
488 iterator = wl_surface_iterator_create(ec->wl_display, 0);
489 while (wl_surface_iterator_next(iterator, &surface)) {
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500490 es = wl_surface_get_data(surface);
491 if (es == NULL)
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400492 continue;
493
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500494 draw_surface(es);
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400495 }
496 wl_surface_iterator_destroy(iterator);
497
Kristian Høgsberg54879822008-11-23 17:07:32 -0500498 draw_surface(ec->overlay);
499
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500500 draw_surface(ec->pointer);
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500501
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400502 eglSwapBuffers(ec->display, ec->surface);
Kristian Høgsberg9af92b32008-11-24 01:12:46 -0500503
504 y = ec->overlay->map.y;
505 force = (ec->overlay_target - ec->overlay->map.y) / 25.0 +
506 (ec->overlay_previous - y) / 25.0;
507
508 ec->overlay->map.y = y + (y - ec->overlay_previous) + force;
509 ec->overlay_previous = y;
510
511 if (ec->overlay->map.y >= 800) {
512 ec->overlay->map.y = 800;
513 ec->overlay_previous = 800;
514 }
515
516 if (ec->overlay->map.y <= 600) {
517 ec->overlay->map.y = 600;
518 ec->overlay_previous = 600;
519 }
520
521 if (ec->overlay->map.y != y)
522 schedule_repaint(ec);
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400523}
524
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500525static void
526schedule_repaint(struct egl_compositor *ec)
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400527{
528 struct wl_event_loop *loop;
529
530 loop = wl_display_get_event_loop(ec->wl_display);
531 wl_event_loop_add_idle(loop, repaint, ec);
532}
533
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500534static void
535notify_surface_create(struct wl_compositor *compositor,
536 struct wl_surface *surface)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400537{
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500538 struct egl_surface *es;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400539
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500540 es = malloc(sizeof *es);
541 if (es == NULL)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400542 return;
543
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500544 es->surface = EGL_NO_SURFACE;
545 wl_surface_set_data(surface, es);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400546
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500547 glGenTextures(1, &es->texture);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400548}
549
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500550static void
551notify_surface_destroy(struct wl_compositor *compositor,
552 struct wl_surface *surface)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400553{
554 struct egl_compositor *ec = (struct egl_compositor *) compositor;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500555 struct egl_surface *es;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400556
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500557 es = wl_surface_get_data(surface);
558 if (es == NULL)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400559 return;
Kristian Høgsberg2d9cd1e2008-11-03 08:09:34 -0500560
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500561 egl_surface_destroy(es, ec);
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400562
563 schedule_repaint(ec);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400564}
565
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500566static void
567notify_surface_attach(struct wl_compositor *compositor,
568 struct wl_surface *surface, uint32_t name,
569 uint32_t width, uint32_t height, uint32_t stride)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400570{
571 struct egl_compositor *ec = (struct egl_compositor *) compositor;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500572 struct egl_surface *es;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400573
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500574 es = wl_surface_get_data(surface);
575 if (es == NULL)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400576 return;
577
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500578 if (es->surface != EGL_NO_SURFACE)
579 eglDestroySurface(ec->display, es->surface);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400580
Kristian Høgsberg56f3c712008-11-05 07:55:45 -0500581 /* FIXME: We need to use a single buffer config without depth
582 * or stencil buffers here to keep egl from creating auxillary
583 * buffers for the pixmap here. */
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500584 es->surface = eglCreateSurfaceForName(ec->display, ec->config,
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500585 name, width, height, stride, NULL);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400586
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500587 glBindTexture(GL_TEXTURE_2D, es->texture);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400588 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
589 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT);
590 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
591 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500592 eglBindTexImage(ec->display, es->surface, GL_TEXTURE_2D);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400593
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400594 schedule_repaint(ec);
Kristian Høgsbergf9212892008-10-11 18:40:23 -0400595}
596
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500597static void
598notify_surface_map(struct wl_compositor *compositor,
599 struct wl_surface *surface, struct wl_map *map)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400600{
601 struct egl_compositor *ec = (struct egl_compositor *) compositor;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500602 struct egl_surface *es;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400603
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500604 es = wl_surface_get_data(surface);
605 if (es == NULL)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400606 return;
607
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500608 es->map = *map;
Kristian Høgsberg5ebb3172008-10-11 19:21:35 -0400609
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400610 schedule_repaint(ec);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400611}
612
Kristian Høgsberg7f77bd82008-11-07 08:39:37 -0500613static void
614notify_surface_copy(struct wl_compositor *compositor,
615 struct wl_surface *surface,
616 int32_t dst_x, int32_t dst_y,
617 uint32_t name, uint32_t stride,
618 int32_t x, int32_t y, int32_t width, int32_t height)
619{
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500620 struct egl_compositor *ec = (struct egl_compositor *) compositor;
621 EGLSurface src;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500622 struct egl_surface *es;
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500623
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500624 es = wl_surface_get_data(surface);
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500625
626 /* FIXME: glCopyPixels should work, but then we'll have to
627 * call eglMakeCurrent to set up the src and dest surfaces
628 * first. This seems cheaper, but maybe there's a better way
629 * to accomplish this. */
630
631 src = eglCreateSurfaceForName(ec->display, ec->config,
632 name, x + width, y + height, stride, NULL);
633
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500634 eglCopyNativeBuffers(ec->display, es->surface, GL_FRONT_LEFT, dst_x, dst_y,
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500635 src, GL_FRONT_LEFT, x, y, width, height);
636 schedule_repaint(ec);
Kristian Høgsberg7f77bd82008-11-07 08:39:37 -0500637}
638
639static void
640notify_surface_damage(struct wl_compositor *compositor,
Kristian Høgsberg78231c82008-11-08 15:06:01 -0500641 struct wl_surface *surface,
642 int32_t x, int32_t y, int32_t width, int32_t height)
Kristian Høgsberg7f77bd82008-11-07 08:39:37 -0500643{
644 struct egl_compositor *ec = (struct egl_compositor *) compositor;
645
646 /* FIXME: This need to take a damage region, of course. */
647 schedule_repaint(ec);
648}
649
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500650static void
651notify_pointer_motion(struct wl_compositor *compositor,
652 struct wl_object *source, int x, int y)
653{
654 struct egl_compositor *ec = (struct egl_compositor *) compositor;
655
656 ec->pointer->map.x = x;
657 ec->pointer->map.y = y;
658 schedule_repaint(ec);
659}
660
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500661static void
662notify_key(struct wl_compositor *compositor,
663 struct wl_object *source, uint32_t key, uint32_t state)
664{
665 struct egl_compositor *ec = (struct egl_compositor *) compositor;
666
Kristian Høgsberg9af92b32008-11-24 01:12:46 -0500667 if (key == KEY_ESC && state == 1) {
668 if (ec->overlay_target == ec->height)
669 ec->overlay_target -= 200;
670 else
671 ec->overlay_target += 200;
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500672 schedule_repaint(ec);
Kristian Høgsberg9af92b32008-11-24 01:12:46 -0500673 }
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500674}
675
Kristian Høgsberg5503bf82008-11-06 10:38:17 -0500676static const struct wl_compositor_interface interface = {
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400677 notify_surface_create,
678 notify_surface_destroy,
679 notify_surface_attach,
Kristian Høgsberg7f77bd82008-11-07 08:39:37 -0500680 notify_surface_map,
681 notify_surface_copy,
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500682 notify_surface_damage,
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500683 notify_pointer_motion,
684 notify_key
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400685};
686
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500687static const char pointer_device_file[] =
688 "/dev/input/by-id/usb-Apple__Inc._Apple_Internal_Keyboard_._Trackpad-event-mouse";
689static const char keyboard_device_file[] =
690 "/dev/input/by-id/usb-Apple__Inc._Apple_Internal_Keyboard_._Trackpad-event-kbd";
691
692static void
693create_input_devices(struct wl_display *display)
694{
695 struct wl_object *obj;
696 const char *path;
697
698 path = getenv("WAYLAND_POINTER");
699 if (path == NULL)
700 path = pointer_device_file;
701
702 obj = wl_input_device_create(display, path);
703 if (obj != NULL)
704 wl_display_add_object(display, obj);
705
706 path = getenv("WAYLAND_KEYBOARD");
707 if (path == NULL)
708 path = keyboard_device_file;
709
710 obj = wl_input_device_create(display, path);
711 if (obj != NULL)
712 wl_display_add_object(display, obj);
713}
714
Kristian Høgsbergbf9541f2008-11-25 12:10:09 -0500715static uint32_t
716create_frontbuffer(int fd, int *width, int *height, int *stride)
717{
718 drmModeConnector *connector;
719 drmModeRes *resources;
720 drmModeEncoder *encoder;
721 struct drm_mode_modeinfo *mode;
722 struct drm_i915_gem_create create;
723 struct drm_i915_gem_pin pin;
724 struct drm_gem_flink flink;
725 unsigned int fb_id;
726 int i, ret;
727
728 resources = drmModeGetResources(fd);
729 if (!resources) {
730 fprintf(stderr, "drmModeGetResources failed\n");
731 return 0;
732 }
733
734 for (i = 0; i < resources->count_connectors; i++) {
735 connector = drmModeGetConnector(fd, resources->connectors[i]);
736 if (connector == NULL)
737 continue;
738
739 if (connector->connection == DRM_MODE_CONNECTED &&
740 connector->count_modes > 0)
741 break;
742
743 drmModeFreeConnector(connector);
744 }
745
746 if (i == resources->count_connectors) {
747 fprintf(stderr, "No currently active connector found.\n");
748 return -1;
749 }
750
751 mode = &connector->modes[0];
752
753 for (i = 0; i < resources->count_encoders; i++) {
754 encoder = drmModeGetEncoder(fd, resources->encoders[i]);
755
756 if (encoder == NULL)
757 continue;
758
759 if (encoder->encoder_id == connector->encoder_id)
760 break;
761
762 drmModeFreeEncoder(encoder);
763 }
764
765 /* Mode size at 32 bpp */
766 create.size = mode->hdisplay * mode->vdisplay * 4;
767 if (ioctl(fd, DRM_IOCTL_I915_GEM_CREATE, &create) != 0) {
768 fprintf(stderr, "gem create failed: %m\n");
769 return 0;
770 }
771
772 pin.handle = create.handle;
773 pin.alignment = 4096;
774 if (ioctl(fd, DRM_IOCTL_I915_GEM_PIN, &pin)) {
775 fprintf(stderr, "failed to pin buffer: %m\n");
776 return 0;
777 }
778
779 ret = drmModeAddFB(fd, mode->hdisplay, mode->vdisplay,
780 32, 32, mode->hdisplay * 4, create.handle, &fb_id);
781 if (ret) {
782 fprintf(stderr, "failed to add fb: %m\n");
783 return 0;
784 }
785
786 ret = drmModeSetCrtc(fd, encoder->crtc_id, fb_id, 0, 0,
787 &connector->connector_id, 1, mode);
788 if (ret) {
789 fprintf(stderr, "failed to set mode: %m\n");
790 return 0;
791 }
792
793 flink.handle = create.handle;
794 if (ioctl(fd, DRM_IOCTL_GEM_FLINK, &flink) != 0) {
795 fprintf(stderr, "gem flink failed: %m\n");
796 return 0;
797 }
798
799 *width = mode->hdisplay;
800 *height = mode->vdisplay;
801 *stride = mode->hdisplay * 4;
802
803 return flink.name;
804}
805
806
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400807static const char gem_device[] = "/dev/dri/card0";
808
Kristian Høgsbergb7a01922008-11-08 15:39:41 -0500809WL_EXPORT struct wl_compositor *
Kristian Høgsbergf9212892008-10-11 18:40:23 -0400810wl_compositor_create(struct wl_display *display)
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400811{
812 EGLConfig configs[64];
813 EGLint major, minor, count;
814 struct egl_compositor *ec;
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500815 const char *filename;
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -0500816 struct screenshooter *shooter;
Kristian Høgsbergbf9541f2008-11-25 12:10:09 -0500817 uint32_t fb_name;
818 int stride;
819 const static EGLint attribs[] =
820 { EGL_RENDER_BUFFER, EGL_BACK_BUFFER, EGL_NONE };
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400821
822 ec = malloc(sizeof *ec);
823 if (ec == NULL)
824 return NULL;
825
826 ec->base.interface = &interface;
Kristian Høgsbergf9212892008-10-11 18:40:23 -0400827 ec->wl_display = display;
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400828
Kristian Høgsbergc508d932008-10-13 22:52:42 -0400829 ec->display = eglCreateDisplayNative(gem_device, "i965");
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400830 if (ec->display == NULL) {
831 fprintf(stderr, "failed to create display\n");
832 return NULL;
833 }
834
835 if (!eglInitialize(ec->display, &major, &minor)) {
836 fprintf(stderr, "failed to initialize display\n");
837 return NULL;
838 }
839
840 if (!eglGetConfigs(ec->display, configs, ARRAY_LENGTH(configs), &count)) {
841 fprintf(stderr, "failed to get configs\n");
842 return NULL;
843 }
Kristian Høgsbergbf9541f2008-11-25 12:10:09 -0500844
Kristian Høgsberg2d9cd1e2008-11-03 08:09:34 -0500845 ec->config = configs[24];
Kristian Høgsbergbf9541f2008-11-25 12:10:09 -0500846 fb_name = create_frontbuffer(eglGetDisplayFD(ec->display),
847 &ec->width, &ec->height, &stride);
848 ec->surface = eglCreateSurfaceForName(ec->display, ec->config,
849 fb_name, ec->width, ec->height, stride, attribs);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400850 if (ec->surface == NULL) {
851 fprintf(stderr, "failed to create surface\n");
852 return NULL;
853 }
854
Kristian Høgsberg2d9cd1e2008-11-03 08:09:34 -0500855 ec->context = eglCreateContext(ec->display, ec->config, NULL, NULL);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400856 if (ec->context == NULL) {
857 fprintf(stderr, "failed to create context\n");
858 return NULL;
859 }
860
861 if (!eglMakeCurrent(ec->display, ec->surface, ec->surface, ec->context)) {
862 fprintf(stderr, "failed to make context current\n");
863 return NULL;
864 }
865
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500866 glViewport(0, 0, ec->width, ec->height);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400867 glMatrixMode(GL_PROJECTION);
868 glLoadIdentity();
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500869 glOrtho(0, ec->width, ec->height, 0, 0, 1000.0);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400870 glMatrixMode(GL_MODELVIEW);
Kristian Høgsbergcddc0ad2008-11-24 00:31:49 -0500871
872 create_input_devices(display);
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400873
Kristian Høgsbergaa5b5be2008-11-21 21:31:54 -0500874 filename = getenv("WAYLAND_BACKGROUND");
875 if (filename == NULL)
876 filename = "background.jpg";
877 ec->background = background_create(filename, 1280, 800);
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500878 ec->pointer = pointer_create(100, 100, 64, 64);
Kristian Høgsberg9af92b32008-11-24 01:12:46 -0500879 ec->overlay = overlay_create(0, ec->height, ec->width, 200);
880 ec->overlay_target = ec->height;
881 ec->overlay_previous = ec->height;
Kristian Høgsberg4c9f2c92008-11-21 19:25:44 -0500882
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400883 ec->gem_fd = open(gem_device, O_RDWR);
884 if (ec->gem_fd < 0) {
885 fprintf(stderr, "failed to open drm device\n");
886 return NULL;
887 }
888
Kristian Høgsberg1e4b86a2008-11-23 23:41:08 -0500889 shooter = screenshooter_create(ec);
890 wl_display_add_object(display, &shooter->base);
891 wl_display_add_global(display, &shooter->base);
Kristian Høgsberg8d7ca6b2008-11-09 00:22:51 -0500892
Kristian Høgsbergef7a9ca2008-10-11 21:21:39 -0400893 schedule_repaint(ec);
894
Kristian Høgsberg16eb6752008-10-08 22:51:32 -0400895 return &ec->base;
896}