]> git.sur5r.net Git - i3/i3/commitdiff
Argument for 'kill' for killing a specific window (now default) or the whole client...
authorMichael Stapelberg <michael@stapelberg.de>
Fri, 13 May 2011 18:41:03 +0000 (20:41 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Fri, 13 May 2011 18:41:03 +0000 (20:41 +0200)
Use 'kill window' to kill a specific window (for example only one specific
popup), use 'kill client' to kill the whole application (or X11 connection to
be specific).

13 files changed:
include/data.h
include/tree.h
include/x.h
src/cmdparse.l
src/cmdparse.y
src/con.c
src/floating.c
src/handlers.c
src/randr.c
src/tree.c
src/workspace.c
src/x.c
testcases/t/64-kill-win-vs-client.t [new file with mode: 0644]

index d4836dfe62c02817578c66cb10ade09456fa0f86..1165da990388ce4856f4f8e7dcad1addfec2fd9e 100644 (file)
@@ -43,6 +43,10 @@ typedef enum { D_LEFT, D_RIGHT, D_UP, D_DOWN } direction_t;
 typedef enum { NO_ORIENTATION = 0, HORIZ, VERT } orientation_t;
 typedef enum { BS_NORMAL = 0, BS_NONE = 1, BS_1PIXEL = 2 } border_style_t;
 
+/** parameter to specify whether tree_close() and x_window_kill() should kill
+ * only this specific window or the whole X11 client */
+typedef enum { DONT_KILL_WINDOW = 0, KILL_WINDOW = 1, KILL_CLIENT = 2 } kill_window_t;
+
 enum {
     BIND_NONE = 0,
     BIND_SHIFT = XCB_MOD_MASK_SHIFT,        /* (1 << 0) */
index 8ffeca0d6afff41350606468d053ecc2ff9c4b73..98358edd622bf5bb02d7014e23b1aeca33f4c0a6 100644 (file)
@@ -56,7 +56,7 @@ void tree_render();
  * Closes the current container using tree_close().
  *
  */
-void tree_close_con();
+void tree_close_con(kill_window_t kill_window);
 
 /**
  * Changes focus in the given way (next/previous) and given orientation
@@ -71,7 +71,7 @@ void tree_next(char way, orientation_t orientation);
  * and the window is expected to kill itself.
  *
  */
-bool tree_close(Con *con, bool kill_window, bool dont_kill_parent);
+bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent);
 
 /**
  * Loads tree from ~/.i3/_restart.json (used for in-place restarts).
index d110d92cd67995fedf1da1913c4ab14f1c7cc0c7..b8ead5eec7210a5155012c19188905fb55234046 100644 (file)
@@ -52,7 +52,7 @@ bool window_supports_protocol(xcb_window_t window, xcb_atom_t atom);
  * Kills the given X11 window using WM_DELETE_WINDOW (if supported).
  *
  */
-void x_window_kill(xcb_window_t window);
+void x_window_kill(xcb_window_t window, kill_window_t kill_window);
 
 /**
  * Draws the decoration of the given container onto its parent.
index 3e5dc889098c50fd28834ec3fae9f183a9359672..6e33c950fbbffde9cfb6d6ac374cba0556fa346c 100644 (file)
@@ -2,7 +2,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2010 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * cmdparse.l: the lexer for commands you send to i3 (or bind on keys)
  *
@@ -92,6 +92,8 @@ exit                            { return TOK_EXIT; }
 reload                          { return TOK_RELOAD; }
 restart                         { return TOK_RESTART; }
 kill                            { return TOK_KILL; }
+window                          { return TOK_WINDOW; }
+client                          { return TOK_CLIENT; }
 fullscreen                      { return TOK_FULLSCREEN; }
 global                          { return TOK_GLOBAL; }
 layout                          { return TOK_LAYOUT; }
index 5dacc64ff3050b7b8608b3699f4d00125f771598..73658eb251413a69f8ed0fb4cc125f891d401882 100644 (file)
@@ -91,7 +91,7 @@ char *parse_cmd(const char *new) {
 
 %}
 
-%expect 4
+%expect 5
 %error-verbose
 %lex-param { struct context *context }
 
@@ -107,6 +107,8 @@ char *parse_cmd(const char *new) {
 %token              TOK_RELOAD          "reload"
 %token              TOK_RESTART         "restart"
 %token              TOK_KILL            "kill"
+%token              TOK_WINDOW          "window"
+%token              TOK_CLIENT          "client"
 %token              TOK_FULLSCREEN      "fullscreen"
 %token              TOK_GLOBAL          "global"
 %token              TOK_LAYOUT          "layout"
@@ -161,6 +163,7 @@ char *parse_cmd(const char *new) {
 %type   <number>    resize_px
 %type   <number>    resize_way
 %type   <number>    resize_tiling
+%type   <number>    optional_kill_mode
 
 %%
 
@@ -398,24 +401,30 @@ focus:
     ;
 
 kill:
-    TOK_KILL
+    TOK_KILL optional_kill_mode
     {
         owindow *current;
 
         printf("killing!\n");
         /* check if the match is empty, not if the result is empty */
         if (match_is_empty(&current_match))
-            tree_close_con();
+            tree_close_con($2);
         else {
             TAILQ_FOREACH(current, &owindows, owindows) {
                 printf("matching: %p / %s\n", current->con, current->con->name);
-                tree_close(current->con, true, false);
+                tree_close(current->con, $2, false);
             }
         }
 
     }
     ;
 
