enlightenment/src/modules/wl_desktop_shell/e_mod_main.c

1489 lines
44 KiB
C

#define E_COMP_WL
#include "e.h"
#include "e_desktop_shell_protocol.h"
#define XDG_SERVER_VERSION 4
static void
_e_shell_surface_parent_set(E_Client *ec, struct wl_resource *parent_resource)
{
E_Pixmap *pp;
E_Client *pc;
uint64_t pwin = 0;
if (!parent_resource)
{
ec->icccm.fetch.transient_for = EINA_FALSE;
ec->icccm.transient_for = 0;
if (ec->parent)
{
ec->parent->transients =
eina_list_remove(ec->parent->transients, ec);
if (ec->parent->modal == ec) ec->parent->modal = NULL;
ec->parent = NULL;
}
return;
}
else if (!(pp = wl_resource_get_user_data(parent_resource)))
{
ERR("Could not get parent resource pixmap");
return;
}
pwin = e_pixmap_window_get(pp);
/* find the parent client */
if (!(pc = e_pixmap_client_get(pp)))
pc = e_pixmap_find_client(E_PIXMAP_TYPE_WL, pwin);
e_pixmap_parent_window_set(ec->pixmap, pwin);
/* If we already have a parent, remove it */
if (ec->parent)
{
if (pc != ec->parent)
{
ec->parent->transients =
eina_list_remove(ec->parent->transients, ec);
if (ec->parent->modal == ec) ec->parent->modal = NULL;
ec->parent = NULL;
}
else
pc = NULL;
}
if ((pc) && (pc != ec) &&
(eina_list_data_find(pc->transients, ec) != ec))
{
pc->transients = eina_list_append(pc->transients, ec);
ec->parent = pc;
}
ec->icccm.fetch.transient_for = EINA_TRUE;
ec->icccm.transient_for = pwin;
}
static void
_e_shell_surface_mouse_down_helper(E_Client *ec, E_Binding_Event_Mouse_Button *ev, Eina_Bool move)
{
if (move)
{
/* tell E to start moving the client */
e_client_act_move_begin(ec, ev);
/* we have to get a reference to the window_move action here, or else
* when e_client stops the move we will never get notified */
ec->cur_mouse_action = e_action_find("window_move");
if (ec->cur_mouse_action)
e_object_ref(E_OBJECT(ec->cur_mouse_action));
}
else
{
/* tell E to start resizing the client */
e_client_act_resize_begin(ec, ev);
/* we have to get a reference to the window_resize action here,
* or else when e_client stops the resize we will never get notified */
ec->cur_mouse_action = e_action_find("window_resize");
if (ec->cur_mouse_action)
e_object_ref(E_OBJECT(ec->cur_mouse_action));
}
e_focus_event_mouse_down(ec);
}
static void
_e_shell_surface_destroy(struct wl_resource *resource)
{
E_Client *ec;
/* DBG("Shell Surface Destroy: %d", wl_resource_get_id(resource)); */
/* get the client for this resource */
if ((ec = wl_resource_get_user_data(resource)))
{
if (e_object_is_del(E_OBJECT(ec))) return;
if (ec->comp_data)
{
if (ec->comp_data->mapped)
{
if ((ec->comp_data->shell.surface) &&
(ec->comp_data->shell.unmap))
ec->comp_data->shell.unmap(ec->comp_data->shell.surface);
}
if (ec->parent)
{
ec->parent->transients =
eina_list_remove(ec->parent->transients, ec);
}
/* wl_resource_destroy(ec->comp_data->shell.surface); */
ec->comp_data->shell.surface = NULL;
}
}
}
static void
_e_shell_surface_cb_destroy(struct wl_resource *resource)
{
/* DBG("Shell Surface Destroy: %d", wl_resource_get_id(resource)); */
_e_shell_surface_destroy(resource);
}
static void
_e_shell_surface_cb_pong(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, uint32_t serial EINA_UNUSED)
{
E_Client *ec;
if ((ec = wl_resource_get_user_data(resource)))
{
ec->ping_ok = EINA_TRUE;
ec->hung = EINA_FALSE;
}
}
static void
_e_shell_surface_cb_move(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial EINA_UNUSED)
{
E_Client *ec;
E_Comp_Data *cdata;
E_Binding_Event_Mouse_Button ev;
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
if ((ec->maximized) || (ec->fullscreen)) return;
/* get compositor data from seat */
if (!(cdata = wl_resource_get_user_data(seat_resource)))
{
wl_resource_post_error(seat_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Comp_Data for Seat");
return;
}
switch (cdata->ptr.button)
{
case BTN_LEFT:
ev.button = 1;
break;
case BTN_MIDDLE:
ev.button = 2;
break;
case BTN_RIGHT:
ev.button = 3;
break;
default:
ev.button = cdata->ptr.button;
break;
}
e_comp_object_frame_xy_unadjust(ec->frame,
wl_fixed_to_int(cdata->ptr.x) + ec->client.x,
wl_fixed_to_int(cdata->ptr.y) + ec->client.y,
&ev.canvas.x, &ev.canvas.y);
_e_shell_surface_mouse_down_helper(ec, &ev, EINA_TRUE);
}
static void
_e_shell_surface_cb_resize(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial EINA_UNUSED, uint32_t edges)
{
E_Client *ec;
E_Comp_Data *cdata;
E_Binding_Event_Mouse_Button ev;
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
if ((edges == 0) || (edges > 15) ||
((edges & 3) == 3) || ((edges & 12) == 12)) return;
if ((ec->maximized) || (ec->fullscreen)) return;
/* get compositor data from seat */
if (!(cdata = wl_resource_get_user_data(seat_resource)))
{
wl_resource_post_error(seat_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Comp_Data for Seat");
return;
}
DBG("Comp Resize Edges Set: %d", edges);
cdata->resize.resource = resource;
cdata->resize.edges = edges;
cdata->ptr.grab_x = cdata->ptr.x;
cdata->ptr.grab_y = cdata->ptr.y;
switch (cdata->ptr.button)
{
case BTN_LEFT:
ev.button = 1;
break;
case BTN_MIDDLE:
ev.button = 2;
break;
case BTN_RIGHT:
ev.button = 3;
break;
default:
ev.button = cdata->ptr.button;
break;
}
e_comp_object_frame_xy_unadjust(ec->frame,
wl_fixed_to_int(cdata->ptr.x) + ec->client.x,
wl_fixed_to_int(cdata->ptr.y) + ec->client.y,
&ev.canvas.x, &ev.canvas.y);
_e_shell_surface_mouse_down_helper(ec, &ev, EINA_FALSE);
}
static void
_e_shell_surface_cb_toplevel_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
E_Client *ec;
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
/* set toplevel client properties */
ec->argb = EINA_TRUE;
ec->no_shape_cut = EINA_TRUE;
ec->borderless = !ec->internal;
ec->lock_border = EINA_TRUE;
ec->border.changed = ec->changes.border = !ec->borderless;
ec->netwm.type = E_WINDOW_TYPE_NORMAL;
ec->comp_data->set_win_type = EINA_TRUE;
if ((!ec->lock_user_maximize) && (ec->maximized))
e_client_unmaximize(ec, E_MAXIMIZE_BOTH);
if ((!ec->lock_user_fullscreen) && (ec->fullscreen))
e_client_unfullscreen(ec);
EC_CHANGED(ec);
}
static void
_e_shell_surface_cb_transient_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *parent_resource, int32_t x EINA_UNUSED, int32_t y EINA_UNUSED, uint32_t flags EINA_UNUSED)
{
E_Client *ec;
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
/* set this client as a transient for parent */
_e_shell_surface_parent_set(ec, parent_resource);
EC_CHANGED(ec);
}
static void
_e_shell_surface_cb_fullscreen_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, uint32_t method EINA_UNUSED, uint32_t framerate EINA_UNUSED, struct wl_resource *output_resource EINA_UNUSED)
{
E_Client *ec;
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
if (!ec->lock_user_fullscreen)
e_client_fullscreen(ec, e_config->fullscreen_policy);
}
static void
_e_shell_surface_cb_popup_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *seat_resource EINA_UNUSED, uint32_t serial EINA_UNUSED, struct wl_resource *parent_resource, int32_t x, int32_t y, uint32_t flags EINA_UNUSED)
{
E_Client *ec;
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
if (ec->comp_data)
{
ec->comp_data->popup.x = x;
ec->comp_data->popup.y = y;
}
ec->argb = EINA_TRUE;
ec->no_shape_cut = EINA_TRUE;
ec->borderless = !ec->internal_elm_win;
ec->lock_border = EINA_TRUE;
ec->border.changed = ec->changes.border = !ec->borderless;
ec->changes.icon = !!ec->icccm.class;
ec->netwm.type = E_WINDOW_TYPE_POPUP_MENU;
ec->comp_data->set_win_type = EINA_TRUE;
ec->layer = E_LAYER_CLIENT_POPUP;
/* set this client as a transient for parent */
_e_shell_surface_parent_set(ec, parent_resource);
EC_CHANGED(ec);
}
static void
_e_shell_surface_cb_maximized_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *output_resource EINA_UNUSED)
{
E_Client *ec;
/* DBG("WL_SHELL: Surface Maximize: %d", wl_resource_get_id(resource)); */
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
/* tell E to maximize this client */
if (!ec->lock_user_maximize)
{
unsigned int edges = 0;
e_client_maximize(ec, ((e_config->maximize_policy & E_MAXIMIZE_TYPE) |
E_MAXIMIZE_BOTH));
edges = (WL_SHELL_SURFACE_RESIZE_TOP | WL_SHELL_SURFACE_RESIZE_LEFT);
wl_shell_surface_send_configure(resource, edges, ec->w, ec->h);
}
}
static void
_e_shell_surface_cb_title_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, const char *title)
{
E_Client *ec;
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
/* set title */
eina_stringshare_replace(&ec->icccm.title, title);
if (ec->frame) e_comp_object_frame_title_set(ec->frame, title);
}
static void
_e_shell_surface_cb_class_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, const char *clas)
{
E_Client *ec;
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
/* use the wl_client to get the pid * and set it in the netwm props */
wl_client_get_credentials(client, &ec->netwm.pid, NULL, NULL);
/* set class */
eina_stringshare_replace(&ec->icccm.class, clas);
ec->changes.icon = !!ec->icccm.class;
EC_CHANGED(ec);
}
static const struct wl_shell_surface_interface _e_shell_surface_interface =
{
_e_shell_surface_cb_pong,
_e_shell_surface_cb_move,
_e_shell_surface_cb_resize,
_e_shell_surface_cb_toplevel_set,
_e_shell_surface_cb_transient_set,
_e_shell_surface_cb_fullscreen_set,
_e_shell_surface_cb_popup_set,
_e_shell_surface_cb_maximized_set,
_e_shell_surface_cb_title_set,
_e_shell_surface_cb_class_set,
};
static void
_e_shell_surface_configure_send(struct wl_resource *resource, uint32_t edges, int32_t width, int32_t height)
{
wl_shell_surface_send_configure(resource, edges, width, height);
}
static void
_e_shell_surface_configure(struct wl_resource *resource, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h)
{
E_Client *ec;
/* DBG("WL_SHELL: Surface Configure: %d \t%d %d %d %d", */
/* wl_resource_get_id(resource), x, y, w, h); */
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
if (ec->parent)
{
if ((ec->netwm.type == E_WINDOW_TYPE_MENU) ||
(ec->netwm.type == E_WINDOW_TYPE_POPUP_MENU) ||
(ec->netwm.type == E_WINDOW_TYPE_DROPDOWN_MENU))
{
x = ec->parent->client.x + ec->comp_data->popup.x;
y = ec->parent->client.y + ec->comp_data->popup.y;
}
}
e_client_util_move_resize_without_frame(ec, x, y, w, h);
}
static void
_e_shell_surface_ping(struct wl_resource *resource)
{
E_Client *ec;
uint32_t serial;
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
serial = wl_display_next_serial(ec->comp->wl_comp_data->wl.disp);
wl_shell_surface_send_ping(ec->comp_data->shell.surface, serial);
}
static void
_e_shell_surface_map(struct wl_resource *resource)
{
E_Client *ec;
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
/* map this surface if needed */
if ((!ec->comp_data->mapped) && (e_pixmap_usable_get(ec->pixmap)))
{
ec->visible = EINA_TRUE;
evas_object_geometry_set(ec->frame, ec->x, ec->y, ec->w, ec->h);
evas_object_show(ec->frame);
ec->comp_data->mapped = EINA_TRUE;
}
}
static void
_e_shell_surface_unmap(struct wl_resource *resource)
{
E_Client *ec;
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
if (ec->comp_data->mapped)
{
ec->visible = EINA_FALSE;
evas_object_hide(ec->frame);
ec->comp_data->mapped = EINA_FALSE;
}
}
static void
_e_shell_cb_shell_surface_get(struct wl_client *client, struct wl_resource *resource EINA_UNUSED, uint32_t id, struct wl_resource *surface_resource)
{
E_Pixmap *ep;
E_Client *ec;
E_Comp_Client_Data *cdata;
/* get the pixmap from this surface so we can find the client */
if (!(ep = wl_resource_get_user_data(surface_resource)))
{
wl_resource_post_error(surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Pixmap Set On Surface");
return;
}
/* make sure it's a wayland pixmap */
if (e_pixmap_type_get(ep) != E_PIXMAP_TYPE_WL) return;
/* find the client for this pixmap */
if (!(ec = e_pixmap_client_get(ep)))
ec = e_pixmap_find_client(E_PIXMAP_TYPE_WL, e_pixmap_window_get(ep));
if (!ec)
{
/* no client found. not internal window. maybe external client app ? */
if (!(ec = e_client_new(e_util_comp_current_get(), ep, 1, 0)))
{
wl_resource_post_error(surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Pixmap");
return;
}
ec->netwm.ping = EINA_TRUE;
}
/* get the client data */
if (!(cdata = ec->comp_data))
{
wl_resource_post_error(surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Data For Client");
return;
}
/* check for existing shell surface */
if (cdata->shell.surface)
{
wl_resource_post_error(surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"Client already has shell surface");
return;
}
/* try to create a shell surface */
if (!(cdata->shell.surface =
wl_resource_create(client, &wl_shell_surface_interface, 1, id)))
{
wl_resource_post_no_memory(surface_resource);
return;
}
wl_resource_set_implementation(cdata->shell.surface,
&_e_shell_surface_interface,
ec, _e_shell_surface_cb_destroy);
cdata->surface = surface_resource;
cdata->shell.configure_send = _e_shell_surface_configure_send;
cdata->shell.configure = _e_shell_surface_configure;
cdata->shell.ping = _e_shell_surface_ping;
cdata->shell.map = _e_shell_surface_map;
cdata->shell.unmap = _e_shell_surface_unmap;
}
static void
_e_xdg_shell_surface_configure_send(struct wl_resource *resource, uint32_t edges, int32_t width, int32_t height)
{
E_Client *ec;
struct wl_array states;
uint32_t *s;
uint32_t serial;
DBG("XDG_SHELL: Surface Configure Send: %d \t%d %d\tEdges: %d",
wl_resource_get_id(resource), width, height, edges);
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
wl_array_init(&states);
if (ec->fullscreen)
{
s = wl_array_add(&states, sizeof(*s));
*s = XDG_SURFACE_STATE_FULLSCREEN;
}
else if (ec->maximized)
{
s = wl_array_add(&states, sizeof(*s));
*s = XDG_SURFACE_STATE_MAXIMIZED;
}
if (edges != 0)
{
s = wl_array_add(&states, sizeof(*s));
*s = XDG_SURFACE_STATE_RESIZING;
}
if (ec->focused)
{
s = wl_array_add(&states, sizeof(*s));
*s = XDG_SURFACE_STATE_ACTIVATED;
}
serial = wl_display_next_serial(ec->comp->wl_comp_data->wl.disp);
if (ec->netwm.type != E_WINDOW_TYPE_POPUP_MENU)
xdg_surface_send_configure(resource, width, height, &states, serial);
wl_array_release(&states);
}
static void
_e_xdg_shell_surface_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
_e_shell_surface_destroy(resource);
}
static void
_e_xdg_shell_surface_cb_parent_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *parent_resource)
{
E_Client *ec;
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
/* set this client as a transient for parent */
_e_shell_surface_parent_set(ec, parent_resource);
}
static void
_e_xdg_shell_surface_cb_title_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, const char *title)
{
E_Client *ec;
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
/* set title */
eina_stringshare_replace(&ec->icccm.title, title);
if (ec->frame) e_comp_object_frame_title_set(ec->frame, title);
}
static void
_e_xdg_shell_surface_cb_app_id_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, const char *id)
{
E_Client *ec;
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
/* use the wl_client to get the pid * and set it in the netwm props */
wl_client_get_credentials(client, &ec->netwm.pid, NULL, NULL);
/* set class */
eina_stringshare_replace(&ec->icccm.class, id);
/* eina_stringshare_replace(&ec->netwm.name, id); */
ec->changes.icon = !!ec->icccm.class;
EC_CHANGED(ec);
}
static void
_e_xdg_shell_surface_cb_window_menu_show(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *seat_resource EINA_UNUSED, uint32_t serial EINA_UNUSED, int32_t x, int32_t y)
{
E_Client *ec;
double timestamp;
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
timestamp = ecore_loop_time_get();
e_int_client_menu_show(ec, x, y, 0, timestamp);
}
static void
_e_xdg_shell_surface_cb_move(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial EINA_UNUSED)
{
E_Client *ec;
E_Comp_Data *cdata;
E_Binding_Event_Mouse_Button ev;
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
if ((ec->maximized) || (ec->fullscreen)) return;
/* get compositor data from seat */
if (!(cdata = wl_resource_get_user_data(seat_resource)))
{
wl_resource_post_error(seat_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Comp_Data for Seat");
return;
}
switch (cdata->ptr.button)
{
case BTN_LEFT:
ev.button = 1;
break;
case BTN_MIDDLE:
ev.button = 2;
break;
case BTN_RIGHT:
ev.button = 3;
break;
default:
ev.button = cdata->ptr.button;
break;
}
e_comp_object_frame_xy_unadjust(ec->frame,
wl_fixed_to_int(cdata->ptr.x) + ec->client.x,
wl_fixed_to_int(cdata->ptr.y) + ec->client.y,
&ev.canvas.x, &ev.canvas.y);
_e_shell_surface_mouse_down_helper(ec, &ev, EINA_TRUE);
}
static void
_e_xdg_shell_surface_cb_resize(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial EINA_UNUSED, uint32_t edges)
{
E_Client *ec;
E_Comp_Data *cdata;
E_Binding_Event_Mouse_Button ev;
/* DBG("XDG_SHELL: Surface Resize: %d\tEdges: %d", */
/* wl_resource_get_id(resource), edges); */
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
if ((edges == 0) || (edges > 15) ||
((edges & 3) == 3) || ((edges & 12) == 12)) return;
if ((ec->maximized) || (ec->fullscreen)) return;
/* get compositor data from seat */
if (!(cdata = wl_resource_get_user_data(seat_resource)))
{
wl_resource_post_error(seat_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Comp_Data for Seat");
return;
}
cdata->resize.resource = resource;
cdata->resize.edges = edges;
cdata->ptr.grab_x = cdata->ptr.x;
cdata->ptr.grab_y = cdata->ptr.y;
switch (cdata->ptr.button)
{
case BTN_LEFT:
ev.button = 1;
break;
case BTN_MIDDLE:
ev.button = 2;
break;
case BTN_RIGHT:
ev.button = 3;
break;
default:
ev.button = cdata->ptr.button;
break;
}
e_comp_object_frame_xy_unadjust(ec->frame,
wl_fixed_to_int(cdata->ptr.x) + ec->client.x,
wl_fixed_to_int(cdata->ptr.y) + ec->client.y,
&ev.canvas.x, &ev.canvas.y);
_e_shell_surface_mouse_down_helper(ec, &ev, EINA_FALSE);
}
static void
_e_xdg_shell_surface_cb_ack_configure(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, uint32_t serial EINA_UNUSED)
{
/* No-Op */
}
static void
_e_xdg_shell_surface_cb_window_geometry_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, int32_t x EINA_UNUSED, int32_t y EINA_UNUSED, int32_t w EINA_UNUSED, int32_t h EINA_UNUSED)
{
/* E_Client *ec; */
/* get the client for this resource */
/* if (!(ec = wl_resource_get_user_data(resource))) */
/* { */
/* wl_resource_post_error(resource, */
/* WL_DISPLAY_ERROR_INVALID_OBJECT, */
/* "No Client For Shell Surface"); */
/* return; */
/* } */
/* DBG("XDG_SHELL: Window Geom Set: %d \t%d %d, %d %d", */
/* wl_resource_get_id(resource), x, y, w, h); */
}
static void
_e_xdg_shell_surface_cb_maximized_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
E_Client *ec;
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
if (!ec->lock_user_maximize)
{
e_client_maximize(ec, ((e_config->maximize_policy & E_MAXIMIZE_TYPE) |
E_MAXIMIZE_BOTH));
_e_xdg_shell_surface_configure_send(resource, 0, ec->w, ec->h);
}
}
static void
_e_xdg_shell_surface_cb_maximized_unset(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
E_Client *ec;
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
e_client_unmaximize(ec, E_MAXIMIZE_BOTH);
_e_xdg_shell_surface_configure_send(resource, 0, ec->w, ec->h);
}
static void
_e_xdg_shell_surface_cb_fullscreen_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *output_resource EINA_UNUSED)
{
E_Client *ec;
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
if (!ec->lock_user_fullscreen)
e_client_fullscreen(ec, e_config->fullscreen_policy);
}
static void
_e_xdg_shell_surface_cb_fullscreen_unset(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
E_Client *ec;
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
if (!ec->lock_user_fullscreen)
e_client_unfullscreen(ec);
}
static void
_e_xdg_shell_surface_cb_minimized_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
E_Client *ec;
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
if (!ec->lock_client_iconify)
e_client_iconify(ec);
}
static const struct xdg_surface_interface _e_xdg_surface_interface =
{
_e_xdg_shell_surface_cb_destroy,
_e_xdg_shell_surface_cb_parent_set,
_e_xdg_shell_surface_cb_title_set,
_e_xdg_shell_surface_cb_app_id_set,
_e_xdg_shell_surface_cb_window_menu_show,
_e_xdg_shell_surface_cb_move,
_e_xdg_shell_surface_cb_resize,
_e_xdg_shell_surface_cb_ack_configure,
_e_xdg_shell_surface_cb_window_geometry_set,
_e_xdg_shell_surface_cb_maximized_set,
_e_xdg_shell_surface_cb_maximized_unset,
_e_xdg_shell_surface_cb_fullscreen_set,
_e_xdg_shell_surface_cb_fullscreen_unset,
_e_xdg_shell_surface_cb_minimized_set,
};
static void
_e_xdg_shell_cb_unstable_version(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t version)
{
if (version > 1)
wl_resource_post_error(resource, 1, "XDG Version Not Implemented Yet");
}
static void
_e_xdg_shell_surface_configure(struct wl_resource *resource, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h)
{
E_Client *ec;
/* DBG("XDG_SHELL: Surface Configure: %d \t%d %d %d %d", */
/* wl_resource_get_id(resource), x, y, w, h); */
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
if (ec->parent)
{
if ((ec->netwm.type == E_WINDOW_TYPE_MENU) ||
(ec->netwm.type == E_WINDOW_TYPE_POPUP_MENU) ||
(ec->netwm.type == E_WINDOW_TYPE_DROPDOWN_MENU))
{
x = ec->parent->client.x + ec->comp_data->popup.x;
y = ec->parent->client.y + ec->comp_data->popup.y;
}
}
e_client_util_move_resize_without_frame(ec, x, y, w, h);
/* TODO: ack configure ?? */
}
static void
_e_xdg_shell_surface_ping(struct wl_resource *resource)
{
E_Client *ec;
uint32_t serial;
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
serial = wl_display_next_serial(ec->comp->wl_comp_data->wl.disp);
if (ec->comp->wl_comp_data->shell_interface.xdg_shell)
xdg_shell_send_ping(ec->comp->wl_comp_data->shell_interface.xdg_shell, serial);
}
static void
_e_xdg_shell_surface_map(struct wl_resource *resource)
{
E_Client *ec;
/* DBG("XDG_SHELL: Map Surface: %d", wl_resource_get_id(resource)); */
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
if ((!ec->comp_data->mapped) && (e_pixmap_usable_get(ec->pixmap)))
{
/* map this surface if needed */
ec->visible = EINA_TRUE;
evas_object_geometry_set(ec->frame, ec->x, ec->y, ec->w, ec->h);
evas_object_show(ec->frame);
ec->comp_data->mapped = EINA_TRUE;
/* FIXME: sometimes popup surfaces Do Not raise above their
* respective parents... */
/* if (ec->netwm.type == E_WINDOW_TYPE_POPUP_MENU) */
/* e_client_raise_latest_set(ec); */
}
}
static void
_e_xdg_shell_surface_unmap(struct wl_resource *resource)
{
E_Client *ec;
/* DBG("XDG_SHELL: Unmap Surface: %d", wl_resource_get_id(resource)); */
/* get the client for this resource */
if (!(ec = wl_resource_get_user_data(resource)))
{
wl_resource_post_error(resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Shell Surface");
return;
}
if (ec->comp_data->mapped)
{
ec->visible = EINA_FALSE;
evas_object_hide(ec->frame);
ec->comp_data->mapped = EINA_FALSE;
}
}
static void
_e_xdg_shell_cb_surface_get(struct wl_client *client, struct wl_resource *resource EINA_UNUSED, uint32_t id, struct wl_resource *surface_resource)
{
E_Pixmap *ep;
E_Client *ec;
E_Comp_Client_Data *cdata;
/* DBG("XDG_SHELL: Surface Get %d", wl_resource_get_id(surface_resource)); */
/* get the pixmap from this surface so we can find the client */
if (!(ep = wl_resource_get_user_data(surface_resource)))
{
wl_resource_post_error(surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Pixmap Set On Surface");
return;
}
/* make sure it's a wayland pixmap */
if (e_pixmap_type_get(ep) != E_PIXMAP_TYPE_WL) return;
/* find the client for this pixmap */
if (!(ec = e_pixmap_client_get(ep)))
ec = e_pixmap_find_client(E_PIXMAP_TYPE_WL, e_pixmap_window_get(ep));
if (!ec)
{
/* no client found. not internal window. maybe external client app ? */
if (!(ec = e_client_new(e_util_comp_current_get(), ep, 1, 0)))
{
wl_resource_post_error(surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Pixmap");
return;
}
}
ec->netwm.ping = EINA_TRUE;
/* get the client data */
if (!(cdata = ec->comp_data))
{
wl_resource_post_error(surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Data For Client");
return;
}
/* check for existing shell surface */
if (cdata->shell.surface)
{
wl_resource_post_error(surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"Client already has XDG shell surface");
return;
}
/* try to create a shell surface */
if (!(cdata->shell.surface =
wl_resource_create(client, &xdg_surface_interface, 1, id)))
{
ERR("Could not create xdg shell surface");
wl_resource_post_no_memory(surface_resource);
return;
}
wl_resource_set_implementation(cdata->shell.surface,
&_e_xdg_surface_interface, ec,
_e_shell_surface_cb_destroy);
cdata->surface = surface_resource;
cdata->shell.configure_send = _e_xdg_shell_surface_configure_send;
cdata->shell.configure = _e_xdg_shell_surface_configure;
cdata->shell.ping = _e_xdg_shell_surface_ping;
cdata->shell.map = _e_xdg_shell_surface_map;
cdata->shell.unmap = _e_xdg_shell_surface_unmap;
/* set toplevel client properties */
ec->argb = EINA_TRUE;
ec->no_shape_cut = EINA_TRUE;
ec->borderless = !ec->internal_elm_win;
ec->lock_border = EINA_TRUE;
ec->border.changed = ec->changes.border = !ec->borderless;
ec->netwm.type = E_WINDOW_TYPE_NORMAL;
ec->comp_data->set_win_type = EINA_TRUE;
EC_CHANGED(ec);
}
static void
_e_xdg_shell_popup_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
_e_shell_surface_destroy(resource);
}
static const struct xdg_popup_interface _e_xdg_popup_interface =
{
_e_xdg_shell_popup_cb_destroy,
};
static void
_e_xdg_shell_cb_popup_get(struct wl_client *client, struct wl_resource *resource EINA_UNUSED, uint32_t id, struct wl_resource *surface_resource, struct wl_resource *parent_resource, struct wl_resource *seat_resource EINA_UNUSED, uint32_t serial EINA_UNUSED, int32_t x, int32_t y, uint32_t flags EINA_UNUSED)
{
E_Pixmap *ep;
E_Client *ec;
E_Comp_Client_Data *cdata;
/* DBG("XDG_SHELL: Popup Get"); */
/* DBG("\tSurface: %d", wl_resource_get_id(surface_resource)); */
/* DBG("\tParent Surface: %d", wl_resource_get_id(parent_resource)); */
/* DBG("\tLocation: %d %d", x, y); */
/* get the pixmap from this surface so we can find the client */
if (!(ep = wl_resource_get_user_data(surface_resource)))
{
wl_resource_post_error(surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Pixmap Set On Surface");
return;
}
/* make sure it's a wayland pixmap */
if (e_pixmap_type_get(ep) != E_PIXMAP_TYPE_WL) return;
/* find the client for this pixmap */
if (!(ec = e_pixmap_client_get(ep)))
ec = e_pixmap_find_client(E_PIXMAP_TYPE_WL, e_pixmap_window_get(ep));
if (!ec)
{
/* no client found. create one */
if (!(ec = e_client_new(e_util_comp_current_get(), ep, 1, 1)))
{
wl_resource_post_error(surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Client For Pixmap");
return;
}
/* e_pixmap_ref(ep); */
}
/* get the client data */
if (!(cdata = ec->comp_data))
{
wl_resource_post_error(surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"No Data For Client");
return;
}
/* check for existing shell surface */
if (cdata->shell.surface)
{
wl_resource_post_error(surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"Client already has shell popup surface");
return;
}
/* check for the parent surface */
if (!parent_resource)
{
wl_resource_post_error(surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"Popup requires a parent shell surface");
return;
}
/* try to create a shell surface */
if (!(cdata->shell.surface =
wl_resource_create(client, &xdg_popup_interface, 1, id)))
{
ERR("Could not create xdg shell surface");
wl_resource_post_no_memory(surface_resource);
return;
}
wl_resource_set_implementation(cdata->shell.surface,
&_e_xdg_popup_interface, ec, NULL);
cdata->popup.x = x;
cdata->popup.y = y;
cdata->surface = surface_resource;
cdata->shell.configure_send = _e_xdg_shell_surface_configure_send;
cdata->shell.configure = _e_xdg_shell_surface_configure;
cdata->shell.ping = _e_xdg_shell_surface_ping;
cdata->shell.map = _e_xdg_shell_surface_map;
cdata->shell.unmap = _e_xdg_shell_surface_unmap;
ec->argb = EINA_TRUE;
ec->no_shape_cut = EINA_TRUE;
ec->borderless = !ec->internal_elm_win;
ec->lock_border = EINA_TRUE;
ec->border.changed = ec->changes.border = !ec->borderless;
ec->changes.icon = !!ec->icccm.class;
ec->netwm.type = E_WINDOW_TYPE_POPUP_MENU;
ec->comp_data->set_win_type = EINA_TRUE;
ec->layer = E_LAYER_CLIENT_POPUP;
/* set this client as a transient for parent */
_e_shell_surface_parent_set(ec, parent_resource);
EC_CHANGED(ec);
}
static void
_e_xdg_shell_cb_pong(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, uint32_t serial EINA_UNUSED)
{
E_Client *ec;
if ((ec = wl_resource_get_user_data(resource)))
{
ec->ping_ok = EINA_TRUE;
ec->hung = EINA_FALSE;
}
}
static const struct wl_shell_interface _e_shell_interface =
{
_e_shell_cb_shell_surface_get
};
static const struct xdg_shell_interface _e_xdg_shell_interface =
{
_e_xdg_shell_cb_unstable_version,
_e_xdg_shell_cb_surface_get,
_e_xdg_shell_cb_popup_get,
_e_xdg_shell_cb_pong
};
static void
_e_xdg_shell_cb_unbind(struct wl_resource *resource)
{
E_Comp_Data *cdata;
if (!(cdata = wl_resource_get_user_data(resource))) return;
cdata->shell_interface.xdg_shell = NULL;
}
static int
_e_xdg_shell_cb_dispatch(const void *implementation EINA_UNUSED, void *target, uint32_t opcode, const struct wl_message *message EINA_UNUSED, union wl_argument *args)
{
E_Comp_Data *cdata;
struct wl_resource *res;
if (!(res = target)) return 0;
if (!(cdata = wl_resource_get_user_data(res))) return 0;
if (opcode != 0)
{
wl_resource_post_error(res, WL_DISPLAY_ERROR_INVALID_OBJECT,
"Must call use_unstable_version first");
return 0;
}
if (args[0].i != XDG_SERVER_VERSION)
{
wl_resource_post_error(res, WL_DISPLAY_ERROR_INVALID_OBJECT,
"Incompatible versions. "
"Server: %d, Client: %d",
XDG_SERVER_VERSION, args[0].i);
return 0;
}
wl_resource_set_implementation(res, &_e_xdg_shell_interface, cdata,
_e_xdg_shell_cb_unbind);
return 1;
}
static void
_e_shell_cb_unbind(struct wl_resource *resource)
{
E_Comp_Data *cdata;
if (!(cdata = wl_resource_get_user_data(resource))) return;
cdata->shell_interface.shell = NULL;
}
static void
_e_shell_cb_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
E_Comp_Data *cdata;
struct wl_resource *res;
if (!(cdata = data))
{
wl_client_post_no_memory(client);
return;
}
res = wl_resource_create(client, &wl_shell_interface, MIN(version, 1), id);
if (!res)
{
wl_client_post_no_memory(client);
return;
}
cdata->shell_interface.shell = res;
wl_resource_set_implementation(res, &_e_shell_interface, cdata,
_e_shell_cb_unbind);
}
static void
_e_xdg_shell_cb_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
E_Comp_Data *cdata;
struct wl_resource *res;
if (!(cdata = data))
{
wl_client_post_no_memory(client);
return;
}
res = wl_resource_create(client, &xdg_shell_interface, MIN(version, 1), id);
if (!res)
{
wl_client_post_no_memory(client);
return;
}
cdata->shell_interface.xdg_shell = res;
wl_resource_set_dispatcher(res, _e_xdg_shell_cb_dispatch, NULL, cdata, NULL);
}
EAPI E_Module_Api e_modapi = { E_MODULE_API_VERSION, "Wl_Desktop_Shell" };
EAPI void *
e_modapi_init(E_Module *m)
{
E_Comp *comp;
E_Comp_Data *cdata;
/* try to get the current compositor */
if (!(comp = e_comp)) return NULL;
/* make sure it's a wayland compositor */
/* if (comp->comp_type != E_PIXMAP_TYPE_WL) return NULL; */
/* try to get the compositor data */
if (!(cdata = comp->wl_comp_data)) return NULL;
/* try to create global shell interface */
if (!wl_global_create(cdata->wl.disp, &wl_shell_interface, 1,
cdata, _e_shell_cb_bind))
{
ERR("Could not create shell global: %m");
return NULL;
}
/* try to create global xdg_shell interface */
if (!wl_global_create(cdata->wl.disp, &xdg_shell_interface, 1,
cdata, _e_xdg_shell_cb_bind))
{
ERR("Could not create xdg_shell global: %m");
return NULL;
}
return m;
}
EAPI int
e_modapi_shutdown(E_Module *m EINA_UNUSED)
{
return 1;
}