2 * Copyright © 2011 Benjamin Franzke
3 * Copyright © 2011 Intel Corporation
5 * Permission to use, copy, modify, distribute, and sell this software and
6 * its documentation for any purpose is hereby granted without fee, provided
7 * that the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation, and that the name of the copyright holders not be used in
10 * advertising or publicity pertaining to distribution of the software
11 * without specific, written prior permission. The copyright holders make
12 * no representations about the suitability of this software for any
13 * purpose. It is provided "as is" without express or implied warranty.
15 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
16 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
19 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
20 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
33 #include <WF/wfdext.h>
37 #include "compositor.h"
40 struct wfd_compositor {
41 struct wlsc_compositor base;
44 struct gbm_device *gbm;
49 struct wl_event_source *wfd_source;
54 uint32_t used_pipelines;
58 struct wlsc_mode base;
63 struct wlsc_output base;
89 wfd_output_prepare_render(struct wlsc_output *output_base)
91 struct wfd_output *output = (struct wfd_output *) output_base;
93 glFramebufferRenderbuffer(GL_FRAMEBUFFER,
96 output->rbo[output->current]);
98 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
105 wfd_output_present(struct wlsc_output *output_base)
107 struct wfd_output *output = (struct wfd_output *) output_base;
108 struct wfd_compositor *c =
109 (struct wfd_compositor *) output->base.compositor;
111 if (wfd_output_prepare_render(&output->base))
115 output->current ^= 1;
117 wfdBindSourceToPipeline(c->dev, output->pipeline,
118 output->source[output->current ^ 1],
119 WFD_TRANSITION_AT_VSYNC, NULL);
121 wfdDeviceCommit(c->dev, WFD_COMMIT_PIPELINE, output->pipeline);
127 init_egl(struct wfd_compositor *ec)
130 const char *extensions;
132 static const EGLint context_attribs[] = {
133 EGL_CONTEXT_CLIENT_VERSION, 2,
137 fd = wfdGetDeviceAttribi(ec->dev, WFD_DEVICE_ID);
142 ec->gbm = gbm_create_device(ec->wfd_fd);
143 ec->base.display = eglGetDisplay(ec->gbm);
144 if (ec->base.display == NULL) {
145 fprintf(stderr, "failed to create display\n");
149 if (!eglInitialize(ec->base.display, &major, &minor)) {
150 fprintf(stderr, "failed to initialize display\n");
154 extensions = eglQueryString(ec->base.display, EGL_EXTENSIONS);
155 if (!strstr(extensions, "EGL_KHR_surfaceless_gles2")) {
156 fprintf(stderr, "EGL_KHR_surfaceless_gles2 not available\n");
160 if (!eglBindAPI(EGL_OPENGL_ES_API)) {
161 fprintf(stderr, "failed to bind api EGL_OPENGL_ES_API\n");
165 ec->base.context = eglCreateContext(ec->base.display, NULL,
166 EGL_NO_CONTEXT, context_attribs);
167 if (ec->base.context == NULL) {
168 fprintf(stderr, "failed to create context\n");
172 if (!eglMakeCurrent(ec->base.display, EGL_NO_SURFACE,
173 EGL_NO_SURFACE, ec->base.context)) {
174 fprintf(stderr, "failed to make context current\n");
182 wfd_output_prepare_scanout_surface(struct wlsc_output *output_base,
183 struct wlsc_surface *es)
189 wfd_output_set_cursor(struct wlsc_output *output_base,
190 struct wlsc_input_device *input)
196 wfd_output_destroy(struct wlsc_output *output_base)
198 struct wfd_output *output = (struct wfd_output *) output_base;
199 struct wfd_compositor *ec =
200 (struct wfd_compositor *) output->base.compositor;
203 glFramebufferRenderbuffer(GL_FRAMEBUFFER,
204 GL_COLOR_ATTACHMENT0,
208 glBindRenderbuffer(GL_RENDERBUFFER, 0);
209 glDeleteRenderbuffers(2, output->rbo);
211 for (i = 0; i < 2; i++) {
212 ec->base.destroy_image(ec->base.display, output->image[i]);
213 gbm_bo_destroy(output->bo[i]);
214 wfdDestroySource(ec->dev, output->source[i]);
217 ec->used_pipelines &= ~(1 << output->pipeline_id);
218 wfdDestroyPipeline(ec->dev, output->pipeline);
219 wfdDestroyPort(ec->dev, output->port);
221 wlsc_output_destroy(&output->base);
222 wl_list_remove(&output->base.link);
228 wfd_output_add_mode(struct wfd_output *output, WFDPortMode mode)
230 struct wfd_compositor *ec =
231 (struct wfd_compositor *) output->base.compositor;
232 struct wfd_mode *wmode;
234 wmode = malloc(sizeof *wmode);
238 wmode->base.flags = 0;
239 wmode->base.width = wfdGetPortModeAttribi(ec->dev, output->port, mode,
240 WFD_PORT_MODE_WIDTH);
241 wmode->base.height = wfdGetPortModeAttribi(ec->dev, output->port, mode,
242 WFD_PORT_MODE_HEIGHT);
243 wmode->base.refresh = wfdGetPortModeAttribi(ec->dev, output->port, mode,
244 WFD_PORT_MODE_REFRESH_RATE);
246 wl_list_insert(output->base.mode_list.prev, &wmode->base.link);
252 create_output_for_port(struct wfd_compositor *ec,
256 struct wfd_output *output;
258 WFDint num_pipelines, *pipelines;
260 union wfd_geometry geometry;
261 struct wfd_mode *mode;
263 WFDfloat physical_size[2];
265 output = malloc(sizeof *output);
269 memset(output, 0, sizeof *output);
270 memset(&geometry, 0, sizeof geometry);
273 wl_list_init(&output->base.mode_list);
275 wfdSetPortAttribi(ec->dev, output->port,
276 WFD_PORT_POWER_MODE, WFD_POWER_MODE_ON);
278 num_modes = wfdGetPortModes(ec->dev, output->port, NULL, 0);
280 fprintf(stderr, "failed to get port mode\n");
284 modes = malloc(sizeof(WFDPortMode) * num_modes);
288 output->base.compositor = &ec->base;
289 num_modes = wfdGetPortModes(ec->dev, output->port, modes, num_modes);
290 for (i = 0; i < num_modes; ++i)
291 wfd_output_add_mode(output, modes[i]);
295 wfdGetPortAttribiv(ec->dev, output->port,
296 WFD_PORT_NATIVE_RESOLUTION,
297 2, &geometry.array[2]);
299 output->base.current = NULL;
300 wl_list_for_each(mode, &output->base.mode_list, base.link) {
301 if (mode->base.width == geometry.g.width &&
302 mode->base.height == geometry.g.height) {
303 output->base.current = &mode->base;
307 if (output->base.current == NULL) {
308 fprintf(stderr, "failed to find a native mode\n");
312 mode = (struct wfd_mode *) output->base.current;
313 mode->base.flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
315 wfdSetPortMode(ec->dev, output->port, mode->mode);
317 wfdEnumeratePipelines(ec->dev, NULL, 0, NULL);
319 num_pipelines = wfdGetPortAttribi(ec->dev, output->port,
320 WFD_PORT_PIPELINE_ID_COUNT);
321 if (num_pipelines < 1) {
322 fprintf(stderr, "failed to get a bindable pipeline\n");
325 pipelines = calloc(num_pipelines, sizeof *pipelines);
326 if (pipelines == NULL)
329 wfdGetPortAttribiv(ec->dev, output->port,
330 WFD_PORT_BINDABLE_PIPELINE_IDS,
331 num_pipelines, pipelines);
333 output->pipeline_id = WFD_INVALID_PIPELINE_ID;
334 for (i = 0; i < num_pipelines; ++i) {
335 if (!(ec->used_pipelines & (1 << pipelines[i]))) {
336 output->pipeline_id = pipelines[i];
340 if (output->pipeline_id == WFD_INVALID_PIPELINE_ID) {
341 fprintf(stderr, "no pipeline found for port: %d\n", port);
342 goto cleanup_pipelines;
345 ec->used_pipelines |= (1 << output->pipeline_id);
347 wfdGetPortAttribfv(ec->dev, output->port,
348 WFD_PORT_PHYSICAL_SIZE,
351 wlsc_output_init(&output->base, &ec->base, x, y,
352 physical_size[0], physical_size[1], 0);
354 output->pipeline = wfdCreatePipeline(ec->dev, output->pipeline_id, NULL);
355 if (output->pipeline == WFD_INVALID_HANDLE) {
356 fprintf(stderr, "failed to create a pipeline\n");
357 goto cleanup_wlsc_output;
360 glGenRenderbuffers(2, output->rbo);
361 for (i = 0; i < 2; i++) {
362 glBindRenderbuffer(GL_RENDERBUFFER, output->rbo[i]);
365 gbm_bo_create(ec->gbm,
366 output->base.current->width,
367 output->base.current->height,
368 GBM_BO_FORMAT_XRGB8888,
369 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
370 output->image[i] = ec->base.create_image(ec->base.display,
372 EGL_NATIVE_PIXMAP_KHR,
373 output->bo[i], NULL);
375 printf("output->image[i]: %p\n", output->image[i]);
376 ec->base.image_target_renderbuffer_storage(GL_RENDERBUFFER,
379 wfdCreateSourceFromImage(ec->dev, output->pipeline,
380 output->image[i], NULL);
382 if (output->source[i] == WFD_INVALID_HANDLE) {
383 fprintf(stderr, "failed to create source\n");
384 goto cleanup_pipeline;
389 glFramebufferRenderbuffer(GL_FRAMEBUFFER,
390 GL_COLOR_ATTACHMENT0,
392 output->rbo[output->current]);
394 wfdSetPipelineAttribiv(ec->dev, output->pipeline,
395 WFD_PIPELINE_SOURCE_RECTANGLE,
397 wfdSetPipelineAttribiv(ec->dev, output->pipeline,
398 WFD_PIPELINE_DESTINATION_RECTANGLE,
401 wfdBindSourceToPipeline(ec->dev, output->pipeline,
402 output->source[output->current ^ 1],
403 WFD_TRANSITION_AT_VSYNC, NULL);
405 wfdBindPipelineToPort(ec->dev, output->port, output->pipeline);
407 wfdDeviceCommit(ec->dev, WFD_COMMIT_ENTIRE_DEVICE, WFD_INVALID_HANDLE);
409 output->base.prepare_render = wfd_output_prepare_render;
410 output->base.present = wfd_output_present;
411 output->base.prepare_scanout_surface =
412 wfd_output_prepare_scanout_surface;
413 output->base.set_hardware_cursor = wfd_output_set_cursor;
414 output->base.destroy = wfd_output_destroy;
416 wl_list_insert(ec->base.output_list.prev, &output->base.link);
421 wfdDestroyPipeline(ec->dev, output->pipeline);
423 wlsc_output_destroy(&output->base);
427 wfdDestroyPort(ec->dev, output->port);
434 create_outputs(struct wfd_compositor *ec, int option_connector)
437 WFDint i, num, *ports;
438 WFDPort port = WFD_INVALID_HANDLE;
440 num = wfdEnumeratePorts(ec->dev, NULL, 0, NULL);
441 ports = calloc(num, sizeof *ports);
445 num = wfdEnumeratePorts(ec->dev, ports, num, NULL);
449 for (i = 0; i < num; ++i) {
450 port = wfdCreatePort(ec->dev, ports[i], NULL);
451 if (port == WFD_INVALID_HANDLE)
454 if (wfdGetPortAttribi(ec->dev, port, WFD_PORT_ATTACHED) &&
455 (option_connector == 0 || ports[i] == option_connector)) {
456 create_output_for_port(ec, port, x, y);
458 x += container_of(ec->base.output_list.prev,
460 link)->current->width;
462 wfdDestroyPort(ec->dev, port);
472 handle_port_state_change(struct wfd_compositor *ec)
474 struct wfd_output *output, *next;
475 WFDint output_port_id;
477 int x_offset = 0, y_offset = 0;
482 port_id = wfdGetEventAttribi(ec->dev, ec->event,
483 WFD_EVENT_PORT_ATTACH_PORT_ID);
484 state = wfdGetEventAttribi(ec->dev, ec->event,
485 WFD_EVENT_PORT_ATTACH_STATE);
488 struct wlsc_output *last_output =
489 container_of(ec->base.output_list.prev,
490 struct wlsc_output, link);
492 /* XXX: not yet needed, we die with 0 outputs */
493 if (!wl_list_empty(&ec->base.output_list))
495 last_output->current->width;
500 port = wfdCreatePort(ec->dev, port_id, NULL);
501 if (port == WFD_INVALID_HANDLE)
504 create_output_for_port(ec, port, x, y);
509 wl_list_for_each_safe(output, next, &ec->base.output_list, base.link) {
511 wfdGetPortAttribi(ec->dev, output->port, WFD_PORT_ID);
513 if (!state && output_port_id == port_id) {
514 x_offset += output->base.current->width;
515 wfd_output_destroy(&output->base);
519 if (x_offset != 0 || y_offset != 0) {
520 wlsc_output_move(&output->base,
521 output->base.x - x_offset,
522 output->base.y - y_offset);
526 if (ec->used_pipelines == 0)
527 wl_display_terminate(ec->base.wl_display);
533 on_wfd_event(int fd, uint32_t mask, void *data)
535 struct wfd_compositor *c = data;
536 struct wfd_output *output = NULL, *output_iter;
538 const WFDtime timeout = 0;
542 type = wfdDeviceEventWait(c->dev, c->event, timeout);
545 case WFD_EVENT_PIPELINE_BIND_SOURCE_COMPLETE:
547 wfdGetEventAttribi(c->dev, c->event,
548 WFD_EVENT_PIPELINE_BIND_PIPELINE_ID);
551 wfdGetEventAttribi(c->dev, c->event,
552 WFD_EVENT_PIPELINE_BIND_TIME_EXT);
554 wl_list_for_each(output_iter, &c->base.output_list, base.link) {
555 if (output_iter->pipeline_id == pipeline_id)
556 output = output_iter;
562 wlsc_output_finish_frame(&output->base,
563 c->start_time + bind_time);
565 case WFD_EVENT_PORT_ATTACH_DETACH:
566 handle_port_state_change(c);
576 wfd_destroy(struct wlsc_compositor *ec)
578 struct wfd_compositor *d = (struct wfd_compositor *) ec;
580 wlsc_compositor_shutdown(ec);
584 wfdDestroyDevice(d->dev);
591 /* FIXME: Just add a stub here for now
592 * handle drm{Set,Drop}Master in owfdrm somehow */
594 vt_func(struct wlsc_compositor *compositor, int event)
599 static const char default_seat[] = "seat0";
601 static struct wlsc_compositor *
602 wfd_compositor_create(struct wl_display *display,
603 int connector, const char *seat, int tty)
605 struct wfd_compositor *ec;
606 struct wl_event_loop *loop;
609 ec = malloc(sizeof *ec);
613 memset(ec, 0, sizeof *ec);
615 gettimeofday(&tv, NULL);
616 ec->start_time = tv.tv_sec * 1000 + tv.tv_usec / 1000;
618 ec->udev = udev_new();
619 if (ec->udev == NULL) {
620 fprintf(stderr, "failed to initialize udev context\n");
624 ec->dev = wfdCreateDevice(WFD_DEFAULT_DEVICE_ID, NULL);
625 if (ec->dev == WFD_INVALID_HANDLE) {
626 fprintf(stderr, "failed to create wfd device\n");
630 ec->event = wfdCreateEvent(ec->dev, NULL);
631 if (ec->event == WFD_INVALID_HANDLE) {
632 fprintf(stderr, "failed to create wfd event\n");
636 ec->base.wl_display = display;
637 if (init_egl(ec) < 0) {
638 fprintf(stderr, "failed to initialize egl\n");
642 ec->base.destroy = wfd_destroy;
645 glGenFramebuffers(1, &ec->base.fbo);
646 glBindFramebuffer(GL_FRAMEBUFFER, ec->base.fbo);
648 /* Can't init base class until we have a current egl context */
649 if (wlsc_compositor_init(&ec->base, display) < 0)
652 if (create_outputs(ec, connector) < 0) {
653 fprintf(stderr, "failed to create outputs\n");
657 evdev_input_create(&ec->base, ec->udev, seat);
659 loop = wl_display_get_event_loop(ec->base.wl_display);
661 wl_event_loop_add_fd(loop,
662 wfdDeviceEventGetFD(ec->dev, ec->event),
663 WL_EVENT_READABLE, on_wfd_event, ec);
664 ec->tty = tty_create(&ec->base, vt_func, tty);
669 struct wlsc_compositor *
670 backend_init(struct wl_display *display, char *options);
672 WL_EXPORT struct wlsc_compositor *
673 backend_init(struct wl_display *display, char *options)
675 int connector = 0, i;
680 static char * const tokens[] = { "connector", "seat", "tty", NULL };
684 while (i = getsubopt(&p, tokens, &value), i != -1) {
687 connector = strtol(value, NULL, 0);
693 tty = strtol(value, NULL, 0);
698 return wfd_compositor_create(display, connector, seat, tty);