From 648f558aa863312605d9b25801edfdf5c7bd3d6b Mon Sep 17 00:00:00 2001 From: Christopher Michael Date: Thu, 29 Mar 2012 01:43:17 +0000 Subject: [PATCH] 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 --- src/modules/Makefile.am | 12 + src/modules/wl_drm/Makefile.am | 35 + src/modules/wl_drm/e-module-wl_drm.edj | Bin 0 -> 13048 bytes src/modules/wl_drm/e_drm_output.c | 604 +++++++++ src/modules/wl_drm/e_evdev.c | 664 ++++++++++ src/modules/wl_drm/e_mod_main.c | 713 ++++++++++ src/modules/wl_drm/e_mod_main.h | 204 +++ src/modules/wl_drm/e_sprite.c | 88 ++ src/modules/wl_drm/e_tty.c | 201 +++ src/modules/wl_drm/module.desktop | 6 + src/modules/wl_screenshot/Makefile.am | 33 + .../wl_screenshot/e-module-wl_screenshot.edj | Bin 0 -> 12194 bytes src/modules/wl_screenshot/e_mod_main.c | 293 +++++ src/modules/wl_screenshot/e_mod_main.h | 13 + .../e_screenshooter_client_protocol.c | 22 + .../e_screenshooter_client_protocol.h | 48 + src/modules/wl_screenshot/module.desktop | 6 + src/modules/wl_shell/Makefile.am | 33 + src/modules/wl_shell/desktop-shell.xml | 51 + src/modules/wl_shell/e-module-wl_shell.edj | Bin 0 -> 13048 bytes .../wl_shell/e_desktop_shell_protocol.c | 56 + .../wl_shell/e_desktop_shell_protocol.h | 85 ++ src/modules/wl_shell/e_mod_main.c | 1146 +++++++++++++++++ src/modules/wl_shell/e_mod_main.h | 93 ++ src/modules/wl_shell/module.desktop | 6 + 25 files changed, 4412 insertions(+) create mode 100644 src/modules/wl_drm/Makefile.am create mode 100644 src/modules/wl_drm/e-module-wl_drm.edj create mode 100644 src/modules/wl_drm/e_drm_output.c create mode 100644 src/modules/wl_drm/e_evdev.c create mode 100644 src/modules/wl_drm/e_mod_main.c create mode 100644 src/modules/wl_drm/e_mod_main.h create mode 100644 src/modules/wl_drm/e_sprite.c create mode 100644 src/modules/wl_drm/e_tty.c create mode 100644 src/modules/wl_drm/module.desktop create mode 100644 src/modules/wl_screenshot/Makefile.am create mode 100644 src/modules/wl_screenshot/e-module-wl_screenshot.edj create mode 100644 src/modules/wl_screenshot/e_mod_main.c create mode 100644 src/modules/wl_screenshot/e_mod_main.h create mode 100644 src/modules/wl_screenshot/e_screenshooter_client_protocol.c create mode 100644 src/modules/wl_screenshot/e_screenshooter_client_protocol.h create mode 100644 src/modules/wl_screenshot/module.desktop create mode 100644 src/modules/wl_shell/Makefile.am create mode 100644 src/modules/wl_shell/desktop-shell.xml create mode 100644 src/modules/wl_shell/e-module-wl_shell.edj create mode 100644 src/modules/wl_shell/e_desktop_shell_protocol.c create mode 100644 src/modules/wl_shell/e_desktop_shell_protocol.h create mode 100644 src/modules/wl_shell/e_mod_main.c create mode 100644 src/modules/wl_shell/e_mod_main.h create mode 100644 src/modules/wl_shell/module.desktop diff --git a/src/modules/Makefile.am b/src/modules/Makefile.am index 56498e786..08851a9b5 100644 --- a/src/modules/Makefile.am +++ b/src/modules/Makefile.am @@ -190,3 +190,15 @@ endif if USE_MODULE_TASKS SUBDIRS += tasks endif + +if HAVE_WAYLAND_DRM + SUBDIRS += wl_drm +endif + +if HAVE_WAYLAND_SHELL + SUBDIRS += wl_shell +endif + +if HAVE_WAYLAND_SCREENSHOT + SUBDIRS += wl_screenshot +endif diff --git a/src/modules/wl_drm/Makefile.am b/src/modules/wl_drm/Makefile.am new file mode 100644 index 000000000..54adf4d1e --- /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 0000000000000000000000000000000000000000..2c1f4d26ef39661e481afc89202019f6d41e07cc GIT binary patch literal 13048 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 literal 0 HcmV?d00001 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 000000000..5578fe25b --- /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 000000000..4ce4760ac --- /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(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 000000000..a665b45dc --- /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 000000000..ee7a62fec --- /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 +# define DLOGFN(fl, ln, fn) printf("-E-DRM: %25s: %5i - %s\n", fl, ln, fn); +# else +# define DLOGFN(fl, ln, fn) +# endif + +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +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 000000000..90a3749b7 --- /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 000000000..40902b780 --- /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 000000000..3ea637542 --- /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 diff --git a/src/modules/wl_screenshot/Makefile.am b/src/modules/wl_screenshot/Makefile.am new file mode 100644 index 000000000..a83ce8d67 --- /dev/null +++ b/src/modules/wl_screenshot/Makefile.am @@ -0,0 +1,33 @@ +MAINTAINERCLEANFILES = Makefile.in +MODULE = wl_screenshot + +# 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_SCREENSHOT_CFLAGS@ + +pkgdir = $(libdir)/enlightenment/modules/$(MODULE)/$(MODULE_ARCH) +pkg_LTLIBRARIES = module.la + +module_la_SOURCES = e_mod_main.c \ + e_mod_main.h \ + e_screenshooter_client_protocol.c \ + e_screenshooter_client_protocol.h + +module_la_LIBADD = @e_wl_libs@ @dlopen_libs@ @WAYLAND_SCREENSHOT_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_screenshot/e-module-wl_screenshot.edj b/src/modules/wl_screenshot/e-module-wl_screenshot.edj new file mode 100644 index 0000000000000000000000000000000000000000..eaf667a88a42e4d4da48cad66ff4381c649ddf75 GIT binary patch literal 12194 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

RcgNXv>P5tyU-5kW_VazK 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 U<`WP#kljP;t3}#8C)i5-KSF}sz5oCK literal 0 HcmV?d00001 diff --git a/src/modules/wl_screenshot/e_mod_main.c b/src/modules/wl_screenshot/e_mod_main.c new file mode 100644 index 000000000..f87e26d29 --- /dev/null +++ b/src/modules/wl_screenshot/e_mod_main.c @@ -0,0 +1,293 @@ +#include "e.h" +#include "e_mod_main.h" + +typedef struct _Instance Instance; +struct _Instance +{ + E_Gadcon_Client *gcc; + Evas_Object *o_btn; +}; + +static E_Gadcon_Client *_gc_init(E_Gadcon *gc, const char *name, const char *id, const char *style); +static void _gc_shutdown(E_Gadcon_Client *gcc); +static void _gc_orient(E_Gadcon_Client *gcc, E_Gadcon_Orient orient); +static const char *_gc_label(E_Gadcon_Client_Class *cc); +static Evas_Object *_gc_icon(E_Gadcon_Client_Class *cc, Evas *evas); +static const char *_gc_id_new(E_Gadcon_Client_Class *cc); +static void _cb_btn_down(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event); +static void _cb_handle_global(struct wl_display *disp, unsigned int id, const char *interface, unsigned int version __UNUSED__, void *data); +static struct wl_buffer *_create_shm_buffer(struct wl_shm *_shm, int width, int height, void **data_out); +static void _cb_handle_geometry(void *data, struct wl_output *wl_output, int x, int y, int w, int h, int subpixel, const char *make, const char *model); +static void _cb_handle_mode(void *data, struct wl_output *wl_output, unsigned int flags, int w, int h, int refresh); +static void _save_png(int w, int h, void *data); +static Eina_Bool _cb_timer(void *data __UNUSED__); + +static const E_Gadcon_Client_Class _gc = +{ + GADCON_CLIENT_CLASS_VERSION, "wl_screenshot", + { + _gc_init, _gc_shutdown, _gc_orient, _gc_label, _gc_icon, _gc_id_new, NULL, + e_gadcon_site_is_not_toolbar + }, + E_GADCON_CLIENT_STYLE_PLAIN +}; + +static const struct wl_output_listener _output_listener = +{ + _cb_handle_geometry, + _cb_handle_mode +}; + +static E_Module *_mod = NULL; +static E_Screenshooter *_shooter = NULL; +static struct wl_output *_output; +static int ow = 0, oh = 0; +static Ecore_Timer *_timer = NULL; + +EAPI E_Module_Api e_modapi = { E_MODULE_API_VERSION, "Screenshooter" }; + +EAPI void * +e_modapi_init(E_Module *m) +{ + struct wl_display *disp; + + /* if (!ecore_wl_init(NULL)) return NULL; */ + + disp = ecore_wl_display_get(); + + _mod = m; + e_gadcon_provider_register(&_gc); + + /* e_module_delayed_set(m, 1); */ + /* e_module_priority_set(m, 1000); */ + + wl_display_add_global_listener(disp, _cb_handle_global, NULL); + + return m; +} + +EAPI int +e_modapi_shutdown(E_Module *m) +{ + _mod = NULL; + e_gadcon_provider_unregister(&_gc); + /* ecore_wl_shutdown(); */ + return 1; +} + +EAPI int +e_modapi_save(E_Module *m __UNUSED__) +{ + return 1; +} + +static E_Gadcon_Client * +_gc_init(E_Gadcon *gc, const char *name, const char *id, const char *style) +{ + Evas_Object *o; + E_Gadcon_Client *gcc; + Instance *inst; + char buff[PATH_MAX]; + + inst = E_NEW(Instance, 1); + + snprintf(buff, sizeof(buff), "%s/e-module-wl_screenshot.edj", _mod->dir); + + o = edje_object_add(gc->evas); + if (!e_theme_edje_object_set(o, "base/theme/modules/wl_screenshot", + "modules/wl_screenshot/main")) + edje_object_file_set(o, buff, "modules/wl_screenshot/main"); + + gcc = e_gadcon_client_new(gc, name, id, style, o); + gcc->data = inst; + + inst->gcc = gcc; + inst->o_btn = o; + + evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN, + _cb_btn_down, inst); + return gcc; +} + +static void +_gc_shutdown(E_Gadcon_Client *gcc) +{ + Instance *inst; + + if (!(inst = gcc->data)) return; + evas_object_del(inst->o_btn); + free(inst); +} + +static void +_gc_orient(E_Gadcon_Client *gcc, E_Gadcon_Orient orient) +{ + Instance *inst; + Evas_Coord mw, mh; + + inst = gcc->data; + mw = 0, mh = 0; + edje_object_size_min_get(inst->o_btn, &mw, &mh); + if ((mw < 1) || (mh < 1)) + edje_object_size_min_calc(inst->o_btn, &mw, &mh); + if (mw < 4) mw = 4; + if (mh < 4) mh = 4; + e_gadcon_client_aspect_set(gcc, mw, mh); + e_gadcon_client_min_size_set(gcc, mw, mh); +} + +static const char * +_gc_label(E_Gadcon_Client_Class *cc) +{ + return _("Screenshooter"); +} + +static Evas_Object * +_gc_icon(E_Gadcon_Client_Class *cc, Evas *evas) +{ + Evas_Object *o; + char buf[PATH_MAX]; + + o = edje_object_add(evas); + snprintf(buf, sizeof(buf), "%s/e-module-wl_screenshot.edj", _mod->dir); + edje_object_file_set(o, buf, "icon"); + return o; +} + +static const char * +_gc_id_new(E_Gadcon_Client_Class *cc) +{ + return _gc.name; +} + +static void +_cb_btn_down(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event) +{ + Instance *inst; + Evas_Event_Mouse_Down *ev; + + inst = data; + ev = event; + if (ev->button == 1) + { + if (_timer) ecore_timer_del(_timer); + _timer = ecore_timer_add(5.0, _cb_timer, NULL); + } +} + +static void +_cb_handle_global(struct wl_display *disp, unsigned int id, const char *interface, unsigned int version __UNUSED__, void *data) +{ + if (!strcmp(interface, "screenshooter")) + _shooter = wl_display_bind(disp, id, &screenshooter_interface); + else if (!strcmp(interface, "wl_output")) + { + _output = wl_display_bind(disp, id, &wl_output_interface); + wl_output_add_listener(_output, &_output_listener, NULL); + } +} + +static struct wl_buffer * +_create_shm_buffer(struct wl_shm *_shm, int width, int height, void **data_out) +{ + char filename[] = "/tmp/wayland-shm-XXXXXX"; + struct wl_buffer *buffer; + int fd, size, stride; + void *data; + + fd = mkstemp(filename); + if (fd < 0) + { + fprintf(stderr, "open %s failed: %m\n", filename); + return NULL; + } + + stride = width * 4; + size = stride * height; + if (ftruncate(fd, size) < 0) + { + fprintf(stderr, "ftruncate failed: %m\n"); + close(fd); + return NULL; + } + + data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + unlink(filename); + + if (data == MAP_FAILED) + { + fprintf(stderr, "mmap failed: %m\n"); + close(fd); + return NULL; + } + + buffer = wl_shm_create_buffer(_shm, fd, width, height, stride, + WL_SHM_FORMAT_ARGB8888); + + close(fd); + + *data_out = data; + + return buffer; +} + +static void +_cb_handle_geometry(void *data, struct wl_output *wl_output, int x, int y, int w, int h, int subpixel, const char *make, const char *model) +{ + +} + +static void +_cb_handle_mode(void *data, struct wl_output *wl_output, unsigned int flags, int w, int h, int refresh) +{ + if (ow == 0) ow = w; + if (oh == 0) oh = h; +} + +static void +_save_png(int w, int h, void *data) +{ + Ecore_Evas *ee; + Evas *evas; + Evas_Object *img; + char buff[1024]; + char fname[PATH_MAX]; + + ee = ecore_evas_buffer_new(w, h); + evas = ecore_evas_get(ee); + + img = evas_object_image_filled_add(evas); + evas_object_image_fill_set(img, 0, 0, w, h); + evas_object_image_size_set(img, w, h); + evas_object_image_data_set(img, data); + evas_object_show(img); + + snprintf(fname, sizeof(fname), "%s/screenshot.png", e_user_homedir_get()); + snprintf(buff, sizeof(buff), "quality=90 compress=9"); + if (!(evas_object_image_save(img, fname, NULL, buff))) + { + printf("Error saving shot\n"); + } + + if (img) evas_object_del(img); + if (ee) ecore_evas_free(ee); +} + +static Eina_Bool +_cb_timer(void *data __UNUSED__) +{ + struct wl_buffer *buffer; + void *d = NULL; + + if (!_shooter) return EINA_FALSE; + + buffer = _create_shm_buffer(ecore_wl_shm_get(), ow, oh, &d); + screenshooter_shoot(_shooter, _output, buffer); + + ecore_wl_sync(); + + printf("Saving Png\n"); + _save_png(ow, oh, d); + + return EINA_FALSE; +} diff --git a/src/modules/wl_screenshot/e_mod_main.h b/src/modules/wl_screenshot/e_mod_main.h new file mode 100644 index 000000000..a3979968e --- /dev/null +++ b/src/modules/wl_screenshot/e_mod_main.h @@ -0,0 +1,13 @@ +#ifndef E_MOD_MAIN_H +# define E_MOD_MAIN_H + +# include +# include "e_screenshooter_client_protocol.h" + +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); + +#endif diff --git a/src/modules/wl_screenshot/e_screenshooter_client_protocol.c b/src/modules/wl_screenshot/e_screenshooter_client_protocol.c new file mode 100644 index 000000000..258d72542 --- /dev/null +++ b/src/modules/wl_screenshot/e_screenshooter_client_protocol.c @@ -0,0 +1,22 @@ +#include +#include +#include "wayland-util.h" + +extern const struct wl_interface wl_output_interface; +extern const struct wl_interface wl_buffer_interface; + +static const struct wl_interface *types[] = { + &wl_output_interface, + &wl_buffer_interface, +}; + +static const struct wl_message screenshooter_requests[] = { + { "shoot", "oo", types + 0 }, +}; + +WL_EXPORT const struct wl_interface screenshooter_interface = { + "screenshooter", 1, + ARRAY_LENGTH(screenshooter_requests), screenshooter_requests, + 0, NULL, +}; + diff --git a/src/modules/wl_screenshot/e_screenshooter_client_protocol.h b/src/modules/wl_screenshot/e_screenshooter_client_protocol.h new file mode 100644 index 000000000..0265f67f7 --- /dev/null +++ b/src/modules/wl_screenshot/e_screenshooter_client_protocol.h @@ -0,0 +1,48 @@ +#ifndef E_SCREENSHOOTER_CLIENT_PROTOCOL_H +#define E_SCREENSHOOTER_CLIENT_PROTOCOL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "wayland-util.h" + +struct wl_client; +struct wl_resource; + +extern const struct wl_interface screenshooter_interface; + +#define SCREENSHOOTER_SHOOT 0 + +static inline void +screenshooter_set_user_data(E_Screenshooter *screenshooter, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) screenshooter, user_data); +} + +static inline void * +screenshooter_get_user_data(E_Screenshooter *screenshooter) +{ + return wl_proxy_get_user_data((struct wl_proxy *) screenshooter); +} + +static inline void +screenshooter_destroy(E_Screenshooter *screenshooter) +{ + wl_proxy_destroy((struct wl_proxy *) screenshooter); +} + +static inline void +screenshooter_shoot(E_Screenshooter *screenshooter, struct wl_output *output, struct wl_buffer *buffer) +{ + wl_proxy_marshal((struct wl_proxy *) screenshooter, + SCREENSHOOTER_SHOOT, output, buffer); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/modules/wl_screenshot/module.desktop b/src/modules/wl_screenshot/module.desktop new file mode 100644 index 000000000..8a9c31a04 --- /dev/null +++ b/src/modules/wl_screenshot/module.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Link +Name=Wayland Screenshot +Icon=e-module-wl_screenshot.edj +Comment=Enlightenment Wayland Screenshot Module +X-Enlightenment-ModuleType=look diff --git a/src/modules/wl_shell/Makefile.am b/src/modules/wl_shell/Makefile.am new file mode 100644 index 000000000..6535bf9bc --- /dev/null +++ b/src/modules/wl_shell/Makefile.am @@ -0,0 +1,33 @@ +MAINTAINERCLEANFILES = Makefile.in +MODULE = wl_shell + +# 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_SHELL_CFLAGS@ + +pkgdir = $(libdir)/enlightenment/modules/$(MODULE)/$(MODULE_ARCH) +pkg_LTLIBRARIES = module.la + +module_la_SOURCES = e_mod_main.c \ + e_mod_main.h \ + e_desktop_shell_protocol.c \ + e_desktop_shell_protocol.h + +module_la_LIBADD = @e_wl_libs@ @dlopen_libs@ @WAYLAND_SHELL_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_shell/desktop-shell.xml b/src/modules/wl_shell/desktop-shell.xml new file mode 100644 index 000000000..c40cf179a --- /dev/null +++ b/src/modules/wl_shell/desktop-shell.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/wl_shell/e-module-wl_shell.edj b/src/modules/wl_shell/e-module-wl_shell.edj new file mode 100644 index 0000000000000000000000000000000000000000..2c1f4d26ef39661e481afc89202019f6d41e07cc GIT binary patch literal 13048 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 literal 0 HcmV?d00001 diff --git a/src/modules/wl_shell/e_desktop_shell_protocol.c b/src/modules/wl_shell/e_desktop_shell_protocol.c new file mode 100644 index 000000000..3ad8687f5 --- /dev/null +++ b/src/modules/wl_shell/e_desktop_shell_protocol.c @@ -0,0 +1,56 @@ +#include +#include +#include "wayland-util.h" + +extern const struct wl_interface wl_output_interface; +extern const struct wl_interface wl_shell_surface_interface; +extern const struct wl_interface wl_output_interface; +extern const struct wl_interface wl_shell_surface_interface; +extern const struct wl_interface wl_shell_surface_interface; +extern const struct wl_interface wl_shell_surface_interface; +extern const struct wl_interface wl_shell_surface_interface; +extern const struct wl_interface wl_output_interface; + +static const struct wl_interface *types[] = { + &wl_output_interface, + &wl_shell_surface_interface, + &wl_output_interface, + &wl_shell_surface_interface, + &wl_shell_surface_interface, + NULL, + NULL, + &wl_shell_surface_interface, + NULL, + NULL, + &wl_shell_surface_interface, + &wl_output_interface, +}; + +static const struct wl_message desktop_shell_requests[] = { + { "set_background", "oo", types + 0 }, + { "set_panel", "oo", types + 2 }, + { "set_lock_surface", "o", types + 4 }, + { "unlock", "", types + 0 }, +}; + +static const struct wl_message desktop_shell_events[] = { + { "configure", "uuoii", types + 5 }, + { "prepare_lock_surface", "", types + 0 }, +}; + +WL_EXPORT const struct wl_interface desktop_shell_interface = { + "desktop_shell", 1, + ARRAY_LENGTH(desktop_shell_requests), desktop_shell_requests, + ARRAY_LENGTH(desktop_shell_events), desktop_shell_events, +}; + +static const struct wl_message screensaver_requests[] = { + { "set_surface", "oo", types + 10 }, +}; + +WL_EXPORT const struct wl_interface screensaver_interface = { + "screensaver", 1, + ARRAY_LENGTH(screensaver_requests), screensaver_requests, + 0, NULL, +}; + diff --git a/src/modules/wl_shell/e_desktop_shell_protocol.h b/src/modules/wl_shell/e_desktop_shell_protocol.h new file mode 100644 index 000000000..fcb310a74 --- /dev/null +++ b/src/modules/wl_shell/e_desktop_shell_protocol.h @@ -0,0 +1,85 @@ +#ifndef DESKTOP_SERVER_PROTOCOL_H +#define DESKTOP_SERVER_PROTOCOL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "wayland-util.h" + +struct wl_client; +struct wl_resource; + +struct desktop_shell; +struct screensaver; + +extern const struct wl_interface desktop_shell_interface; +extern const struct wl_interface screensaver_interface; + +struct desktop_shell_interface { + /** + * set_background - (none) + * @output: (none) + * @surface: (none) + */ + void (*set_background)(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *output, + struct wl_resource *surface); + /** + * set_panel - (none) + * @output: (none) + * @surface: (none) + */ + void (*set_panel)(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *output, + struct wl_resource *surface); + /** + * set_lock_surface - (none) + * @surface: (none) + */ + void (*set_lock_surface)(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *surface); + /** + * unlock - (none) + */ + void (*unlock)(struct wl_client *client, + struct wl_resource *resource); +}; + +#define DESKTOP_SHELL_CONFIGURE 0 +#define DESKTOP_SHELL_PREPARE_LOCK_SURFACE 1 + +static inline void +desktop_shell_send_configure(struct wl_resource *resource_, uint32_t time, uint32_t edges, struct wl_resource *surface, int32_t width, int32_t height) +{ + wl_resource_post_event(resource_, DESKTOP_SHELL_CONFIGURE, time, edges, surface, width, height); +} + +static inline void +desktop_shell_send_prepare_lock_surface(struct wl_resource *resource_) +{ + wl_resource_post_event(resource_, DESKTOP_SHELL_PREPARE_LOCK_SURFACE); +} + +struct screensaver_interface { + /** + * set_surface - (none) + * @surface: (none) + * @output: (none) + */ + void (*set_surface)(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *surface, + struct wl_resource *output); +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/modules/wl_shell/e_mod_main.c b/src/modules/wl_shell/e_mod_main.c new file mode 100644 index 000000000..48b045563 --- /dev/null +++ b/src/modules/wl_shell/e_mod_main.c @@ -0,0 +1,1146 @@ +#include "e.h" +#include "e_mod_main.h" + +/* local function prototypes */ +static void _shell_cb_lock(E_Shell *base); +static void _shell_cb_unlock(E_Shell *base); +static void _shell_cb_map(E_Shell *base, E_Surface *surface, int w, int h, int sx, int sy); +static void _shell_cb_configure(E_Shell *base, E_Surface *surface, GLfloat x, GLfloat y, int w, int h); +static void _shell_cb_destroy(E_Shell *base); +static void _shell_cb_destroy_shell_surface(struct wl_resource *resource); +static void _shell_cb_bind(struct wl_client *client, void *data, unsigned int version __UNUSED__, unsigned int id); +static void _shell_cb_bind_desktop(struct wl_client *client, void *data, unsigned int version __UNUSED__, unsigned int id); +static void _shell_cb_unbind_desktop(struct wl_resource *resource); +static void _shell_cb_get_shell_surface(struct wl_client *client, struct wl_resource *resource, unsigned int id, struct wl_resource *surface_resource); +static void _shell_cb_handle_surface_destroy(struct wl_listener *listener, struct wl_resource *resource __UNUSED__, unsigned int timestamp); +static void _shell_cb_handle_lock_surface_destroy(struct wl_listener *listener, struct wl_resource *resource __UNUSED__, unsigned int timestamp __UNUSED__); +static void _shell_cb_activate(E_Shell *base, E_Surface *es, E_Input_Device *eid, unsigned int timestamp); + +static void _shell_cb_desktop_set_background(struct wl_client *client __UNUSED__, struct wl_resource *resource, struct wl_resource *output_resource, struct wl_resource *surface_resource); +static void _shell_cb_desktop_set_panel(struct wl_client *client __UNUSED__, struct wl_resource *resource, struct wl_resource *output_resource, struct wl_resource *surface_resource); +static void _shell_cb_desktop_set_lock_surface(struct wl_client *client __UNUSED__, struct wl_resource *resource, struct wl_resource *surface_resource); +static void _shell_cb_desktop_unlock(struct wl_client *client __UNUSED__, struct wl_resource *resource); + +static void _shell_cb_shell_surface_move(struct wl_client *client __UNUSED__, struct wl_resource *resource, struct wl_resource *input_resource, unsigned int timestamp); +static void _shell_cb_shell_surface_resize(struct wl_client *client __UNUSED__, struct wl_resource *resource, struct wl_resource *input_resource, unsigned int timestamp, unsigned int edges); +static void _shell_cb_shell_surface_set_toplevel(struct wl_client *client __UNUSED__, struct wl_resource *resource); +static void _shell_cb_shell_surface_set_transient(struct wl_client *client __UNUSED__, struct wl_resource *resource, struct wl_resource *parent_resource, int x, int y, unsigned int flags __UNUSED__); +static void _shell_cb_shell_surface_set_fullscreen(struct wl_client *client __UNUSED__, struct wl_resource *resource, unsigned int method, unsigned int framerate, struct wl_resource *output_resource); +static void _shell_cb_shell_surface_set_popup(struct wl_client *client __UNUSED__, struct wl_resource *resource, struct wl_resource *input_device_resource __UNUSED__, unsigned int timestamp __UNUSED__, struct wl_resource *parent_resource, int x, int y, unsigned int flags __UNUSED__); +static void _shell_cb_shell_surface_set_maximized(struct wl_client *client __UNUSED__, struct wl_resource *resource, struct wl_resource *output_resource); + +static E_Shell_Surface *_shell_get_shell_surface(E_Surface *es); +static int _shell_reset_shell_surface_type(E_Shell_Surface *ess); +static void _shell_center_on_output(E_Surface *es, E_Output *output); +static void _shell_map_fullscreen(E_Shell_Surface *ess); +static void _shell_configure_fullscreen(E_Shell_Surface *ess); +static void _shell_stack_fullscreen(E_Shell_Surface *ess); +static void _shell_unset_fullscreen(E_Shell_Surface *ess); +static struct wl_shell *_shell_surface_get_shell(E_Shell_Surface *ess); +static E_Surface *_shell_create_black_surface(E_Compositor *comp, GLfloat x, GLfloat y, int w, int h); +static E_Shell_Surface_Type _shell_get_shell_surface_type(E_Surface *es); + +static void _shell_map_popup(E_Shell_Surface *ess, unsigned int timestamp); +static void _shell_cb_popup_grab_focus(struct wl_pointer_grab *grab, unsigned int timestamp, struct wl_surface *surface, int x, int y); +static void _shell_cb_popup_grab_motion(struct wl_pointer_grab *grab, unsigned int timestamp, int sx, int sy); +static void _shell_cb_popup_grab_button(struct wl_pointer_grab *grab, unsigned int timestamp, int button, int state); + +/* local variables */ + +/* wayland interfaces */ +static const struct wl_shell_interface _e_shell_interface = +{ + _shell_cb_get_shell_surface +}; +static const struct desktop_shell_interface _e_desktop_shell_interface = +{ + _shell_cb_desktop_set_background, + _shell_cb_desktop_set_panel, + _shell_cb_desktop_set_lock_surface, + _shell_cb_desktop_unlock +}; +static const struct wl_shell_surface_interface _e_shell_surface_interface = +{ + _shell_cb_shell_surface_move, + _shell_cb_shell_surface_resize, + _shell_cb_shell_surface_set_toplevel, + _shell_cb_shell_surface_set_transient, + _shell_cb_shell_surface_set_fullscreen, + _shell_cb_shell_surface_set_popup, + _shell_cb_shell_surface_set_maximized +}; +static const struct wl_pointer_grab_interface _e_popup_grab_interface = +{ + _shell_cb_popup_grab_focus, + _shell_cb_popup_grab_motion, + _shell_cb_popup_grab_button +}; + +/* external variables */ + +EAPI E_Module_Api e_modapi = { E_MODULE_API_VERSION, "Shell" }; + +EAPI void * +e_modapi_init(E_Module *m) +{ + E_Compositor *comp; + struct wl_shell *shell; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + if (!(comp = m->data)) return NULL; + + if (!(shell = malloc(sizeof(*shell)))) return NULL; + + memset(shell, 0, sizeof(*shell)); + + shell->compositor = comp; + shell->shell.lock = _shell_cb_lock; + shell->shell.unlock = _shell_cb_unlock; + shell->shell.map = _shell_cb_map; + shell->shell.configure = _shell_cb_configure; + shell->shell.destroy = _shell_cb_destroy; + + wl_list_init(&shell->backgrounds); + wl_list_init(&shell->panels); + + e_layer_init(&shell->fullscreen_layer, &comp->cursor_layer.link); + e_layer_init(&shell->panel_layer, &shell->fullscreen_layer.link); + e_layer_init(&shell->toplevel_layer, &shell->panel_layer.link); + e_layer_init(&shell->background_layer, &shell->toplevel_layer.link); + + wl_list_init(&shell->lock_layer.surfaces); + + if (!wl_display_add_global(comp->display, &wl_shell_interface, + shell, _shell_cb_bind)) + return NULL; + + if (!wl_display_add_global(comp->display, &desktop_shell_interface, + shell, _shell_cb_bind_desktop)) + return NULL; + + comp->shell = &shell->shell; + + /* m->data = &shell->shell; */ + + return m; +} + +EAPI int +e_modapi_shutdown(E_Module *m __UNUSED__) +{ + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + return 1; +} + +EAPI int +e_modapi_save(E_Module *m __UNUSED__) +{ + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + return 1; +} + +/* local functions */ +static void +_shell_cb_lock(E_Shell *base) +{ + struct wl_shell *shell; + unsigned int timestamp; + E_Input_Device *device; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + shell = container_of(base, struct wl_shell, shell); + + if (shell->locked) + { + E_Output *output; + + wl_list_for_each(output, &shell->compositor->outputs, link) + if (output->set_dpms) output->set_dpms(output, E_DPMS_STANDBY); + + return; + } + + shell->locked = EINA_TRUE; + + wl_list_remove(&shell->panel_layer.link); + wl_list_remove(&shell->toplevel_layer.link); + wl_list_remove(&shell->fullscreen_layer.link); + wl_list_insert(&shell->compositor->cursor_layer.link, + &shell->lock_layer.link); + + /* TODO: launch screensaver */ + /* TODO: shell screensaver */ + /* TODO: loop screensaver surfaces */ + + e_compositor_schedule_repaint(shell->compositor); + + timestamp = e_compositor_get_time(); + wl_list_for_each(device, &shell->compositor->inputs, link) + wl_input_device_set_keyboard_focus(&device->input_device, NULL, + timestamp); +} + +static void +_shell_cb_unlock(E_Shell *base) +{ + struct wl_shell *shell; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + shell = container_of(base, struct wl_shell, shell); + if ((!shell->locked) || (shell->lock_surface)) + { + e_compositor_wake(shell->compositor); + return; + } + + /* TODO: handle desktop shell going away ? */ + + if (shell->prepare_event_sent) return; + + /* TODO: handle desktop_shell_send_prepare_lock_surface */ + /* desktop_shell_send_prepare_lock_surface(shell->); */ + + shell->prepare_event_sent = EINA_TRUE; +} + +static void +_shell_cb_map(E_Shell *base, E_Surface *surface, int w, int h, int sx, int sy) +{ + struct wl_shell *shell; + E_Compositor *comp; + E_Shell_Surface *ess; + E_Shell_Surface_Type type = E_SHELL_SURFACE_NONE; + E_Surface *parent; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + shell = container_of(base, struct wl_shell, shell); + comp = shell->compositor; + + if ((ess = _shell_get_shell_surface(surface))) + type = ess->type; + + surface->geometry.w = w; + surface->geometry.h = h; + surface->geometry.dirty = EINA_TRUE; + + /* TODO: e_comp_update_drag_surfaces */ + + /* initial position */ + switch (type) + { + case E_SHELL_SURFACE_TOPLEVEL: + e_surface_set_position(surface, surface->geometry.x, surface->geometry.y); + break; + case E_SHELL_SURFACE_SCREENSAVER: + _shell_center_on_output(surface, ess->fullscreen_output); + break; + case E_SHELL_SURFACE_FULLSCREEN: + _shell_map_fullscreen(ess); + break; + case E_SHELL_SURFACE_MAXIMIZED: + /* TODO: account for panel height ? */ + e_surface_set_position(surface, surface->output->x, surface->output->y); + break; + case E_SHELL_SURFACE_LOCK: + _shell_center_on_output(surface, e_output_get_default(comp)); + break; + case E_SHELL_SURFACE_POPUP: + _shell_map_popup(ess, ess->popup.timestamp); + break; + case E_SHELL_SURFACE_NONE: + e_surface_set_position(surface, surface->geometry.x + sx, + surface->geometry.y + sy); + break; + default: + break; + } + + /* stacking */ + switch (type) + { + case E_SHELL_SURFACE_BACKGROUND: + wl_list_insert(&shell->background_layer.surfaces, &surface->layers); + break; + case E_SHELL_SURFACE_PANEL: + wl_list_insert(&shell->panel_layer.surfaces, &surface->layers); + break; + case E_SHELL_SURFACE_LOCK: + wl_list_insert(&shell->lock_layer.surfaces, &surface->layers); + e_compositor_wake(comp); + break; + case E_SHELL_SURFACE_SCREENSAVER: + if (shell->locked) + { + /* TODO: show screensaver */ + /* TODO: set idle time ? */ + e_compositor_wake(comp); + if (!shell->lock_surface) + comp->state = E_COMPOSITOR_STATE_IDLE; + } + break; + case E_SHELL_SURFACE_POPUP: + case E_SHELL_SURFACE_TRANSIENT: + parent = ess->parent->surface; + wl_list_insert(parent->layers.prev, &surface->layers); + break; + case E_SHELL_SURFACE_FULLSCREEN: + case E_SHELL_SURFACE_NONE: + break; + default: + wl_list_insert(&shell->toplevel_layer.surfaces, &surface->layers); + break; + } + + if (type != E_SHELL_SURFACE_NONE) + { + e_surface_assign_output(surface); + if (type == E_SHELL_SURFACE_MAXIMIZED) + surface->output = ess->output; + } + + switch (type) + { + case E_SHELL_SURFACE_TOPLEVEL: + case E_SHELL_SURFACE_TRANSIENT: + case E_SHELL_SURFACE_FULLSCREEN: + case E_SHELL_SURFACE_MAXIMIZED: + if (!shell->locked) + _shell_cb_activate(base, surface, + (E_Input_Device *)comp->input_device, + e_compositor_get_time()); + break; + default: + break; + } + + if (type == E_SHELL_SURFACE_TOPLEVEL) + { + /* TODO: run zoom ? */ + } +} + +static void +_shell_cb_configure(E_Shell *base, E_Surface *surface, GLfloat x, GLfloat y, int w, int h) +{ + struct wl_shell *shell; + E_Shell_Surface_Type type = E_SHELL_SURFACE_NONE; + E_Shell_Surface_Type ptype = E_SHELL_SURFACE_NONE; + E_Shell_Surface *ess; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + shell = container_of(base, struct wl_shell, shell); + + if ((ess = _shell_get_shell_surface(surface))) + type = ess->type; + + surface->geometry.x = x; + surface->geometry.y = y; + surface->geometry.w = w; + surface->geometry.h = h; + surface->geometry.dirty = EINA_TRUE; + + switch (type) + { + case E_SHELL_SURFACE_SCREENSAVER: + _shell_center_on_output(surface, ess->fullscreen_output); + break; + case E_SHELL_SURFACE_FULLSCREEN: + _shell_configure_fullscreen(ess); + if (ptype != E_SHELL_SURFACE_FULLSCREEN) + _shell_stack_fullscreen(ess); + break; + case E_SHELL_SURFACE_MAXIMIZED: + surface->geometry.x = surface->output->x; + /* TODO: handle panel height ? */ + surface->geometry.y = surface->output->y; + break; + case E_SHELL_SURFACE_TOPLEVEL: + break; + default: + break; + } + + if (surface->output) + { + e_surface_assign_output(surface); + if (type == E_SHELL_SURFACE_SCREENSAVER) + surface->output = ess->output; + else if (type == E_SHELL_SURFACE_MAXIMIZED) + surface->output = ess->output; + } +} + +static void +_shell_cb_destroy(E_Shell *base) +{ + struct wl_shell *shell; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + shell = container_of(base, struct wl_shell, shell); + free(shell); +} + +static void +_shell_cb_destroy_shell_surface(struct wl_resource *resource) +{ + E_Shell_Surface *ess; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + ess = resource->data; + if (ess->popup.grab.input_device) + wl_input_device_end_pointer_grab(ess->popup.grab.input_device, 0); + if (ess->surface) + wl_list_remove(&ess->surface_destroy_listener.link); + if (ess->fullscreen.black_surface) + e_surface_destroy(ess->fullscreen.black_surface); + wl_list_remove(&ess->link); + free(ess); +} + +static void +_shell_cb_bind(struct wl_client *client, void *data, unsigned int version __UNUSED__, unsigned int id) +{ + struct wl_shell *shell; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + if (!(shell = data)) return; + wl_client_add_object(client, &wl_shell_interface, + &_e_shell_interface, id, shell); +} + +static void +_shell_cb_bind_desktop(struct wl_client *client, void *data, unsigned int version __UNUSED__, unsigned int id) +{ + struct wl_shell *shell; + struct wl_resource *resource; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + if (!(shell = data)) return; + resource = wl_client_add_object(client, &desktop_shell_interface, + &_e_desktop_shell_interface, id, shell); + resource->destroy = _shell_cb_unbind_desktop; +} + +static void +_shell_cb_unbind_desktop(struct wl_resource *resource) +{ + struct wl_shell *shell; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + shell = resource->data; + if (shell->locked) + { + /* TODO: resume desktop */ + } + shell->prepare_event_sent = EINA_FALSE; + free(resource); +} + +static void +_shell_cb_get_shell_surface(struct wl_client *client, struct wl_resource *resource, unsigned int id, struct wl_resource *surface_resource) +{ + E_Surface *es; + E_Shell_Surface *ess; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + es = surface_resource->data; + + if (_shell_get_shell_surface(es)) + { + wl_resource_post_error(surface_resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "wl_shell::shell_surface already requested"); + return; + } + + if (!(ess = calloc(1, sizeof(E_Shell_Surface)))) + { + wl_resource_post_no_memory(resource); + return; + } + + ess->resource.destroy = _shell_cb_destroy_shell_surface; + ess->resource.object.id = id; + ess->resource.object.interface = &wl_shell_surface_interface; + ess->resource.object.implementation = + (void (**)(void)) &_e_shell_surface_interface; + ess->resource.data = ess; + + ess->saved_pos_valid = EINA_FALSE; + ess->surface = es; + ess->fullscreen.type = WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT; + ess->fullscreen.framerate = 0; + ess->fullscreen.black_surface = NULL; + wl_list_init(&ess->fullscreen.transform.link); + + ess->surface_destroy_listener.func = _shell_cb_handle_surface_destroy; + wl_list_insert(es->surface.resource.destroy_listener_list.prev, + &ess->surface_destroy_listener.link); + + wl_list_init(&ess->link); + wl_list_init(&ess->rotation.transform.link); + e_matrix_init(&ess->rotation.rotation); + + ess->type = E_SHELL_SURFACE_NONE; + + wl_client_add_resource(client, &ess->resource); +} + +static void +_shell_cb_handle_surface_destroy(struct wl_listener *listener, struct wl_resource *resource __UNUSED__, unsigned int timestamp) +{ + E_Shell_Surface *ess; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + ess = container_of(listener, E_Shell_Surface, surface_destroy_listener); + ess->surface = NULL; + wl_resource_destroy(&ess->resource, timestamp); +} + +static void +_shell_cb_handle_lock_surface_destroy(struct wl_listener *listener, struct wl_resource *resource __UNUSED__, unsigned int timestamp __UNUSED__) +{ + struct wl_shell *shell; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + shell = container_of(listener, struct wl_shell, lock_surface_listener); + shell->lock_surface = NULL; +} + +static void +_shell_cb_activate(E_Shell *base, E_Surface *es, E_Input_Device *eid, unsigned int timestamp) +{ + struct wl_shell *shell; + E_Compositor *comp; + E_Shell_Surface_Type type = E_SHELL_SURFACE_NONE; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + shell = container_of(base, struct wl_shell, shell); + comp = shell->compositor; + + e_surface_activate(es, eid, timestamp); + + type = _shell_get_shell_surface_type(es); + switch (type) + { + case E_SHELL_SURFACE_BACKGROUND: + case E_SHELL_SURFACE_PANEL: + case E_SHELL_SURFACE_LOCK: + break; + case E_SHELL_SURFACE_SCREENSAVER: + if (shell->lock_surface) + e_surface_restack(es, &shell->lock_surface->surface->layers); + break; + case E_SHELL_SURFACE_FULLSCREEN: + break; + default: + e_surface_restack(es, &shell->toplevel_layer.surfaces); + break; + } +} + +static void +_shell_cb_desktop_set_background(struct wl_client *client __UNUSED__, struct wl_resource *resource, struct wl_resource *output_resource, struct wl_resource *surface_resource) +{ + struct wl_shell *shell; + E_Shell_Surface *ess, *priv; + E_Surface *es; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + shell = resource->data; + ess = surface_resource->data; + es = ess->surface; + + if (_shell_reset_shell_surface_type(ess)) return; + + wl_list_for_each(priv, &shell->backgrounds, link) + { + if (priv->output == output_resource->data) + { + priv->surface->output = NULL; + wl_list_remove(&priv->surface->layers); + wl_list_remove(&priv->link); + break; + } + } + + ess->type = E_SHELL_SURFACE_BACKGROUND; + ess->output = output_resource->data; + wl_list_insert(&shell->backgrounds, &ess->link); + + e_surface_set_position(es, ess->output->x, ess->output->y); + + desktop_shell_send_configure(resource, e_compositor_get_time(), 0, + surface_resource, ess->output->current->w, + ess->output->current->h); +} + +static void +_shell_cb_desktop_set_panel(struct wl_client *client __UNUSED__, struct wl_resource *resource, struct wl_resource *output_resource, struct wl_resource *surface_resource) +{ + struct wl_shell *shell; + E_Shell_Surface *ess, *priv; + E_Surface *es; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + shell = resource->data; + ess = surface_resource->data; + es = ess->surface; + + if (_shell_reset_shell_surface_type(ess)) return; + + wl_list_for_each(priv, &shell->panels, link) + { + if (priv->output == output_resource->data) + { + priv->surface->output = NULL; + wl_list_remove(&priv->surface->layers); + wl_list_remove(&priv->link); + break; + } + } + + ess->type = E_SHELL_SURFACE_PANEL; + ess->output = output_resource->data; + + wl_list_insert(&shell->panels, &ess->link); + + e_surface_set_position(es, ess->output->x, ess->output->y); + + desktop_shell_send_configure(resource, e_compositor_get_time(), 0, + surface_resource, ess->output->current->w, + ess->output->current->h); +} + +static void +_shell_cb_desktop_set_lock_surface(struct wl_client *client __UNUSED__, struct wl_resource *resource, struct wl_resource *surface_resource) +{ + struct wl_shell *shell; + E_Shell_Surface *ess; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + shell = resource->data; + ess = surface_resource->data; + + if (_shell_reset_shell_surface_type(ess)) return; + + shell->prepare_event_sent = EINA_FALSE; + if (!shell->locked) return; + + shell->lock_surface = ess; + shell->lock_surface_listener.func = _shell_cb_handle_lock_surface_destroy; + wl_list_insert(&surface_resource->destroy_listener_list, + &shell->lock_surface_listener.link); + + shell->lock_surface->type = E_SHELL_SURFACE_LOCK; +} + +static void +_shell_cb_desktop_unlock(struct wl_client *client __UNUSED__, struct wl_resource *resource) +{ + struct wl_shell *shell; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + shell = resource->data; + shell->prepare_event_sent = EINA_FALSE; + if (shell->locked) + { + /* TODO: resume desktop */ + } +} + +static void +_shell_cb_shell_surface_move(struct wl_client *client __UNUSED__, struct wl_resource *resource, struct wl_resource *input_resource, unsigned int timestamp) +{ + E_Input_Device *eid; + E_Shell_Surface *ess; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + eid = input_resource->data; + ess = resource->data; + + if ((eid->input_device.button_count == 0) || + (eid->input_device.grab_time != timestamp) || + (eid->input_device.pointer_focus != &ess->surface->surface)) + return; + + if (!e_surface_move(ess->surface, eid, timestamp)) + wl_resource_post_no_memory(resource); +} + +static void +_shell_cb_shell_surface_resize(struct wl_client *client __UNUSED__, struct wl_resource *resource, struct wl_resource *input_resource, unsigned int timestamp, unsigned int edges) +{ + E_Input_Device *eid; + E_Shell_Surface *ess; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + eid = input_resource->data; + ess = resource->data; + + if (ess->type == E_SHELL_SURFACE_FULLSCREEN) return; + + if ((eid->input_device.button_count == 0) || + (eid->input_device.grab_time != timestamp) || + (eid->input_device.pointer_focus != &ess->surface->surface)) + return; + + if (!e_surface_resize(ess->surface, eid, timestamp, edges)) + wl_resource_post_no_memory(resource); +} + +static void +_shell_cb_shell_surface_set_toplevel(struct wl_client *client __UNUSED__, struct wl_resource *resource) +{ + E_Shell_Surface *ess; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + ess = resource->data; + + if (_shell_reset_shell_surface_type(ess)) return; + ess->type = E_SHELL_SURFACE_TOPLEVEL; +} + +static void +_shell_cb_shell_surface_set_transient(struct wl_client *client __UNUSED__, struct wl_resource *resource, struct wl_resource *parent_resource, int x, int y, unsigned int flags __UNUSED__) +{ + E_Shell_Surface *ess, *pess; + E_Surface *es, *pes; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + ess = resource->data; + es = ess->surface; + pess = parent_resource->data; + pes = pess->surface; + + if (_shell_reset_shell_surface_type(ess)) return; + + ess->output = pes->output; + e_surface_set_position(es, pes->geometry.x + x, pes->geometry.y + y); + ess->type = E_SHELL_SURFACE_TRANSIENT; +} + +static void +_shell_cb_shell_surface_set_fullscreen(struct wl_client *client __UNUSED__, struct wl_resource *resource, unsigned int method, unsigned int framerate, struct wl_resource *output_resource) +{ + E_Shell_Surface *ess; + E_Surface *es; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + ess = resource->data; + es = ess->surface; + + if (output_resource) + ess->output = output_resource->data; + else + ess->output = e_output_get_default(es->compositor); + + if (_shell_reset_shell_surface_type(ess)) return; + + ess->fullscreen_output = ess->output; + ess->fullscreen.type = method; + ess->fullscreen.framerate = framerate; + ess->type = E_SHELL_SURFACE_FULLSCREEN; + ess->sx = es->geometry.x; + ess->sy = es->geometry.y; + ess->saved_pos_valid = EINA_TRUE; + + if (es->output) ess->surface->force_configure = EINA_TRUE; + + wl_shell_surface_send_configure(&ess->resource, e_compositor_get_time(), + 0, ess->output->current->w, + ess->output->current->h); +} + +static void +_shell_cb_shell_surface_set_popup(struct wl_client *client __UNUSED__, struct wl_resource *resource, struct wl_resource *input_device_resource __UNUSED__, unsigned int timestamp __UNUSED__, struct wl_resource *parent_resource, int x, int y, unsigned int flags __UNUSED__) +{ + E_Shell_Surface *ess; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + ess = resource->data; + ess->type = E_SHELL_SURFACE_POPUP; + ess->parent = parent_resource->data; + ess->popup.x = x; + ess->popup.y = y; +} + +static void +_shell_cb_shell_surface_set_maximized(struct wl_client *client __UNUSED__, struct wl_resource *resource, struct wl_resource *output_resource) +{ + E_Shell_Surface *ess; + E_Surface *es; + /* struct wl_shell *shell = NULL; */ + unsigned int edges = 0; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + ess = resource->data; + es = ess->surface; + + if (output_resource) + ess->output = output_resource->data; + else + ess->output = e_output_get_default(es->compositor); + + if (_shell_reset_shell_surface_type(ess)) return; + + ess->sx = es->geometry.x; + ess->sy = es->geometry.y; + ess->saved_pos_valid = EINA_TRUE; + + /* TODO: handle getting panel size ?? */ + + edges = WL_SHELL_SURFACE_RESIZE_TOP | WL_SHELL_SURFACE_RESIZE_LEFT; + wl_shell_surface_send_configure(&ess->resource, e_compositor_get_time(), + edges, es->output->current->w, + es->output->current->h); + ess->type = E_SHELL_SURFACE_MAXIMIZED; +} + +static E_Shell_Surface * +_shell_get_shell_surface(E_Surface *es) +{ + struct wl_list *list; + struct wl_listener *listener; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + list = &es->surface.resource.destroy_listener_list; + + wl_list_for_each(listener, list, link) + if (listener->func == _shell_cb_handle_surface_destroy) + return container_of(listener, E_Shell_Surface, surface_destroy_listener); + + return NULL; +} + +static int +_shell_reset_shell_surface_type(E_Shell_Surface *ess) +{ + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + switch (ess->type) + { + case E_SHELL_SURFACE_FULLSCREEN: + _shell_unset_fullscreen(ess); + break; + case E_SHELL_SURFACE_MAXIMIZED: + ess->output = e_output_get_default(ess->surface->compositor); + e_surface_set_position(ess->surface, ess->sx, ess->sy); + break; + case E_SHELL_SURFACE_PANEL: + case E_SHELL_SURFACE_BACKGROUND: + wl_list_remove(&ess->link); + wl_list_init(&ess->link); + break; + case E_SHELL_SURFACE_SCREENSAVER: + case E_SHELL_SURFACE_LOCK: + wl_resource_post_error(&ess->resource, + WL_DISPLAY_ERROR_INVALID_METHOD, + "cannot reassign surface type"); + return -1; + break; + case E_SHELL_SURFACE_NONE: + case E_SHELL_SURFACE_TOPLEVEL: + case E_SHELL_SURFACE_TRANSIENT: + case E_SHELL_SURFACE_POPUP: + break; + } + + ess->type = E_SHELL_SURFACE_NONE; + return 0; +} + +static void +_shell_center_on_output(E_Surface *es, E_Output *output) +{ + E_Output_Mode *mode; + GLfloat x, y; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + mode = output->current; + x = (mode->w - es->geometry.w) / 2; + y = (mode->h - es->geometry.h) / 2; + e_surface_set_position(es, output->x + x, output->y + y); +} + +static void +_shell_map_fullscreen(E_Shell_Surface *ess) +{ + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + _shell_configure_fullscreen(ess); + _shell_stack_fullscreen(ess); +} + +static void +_shell_configure_fullscreen(E_Shell_Surface *ess) +{ + E_Output *output; + E_Surface *es; + E_Matrix *matrix; + float scale; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + output = ess->fullscreen_output; + es = ess->surface; + + _shell_center_on_output(es, output); + + if (!ess->fullscreen.black_surface) + { + ess->fullscreen.black_surface = + _shell_create_black_surface(es->compositor, output->x, output->y, + output->current->w, output->current->h); + } + + wl_list_remove(&ess->fullscreen.black_surface->layers); + wl_list_insert(&es->layers, &ess->fullscreen.black_surface->layers); + ess->fullscreen.black_surface->output = output; + + switch (ess->fullscreen.type) + { + case WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT: + break; + case WL_SHELL_SURFACE_FULLSCREEN_METHOD_SCALE: + matrix = &ess->fullscreen.transform.matrix; + e_matrix_init(matrix); + scale = (float)output->current->w / (float)es->geometry.w; + e_matrix_scale(matrix, scale, scale, 1); + wl_list_remove(&ess->fullscreen.transform.link); + wl_list_insert(es->geometry.transforms.prev, + &ess->fullscreen.transform.link); + e_surface_set_position(es, output->x, output->y); + break; + case WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER: + break; + case WL_SHELL_SURFACE_FULLSCREEN_METHOD_FILL: + break; + default: + break; + } +} + +static void +_shell_stack_fullscreen(E_Shell_Surface *ess) +{ + E_Surface *es; + struct wl_shell *shell; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + es = ess->surface; + shell = _shell_surface_get_shell(ess); + + wl_list_remove(&es->layers); + wl_list_remove(&ess->fullscreen.black_surface->layers); + + wl_list_insert(&shell->fullscreen_layer.surfaces, &es->layers); + wl_list_insert(&es->layers, &ess->fullscreen.black_surface->layers); + + e_surface_damage(es); + e_surface_damage(ess->fullscreen.black_surface); +} + +static void +_shell_unset_fullscreen(E_Shell_Surface *ess) +{ + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + ess->fullscreen.type = WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT; + ess->fullscreen.framerate = 0; + wl_list_remove(&ess->fullscreen.transform.link); + wl_list_init(&ess->fullscreen.transform.link); + e_surface_destroy(ess->fullscreen.black_surface); + ess->fullscreen.black_surface = NULL; + ess->fullscreen_output = NULL; + ess->surface->force_configure = EINA_TRUE; + e_surface_set_position(ess->surface, ess->sx, ess->sy); +} + +static struct wl_shell * +_shell_surface_get_shell(E_Shell_Surface *ess) +{ + E_Surface *es; + E_Shell *shell; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + es = ess->surface; + shell = es->compositor->shell; + return (struct wl_shell *)container_of(shell, struct wl_shell, shell); +} + +static E_Surface * +_shell_create_black_surface(E_Compositor *comp, GLfloat x, GLfloat y, int w, int h) +{ + E_Surface *es = NULL; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + if (!(es = e_surface_create(comp))) + return NULL; + + e_surface_configure(es, x, y, w, h); + e_surface_set_color(es, 0.0, 0.0, 0.0, 1); + + return es; +} + +static E_Shell_Surface_Type +_shell_get_shell_surface_type(E_Surface *es) +{ + E_Shell_Surface *ess; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + if (!(ess = _shell_get_shell_surface(es))) + return E_SHELL_SURFACE_NONE; + + return ess->type; +} + +static void +_shell_map_popup(E_Shell_Surface *ess, unsigned int timestamp) +{ + struct wl_input_device *device; + E_Surface *es, *parent; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + es = ess->surface; + parent = ess->parent->surface; + es->output = parent->output; + device = es->compositor->input_device; + + ess->popup.grab.interface = &_e_popup_grab_interface; + e_surface_update_transform(parent); + if (parent->transform.enabled) + ess->popup.parent_transform.matrix = parent->transform.matrix; + else + { + e_matrix_init(&ess->popup.parent_transform.matrix); + ess->popup.parent_transform.matrix.d[12] = parent->geometry.x; + ess->popup.parent_transform.matrix.d[13] = parent->geometry.y; + } + + wl_list_insert(es->geometry.transforms.prev, + &ess->popup.parent_transform.link); + e_surface_set_position(es, ess->popup.x, ess->popup.y); + + ess->popup.grab.input_device = device; + ess->popup.timestamp = device->grab_time; + ess->popup.initial_up = EINA_FALSE; + + wl_input_device_start_pointer_grab(ess->popup.grab.input_device, + &ess->popup.grab, ess->popup.timestamp); +} + +static void +_shell_cb_popup_grab_focus(struct wl_pointer_grab *grab, unsigned int timestamp, struct wl_surface *surface, int x, int y) +{ + struct wl_input_device *device; + E_Shell_Surface *priv; + struct wl_client *client; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + device = grab->input_device; + priv = container_of(grab, E_Shell_Surface, popup.grab); + client = priv->surface->surface.resource.client; + + if ((surface) && (surface->resource.client == client)) + { + wl_input_device_set_pointer_focus(device, surface, timestamp, x, y); + grab->focus = surface; + } + else + { + wl_input_device_set_pointer_focus(device, NULL, timestamp, 0, 0); + grab->focus = NULL; + } +} + +static void +_shell_cb_popup_grab_motion(struct wl_pointer_grab *grab, unsigned int timestamp, int sx, int sy) +{ + /* struct wl_input_device *device; */ + /* E_Surface_Move_Grab *move; */ + /* E_Surface *es; */ + + /* device = grab->input_device; */ + /* move = (E_Surface_Move_Grab *)grab; */ + + /* es = move->surface; */ + /* e_surface_configure(es, device->x + move->dx, device->y + move->dy, */ + /* es->geometry.w, es->geometry.h); */ + + struct wl_resource *resource; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + if ((resource = grab->input_device->pointer_focus_resource)) + wl_input_device_send_motion(resource, timestamp, sx, sy); +} + +static void +_shell_cb_popup_grab_button(struct wl_pointer_grab *grab, unsigned int timestamp, int button, int state) +{ + /* struct wl_input_device *device; */ + + /* device = grab->input_device; */ + /* if ((device->button_count == 0) && (state == 0)) */ + /* { */ + /* wl_input_device_end_pointer_grab(device, timestamp); */ + /* free(grab); */ + /* } */ + + struct wl_resource *resource; + E_Shell_Surface *ess = NULL; + + SLOGFN(__FILE__, __LINE__, __FUNCTION__); + + ess = container_of(grab, E_Shell_Surface, popup.grab); + + if ((resource = grab->input_device->pointer_focus_resource)) + wl_input_device_send_button(resource, timestamp, button, state); + else if ((state == 0) && + ((ess->popup.initial_up) || + (timestamp - ess->popup.timestamp > 500))) + { + wl_shell_surface_send_popup_done(&ess->resource); + wl_input_device_end_pointer_grab(grab->input_device, timestamp); + ess->popup.grab.input_device = NULL; + } + + if (state == 0) ess->popup.initial_up = EINA_TRUE; +} diff --git a/src/modules/wl_shell/e_mod_main.h b/src/modules/wl_shell/e_mod_main.h new file mode 100644 index 000000000..6713eaa47 --- /dev/null +++ b/src/modules/wl_shell/e_mod_main.h @@ -0,0 +1,93 @@ +#ifndef E_MOD_MAIN_H +# define E_MOD_MAIN_H + +# define SLOGFNS 1 + +# ifdef SLOGFNS +# include +# define SLOGFN(fl, ln, fn) printf("-E-SHELL: %25s: %5i - %s\n", fl, ln, fn); +# else +# define SLOGFN(fl, ln, fn) +# endif + +# include +# include "e_desktop_shell_protocol.h" + +typedef enum _E_Shell_Surface_Type E_Shell_Surface_Type; +typedef struct _E_Shell_Surface E_Shell_Surface; + +struct wl_shell +{ + E_Compositor *compositor; + E_Shell shell; + + E_Layer fullscreen_layer; + E_Layer panel_layer; + E_Layer toplevel_layer; + E_Layer background_layer; + E_Layer lock_layer; + + Eina_Bool locked : 1; + Eina_Bool prepare_event_sent : 1; + + E_Shell_Surface *lock_surface; + struct wl_listener lock_surface_listener; + + struct wl_list backgrounds, panels; +}; + +enum _E_Shell_Surface_Type +{ + E_SHELL_SURFACE_NONE, + E_SHELL_SURFACE_PANEL, + E_SHELL_SURFACE_BACKGROUND, + E_SHELL_SURFACE_LOCK, + E_SHELL_SURFACE_SCREENSAVER, + E_SHELL_SURFACE_TOPLEVEL, + E_SHELL_SURFACE_TRANSIENT, + E_SHELL_SURFACE_FULLSCREEN, + E_SHELL_SURFACE_MAXIMIZED, + E_SHELL_SURFACE_POPUP +}; + +struct _E_Shell_Surface +{ + struct wl_resource resource; + E_Surface *surface; + struct wl_listener surface_destroy_listener; + E_Shell_Surface *parent; + E_Shell_Surface_Type type; + int sx, sy; + Eina_Bool saved_pos_valid : 1; + struct + { + E_Transform transform; + E_Matrix rotation; + } rotation; + struct + { + struct wl_pointer_grab grab; + unsigned int timestamp; + int x, y; + E_Transform parent_transform; + Eina_Bool initial_up : 1; + } popup; + struct + { + enum wl_shell_surface_fullscreen_method type; + E_Transform transform; + unsigned int framerate; + E_Surface *black_surface; + } fullscreen; + + E_Output *output, *fullscreen_output; + struct wl_list link; +}; + +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); + +#endif diff --git a/src/modules/wl_shell/module.desktop b/src/modules/wl_shell/module.desktop new file mode 100644 index 000000000..5f335ae41 --- /dev/null +++ b/src/modules/wl_shell/module.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Link +Name=Wayland Shell +Icon=e-module-wl_shell +Comment=Enlightenment Wayland Shell +X-Enlightenment-ModuleType=look