Run compositor fullscreen, repaint when surfaces come and go.
[profile/ivi/wayland.git] / egl-compositor.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <stdint.h>
5 #include <i915_drm.h>
6 #include <sys/ioctl.h>
7 #include <sys/mman.h>
8 #include <fcntl.h>
9 #include <unistd.h>
10 #include <linux/fb.h>
11
12 #include "wayland.h"
13
14 #include <GL/gl.h>
15 #include <eagle.h>
16
17 #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
18
19 struct egl_compositor {
20         struct wl_compositor base;
21         EGLDisplay display;
22         EGLSurface surface;
23         EGLContext context;
24         struct wl_display *wl_display;
25         int gem_fd;
26 };
27
28 struct surface_data {
29         uint32_t handle;
30         int32_t width, height, stride;
31         GLuint texture;
32         struct wl_map map;
33 };
34
35 static void
36 repaint(void *data)
37 {
38         struct egl_compositor *ec = data;
39         struct wl_surface_iterator *iterator;
40         struct wl_surface *surface;
41         struct surface_data *sd;
42         GLint vertices[12];
43         GLint tex_coords[8] = { 1, 0,  1, 1,  0, 1,  0, 0 };
44         GLuint indices[4] = { 0, 1, 2, 3 };
45                
46         /* This part is where we actually copy the buffer to screen.
47          * Needs to be part of the repaint loop, not called from the
48          * notify_map handler. */
49
50         glClear(GL_COLOR_BUFFER_BIT); 
51
52         iterator = wl_surface_iterator_create(ec->wl_display, 0);
53         while (wl_surface_iterator_next(iterator, &surface)) {
54                 sd = wl_surface_get_data(surface);
55                 if (sd == NULL)
56                         continue;
57
58                 vertices[0] = sd->map.x;
59                 vertices[1] = sd->map.y;
60                 vertices[2] = 0;
61
62                 vertices[3] = sd->map.x;
63                 vertices[4] = sd->map.y + sd->map.height;
64                 vertices[5] = 0;
65
66                 vertices[6] = sd->map.x + sd->map.width;
67                 vertices[7] = sd->map.y + sd->map.height;
68                 vertices[8] = 0;
69
70                 vertices[9] = sd->map.x + sd->map.width;
71                 vertices[10] = sd->map.y;
72                 vertices[11] = 0;
73
74                 glBindTexture(GL_TEXTURE_2D, sd->texture);
75                 glEnable(GL_TEXTURE_2D);
76                 glEnable(GL_BLEND);
77                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
78
79                 glEnableClientState(GL_VERTEX_ARRAY);
80                 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
81                 glVertexPointer(3, GL_INT, 0, vertices);
82                 glTexCoordPointer(2, GL_INT, 0, tex_coords);
83                 glDrawElements(GL_QUADS, 4, GL_UNSIGNED_INT, indices);
84         }
85         wl_surface_iterator_destroy(iterator);
86
87         glFlush();
88
89         eglSwapBuffers(ec->display, ec->surface);
90 }
91
92 static void schedule_repaint(struct egl_compositor *ec)
93 {
94         struct wl_event_loop *loop;
95
96         loop = wl_display_get_event_loop(ec->wl_display);
97         wl_event_loop_add_idle(loop, repaint, ec);
98 }
99
100 void notify_surface_create(struct wl_compositor *compositor,
101                            struct wl_surface *surface)
102 {
103         struct surface_data *sd;
104
105         sd = malloc(sizeof *sd);
106         if (sd == NULL)
107                 return;
108
109         sd->handle = 0;
110         wl_surface_set_data(surface, sd);
111
112         glGenTextures(1, &sd->texture);
113 }
114                                    
115 void notify_surface_destroy(struct wl_compositor *compositor,
116                             struct wl_surface *surface)
117 {
118         struct egl_compositor *ec = (struct egl_compositor *) compositor;
119         struct surface_data *sd;
120         struct drm_gem_close close_arg;
121         int ret;
122
123         sd = wl_surface_get_data(surface);
124         if (sd == NULL || sd->handle == 0)
125                 return;
126         
127         close_arg.handle = sd->handle;
128         ret = ioctl(ec->gem_fd, DRM_IOCTL_GEM_CLOSE, &close_arg);
129         if (ret != 0) {
130                 fprintf(stderr, "failed to gem_close handle %d: %m\n", sd->handle);
131         }
132
133         glDeleteTextures(1, &sd->texture);
134
135         free(sd);
136
137         schedule_repaint(ec);
138 }
139
140 void notify_surface_attach(struct wl_compositor *compositor,
141                            struct wl_surface *surface, uint32_t name, 
142                            uint32_t width, uint32_t height, uint32_t stride)
143 {
144         struct egl_compositor *ec = (struct egl_compositor *) compositor;
145         struct surface_data *sd;
146         struct drm_gem_open open_arg;
147         struct drm_gem_close close_arg;
148         struct drm_i915_gem_pread pread;
149         void *data;
150         uint32_t size;
151         int ret;
152
153         sd = wl_surface_get_data(surface);
154         if (sd == NULL)
155                 return;
156
157         if (sd->handle != 0) {
158                 close_arg.handle = sd->handle;
159                 ret = ioctl(ec->gem_fd, DRM_IOCTL_GEM_CLOSE, &close_arg);
160                 if (ret != 0) {
161                         fprintf(stderr, "failed to gem_close name %d: %m\n", name);
162                 }
163         }
164
165         open_arg.name = name;
166         ret = ioctl(ec->gem_fd, DRM_IOCTL_GEM_OPEN, &open_arg);
167         if (ret != 0) {
168                 fprintf(stderr, "failed to gem_open name %d, fd=%d: %m\n", name, ec->gem_fd);
169                 return;
170         }
171
172         sd->handle = open_arg.handle;
173         sd->width = width;
174         sd->height = height;
175         sd->stride = stride;
176
177         size = sd->height * sd->stride;
178         data = malloc(size);
179         if (data == NULL) {
180                 fprintf(stderr, "swap buffers malloc failed\n");
181                 return;
182         }
183
184         pread.handle = sd->handle;
185         pread.pad = 0;
186         pread.offset = 0;
187         pread.size = size;
188         pread.data_ptr = (long) data;
189
190         if (ioctl(ec->gem_fd, DRM_IOCTL_I915_GEM_PREAD, &pread)) {
191                 fprintf(stderr, "gem pread failed");
192                 return;
193         }
194
195         glBindTexture(GL_TEXTURE_2D, sd->texture);
196         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
197         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT);
198         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
199         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
200         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
201                      GL_BGRA, GL_UNSIGNED_BYTE, data);
202
203         free(data);
204
205         schedule_repaint(ec);
206 }
207
208 void notify_surface_map(struct wl_compositor *compositor,
209                         struct wl_surface *surface, struct wl_map *map)
210 {
211         struct egl_compositor *ec = (struct egl_compositor *) compositor;
212         struct surface_data *sd;
213
214         sd = wl_surface_get_data(surface);
215         if (sd == NULL)
216                 return;
217
218         sd->map = *map;
219
220         schedule_repaint(ec);
221 }
222
223 struct wl_compositor_interface interface = {
224         notify_surface_create,
225         notify_surface_destroy,
226         notify_surface_attach,
227         notify_surface_map
228 };
229
230 static const char gem_device[] = "/dev/dri/card0";
231
232 struct wl_compositor *
233 wl_compositor_create(struct wl_display *display)
234 {
235         EGLConfig configs[64];
236         EGLint major, minor, count;
237         struct egl_compositor *ec;
238         int width, height;
239
240         ec = malloc(sizeof *ec);
241         if (ec == NULL)
242                 return NULL;
243
244         ec->base.interface = &interface;
245         ec->wl_display = display;
246
247         ec->display = eglCreateDisplay(gem_device, "i965");
248         if (ec->display == NULL) {
249                 fprintf(stderr, "failed to create display\n");
250                 return NULL;
251         }
252
253         if (!eglInitialize(ec->display, &major, &minor)) {
254                 fprintf(stderr, "failed to initialize display\n");
255                 return NULL;
256         }
257
258         if (!eglGetConfigs(ec->display, configs, ARRAY_LENGTH(configs), &count)) {
259                 fprintf(stderr, "failed to get configs\n");
260                 return NULL;
261         }
262
263         ec->surface = eglGetFullscreenSurface(ec->display, configs[24], &width, &height);
264         if (ec->surface == NULL) {
265                 fprintf(stderr, "failed to create surface\n");
266                 return NULL;
267         }
268
269         ec->context = eglCreateContext(ec->display, configs[24], NULL, NULL);
270         if (ec->context == NULL) {
271                 fprintf(stderr, "failed to create context\n");
272                 return NULL;
273         }
274
275         if (!eglMakeCurrent(ec->display, ec->surface, ec->surface, ec->context)) {
276                 fprintf(stderr, "failed to make context current\n");
277                 return NULL;
278         }
279
280         glViewport(0, 0, width, height);
281         glMatrixMode(GL_PROJECTION);
282         glLoadIdentity();
283         glOrtho(0, width, height, 0, 0, 1000.0);
284         glMatrixMode(GL_MODELVIEW);
285         glClearColor(0.0, 0.1, 0.3, 0.0);
286
287         ec->gem_fd = open(gem_device, O_RDWR);
288         if (ec->gem_fd < 0) {
289                 fprintf(stderr, "failed to open drm device\n");
290                 return NULL;
291         }
292
293         schedule_repaint(ec);
294
295         return &ec->base;
296 }