#include "e.h" #include "e_comp_wl.h" #include "e_surface.h" #include /* compositor function prototypes */ static void _e_comp_wl_cb_bind(struct wl_client *client, void *data EINA_UNUSED, unsigned int version EINA_UNUSED, unsigned int id); static Eina_Bool _e_comp_wl_cb_read(void *data EINA_UNUSED, Ecore_Fd_Handler *hdl EINA_UNUSED); static Eina_Bool _e_comp_wl_cb_idle(void *data EINA_UNUSED); static Eina_Bool _e_comp_wl_cb_module_idle(void *data EINA_UNUSED); /* compositor interface prototypes */ static void _e_comp_wl_cb_surface_create(struct wl_client *client, struct wl_resource *resource, unsigned int id); static void _e_comp_wl_cb_surface_destroy(struct wl_resource *resource); static void _e_comp_wl_cb_region_create(struct wl_client *client, struct wl_resource *resource, unsigned int id); static void _e_comp_wl_cb_region_destroy(struct wl_resource *resource); /* input function prototypes */ static Eina_Bool _e_comp_wl_input_init(void); static void _e_comp_wl_input_shutdown(void); static void _e_comp_wl_input_cb_bind(struct wl_client *client, void *data, unsigned int version EINA_UNUSED, unsigned int id); static void _e_comp_wl_input_cb_unbind(struct wl_resource *resource); static struct xkb_keymap *_e_comp_wl_input_keymap_get(void); static int _e_comp_wl_input_keymap_fd_get(off_t size); static E_Wayland_Keyboard_Info *_e_comp_wl_input_keyboard_info_get(struct xkb_keymap *keymap); /* input interface prototypes */ static void _e_comp_wl_input_cb_pointer_get(struct wl_client *client, struct wl_resource *resource, unsigned int id); static void _e_comp_wl_input_cb_keyboard_get(struct wl_client *client, struct wl_resource *resource, unsigned int id); static void _e_comp_wl_input_cb_touch_get(struct wl_client *client, struct wl_resource *resource, unsigned int id); /* pointer function prototypes */ static void _e_comp_wl_pointer_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED); static void _e_comp_wl_pointer_configure(E_Wayland_Surface *ews, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h); static void _e_comp_wl_pointer_unmap(E_Wayland_Surface *ews); /* pointer interface prototypes */ static void _e_comp_wl_pointer_cb_cursor_set(struct wl_client *client, struct wl_resource *resource, unsigned int serial, struct wl_resource *surface_resource, int x, int y); /* region interface prototypes */ static void _e_comp_wl_region_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource); static void _e_comp_wl_region_cb_add(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int x, int y, int w, int h); static void _e_comp_wl_region_cb_subtract(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int x, int y, int w, int h); /* surface function prototypes */ static void _e_comp_wl_surface_cb_pending_buffer_destroy(struct wl_listener *listener, void *data EINA_UNUSED); static void _e_comp_wl_surface_cb_frame_destroy(struct wl_resource *resource); static void _e_comp_wl_surface_buffer_reference(E_Wayland_Surface *ews, struct wl_buffer *buffer); static void _e_comp_wl_surface_buffer_reference_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED); /* surface interface prototypes */ static void _e_comp_wl_surface_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource); static void _e_comp_wl_surface_cb_attach(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *buffer_resource, int x, int y); static void _e_comp_wl_surface_cb_damage(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int x, int y, int w, int h); static void _e_comp_wl_surface_cb_frame(struct wl_client *client, struct wl_resource *resource, unsigned int callback); static void _e_comp_wl_surface_cb_opaque_region_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *region_resource); static void _e_comp_wl_surface_cb_input_region_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *region_resource); static void _e_comp_wl_surface_cb_commit(struct wl_client *client EINA_UNUSED, struct wl_resource *resource); /* local wayland interfaces */ static const struct wl_compositor_interface _e_compositor_interface = { _e_comp_wl_cb_surface_create, _e_comp_wl_cb_region_create }; static const struct wl_seat_interface _e_input_interface = { _e_comp_wl_input_cb_pointer_get, _e_comp_wl_input_cb_keyboard_get, _e_comp_wl_input_cb_touch_get, }; static const struct wl_pointer_interface _e_pointer_interface = { _e_comp_wl_pointer_cb_cursor_set }; static const struct wl_region_interface _e_region_interface = { _e_comp_wl_region_cb_destroy, _e_comp_wl_region_cb_add, _e_comp_wl_region_cb_subtract }; static const struct wl_surface_interface _e_surface_interface = { _e_comp_wl_surface_cb_destroy, _e_comp_wl_surface_cb_attach, _e_comp_wl_surface_cb_damage, _e_comp_wl_surface_cb_frame, _e_comp_wl_surface_cb_opaque_region_set, _e_comp_wl_surface_cb_input_region_set, _e_comp_wl_surface_cb_commit, NULL // cb_buffer_transform_set }; /* local variables */ static Ecore_Idler *_module_idler = NULL; /* external variables */ E_Wayland_Compositor *_e_wl_comp; /* external functions */ EINTERN Eina_Bool e_comp_wl_init(void) { int fd = 0; /* try to allocate space for a new compositor */ if (!(_e_wl_comp = E_NEW(E_Wayland_Compositor, 1))) return EINA_FALSE; /* try to create a wayland display */ if (!(_e_wl_comp->wl.display = wl_display_create())) { ERR("Could not create a Wayland Display: %m"); goto err; } /* init compositor signals */ wl_signal_init(&_e_wl_comp->signals.destroy); wl_signal_init(&_e_wl_comp->signals.activate); wl_signal_init(&_e_wl_comp->signals.kill); wl_signal_init(&_e_wl_comp->signals.seat); /* try to add compositor to the displays globals */ if (!wl_display_add_global(_e_wl_comp->wl.display, &wl_compositor_interface, _e_wl_comp, _e_comp_wl_cb_bind)) { ERR("Could not add compositor to globals: %m"); goto err; } /* init data device manager */ wl_data_device_manager_init(_e_wl_comp->wl.display); /* try to init shm mechanism */ if (wl_display_init_shm(_e_wl_comp->wl.display) < 0) ERR("Could not initialize SHM: %m"); #ifdef HAVE_WAYLAND_EGL /* try to get the egl display * * NB: This is interesting....if we try to eglGetDisplay and pass in the * wayland display, then EGL fails due to XCB not owning the event queue. * If we pass it a NULL, it inits just fine */ _e_wl_comp->egl.display = eglGetDisplay(NULL); if (_e_wl_comp->egl.display == EGL_NO_DISPLAY) ERR("Could not get EGL display: %m"); else { EGLint major, minor; /* try to initialize egl */ if (!eglInitialize(_e_wl_comp->egl.display, &major, &minor)) { ERR("Could not initialize EGL: %m"); eglTerminate(_e_wl_comp->egl.display); } else { EGLint n; EGLint attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, EGL_ALPHA_SIZE, 1, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE }; if ((!eglChooseConfig(_e_wl_comp->egl.display, attribs, &_e_wl_comp->egl.config, 1, &n) || (n == 0))) { ERR("Could not choose EGL config: %m"); eglTerminate(_e_wl_comp->egl.display); } } } #endif /* try to initialize input */ if (!_e_comp_wl_input_init()) { ERR("Could not initialize input: %m"); goto err; } /* add an idler for deferred shell module loading */ _module_idler = ecore_idler_add(_e_comp_wl_cb_module_idle, NULL); /* get the displays event loop */ _e_wl_comp->wl.loop = wl_display_get_event_loop(_e_wl_comp->wl.display); /* get the event loop's file descriptor so we can listen on it */ fd = wl_event_loop_get_fd(_e_wl_comp->wl.loop); /* add the fd to E's main loop */ _e_wl_comp->fd_handler = ecore_main_fd_handler_add(fd, ECORE_FD_READ, _e_comp_wl_cb_read, NULL, NULL, NULL); /* add an idler for flushing clients */ _e_wl_comp->idler = ecore_idle_enterer_add(_e_comp_wl_cb_idle, NULL); /* TODO: event handlers ?? */ /* try to add a display socket */ if (wl_display_add_socket(_e_wl_comp->wl.display, NULL) < 0) { ERR("Could not add a Wayland Display socket: %m"); goto err; } /* return success */ return EINA_TRUE; err: /* remove the module idler */ if (_module_idler) ecore_idler_del(_module_idler); #ifdef HAVE_WAYLAND_EGL /* terminate the egl display */ if (_e_wl_comp->egl.display) eglTerminate(_e_wl_comp->egl.display); eglReleaseThread(); #endif /* if we have a display, destroy it */ if (_e_wl_comp->wl.display) wl_display_destroy(_e_wl_comp->wl.display); /* free the compositor */ E_FREE(_e_wl_comp); /* return failure */ return EINA_FALSE; } EINTERN void e_comp_wl_shutdown(void) { E_Module *mod = NULL; /* remove the idler */ if (_e_wl_comp->idler) ecore_idler_del(_e_wl_comp->idler); /* remove the fd handler */ if (_e_wl_comp->fd_handler) ecore_main_fd_handler_del(_e_wl_comp->fd_handler); /* shutdown input */ _e_comp_wl_input_shutdown(); #ifdef HAVE_WAYLAND_EGL /* terminate the egl display */ if (_e_wl_comp->egl.display) eglTerminate(_e_wl_comp->egl.display); eglReleaseThread(); #endif /* if we have a display, destroy it */ if (_e_wl_comp->wl.display) wl_display_destroy(_e_wl_comp->wl.display); /* free the compositor */ E_FREE(_e_wl_comp); /* disable the loaded shell module */ /* TODO: we should have a config variable somewhere to store which * shell we want to unload (tablet, mobile, etc) */ if ((mod = e_module_find("wl_desktop_shell"))) e_module_disable(mod); } EAPI unsigned int e_comp_wl_time_get(void) { struct timeval tm; gettimeofday(&tm, NULL); return (tm.tv_sec * 1000 + tm.tv_usec / 1000); } EAPI void e_comp_wl_input_modifiers_update(unsigned int serial) { struct wl_keyboard *kbd; struct wl_keyboard_grab *grab; unsigned int pressed = 0, latched = 0, locked = 0, group = 0; Eina_Bool changed = EINA_FALSE; /* check for valid keyboard */ if (!(kbd = _e_wl_comp->input->wl.seat.keyboard)) return; /* try to get the current keyboard's grab interface. * Fallback to the default grab */ if (!(grab = kbd->grab)) grab = &kbd->default_grab; pressed = xkb_state_serialize_mods(_e_wl_comp->input->xkb.state, XKB_STATE_DEPRESSED); latched = xkb_state_serialize_mods(_e_wl_comp->input->xkb.state, XKB_STATE_LATCHED); locked = xkb_state_serialize_mods(_e_wl_comp->input->xkb.state, XKB_STATE_LOCKED); group = xkb_state_serialize_group(_e_wl_comp->input->xkb.state, XKB_STATE_EFFECTIVE); if ((pressed != kbd->modifiers.mods_depressed) || (latched != kbd->modifiers.mods_latched) || (locked != kbd->modifiers.mods_locked) || (group != kbd->modifiers.group)) changed = EINA_TRUE; kbd->modifiers.mods_depressed = pressed; kbd->modifiers.mods_latched = latched; kbd->modifiers.mods_locked = locked; kbd->modifiers.group = group; /* TODO: update leds ? */ if (changed) grab->interface->modifiers(grab, serial, kbd->modifiers.mods_depressed, kbd->modifiers.mods_latched, kbd->modifiers.mods_locked, kbd->modifiers.group); } /* local functions */ static void _e_comp_wl_cb_bind(struct wl_client *client, void *data EINA_UNUSED, unsigned int version EINA_UNUSED, unsigned int id) { /* add the compositor object to the client */ wl_client_add_object(client, &wl_compositor_interface, &_e_compositor_interface, id, _e_wl_comp); } static Eina_Bool _e_comp_wl_cb_read(void *data EINA_UNUSED, Ecore_Fd_Handler *hdl EINA_UNUSED) { /* flush any events before we sleep */ wl_event_loop_dispatch(_e_wl_comp->wl.loop, 0); wl_display_flush_clients(_e_wl_comp->wl.display); return ECORE_CALLBACK_RENEW; } static Eina_Bool _e_comp_wl_cb_idle(void *data EINA_UNUSED) { /* flush any clients before we idle */ wl_display_flush_clients(_e_wl_comp->wl.display); return ECORE_CALLBACK_RENEW; } static Eina_Bool _e_comp_wl_cb_module_idle(void *data EINA_UNUSED) { E_Module *mod = NULL; /* if we are still in the process of loading modules, then we will wait */ if (e_module_loading_get()) return ECORE_CALLBACK_RENEW; /* try to find the shell module, and create it if not found * * TODO: we should have a config variable somewhere to store which * shell we want to load (tablet, mobile, etc) */ if (!(mod = e_module_find("wl_desktop_shell"))) mod = e_module_new("wl_desktop_shell"); /* if we have the module now, load it */ if (mod) { e_module_enable(mod); _module_idler = NULL; /* flush any pending events * * NB: This advertises out any globals so it needs to be deferred * until after the shell has been loaded */ wl_event_loop_dispatch(_e_wl_comp->wl.loop, 0); return ECORE_CALLBACK_CANCEL; } return ECORE_CALLBACK_RENEW; } /* compositor interface functions */ static void _e_comp_wl_cb_surface_create(struct wl_client *client, struct wl_resource *resource, unsigned int id) { E_Wayland_Surface *ews = NULL; /* try to allocate space for a new surface */ if (!(ews = E_NEW(E_Wayland_Surface, 1))) { wl_resource_post_no_memory(resource); return; } /* initialize the destroy signal */ wl_signal_init(&ews->wl.surface.resource.destroy_signal); /* initialize the link */ wl_list_init(&ews->wl.link); /* initialize the lists of frames */ wl_list_init(&ews->wl.frames); wl_list_init(&ews->pending.frames); ews->wl.surface.resource.client = NULL; /* set destroy function for pending buffers */ ews->pending.buffer_destroy.notify = _e_comp_wl_surface_cb_pending_buffer_destroy; /* initialize regions */ pixman_region32_init(&ews->region.opaque); pixman_region32_init(&ews->region.damage); pixman_region32_init(&ews->region.clip); pixman_region32_init_rect(&ews->region.input, INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX); /* initialize pending regions */ pixman_region32_init(&ews->pending.opaque); pixman_region32_init(&ews->pending.damage); pixman_region32_init_rect(&ews->pending.input, INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX); /* set some properties of the surface */ ews->wl.surface.resource.destroy = _e_comp_wl_cb_surface_destroy; ews->wl.surface.resource.object.id = id; ews->wl.surface.resource.object.interface = &wl_surface_interface; ews->wl.surface.resource.object.implementation = (void (**)(void))&_e_surface_interface; ews->wl.surface.resource.data = ews; /* add this surface to the client */ wl_client_add_resource(client, &ews->wl.surface.resource); /* add this surface to the list of surfaces */ _e_wl_comp->surfaces = eina_list_append(_e_wl_comp->surfaces, ews); } static void _e_comp_wl_cb_surface_destroy(struct wl_resource *resource) { E_Wayland_Surface *ews = NULL; E_Wayland_Surface_Frame_Callback *cb = NULL, *ncb = NULL; /* try to get the surface from this resource */ if (!(ews = container_of(resource, E_Wayland_Surface, wl.surface.resource))) return; /* if this surface is mapped, unmap it */ if (ews->mapped) { if (ews->unmap) ews->unmap(ews); } /* loop any pending surface frame callbacks and destroy them */ wl_list_for_each_safe(cb, ncb, &ews->pending.frames, wl.link) wl_resource_destroy(&cb->wl.resource); /* clear any pending regions */ pixman_region32_fini(&ews->pending.damage); pixman_region32_fini(&ews->pending.opaque); pixman_region32_fini(&ews->pending.input); /* remove the pending buffer from the list */ if (ews->pending.buffer) wl_list_remove(&ews->pending.buffer_destroy.link); /* dereference any existing buffers */ _e_comp_wl_surface_buffer_reference(ews, NULL); /* clear any active regions */ pixman_region32_fini(&ews->region.damage); pixman_region32_fini(&ews->region.opaque); pixman_region32_fini(&ews->region.input); pixman_region32_fini(&ews->region.clip); /* loop any active surface frame callbacks and destroy them */ wl_list_for_each_safe(cb, ncb, &ews->wl.frames, wl.link) wl_resource_destroy(&cb->wl.resource); /* remove this surface from the compositor's list of surfaces */ _e_wl_comp->surfaces = eina_list_remove(_e_wl_comp->surfaces, ews); /* free the allocated surface structure */ E_FREE(ews); } static void _e_comp_wl_cb_region_create(struct wl_client *client, struct wl_resource *resource, unsigned int id) { E_Wayland_Region *ewr = NULL; /* try to allocate space for a new region */ if (!(ewr = E_NEW_RAW(E_Wayland_Region, 1))) { wl_resource_post_no_memory(resource); return; } pixman_region32_init(&ewr->region); /* set some properties of the region */ ewr->wl.resource.destroy = _e_comp_wl_cb_region_destroy; ewr->wl.resource.object.id = id; ewr->wl.resource.object.interface = &wl_region_interface; ewr->wl.resource.object.implementation = (void (**)(void))&_e_region_interface; ewr->wl.resource.data = ewr; /* add this region to the client */ wl_client_add_resource(client, &ewr->wl.resource); } static void _e_comp_wl_cb_region_destroy(struct wl_resource *resource) { E_Wayland_Region *ewr = NULL; /* try to get the region from this resource */ if (!(ewr = container_of(resource, E_Wayland_Region, wl.resource))) return; /* tell pixman we are finished with this region */ pixman_region32_fini(&ewr->region); /* free the allocated region structure */ E_FREE(ewr); } /* input functions */ static Eina_Bool _e_comp_wl_input_init(void) { struct xkb_keymap *keymap; /* try to allocate space for a new compositor */ if (!(_e_wl_comp->input = E_NEW(E_Wayland_Input, 1))) return EINA_FALSE; /* initialize the seat */ wl_seat_init(&_e_wl_comp->input->wl.seat); /* try to add this input to the diplay's list of globals */ if (!wl_display_add_global(_e_wl_comp->wl.display, &wl_seat_interface, _e_wl_comp->input, _e_comp_wl_input_cb_bind)) { ERR("Could not add Input to Wayland Display Globals: %m"); goto err; } _e_wl_comp->input->pointer.surface = NULL; _e_wl_comp->input->pointer.surface_destroy.notify = _e_comp_wl_pointer_cb_destroy; _e_wl_comp->input->pointer.hot.x = 16; _e_wl_comp->input->pointer.hot.y = 16; /* initialize wayland pointer */ wl_pointer_init(&_e_wl_comp->input->wl.pointer); /* tell the seat about this pointer */ wl_seat_set_pointer(&_e_wl_comp->input->wl.seat, &_e_wl_comp->input->wl.pointer); /* set flag to indicate we have a pointer */ _e_wl_comp->input->has_pointer = EINA_TRUE; /* create the xkb context */ _e_wl_comp->xkb.context = xkb_context_new(0); /* try to fetch the keymap */ if ((keymap = _e_comp_wl_input_keymap_get())) { /* try to create new keyboard info */ _e_wl_comp->input->xkb.info = _e_comp_wl_input_keyboard_info_get(keymap); /* create new xkb state */ _e_wl_comp->input->xkb.state = xkb_state_new(keymap); /* unreference the keymap */ xkb_map_unref(keymap); } /* initialize the keyboard */ wl_keyboard_init(&_e_wl_comp->input->wl.keyboard); /* tell the seat about this keyboard */ wl_seat_set_keyboard(&_e_wl_comp->input->wl.seat, &_e_wl_comp->input->wl.keyboard); /* set flag to indicate we have a keyboard */ _e_wl_comp->input->has_keyboard = EINA_TRUE; wl_list_init(&_e_wl_comp->input->wl.link); /* append this input to the list */ _e_wl_comp->seats = eina_list_append(_e_wl_comp->seats, _e_wl_comp->input); /* emit a signal saying that input has been initialized */ wl_signal_emit(&_e_wl_comp->signals.seat, _e_wl_comp->input); /* return success */ return EINA_TRUE; err: /* release the wl_seat */ wl_seat_release(&_e_wl_comp->input->wl.seat); /* free the allocated input structure */ E_FREE(_e_wl_comp->input); /* return failure */ return EINA_FALSE; } static void _e_comp_wl_input_shutdown(void) { /* safety check */ if (!_e_wl_comp->input) return; /* destroy keyboard */ if (_e_wl_comp->input->xkb.info) { /* if we have a keymap, unreference it */ if (_e_wl_comp->input->xkb.info->keymap) xkb_map_unref(_e_wl_comp->input->xkb.info->keymap); /* if we have a keymap mmap'd area, unmap it */ if (_e_wl_comp->input->xkb.info->area) munmap(_e_wl_comp->input->xkb.info->area, _e_wl_comp->input->xkb.info->size); /* if we created an fd for keyboard input, close it */ if (_e_wl_comp->input->xkb.info->fd) close(_e_wl_comp->input->xkb.info->fd); /* free the allocated keyboard info structure */ E_FREE(_e_wl_comp->input->xkb.info); } /* unreference the xkb state we created */ if (_e_wl_comp->input->xkb.state) xkb_state_unref(_e_wl_comp->input->xkb.state); /* unreference the xkb context we created */ if (_e_wl_comp->xkb.context) xkb_context_unref(_e_wl_comp->xkb.context); /* TODO: destroy pointer surface * * NB: Currently, we do not create one */ wl_list_remove(&_e_wl_comp->input->wl.link); /* release the seat */ wl_seat_release(&_e_wl_comp->input->wl.seat); /* free the allocated input structure */ E_FREE(_e_wl_comp->input); } static void _e_comp_wl_input_cb_bind(struct wl_client *client, void *data, unsigned int version EINA_UNUSED, unsigned int id) { struct wl_seat *seat = NULL; struct wl_resource *resource = NULL; enum wl_seat_capability caps = 0; /* try to cast data to our seat */ if (!(seat = data)) return; /* add the seat object to the client */ resource = wl_client_add_object(client, &wl_seat_interface, &_e_input_interface, id, data); wl_list_insert(&seat->base_resource_list, &resource->link); /* set resource destroy callback */ resource->destroy = _e_comp_wl_input_cb_unbind; /* set capabilities based on seat */ if (seat->pointer) caps |= WL_SEAT_CAPABILITY_POINTER; if (seat->keyboard) caps |= WL_SEAT_CAPABILITY_KEYBOARD; if (seat->touch) caps |= WL_SEAT_CAPABILITY_TOUCH; /* inform the resource about the seat capabilities */ wl_seat_send_capabilities(resource, caps); } static void _e_comp_wl_input_cb_unbind(struct wl_resource *resource) { /* remove the link */ wl_list_remove(&resource->link); /* free the resource */ free(resource); } static struct xkb_keymap * _e_comp_wl_input_keymap_get(void) { struct xkb_rule_names names; memset(&names, 0, sizeof(names)); /* if we are running under X11, try to get the xkb rule names atom */ if (getenv("DISPLAY")) { Ecore_X_Atom rules = 0; Ecore_X_Window root = 0; int len = 0; unsigned char *data; /* TODO: FIXME: NB: * * The below commented out code is for using the "already" configured * E xkb settings in the wayland clients. The only Major problem with * that is: That the E_Config_XKB_Layout does not define a * 'RULES' which we need .... * */ /* E_Config_XKB_Layout *kbd_layout; */ /* kbd_layout = e_xkb_layout_get(); */ /* names.model = strdup(kbd_layout->model); */ /* names.layout = strdup(kbd_layout->name); */ root = ecore_x_window_root_first_get(); rules = ecore_x_atom_get("_XKB_RULES_NAMES"); ecore_x_window_prop_property_get(root, rules, ECORE_X_ATOM_STRING, 1024, &data, &len); if ((data) && (len > 0)) { names.rules = strdup((const char *)data); data += strlen((const char *)data) + 1; names.model = strdup((const char *)data); data += strlen((const char *)data) + 1; names.layout = strdup((const char *)data); // free(data); } } printf("Keymap\n"); printf("\tRules: %s\n", names.rules); printf("\tModel: %s\n", names.model); printf("\tLayout: %s\n", names.layout); return xkb_map_new_from_names(_e_wl_comp->xkb.context, &names, 0); } static int _e_comp_wl_input_keymap_fd_get(off_t size) { char tmp[PATH_MAX]; const char *path; int fd = 0; long flags; if (!(path = getenv("XDG_RUNTIME_DIR"))) return -1; strcpy(tmp, path); strcat(tmp, "/e-wl-keymap-XXXXXX"); if ((fd = mkstemp(tmp)) < 0) return -1; /* TODO: really should error check the returns here */ flags = fcntl(fd, F_GETFD); fcntl(fd, F_SETFD, flags | FD_CLOEXEC); if (ftruncate(fd, size) < 0) { close(fd); return -1; } unlink(tmp); return fd; } static E_Wayland_Keyboard_Info * _e_comp_wl_input_keyboard_info_get(struct xkb_keymap *keymap) { E_Wayland_Keyboard_Info *info = NULL; char *tmp; /* try to allocate space for the keyboard info structure */ if (!(info = E_NEW(E_Wayland_Keyboard_Info, 1))) return NULL; info->keymap = xkb_map_ref(keymap); /* init modifiers */ info->mod_shift = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_SHIFT); info->mod_caps = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_CAPS); info->mod_ctrl = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_CTRL); info->mod_alt = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_ALT); info->mod_super = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_LOGO); /* try to get a string of this keymap */ if (!(tmp = xkb_map_get_as_string(keymap))) { printf("Could not map get as string\n"); } info->size = strlen(tmp) + 1; /* try to create an fd we can listen on for input */ info->fd = _e_comp_wl_input_keymap_fd_get(info->size); if (info->fd < 0) { printf("Could not create keymap fd\n"); } info->area = mmap(NULL, info->size, PROT_READ | PROT_WRITE, MAP_SHARED, info->fd, 0); /* TODO: error check mmap */ strcpy(info->area, tmp); free(tmp); return info; } /* input interface functions */ static void _e_comp_wl_input_cb_pointer_get(struct wl_client *client, struct wl_resource *resource, unsigned int id) { E_Wayland_Input *input = NULL; struct wl_resource *ptr = NULL; /* try to cast the resource data to our input structure */ if (!(input = resource->data)) return; /* check that input has a pointer */ if (!input->has_pointer) return; /* add a pointer object to the client */ ptr = wl_client_add_object(client, &wl_pointer_interface, &_e_pointer_interface, id, input); wl_list_insert(&input->wl.seat.pointer->resource_list, &ptr->link); /* set pointer destroy callback */ ptr->destroy = _e_comp_wl_input_cb_unbind; /* if the pointer has a focused surface, set it */ if ((input->wl.seat.pointer->focus) && (input->wl.seat.pointer->focus->resource.client == client)) { /* tell pointer which surface is focused */ wl_pointer_set_focus(input->wl.seat.pointer, input->wl.seat.pointer->focus, input->wl.seat.pointer->x, input->wl.seat.pointer->y); } } static void _e_comp_wl_input_cb_keyboard_get(struct wl_client *client, struct wl_resource *resource, unsigned int id) { E_Wayland_Input *input = NULL; struct wl_resource *kbd = NULL; /* try to cast the resource data to our input structure */ if (!(input = resource->data)) return; /* check that input has a keyboard */ if (!input->has_keyboard) return; /* add a keyboard object to the client */ kbd = wl_client_add_object(client, &wl_keyboard_interface, NULL, id, input); wl_list_insert(&input->wl.seat.keyboard->resource_list, &kbd->link); /* set keyboard destroy callback */ kbd->destroy = _e_comp_wl_input_cb_unbind; /* send the current keymap to the keyboard object */ wl_keyboard_send_keymap(kbd, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, input->xkb.info->fd, input->xkb.info->size); /* test if keyboard has a focused client */ if ((input->wl.seat.keyboard->focus) && (input->wl.seat.keyboard->focus->resource.client == client)) { /* set keyboard focus */ wl_keyboard_set_focus(input->wl.seat.keyboard, input->wl.seat.keyboard->focus); /* update the keyboard focus in the data device */ wl_data_device_set_keyboard_focus(&input->wl.seat); } } static void _e_comp_wl_input_cb_touch_get(struct wl_client *client, struct wl_resource *resource, unsigned int id) { E_Wayland_Input *input = NULL; struct wl_resource *tch = NULL; /* try to cast the resource data to our input structure */ if (!(input = resource->data)) return; /* check that input has a touch */ if (!input->has_touch) return; /* add a touch object to the client */ tch = wl_client_add_object(client, &wl_touch_interface, NULL, id, input); wl_list_insert(&input->wl.seat.touch->resource_list, &tch->link); /* set touch destroy callback */ tch->destroy = _e_comp_wl_input_cb_unbind; } /* pointer functions */ static void _e_comp_wl_pointer_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED) { E_Wayland_Input *input = NULL; /* try to get the input from this listener */ if (!(input = container_of(listener, E_Wayland_Input, pointer.surface_destroy))) return; input->pointer.surface = NULL; } static void _e_comp_wl_pointer_configure(E_Wayland_Surface *ews, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h) { E_Wayland_Input *input = NULL; E_Wayland_Surface *focus = NULL; /* safety check */ if (!ews) return; /* see if this surface has 'input' */ if (!(input = ews->input)) return; input->pointer.hot.x -= x; input->pointer.hot.y -= y; /* configure the surface geometry */ ews->geometry.x = x; ews->geometry.h = h; ews->geometry.w = w; ews->geometry.h = h; ews->geometry.changed = EINA_TRUE; /* tell pixman we are finished with this region */ pixman_region32_fini(&ews->pending.input); /* reinitalize the pending input region */ pixman_region32_init(&ews->pending.input); /* do we have a focused surface ? */ if ((focus = (E_Wayland_Surface *)input->wl.seat.pointer->focus)) { /* NB: Ideally, I wanted to use the e_pointer methods here so that * the cursor would match the E theme, however Wayland currently * provides NO Method to get the cursor name :( so we are stuck * using the pixels from their cursor surface */ /* is it mapped ? */ if ((focus->mapped) && (focus->ee)) { Ecore_Window win; /* try to get the ecore_window */ if ((win = ecore_evas_window_get(focus->ee))) { void *pixels; /* grab the pixels from the cursor surface */ if ((pixels = wl_shm_buffer_get_data(ews->reference.buffer))) { Ecore_X_Cursor cur; /* create the new X cursor with this image */ cur = ecore_x_cursor_new(win, pixels, w, h, input->pointer.hot.x, input->pointer.hot.y); /* set the cursor on this window */ ecore_x_window_cursor_set(win, cur); /* free the cursor */ ecore_x_cursor_free(cur); } else ecore_x_window_cursor_set(win, 0); } } } } static void _e_comp_wl_pointer_unmap(E_Wayland_Surface *ews) { E_Wayland_Input *input = NULL; /* safety check */ if (!ews) return; if (!(input = ews->input)) return; wl_list_remove(&input->pointer.surface_destroy.link); if (input->pointer.surface) input->pointer.surface->configure = NULL; input->pointer.surface = NULL; } /* pointer interface functions */ static void _e_comp_wl_pointer_cb_cursor_set(struct wl_client *client, struct wl_resource *resource, unsigned int serial, struct wl_resource *surface_resource, int x, int y) { E_Wayland_Input *input = NULL; E_Wayland_Surface *ews = NULL; /* try to cast the resource data to our input structure */ if (!(input = resource->data)) return; /* if we were passed in a surface, try to cast it to our structure */ if (surface_resource) ews = (E_Wayland_Surface *)surface_resource->data; /* if this input has no pointer, get out */ if (!input->has_pointer) return; /* if the input has no current focus, get out */ if (!input->wl.seat.pointer->focus) return; if (input->wl.seat.pointer->focus->resource.client != client) return; if ((input->wl.seat.pointer->focus_serial - serial) > (UINT32_MAX / 2)) return; /* is the passed in surface the same as our pointer surface ? */ if ((ews) && (ews != input->pointer.surface)) { if (ews->configure) { wl_resource_post_error(&ews->wl.surface.resource, WL_DISPLAY_ERROR_INVALID_OBJECT, "Surface already configured"); return; } } /* if we have an existing pointer surface, unmap it */ if (input->pointer.surface) { /* call the unmap function */ if (input->pointer.surface->unmap) input->pointer.surface->unmap(input->pointer.surface); } input->pointer.surface = ews; /* if we don't have a pointer surface, we are done here */ if (!ews) return; /* set the destroy listener */ wl_signal_add(&ews->wl.surface.resource.destroy_signal, &input->pointer.surface_destroy); /* set some properties on this surface */ ews->unmap = _e_comp_wl_pointer_unmap; ews->configure = _e_comp_wl_pointer_configure; ews->input = input; /* update input structure with new values */ input->pointer.hot.x = x; input->pointer.hot.y = y; if (ews->reference.buffer) { Evas_Coord bw = 0, bh = 0; /* grab the size of the buffer */ bw = ews->reference.buffer->width; bh = ews->reference.buffer->height; /* configure the pointer surface */ _e_comp_wl_pointer_configure(ews, 0, 0, bw, bh); } } /* region interface functions */ static void _e_comp_wl_region_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource) { wl_resource_destroy(resource); } static void _e_comp_wl_region_cb_add(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int x, int y, int w, int h) { E_Wayland_Region *ewr = NULL; /* try to cast the resource data to our region structure */ if (!(ewr = resource->data)) return; /* tell pixman to union this region with any previous one */ pixman_region32_union_rect(&ewr->region, &ewr->region, x, y, w, h); } static void _e_comp_wl_region_cb_subtract(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int x, int y, int w, int h) { E_Wayland_Region *ewr = NULL; pixman_region32_t region; /* try to cast the resource data to our region structure */ if (!(ewr = resource->data)) return; /* ask pixman to create a new temporary rect */ pixman_region32_init_rect(®ion, x, y, w, h); /* ask pixman to subtract this temp rect from the existing region */ pixman_region32_subtract(&ewr->region, &ewr->region, ®ion); /* tell pixman we are finished with the temporary rect */ pixman_region32_fini(®ion); } /* surface functions */ static void _e_comp_wl_surface_cb_pending_buffer_destroy(struct wl_listener *listener, void *data EINA_UNUSED) { E_Wayland_Surface *ews = NULL; /* try to get the surface from this listener */ if (!(ews = container_of(listener, E_Wayland_Surface, pending.buffer_destroy))) return; /* set surface pending buffer to null */ ews->pending.buffer = NULL; } static void _e_comp_wl_surface_cb_frame_destroy(struct wl_resource *resource) { E_Wayland_Surface_Frame_Callback *cb = NULL; /* try to cast the resource data to our surface frame callback structure */ if (!(cb = resource->data)) return; wl_list_remove(&cb->wl.link); /* free the allocated callback structure */ E_FREE(cb); } static void _e_comp_wl_surface_buffer_reference(E_Wayland_Surface *ews, struct wl_buffer *buffer) { /* check for valid surface */ if (!ews) return; /* if the surface already has a buffer referenced and it is not the * same as the one passed in */ if ((ews->reference.buffer) && (buffer != ews->reference.buffer)) { /* decrement the reference buffer busy count */ ews->reference.buffer->busy_count--; /* if the compositor is finished with this referenced buffer, then * we need to release it */ if (ews->reference.buffer->busy_count == 0) { if (ews->reference.buffer->resource.client) wl_resource_queue_event(&ews->reference.buffer->resource, WL_BUFFER_RELEASE); } /* remove the destroy link on the referenced buffer */ wl_list_remove(&ews->reference.buffer_destroy.link); } /* if we are passed in a buffer and it is not the one referenced */ if ((buffer) && (buffer != ews->reference.buffer)) { /* increment busy count */ buffer->busy_count++; /* setup destroy signal */ wl_signal_add(&buffer->resource.destroy_signal, &ews->reference.buffer_destroy); } /* set buffer reference */ ews->reference.buffer = buffer; /* setup destroy listener */ ews->reference.buffer_destroy.notify = _e_comp_wl_surface_buffer_reference_cb_destroy; } static void _e_comp_wl_surface_buffer_reference_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED) { E_Wayland_Surface *ews = NULL; /* try to get the surface from this listener */ ews = container_of(listener, E_Wayland_Surface, reference.buffer_destroy); if (!ews) return; /* set referenced buffer to null */ ews->reference.buffer = NULL; } /* surface interface functionss */ static void _e_comp_wl_surface_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource) { wl_resource_destroy(resource); } static void _e_comp_wl_surface_cb_attach(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *buffer_resource, int x, int y) { E_Wayland_Surface *ews = NULL; struct wl_buffer *buffer = NULL; /* try to cast the resource data to our surface structure */ if (!(ews = resource->data)) return; if (buffer_resource) buffer = buffer_resource->data; /* reference any existing buffers */ _e_comp_wl_surface_buffer_reference(ews, buffer); /* if we are setting a null buffer, then unmap the surface */ if (!buffer) { if (ews->mapped) { if (ews->unmap) ews->unmap(ews); } } /* if we already have a pending buffer, remove the listener */ if (ews->pending.buffer) wl_list_remove(&ews->pending.buffer_destroy.link); /* set some pending values */ ews->pending.x = x; ews->pending.y = y; ews->pending.buffer = buffer; ews->pending.new_buffer = EINA_TRUE; /* if we were given a buffer, initialize the destroy signal */ if (buffer) wl_signal_add(&buffer->resource.destroy_signal, &ews->pending.buffer_destroy); } static void _e_comp_wl_surface_cb_damage(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int x, int y, int w, int h) { E_Wayland_Surface *ews = NULL; /* try to cast the resource data to our surface structure */ if (!(ews = resource->data)) return; /* tell pixman to add this damage to pending */ pixman_region32_union_rect(&ews->pending.damage, &ews->pending.damage, x, y, w, h); } static void _e_comp_wl_surface_cb_frame(struct wl_client *client, struct wl_resource *resource, unsigned int callback) { E_Wayland_Surface *ews = NULL; E_Wayland_Surface_Frame_Callback *cb = NULL; /* try to cast the resource data to our surface structure */ if (!(ews = resource->data)) return; /* try to allocate space for a new frame callback */ if (!(cb = E_NEW(E_Wayland_Surface_Frame_Callback, 1))) { wl_resource_post_no_memory(resource); return; } /* set some properties on the callback */ cb->wl.resource.object.interface = &wl_callback_interface; cb->wl.resource.object.id = callback; cb->wl.resource.destroy = _e_comp_wl_surface_cb_frame_destroy; cb->wl.resource.client = client; cb->wl.resource.data = cb; /* add frame callback to client */ wl_client_add_resource(client, &cb->wl.resource); /* add this callback to the surface list of pending frames */ wl_list_insert(ews->pending.frames.prev, &cb->wl.link); } static void _e_comp_wl_surface_cb_opaque_region_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *region_resource) { E_Wayland_Surface *ews = NULL; /* try to cast the resource data to our surface structure */ if (!(ews = resource->data)) return; if (region_resource) { E_Wayland_Region *ewr = NULL; /* copy this region to the pending opaque region */ if ((ewr = region_resource->data)) pixman_region32_copy(&ews->pending.opaque, &ewr->region); } else { /* tell pixman we are finished with this region */ pixman_region32_fini(&ews->pending.opaque); /* reinitalize the pending opaque region */ pixman_region32_init(&ews->pending.opaque); } } static void _e_comp_wl_surface_cb_input_region_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *region_resource) { E_Wayland_Surface *ews = NULL; /* try to cast the resource data to our surface structure */ if (!(ews = resource->data)) return; if (region_resource) { E_Wayland_Region *ewr = NULL; /* copy this region to the pending input region */ if ((ewr = region_resource->data)) pixman_region32_copy(&ews->pending.input, &ewr->region); } else { /* tell pixman we are finished with this region */ pixman_region32_fini(&ews->pending.input); /* reinitalize the pending input region */ pixman_region32_init_rect(&ews->pending.input, INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX); } } static void _e_comp_wl_surface_cb_commit(struct wl_client *client EINA_UNUSED, struct wl_resource *resource) { E_Wayland_Surface *ews = NULL; Evas_Coord bw = 0, bh = 0; pixman_region32_t opaque; pixman_box32_t *rects; int n = 0; /* FIXME */ /* try to cast the resource data to our surface structure */ if (!(ews = resource->data)) return; /* if we have a pending buffer or a new pending buffer, attach it */ if ((ews->pending.buffer) || (ews->pending.new_buffer)) { /* reference the pending buffer */ _e_comp_wl_surface_buffer_reference(ews, ews->pending.buffer); /* if the pending buffer is NULL, unmap the surface */ if (!ews->pending.buffer) { if (ews->mapped) { if (ews->unmap) ews->unmap(ews); } } else { if (ews->obj) { void *data; bw = ews->pending.buffer->width; bh = ews->pending.buffer->height; /* grab the pixel data from the buffer */ data = wl_shm_buffer_get_data(ews->pending.buffer); /* send the pixel data to the smart object */ e_surface_image_set(ews->obj, bw, bh, data); } } } /* if we have a reference to a buffer, get it's size */ if (ews->reference.buffer) { bw = ews->reference.buffer->width; bh = ews->reference.buffer->height; } /* if we have a new pending buffer, call configure */ if ((ews->configure) && (ews->pending.new_buffer)) ews->configure(ews, ews->geometry.x, ews->geometry.y, bw, bh); if (ews->pending.buffer) wl_list_remove(&ews->pending.buffer_destroy.link); /* set some pending values */ ews->pending.buffer = NULL; ews->pending.x = 0; ews->pending.y = 0; ews->pending.new_buffer = EINA_FALSE; /* set surface damage */ pixman_region32_union(&ews->region.damage, &ews->region.damage, &ews->pending.damage); pixman_region32_intersect_rect(&ews->region.damage, &ews->region.damage, 0, 0, ews->geometry.w, ews->geometry.h); /* empty any pending damage */ pixman_region32_fini(&ews->pending.damage); pixman_region32_init(&ews->pending.damage); /* get the extent of the damage region */ rects = pixman_region32_rectangles(&ews->region.damage, &n); while (n--) { pixman_box32_t *r; r = &rects[n]; /* send damages to the image */ e_surface_damage_add(ews->obj, r->x1, r->y1, r->x2, r->y2); } /* tell pixman we are finished with this region */ /* pixman_region32_fini(&ews->region.damage); */ /* reinitalize the damage region */ /* pixman_region32_init(&ews->region.damage); */ /* calculate new opaque region */ pixman_region32_init_rect(&opaque, 0, 0, ews->geometry.w, ews->geometry.h); pixman_region32_intersect(&opaque, &opaque, &ews->pending.opaque); /* if new opaque region is not equal to the current one, then update */ if (!pixman_region32_equal(&opaque, &ews->region.opaque)) { pixman_region32_copy(&ews->region.opaque, &opaque); ews->geometry.changed = EINA_TRUE; } /* tell pixman we are done with this temporary region */ pixman_region32_fini(&opaque); /* clear any existing input region */ pixman_region32_fini(&ews->region.input); /* initialize a new input region */ pixman_region32_init_rect(&ews->region.input, 0, 0, ews->geometry.w, ews->geometry.h); /* put pending input region into new input region */ pixman_region32_intersect(&ews->region.input, &ews->region.input, &ews->pending.input); /* check for valid input region */ // if (pixman_region32_not_empty(&ews->region.input)) { /* get the extent of the input region */ rects = pixman_region32_extents(&ews->region.input); /* update the smart object's input region */ if (ews->obj) e_surface_input_set(ews->obj, rects->x1, rects->y1, (rects->x2 - rects->x1), (rects->y2 - rects->y1)); } /* put any pending frame callbacks into active list */ wl_list_insert_list(&ews->wl.frames, &ews->pending.frames); /* clear list of pending frame callbacks */ wl_list_init(&ews->pending.frames); /* TODO: schedule repaint ?? */ }