#include "e.h" /* local function prototypes */ static void _e_comp_cb_bind(struct wl_client *client, void *data, unsigned int version EINA_UNUSED, unsigned int id); static void _e_comp_cb_bind_manager(struct wl_client *client, void *data EINA_UNUSED, unsigned int version EINA_UNUSED, unsigned int id); static void _e_comp_cb_surface_create(struct wl_client *client, struct wl_resource *resource, unsigned int id); static void _e_comp_cb_surface_destroy(struct wl_resource *resource); static void _e_comp_cb_region_create(struct wl_client *client, struct wl_resource *resource, unsigned int id); static void _e_comp_cb_region_destroy(struct wl_resource *resource); static Eina_Bool _e_comp_cb_read(void *data, Ecore_Fd_Handler *hdl EINA_UNUSED); static Eina_Bool _e_comp_cb_idle(void *data); /* local interfaces */ static const struct wl_compositor_interface _e_compositor_interface = { _e_comp_cb_surface_create, _e_comp_cb_region_create }; static const struct wl_data_device_manager_interface _e_manager_interface = { NULL, // create data source NULL // get data device }; /* local variables */ EAPI E_Compositor *_e_comp = NULL; EINTERN int e_comp_init(void) { E_Module *mod = NULL; const char *modname; /* NB: Basically, this function needs to load the appropriate * compositor module (drm, fb, x11, etc) */ if (getenv("DISPLAY")) modname = "wl_x11"; else modname = "wl_drm"; if (!(mod = e_module_find(modname))) mod = e_module_new(modname); if (mod) { if ((e_module_enable(mod))) return 1; } return 0; } EINTERN int e_comp_shutdown(void) { E_Module *mod = NULL; const char *modname; /* NB: This function needs to unload the compositor module */ if (getenv("DISPLAY")) modname = "wl_x11"; else modname = "wl_drm"; if ((mod = e_module_find(modname))) e_module_disable(mod); return 1; } EAPI Eina_Bool e_compositor_init(E_Compositor *comp, void *display) { E_Plane *p; int fd = 0; if (_e_comp) { ERR("Compositor already exists"); return EINA_FALSE; } /* try to create a wayland display */ if (!(comp->wl.display = wl_display_create())) { ERR("Could not create a wayland display: %m"); return EINA_FALSE; } comp->output_pool = 0; /* initialize signals */ wl_signal_init(&comp->signals.destroy); wl_signal_init(&comp->signals.activate); wl_signal_init(&comp->signals.kill); wl_signal_init(&comp->signals.seat); /* try to add the compositor to the displays global list */ if (!wl_display_add_global(comp->wl.display, &wl_compositor_interface, comp, _e_comp_cb_bind)) { ERR("Could not add compositor to globals: %m"); goto global_err; } /* initialize hardware plane */ e_plane_init(&comp->plane, 0, 0); e_compositor_plane_stack(comp, &comp->plane, NULL); /* TODO: init xkb */ /* initialize the data device manager */ if (!wl_display_add_global(comp->wl.display, &wl_data_device_manager_interface, NULL, _e_comp_cb_bind_manager)) { ERR("Could not add data device manager to globals: %m"); goto global_err; } /* try to initialize the shm mechanism */ if (wl_display_init_shm(comp->wl.display) < 0) ERR("Could not initialize SHM mechanism: %m"); #ifdef HAVE_WAYLAND_EGL /* try to get the egl display * * NB: This is interesting....if we try to eglGetDisplay and pass in the * wayland display, then EGL fails due to XCB not owning the event queue. * If we pass it a NULL, it inits just fine */ comp->egl.display = eglGetDisplay((EGLNativeDisplayType)display); if (comp->egl.display == EGL_NO_DISPLAY) ERR("Could not get EGL display: %m"); else { EGLint major, minor; /* try to initialize EGL */ if (!eglInitialize(comp->egl.display, &major, &minor)) { ERR("Could not initialize EGL: %m"); eglTerminate(comp->egl.display); eglReleaseThread(); } else { EGLint n; EGLint attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, EGL_ALPHA_SIZE, 1, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE }; /* try to find a matching egl config */ if ((!eglChooseConfig(comp->egl.display, attribs, &comp->egl.config, 1, &n) || (n == 0))) { ERR("Could not choose EGL config: %m"); eglTerminate(comp->egl.display); eglReleaseThread(); } } } #endif /* create the input loop */ comp->wl.input_loop = wl_event_loop_create(); /* get the display event loop */ comp->wl.loop = wl_display_get_event_loop(comp->wl.display); /* get the event loop fd */ fd = wl_event_loop_get_fd(comp->wl.loop); /* add the event loop fd to main ecore */ comp->fd_hdlr = ecore_main_fd_handler_add(fd, ECORE_FD_READ, _e_comp_cb_read, comp, NULL, NULL); /* add an idler for flushing clients */ comp->idler = ecore_idle_enterer_add(_e_comp_cb_idle, comp); /* add the socket */ if (wl_display_add_socket(comp->wl.display, NULL)) { ERR("Failed to add socket: %m"); goto sock_err; } wl_event_loop_dispatch(comp->wl.loop, 0); /* set a reference to the compositor */ _e_comp = comp; return EINA_TRUE; sock_err: /* destroy the idler */ if (comp->idler) ecore_idler_del(comp->idler); /* destroy the fd handler */ if (comp->fd_hdlr) ecore_main_fd_handler_del(comp->fd_hdlr); /* destroy the input loop */ if (comp->wl.input_loop) wl_event_loop_destroy(comp->wl.input_loop); #ifdef HAVE_WAYLAND_EGL /* destroy the egl display */ if (comp->egl.display) eglTerminate(comp->egl.display); eglReleaseThread(); #endif /* free any planes */ EINA_LIST_FREE(comp->planes, p) e_plane_shutdown(p); global_err: /* destroy the previously created display */ if (comp->wl.display) wl_display_destroy(comp->wl.display); return EINA_FALSE; } EAPI Eina_Bool e_compositor_shutdown(E_Compositor *comp) { E_Surface *es; E_Plane *p; /* free any surfaces */ EINA_LIST_FREE(comp->surfaces, es) e_surface_destroy(es); /* free any planes */ EINA_LIST_FREE(comp->planes, p) e_plane_shutdown(p); #ifdef HAVE_WAYLAND_EGL /* destroy the egl display */ if (comp->egl.display) eglTerminate(comp->egl.display); eglReleaseThread(); #endif /* destroy the input loop */ if (comp->wl.input_loop) wl_event_loop_destroy(comp->wl.input_loop); /* destroy the fd handler */ if (comp->fd_hdlr) ecore_main_fd_handler_del(comp->fd_hdlr); /* destroy the previously created display */ if (comp->wl.display) wl_display_destroy(comp->wl.display); return EINA_TRUE; } EAPI E_Compositor * e_compositor_get(void) { return _e_comp; } EAPI void e_compositor_plane_stack(E_Compositor *comp, E_Plane *plane, E_Plane *above) { /* add this plane to the compositors list */ comp->planes = eina_list_prepend_relative(comp->planes, plane, above); } EAPI int e_compositor_input_read(int fd EINA_UNUSED, unsigned int mask EINA_UNUSED, void *data) { E_Compositor *comp; if (!(comp = data)) return 1; wl_event_loop_dispatch(comp->wl.input_loop, 0); return 1; } EAPI void e_compositor_damage_calculate(E_Compositor *comp) { E_Plane *plane; E_Surface *es; Eina_List *l; pixman_region32_t clip, opaque; /* check for valid compositor */ if (!comp) return; pixman_region32_init(&clip); /* loop the planes */ EINA_LIST_FOREACH(comp->planes, l, plane) { pixman_region32_copy(&plane->clip, &clip); pixman_region32_init(&opaque); /* loop the surfaces */ EINA_LIST_FOREACH(comp->surfaces, l, es) { if (es->plane != plane) continue; e_surface_damage_calculate(es, &opaque); } pixman_region32_union(&clip, &clip, &opaque); pixman_region32_fini(&opaque); } pixman_region32_fini(&clip); /* loop the surfaces */ EINA_LIST_FOREACH(comp->surfaces, l, es) { if (!es->buffer.keep) e_buffer_reference(&es->buffer.reference, NULL); } } EAPI unsigned int e_compositor_get_time(void) { struct timeval tv; gettimeofday(&tv, NULL); return (tv.tv_sec * 1000 + tv.tv_usec / 1000); } /* local functions */ static void _e_comp_cb_bind(struct wl_client *client, void *data, unsigned int version EINA_UNUSED, unsigned int id) { E_Compositor *comp; if (!(comp = data)) return; /* add the compositor to the client */ wl_client_add_object(client, &wl_compositor_interface, &_e_compositor_interface, id, comp); } static void _e_comp_cb_bind_manager(struct wl_client *client, void *data EINA_UNUSED, unsigned int version EINA_UNUSED, unsigned int id) { /* add the data device manager to the client */ wl_client_add_object(client, &wl_data_device_manager_interface, &_e_manager_interface, id, NULL); } static void _e_comp_cb_surface_create(struct wl_client *client, struct wl_resource *resource, unsigned int id) { E_Compositor *comp; E_Surface *es; printf("E_Comp Surface Create\n"); /* try to cast to our compositor */ if (!(comp = resource->data)) return; /* try to create a new surface */ if (!(es = e_surface_new(id))) { wl_resource_post_no_memory(resource); return; } /* ask the renderer to create any internal representation of this surface * that it may need */ if (!comp->renderer->surface_create(es)) { e_surface_destroy(es); return; } /* set surface plane to compositor primary plane */ es->plane = &comp->plane; /* set destroy callback */ es->wl.resource.destroy = _e_comp_cb_surface_destroy; /* add this surface to the client */ wl_client_add_resource(client, &es->wl.resource); /* add this surface to the compositors list */ comp->surfaces = eina_list_append(comp->surfaces, es); printf("\tCreated: %p\n", es); } static void _e_comp_cb_surface_destroy(struct wl_resource *resource) { E_Surface *es; E_Surface_Frame *cb, *cbnext; printf("E_Comp Surface Destroy\n"); /* try to get the surface from this resource */ if (!(es = container_of(resource, E_Surface, wl.resource))) return; printf("\tDestroyed: %p\n", es); /* remove this surface from the compositor */ _e_comp->surfaces = eina_list_remove(_e_comp->surfaces, es); /* if this surface is mapped, unmap it */ if (es->mapped) e_surface_unmap(es); /* remove any pending frame callbacks */ wl_list_for_each_safe(cb, cbnext, &es->pending.frames, link) wl_resource_destroy(&cb->resource); pixman_region32_fini(&es->pending.damage); pixman_region32_fini(&es->pending.opaque); pixman_region32_fini(&es->pending.input); /* destroy pending buffer */ if (es->pending.buffer) wl_list_remove(&es->pending.buffer_destroy.link); /* remove any buffer references */ e_buffer_reference(&es->buffer.reference, NULL); if (_e_comp->renderer->surface_destroy) _e_comp->renderer->surface_destroy(es); /* free regions */ pixman_region32_fini(&es->bounding); pixman_region32_fini(&es->damage); pixman_region32_fini(&es->opaque); pixman_region32_fini(&es->input); pixman_region32_fini(&es->clip); /* remove any active frame callbacks */ wl_list_for_each_safe(cb, cbnext, &es->frames, link) wl_resource_destroy(&cb->resource); /* free the surface structure */ E_FREE(es); } static void _e_comp_cb_region_create(struct wl_client *client, struct wl_resource *resource, unsigned int id) { E_Compositor *comp; E_Region *reg; /* try to cast to our compositor */ if (!(comp = resource->data)) return; /* try to create a new region */ if (!(reg = e_region_new(id))) { wl_resource_post_no_memory(resource); return; } reg->resource.destroy = _e_comp_cb_region_destroy; wl_client_add_resource(client, ®->resource); } static void _e_comp_cb_region_destroy(struct wl_resource *resource) { E_Region *reg; /* try to get the region from this resource */ if (!(reg = container_of(resource, E_Region, resource))) return; /* free the region */ pixman_region32_fini(®->region); /* free the structure */ E_FREE(reg); } static Eina_Bool _e_comp_cb_read(void *data, Ecore_Fd_Handler *hdl EINA_UNUSED) { E_Compositor *comp; if ((comp = data)) { wl_event_loop_dispatch(comp->wl.loop, 0); wl_display_flush_clients(comp->wl.display); } return ECORE_CALLBACK_RENEW; } static Eina_Bool _e_comp_cb_idle(void *data) { E_Compositor *comp; if ((comp = data)) { /* flush any clients before we idle */ wl_display_flush_clients(comp->wl.display); } return ECORE_CALLBACK_RENEW; }