]> git.sur5r.net Git - i3/i3/commitdiff
Merge pull request #3184 from orestisf1993/issue-1341
authorIngo Bürk <admin@airblader.de>
Fri, 23 Mar 2018 18:17:12 +0000 (19:17 +0100)
committerGitHub <noreply@github.com>
Fri, 23 Mar 2018 18:17:12 +0000 (19:17 +0100)
floating_enable & floating_maybe_reassign_ws changes

22 files changed:
docs/userguide
i3-msg/main.c
i3-sensible-terminal
include/atoms_NET_SUPPORTED.xmacro
include/commands.h
include/data.h
include/ewmh.h
include/floating.h
man/i3-sensible-terminal.man
parser-specs/commands.spec
src/assignments.c
src/commands.c
src/config.c
src/ewmh.c
src/floating.c
src/move.c
src/x.c
testcases/lib/i3test.pm.in
testcases/t/158-wm_take_focus.t
testcases/t/253-multiple-net-wm-state-atoms.t
testcases/t/295-net-wm-state-focused.t [new file with mode: 0644]
testcases/t/516-move.t

index ccedc577924ff53997aa18560c4d488164b31ba1..5fc36585d02b3fb7a67bfe7adc43e6aca6bf2a62 100644 (file)
@@ -2030,10 +2030,13 @@ Use the +move+ command to move a container.
 # defaults to 10 pixels.
 move <left|right|down|up> [<px> px]
 
-# Moves the container either to a specific location
-# or to the center of the screen. If 'absolute' is
-# used, it is moved to the center of all outputs.
-move [absolute] position <pos_x> [px] <pos_y> [px]
+# Moves the container to the specified pos_x and pos_y
+# coordinates on the screen.
+move position <pos_x> [px] <pos_y> [px]
+
+# Moves the container to the center of the screen.
+# If 'absolute' is used, it is moved to the center of
+# all outputs.
 move [absolute] position center
 
 # Moves the container to the current position of the
@@ -2258,8 +2261,7 @@ See <<move_to_outputs>> for how to move a container/workspace to a different
 RandR output.
 
 [[move_to_outputs]]
