xwm: Support _NET_WM_STATE_FULLSCREEN

We can now handle fullscreen X windows.  X clients request to go fullscreen
buy sending a _NET_WM_STATE client message to the root window.  When that
happens we call into the shell interface and asks the shell to make the
surface fullscreen.  The shell will then resize the window, which causes
the X wm to configure the X window appropriately.

Make sure we ignore configure requests from fullscreened clients and send out
the synthetic configure notify as required in that case.

Finally, inspect _NET_WM_STATE before mapping so we can handle initial
fullscreen correctly.
This commit is contained in:
Kristian Høgsberg 2013-02-12 20:07:05 -05:00
parent 088c62e267
commit b810eb5750
3 changed files with 204 additions and 31 deletions

View file

@ -85,6 +85,10 @@ struct weston_shell_interface {
void (*set_transient)(struct shell_surface *shsurf,
struct weston_surface *parent,
int x, int y, uint32_t flags);
void (*set_fullscreen)(struct shell_surface *shsurf,
uint32_t method,
uint32_t framerate,
struct weston_output *output);
int (*move)(struct shell_surface *shsurf, struct weston_seat *ws);
int (*resize)(struct shell_surface *shsurf,
struct weston_seat *ws, uint32_t edges);

View file

@ -1754,17 +1754,15 @@ shell_map_fullscreen(struct shell_surface *shsurf)
}
static void
shell_surface_set_fullscreen(struct wl_client *client,
struct wl_resource *resource,
uint32_t method,
uint32_t framerate,
struct wl_resource *output_resource)
set_fullscreen(struct shell_surface *shsurf,
uint32_t method,
uint32_t framerate,
struct weston_output *output)
{
struct shell_surface *shsurf = resource->data;
struct weston_surface *es = shsurf->surface;
if (output_resource)
shsurf->output = output_resource->data;
if (output)
shsurf->output = output;
else if (es->output)
shsurf->output = es->output;
else
@ -1780,6 +1778,24 @@ shell_surface_set_fullscreen(struct wl_client *client,
shsurf->output->height);
}
static void
shell_surface_set_fullscreen(struct wl_client *client,
struct wl_resource *resource,
uint32_t method,
uint32_t framerate,
struct wl_resource *output_resource)
{
struct shell_surface *shsurf = resource->data;
struct weston_output *output;
if (output_resource)
output = output_resource->data;
else
output = NULL;
set_fullscreen(shsurf, method, framerate, output);
}
static void
popup_grab_focus(struct wl_pointer_grab *grab,
struct wl_surface *surface,
@ -3832,6 +3848,7 @@ module_init(struct weston_compositor *ec)
ec->shell_interface.create_shell_surface = create_shell_surface;
ec->shell_interface.set_toplevel = set_toplevel;
ec->shell_interface.set_transient = set_transient;
ec->shell_interface.set_fullscreen = set_fullscreen;
ec->shell_interface.move = surface_move;
ec->shell_interface.resize = surface_resize;

View file

@ -89,8 +89,6 @@ struct motif_wm_hints {
#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */
#define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */
struct weston_wm_window {
struct weston_wm *wm;
xcb_window_t id;
@ -111,8 +109,10 @@ struct weston_wm_window {
xcb_atom_t type;
int width, height;
int x, y;
int saved_width, saved_height;
int decorate;
int override_redirect;
int fullscreen;
};
static struct weston_wm_window *
@ -287,6 +287,7 @@ read_and_dump_property(struct weston_wm *wm,
/* We reuse some predefined, but otherwise useles atoms */
#define TYPE_WM_PROTOCOLS XCB_ATOM_CUT_BUFFER0
#define TYPE_MOTIF_WM_HINTS XCB_ATOM_CUT_BUFFER1
#define TYPE_NET_WM_STATE XCB_ATOM_CUT_BUFFER2
static void
weston_wm_window_read_properties(struct weston_wm_window *window)
@ -303,6 +304,7 @@ weston_wm_window_read_properties(struct weston_wm_window *window)
{ XCB_ATOM_WM_NAME, XCB_ATOM_STRING, F(name) },
{ XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, F(transient_for) },
{ wm->atom.wm_protocols, TYPE_WM_PROTOCOLS, F(protocols) },
{ wm->atom.net_wm_state, TYPE_NET_WM_STATE },
{ wm->atom.net_wm_window_type, XCB_ATOM_ATOM, F(type) },
{ wm->atom.net_wm_name, XCB_ATOM_STRING, F(name) },
{ wm->atom.net_wm_pid, XCB_ATOM_CARDINAL, F(pid) },
@ -368,6 +370,13 @@ weston_wm_window_read_properties(struct weston_wm_window *window)
break;
case TYPE_WM_PROTOCOLS:
break;
case TYPE_NET_WM_STATE:
window->fullscreen = 0;
atom = xcb_get_property_value(reply);
for (i = 0; i < reply->value_len; i++)
if (atom[i] == wm->atom.net_wm_state_fullscreen)
window->fullscreen = 1;
break;
case TYPE_MOTIF_WM_HINTS:
hints = xcb_get_property_value(reply);
if (hints->flags & MWM_HINTS_DECORATIONS)
@ -386,7 +395,10 @@ weston_wm_window_get_frame_size(struct weston_wm_window *window,
{
struct theme *t = window->wm->theme;
if (window->decorate) {
if (window->fullscreen) {
*width = window->width;
*height = window->height;
} else if (window->decorate) {
*width = window->width + (t->margin + t->width) * 2;
*height = window->height +
t->margin * 2 + t->width + t->titlebar_height;
@ -402,7 +414,10 @@ weston_wm_window_get_child_position(struct weston_wm_window *window,
{
struct theme *t = window->wm->theme;
if (window->decorate) {
if (window->fullscreen) {
*x = 0;
*y = 0;
} else if (window->decorate) {
*x = t->margin + t->width;
*y = t->margin + t->titlebar_height;
} else {
@ -411,6 +426,32 @@ weston_wm_window_get_child_position(struct weston_wm_window *window,
}
}
static void
weston_wm_window_send_configure_notify(struct weston_wm_window *window)
{
xcb_configure_notify_event_t configure_notify;
struct weston_wm *wm = window->wm;
int x, y;
weston_wm_window_get_child_position(window, &x, &y);
configure_notify.response_type = XCB_CONFIGURE_NOTIFY;
configure_notify.pad0 = 0;
configure_notify.event = window->id;
configure_notify.window = window->id;
configure_notify.above_sibling = XCB_WINDOW_NONE;
configure_notify.x = x;
configure_notify.y = y;
configure_notify.width = window->width;
configure_notify.height = window->height;
configure_notify.border_width = 0;
configure_notify.override_redirect = 0;
configure_notify.pad1 = 0;
xcb_send_event(wm->conn, 0, window->id,
XCB_EVENT_MASK_STRUCTURE_NOTIFY,
(char *) &configure_notify);
}
static void
weston_wm_handle_configure_request(struct weston_wm *wm, xcb_generic_event_t *event)
{
@ -427,6 +468,11 @@ weston_wm_handle_configure_request(struct weston_wm *wm, xcb_generic_event_t *ev
window = hash_table_lookup(wm->window_hash, configure_request->window);
if (window->fullscreen) {
weston_wm_window_send_configure_notify(window);
return;
}
if (configure_request->value_mask & XCB_CONFIG_WINDOW_WIDTH)
window->width = configure_request->width;
if (configure_request->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
@ -559,7 +605,7 @@ our_resource(struct weston_wm *wm, uint32_t id)
#define ICCCM_ICONIC_STATE 3
static void
weston_wm_window_set_state(struct weston_wm_window *window, int32_t state)
weston_wm_window_set_wm_state(struct weston_wm_window *window, int32_t state)
{
struct weston_wm *wm = window->wm;
uint32_t property[2];
@ -576,6 +622,26 @@ weston_wm_window_set_state(struct weston_wm_window *window, int32_t state)
2, property);
}
static void
weston_wm_window_set_net_wm_state(struct weston_wm_window *window)
{
struct weston_wm *wm = window->wm;
uint32_t property[1];
int i;
i = 0;
if (window->fullscreen)
property[i++] = wm->atom.net_wm_state_fullscreen;
xcb_change_property(wm->conn,
XCB_PROP_MODE_REPLACE,
window->id,
wm->atom.net_wm_state,
XCB_ATOM_ATOM,
32, /* format */
i, property);
}
static void
weston_wm_handle_map_request(struct weston_wm *wm, xcb_generic_event_t *event)
{
@ -632,9 +698,11 @@ weston_wm_handle_map_request(struct weston_wm *wm, xcb_generic_event_t *event)
weston_log("XCB_MAP_REQUEST (window %d, %p, frame %d)\n",
window->id, window, window->frame_id);
weston_wm_window_set_wm_state(window, ICCCM_NORMAL_STATE);
weston_wm_window_set_net_wm_state(window);
xcb_map_window(wm->conn, map_request->window);
xcb_map_window(wm->conn, window->frame_id);
weston_wm_window_set_state(window, ICCCM_NORMAL_STATE);
window->cairo_surface =
cairo_xcb_surface_create_with_xrender_format(wm->conn,
@ -689,7 +757,7 @@ weston_wm_handle_unmap_notify(struct weston_wm *wm, xcb_generic_event_t *event)
if (window->frame_id) {
xcb_reparent_window(wm->conn, window->id, wm->wm_window, 0, 0);
xcb_destroy_window(wm->conn, window->frame_id);
weston_wm_window_set_state(window, ICCCM_WITHDRAWN_STATE);
weston_wm_window_set_wm_state(window, ICCCM_WITHDRAWN_STATE);
hash_table_remove(wm->window_hash, window->frame_id);
window->frame_id = XCB_WINDOW_NONE;
}
@ -722,7 +790,9 @@ weston_wm_window_draw_decoration(void *data)
cairo_xcb_surface_set_size(window->cairo_surface, width, height);
cr = cairo_create(window->cairo_surface);
if (window->decorate) {
if (window->fullscreen) {
/* nothing */
} else if (window->decorate) {
if (wm->focus_window == window)
flags |= THEME_FRAME_ACTIVE;
@ -965,6 +1035,68 @@ weston_wm_window_handle_moveresize(struct weston_wm_window *window,
}
}
#define _NET_WM_STATE_REMOVE 0
#define _NET_WM_STATE_ADD 1
#define _NET_WM_STATE_TOGGLE 2
static int
update_state(int action, int *state)
{
int new_state, changed;
switch (action) {
case _NET_WM_STATE_REMOVE:
new_state = 0;
break;
case _NET_WM_STATE_ADD:
new_state = 1;
break;
case _NET_WM_STATE_TOGGLE:
new_state = !*state;
break;
default:
return 0;
}
changed = (*state != new_state);
*state = new_state;
return changed;
}
static void
weston_wm_window_configure(void *data);
static void
weston_wm_window_handle_state(struct weston_wm_window *window,
xcb_client_message_event_t *client_message)
{
struct weston_wm *wm = window->wm;
struct weston_shell_interface *shell_interface =
&wm->server->compositor->shell_interface;
uint32_t action, property;
action = client_message->data.data32[0];
property = client_message->data.data32[1];
if (property == wm->atom.net_wm_state_fullscreen &&
update_state(action, &window->fullscreen)) {
weston_wm_window_set_net_wm_state(window);
if (window->fullscreen) {
window->saved_width = window->width;
window->saved_height = window->height;
shell_interface->set_fullscreen(window->shsurf,
WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
0, NULL);
} else {
shell_interface->set_toplevel(window->shsurf);
window->width = window->saved_width;
window->height = window->saved_height;
weston_wm_window_configure(window);
}
}
}
static void
weston_wm_handle_client_message(struct weston_wm *wm,
xcb_generic_event_t *event)
@ -975,16 +1107,19 @@ weston_wm_handle_client_message(struct weston_wm *wm,
window = hash_table_lookup(wm->window_hash, client_message->window);
weston_log("XCB_CLIENT_MESSAGE (%s %d %d %d %d %d)\n",
get_atom_name(wm->conn, client_message->type),
client_message->data.data32[0],
client_message->data.data32[1],
client_message->data.data32[2],
client_message->data.data32[3],
client_message->data.data32[4]);
weston_log("XCB_CLIENT_MESSAGE (%s %d %d %d %d %d win %d)\n",
get_atom_name(wm->conn, client_message->type),
client_message->data.data32[0],
client_message->data.data32[1],
client_message->data.data32[2],
client_message->data.data32[3],
client_message->data.data32[4],
client_message->window);
if (client_message->type == wm->atom.net_wm_moveresize)
weston_wm_window_handle_moveresize(window, client_message);
else if (client_message->type == wm->atom.net_wm_state)
weston_wm_window_handle_state(window, client_message);
}
enum cursor_type {
@ -1428,7 +1563,7 @@ weston_wm_create(struct weston_xserver *wxs)
xcb_screen_iterator_t s;
uint32_t values[1];
int sv[2];
xcb_atom_t supported[1];
xcb_atom_t supported[3];
wm = malloc(sizeof *wm);
if (wm == NULL)
@ -1486,6 +1621,8 @@ weston_wm_create(struct weston_xserver *wxs)
weston_wm_create_wm_window(wm);
supported[0] = wm->atom.net_wm_moveresize;
supported[1] = wm->atom.net_wm_state;
supported[2] = wm->atom.net_wm_state_fullscreen;
xcb_change_property(wm->conn,
XCB_PROP_MODE_REPLACE,
wm->screen->root,
@ -1557,13 +1694,18 @@ weston_wm_window_configure(void *data)
{
struct weston_wm_window *window = data;
struct weston_wm *wm = window->wm;
uint32_t values[2];
int width, height;
uint32_t values[4];
int x, y, width, height;
values[0] = window->width;
values[1] = window->height;
weston_wm_window_get_child_position(window, &x, &y);
values[0] = x;
values[1] = y;
values[2] = window->width;
values[3] = window->height;
xcb_configure_window(wm->conn,
window->id,
XCB_CONFIG_WINDOW_X |
XCB_CONFIG_WINDOW_Y |
XCB_CONFIG_WINDOW_WIDTH |
XCB_CONFIG_WINDOW_HEIGHT,
values);
@ -1590,7 +1732,10 @@ send_configure(struct weston_surface *surface,
struct weston_wm *wm = window->wm;
struct theme *t = window->wm->theme;
if (window->decorate) {
if (window->fullscreen) {
window->width = width;
window->height = height;
} else if (window->decorate) {
window->width = width - 2 * (t->margin + t->width);
window->height = height - 2 * t->margin -
t->titlebar_height - t->width;
@ -1629,8 +1774,15 @@ xserver_map_shell_surface(struct weston_wm *wm,
window->surface,
&shell_client);
/* ICCCM 4.1.1 */
if (!window->override_redirect) {
if (window->fullscreen) {
window->saved_width = window->width;
window->saved_height = window->height;
shell_interface->set_fullscreen(window->shsurf,
WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
0, NULL);
return;
} else if (!window->override_redirect) {
/* ICCCM 4.1.1 */
shell_interface->set_toplevel(window->shsurf);
return;
}