summaryrefslogtreecommitdiff
path: root/src/modules/wl_drm
diff options
context:
space:
mode:
authorChristopher Michael <cpmichael1@comcast.net>2012-03-29 01:43:17 +0000
committerChristopher Michael <cpmichael1@comcast.net>2012-03-29 01:43:17 +0000
commit648f558aa863312605d9b25801edfdf5c7bd3d6b (patch)
tree853d847703654d48f64290845548f23264e186d9 /src/modules/wl_drm
parent56c31ab510f1270abcb84eb9761ce0ba5af7ebb9 (diff)
downloadenlightenment-648f558aa863312605d9b25801edfdf5c7bd3d6b.tar.gz
E17 (wayland): Add wayland drm compositing module.
Add wayland shell module. Add wayland screenshot module. NB: I am adding these modules but NOT adding the needed build infrastructure just yet because people should not even 'play' with this yet (still some buggers to iron out). SVN revision: 69709
Diffstat (limited to 'src/modules/wl_drm')
-rw-r--r--src/modules/wl_drm/Makefile.am35
-rw-r--r--src/modules/wl_drm/e-module-wl_drm.edjbin0 -> 13048 bytes
-rw-r--r--src/modules/wl_drm/e_drm_output.c604
-rw-r--r--src/modules/wl_drm/e_evdev.c664
-rw-r--r--src/modules/wl_drm/e_mod_main.c713
-rw-r--r--src/modules/wl_drm/e_mod_main.h204
-rw-r--r--src/modules/wl_drm/e_sprite.c88
-rw-r--r--src/modules/wl_drm/e_tty.c201
-rw-r--r--src/modules/wl_drm/module.desktop6
9 files changed, 2515 insertions, 0 deletions
diff --git a/src/modules/wl_drm/Makefile.am b/src/modules/wl_drm/Makefile.am
new file mode 100644
index 0000000000..54adf4d1e7
--- /dev/null
+++ b/src/modules/wl_drm/Makefile.am
@@ -0,0 +1,35 @@
+MAINTAINERCLEANFILES = Makefile.in
+MODULE = wl_drm
+
+# data files for the module
+filesdir = $(libdir)/enlightenment/modules/$(MODULE)
+files_DATA = \
+e-module-$(MODULE).edj module.desktop
+
+EXTRA_DIST = $(files_DATA)
+
+# the module .so file
+INCLUDES = -I. \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/src/modules/$(MODULE) \
+ -I$(top_srcdir)/src/bin/e_wayland \
+ -I$(top_builddir)/src/bin/e_wayland \
+ -I$(top_srcdir)/src/modules \
+ @e_wl_cflags@ @WAYLAND_DRM_CFLAGS@
+
+pkgdir = $(libdir)/enlightenment/modules/$(MODULE)/$(MODULE_ARCH)
+pkg_LTLIBRARIES = module.la
+
+module_la_SOURCES = e_mod_main.c \
+ e_mod_main.h \
+ e_tty.c \
+ e_sprite.c \
+ e_drm_output.c \
+ e_evdev.c
+
+module_la_LIBADD = @e_wl_libs@ @dlopen_libs@ @WAYLAND_DRM_LIBS@
+module_la_LDFLAGS = -module -avoid-version
+module_la_DEPENDENCIES = $(top_builddir)/config.h
+
+uninstall:
+ rm -rf $(DESTDIR)$(libdir)/enlightenment/modules/$(MODULE)
diff --git a/src/modules/wl_drm/e-module-wl_drm.edj b/src/modules/wl_drm/e-module-wl_drm.edj
new file mode 100644
index 0000000000..2c1f4d26ef
--- /dev/null
+++ b/src/modules/wl_drm/e-module-wl_drm.edj
Binary files differ
diff --git a/src/modules/wl_drm/e_drm_output.c b/src/modules/wl_drm/e_drm_output.c
new file mode 100644
index 0000000000..5578fe25b6
--- /dev/null
+++ b/src/modules/wl_drm/e_drm_output.c
@@ -0,0 +1,604 @@
+#include "e.h"
+#include "e_mod_main.h"
+
+EINTERN int
+e_drm_output_subpixel_convert(int value)
+{
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ switch (value)
+ {
+ case DRM_MODE_SUBPIXEL_NONE:
+ return WL_OUTPUT_SUBPIXEL_NONE;
+ case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
+ return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB;
+ case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
+ return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR;
+ case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
+ return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB;
+ case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
+ return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR;
+ case DRM_MODE_SUBPIXEL_UNKNOWN:
+ default:
+ return WL_OUTPUT_SUBPIXEL_UNKNOWN;
+ }
+}
+
+EINTERN Eina_Bool
+e_drm_output_add_mode(E_Drm_Output *output, drmModeModeInfo *info)
+{
+ E_Drm_Output_Mode *mode;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if (!(mode = malloc(sizeof(E_Drm_Output_Mode))))
+ return EINA_FALSE;
+
+ mode->base.flags = 0;
+ mode->base.w = info->hdisplay;
+ mode->base.h = info->vdisplay;
+ mode->base.refresh = info->vrefresh;
+ mode->info = *info;
+
+ wl_list_insert(output->base.modes.prev, &mode->base.link);
+
+ return EINA_TRUE;
+}
+
+EINTERN void
+e_drm_output_set_modes(E_Drm_Compositor *dcomp)
+{
+ E_Drm_Output *output;
+ E_Drm_Output_Mode *mode;
+ int ret = 0;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ wl_list_for_each(output, &dcomp->base.outputs, base.link)
+ {
+ mode = (E_Drm_Output_Mode *)output->base.current;
+ ret = drmModeSetCrtc(dcomp->drm.fd, output->crtc_id,
+ output->fb_id[output->current ^ 1], 0, 0,
+ &output->conn_id, 1, &mode->info);
+ if (ret < 0)
+ printf("Failed to set drm mode: %m\n");
+ }
+}
+
+EINTERN void
+e_drm_output_scanout_buffer_destroy(struct wl_listener *listener, struct wl_resource *resource __UNUSED__, unsigned int timestamp __UNUSED__)
+{
+ E_Drm_Output *output;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ output = container_of(listener, E_Drm_Output, scanout_buffer_destroy_listener);
+ output->scanout_buffer = NULL;
+ if (!output->pending_scanout_buffer)
+ e_compositor_schedule_repaint(output->base.compositor);
+}
+
+EINTERN void
+e_drm_output_pending_scanout_buffer_destroy(struct wl_listener *listener, struct wl_resource *resource __UNUSED__, unsigned int timestamp __UNUSED__)
+{
+ E_Drm_Output *output;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ output = container_of(listener, E_Drm_Output,
+ pending_scanout_buffer_destroy_listener);
+ output->pending_scanout_buffer = NULL;
+ e_compositor_schedule_repaint(output->base.compositor);
+}
+
+EINTERN void
+e_drm_output_repaint(E_Output *base, pixman_region32_t *damage)
+{
+ E_Drm_Output *output;
+ E_Drm_Compositor *dcomp;
+ E_Surface *es;
+ E_Sprite *sprite;
+ unsigned int fb_id = 0;
+ int ret = 0;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ output = (E_Drm_Output *)base;
+ dcomp = (E_Drm_Compositor *)output->base.compositor;
+
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER, output->rbo[output->current]);
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+ return;
+
+ e_drm_output_prepare_scanout_surface(output);
+
+ wl_list_for_each_reverse(es, &dcomp->base.surfaces, link)
+ e_surface_draw(es, &output->base, damage);
+
+ glFlush();
+
+ output->current ^= 1;
+
+ if (output->pending_fs_surf_fb_id != 0)
+ fb_id = output->pending_fs_surf_fb_id;
+ else
+ fb_id = output->fb_id[output->current ^ 1];
+
+ if (drmModePageFlip(dcomp->drm.fd, output->crtc_id, fb_id,
+ DRM_MODE_PAGE_FLIP_EVENT, output) < 0)
+ return;
+
+ wl_list_for_each(sprite, &dcomp->sprites, link)
+ {
+ unsigned int flags = 0;
+ drmVBlank vbl;
+
+ vbl.request.type = (DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT);
+ vbl.request.sequence = 1;
+ if (!e_sprite_crtc_supported(base, sprite->possible_crtcs))
+ continue;
+ ret = drmModeSetPlane(dcomp->drm.fd, sprite->plane_id, output->crtc_id,
+ sprite->pending_fb_id, flags, sprite->dx,
+ sprite->dy, sprite->dw, sprite->dh, sprite->sx,
+ sprite->sy, sprite->sw, sprite->sh);
+ if (ret)
+ printf("Setplane Failed: %s\n", strerror(errno));
+
+ vbl.request.signal = (unsigned long)sprite;
+ ret = drmWaitVBlank(dcomp->drm.fd, &vbl);
+ if (ret)
+ printf("VBlank event request failed: %s\n", strerror(errno));
+ }
+}
+
+EINTERN void
+e_drm_output_destroy(E_Output *base)
+{
+ E_Drm_Output *output;
+ E_Drm_Compositor *dcomp;
+ drmModeCrtcPtr ocrtc;
+ int i = 0;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ output = (E_Drm_Output *)base;
+ dcomp = (E_Drm_Compositor *)output->base.compositor;
+ ocrtc = output->orig_crtc;
+
+ /* TODO: backlight */
+ /* if (base->backlight) */
+
+ e_drm_output_set_cursor(&output->base, NULL);
+
+ drmModeSetCrtc(dcomp->drm.fd, ocrtc->crtc_id, ocrtc->buffer_id,
+ ocrtc->x, ocrtc->y, &output->conn_id, 1, &ocrtc->mode);
+ drmModeFreeCrtc(ocrtc);
+
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER, 0);
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ glDeleteRenderbuffers(2, output->rbo);
+
+ for (i = 0; i < 2; i++)
+ {
+ drmModeRmFB(dcomp->drm.fd, output->fb_id[i]);
+ dcomp->base.destroy_image(dcomp->base.egl_display, output->image[i]);
+ gbm_bo_destroy(output->bo[i]);
+ }
+
+ dcomp->crtc_alloc &= ~(1 << output->crtc_id);
+ dcomp->conn_alloc &= ~(1 << output->conn_id);
+
+ e_output_destroy(&output->base);
+ wl_list_remove(&output->base.link);
+
+ free(output);
+}
+
+EINTERN void
+e_drm_output_assign_planes(E_Output *base)
+{
+ E_Compositor *comp;
+ E_Surface *es;
+ pixman_region32_t overlap, soverlap;
+ E_Input_Device *dev;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ comp = base->compositor;
+ pixman_region32_init(&overlap);
+ wl_list_for_each(es, &comp->surfaces, link)
+ {
+ pixman_region32_init(&soverlap);
+ pixman_region32_intersect(&soverlap, &overlap, &es->transform.box);
+ dev = (E_Input_Device *)comp->input_device;
+ if (es == dev->sprite)
+ {
+ e_drm_output_set_cursor_region(base, dev, &soverlap);
+ if (!dev->hw_cursor)
+ pixman_region32_union(&overlap, &overlap, &es->transform.box);
+ }
+ else if (!e_drm_output_prepare_overlay_surface(base, es, &soverlap))
+ {
+ pixman_region32_fini(&es->damage);
+ pixman_region32_init(&es->damage);
+ }
+ else
+ pixman_region32_union(&overlap, &overlap, &es->transform.box);
+
+ pixman_region32_fini(&soverlap);
+ }
+ pixman_region32_fini(&overlap);
+
+ e_drm_output_disable_sprites(base);
+}
+
+EINTERN void
+e_drm_output_set_dpms(E_Output *base, E_Dpms_Level level)
+{
+ E_Drm_Output *output;
+ E_Compositor *comp;
+ E_Drm_Compositor *dcomp;
+ drmModeConnectorPtr conn;
+ drmModePropertyPtr prop;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ output = (E_Drm_Output *)base;
+ comp = base->compositor;
+ dcomp = (E_Drm_Compositor *)comp;
+
+ if (!(conn = drmModeGetConnector(dcomp->drm.fd, output->conn_id)))
+ return;
+ if (!(prop = e_drm_output_get_property(dcomp->drm.fd, conn, "DPMS")))
+ {
+ drmModeFreeConnector(conn);
+ return;
+ }
+ drmModeConnectorSetProperty(dcomp->drm.fd, conn->connector_id,
+ prop->prop_id, level);
+ drmModeFreeProperty(prop);
+ drmModeFreeConnector(conn);
+}
+
+EINTERN Eina_Bool
+e_drm_output_prepare_scanout_surface(E_Drm_Output *output)
+{
+ E_Drm_Compositor *dcomp;
+ E_Surface *es;
+ EGLint hdl, stride;
+ int ret = 0;
+ unsigned int fb_id = 0;
+ struct gbm_bo *bo;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ dcomp = (E_Drm_Compositor *)output->base.compositor;
+ es = container_of(dcomp->base.surfaces.next, E_Surface, link);
+
+ /* Need to verify output->region contained in surface opaque
+ * region. Or maybe just that format doesn't have alpha. */
+ return EINA_TRUE;
+
+ if ((es->geometry.x != output->base.x) ||
+ (es->geometry.y != output->base.y) ||
+ (es->geometry.w != output->base.current->w) ||
+ (es->geometry.h != output->base.current->h) ||
+ (es->transform.enabled) || (es->image == EGL_NO_IMAGE_KHR))
+ return EINA_FALSE;
+
+ bo = gbm_bo_create_from_egl_image(dcomp->gbm, dcomp->base.egl_display,
+ es->image, es->geometry.w,
+ es->geometry.h, GBM_BO_USE_SCANOUT);
+ hdl = gbm_bo_get_handle(bo).s32;
+ stride = gbm_bo_get_pitch(bo);
+
+ gbm_bo_destroy(bo);
+
+ if (hdl == 0) return EINA_FALSE;
+
+ ret = drmModeAddFB(dcomp->drm.fd, output->base.current->w,
+ output->base.current->h, 24, 32, stride, hdl, &fb_id);
+ if (ret) return EINA_FALSE;
+
+ output->pending_fs_surf_fb_id = fb_id;
+
+ output->pending_scanout_buffer = es->buffer;
+ output->pending_scanout_buffer->busy_count++;
+
+ wl_list_insert(output->pending_scanout_buffer->resource.destroy_listener_list.prev,
+ &output->pending_scanout_buffer_destroy_listener.link);
+
+ pixman_region32_fini(&es->damage);
+ pixman_region32_init(&es->damage);
+
+ return EINA_TRUE;
+}
+
+EINTERN void
+e_drm_output_disable_sprites(E_Output *base)
+{
+ E_Compositor *comp;
+ E_Drm_Compositor *dcomp;
+ E_Drm_Output *output;
+ E_Sprite *s;
+ int ret = 0;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ comp = base->compositor;
+ dcomp = (E_Drm_Compositor *)comp;
+ output = (E_Drm_Output *)base;
+
+ wl_list_for_each(s, &dcomp->sprites, link)
+ {
+ if (s->pending_fb_id) continue;
+ ret = drmModeSetPlane(dcomp->drm.fd, s->plane_id, output->crtc_id,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ if (ret)
+ printf("Failed to disable plane: %s\n", strerror(errno));
+ drmModeRmFB(dcomp->drm.fd, s->fb_id);
+ s->surface = NULL;
+ s->pending_surface = NULL;
+ s->fb_id = 0;
+ s->pending_fb_id = 0;
+ }
+}
+
+EINTERN drmModePropertyPtr
+e_drm_output_get_property(int fd, drmModeConnectorPtr conn, const char *name)
+{
+ drmModePropertyPtr props;
+ int i = 0;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ for (i = 0; i < conn->count_props; i++)
+ {
+ if (!(props = drmModeGetProperty(fd, conn->props[i])))
+ continue;
+ if (!strcmp(props->name, name)) return props;
+ drmModeFreeProperty(props);
+ }
+
+ return NULL;
+}
+
+EINTERN int
+e_drm_output_prepare_overlay_surface(E_Output *base, E_Surface *es, pixman_region32_t *overlap)
+{
+ E_Compositor *comp;
+ E_Drm_Compositor *dcomp;
+ E_Sprite *s;
+ Eina_Bool found = EINA_FALSE;
+ EGLint hdl, stride;
+ struct gbm_bo *bo;
+ unsigned int fb_id = 0;
+ unsigned int hdls[4], pitches[4], offsets[4];
+ int ret = 0;
+ pixman_region32_t drect, srect;
+ pixman_box32_t *box;
+ unsigned int format;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ comp = base->compositor;
+ dcomp = (E_Drm_Compositor *)comp;
+
+ if (dcomp->sprites_broken) return -1;
+ if (e_surface_is_primary(comp, es)) return -1;
+ if (es->image == EGL_NO_IMAGE_KHR) return -1;
+
+ if (!e_drm_output_surface_transform_supported(es))
+ return -1;
+ if (!e_drm_output_surface_overlap_supported(base, overlap))
+ return -1;
+
+ wl_list_for_each(s, &dcomp->sprites, link)
+ {
+ if (!e_sprite_crtc_supported(base, s->possible_crtcs))
+ continue;
+ if (!s->pending_fb_id)
+ {
+ found = EINA_TRUE;
+ break;
+ }
+ }
+
+ if (!found) return -1;
+
+ bo = gbm_bo_create_from_egl_image(dcomp->gbm, dcomp->base.egl_display,
+ es->image, es->geometry.w,
+ es->geometry.h, GBM_BO_USE_SCANOUT);
+ format = gbm_bo_get_format(bo);
+ hdl = gbm_bo_get_handle(bo).s32;
+ stride = gbm_bo_get_pitch(bo);
+
+ gbm_bo_destroy(bo);
+
+ if (!e_drm_output_surface_format_supported(s, format))
+ return -1;
+
+ if (!hdl) return -1;
+
+ hdls[0] = hdl;
+ pitches[0] = stride;
+ offsets[0] = 0;
+
+ ret = drmModeAddFB2(dcomp->drm.fd, es->geometry.w, es->geometry.h,
+ format, hdls, pitches, offsets, &fb_id, 0);
+ if (ret)
+ {
+ dcomp->sprites_broken = EINA_TRUE;
+ return -1;
+ }
+
+ if ((s->surface) && (s->surface != es))
+ {
+ E_Surface *os;
+
+ os = s->surface;
+ pixman_region32_fini(&os->damage);
+ pixman_region32_init_rect(&os->damage, os->geometry.x, os->geometry.y,
+ os->geometry.w, os->geometry.h);
+ }
+
+ s->pending_fb_id = fb_id;
+ s->pending_surface = es;
+ es->buffer->busy_count++;
+
+ pixman_region32_init(&drect);
+ pixman_region32_intersect(&drect, &es->transform.box, &base->region);
+ pixman_region32_translate(&drect, -base->x, -base->y);
+
+ box = pixman_region32_extents(&drect);
+ s->dx = box->x1;
+ s->dy = box->y1;
+ s->dw = (box->x2 - box->x1);
+ s->dh = (box->y2 - box->y1);
+ pixman_region32_fini(&drect);
+
+ pixman_region32_init(&srect);
+ pixman_region32_intersect(&srect, &es->transform.box, &base->region);
+ pixman_region32_translate(&srect, -es->geometry.x, -es->geometry.y);
+
+ box = pixman_region32_extents(&srect);
+ s->sx = box->x1 << 16;
+ s->sy = box->y1 << 16;
+ s->sw = (box->x2 - box->x1) << 16;
+ s->sh = (box->y2 - box->y1) << 16;
+ pixman_region32_fini(&srect);
+
+ wl_list_insert(es->buffer->resource.destroy_listener_list.prev,
+ &s->pending_destroy_listener.link);
+
+ return 0;
+}
+
+EINTERN Eina_Bool
+e_drm_output_surface_transform_supported(E_Surface *es)
+{
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if ((es) && (es->transform.enabled)) return EINA_TRUE;
+ return EINA_FALSE;
+}
+
+EINTERN Eina_Bool
+e_drm_output_surface_overlap_supported(E_Output *base __UNUSED__, pixman_region32_t *overlap)
+{
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if (pixman_region32_not_empty(overlap)) return EINA_TRUE;
+ return EINA_FALSE;
+}
+
+EINTERN Eina_Bool
+e_drm_output_surface_format_supported(E_Sprite *s, unsigned int format)
+{
+ unsigned int i = 0;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ for (i = 0; i < s->format_count; i++)
+ if (s->formats[i] == format) return EINA_TRUE;
+
+ return EINA_FALSE;
+}
+
+EINTERN void
+e_drm_output_set_cursor_region(E_Output *output, E_Input_Device *device, pixman_region32_t *overlap)
+{
+ pixman_region32_t cregion;
+ Eina_Bool prior = EINA_FALSE;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if (!device->sprite) return;
+ pixman_region32_init(&cregion);
+ pixman_region32_intersect(&cregion, &device->sprite->transform.box,
+ &output->region);
+ if (!pixman_region32_not_empty(&cregion))
+ {
+ e_drm_output_set_cursor(output, NULL);
+ goto out;
+ }
+
+ prior = device->hw_cursor;
+ if ((pixman_region32_not_empty(overlap)) ||
+ (!e_drm_output_set_cursor(output, device)))
+ {
+ if (prior)
+ {
+ e_surface_damage(device->sprite);
+ e_drm_output_set_cursor(output, NULL);
+ }
+ device->hw_cursor = EINA_FALSE;
+ }
+ else
+ {
+ if (!prior) e_surface_damage_below(device->sprite);
+ pixman_region32_fini(&device->sprite->damage);
+ pixman_region32_init(&device->sprite->damage);
+ device->hw_cursor = EINA_TRUE;
+ }
+
+out:
+ pixman_region32_fini(&cregion);
+}
+
+EINTERN Eina_Bool
+e_drm_output_set_cursor(E_Output *output, E_Input_Device *device)
+{
+ E_Drm_Output *doutput;
+ E_Drm_Compositor *dcomp;
+ EGLint hdl, stride;
+ int ret = -1;
+ struct gbm_bo *bo;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ doutput = (E_Drm_Output *)output;
+ dcomp = (E_Drm_Compositor *)doutput->base.compositor;
+
+ if (!device)
+ {
+ drmModeSetCursor(dcomp->drm.fd, doutput->crtc_id, 0, 0, 0);
+ return EINA_TRUE;
+ }
+
+ if (device->sprite->image == EGL_NO_IMAGE_KHR) return EINA_FALSE;
+
+ if ((device->sprite->geometry.w > 64) ||
+ (device->sprite->geometry.h > 64))
+ return EINA_FALSE;
+
+ bo = gbm_bo_create_from_egl_image(dcomp->gbm, dcomp->base.egl_display,
+ device->sprite->image, 64, 64,
+ GBM_BO_USE_CURSOR_64X64);
+ if (!bo) return EINA_FALSE;
+
+ hdl = gbm_bo_get_handle(bo).s32;
+ stride = gbm_bo_get_pitch(bo);
+ gbm_bo_destroy(bo);
+
+ if (stride != (64 * 4)) return EINA_FALSE;
+
+ if ((ret = drmModeSetCursor(dcomp->drm.fd, doutput->crtc_id, hdl, 64, 64)))
+ {
+ drmModeSetCursor(dcomp->drm.fd, doutput->crtc_id, 0, 0, 0);
+ return EINA_FALSE;
+ }
+
+ ret = drmModeMoveCursor(dcomp->drm.fd, doutput->crtc_id,
+ device->sprite->geometry.x - doutput->base.x,
+ device->sprite->geometry.y - doutput->base.y);
+ if (ret)
+ {
+ drmModeSetCursor(dcomp->drm.fd, doutput->crtc_id, 0, 0, 0);
+ return EINA_FALSE;
+ }
+
+ return EINA_TRUE;
+}
diff --git a/src/modules/wl_drm/e_evdev.c b/src/modules/wl_drm/e_evdev.c
new file mode 100644
index 0000000000..4ce4760ac5
--- /dev/null
+++ b/src/modules/wl_drm/e_evdev.c
@@ -0,0 +1,664 @@
+#include "e.h"
+#include "e_mod_main.h"
+
+#define E_EVDEV_ABSOLUTE_MOTION (1 << 0)
+#define E_EVDEV_ABSOLUTE_MT_DOWN (1 << 1)
+#define E_EVDEV_ABSOLUTE_MT_MOTION (1 << 2)
+#define E_EVDEV_ABSOLUTE_MT_UP (1 << 3)
+#define E_EVDEV_RELATIVE_MOTION (1 << 4)
+
+#define BITS_PER_LONG (sizeof(unsigned long) * 8)
+#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
+#define OFF(x) ((x)%BITS_PER_LONG)
+#define BIT(x) (1UL<<OFF(x))
+#define LONG(x) ((x)/BITS_PER_LONG)
+#define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1)
+
+#define MODIFIER_CTRL (1 << 8)
+#define MODIFIER_ALT (1 << 9)
+#define MODIFIER_SUPER (1 << 10)
+
+/* local function prototypes */
+static Eina_Bool _e_evdev_config_udev_monitor(struct udev *udev, E_Evdev_Input *master);
+static E_Evdev_Input_Device *_e_evdev_input_device_create(E_Evdev_Input *master, struct wl_display *display __UNUSED__, const char *path);
+static Eina_Bool _e_evdev_configure_device(E_Evdev_Input_Device *dev);
+static Eina_Bool _e_evdev_is_motion_event(struct input_event *e);
+static void _e_evdev_flush_motion(E_Evdev_Input_Device *dev, unsigned int timestamp);
+static void _e_evdev_process_touch(E_Evdev_Input_Device *device, struct input_event *e);
+static void _e_evdev_process_events(E_Evdev_Input_Device *device, struct input_event *ev, int count);
+
+static int _e_evdev_cb_udev(int fd __UNUSED__, unsigned int mask __UNUSED__, void *data);
+static void _e_evdev_cb_device_added(struct udev_device *dev, E_Evdev_Input *master);
+static void _e_evdev_cb_device_removed(struct udev_device *dev, E_Evdev_Input *master);
+static int _e_evdev_cb_device_data(int fd, unsigned int mask __UNUSED__, void *data);
+
+/* local variables */
+/* wayland interfaces */
+/* external variables */
+
+EINTERN void
+e_evdev_add_devices(struct udev *udev, E_Input_Device *base)
+{
+ E_Evdev_Input *input;
+ struct udev_enumerate *ue;
+ struct udev_list_entry *entry;
+ struct udev_device *device;
+ const char *path = NULL;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ input = (E_Evdev_Input *)base;
+ ue = udev_enumerate_new(udev);
+ udev_enumerate_add_match_subsystem(ue, "input");
+ udev_enumerate_scan_devices(ue);
+ udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(ue))
+ {
+ path = udev_list_entry_get_name(entry);
+ device = udev_device_new_from_syspath(udev, path);
+ if (strncmp("event", udev_device_get_sysname(device), 5) != 0)
+ continue;
+ _e_evdev_cb_device_added(device, input);
+ udev_device_unref(device);
+ }
+ udev_enumerate_unref(ue);
+
+ if (wl_list_empty(&input->devices))
+ printf("No Input Devices Found\n");
+}
+
+EINTERN void
+e_evdev_remove_devices(E_Input_Device *base)
+{
+ E_Evdev_Input *input;
+ E_Evdev_Input_Device *device, *next;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ input = (E_Evdev_Input *)base;
+ wl_list_for_each_safe(device, next, &input->devices, link)
+ {
+ wl_event_source_remove(device->source);
+ wl_list_remove(&device->link);
+ if (device->mtdev) mtdev_close_delete(device->mtdev);
+ close(device->fd);
+ free(device->devnode);
+ free(device);
+ }
+}
+
+EINTERN void
+e_evdev_input_create(E_Compositor *comp, struct udev *udev, const char *seat)
+{
+ E_Evdev_Input *input;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if (!(input = malloc(sizeof(E_Evdev_Input)))) return;
+ memset(input, 0, sizeof(E_Evdev_Input));
+ e_input_device_init(&input->base, comp);
+ wl_list_init(&input->devices);
+ input->seat = strdup(seat);
+ if (!_e_evdev_config_udev_monitor(udev, input))
+ {
+ free(input->seat);
+ free(input);
+ return;
+ }
+ e_evdev_add_devices(udev, &input->base);
+ comp->input_device = &input->base.input_device;
+}
+
+EINTERN void
+e_evdev_input_destroy(E_Input_Device *base)
+{
+ E_Evdev_Input *input;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ input = (E_Evdev_Input *)base;
+ e_evdev_remove_devices(base);
+ wl_list_remove(&input->base.link);
+ free(input->seat);
+ free(input);
+}
+
+/* local functions */
+static Eina_Bool
+_e_evdev_config_udev_monitor(struct udev *udev, E_Evdev_Input *master)
+{
+ struct wl_event_loop *loop;
+ E_Compositor *comp;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ comp = master->base.compositor;
+ if (!(master->monitor = udev_monitor_new_from_netlink(udev, "udev")))
+ return EINA_FALSE;
+ udev_monitor_filter_add_match_subsystem_devtype(master->monitor, "input", NULL);
+ if (udev_monitor_enable_receiving(master->monitor))
+ return EINA_FALSE;
+ loop = wl_display_get_event_loop(comp->display);
+ wl_event_loop_add_fd(loop, udev_monitor_get_fd(master->monitor),
+ WL_EVENT_READABLE, _e_evdev_cb_udev, master);
+ return EINA_TRUE;
+}
+
+static E_Evdev_Input_Device *
+_e_evdev_input_device_create(E_Evdev_Input *master, struct wl_display *display __UNUSED__, const char *path)
+{
+ E_Evdev_Input_Device *device;
+ E_Compositor *comp;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if (!(device = malloc(sizeof(E_Evdev_Input_Device)))) return NULL;
+ comp = master->base.compositor;
+ device->output = container_of(comp->outputs.next, E_Output, link);
+ device->master = master;
+ device->is_pad = EINA_FALSE;
+ device->is_mt = EINA_FALSE;
+ device->mtdev = NULL;
+ device->devnode = strdup(path);
+ device->mt.slot = -1;
+ device->rel.dx = 0;
+ device->rel.dy = 0;
+
+ device->fd = open(path, O_RDONLY | O_NONBLOCK);
+ if (device->fd < 0) goto err0;
+
+ if (!_e_evdev_configure_device(device)) goto err1;
+
+ if (device->is_mt)
+ {
+ if (!(device->mtdev = mtdev_new_open(device->fd)))
+ printf("mtdev Failed to open device: %s\n", path);
+ }
+
+ device->source =
+ wl_event_loop_add_fd(comp->input_loop, device->fd, WL_EVENT_READABLE,
+ _e_evdev_cb_device_data, device);
+ if (!device->source) goto err1;
+
+ wl_list_insert(master->devices.prev, &device->link);
+
+ return device;
+
+err1:
+ close(device->fd);
+
+err0:
+ free(device->devnode);
+ free(device);
+ return NULL;
+}
+
+static Eina_Bool
+_e_evdev_configure_device(E_Evdev_Input_Device *dev)
+{
+ struct input_absinfo absinfo;
+ unsigned long ebits[NBITS(EV_MAX)];
+ unsigned long abits[NBITS(ABS_MAX)];
+ unsigned long kbits[NBITS(KEY_MAX)];
+ Eina_Bool has_key = EINA_FALSE, has_abs = EINA_FALSE;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ ioctl(dev->fd, EVIOCGBIT(0, sizeof(ebits)), ebits);
+ if (TEST_BIT(ebits, EV_ABS))
+ {
+ has_abs = EINA_TRUE;
+ ioctl(dev->fd, EVIOCGBIT(EV_ABS, sizeof(abits)), abits);
+ if (TEST_BIT(abits, ABS_X))
+ {
+ ioctl(dev->fd, EVIOCGABS(ABS_X), &absinfo);
+ dev->absolute.min_x = absinfo.minimum;
+ dev->absolute.max_x = absinfo.maximum;
+ }
+ if (TEST_BIT(abits, ABS_Y))
+ {
+ ioctl(dev->fd, EVIOCGABS(ABS_Y), &absinfo);
+ dev->absolute.min_y = absinfo.minimum;
+ dev->absolute.max_y = absinfo.maximum;
+ }
+ if (TEST_BIT(abits, ABS_MT_SLOT))
+ {
+ dev->is_mt = EINA_TRUE;
+ dev->mt.slot = 0;
+ }
+ }
+ if (TEST_BIT(ebits, EV_KEY))
+ {
+ has_key = EINA_TRUE;
+ ioctl(dev->fd, EVIOCGBIT(EV_KEY, sizeof(kbits)), kbits);
+ if ((TEST_BIT(kbits, BTN_TOOL_FINGER)) &&
+ (!TEST_BIT(kbits, BTN_TOOL_PEN)))
+ dev->is_pad = EINA_TRUE;
+ }
+
+ if ((has_abs) && (!has_key)) return EINA_FALSE;
+
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_evdev_is_motion_event(struct input_event *e)
+{
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ switch (e->type)
+ {
+ case EV_REL:
+ switch (e->code)
+ {
+ case REL_X:
+ case REL_Y:
+ return EINA_TRUE;
+ }
+ case EV_ABS:
+ switch (e->code)
+ {
+ case ABS_X:
+ case ABS_Y:
+ case ABS_MT_POSITION_X:
+ case ABS_MT_POSITION_Y:
+ return EINA_TRUE;
+ }
+ }
+
+ return EINA_FALSE;
+}
+
+static void
+_e_evdev_flush_motion(E_Evdev_Input_Device *dev, unsigned int timestamp)
+{
+ struct wl_input_device *master;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if (!dev->type) return;
+ master = &dev->master->base.input_device;
+ if (dev->type & E_EVDEV_RELATIVE_MOTION)
+ {
+ e_input_notify_motion(master, timestamp,
+ master->x + dev->rel.dx,
+ master->y + dev->rel.dy);
+ dev->type &= ~E_EVDEV_RELATIVE_MOTION;
+ dev->rel.dx = 0;
+ dev->rel.dy = 0;
+ }
+ if (dev->type & E_EVDEV_ABSOLUTE_MT_DOWN)
+ {
+ e_input_notify_touch(master, timestamp, dev->mt.slot,
+ dev->mt.x[dev->mt.slot],
+ dev->mt.y[dev->mt.slot],
+ WL_INPUT_DEVICE_TOUCH_DOWN);
+ dev->type &= ~E_EVDEV_ABSOLUTE_MT_DOWN;
+ dev->type &= ~E_EVDEV_ABSOLUTE_MT_MOTION;
+ }
+ if (dev->type & E_EVDEV_ABSOLUTE_MT_MOTION)
+ {
+ e_input_notify_touch(master, timestamp, dev->mt.slot,
+ dev->mt.x[dev->mt.slot],
+ dev->mt.y[dev->mt.slot],
+ WL_INPUT_DEVICE_TOUCH_MOTION);
+ dev->type &= ~E_EVDEV_ABSOLUTE_MT_DOWN;
+ dev->type &= ~E_EVDEV_ABSOLUTE_MT_MOTION;
+ }
+ if (dev->type & E_EVDEV_ABSOLUTE_MT_UP)
+ {
+ e_input_notify_touch(master, timestamp, dev->mt.slot, 0, 0,
+ WL_INPUT_DEVICE_TOUCH_UP);
+ dev->type &= ~E_EVDEV_ABSOLUTE_MT_UP;
+ }
+ if (dev->type & E_EVDEV_ABSOLUTE_MOTION)
+ {
+ e_input_notify_motion(master, timestamp,
+ dev->absolute.x, dev->absolute.y);
+ dev->type &= ~E_EVDEV_ABSOLUTE_MOTION;
+ }
+}
+
+static inline void
+_e_evdev_process_key(E_Evdev_Input_Device *device, struct input_event *e, int timestamp)
+{
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if (e->value == 2) return;
+
+ switch (e->code)
+ {
+ case BTN_TOOL_PEN:
+ case BTN_TOOL_RUBBER:
+ case BTN_TOOL_BRUSH:
+ case BTN_TOOL_PENCIL:
+ case BTN_TOOL_AIRBRUSH:
+ case BTN_TOOL_FINGER:
+ case BTN_TOOL_MOUSE:
+ case BTN_TOOL_LENS:
+ if (device->is_pad)
+ {
+ device->absolute.rx = 1;
+ device->absolute.ry = 1;
+ }
+ break;
+ case BTN_TOUCH:
+ if (device->is_mt) break;
+ e->code = BTN_LEFT;
+ case BTN_LEFT:
+ case BTN_RIGHT:
+ case BTN_MIDDLE:
+ case BTN_SIDE:
+ case BTN_EXTRA:
+ case BTN_FORWARD:
+ case BTN_BACK:
+ case BTN_TASK:
+ e_input_notify_button(&device->master->base.input_device,
+ timestamp, e->code, e->value);
+ break;
+ default:
+ e_input_notify_key(&device->master->base.input_device,
+ timestamp, e->code, e->value);
+ break;
+ }
+}
+
+static inline void
+_e_evdev_process_absolute_motion(E_Evdev_Input_Device *device, struct input_event *e)
+{
+ int sw, sh;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ sw = device->output->current->w;
+ sh = device->output->current->h;
+
+ switch (e->code)
+ {
+ case ABS_X:
+ device->absolute.x =
+ (e->value - device->absolute.min_x) * sw /
+ (device->absolute.max_x - device->absolute.min_x) +
+ device->output->x;
+ device->type |= E_EVDEV_ABSOLUTE_MOTION;
+ break;
+ case ABS_Y:
+ device->absolute.y =
+ (e->value - device->absolute.min_y) * sh /
+ (device->absolute.max_y - device->absolute.min_y) +
+ device->output->y;
+ device->type |= E_EVDEV_ABSOLUTE_MOTION;
+ break;
+ }
+}
+
+static inline void
+_e_evdev_process_absolute_motion_touchpad(E_Evdev_Input_Device *device, struct input_event *e)
+{
+ /* FIXME: Make this configurable. */
+ const int touchpad_speed = 700;
+
+ switch (e->code)
+ {
+ case ABS_X:
+ e->value -= device->absolute.min_x;
+ if (device->absolute.rx)
+ device->absolute.rx = 0;
+ else
+ {
+ device->rel.dx =
+ (e->value - device->absolute.ox) * touchpad_speed /
+ (device->absolute.max_x - device->absolute.min_x);
+ }
+ device->absolute.ox = e->value;
+ device->type |= E_EVDEV_RELATIVE_MOTION;
+ break;
+ case ABS_Y:
+ e->value -= device->absolute.min_y;
+ if (device->absolute.ry)
+ device->absolute.ry = 0;
+ else
+ {
+ device->rel.dy =
+ (e->value - device->absolute.oy) * touchpad_speed /
+ (device->absolute.max_y - device->absolute.min_y);
+ }
+ device->absolute.oy = e->value;
+ device->type |= E_EVDEV_RELATIVE_MOTION;
+ break;
+ }
+}
+
+static inline void
+_e_evdev_process_relative(E_Evdev_Input_Device *device, struct input_event *e, unsigned int timestamp)
+{
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ switch (e->code)
+ {
+ case REL_X:
+ device->rel.dx += e->value;
+ device->type |= E_EVDEV_RELATIVE_MOTION;
+ break;
+ case REL_Y:
+ device->rel.dy += e->value;
+ device->type |= E_EVDEV_RELATIVE_MOTION;
+ break;
+ case REL_WHEEL:
+ e_input_notify_axis(&device->master->base.input_device, timestamp,
+ WL_INPUT_DEVICE_AXIS_VERTICAL_SCROLL, e->value);
+ break;
+ case REL_HWHEEL:
+ e_input_notify_axis(&device->master->base.input_device, timestamp,
+ WL_INPUT_DEVICE_AXIS_HORIZONTAL_SCROLL, e->value);
+ break;
+ }
+}
+
+static inline void
+_e_evdev_process_absolute(E_Evdev_Input_Device *device, struct input_event *e)
+{
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if (device->is_pad)
+ _e_evdev_process_absolute_motion_touchpad(device, e);
+ else if (device->is_mt)
+ _e_evdev_process_touch(device, e);
+ else
+ _e_evdev_process_absolute_motion(device, e);
+}
+
+static void
+_e_evdev_process_touch(E_Evdev_Input_Device *device, struct input_event *e)
+{
+ int sw, sh;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ sw = device->output->current->w;
+ sh = device->output->current->h;
+
+ switch (e->code)
+ {
+ case ABS_MT_SLOT:
+ device->mt.slot = e->value;
+ break;
+ case ABS_MT_TRACKING_ID:
+ if (e->value >= 0)
+ device->type |= E_EVDEV_ABSOLUTE_MT_DOWN;
+ else
+ device->type |= E_EVDEV_ABSOLUTE_MT_UP;
+ break;
+ case ABS_MT_POSITION_X:
+ device->mt.x[device->mt.slot] =
+ (e->value - device->absolute.min_x) * sw /
+ (device->absolute.max_x - device->absolute.min_x) +
+ device->output->x;
+ device->type |= E_EVDEV_ABSOLUTE_MT_MOTION;
+ break;
+ case ABS_MT_POSITION_Y:
+ device->mt.y[device->mt.slot] =
+ (e->value - device->absolute.min_y) * sh /
+ (device->absolute.max_y - device->absolute.min_y) +
+ device->output->y;
+ device->type |= E_EVDEV_ABSOLUTE_MT_MOTION;
+ break;
+ }
+}
+
+static void
+_e_evdev_process_events(E_Evdev_Input_Device *device, struct input_event *ev, int count)
+{
+ struct input_event *e, *end;
+ unsigned int timestamp = 0;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ device->type = 0;
+ e = ev;
+ end = e + count;
+ for (e = ev; e < end; e++)
+ {
+ timestamp = e->time.tv_sec * 1000 + e->time.tv_usec / 1000;
+ if (!_e_evdev_is_motion_event(e))
+ _e_evdev_flush_motion(device, timestamp);
+ switch (e->type)
+ {
+ case EV_REL:
+ _e_evdev_process_relative(device, e, timestamp);
+ break;
+ case EV_ABS:
+ _e_evdev_process_absolute(device, e);
+ break;
+ case EV_KEY:
+ _e_evdev_process_key(device, e, timestamp);
+ break;
+ }
+ }
+ _e_evdev_flush_motion(device, timestamp);
+}
+
+static int
+_e_evdev_cb_udev(int fd __UNUSED__, unsigned int mask __UNUSED__, void *data)
+{
+ E_Evdev_Input *master;
+ struct udev_device *dev;
+ const char *action;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if (!(master = data)) return 1;
+ if (!(dev = udev_monitor_receive_device(master->monitor)))
+ return 1;
+ if ((action = udev_device_get_action(dev)))
+ {
+ if (strncmp("event", udev_device_get_sysname(dev), 5) != 0)
+ return 0;
+ if (!strcmp(action, "add"))
+ _e_evdev_cb_device_added(dev, master);
+ else if (!strcmp(action, "remove"))
+ _e_evdev_cb_device_removed(dev, master);
+ }
+ udev_device_unref(dev);
+ return 0;
+}
+
+static void
+_e_evdev_cb_device_added(struct udev_device *dev, E_Evdev_Input *master)
+{
+ E_Compositor *comp;
+ const char *node, *seat;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if (!(seat = udev_device_get_property_value(dev, "ID_SEAT")))
+ seat = "seat0";
+ if (strcmp(seat, master->seat)) return;
+ comp = master->base.compositor;
+ node = udev_device_get_devnode(dev);
+ _e_evdev_input_device_create(master, comp->display, node);
+}
+
+static void
+_e_evdev_cb_device_removed(struct udev_device *dev, E_Evdev_Input *master)
+{
+ const char *node;
+ E_Evdev_Input_Device *device, *next;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ node = udev_device_get_devnode(dev);
+ wl_list_for_each_safe(device, next, &master->devices, link)
+ {
+ if (!strcmp(device->devnode, node))
+ {
+ wl_event_source_remove(device->source);
+ wl_list_remove(&device->link);
+ if (device->mtdev) mtdev_close_delete(device->mtdev);
+ close(device->fd);
+ free(device->devnode);
+ free(device);
+ break;
+ }
+ }
+}
+
+static int
+_e_evdev_cb_device_data(int fd, unsigned int mask __UNUSED__, void *data)
+{
+ E_Compositor *comp;
+ E_Evdev_Input_Device *device;
+ struct input_event ev[32];
+ int len = 0;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ device = data;
+ comp = device->master->base.compositor;
+ if (!comp->focus) return 1;
+
+ do
+ {
+ if (device->mtdev)
+ len = mtdev_get(device->mtdev, fd, ev,
+ (sizeof(ev) / sizeof(ev)[0]) *
+ sizeof(struct input_event));
+ else
+ len = read(fd, &ev, sizeof(ev));
+
+ if ((len < 0) || (len % sizeof(ev[0]) != 0))
+ return 1;
+
+ printf("Process Input Events Len: %d\n", len);
+
+ _e_evdev_process_events(device, ev, (len / sizeof(ev[0])));
+
+ } while (len > 0);
+
+ /* len = read(fd, &ev, sizeof(ev)); */
+
+ /* device->type = 0; */
+ /* e = ev; */
+ /* end = (void *)ev + len; */
+ /* for (e = ev; e < end; e++) */
+ /* { */
+ /* timestamp = (e->time.tv_sec * 1000 + e->time.tv_usec / 1000); */
+ /* if (!_e_evdev_is_motion_event(e)) */
+ /* _e_evdev_flush_motion(device, timestamp); */
+ /* switch (e->type) */
+ /* { */
+ /* case EV_REL: */
+ /* _e_evdev_process_relative_motion(device, e); */
+ /* break; */
+ /* case EV_ABS: */
+ /* _e_evdev_process_absolute(device, e); */
+ /* break; */
+ /* case EV_KEY: */
+ /* _e_evdev_process_key(device, e, timestamp); */
+ /* break; */
+ /* } */
+ /* } */
+
+ /* _e_evdev_flush_motion(device, timestamp); */
+
+ return 1;
+}
diff --git a/src/modules/wl_drm/e_mod_main.c b/src/modules/wl_drm/e_mod_main.c
new file mode 100644
index 0000000000..a665b45dc9
--- /dev/null
+++ b/src/modules/wl_drm/e_mod_main.c
@@ -0,0 +1,713 @@
+#include "e.h"
+#include "e_mod_main.h"
+
+/* local function prototypes */
+static void _cb_tty(E_Compositor *comp, int event);
+static int _cb_drm_input(int fd, unsigned int mask __UNUSED__, void *data __UNUSED__);
+static int _cb_drm_udev_event(int fd __UNUSED__, unsigned int mask __UNUSED__, void *data);
+static void _cb_drm_page_flip(int fd __UNUSED__, unsigned int frame __UNUSED__, unsigned int sec, unsigned int usec, void *data);
+static void _cb_drm_vblank(int fd __UNUSED__, unsigned int frame __UNUSED__, unsigned int sec __UNUSED__, unsigned int usec __UNUSED__, void *data);
+static Eina_Bool _egl_init(E_Drm_Compositor *dcomp, struct udev_device *dev);
+static void _sprites_init(E_Drm_Compositor *dcomp);
+static void _sprites_shutdown(E_Drm_Compositor *dcomp);
+static Eina_Bool _outputs_init(E_Drm_Compositor *dcomp, unsigned int conn, struct udev_device *drm_device);
+static Eina_Bool _output_create(E_Drm_Compositor *dcomp, drmModeRes *res, drmModeConnector *conn, int x, int y, struct udev_device *drm_device);
+static void _outputs_update(E_Drm_Compositor *dcomp, struct udev_device *drm_device);
+static Eina_Bool _udev_event_is_hotplug(E_Drm_Compositor *dcomp, struct udev_device *dev);
+
+/* local variables */
+static drmModeModeInfo builtin_mode =
+{
+ 63500, /* clock */
+ 1024, 1072, 1176, 1328, 0,
+ 768, 771, 775, 798, 0,
+ 59920,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC,
+ 0,
+ "1024x768"
+};
+
+/* external variables */
+E_Drm_Compositor *_drm_comp = NULL;
+
+EAPI E_Module_Api e_modapi = { E_MODULE_API_VERSION, "Drm" };
+
+EAPI void *
+e_modapi_init(E_Module *m)
+{
+ struct wl_display *disp;
+ struct udev_enumerate *ue;
+ struct udev_list_entry *entry;
+ struct udev_device *drm_dev = NULL;
+ struct wl_event_loop *loop;
+ const char *seat = NULL;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ /* try to get the wayland display */
+ if (!(disp = (struct wl_display *)m->data)) return NULL;
+
+ /* allocate space for the drm compositor */
+ if (!(_drm_comp = malloc(sizeof(E_Drm_Compositor)))) return NULL;
+
+ memset(_drm_comp, 0, sizeof(E_Drm_Compositor));
+
+ if (!(_drm_comp->udev = udev_new()))
+ {
+ free(_drm_comp);
+ return NULL;
+ }
+
+ _drm_comp->base.display = disp;
+ if (!(_drm_comp->tty = e_tty_create(&_drm_comp->base, _cb_tty, 0)))
+ {
+ free(_drm_comp);
+ return NULL;
+ }
+
+ ue = udev_enumerate_new(_drm_comp->udev);
+ udev_enumerate_add_match_subsystem(ue, "drm");
+ udev_enumerate_add_match_sysname(ue, "card[0-9]*");
+
+ udev_enumerate_scan_devices(ue);
+ udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(ue))
+ {
+ struct udev_device *dev;
+ const char *path = NULL;
+
+ path = udev_list_entry_get_name(entry);
+ dev = udev_device_new_from_syspath(_drm_comp->udev, path);
+ if (!(seat = udev_device_get_property_value(dev, "ID_SEAT")))
+ seat = "seat0";
+ if (!strcmp(seat, "seat0"))
+ {
+ drm_dev = dev;
+ break;
+ }
+ udev_device_unref(dev);
+ }
+
+ if (!drm_dev)
+ {
+ free(_drm_comp);
+ return NULL;
+ }
+
+ /* init egl */
+ if (!_egl_init(_drm_comp, drm_dev))
+ {
+ free(_drm_comp);
+ return NULL;
+ }
+
+ /* _drm_comp->base.destroy = _cb_destroy; */
+ _drm_comp->base.focus = EINA_TRUE;
+
+ _drm_comp->prev_state = E_COMPOSITOR_STATE_ACTIVE;
+
+ glGenFramebuffers(1, &_drm_comp->base.fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, _drm_comp->base.fbo);
+
+ if (!e_compositor_init(&_drm_comp->base, disp))
+ {
+ free(_drm_comp);
+ return NULL;
+ }
+
+ wl_list_init(&_drm_comp->sprites);
+ _sprites_init(_drm_comp);
+
+ if (!_outputs_init(_drm_comp, 0, drm_dev))
+ {
+ free(_drm_comp);
+ return NULL;
+ }
+
+ udev_device_unref(drm_dev);
+ udev_enumerate_unref(ue);
+
+ e_evdev_input_create(&_drm_comp->base, _drm_comp->udev, seat);
+
+ loop = wl_display_get_event_loop(_drm_comp->base.display);
+ _drm_comp->drm_source =
+ wl_event_loop_add_fd(loop, _drm_comp->drm.fd, WL_EVENT_READABLE,
+ _cb_drm_input, _drm_comp);
+
+ _drm_comp->udev_monitor =
+ udev_monitor_new_from_netlink(_drm_comp->udev, "udev");
+ if (!_drm_comp->udev_monitor)
+ {
+ free(_drm_comp);
+ return NULL;
+ }
+ udev_monitor_filter_add_match_subsystem_devtype(_drm_comp->udev_monitor,
+ "drm", NULL);
+
+ _drm_comp->udev_drm_source =
+ wl_event_loop_add_fd(loop, udev_monitor_get_fd(_drm_comp->udev_monitor),
+ WL_EVENT_READABLE, _cb_drm_udev_event, _drm_comp);
+ if (udev_monitor_enable_receiving(_drm_comp->udev_monitor) < 0)
+ {
+ free(_drm_comp);
+ return NULL;
+ }
+
+ return &_drm_comp->base;
+}
+
+EAPI int
+e_modapi_shutdown(E_Module *m __UNUSED__)
+{
+ E_Input_Device *input, *next;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ e_compositor_shutdown(&_drm_comp->base);
+ gbm_device_destroy(_drm_comp->gbm);
+ _sprites_shutdown(_drm_comp);
+ drmDropMaster(_drm_comp->drm.fd);
+ e_tty_destroy(_drm_comp->tty);
+
+ wl_list_for_each_safe(input, next, &_drm_comp->base.inputs, link)
+ e_evdev_input_destroy(input);
+
+ free(_drm_comp);
+ _drm_comp = NULL;
+
+ return 1;
+}
+
+EAPI int
+e_modapi_save(E_Module *m __UNUSED__)
+{
+ return 1;
+}
+
+/* local function prototypes */
+static void
+_cb_tty(E_Compositor *comp, int event)
+{
+ E_Output *output;
+ E_Drm_Output *doutput;
+ E_Sprite *sprite;
+ E_Input_Device *input;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ switch (event)
+ {
+ case 0: // TTY_ENTER_VT
+ comp->focus = EINA_TRUE;
+ if (drmSetMaster(_drm_comp->drm.fd))
+ {
+ printf("Failed to set master: %m\n");
+ wl_display_terminate(comp->display);
+ }
+ comp->state = _drm_comp->prev_state;
+ e_drm_output_set_modes(_drm_comp);
+ e_compositor_damage_all(comp);
+ wl_list_for_each(input, &comp->inputs, link)
+ e_evdev_add_devices(_drm_comp->udev, input);
+ break;
+ case 1: // TTY_LEAVE_VT
+ comp->focus = EINA_FALSE;
+ _drm_comp->prev_state = comp->state;
+ comp->state = E_COMPOSITOR_STATE_SLEEPING;
+ wl_list_for_each(output, &comp->outputs, link)
+ {
+ output->repaint_needed = EINA_FALSE;
+ e_drm_output_set_cursor(output, NULL);
+ }
+ doutput =
+ container_of(comp->outputs.next, E_Drm_Output, base.link);
+
+ wl_list_for_each(sprite, &_drm_comp->sprites, link)
+ drmModeSetPlane(_drm_comp->drm.fd, sprite->plane_id,
+ doutput->crtc_id, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ wl_list_for_each(input, &comp->inputs, link)
+ e_evdev_remove_devices(input);
+
+ if (drmDropMaster(_drm_comp->drm.fd < 0))
+ printf("Failed to drop master: %m\n");
+
+ break;
+ }
+}
+
+static int
+_cb_drm_input(int fd, unsigned int mask __UNUSED__, void *data __UNUSED__)
+{
+ drmEventContext ectxt;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ memset(&ectxt, 0, sizeof(ectxt));
+
+ ectxt.version = DRM_EVENT_CONTEXT_VERSION;
+ ectxt.page_flip_handler = _cb_drm_page_flip;
+ ectxt.vblank_handler = _cb_drm_vblank;
+ drmHandleEvent(fd, &ectxt);
+
+ return 1;
+}
+
+static int
+_cb_drm_udev_event(int fd __UNUSED__, unsigned int mask __UNUSED__, void *data)
+{
+ E_Drm_Compositor *dcomp;
+ struct udev_device *event;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if (!(dcomp = data)) return 1;
+ event = udev_monitor_receive_device(dcomp->udev_monitor);
+ if (_udev_event_is_hotplug(dcomp, event))
+ _outputs_update(dcomp, event);
+ udev_device_unref(event);
+ return 1;
+}
+
+static void
+_cb_drm_page_flip(int fd __UNUSED__, unsigned int frame __UNUSED__, unsigned int sec, unsigned int usec, void *data)
+{
+ E_Drm_Output *doutput;
+ E_Drm_Compositor *dcomp;
+ unsigned int msecs;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if (!(doutput = data)) return;
+ dcomp = (E_Drm_Compositor *)doutput->base.compositor;
+
+ if (doutput->scanout_buffer)
+ {
+ e_buffer_post_release(doutput->scanout_buffer);
+ wl_list_remove(&doutput->scanout_buffer_destroy_listener.link);
+ doutput->scanout_buffer = NULL;
+ drmModeRmFB(dcomp->drm.fd, doutput->fs_surf_fb_id);
+ doutput->fs_surf_fb_id = 0;
+ }
+
+ if (doutput->pending_scanout_buffer)
+ {
+ doutput->scanout_buffer = doutput->pending_scanout_buffer;
+ wl_list_remove(&doutput->pending_scanout_buffer_destroy_listener.link);
+ wl_list_insert(doutput->scanout_buffer->resource.destroy_listener_list.prev,
+ &doutput->scanout_buffer_destroy_listener.link);
+ doutput->pending_scanout_buffer = NULL;
+ doutput->fs_surf_fb_id = doutput->pending_fs_surf_fb_id;
+ doutput->pending_fs_surf_fb_id = 0;
+ }
+
+ msecs = sec * 1000 + usec / 1000;
+ e_output_finish_frame(&doutput->base, msecs);
+}
+
+static void
+_cb_drm_vblank(int fd __UNUSED__, unsigned int frame __UNUSED__, unsigned int sec __UNUSED__, unsigned int usec __UNUSED__, void *data)
+{
+ E_Sprite *es;
+ E_Drm_Compositor *dcomp;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if (!(es = data)) return;
+ dcomp = es->compositor;
+
+ if (es->surface)
+ {
+ e_buffer_post_release(es->surface->buffer);
+ wl_list_remove(&es->destroy_listener.link);
+ es->surface = NULL;
+ drmModeRmFB(dcomp->drm.fd, es->fb_id);
+ es->fb_id = 0;
+ }
+
+ if (es->pending_surface)
+ {
+ wl_list_remove(&es->pending_destroy_listener.link);
+ wl_list_insert(es->pending_surface->buffer->resource.destroy_listener_list.prev,
+ &es->destroy_listener.link);
+ es->surface = es->pending_surface;
+ es->pending_surface = NULL;
+ es->fb_id = es->pending_fb_id;
+ es->pending_fb_id = 0;
+ }
+}
+
+static Eina_Bool
+_egl_init(E_Drm_Compositor *dcomp, struct udev_device *dev)
+{
+ EGLint maj, min;
+ const char *ext, *fname, *snum;
+ int fd = 0;
+ static const EGLint ctxt_att[] =
+ {
+ EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE
+ };
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if ((snum = udev_device_get_sysnum(dev)))
+ dcomp->drm.id = atoi(snum);
+ if ((!snum) || (dcomp->drm.id < 0))
+ return EINA_FALSE;
+
+ fname = udev_device_get_devnode(dev);
+ if (!(fd = open(fname, (O_RDWR | O_CLOEXEC))))
+ return EINA_FALSE;
+
+ dcomp->drm.fd = fd;
+ dcomp->gbm = gbm_create_device(dcomp->drm.fd);
+
+// dcomp->base.egl_display = eglGetDisplay(dcomp->base.display);
+ dcomp->base.egl_display = eglGetDisplay(dcomp->gbm);
+ if (!dcomp->base.egl_display)
+ return EINA_FALSE;
+
+ if (!eglInitialize(dcomp->base.egl_display, &maj, &min))
+ return EINA_FALSE;
+
+ ext = eglQueryString(dcomp->base.egl_display, EGL_EXTENSIONS);
+ if (!strstr(ext, "EGL_KHR_surfaceless_gles2"))
+ return EINA_FALSE;
+
+ if (!eglBindAPI(EGL_OPENGL_ES_API)) return EINA_FALSE;
+
+ dcomp->base.egl_context =
+ eglCreateContext(dcomp->base.egl_display, NULL, EGL_NO_CONTEXT, ctxt_att);
+ if (!dcomp->base.egl_context) return EINA_FALSE;
+
+ if (!eglMakeCurrent(dcomp->base.egl_display, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, dcomp->base.egl_context))
+ return EINA_FALSE;
+
+ return EINA_TRUE;
+}
+
+static void
+_sprites_init(E_Drm_Compositor *dcomp)
+{
+ E_Sprite *es;
+ drmModePlaneRes *res;
+ drmModePlane *plane;
+ unsigned int i = 0;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if (!(res = drmModeGetPlaneResources(dcomp->drm.fd)))
+ return;
+
+ for (i = 0; i < res->count_planes; i++)
+ {
+ if (!(plane = drmModeGetPlane(dcomp->drm.fd, res->planes[i])))
+ continue;
+ if (!(es = e_sprite_create(dcomp, plane)))
+ {
+ free(plane);
+ continue;
+ }
+ drmModeFreePlane(plane);
+ wl_list_insert(&dcomp->sprites, &es->link);
+ }
+ free(res->planes);
+ free(res);
+}
+
+static void
+_sprites_shutdown(E_Drm_Compositor *dcomp)
+{
+ E_Drm_Output *doutput;
+ E_Sprite *es, *next;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ doutput = container_of(dcomp->base.outputs.next, E_Drm_Output, base.link);
+
+ wl_list_for_each_safe(es, next, &dcomp->sprites, link)
+ {
+ drmModeSetPlane(dcomp->drm.fd, es->plane_id, doutput->crtc_id,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ drmModeRmFB(dcomp->drm.fd, es->fb_id);
+ free(es);
+ }
+}
+
+static Eina_Bool
+_outputs_init(E_Drm_Compositor *dcomp, unsigned int conn, struct udev_device *drm_device)
+{
+ drmModeConnector *connector;
+ drmModeRes *res;
+ int i = 0, x = 0, y = 0;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if (!(res = drmModeGetResources(dcomp->drm.fd)))
+ return EINA_FALSE;
+
+ if (!(dcomp->crtcs = calloc(res->count_crtcs, sizeof(unsigned int))))
+ {
+ drmModeFreeResources(res);
+ return EINA_FALSE;
+ }
+
+ dcomp->num_crtcs = res->count_crtcs;
+ memcpy(dcomp->crtcs, res->crtcs, sizeof(unsigned int) * dcomp->num_crtcs);
+
+ for (i = 0; i < res->count_connectors; i++)
+ {
+ if (!(connector =
+ drmModeGetConnector(dcomp->drm.fd, res->connectors[i])))
+ continue;
+ if ((connector->connection == DRM_MODE_CONNECTED) &&
+ ((conn == 0) || (connector->connector_id == conn)))
+ {
+ if (!_output_create(dcomp, res, connector, x, y, drm_device))
+ {
+ drmModeFreeConnector(connector);
+ continue;
+ }
+ x += container_of(dcomp->base.outputs.prev, E_Output,
+ link)->current->w;
+ }
+ drmModeFreeConnector(connector);
+ }
+
+ if (wl_list_empty(&dcomp->base.outputs))
+ {
+ drmModeFreeResources(res);
+ return EINA_FALSE;
+ }
+
+ drmModeFreeResources(res);
+
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_output_create(E_Drm_Compositor *dcomp, drmModeRes *res, drmModeConnector *conn, int x, int y, struct udev_device *drm_device)
+{
+ E_Drm_Output *output;
+ E_Drm_Output_Mode *mode, *next;
+ drmModeEncoder *encoder;
+ int i = 0, ret = 0;
+ unsigned int hdl, stride;
+
+ if (!(encoder = drmModeGetEncoder(dcomp->drm.fd, conn->encoders[0])))
+ return EINA_FALSE;
+
+ for (i = 0; i < res->count_crtcs; i++)
+ {
+ if ((encoder->possible_crtcs & (1 << i)) &&
+ !(dcomp->crtc_alloc & (1 << res->crtcs[i])))
+ break;
+ }
+ if (i == res->count_crtcs)
+ {
+ drmModeFreeEncoder(encoder);
+ return EINA_FALSE;
+ }
+
+ if (!(output = malloc(sizeof(E_Drm_Output))))
+ {
+ drmModeFreeEncoder(encoder);
+ return EINA_FALSE;
+ }
+
+ memset(output, 0, sizeof(E_Drm_Output));
+
+ output->fb_id[0] = -1;
+ output->fb_id[1] = -1;
+ output->base.subpixel = e_drm_output_subpixel_convert(conn->subpixel);
+ output->base.make = "unknown";
+ output->base.model = "unknown";
+ wl_list_init(&output->base.modes);
+
+ output->crtc_id = res->crtcs[i];
+ dcomp->crtc_alloc |= (1 << output->crtc_id);
+ output->conn_id = conn->connector_id;
+ dcomp->conn_alloc |= (1 << output->conn_id);
+
+ output->orig_crtc = drmModeGetCrtc(dcomp->drm.fd, output->crtc_id);
+ drmModeFreeEncoder(encoder);
+
+ for (i = 0; i < conn->count_modes; i++)
+ {
+ if (!e_drm_output_add_mode(output, &conn->modes[i]))
+ goto efree;
+ }
+
+ if (conn->count_modes == 0)
+ {
+ if (!e_drm_output_add_mode(output, &builtin_mode))
+ goto efree;
+ }
+
+ mode = container_of(output->base.modes.next, E_Drm_Output_Mode, base.link);
+ output->base.current = &mode->base;
+ mode->base.flags = (WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED);
+
+ glGenRenderbuffers(2, output->rbo);
+
+ for (i = 0; i < 2; i++)
+ {
+ glBindRenderbuffer(GL_RENDERBUFFER, output->rbo[i]);
+ output->bo[i] =
+ gbm_bo_create(dcomp->gbm, output->base.current->w,
+ output->base.current->h, GBM_BO_FORMAT_XRGB8888,
+ (GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING));
+ if (!output->bo[i]) goto ebuffs;
+ output->image[i] =
+ dcomp->base.create_image(dcomp->base.egl_display, NULL,
+ EGL_NATIVE_PIXMAP_KHR, output->bo[i], NULL);
+ if (!output->image[i]) goto ebuffs;
+ dcomp->base.image_target_renderbuffer_storage(GL_RENDERBUFFER,
+ output->image[i]);
+ stride = gbm_bo_get_pitch(output->bo[i]);
+ hdl = gbm_bo_get_handle(output->bo[i]).u32;
+ ret = drmModeAddFB(dcomp->drm.fd, output->base.current->w,
+ output->base.current->h, 24, 32, stride, hdl,
+ &output->fb_id[i]);
+ if (ret) goto ebuffs;
+ }
+
+ output->current = 0;
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER, output->rbo[output->current]);
+ ret = drmModeSetCrtc(dcomp->drm.fd, output->crtc_id,
+ output->fb_id[output->current ^ 1], 0, 0,
+ &output->conn_id, 1, &mode->info);
+ if (ret) goto efb;
+
+ /* TODO: backlight init */
+
+ e_output_init(&output->base, &dcomp->base, x, y,
+ conn->mmWidth, conn->mmHeight, 0);
+
+ wl_list_insert(dcomp->base.outputs.prev, &output->base.link);
+
+ output->scanout_buffer_destroy_listener.func =
+ e_drm_output_scanout_buffer_destroy;
+ output->pending_scanout_buffer_destroy_listener.func =
+ e_drm_output_pending_scanout_buffer_destroy;
+
+ output->pending_fs_surf_fb_id = 0;
+ output->base.repaint = e_drm_output_repaint;
+ output->base.destroy = e_drm_output_destroy;
+ output->base.assign_planes = e_drm_output_assign_planes;
+ output->base.set_dpms = e_drm_output_set_dpms;
+
+ return EINA_TRUE;
+
+efb:
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER, 0);
+
+ebuffs:
+ for (i = 0; i < 2; i++)
+ {
+ if ((int)output->fb_id[i] != -1)
+ drmModeRmFB(dcomp->drm.fd, output->fb_id[i]);
+ if (output->image[i])
+ dcomp->base.destroy_image(dcomp->base.egl_display, output->image[i]);
+ if (output->bo[i]) gbm_bo_destroy(output->bo[i]);
+ }
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ glDeleteRenderbuffers(2, output->rbo);
+
+efree:
+ wl_list_for_each_safe(mode, next, &output->base.modes, base.link)
+ {
+ wl_list_remove(&mode->base.link);
+ free(mode);
+ }
+ drmModeFreeCrtc(output->orig_crtc);
+ dcomp->crtc_alloc &= ~(1 << output->crtc_id);
+ dcomp->conn_alloc &= ~(1 << output->conn_id);
+ free(output);
+
+ return EINA_TRUE;
+}
+
+static void
+_outputs_update(E_Drm_Compositor *dcomp, struct udev_device *drm_device)
+{
+ drmModeConnector *conn;
+ drmModeRes *res;
+ E_Drm_Output *doutput, *next;
+ int x = 0, y = 0;
+ int xo = 0, yo = 0;
+ unsigned int connected = 0, disconnects = 0;
+ int i = 0;
+
+ if (!(res = drmModeGetResources(dcomp->drm.fd)))
+ return;
+
+ for (i = 0; i < res->count_connectors; i++)
+ {
+ int conn_id;
+
+ conn_id = res->connectors[i];
+ if (!(conn = drmModeGetConnector(dcomp->drm.fd, conn_id)))
+ continue;
+ if (conn->connection != DRM_MODE_CONNECTED)
+ {
+ drmModeFreeConnector(conn);
+ continue;
+ }
+ connected |= (1 << conn_id);
+ if (!(dcomp->conn_alloc & (1 << conn_id)))
+ {
+ E_Output *last;
+
+ last = container_of(dcomp->base.outputs.prev, E_Output, link);
+ if (!wl_list_empty(&dcomp->base.outputs))
+ x = last->x + last->current->w;
+ else
+ x = 0;
+ y = 0;
+ _output_create(dcomp, res, conn, x, y, drm_device);
+ }
+ drmModeFreeConnector(conn);
+ }
+ drmModeFreeResources(res);
+
+ if ((disconnects = dcomp->conn_alloc & ~connected))
+ {
+ wl_list_for_each_safe(doutput, next, &dcomp->base.outputs, base.link)
+ {
+ if ((xo != 0) || (yo != 0))
+ e_output_move(&doutput->base, doutput->base.x - xo,
+ doutput->base.y - yo);
+ if (disconnects & (1 << doutput->conn_id))
+ {
+ disconnects &= ~(1 << doutput->conn_id);
+ xo += doutput->base.current->w;
+ e_drm_output_destroy(&doutput->base);
+ }
+ }
+ }
+
+ if (dcomp->conn_alloc == 0)
+ wl_display_terminate(dcomp->base.display);
+}
+
+static Eina_Bool
+_udev_event_is_hotplug(E_Drm_Compositor *dcomp, struct udev_device *dev)
+{
+ const char *snum, *val;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ snum = udev_device_get_sysnum(dev);
+ if ((!snum) || (atoi(snum) != dcomp->drm.id))
+ return EINA_FALSE;
+
+ if (!(val = udev_device_get_property_value(dev, "HOTPLUG")))
+ return EINA_FALSE;
+
+ if (!strcmp(val, "1")) return EINA_TRUE;
+
+ return EINA_FALSE;
+}
diff --git a/src/modules/wl_drm/e_mod_main.h b/src/modules/wl_drm/e_mod_main.h
new file mode 100644
index 0000000000..ee7a62fec3
--- /dev/null
+++ b/src/modules/wl_drm/e_mod_main.h
@@ -0,0 +1,204 @@
+#ifndef E_MOD_MAIN_H
+# define E_MOD_MAIN_H
+
+# define DLOGFNS 1
+
+# ifdef DLOGFNS
+# include <stdio.h>
+# define DLOGFN(fl, ln, fn) printf("-E-DRM: %25s: %5i - %s\n", fl, ln, fn);
+# else
+# define DLOGFN(fl, ln, fn)
+# endif
+
+# include <libudev.h>
+# include <gbm.h>
+# include <xf86drm.h>
+# include <xf86drmMode.h>
+# include <drm_fourcc.h>
+# include <termios.h>
+# include <linux/kd.h>
+# include <linux/vt.h>
+# include <linux/major.h>
+# include <sys/ioctl.h>
+# include <wayland-server.h>
+# include <mtdev.h>
+
+typedef void (*tty_vt_func_t)(E_Compositor *comp, int event);
+typedef struct _E_Tty E_Tty;
+typedef struct _E_Drm_Compositor E_Drm_Compositor;
+typedef struct _E_Sprite E_Sprite;
+typedef struct _E_Drm_Output_Mode E_Drm_Output_Mode;
+typedef struct _E_Drm_Output E_Drm_Output;
+typedef struct _E_Evdev_Input E_Evdev_Input;
+typedef struct _E_Evdev_Input_Device E_Evdev_Input_Device;
+
+struct _E_Drm_Compositor
+{
+ E_Compositor base;
+
+ struct udev *udev;
+ struct wl_event_source *drm_source;
+ struct udev_monitor *udev_monitor;
+ struct wl_event_source *udev_drm_source;
+ struct
+ {
+ int id, fd;
+ } drm;
+ struct gbm_device *gbm;
+ unsigned int *crtcs;
+ int num_crtcs;
+ unsigned int crtc_alloc;
+ unsigned int conn_alloc;
+
+ E_Tty *tty;
+
+ struct wl_list sprites;
+ Eina_Bool sprites_broken;
+ E_Compositor_State prev_state;
+};
+
+struct _E_Tty
+{
+ E_Compositor *comp;
+
+ int fd;
+ struct termios term_attribs;
+ struct wl_event_source *input_source;
+ struct wl_event_source *vt_source;
+ tty_vt_func_t vt_func;
+ int vt, start_vt;
+ Eina_Bool has_vt : 1;
+};
+
+struct _E_Sprite
+{
+ struct wl_list link;
+
+ unsigned int fb_id;
+ unsigned int pending_fb_id;
+
+ E_Surface *surface, *pending_surface;
+ E_Drm_Compositor *compositor;
+
+ struct wl_listener destroy_listener;
+ struct wl_listener pending_destroy_listener;
+
+ unsigned int possible_crtcs;
+ unsigned int plane_id;
+
+ int sx, sy;
+ unsigned int sw, sh;
+ unsigned int dx, dy, dw, dh;
+
+ unsigned int format_count;
+ unsigned int formats[];
+};
+
+struct _E_Drm_Output_Mode
+{
+ E_Output_Mode base;
+ drmModeModeInfo info;
+};
+
+struct _E_Drm_Output
+{
+ E_Output base;
+
+ unsigned int crtc_id;
+ unsigned int conn_id;
+ drmModeCrtcPtr orig_crtc;
+ GLuint rbo[2];
+ unsigned int fb_id[2];
+
+ EGLImageKHR image[2];
+ struct gbm_bo *bo[2];
+
+ unsigned int current;
+ unsigned int fs_surf_fb_id;
+ unsigned int pending_fs_surf_fb_id;
+
+ struct wl_buffer *scanout_buffer, *pending_scanout_buffer;
+ struct wl_listener scanout_buffer_destroy_listener;
+ struct wl_listener pending_scanout_buffer_destroy_listener;
+
+ /* TODO: backlight */
+};
+
+struct _E_Evdev_Input
+{
+ E_Input_Device base;
+ struct wl_list devices;
+ struct udev_monitor *monitor;
+ char *seat;
+};
+
+struct _E_Evdev_Input_Device
+{
+ E_Evdev_Input *master;
+ struct wl_list link;
+ struct wl_event_source *source;
+ E_Output *output;
+ char *devnode;
+ int fd;
+ struct
+ {
+ int min_x, max_x, min_y, max_y;
+ int ox, oy, rx, ry;
+ int x, y;
+ } absolute;
+ struct
+ {
+ int slot;
+ int x[16], y[16];
+ } mt;
+ struct mtdev *mtdev;
+ struct
+ {
+ int dx, dy;
+ } rel;
+
+ int type;
+
+ Eina_Bool is_pad : 1;
+ Eina_Bool is_mt : 1;
+};
+
+extern E_Drm_Compositor *_comp;
+
+EAPI extern E_Module_Api e_modapi;
+
+EAPI void *e_modapi_init(E_Module *m);
+EAPI int e_modapi_shutdown(E_Module *m);
+EAPI int e_modapi_save(E_Module *m);
+
+EINTERN E_Sprite *e_sprite_create(E_Drm_Compositor *dcomp, drmModePlane *plane);
+EINTERN Eina_Bool e_sprite_crtc_supported(E_Output *output, unsigned int supported);
+
+EINTERN E_Tty *e_tty_create(E_Compositor *comp, tty_vt_func_t vt_func, int tty);
+EINTERN void e_tty_destroy(E_Tty *et);
+
+EINTERN int e_drm_output_subpixel_convert(int value);
+EINTERN Eina_Bool e_drm_output_add_mode(E_Drm_Output *output, drmModeModeInfo *info);
+EINTERN void e_drm_output_set_modes(E_Drm_Compositor *dcomp);
+EINTERN void e_drm_output_scanout_buffer_destroy(struct wl_listener *listener, struct wl_resource *resource __UNUSED__, unsigned int timestamp __UNUSED__);
+EINTERN void e_drm_output_pending_scanout_buffer_destroy(struct wl_listener *listener, struct wl_resource *resource __UNUSED__, unsigned int timestamp __UNUSED__);
+EINTERN void e_drm_output_repaint(E_Output *base, pixman_region32_t *damage);
+EINTERN void e_drm_output_destroy(E_Output *base);
+EINTERN void e_drm_output_assign_planes(E_Output *base);
+EINTERN void e_drm_output_set_dpms(E_Output *base, E_Dpms_Level level);
+EINTERN Eina_Bool e_drm_output_prepare_scanout_surface(E_Drm_Output *output);
+EINTERN void e_drm_output_disable_sprites(E_Output *base);
+EINTERN drmModePropertyPtr e_drm_output_get_property(int fd, drmModeConnectorPtr conn, const char *name);
+EINTERN int e_drm_output_prepare_overlay_surface(E_Output *base, E_Surface *es, pixman_region32_t *overlap);
+EINTERN Eina_Bool e_drm_output_surface_transform_supported(E_Surface *es);
+EINTERN Eina_Bool e_drm_output_surface_overlap_supported(E_Output *base __UNUSED__, pixman_region32_t *overlap);
+EINTERN Eina_Bool e_drm_output_surface_format_supported(E_Sprite *s, unsigned int format);
+EINTERN void e_drm_output_set_cursor_region(E_Output *output, E_Input_Device *device, pixman_region32_t *overlap);
+EINTERN Eina_Bool e_drm_output_set_cursor(E_Output *output, E_Input_Device *device);
+
+EINTERN void e_evdev_add_devices(struct udev *udev, E_Input_Device *base);
+EINTERN void e_evdev_remove_devices(E_Input_Device *base);
+EINTERN void e_evdev_input_create(E_Compositor *comp, struct udev *udev, const char *seat);
+EINTERN void e_evdev_input_destroy(E_Input_Device *base);
+
+#endif
diff --git a/src/modules/wl_drm/e_sprite.c b/src/modules/wl_drm/e_sprite.c
new file mode 100644
index 0000000000..90a3749b71
--- /dev/null
+++ b/src/modules/wl_drm/e_sprite.c
@@ -0,0 +1,88 @@
+#include "e.h"
+#include "e_mod_main.h"
+
+/* local function prototypes */
+static void _e_sprite_cb_buffer_destroy(struct wl_listener *listener, struct wl_resource *resource __UNUSED__, unsigned int timestamp __UNUSED__);
+static void _e_sprite_cb_pending_buffer_destroy(struct wl_listener *listener, struct wl_resource *resource __UNUSED__, unsigned int timestamp __UNUSED__);
+
+/* local variables */
+/* wayland interfaces */
+/* external variables */
+
+EINTERN E_Sprite *
+e_sprite_create(E_Drm_Compositor *dcomp, drmModePlane *plane)
+{
+ E_Sprite *es;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if (!plane) return NULL;
+
+ es = malloc(sizeof(E_Sprite) + ((sizeof(unsigned int)) * plane->count_formats));
+ if (!es) return NULL;
+
+ memset(es, 0, sizeof(E_Sprite));
+
+ es->compositor = dcomp;
+ es->possible_crtcs = plane->possible_crtcs;
+ es->plane_id = plane->plane_id;
+ es->surface = NULL;
+ es->pending_surface = NULL;
+ es->fb_id = 0;
+ es->pending_fb_id = 0;
+ es->destroy_listener.func = _e_sprite_cb_buffer_destroy;
+ es->pending_destroy_listener.func = _e_sprite_cb_pending_buffer_destroy;
+ es->format_count = plane->count_formats;
+ memcpy(es->formats, plane->formats,
+ plane->count_formats * sizeof(plane->formats[0]));
+
+ return es;
+}
+
+EINTERN Eina_Bool
+e_sprite_crtc_supported(E_Output *output, unsigned int supported)
+{
+ E_Compositor *comp;
+ E_Drm_Compositor *dcomp;
+ E_Drm_Output *doutput;
+ int crtc = 0;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ comp = output->compositor;
+ dcomp = (E_Drm_Compositor *)comp;
+ doutput = (E_Drm_Output *)output;
+
+ for (crtc = 0; crtc < dcomp->num_crtcs; crtc++)
+ {
+ if (dcomp->crtcs[crtc] != doutput->crtc_id)
+ continue;
+ if (supported & (1 << crtc))
+ return EINA_TRUE;
+ }
+
+ return EINA_FALSE;
+}
+
+/* local functions */
+static void
+_e_sprite_cb_buffer_destroy(struct wl_listener *listener, struct wl_resource *resource __UNUSED__, unsigned int timestamp __UNUSED__)
+{
+ E_Sprite *es;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ es = container_of(listener, E_Sprite, destroy_listener);
+ es->surface = NULL;
+}
+
+static void
+_e_sprite_cb_pending_buffer_destroy(struct wl_listener *listener, struct wl_resource *resource __UNUSED__, unsigned int timestamp __UNUSED__)
+{
+ E_Sprite *es;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ es = container_of(listener, E_Sprite, pending_destroy_listener);
+ es->pending_surface = NULL;
+}
diff --git a/src/modules/wl_drm/e_tty.c b/src/modules/wl_drm/e_tty.c
new file mode 100644
index 0000000000..40902b780e
--- /dev/null
+++ b/src/modules/wl_drm/e_tty.c
@@ -0,0 +1,201 @@
+#include "e.h"
+#include "e_mod_main.h"
+
+/* local function prototypes */
+static int _e_tty_open_vt(E_Tty *et);
+static int _e_tty_cb_input(int fd __UNUSED__, uint32_t mask __UNUSED__, void *data);
+static int _e_tty_cb_handle(int sig __UNUSED__, void *data);
+
+EINTERN E_Tty *
+e_tty_create(E_Compositor *comp, tty_vt_func_t vt_func, int tty)
+{
+ E_Tty *et = NULL;
+ struct stat buff;
+ struct termios raw_attribs;
+ struct wl_event_loop *loop;
+ struct vt_mode mode;
+ int ret = 0;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if (!(et = malloc(sizeof(E_Tty))))
+ {
+ e_error_message_show(_("Could not allocate space for E_Tty\n"));
+ return NULL;
+ }
+
+ memset(et, 0, sizeof(E_Tty));
+
+ et->comp = comp;
+ et->vt_func = vt_func;
+ if (tty > 0)
+ {
+ char fname[16];
+
+ snprintf(fname, sizeof(fname), "/dev/tty%d", tty);
+ printf("Using %s for tty\n", fname);
+ et->fd = open(fname, O_RDWR | O_NOCTTY | O_CLOEXEC);
+ }
+ else if ((fstat(et->fd, &buff) == 0) &&
+ (major(buff.st_rdev) == TTY_MAJOR) && (minor(buff.st_rdev) > 0))
+ et->fd = fcntl(0, F_DUPFD_CLOEXEC, 0);
+ else
+ et->fd = _e_tty_open_vt(et);
+
+ if (et->fd <= 0)
+ {
+ e_error_message_show(_("Could not open a tty.\n"));
+ free(et);
+ return NULL;
+ }
+
+ if (tcgetattr(et->fd, &et->term_attribs) < 0)
+ {
+ e_error_message_show(_("Could not get terminal attributes: %m\n"));
+ free(et);
+ return NULL;
+ }
+
+ raw_attribs = et->term_attribs;
+ cfmakeraw(&raw_attribs);
+
+ raw_attribs.c_oflag |= (OPOST | OCRNL);
+ if (tcsetattr(et->fd, TCSANOW, &raw_attribs) < 0)
+ {
+ e_error_message_show(_("Could not put terminal in raw mode: %m\n"));
+ free(et);
+ return NULL;
+ }
+
+ loop = wl_display_get_event_loop(comp->display);
+ et->input_source =
+ wl_event_loop_add_fd(loop, et->fd, WL_EVENT_READABLE, _e_tty_cb_input, et);
+
+ if ((ret = ioctl(et->fd, KDSETMODE, KD_GRAPHICS)))
+ {
+ e_error_message_show(_("Failed to set graphics mode on tty: %m\n"));
+ free(et);
+ return NULL;
+ }
+
+ et->has_vt = EINA_TRUE;
+ mode.mode = VT_PROCESS;
+ mode.relsig = SIGUSR1;
+ mode.acqsig = SIGUSR1;
+ if (ioctl(et->fd, VT_SETMODE, &mode) < 0)
+ {
+ e_error_message_show(_("Failed to take control of vt handling: %m\n"));
+ free(et);
+ return NULL;
+ }
+
+ et->vt_source =
+ wl_event_loop_add_signal(loop, SIGUSR1, _e_tty_cb_handle, et);
+
+ return et;
+}
+
+EINTERN void
+e_tty_destroy(E_Tty *et)
+{
+ struct vt_mode mode;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if (!et) return;
+ if (ioctl(et->fd, KDSETMODE, KD_TEXT))
+ e_error_message_show(_("Failed to set KD_TEXT mode on tty: %m\n"));
+ if (tcsetattr(et->fd, TCSANOW, &et->term_attribs) < 0)
+ e_error_message_show(_("Could not restore terminal to canonical mode: %m\n"));
+ mode.mode = VT_AUTO;
+ if (ioctl(et->fd, VT_SETMODE, &mode) < 0)
+ e_error_message_show(_("Could not reset vt handling: %m\n"));
+ if ((et->has_vt) && (et->vt != et->start_vt))
+ {
+ ioctl(et->fd, VT_ACTIVATE, et->start_vt);
+ ioctl(et->fd, VT_WAITACTIVE, et->start_vt);
+ }
+ close(et->fd);
+ free(et);
+}
+
+/* local functions */
+static int
+_e_tty_open_vt(E_Tty *et)
+{
+ int tty0, fd;
+ char fname[16];
+ struct vt_stat vts;
+
+ DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ if ((tty0 = open("/dev/tty0", O_WRONLY | O_CLOEXEC)) < 0)
+ {
+ e_error_message_show(_("Failed to open tty0: %m\n"));
+ return -1;
+ }
+
+ if ((ioctl(tty0, VT_OPENQRY, &et->vt) < 0) || (et->vt == -1))
+ {
+ e_error_message_show(_("Failed to open tty0: %m\n"));
+ close(tty0);
+ return -1;
+ }
+ close(tty0);
+
+ snprintf(fname, sizeof(fname), "/dev/tty%d", et->vt);
+ printf("Compositor: Using new vt %s\n", fname);
+ fd = open(fname, O_RDWR | O_NOCTTY | O_CLOEXEC);
+ if (fd < 0) return fd;
+
+ if (ioctl(fd, VT_GETSTATE, &vts) == 0)
+ et->start_vt = vts.v_active;
+ else
+ et->start_vt = et->vt;
+
+ if ((ioctl(fd, VT_ACTIVATE, et->vt) < 0) ||
+ (ioctl(fd, VT_WAITACTIVE, et->vt) < 0))
+ {
+ e_error_message_show(_("Failed to switch to new vt: %m\n"));
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static int
+_e_tty_cb_input(int fd __UNUSED__, uint32_t mask __UNUSED__, void *data)
+{
+ E_Tty *et;
+
+// DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ et = data;
+ tcflush(et->fd, TCIFLUSH);
+ return 1;
+}
+
+static int
+_e_tty_cb_handle(int sig __UNUSED__, void *data)
+{
+ E_Tty *et;
+
+// DLOGFN(__FILE__, __LINE__, __FUNCTION__);
+
+ et = data;
+ if (et->has_vt)
+ {
+ et->vt_func(et->comp, 1);
+ et->has_vt = EINA_FALSE;
+ ioctl(et->fd, VT_RELDISP, 1);
+ }
+ else
+ {
+ ioctl(et->fd, VT_RELDISP, VT_ACKACQ);
+ et->vt_func(et->comp, 0);
+ et->has_vt = EINA_TRUE;
+ }
+
+ return 1;
+}
diff --git a/src/modules/wl_drm/module.desktop b/src/modules/wl_drm/module.desktop
new file mode 100644
index 0000000000..3ea637542b
--- /dev/null
+++ b/src/modules/wl_drm/module.desktop
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Type=Link
+Name=Wayland Drm
+Icon=e-module-wl_drm
+Comment=Enlightenment DRM Composite Manager
+X-Enlightenment-ModuleType=look