Project

General

Profile

unexist.dev

/

subtle

Assorted tidbits and projects

Snippets

This page shows small snippets that might be useful. Without further instructions they just need to be copied into the config of subtle.

Alt-Tab

This cycles through windows of view

grab "A-Tab" do
  clients = Subtlext::Client.visible

  clients.last.instance_eval do
    focus
    raise
  end
end

grab "A-S-Tab" do 
clients = Subtlext::Client.visible

  clients.first.instance_eval do                                                 
    lower                                                                        
  end
  clients.first.instance_eval do
    focus
  end                                                                            
end

Extend view

require "subtle/subtlext" 

STORE ||= {}

module Subtlext
  class View
    def method_missing(meth, *args)
      STORE[self.name] = { } unless(STORE.has_key?(self.name))

      if meth.to_s.end_with?("=")
        meth = meth.to_s.chop.to_sym

        STORE[self.name][meth] = args[0]
      else
        STORE[self.name][meth]
      end
    end
  end
end

Finally make some use of this like following hook:

on :view_jump do |v|
  v.visits += 1 rescue v.visits = 1
  puts "View %s, %d visits" % [ v.name, v.visits ]
end

Focus gravities

Focus window a specific gravities on view.

{
  "KP_7" => :top_left,    "KP_8" => :top,    "KP_9" => :top_right,
  "KP_4" => :left,        "KP_5" => :center, "KP_6" => :right,
  "KP_1" => :bottom_left, "KP_2" => :bottom, "KP_3" => :bottom_right
}.each do |k, v|
  grab "A-C-" + k, lambda {
    c = Subtlext::View.current.clients.select { |c|
      c.gravity.name.to_sym == v
    }

    c.first.focus unless(c.empty?)
  }
end

Move windows

This snippet adds nine grabs to move windows on the fly to nine defined views. It uses tagging for this, creates tags based on the view names and applies them when needed.

on :start do
  # Create missing tags
  views = Subtlext::View.all.map { |v| v.name }
  tags  = Subtlext::Tag.all.map { |t| t.name }

  views.each do |v|
    unless tags.include?(v)
      t = Subtlext::Tag.new(v)
      t.save
    end
  end
end

# Add nine C-< number> grabs
(1..9).each do |i|
 grab "C-%d" % [ i ] do |c|
   views = Subtlext::View.all
   names = views.map { |v| v.name }

   # Sanity check
   if i <= views.size
     # Tag client
     tags = c.tags.reject { |t| names.include?(t.name) or "default" == t.name }
     tags << names[i - 1]

     c.tags = tags

     # Tag view
     views[i - 1].tag(names[i - 1])
   end
 end
end

Current view

This snippet works similar to the previous, it adds tags based on the view names. When there is an untagged window (a window with the default tag only) it adds the name of the current view as tag, which effectively moves the window to the current view.

on :start do
  # Create missing tags
  views = Subtlext::View.all.map { |v| v.name }
  tags  = Subtlext::Tag.all.map { |t| t.name }

  views.each do |v|
    unless tags.include?(v)
      t = Subtlext::Tag.new(v)
      t.save
    end
  end
end

# Assign tags to clients
on :client_create do |c|
  view = Subtlext::View.current
  tags = c.tags.map { |t| t.name }

  # Add tag to view
  view.tag(view.name) unless(view.tags.include?(view.name))

  # Exclusive for clients with default tag only
  if tags.include?("default") and 1 == tags.size
    c.tags = [ view.name ]
  end
end

Scratchpad

The scratchpad snippet is just a small hack of the tagging. Normally subtle doesn't allow to create a window without tags, so that it's never visible. This grab just creates a urxvt, strips all tags and sets sticky. On the next press it just toggles sticky and blends the window in and out, like a scratchpad.

grab "A-b" do
  if (c = Subtlext::Client.first("scratch"))
    c.toggle_stick
    c.focus
  elsif (c = Subtlext::Client.spawn("urxvt -name scratch"))
    c.tags  = [] 
    c.flags = [ :stick ]
  end
end

Scratchstack

Like the Scratchpad, this snippet can show and hide a client, but instead just one specific client, it can cycle through multiple running ones. There are basically three grabs:

  1. Win-Keypad+ Adds a client to the stack and initially hides it
  2. Win-Keypad- Removes a client fromt he stack and retags it
  3. Win-comma Cycles through the clients on the stack
scratch_stack   = []
scratch_current = 0

# Add window to stack
grab modkey + "-KP_Add" do |c|
  unless scratch_stack.include?(c.win)
    scratch_stack << c.win
    c.tags = []
    c.toggle_stick if c.is_stick?
  end
end

# Remove window from stack
grab modkey + "-KP_Subtract" do |c|
  if scratch_stack.include?(c.win)
    c.retag
    scratch_stack.delete(c.win)
  end
end

# Cycle through stack windows
grab modkey + "-comma" do
  # Get id of next window
  if 0 < scratch_current
    cur_idx = scratch_stack.index(scratch_current)

    # Hide current window
    cur_client = Subtlext::Client[scratch_current]
    cur_client.toggle_stick

    # Check whether cur is last window of stack
    if cur_idx == scratch_stack.size - 1
      scratch_current = 0

      return
    end

    idx = cur_idx + 1
  else
    idx = 0
  end

  # Show next window
  cur = Subtlext::Client[scratch_stack[idx]]

  scratch_current = cur.win
  cur.toggle_stick
end

Check config

In case you keep forgetting to run subtle -k after changing the config this might be handy for you. It checks the config, displays a message via xmessage or just reloads the config.

# Make xmessage stick and urgent
tag "xmessage" do
  match  "xmessage" 
  float  true
  stick  true
  urgent true
end

# The actual grab
grab "A-C-r", <<SCRIPT
subtle -k &>/dev/null
reload=$?

if [ $reload -eq 1 ] ; then
  xmessage 'Syntax error, reload anyway?' -center -buttons NO:1,YES:0
  reload=$?
fi

[ $reload -eq 0 ] && subtler -r
SCRIPT

Switch view

Allow you to go to the next non-empty view. very useful when you don't want :ViewNext to go into hidden views (Mostly with dynamic views)

def goto_next_view(vArr)
    cindx = vArr.index(Subtlext::View.current);

    #Find the next view beyond all existing
    for i in 1..vArr.size do
        cV = vArr[(i + cindx) % vArr.size];

        # Verify that the potential next view isn't displayed on another screens
        if (Subtlext::View.visible.index(cV) == nil) then
            containsClients = false;
            # Check if the view has clients and if those clients are not only sticky one.
            cV.clients.each {|c|
                containsClients = !(cV.tags & c.tags).empty?;
                if(containsClients)
                    break;
                end
            }

            if (containsClients) then
                cV.jump;
                break;
            end;
        end
    end
end

grab "W-Right" do
    goto_next_view(Subtlext::View[:all]);
end

grab "W-Left" do
    goto_next_view(Subtlext::View[:all].reverse);
end