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.
dev
Kristian Høgsberg 12 years ago
parent 088c62e267
commit b810eb5750
  1. 4
      src/compositor.h
  2. 29
      src/shell.c
  3. 186
      src/xwayland/window-manager.c

@ -85,6 +85,10 @@ struct weston_shell_interface {
void (*set_transient)(struct shell_surface *shsurf, void (*set_transient)(struct shell_surface *shsurf,
struct weston_surface *parent, struct weston_surface *parent,
int x, int y, uint32_t flags); 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 (*move)(struct shell_surface *shsurf, struct weston_seat *ws);
int (*resize)(struct shell_surface *shsurf, int (*resize)(struct shell_surface *shsurf,
struct weston_seat *ws, uint32_t edges); struct weston_seat *ws, uint32_t edges);

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

@ -89,8 +89,6 @@ struct motif_wm_hints {
#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */ #define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */
#define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */ #define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */
struct weston_wm_window { struct weston_wm_window {
struct weston_wm *wm; struct weston_wm *wm;
xcb_window_t id; xcb_window_t id;
@ -111,8 +109,10 @@ struct weston_wm_window {
xcb_atom_t type; xcb_atom_t type;
int width, height; int width, height;
int x, y; int x, y;
int saved_width, saved_height;
int decorate; int decorate;
int override_redirect; int override_redirect;
int fullscreen;
}; };
static struct weston_wm_window * 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 */ /* We reuse some predefined, but otherwise useles atoms */
#define TYPE_WM_PROTOCOLS XCB_ATOM_CUT_BUFFER0 #define TYPE_WM_PROTOCOLS XCB_ATOM_CUT_BUFFER0
#define TYPE_MOTIF_WM_HINTS XCB_ATOM_CUT_BUFFER1 #define TYPE_MOTIF_WM_HINTS XCB_ATOM_CUT_BUFFER1
#define TYPE_NET_WM_STATE XCB_ATOM_CUT_BUFFER2
static void static void
weston_wm_window_read_properties(struct weston_wm_window *window) 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_NAME, XCB_ATOM_STRING, F(name) },
{ XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, F(transient_for) }, { XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, F(transient_for) },
{ wm->atom.wm_protocols, TYPE_WM_PROTOCOLS, F(protocols) }, { 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_window_type, XCB_ATOM_ATOM, F(type) },
{ wm->atom.net_wm_name, XCB_ATOM_STRING, F(name) }, { wm->atom.net_wm_name, XCB_ATOM_STRING, F(name) },
{ wm->atom.net_wm_pid, XCB_ATOM_CARDINAL, F(pid) }, { 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; break;
case TYPE_WM_PROTOCOLS: case TYPE_WM_PROTOCOLS:
break; 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: case TYPE_MOTIF_WM_HINTS:
hints = xcb_get_property_value(reply); hints = xcb_get_property_value(reply);
if (hints->flags & MWM_HINTS_DECORATIONS) 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; 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; *width = window->width + (t->margin + t->width) * 2;
*height = window->height + *height = window->height +
t->margin * 2 + t->width + t->titlebar_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; 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; *x = t->margin + t->width;
*y = t->margin + t->titlebar_height; *y = t->margin + t->titlebar_height;
} else { } 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 static void
weston_wm_handle_configure_request(struct weston_wm *wm, xcb_generic_event_t *event) 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); 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) if (configure_request->value_mask & XCB_CONFIG_WINDOW_WIDTH)
window->width = configure_request->width; window->width = configure_request->width;
if (configure_request->value_mask & XCB_CONFIG_WINDOW_HEIGHT) 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 #define ICCCM_ICONIC_STATE 3
static void 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; struct weston_wm *wm = window->wm;
uint32_t property[2]; uint32_t property[2];
@ -576,6 +622,26 @@ weston_wm_window_set_state(struct weston_wm_window *window, int32_t state)
2, property); 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 static void
weston_wm_handle_map_request(struct weston_wm *wm, xcb_generic_event_t *event) 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", weston_log("XCB_MAP_REQUEST (window %d, %p, frame %d)\n",
window->id, window, window->frame_id); 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, map_request->window);
xcb_map_window(wm->conn, window->frame_id); xcb_map_window(wm->conn, window->frame_id);
weston_wm_window_set_state(window, ICCCM_NORMAL_STATE);
window->cairo_surface = window->cairo_surface =
cairo_xcb_surface_create_with_xrender_format(wm->conn, 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) { if (window->frame_id) {
xcb_reparent_window(wm->conn, window->id, wm->wm_window, 0, 0); xcb_reparent_window(wm->conn, window->id, wm->wm_window, 0, 0);
xcb_destroy_window(wm->conn, window->frame_id); 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); hash_table_remove(wm->window_hash, window->frame_id);
window->frame_id = XCB_WINDOW_NONE; 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); cairo_xcb_surface_set_size(window->cairo_surface, width, height);
cr = cairo_create(window->cairo_surface); cr = cairo_create(window->cairo_surface);
if (window->decorate) { if (window->fullscreen) {
/* nothing */
} else if (window->decorate) {
if (wm->focus_window == window) if (wm->focus_window == window)
flags |= THEME_FRAME_ACTIVE; 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 static void
weston_wm_handle_client_message(struct weston_wm *wm, weston_wm_handle_client_message(struct weston_wm *wm,
xcb_generic_event_t *event) 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); window = hash_table_lookup(wm->window_hash, client_message->window);
weston_log("XCB_CLIENT_MESSAGE (%s %d %d %d %d %d)\n", weston_log("XCB_CLIENT_MESSAGE (%s %d %d %d %d %d win %d)\n",
get_atom_name(wm->conn, client_message->type), get_atom_name(wm->conn, client_message->type),
client_message->data.data32[0], client_message->data.data32[0],
client_message->data.data32[1], client_message->data.data32[1],
client_message->data.data32[2], client_message->data.data32[2],
client_message->data.data32[3], client_message->data.data32[3],
client_message->data.data32[4]); client_message->data.data32[4],
client_message->window);
if (client_message->type == wm->atom.net_wm_moveresize) if (client_message->type == wm->atom.net_wm_moveresize)
weston_wm_window_handle_moveresize(window, client_message); 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 { enum cursor_type {
@ -1428,7 +1563,7 @@ weston_wm_create(struct weston_xserver *wxs)
xcb_screen_iterator_t s; xcb_screen_iterator_t s;
uint32_t values[1]; uint32_t values[1];
int sv[2]; int sv[2];
xcb_atom_t supported[1]; xcb_atom_t supported[3];
wm = malloc(sizeof *wm); wm = malloc(sizeof *wm);
if (wm == NULL) if (wm == NULL)
@ -1486,6 +1621,8 @@ weston_wm_create(struct weston_xserver *wxs)
weston_wm_create_wm_window(wm); weston_wm_create_wm_window(wm);
supported[0] = wm->atom.net_wm_moveresize; 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_change_property(wm->conn,
XCB_PROP_MODE_REPLACE, XCB_PROP_MODE_REPLACE,
wm->screen->root, wm->screen->root,
@ -1557,13 +1694,18 @@ weston_wm_window_configure(void *data)
{ {
struct weston_wm_window *window = data; struct weston_wm_window *window = data;
struct weston_wm *wm = window->wm; struct weston_wm *wm = window->wm;
uint32_t values[2]; uint32_t values[4];
int width, height; int x, y, width, height;
values[0] = window->width; weston_wm_window_get_child_position(window, &x, &y);
values[1] = window->height; values[0] = x;
values[1] = y;
values[2] = window->width;
values[3] = window->height;
xcb_configure_window(wm->conn, xcb_configure_window(wm->conn,
window->id, window->id,
XCB_CONFIG_WINDOW_X |
XCB_CONFIG_WINDOW_Y |
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_WIDTH |
XCB_CONFIG_WINDOW_HEIGHT, XCB_CONFIG_WINDOW_HEIGHT,
values); values);
@ -1590,7 +1732,10 @@ send_configure(struct weston_surface *surface,
struct weston_wm *wm = window->wm; struct weston_wm *wm = window->wm;
struct theme *t = window->wm->theme; 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->width = width - 2 * (t->margin + t->width);
window->height = height - 2 * t->margin - window->height = height - 2 * t->margin -
t->titlebar_height - t->width; t->titlebar_height - t->width;
@ -1629,8 +1774,15 @@ xserver_map_shell_surface(struct weston_wm *wm,
window->surface, window->surface,
&shell_client); &shell_client);
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 */ /* ICCCM 4.1.1 */
if (!window->override_redirect) {
shell_interface->set_toplevel(window->shsurf); shell_interface->set_toplevel(window->shsurf);
return; return;
} }

Loading…
Cancel
Save