+optional_kill_mode:
+    /* empty */             { $$ = KILL_WINDOW; }
+    | WHITESPACE TOK_WINDOW { $$ = KILL_WINDOW; }
+    | WHITESPACE TOK_CLIENT { $$ = KILL_CLIENT; }
+    ;
+
 workspace:
     TOK_WORKSPACE WHITESPACE STR
     {
index 4bfd5b0787576f11f0116cc1e39db872d85ea704..45021f5f71320cf56cbde11e2a4a392fe653e593 100644 (file)
--- a/src/con.c
+++ b/src/con.c
@@ -885,7 +885,7 @@ static void con_on_remove_child(Con *con) {
     int children = con_num_children(con);
     if (children == 0) {
         DLOG("Container empty, closing\n");
-        tree_close(con, false, false);
+        tree_close(con, DONT_KILL_WINDOW, false);
         return;
     }
 }
index 2ac4afda0d39bb5e32a14aa38ca3715a50ec8905..2f3f9bac3413d53b9f4bfb457e21104df29f951a 100644 (file)
@@ -85,7 +85,7 @@ void floating_enable(Con *con, bool automatic) {
     /* check if the parent container is empty and close it if so */
     if ((con->parent->type == CT_CON || con->parent->type == CT_FLOATING_CON) && con_num_children(con->parent) == 0) {
         DLOG("Old container empty after setting this child to floating, closing\n");
-        tree_close(con->parent, false, false);
+        tree_close(con->parent, DONT_KILL_WINDOW, false);
     }
 
     char *name;
@@ -186,7 +186,7 @@ void floating_disable(Con *con, bool automatic) {
     /* 2: kill parent container */
     TAILQ_REMOVE(&(con->parent->parent->floating_head), con->parent, floating_windows);
     TAILQ_REMOVE(&(con->parent->parent->focus_head), con->parent, focused);
-    tree_close(con->parent, false, false);
+    tree_close(con->parent, DONT_KILL_WINDOW, false);
 
     /* 3: re-attach to the parent of the currently focused con on the workspace
      * this floating con was on */
index c63f236c1c8510571d82dd824c257f578ac6fa1f..f1e1c4b02704c6d3a828a47c88b858f70def6913 100644 (file)
@@ -466,7 +466,7 @@ static int handle_unmap_notify_event(xcb_unmap_notify_event_t *event) {
         return 1;
     }
 
-    tree_close(con, false, false);
+    tree_close(con, DONT_KILL_WINDOW, false);
     tree_render();
     x_push_changes(croot);
     return 1;
index 0c43670d8c3542b5e25e4408f8942fa04a89f9e1..b61e309056f9516dff8cd318bd36d362ee4910bc 100644 (file)
@@ -709,7 +709,7 @@ void randr_query_outputs() {
                 }
 
                 DLOG("destroying disappearing con %p\n", output->con);
-                tree_close(output->con, false, true);
+                tree_close(output->con, DONT_KILL_WINDOW, true);
                 DLOG("Done. Should be fine now\n");
                 output->con = NULL;
             }
index 411e525145ee0b6873ebd5092f3d9302ce52500d..8d55c14b69b105c251fd13134b878237f824854a 100644 (file)
@@ -99,7 +99,7 @@ static bool _is_con_mapped(Con *con) {
  * and the window is expected to kill itself.
  *
  */
-bool tree_close(Con *con, bool kill_window, bool dont_kill_parent) {
+bool tree_close(Con *con, kill_window_t kill_window, bool dont_kill_parent) {
     bool was_mapped = con->mapped;
     Con *parent = con->parent;
 
@@ -133,8 +133,8 @@ bool tree_close(Con *con, bool kill_window, bool dont_kill_parent) {
     }
 
     if (con->window != NULL) {
-        if (kill_window) {
-            x_window_kill(con->window->id);
+        if (kill_window != DONT_KILL_WINDOW) {
+            x_window_kill(con->window->id, kill_window);
             return false;
         } else {
             /* un-parent the window */
@@ -165,7 +165,7 @@ bool tree_close(Con *con, bool kill_window, bool dont_kill_parent) {
     if (con_is_floating(con)) {
         Con *ws = con_get_workspace(con);
         DLOG("Container was floating, killing floating container\n");
-        tree_close(parent, false, false);
+        tree_close(parent, DONT_KILL_WINDOW, false);
         DLOG("parent container killed\n");
         if (con == focused) {
             DLOG("This is the focused container, i need to find another one to focus. I start looking at ws = %p\n", ws);
@@ -192,7 +192,7 @@ bool tree_close(Con *con, bool kill_window, bool dont_kill_parent) {
     }
 
     if (was_mapped || con == focused) {
-        if (kill_window || !dont_kill_parent || con == focused) {
+        if ((kill_window != DONT_KILL_WINDOW) || !dont_kill_parent || con == focused) {
             DLOG("focusing %p / %s\n", next, next->name);
             /* TODO: check if the container (or one of its children) was focused */
             if (next->type == CT_DOCKAREA) {
@@ -220,7 +220,7 @@ bool tree_close(Con *con, bool kill_window, bool dont_kill_parent) {
  * Closes the current container using tree_close().
  *
  */
-void tree_close_con() {
+void tree_close_con(kill_window_t kill_window) {
     assert(focused != NULL);
     if (focused->type == CT_WORKSPACE) {
         LOG("Cannot close workspace\n");
@@ -232,7 +232,7 @@ void tree_close_con() {
     assert(focused->type != CT_ROOT);
 
     /* Kill con */
-    tree_close(focused, true, false);
+    tree_close(focused, kill_window, false);
 }
 
 /*
@@ -463,7 +463,7 @@ void tree_flatten(Con *con) {
 
     /* 4: close the redundant cons */
     DLOG("closing redundant cons\n");
-    tree_close(con, false, true);
+    tree_close(con, DONT_KILL_WINDOW, true);
 
     /* Well, we got to abort the recursion here because we destroyed the
      * container. However, if tree_flatten() is called sufficiently often,
index 8104aa89fc35d6da21765408718255e5d06a0473..7ff31157f8d7c22443e5eab0cefe5e354970c62b 100644 (file)
@@ -244,7 +244,7 @@ void workspace_show(const char *num) {
         /* check if this workspace is currently visible */
         if (!workspace_is_visible(old)) {
             LOG("Closing old workspace (%p / %s), it is empty\n", old, old->name);
-            tree_close(old, false, false);
+            tree_close(old, DONT_KILL_WINDOW, false);
             ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}");
             changed_num_workspaces = true;
         }
diff --git a/src/x.c b/src/x.c
index 76add0ff7090fdfc1fcbd9b97506c25548a4fa47..4fd8a8ba5fa2993f90f0012f37c68856138949b7 100644 (file)
--- a/src/x.c
+++ b/src/x.c
@@ -199,11 +199,16 @@ bool window_supports_protocol(xcb_window_t window, xcb_atom_t atom) {
  * Kills the given X11 window using WM_DELETE_WINDOW (if supported).
  *
  */
-void x_window_kill(xcb_window_t window) {
+void x_window_kill(xcb_window_t window, kill_window_t kill_window) {
     /* if this window does not support WM_DELETE_WINDOW, we kill it the hard way */
     if (!window_supports_protocol(window, A_WM_DELETE_WINDOW)) {
-        LOG("Killing window the hard way\n");
-        xcb_kill_client(conn, window);
+        if (kill_window == KILL_WINDOW) {
+            LOG("Killing specific window 0x%08x\n", window);
+            xcb_destroy_window(conn, window);
+        } else {
+            LOG("Killing the X11 client which owns window 0x%08x\n", window);
+            xcb_kill_client(conn, window);
+        }
         return;
     }
 
diff --git a/testcases/t/64-kill-win-vs-client.t b/testcases/t/64-kill-win-vs-client.t
new file mode 100644 (file)
index 0000000..2e0669b
--- /dev/null
@@ -0,0 +1,66 @@
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Tests if WM_STATE is WM_STATE_NORMAL when mapped and WM_STATE_WITHDRAWN when
+# unmapped.
+#
+use X11::XCB qw(:all);
+use X11::XCB::Connection;
+use i3test;
+
+my $x = X11::XCB::Connection->new;
+
+sub two_windows {
+    my $tmp = fresh_workspace;
+
+    ok(@{get_ws_content($tmp)} == 0, 'no containers yet');
+
+    my $first = open_standard_window($x);
+    my $second = open_standard_window($x);
+
+    is($x->input_focus, $second->id, 'second window focused');
+    ok(@{get_ws_content($tmp)} == 2, 'two containers opened');
+
+    return $tmp;
+}
+
+##############################################################
+# 1: open two windows (in the same client), kill one and see if
+# the other one is still there
+##############################################################
+
+my $tmp = two_windows;
+
+cmd 'kill';
+
+sleep 0.25;
+
+ok(@{get_ws_content($tmp)} == 1, 'one container left after killing');
+
+##############################################################
+# 2: same test case as test 1, but with the explicit variant
+# 'kill window'
+##############################################################
+
+my $tmp = two_windows;
+
+cmd 'kill window';
+
+sleep 0.25;
+
+ok(@{get_ws_content($tmp)} == 1, 'one container left after killing');
+
+##############################################################
+# 3: open two windows (in the same client), use 'kill client'
+# and check if both are gone
+##############################################################
+
+my $tmp = two_windows;
+
+cmd 'kill client';
+
+sleep 0.25;
+
+ok(@{get_ws_content($tmp)} == 0, 'no containers left after killing');
+
+done_testing;