-[[_moving_containers_workspaces_to_randr_outputs]]
-=== Moving containers/workspaces to RandR outputs
+=== [[_moving_containers_workspaces_to_randr_outputs]]Moving containers/workspaces to RandR outputs
 
 To move a container to another RandR output (addressed by names like +LVDS1+ or
 +VGA1+) or to a RandR output identified by a specific direction (like +left+,
index 91a714e56637b5df408885509f680ad14e6c4b48..96edb2c3a6e0a668dfe0fb36bd19d95124e70a38 100644 (file)
@@ -95,8 +95,10 @@ static int reply_start_map_cb(void *params) {
 
 static int reply_end_map_cb(void *params) {
     if (!last_reply.success) {
-        fprintf(stderr, "ERROR: Your command: %s\n", last_reply.input);
-        fprintf(stderr, "ERROR:               %s\n", last_reply.errorposition);
+        if (last_reply.input) {
+            fprintf(stderr, "ERROR: Your command: %s\n", last_reply.input);
+            fprintf(stderr, "ERROR:               %s\n", last_reply.errorposition);
+        }
         fprintf(stderr, "ERROR: %s\n", last_reply.error);
     }
     return 1;
index f1eb256ecdf4c0a6cf9543cd9c47222d3a31412f..6638098c560dbdf96c1b4b3fe2961b3cd67ae3e3 100755 (executable)
@@ -8,7 +8,7 @@
 # We welcome patches that add distribution-specific mechanisms to find the
 # preferred terminal emulator. On Debian, there is the x-terminal-emulator
 # symlink for example.
-for terminal in "$TERMINAL" x-terminal-emulator urxvt rxvt termit terminator Eterm aterm uxterm xterm gnome-terminal roxterm xfce4-terminal termite lxterminal mate-terminal terminology st qterminal lilyterm tilix terminix konsole kitty guake tilda; do
+for terminal in "$TERMINAL" x-terminal-emulator urxvt rxvt termit terminator Eterm aterm uxterm xterm gnome-terminal roxterm xfce4-terminal termite lxterminal mate-terminal terminology st qterminal lilyterm tilix terminix konsole kitty guake tilda alacritty; do
     if command -v "$terminal" > /dev/null 2>&1; then
         exec "$terminal" "$@"
     fi
index a7b9676d9817e21dcd142c6f62fc9606cada2773..a81948a983fde44853e7da19889743a4adb78f96 100644 (file)
@@ -8,6 +8,7 @@ xmacro(_NET_WM_STATE_FULLSCREEN)
 xmacro(_NET_WM_STATE_DEMANDS_ATTENTION)
 xmacro(_NET_WM_STATE_MODAL)
 xmacro(_NET_WM_STATE_HIDDEN)
+xmacro(_NET_WM_STATE_FOCUSED)
 xmacro(_NET_WM_STATE)
 xmacro(_NET_WM_WINDOW_TYPE)
 xmacro(_NET_WM_WINDOW_TYPE_NORMAL)
index 1057f021db5a3d2aa45034b3557f51071cf03c07..aaa0875fbf399380b503545bcadd816d148ac198 100644 (file)
@@ -264,7 +264,7 @@ void cmd_focus_output(I3_CMD, const char *name);
  * Implementation of 'move [window|container] [to] [absolute] position <px> [px] <px> [px]
  *
  */
-void cmd_move_window_to_position(I3_CMD, const char *method, long x, long y);
+void cmd_move_window_to_position(I3_CMD, long x, long y);
 
 /**
  * Implementation of 'move [window|container] [to] [absolute] position center
index 69a46e464331172172873a70940af6d348cb09bd..32fb098f970bb1bed29e036b9bb46550882e851e 100644 (file)
@@ -573,7 +573,7 @@ struct Assignment {
     /** the criteria to check if a window matches */
     Match match;
 
-    /** destination workspace/command, depending on the type */
+    /** destination workspace/command/output, depending on the type */
     union {
         char *command;
         char *workspace;
index 5844faa660a3886f78d0e1669f3a162c1385fbc9..01ae67f969c943392ab8506b583cd3b911d5e019 100644 (file)
@@ -83,6 +83,12 @@ void ewmh_update_client_list_stacking(xcb_window_t *stack, int num_windows);
  */
 void ewmh_update_sticky(xcb_window_t window, bool sticky);
 
+/**
+ * Set or remove _NEW_WM_STATE_FOCUSED on the window.
+ *
+ */
+void ewmh_update_focused(xcb_window_t window, bool is_focused);
+
 /**
  * Set up the EWMH hints on the root window.
  *
index babfafc9a583f3321dbe32c4394b9d4ca8e22070..4382437bbbb8f01ef262ca99b4db0fb76ddc2dce 100644 (file)
@@ -143,7 +143,7 @@ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event,
  * outputs.
  *
  */
-void floating_reposition(Con *con, Rect newrect);
+bool floating_reposition(Con *con, Rect newrect);
 
 /**
  * Sets size of the CT_FLOATING_CON to specified dimensions. Might limit the
index 894af9120ed404da00610a8a8d797b5c92aa7e8c..fc956eef6d2c064420ef1edf5790bf7ec1bf583a 100644 (file)
@@ -47,6 +47,7 @@ It tries to start one of the following (in that order):
 * kitty
 * guake
 * tilda
+* alacritty
 
 Please don’t complain about the order: If the user has any preference, they will
 have $TERMINAL set or modified their i3 configuration file.
index 0289fa1ab6018a404cb2445f5fa57b61eaf283eb..4048768e5cee375b1ef31e55ca0ceef50e0c0fcd 100644 (file)
@@ -396,7 +396,7 @@ state MOVE_TO_POSITION_X:
 
 state MOVE_TO_POSITION_Y:
   'px', end
-      -> call cmd_move_window_to_position($method, &coord_x, &coord_y)
+      -> call cmd_move_window_to_position(&coord_x, &coord_y)
 
 # mode <string>
 state MODE:
index a0f5d5a747b79f02c23d1dfe478b5ef59c9e03ae..abacc0a3658990b0e752749e6ff8146afdf24039 100644 (file)
@@ -22,7 +22,7 @@ void run_assignments(i3Window *window) {
     /* Check if any assignments match */
     Assignment *current;
     TAILQ_FOREACH(current, &assignments, assignments) {
-        if (!match_matches_window(&(current->match), window))
+        if (current->type != A_COMMAND || !match_matches_window(&(current->match), window))
             continue;
 
         bool skip = false;
@@ -45,19 +45,16 @@ void run_assignments(i3Window *window) {
         window->ran_assignments = srealloc(window->ran_assignments, sizeof(Assignment *) * window->nr_assignments);
         window->ran_assignments[window->nr_assignments - 1] = current;
 
-        DLOG("matching assignment, would do:\n");
-        if (current->type == A_COMMAND) {
-            DLOG("execute command %s\n", current->dest.command);
-            char *full_command;
-            sasprintf(&full_command, "[id=\"%d\"] %s", window->id, current->dest.command);
-            CommandResult *result = parse_command(full_command, NULL);
-            free(full_command);
+        DLOG("matching assignment, execute command %s\n", current->dest.command);
+        char *full_command;
+        sasprintf(&full_command, "[id=\"%d\"] %s", window->id, current->dest.command);
+        CommandResult *result = parse_command(full_command, NULL);
+        free(full_command);
 
-            if (result->needs_tree_render)
-                needs_tree_render = true;
+        if (result->needs_tree_render)
+            needs_tree_render = true;
 
-            command_result_free(result);
-        }
+        command_result_free(result);
     }
 
     /* If any of the commands required re-rendering, we will do that now. */
index 899bbb90c8f0854254b4d36b52e0e2a21619e034..98625a911bd5b121a3b8574aef5e558ab0beef73 100644 (file)
@@ -1694,7 +1694,7 @@ void cmd_focus_output(I3_CMD, const char *name) {
  * Implementation of 'move [window|container] [to] [absolute] position <px> [px] <px> [px]
  *
  */
-void cmd_move_window_to_position(I3_CMD, const char *method, long x, long y) {
+void cmd_move_window_to_position(I3_CMD, long x, long y) {
     bool has_error = false;
 
     owindow *current;
@@ -1712,27 +1712,18 @@ void cmd_move_window_to_position(I3_CMD, const char *method, long x, long y) {
             continue;
         }
 
-        if (strcmp(method, "absolute") == 0) {
-            current->con->parent->rect.x = x;
-            current->con->parent->rect.y = y;
+        Rect newrect = current->con->parent->rect;
 
-            DLOG("moving to absolute position %ld %ld\n", x, y);
-            floating_maybe_reassign_ws(current->con->parent);
-            cmd_output->needs_tree_render = true;
-        }
+        DLOG("moving to position %ld %ld\n", x, y);
+        newrect.x = x;
+        newrect.y = y;
 
-        if (strcmp(method, "position") == 0) {
-            Rect newrect = current->con->parent->rect;
-
-            DLOG("moving to position %ld %ld\n", x, y);
-            newrect.x = x;
-            newrect.y = y;
-
-            floating_reposition(current->con->parent, newrect);
+        if (!floating_reposition(current->con->parent, newrect)) {
+            yerror("Cannot move window/container out of bounds.");
+            has_error = true;
         }
     }
 
-    // XXX: default reply for now, make this a better reply
     if (!has_error)
         ysuccess(true);
 }
index 24c7b541e447e4f80ab2b676c4d3f0b140bdff6c..fd379fdea475d234a47932b5f234b9116d144c9a 100644 (file)
@@ -103,6 +103,8 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
                 FREE(assign->dest.workspace);
             else if (assign->type == A_COMMAND)
                 FREE(assign->dest.command);
+            else if (assign->type == A_TO_OUTPUT)
+                FREE(assign->dest.output);
             match_free(&(assign->match));
             TAILQ_REMOVE(&assignments, assign, assignments);
             FREE(assign);
@@ -160,10 +162,16 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath,
             FREE(barconfig);
         }
 
-        /* Invalidate pixmap caches in case font or colors changed */
         Con *con;
-        TAILQ_FOREACH(con, &all_cons, all_cons)
-        FREE(con->deco_render_params);
+        TAILQ_FOREACH(con, &all_cons, all_cons) {
+            /* Assignments changed, previously ran assignments are invalid. */
+            if (con->window) {
+                con->window->nr_assignments = 0;
+                FREE(con->window->ran_assignments);
+            }
+            /* Invalidate pixmap caches in case font or colors changed. */
+            FREE(con->deco_render_params);
+        }
 
         /* Get rid of the current font */
         free_font();
index f8422bdadaf1f103f146ecde24a4a94cd279a30a..e5dcafcb012a6572e336e23594fae26301bd3a95 100644 (file)
@@ -284,6 +284,20 @@ void ewmh_update_sticky(xcb_window_t window, bool sticky) {
     }
 }
 
+/*
+ * Set or remove _NEW_WM_STATE_FOCUSED on the window.
+ *
+ */
+void ewmh_update_focused(xcb_window_t window, bool is_focused) {
+    if (is_focused) {
+        DLOG("Setting _NET_WM_STATE_FOCUSED for window = %d.\n", window);
+        xcb_add_property_atom(conn, window, A__NET_WM_STATE, A__NET_WM_STATE_FOCUSED);
+    } else {
+        DLOG("Removing _NET_WM_STATE_FOCUSED for window = %d.\n", window);
+        xcb_remove_property_atom(conn, window, A__NET_WM_STATE, A__NET_WM_STATE_FOCUSED);
+    }
+}
+
 /*
  * Set up the EWMH hints on the root window.
  *
index 13b30f162656d544878ead234af51b7199b2cde4..fc68df8e6b587482ab5043751a56bc711c23db85 100644 (file)
@@ -846,12 +846,12 @@ drag_result_t drag_pointer(Con *con, const xcb_button_press_event_t *event, xcb_
  * outputs.
  *
  */
-void floating_reposition(Con *con, Rect newrect) {
+bool floating_reposition(Con *con, Rect newrect) {
     /* Sanity check: Are the new coordinates on any output? If not, we
      * ignore that request. */
     if (!output_containing_rect(newrect)) {
         ELOG("No output found at destination coordinates. Not repositioning.\n");
-        return;
+        return false;
     }
 
     con->rect = newrect;
@@ -863,6 +863,7 @@ void floating_reposition(Con *con, Rect newrect) {
         con->scratchpad_state = SCRATCHPAD_CHANGED;
 
     tree_render();
+    return true;
 }
 
 /*
index a60a27caae947e9c97a92b6135e686aaf807752b..e8620c247b0339d4b0fe529ef97108816ad703c9 100644 (file)
@@ -256,7 +256,13 @@ void tree_move(Con *con, int direction) {
         return;
     }
 
-    if (con->parent->type == CT_WORKSPACE && con_num_children(con->parent) == 1) {
+    if (con->fullscreen_mode == CF_GLOBAL) {
+        DLOG("Not moving fullscreen global container\n");
+        return;
+    }
+
+    if ((con->fullscreen_mode == CF_OUTPUT) ||
+        (con->parent->type == CT_WORKSPACE && con_num_children(con->parent) == 1)) {
         /* This is the only con on this workspace */
         move_to_output_directed(con, direction);
         return;
diff --git a/src/x.c b/src/x.c
index 7829079bb0363f8514e5b1abe6513434c0db809a..629520d4e0d2ab189e3607e06bcdae24d2cbb305 100644 (file)
--- a/src/x.c
+++ b/src/x.c
@@ -99,6 +99,23 @@ static con_state *state_for_frame(xcb_window_t window) {
     return NULL;
 }
 
+/*
+ * Changes the atoms on the root window and the windows themselves to properly
+ * reflect the current focus for ewmh compliance.
+ *
+ */
+static void change_ewmh_focus(xcb_window_t new_focus, xcb_window_t old_focus) {
+    ewmh_update_active_window(new_focus);
+
+    if (new_focus != XCB_WINDOW_NONE) {
+        ewmh_update_focused(new_focus, true);
+    }
+
+    if (old_focus != XCB_WINDOW_NONE) {
+        ewmh_update_focused(old_focus, false);
+    }
+}
+
 /*
  * Initializes the X11 part for the given container. Called exactly once for
  * every container from con_new().
@@ -1120,7 +1137,7 @@ void x_push_changes(Con *con) {
                      to_focus, focused, focused->name);
                 send_take_focus(to_focus, last_timestamp);
 
-                ewmh_update_active_window((con_has_managed_window(focused) ? focused->window->id : XCB_WINDOW_NONE));
+                change_ewmh_focus((con_has_managed_window(focused) ? focused->window->id : XCB_WINDOW_NONE), last_focused);
 
                 if (to_focus != last_focused && is_con_attached(focused))
                     ipc_send_window_event("focus", focused);
@@ -1139,7 +1156,7 @@ void x_push_changes(Con *con) {
                     xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values);
                 }
 
-                ewmh_update_active_window((con_has_managed_window(focused) ? focused->window->id : XCB_WINDOW_NONE));
+                change_ewmh_focus((con_has_managed_window(focused) ? focused->window->id : XCB_WINDOW_NONE), last_focused);
 
                 if (to_focus != XCB_NONE && to_focus != last_focused && focused->window != NULL && is_con_attached(focused))
                     ipc_send_window_event("focus", focused);
@@ -1154,7 +1171,8 @@ void x_push_changes(Con *con) {
          * root window in order to avoid an X11 fallback mechanism causing a ghosting effect (see #1378). */
         DLOG("Still no window focused, better set focus to the EWMH support window (%d)\n", ewmh_window);
         xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, ewmh_window, last_timestamp);
-        ewmh_update_active_window(XCB_WINDOW_NONE);
+        change_ewmh_focus(XCB_WINDOW_NONE, last_focused);
+
         focused_id = ewmh_window;
     }
 
index e754c0c17f6bf3b8b5b92e72233ebbe05ab8198a..68ac1ee569827edbef609ff00c1f388d1a7a8d9b 100644 (file)
@@ -51,6 +51,7 @@ our @EXPORT = qw(
     kill_all_windows
     events_for
     listen_for_binding
+    is_net_wm_state_focused
 );
 
 =head1 NAME
@@ -1026,6 +1027,40 @@ sub listen_for_binding {
     return $command;
 }
 
+=head2 is_net_wm_state_focused
+
+Returns true if the given window has the _NET_WM_STATE_FOCUSED atom.
+
+    ok(is_net_wm_state_focused($window), '_NET_WM_STATE_FOCUSED set');
+
+=cut
+sub is_net_wm_state_focused {
+    my ($window) = @_;
+
+    sync_with_i3;
+    my $atom = $x->atom(name => '_NET_WM_STATE_FOCUSED');
+    my $cookie = $x->get_property(
+        0,
+        $window->{id},
+        $x->atom(name => '_NET_WM_STATE')->id,
+        GET_PROPERTY_TYPE_ANY,
+        0,
+        4096
+    );
+
+    my $reply = $x->get_property_reply($cookie->{sequence});
+    my $len = $reply->{length};
+    return 0 if $len == 0;
+
+    my @atoms = unpack("L$len", $reply->{value});
+    for (my $i = 0; $i < $len; $i++) {
+        return 1 if $atoms[$i] == $atom->id;
+    }
+
+    return 0;
+}
+
+
 =head1 AUTHOR
 
 Michael Stapelberg <michael@i3wm.org>
index 41d400d5cd15cc34f91ff6822273b56f3ea810b0..c4b42964fc3875580b2dc40e6ee691785e13ce0a 100644 (file)
@@ -55,6 +55,7 @@ subtest 'Window without WM_TAKE_FOCUS', sub {
     my $window = open_window;
 
     ok(!recv_take_focus($window), 'did not receive ClientMessage');
+    ok(is_net_wm_state_focused($window), '_NET_WM_STATE_FOCUSED set');
 
     my ($nodes) = get_ws_content($ws);
     my $con = shift @$nodes;
@@ -91,6 +92,7 @@ subtest 'Window with WM_TAKE_FOCUS and without InputHint', sub {
     $window->map;
 
     ok(!recv_take_focus($window), 'did not receive ClientMessage');
+    ok(is_net_wm_state_focused($window), '_NET_WM_STATE_FOCUSED set');
 
     my ($nodes) = get_ws_content($ws);
     my $con = shift @$nodes;
@@ -112,6 +114,7 @@ subtest 'Window with WM_TAKE_FOCUS and unspecified InputHint', sub {
     my $window = open_window({ protocols => [ $take_focus ] });
 
     ok(!recv_take_focus($window), 'did not receive ClientMessage');
+    ok(is_net_wm_state_focused($window), '_NET_WM_STATE_FOCUSED set');
 
     my ($nodes) = get_ws_content($ws);
     my $con = shift @$nodes;
index 392beae1330e456dc00bc64ae3f20eed708db422..3a3e7c6e8bcf8a87b7686c3c43586bbba2aa839a 100644 (file)
@@ -36,7 +36,7 @@ sub get_wm_state {
     return undef if $len == 0;
 
     my @atoms = unpack("L$len", $reply->{value});
-    return \@atoms;
+    return @atoms;
 }
 
 my $wm_state_sticky = $x->atom(name => '_NET_WM_STATE_STICKY')->id;
@@ -51,18 +51,24 @@ my $wm_state_fullscreen = $x->atom(name => '_NET_WM_STATE_FULLSCREEN')->id;
 fresh_workspace;
 my $window = open_window;
 cmd 'sticky enable';
-is_deeply(get_wm_state($window), [ $wm_state_sticky ], 'sanity check: _NET_WM_STATE_STICKY is set');
+my @state = get_wm_state($window);
+ok((scalar grep { $_ == $wm_state_sticky } @state) > 0, 'sanity check: _NET_WM_STATE_STICKY is set');
 
 cmd 'fullscreen enable';
-is_deeply(get_wm_state($window), [ $wm_state_sticky, $wm_state_fullscreen ],
-    'both _NET_WM_STATE_FULLSCREEN and _NET_WM_STATE_STICKY are set');
+@state = get_wm_state($window);
+ok((scalar grep { $_ == $wm_state_sticky } @state) > 0, '_NET_WM_STATE_STICKY is set');
+ok((scalar grep { $_ == $wm_state_fullscreen } @state) > 0, '_NET_WM_STATE_FULLSCREEN is set');
 
 cmd 'sticky disable';
-is_deeply(get_wm_state($window), [ $wm_state_fullscreen ], 'only _NET_WM_STATE_FULLSCREEN is set');
+@state = get_wm_state($window);
+ok((scalar grep { $_ == $wm_state_sticky } @state) == 0, '_NET_WM_STATE_STICKY is not set');
+ok((scalar grep { $_ == $wm_state_fullscreen } @state) > 0, '_NET_WM_STATE_FULLSCREEN is set');
 
 cmd 'sticky enable';
 cmd 'fullscreen disable';
-is_deeply(get_wm_state($window), [ $wm_state_sticky ], 'only _NET_WM_STATE_STICKY is set');
+@state = get_wm_state($window);
+ok((scalar grep { $_ == $wm_state_sticky } @state) > 0, '_NET_WM_STATE_STICKY is set');
+ok((scalar grep { $_ == $wm_state_fullscreen } @state) == 0, '_NET_WM_STATE_FULLSCREEN is not set');
 
 ###############################################################################
 # _NET_WM_STATE is removed when the window is withdrawn.
@@ -71,7 +77,8 @@ is_deeply(get_wm_state($window), [ $wm_state_sticky ], 'only _NET_WM_STATE_STICK
 fresh_workspace;
 $window = open_window;
 cmd 'sticky enable';
-is_deeply(get_wm_state($window), [ $wm_state_sticky ], 'sanity check: _NET_WM_STATE_STICKY is set');
+@state = get_wm_state($window);
+ok((scalar grep { $_ == $wm_state_sticky } @state) > 0, '_NET_WM_STATE_STICKY is set');
 
 $window->unmap;
 wait_for_unmap($window);
diff --git a/testcases/t/295-net-wm-state-focused.t b/testcases/t/295-net-wm-state-focused.t
new file mode 100644 (file)
index 0000000..1881154
--- /dev/null
@@ -0,0 +1,37 @@
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+#   (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+#   (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+#   (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+#   (unless you are already familiar with Perl)
+#
+# Tests for setting and removing the _NET_WM_STATE_FOCUSED atom properly.
+# Ticket: #2273
+use i3test;
+use X11::XCB qw(:all);
+
+my ($windowA, $windowB);
+
+fresh_workspace;
+$windowA = open_window;
+
+ok(is_net_wm_state_focused($windowA), 'a newly opened window that is focused should have _NET_WM_STATE_FOCUSED set');
+
+$windowB = open_window;
+
+ok(!is_net_wm_state_focused($windowA), 'when a another window is focused, the old window should not have _NET_WM_STATE_FOCUSED set');
+
+fresh_workspace;
+
+ok(!is_net_wm_state_focused($windowB), 'when focus moves to the ewmh support window, neither window should have _NET_WM_STATE_FOCUSED set');
+
+done_testing;
index 5bcdb09afe8f59c756286bdd90e370998b46929b..3db8c4f01a005e5bb4da0e175fa51b417b357133 100644 (file)
@@ -97,4 +97,32 @@ is(scalar @{get_ws_content('left-top')}, 1, 'moved some window to left-bottom wo
 $compare_window = shift @{get_ws_content('left-top')};
 is($social_window->name, $compare_window->{name}, 'moved correct window to left-bottom workspace');
 
+#####################################################################
+# Moving a fullscreen container should change its output.
+#####################################################################
+
+kill_all_windows;
+
+cmd 'workspace left-top';
+open_window;
+my $fs_window = open_window;
+open_window;
+
+cmd '[id=' . $fs_window->id . '] fullscreen enable, move right';
+is(scalar @{get_ws_content('right-top')}, 1, 'moved fullscreen window to right-top workspace');
+
+#####################################################################
+# Moving a global fullscreen container should not change its output.
+#####################################################################
+
+kill_all_windows;
+
+cmd 'workspace left-top';
+open_window;
+open_window;
+open_window;
+
+cmd 'fullscreen global, move right, fullscreen disable';
+is(scalar @{get_ws_content('right-top')}, 0, 'global fullscreen window didn\'t change workspace with move');
+
 done_testing;