From b810eb57503e1cf38aeca45b97ab78e896cbc129 Mon Sep 17 00:00:00 2001 From: Kristian Høgsberg Date: Tue, 12 Feb 2013 20:07:05 -0500 Subject: 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. --- src/compositor.h | 4 + src/shell.c | 33 +++++-- src/xwayland/window-manager.c | 198 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 204 insertions(+), 31 deletions(-) diff --git a/src/compositor.h b/src/compositor.h index 544cf33e..004f9312 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -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); diff --git a/src/shell.c b/src/shell.c index 368fa5b7..72d2a73f 100644 --- a/src/shell.c +++ b/src/shell.c @@ -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; diff --git a/src/xwayland/window-manager.c b/src/xwayland/window-manager.c index b6e8e79e..392d05c8 100644 --- a/src/xwayland/window-manager.c +++ b/src/xwayland/window-manager.c @@ -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; } -- cgit v1.2.3