Search code examples
luaclientfocusawesome-wm

Is there something like "awful.client.focus.global_byidx()"?


I have two monitors and I would like to change client focused by shortcut. Right now I have awful.client.focus.byidx(1) but it only changes clients on one monitor. I know there is awful.client.focus.global_bydirection("right") that works on multiple screens but it only goes to one direction and it stops working when get to last client. I like awful.client.focus.byidx(1) behavior which loops through all clients, so from the last client it goes to the first one.

I would like to use a hybrid between: going through clients as in awful.client.focus.byidx(1) but on all monitors/screens like awful.client.focus.global_bydirection("right") does. Is there a function that would have that behavior so I could use it? If not, do you know how could I achieve that goal?


Solution

  • First, let's look at how awful.client.focus.byidx is implemented.

    The function itself calls awful.client.next and then focused the returned client, if any is found:

    https://github.com/awesomeWM/awesome/blob/9781f14b105ee700c5bca339bf49bb52d2cca4b5/lib/awful/client/focus.lua#L65-L71

    awful.client.next is implemented here: https://github.com/awesomeWM/awesome/blob/9781f14b105ee700c5bca339bf49bb52d2cca4b5/lib/awful/client.lua#L262-L284

    It does the following:

    • line 263: Get the currently focused client
    • line 267: Get all visible clients on the screen of the visible client
    • line 270-274: Filter out unfocusable clients from the list
    • line 277 - 278: Find the focused client in the resulting list
    • line 280: Get the client at the wanted offset and return it

    So, your request sounds like you only need to change one step here: Get the list of all visible clients instead of only those at the currently focused screen. To get that list, you need to call awful.client.visible with nil instead of a specific screen.

    Untested code:

    function next_global(i, sel, stacked)
      sel = sel or client.focus
      if not sel then return end
      local cls = awful.client.visible(nil, stacked)
      local fcls = {}
      for _, c in ipairs(cls) do
        if awful.client.focus.filter(c) or c == sel then
          table.insert(fcls, c)
        end
      end
      cls = fcls
      for idx, c in ipairs(cls) do
        if c == sel then
          return cls[gears.math.cycle(#cls, idx + i)]
        end
      end
    end
    function focus_byidx_global(i, c)
      local target = next_global(i, c)
      if target then
        target:emit_signal("request::activate", "client.focus.byidx", {raise=true})
      end
    end
    

    I don't know which version of AwesomeWM you are using. If in doubt, look at the installed files in /usr/share/awesome/lib/ and copy the code from there, then change it.

    (A little more ugly solution would be something like the following, even though it requires less code; I leave it as an exercise to the reader to figure out what this does, but note that this "breaks stuff" in case a Lua error occurs and the monkey-patching becomes permanent)

    function focus_byidx_global(i, c)
      local old = awful.client.visible
      awful.client.visible = function(_, s) return old(nil, s) end
      awful.client.focus.byidx(i, c)
      awful.client.visible = old
    end