xwm: Support _NET_WM_STATE_FULLSCREEN
authorKristian Høgsberg <krh@bitplanet.net>
Wed, 13 Feb 2013 01:07:05 +0000 (20:07 -0500)
committerKristian Høgsberg <krh@bitplanet.net>
Wed, 13 Feb 2013 02:55:51 +0000 (21:55 -0500)
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
src/shell.c
src/xwayland/window-manager.c

index 544cf33..004f931 100644 (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);
index 368fa5b..72d2a73 100644 (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
@@ -1781,6 +1779,24 @@ shell_surface_set_fullscreen(struct wl_client *client,
 }
 
 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,
                 wl_fixed_t x,
@@ -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;
 
index b6e8e79..392d05c 100644 (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 {
@@ -412,6 +427,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)
 {
        xcb_configure_request_event_t *configure_request = 
@@ -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];
@@ -577,6 +623,26 @@ weston_wm_window_set_state(struct weston_wm_window *window, int32_t state)
 }
 
 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)
 {
        xcb_map_request_event_t *map_request =
@@ -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;
        }