Skip to content

Client effects

Simon B edited this page Nov 16, 2022 · 3 revisions

nomaximizedclients

withmaximizedclients

To make window effects look nice and fancy without them becoming overly annoying or distracting, I added some tweaks making them more "responsive" to what you're doing. These tweaks consist of two main parts:

Client-side tweaks

Whenever you maximize a client instance or enable full screen mode, three things will be done to it:

  • The border width will be set to 0

  • The shape will be set to gears.shape.rectange, effectively disabling the shape

  • A special property will be added, causing picom to (temporarily) disable shadows for that client instance

When you un-maximize the client instance / disable full screen mode, all of these values will be restored to their previous values (not to defaults!).

Panel tweaks

Because it looks very weird (to me) to have cutout corners in the panel when a client instance is maximized / full screen, I also added a tweak that, whenever any client instance's maximized or fullscreen property, or the currently active tag changes, the bar will loop through each visible client instance and, if one of them has either a true maximized or fullscreen property, the bar's shape will be set to gears.shape.rectange.

But how is it actually implemented?

The code for the client-side effects looks something like this (I intentionally omitted some details compared to the actually used code to keep this short, although there's little functional difference):

local awful = require("awful")
local gears = require("gears")

local function update_shape_of(c)
    if c.maximized or c.fullscreen then
        c.shape = gears.shape.rectangle
        c.border_width = 0
        awful.spawn { "xprop", "-id", tostring(c.window), "-f", "_PICOM_SHADOW", "32c", "-set", "_PICOM_SHADOW", "0" }
    else
        c.shape = function(cr, w, h)
            gears.shape.rounded_rect(cr, w, h, 20)
        end
        c.border_width = 2
        awful.spawn { "xprop", "-id", tostring(c.window), "-f", "_PICOM_SHADOW", "32c", "-set", "_PICOM_SHADOW", "1" }
    end
end

for _, c in ipairs(client.get()) do
    update_shape_of(c)
end

client.connect_signal("manage", update_shape_of)
client.connect_signal("property::fullscreen", update_shape_of)
client.connect_signal("property::maximized",  update_shape_of)

Since that's quite noisy, let's go through that one by one.

  • First, we import awful and gears. That should be done at the top of your rc.lua by default if you haven't changed anything, in which case you can omit the first 2 lines here.

  • Next, we define a new local function called update_shape_of, which takes a client instance (an app window) as it's first parameter. A client instance has to be passed or it will crash.

  • In this function, we check whether either the maximized or fullscreen property or both is/are true.

    • If true, we make the client instance rectangular, set it's border width to 0 (thus hiding it) and add a special property called _PICOM_SHADOW to the client instance using the xprop command line utility and set its value to 0 to set it to off. In the picom.conf, we can react to this using the shadow-exclude block:

    • shadow-exclude = [
          # ...
          "_PICOM_SHADOW@:32c = 0",
          # ...
      ]
    • If false, we set the shape and border width to the desired values. We also use xprop again here, to make sure that, when a client gets un-maximized / un-fullscreened, it also gets its shadow back.

  • After the function, we loop through each existing client instance to make sure that these rules are immediately applied to all already existing client instances.

  • Finally, we connect to some client signals, which makes sure that the correct style of a client instance is applied at its launch and changes when one of its properties does.

For the panel:    

local awful = require("awful")
local gears = require("gears")

screen.connect_signal("request::desktop_decoration", function(s)
        local my_panel = awful.wibar {
            -- ...
        }

        local panel_shape

        function s:update_bar_shape()
            for _, c in pairs(self.clients) do
                local screen_tags = self.selected_tags
                local client_tags = c:tags()

                for _, st in ipairs(screen_tags) do
                    for _, ct in ipairs(client_tags) do
                        if st == ct and c.maximized and c:isvisible() then
                            self.my_panel.shape = gears.shape.rectangle
                            self.my_panel.widget.shape = gears.shape.rectangle
                            return
                        end
                    end
                end
            end

            self.my_panel.shape = panel_shape
            self.my_panel.widget.shape = panel_shape
        end

    client.connect_signal("property::minimized", function(c) s:update_bar_shape() end)
    client.connect_signal("property::maximized", function(c) s:update_bar_shape() end)
    for _, t in pairs(s.tags) do
        t:connect_signal("property::selected", function(c) s:update_bar_shape() end)
    end
end

client.connect_signal("manage", function(c)
    for s in screen do
        s:update_bar_shape()
    end
end)