This is the full thing, with no known regressions.

There is a minor semantics change (which is also changed in the man page). Mod+m is now used to cycle through maximized clients, while Mod+Shift+m is used to enable and disable it. The usual cycling avoids maximized clients (because they are minimized).

The changes fall into:

- correctness fixes (some split up into a separate patch)
- get, is and next functions for maximized windows
- an override layout property
- some hooks to enable and disable the override when appropriate

I suggest you try it out to see the effects.

Sincerely,

Maarten.


>From e0805cf585d9db8573da3d8f695014cc6382d71d Mon Sep 17 00:00:00 2001
From: Maarten Maathuis <[EMAIL PROTECTED]>
Date: Thu, 4 Dec 2008 15:16:38 +0100
Subject: [PATCH] client: Improve handling of transient windows.

Signed-off-by: Maarten Maathuis <[EMAIL PROTECTED]>
---
 client.c |   14 ++++++++++----
 client.h |   34 ++++++++++++++++++++++++++++++----
 2 files changed, 40 insertions(+), 8 deletions(-)

diff --git a/client.c b/client.c
index 68ef4b2..1cf4eea 100644
--- a/client.c
+++ b/client.c
@@ -414,7 +414,7 @@ void
 client_manage(xcb_window_t w, xcb_get_geometry_reply_t *wgeom, int 
phys_screen, int screen)
 {
     xcb_get_property_cookie_t ewmh_icon_cookie;
-    client_t *c, *group = NULL;
+    client_t *c, *tc = NULL, *group = NULL;
     image_t *icon;
     const uint32_t select_input_val[] =
     {
@@ -459,8 +459,14 @@ client_manage(xcb_window_t w, xcb_get_geometry_reply_t 
*wgeom, int phys_screen,
     property_update_wm_transient_for(c, NULL);
     property_update_wm_client_leader(c, NULL);
 
-    if(c->transient_for)
-        screen = c->transient_for->screen;
+    /* Recursively find the parent. */
+    if(c->transient_for) {
+        tc = c;
+        do {
+            tc = tc->transient_for;
+        } while (tc->transient_for);
+        screen = tc->screen;
+    }
 
     /* Try to load props, if it fails check for group windows.
      * transient_for windows are excluded, because they inherit the parent 
tags. */
@@ -489,7 +495,7 @@ client_manage(xcb_window_t w, xcb_get_geometry_reply_t 
*wgeom, int phys_screen,
     if (!c->issticky)
     {
         if(c->transient_for)
-            client_duplicate_tags(c->transient_for, c);
+            client_duplicate_tags(tc, c);
         else if (group)
             client_duplicate_tags(group, c);
         else
diff --git a/client.h b/client.h
index 5e403c0..c6ae25a 100644
--- a/client.h
+++ b/client.h
@@ -79,9 +79,28 @@ DO_SLIST(client_t, client, client_unref)
 static inline void
 client_raise(client_t *c)
 {
+    client_t *tc = c;
+    int i, counter = 0;
+
+    /* Find number of transient layers. */
+    if(c->transient_for) {
+        do {
+            tc = tc->transient_for;
+            counter++;
+        } while (tc->transient_for);
+    }
+
+    /* Push them in reverse order. */
+    while (counter > 0) {
+        tc = c;
+        for (i = 0; i < counter; i++) {
+            tc = tc->transient_for;
+        }
+        stack_client_push(tc);
+        counter--;
+    }
+
     /* Push c on top of the stack. */
-    if(c->transient_for)
-        stack_client_push(c->transient_for);
     stack_client_push(c);
     client_stack();
 }
@@ -92,9 +111,16 @@ client_raise(client_t *c)
 static inline void
 client_lower(client_t *c)
 {
+    client_t *tc = c;
     stack_client_append(c);
-    if(c->transient_for)
-        stack_client_append(c->transient_for);
+    /* Traverse all transient layers. */
+    if(c->transient_for) {
+        do {
+            tc = tc->transient_for;
+            stack_client_append(tc);
+        } while (tc->transient_for);
+    }
+
     client_stack();
 }
 
-- 
1.6.0.4

>From 438f338c998953cd12c2c2007615cfbe5aa6dd83 Mon Sep 17 00:00:00 2001
From: Maarten Maathuis <[EMAIL PROTECTED]>
Date: Thu, 4 Dec 2008 19:11:37 +0100
Subject: [PATCH] client: Improved handling of maximized windows.

Signed-off-by: Maarten Maathuis <[EMAIL PROTECTED]>
---
 awesome.1.txt                |    4 +-
 awesomerc.lua.in             |   28 ++++++++++++-
 client.h                     |    9 ++++-
 lib/awful/client.lua.in      |   57 ++++++++++++++++++++++++-
 lib/awful/layout/init.lua.in |   93 +++++++++++++++++++++++++++++++++++++++++-
 5 files changed, 183 insertions(+), 8 deletions(-)

diff --git a/awesome.1.txt b/awesome.1.txt
index 52303b0..d911c09 100644
--- a/awesome.1.txt
+++ b/awesome.1.txt
@@ -91,7 +91,7 @@ Clients
     Print the client class and instance.
 *Mod4 \+ Shift \+ r*::
     Redraw the focused window.
-*Mod4 \+ m*::
+*Mod4 \+ Shift \+ m*::
     Maximize client.
 *Mod4 \+ f*::
     Set client fullscreen.
@@ -106,6 +106,8 @@ Navigation
     Focus next client.
 *Mod4 \+ k*::
     Focus previous client.
+*Mod4 \+ m*::
+    Focus next maximized client.
 *Mod4 \+ u*::
     Focus first urgent client.
 *Mod4 \+ Left*::
diff --git a/awesomerc.lua.in b/awesomerc.lua.in
index 61d4ac2..6f202c9 100644
--- a/awesomerc.lua.in
+++ b/awesomerc.lua.in
@@ -217,8 +217,32 @@ keybinding({ modkey, "Control" }, "r", function ()
 keybinding({ modkey, "Shift" }, "q", awesome.quit):add()
 
 -- Client manipulation
-keybinding({ modkey }, "m", function () if client.focus then 
client.focus.maximized_horizontal = not client.focus.maximized_horizontal
-                                                             
client.focus.maximized_vertical = not client.focus.maximized_vertical end 
end):add()
+keybinding({ modkey }, "m",
+function()
+    local c = awful.client.next_maximized(1)
+    local cls = {}
+    if c == nil then
+        cls = awful.client.maximized()
+        if #cls > 0 then
+            c = cls[1]
+        end
+    end
+    if c then
+        client.focus = c
+        c:raise()
+    end
+end):add()
+keybinding({ modkey, "Shift" }, "m",
+function()
+    local c = client.focus
+    if c and awful.client.ismaximized(c) then
+        c.maximized_horizontal = false
+        c.maximized_vertical = false
+    elseif c then
+        c.maximized_horizontal = true
+        c.maximized_vertical = true
+    end
+end):add()
 keybinding({ modkey }, "f", function () if client.focus then 
client.focus.fullscreen = not client.focus.fullscreen end end):add()
 keybinding({ modkey, "Shift" }, "c", function () if client.focus then 
client.focus:kill() end end):add()
 keybinding({ modkey }, "j", function () awful.client.focus.byidx(1); if 
client.focus then client.focus:raise() end end):add()
diff --git a/client.h b/client.h
index c6ae25a..2895788 100644
--- a/client.h
+++ b/client.h
@@ -38,10 +38,13 @@ client_delete(client_t **c)
 ARRAY_FUNCS(client_t *, client, DO_NOTHING)
 DO_RCNT(client_t, client, client_delete)
 
+/* client_isvisible_exclude_banned was chosen with reason.
+ * Otherwise clients never become visible under certain conditions.
+*/
 #define client_need_arrange(c) \
     do { \
         if(!globalconf.screens[(c)->screen].need_arrange \
-           && client_isvisible(c, (c)->screen)) \
+           && client_isvisible_exclude_banned(c, (c)->screen)) \
             globalconf.screens[(c)->screen].need_arrange = true; \
     } while(0)
 
@@ -144,6 +147,10 @@ client_isfixed(client_t *c)
 static inline bool
 client_isvisible_exclude_banned(client_t *c, int screen)
 {
+    /* If the parent is invisible, then so should the child. */
+    if(c->transient_for)
+        if(!client_isvisible_exclude_banned(c->transient_for, screen))
+            return false;
     return (!c->ishidden && !c->isminimized && client_maybevisible(c, screen));
 }
 
diff --git a/lib/awful/client.lua.in b/lib/awful/client.lua.in
index 5ad971f..0ad9d77 100644
--- a/lib/awful/client.lua.in
+++ b/lib/awful/client.lua.in
@@ -18,6 +18,7 @@ local capi =
     client = client,
     mouse = mouse,
     screen = screen,
+    tag = tag,
     hooks = hooks
 }
 local hooks = require("awful.hooks")
@@ -174,6 +175,38 @@ function focus.history.previous()
     if c then capi.client.focus = c end
 end
 
+--- Return true if client is maximized.
+-- @param c The client.
+-- @return A boolean.
+function ismaximized(c)
+    local ismax = false
+
+    if c and (c.maximized_horizontal or c.maximized_vertical) then
+        ismax = true
+    end
+
+    return ismax
+end
+
+--- Get maximized clients from a screen that would otherwise be visible.
+-- This means only maximized clients from the currently selected tags.
+-- @param screen The screen number, or nil for the current screen.
+-- @return A table with maximized clients.
+function maximized(screen)
+    local tls = tag.selectedlist(screen)
+    local cls = {}
+    local mcls = {}
+    for k, t in pairs(tls) do
+        cls = t:clients()
+        for l, c in pairs(cls) do
+            if ismaximized(c) then
+                table.insert(mcls, c)
+            end
+        end
+    end
+    return mcls
+end
+
 --- Get visible clients from a screen.
 -- @param screen The screen number, or nil for all screens.
 -- @return A table with all visible clients.
@@ -203,6 +236,26 @@ function tiled(screen)
     return tclients
 end
 
+--- Get a maximized client by it's relative index to the focussed maximized 
window.
+-- @usage Set i to 1 to get next, -1 to get previous.
+-- @param i The index.
+-- @param c Optional client.
+-- @return A client, or nil if no client is available.
+function next_maximized(i, c)
+    local sel = c or capi.client.focus
+    if sel and ismaximized(sel) then
+        -- Get maximized clients
+        local cls = maximized(sel.screen)
+        -- Loop upon each client
+        for idx, c in ipairs(cls) do
+            if c == sel then
+                -- Cycle
+                return cls[util.cycle(#cls, idx + i)]
+            end
+        end
+    end
+end
+
 --- Get a client by its relative index to the focused window.
 -- @usage Set i to 1 to get next, -1 to get previous.
 -- @param i The index.
@@ -544,9 +597,7 @@ function floating.get(c)
             return data.floating[c]
         end
         return (c.type ~= "normal"
-            or c.fullscreen
-            or c.maximized_vertical
-            or c.maximized_horizontal)
+            or c.fullscreen)
     end
 end
 
diff --git a/lib/awful/layout/init.lua.in b/lib/awful/layout/init.lua.in
index 4b0bd40..4496bae 100644
--- a/lib/awful/layout/init.lua.in
+++ b/lib/awful/layout/init.lua.in
@@ -5,12 +5,15 @@
 ---------------------------------------------------------------------------
 
 -- Grab environment we need
+local pairs = pairs
 local ipairs = ipairs
 local tag = require("awful.tag")
 local util = require("awful.util")
 local suit = require("awful.layout.suit")
+local client = require("awful.client")
 local capi =
 {
+    client = client,
     hooks = hooks,
     screen = screen
 }
@@ -60,7 +63,7 @@ end
 -- Register an arrange hook.
 local function on_arrange (screen)
     local t = tag.selected(screen)
-    local l = tag.getproperty(t, "layout") or suit.tile
+    local l = tag.getproperty(t, "layout_override") or tag.getproperty(t, 
"layout") or suit.tile
     l(screen)
 end
 
@@ -85,6 +88,94 @@ function getname(layout)
     return layouts_name[layout]
 end
 
+-- Set layout override and also clean up.
+-- Being a local function it shouldn't end up in luadoc.
+-- @param c The client, nil for no override.
+-- @param screen The screen, required.
+-- @param to Optionally, the tag where the override should be set.
+local function set_override(c, screen, to)
+    local t = to or tag.selected(screen)
+    local lo = tag.getproperty(t, "layout_override")
+    local co = tag.getproperty(t, "client_override")
+
+    -- Do we need to change?
+    if c ~= co then
+        -- Cleanup first
+        tag.setproperty(t, "layout_override", nil)
+        if co then
+            tag.setproperty(t, "client_override", nil)
+            -- An ex-override window may have become normal
+            if client.ismaximized(co) then
+                co.minimized = true
+            else
+                co.minimized = false
+            end
+        end
+
+        -- New override
+        if c then
+            tag.setproperty(t, "layout_override", suit.max)
+            tag.setproperty(t, "client_override", c)
+            c.minimized = false
+        end
+    end
+end
+
+hooks.focus.register(function(c)
+    local tc = c
+
+    -- Focussing the transient client should also expose the parent
+    if tc.transient_for then
+        while tc.transient_for do
+            tc = tc.transient_for
+        end
+    end
+
+     -- Stay away from floating clients
+    if not client.floating.get(tc) then
+        if client.ismaximized(tc) then
+            set_override(tc, tc.screen)
+        else
+            set_override(nil, tc.screen)
+        end
+    end
+end)
+
+-- We need to make sure any overrides are removed upon untagging.
+-- This covers screen switches and unmanages as well.
+hooks.tagged.register(function(c, t)
+    local tls = c:tags()
+    local co = tag.getproperty(t, "client_override")
+    local done = false
+
+    if co == c then
+        -- Are we part of this tag?
+        for k, t2 in pairs(tls) do
+            if t2 == t then
+                done = true
+            end
+        end
+
+        -- We're not part of this tag
+        if not done then
+            set_override(nil, c.screen, t)
+        end
+    end
+end)
+
+hooks.property.register(function(c, prop)
+    if prop == "maximized_horizontal" or prop == "maximized_vertical" then
+        -- Stay away from floating clients
+        if not client.floating.get(c) then
+            if client.ismaximized(c) then
+                set_override(c, c.screen)
+            else
+                set_override(nil, c.screen)
+            end
+        end
+    end
+end)
+
 hooks.arrange.register(on_arrange)
 
 -- vim: 
filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
-- 
1.6.0.4

Reply via email to