daily pastebin goal
27%
SHARE
TWEET

Subtle Config

Pauan May 31st, 2012 21 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. # coding: utf-8
  2. #-----------------------------------------------------------------------------
  3. #  New API
  4. #-----------------------------------------------------------------------------
  5. ## Clients get extended with this module, because apparently I can't change
  6. ## the Subtlext::Client class
  7. module ClientWrapper
  8.   def self.make_setter *args
  9.     args.each do |arg|
  10.       class_eval "def #{arg} x
  11.                    self.#{arg} = x
  12.                  end"
  13.     end
  14.   end
  15.  
  16.   def self.make_toggle_setter *args
  17.     args.each do |arg|
  18.       class_eval "def #{arg} x
  19.                    toggle_#{arg} if x != is_#{arg}?
  20.                  end"
  21.     end
  22.   end
  23.  
  24.   ## Subtle provides things like `c.gravity = :foo` but that won't work if
  25.   ## the client is instance_eval'd into a block, so this provides versions
  26.   ## that do the assignment so you can say things like `gravity :foo`
  27.   make_setter :flags, :geometry, :gravity, :tags
  28.  
  29.   ## Subtle only provides things like `is_float?` and `toggle_float`, so this
  30.   ## creates some nice wrappers so you can say things like `c.float true`
  31.   ## rather than `c.toggle_float unless c.is_float?`
  32.   make_toggle_setter :borderless, :fixed, :float,  :full,
  33.                      :resize,     :stick, :urgent, :zaphod
  34.  
  35.   ## Needed so assigning the gravity sets it on all the client's views rather
  36.   ## than only the current one
  37.   def gravity= x
  38.     if Symbol === x
  39.       views.each { |v| super v => x }
  40.     else
  41.       super
  42.     end
  43.   end
  44.  
  45.   ## Returns true if the client would be matched by the argument `x`
  46.   ##
  47.   ## It uses the same matching rules as `match foo` inside a tag, with the
  48.   ## sole exception that strings are exact matches rather than treated as
  49.   ## regexps
  50.   def matches? x
  51.     case x
  52.       when String
  53.         x == instance or x == klass
  54.       when Regexp
  55.         x.match(instance) or x.match(klass)
  56.       when Hash
  57.         x.all? do |k, v|
  58.           k = (k == :class) ? klass
  59.                             : send(k)
  60.           (String === v) ? v == k
  61.                          : v.match(k)
  62.         end
  63.       else
  64.         raise TypeError
  65.     end
  66.   end
  67. end
  68.  
  69.  
  70. # TODO: better way to access the old stuff...
  71. $self = self
  72. alias old_view view
  73. alias old_tag  tag
  74.  
  75. class Tag
  76.   def initialize name, &f
  77.     @clients  = []
  78.     @on_match = []
  79.  
  80.     ## Special DSL sugar that lets you pass in a block with no arguments
  81.     if block_given?
  82.       if f.arity == 0
  83.         instance_eval &f
  84.       else
  85.         f.call self
  86.       end
  87.     end
  88.  
  89.     # TODO: very hacky
  90.     unless Symbol === name
  91.       ## Needed because `$self.old_tag` evaluates the block's body in a
  92.       ## different environment
  93.       _clients  = @clients
  94.       _gravity  = @gravity
  95.       _on_match = @on_match
  96.  
  97.       $self.old_tag name do
  98.         _clients.each { |x| match x }
  99.         gravity _gravity if _gravity
  100.         on_match do |c|
  101.           ## Makes the methods in `ClientWrapper` available to the client
  102.           # TODO: potentially slow, maybe I can cache it somehow?
  103.           c.extend ClientWrapper
  104.           _on_match.each { |x| x.call c }
  105.         end
  106.       end
  107.     end
  108.   end
  109.  
  110.   def on x, &f
  111.     if x == :match
  112.                    ## DSL sugar
  113.       @on_match << (f.arity == 0 ? lambda { |c| c.instance_eval &f } : f)
  114.     else
  115.       raise NoMethodError, String(x)
  116.     end
  117.   end
  118.  
  119.   ## Sets the gravity of either the entire view, or an individual client
  120.   ## within the view
  121.   def gravity g, x=nil
  122.     if x
  123.       @clients  << x
  124.       @on_match << lambda { |c| c.gravity = g if c.matches? x }
  125.     else
  126.       @gravity = g
  127.     end
  128.   end
  129.  
  130.   ## Matches a client and optionally calls a block, passing it the client
  131.   def client x, &f
  132.     @clients << x
  133.     if block_given?
  134.                    ## DSL sugar
  135.       @on_match << (f.arity == 0 ? lambda { |c| c.instance_eval &f if c.matches? x }
  136.                                  : lambda { |c| yield c if c.matches? x })
  137.     end
  138.   end
  139. end
  140.  
  141. class View < Tag
  142.   def self.attr_maker x
  143.     x.each do |k, v|
  144.       class_eval "def #{k} x
  145.                    raise TypeError if Symbol === @name
  146.                    @#{k} #{v} x
  147.                  end"
  148.     end
  149.   end
  150.  
  151.   attr_maker icon: "<<", tag: "<<", dynamic: "=", icon_only: "="
  152.  
  153.   def initialize name, &f
  154.     @name = name
  155.     @icon = []
  156.     @tag  = [name]
  157.  
  158.     ## Calls the Tag class's initialize
  159.     super
  160.  
  161.     # TODO: very hacky
  162.     if Symbol === name
  163.       if name == :current
  164. =begin
  165.         super "default" do
  166.           f.call self
  167.           on :match do
  168.             self.tags = [ Subtlext::View.current.name ]
  169.           end
  170.         end
  171.         $self.old_tag "default" do
  172.           on_match
  173.         end
  174. =end
  175.         $self.on :client_create do |c|
  176.           ## Checks to see if the client is tagged with "default" only
  177.           if c.tags.size == 1 and c.tags[0].name == "default"
  178.             # TODO: code duplication
  179.             # TODO: ew, having to extend it just for the sake of matches? ...
  180.             c.extend ClientWrapper
  181.             ## Checks to see if the client matches any of the rules
  182.             if @clients.any? { |x| c.matches? x }
  183.               c.tags = [ Subtlext::View.current.name ]
  184.               c.gravity = @gravity if @gravity
  185.               # TODO: code duplication
  186.               @on_match.each { |x| x.call c }
  187.             end
  188.           end
  189.         end
  190.       else
  191.         raise NoMethodError, String(name)
  192.       end
  193.     else
  194.                    ## This is needed because views can't have more than one
  195.                    ## `match` method, so we have to union them together into
  196.                    ## a single `match`
  197.       _tag       = Regexp.union @tag
  198.       _icon      = @icon
  199.       _dynamic   = @dynamic
  200.       _icon_only = @icon_only
  201.  
  202.       $self.old_view name do
  203.         match _tag
  204.         _icon.each { |x| icon x }
  205.         dynamic   true if _dynamic
  206.         icon_only true if _icon_only
  207.       end
  208.     end
  209.   end
  210. end
  211.  
  212. ## These two are just to make it a bit shorter and cleaner looking
  213. def view name, &f
  214.   View.new(name, &f)
  215. end
  216.  
  217. def tag name, &f
  218.   Tag.new(name, &f)
  219. end
  220.  
  221.  
  222. #-----------------------------------------------------------------------------
  223. #  Options
  224. #-----------------------------------------------------------------------------
  225. ## Window screen border snapping
  226. set :snap, 10
  227.  
  228. ## Default starting gravity for windows. Comment out to use gravity of
  229. ## currently active client
  230. set :gravity, :g100_g
  231.  
  232. ## Make transient windows urgent
  233. set :urgent, false
  234.  
  235. ## Honor resize size hints globally
  236. set :resize, false
  237.  
  238. #-----------------------------------------------------------------------------
  239. #  Screens
  240. #-----------------------------------------------------------------------------
  241. screen 1 do
  242.   top    [ :title  , :keychain  , :spacer  ,
  243.            :center , :views     , :center  ,# :layout ,
  244.            :tray   , :separator , :sublets , :clock ]
  245.   bottom [ ]
  246. end
  247.  
  248.  
  249. #-----------------------------------------------------------------------------
  250. #  Styles
  251. #-----------------------------------------------------------------------------
  252. style :all do
  253.   # Font string either take from e.g. xfontsel or use xft
  254.   font            "-*-*-medium-*-*-*-14-*-*-*-*-*-*-*"
  255.   padding_bottom  1
  256. end
  257.  
  258. style :views do
  259.   foreground     "#656565"
  260.   padding_left   5
  261.   padding_right  5
  262.  
  263.   style :focus do
  264.     foreground  "#fecf35"
  265.   end
  266.  
  267.   style :urgent do
  268.     foreground  "#ff9800"
  269.   end
  270.  
  271.   # Style for occupied views (views with clients)
  272.   style :occupied do
  273.     foreground  "#b8b8b8"
  274.   end
  275. end
  276.  
  277. style :sublets do
  278.   foreground  "#bbbbbb"
  279.  
  280.   style :clock do
  281.     icon           "#000000"
  282.     padding_right  10
  283.     padding_left   0
  284.   end
  285. end
  286.  
  287. style :separator do
  288.   separator   "ǁ"
  289.   foreground  "#222222"
  290. end
  291.  
  292. style :title do
  293.   foreground    "#fecf35"
  294.   padding_left  10
  295. end
  296.  
  297. style :clients do
  298.   active    "#00ff00", 1
  299.   inactive  "#000000", 1
  300.   width     90
  301. end
  302.  
  303. # TODO: will be obsolete eventually
  304. style :subtle do
  305.   panel  "#000000"
  306. end
  307.  
  308.  
  309. #-----------------------------------------------------------------------------
  310. #  Grabs
  311. #-----------------------------------------------------------------------------
  312. #def view_find_visible
  313. #  # get the next view from the iterator
  314. #  view = yield Subtlext::View.current
  315. #  # mostly equivalent to `loop do`
  316. #  while true
  317. #    # view has visible clients or is nil, so we can return it
  318. #    if not view.clients.empty? or !view
  319. #      return view
  320. #    else
  321. #      # get the next view from the iterator
  322. #      view = yield view
  323. #    end
  324. #  end
  325. #end
  326.  
  327. def view_find_visible
  328.   views = Subtlext::View.all
  329.   index = yield views.index Subtlext::View.current
  330.   while true
  331.     view = views[index % views.size]
  332.     # if view has clients, we can return it
  333.     unless view.clients.empty?
  334.       return view
  335.     else
  336.       index = yield index
  337.     end
  338.   end
  339. end
  340.  
  341. grab "A-Tab" do
  342.   view_find_visible { |x| x + 1 }.jump
  343.   #view_find_visible { |x| x.next or Subtlext::View[0] }.jump
  344. end
  345.  
  346. grab "A-S-Tab" do
  347.   view_find_visible { |x| x - 1 }.jump
  348.   #view_find_visible { |x| x.prev or Subtlext::View[-1] }.jump
  349. end
  350.  
  351. # Force reload of config and sublets
  352. grab "M-C-r",   :SubtleReload
  353. grab "M-C-S-r", :SubtleRestart
  354.  
  355. grab "M-B1", :WindowMove
  356. grab "M-B3", :WindowResize
  357. grab "M-f",  :WindowFloat
  358.  
  359. grab "M-r", :WindowRaise
  360. grab "M-l", :WindowLower
  361.  
  362. # Select windows
  363. grab "M-Left",  :WindowLeft
  364. grab "M-Down",  :WindowDown
  365. grab "M-Up",    :WindowUp
  366. grab "M-Right", :WindowRight
  367.  
  368. grab "M-c", :WindowKill
  369.  
  370. grab "M-Return", "xfce4-terminal" #"urxvt"
  371.  
  372. grab "Print",   "xfce4-screenshooter -s Screenshots -w -m"
  373. grab "M-Print", "xfce4-screenshooter -s Screenshots -f"
  374.  
  375. grab "M-v", "volume"
  376.  
  377. # Assign gravities
  378. grab "M-KP_7", [ :g50_ul ]
  379. grab "M-KP_9", [ :g50_ur ]
  380. grab "M-KP_5", [ :g100_g ]
  381. grab "M-KP_1", [ :g50_ll ]
  382. grab "M-KP_3", [ :g50_lr ]
  383.  
  384. # M 1-9 to send a window to that view
  385. (1..9).each do |i|
  386.   grab "M-#{i}" do |c|
  387.     views = Subtlext::View.all
  388.     grav  = c.gravity
  389.     if i <= views.size
  390.       view      = views[i - 1]
  391.       c.tags    = [ view.name ]
  392.       c.gravity = { view => grav }
  393.     end
  394.   end
  395. end
  396.  
  397.  
  398. #-----------------------------------------------------------------------------
  399. #  Gravities
  400. #-----------------------------------------------------------------------------
  401. ## Grid15
  402. gravity :g15_l,     [   0,   0,  15, 100 ], :vert
  403. gravity :g15_r,     [  15,   0,  85, 100 ], :vert
  404. gravity :g15_ul,    [   0,   0,  15,  50 ]
  405. gravity :g15_ur,    [  15,   0,  85,  50 ]
  406. gravity :g15_ll,    [   0,  50,  15,  50 ]
  407. gravity :g15_lr,    [  15,  50,  85,  50 ]
  408.  
  409. ## Grid50
  410. gravity :g50_c,     [  25,  25,  50,  50 ]
  411. gravity :g50_ul,    [   0,   0,  50,  50 ]
  412. gravity :g50_ur,    [  50,   0,  50,  50 ]
  413. gravity :g50_ll,    [   0,  50,  50,  50 ]
  414. gravity :g50_lr,    [  50,  50,  50,  50 ]
  415.  
  416. ## Grid60
  417. gravity :g60_l,     [   0,   0,  60, 100 ], :vert
  418. gravity :g60_r,     [  60,   0,  40, 100 ], :vert
  419.  
  420. ## Grid100
  421. gravity :g100_g,    [   0,   0, 100, 100 ], :horz #:grid
  422. gravity :g100_c,    [   0,   0, 100, 100 ]
  423.  
  424. ## Chrome
  425. gravity :chrome_l,  [   0,   0,  17, 100 ]
  426. gravity :chrome_r,  [  17,   0,  83, 100 ]
  427.  
  428.  
  429. #-----------------------------------------------------------------------------
  430. #  Views
  431. #-----------------------------------------------------------------------------
  432. =begin
  433. move_to_current = lambda do |c|
  434.   c.on :match do
  435.     self.tags = [ Subtlext::View.current.name ]
  436.   end
  437. end
  438.  
  439. client "Steam.exe", &move_to_current
  440. client "xfrun4" do
  441.   move_to_current self
  442.   gravity :g50_c
  443.   float true
  444. end
  445. =end
  446.  
  447. view :current do
  448.   client "Steam.exe"
  449.   client "xfrun4" do
  450.     gravity :g50_c
  451.     float true
  452.   end
  453. end
  454.  
  455. view "stat" do
  456.   gravity :g50_ul, "xfce4-taskmanager"
  457.   gravity :g50_ll, "transmission-gtk"
  458.   gravity :g50_lr, "pavucontrol"
  459. end
  460.  
  461. view "www" do
  462.   client "Firefox"
  463.   client class: "Firefox", role: "browser" do
  464.     borderless true
  465.   end
  466. end
  467.  
  468. view "text" do
  469.   gravity :g60_l, "gedit"
  470.   gravity :g60_r, "xfce4-terminal"
  471. end
  472.  
  473. view "chat" do
  474.   client "Pidgin"
  475.   gravity :g15_ul, instance: "Pidgin", role: "buddy_list"
  476.   gravity :g15_ur, instance: "Pidgin", role: "conversation"
  477.   gravity :g15_ll, instance: "Steam.exe", name: "Friends"
  478.   gravity :g15_lr, instance: "Steam.exe", name: /^.+\ -\ Chat$/
  479. end
  480.  
  481. view "5" do
  482.   tag "default"
  483.   client "hl2.exe" do
  484.     fixed true
  485.   end
  486.   client instance: "phoenix", name: /^bsnes\ v\d+$/ do
  487.     geometry [ 508, 195, 584, 510 ]
  488.     fixed true
  489.   end
  490. end
  491.  
  492.  
  493. #-----------------------------------------------------------------------------
  494. #  Dynamic Views
  495. #-----------------------------------------------------------------------------
  496. view "gimp" do
  497.   dynamic true
  498.   client /^gimp-\d\.\d$/
  499.   gravity :g15_l, instance: /^gimp-\d\.\d$/, role: "gimp-toolbox"
  500.   gravity :g15_r, instance: /^gimp-\d\.\d$/, role: "gimp-image-window"
  501. end
  502.  
  503. view "files" do
  504.   dynamic true
  505.   client "Thunar"
  506. end
  507.  
  508. view "chrome" do
  509.   dynamic true
  510.   client class: "Google-chrome" do
  511.     borderless true
  512.   end
  513.   gravity :chrome_l, instance: /^crx_[a-z]{32}$/, class: "Google-chrome"
  514.   gravity :chrome_r, instance: "google-chrome"
  515. end
  516.  
  517.  
  518. #-----------------------------------------------------------------------------
  519. #  Sublets
  520. #-----------------------------------------------------------------------------
  521. sublet :clock do
  522.   interval 1
  523.   format_string "%a %m.%d - %H:%M"
  524. end
  525.  
  526.  
  527. #-----------------------------------------------------------------------------
  528. #  Events
  529. #-----------------------------------------------------------------------------
  530. on :client_create do |c|
  531.   #puts "CREATE"
  532.   c.focus
  533.   #puts c.name
  534.   #puts c.gravity
  535.   #puts c.gravity.name
  536.   #c.views.first.jump
  537.   #client_gravity_grid c
  538.   #puts "END CREATE"
  539. end
  540.  
  541. #on :tile do |g|
  542. #  puts "TILE"
  543. #  puts g
  544. #  #gravs = Subtlext::Gravity[:all].reject { |x| not x.tiling == :grid }
  545. #  #puts gravs
  546. #  puts "END TILE"
  547. #end
  548.  
  549. on :client_create do |c|
  550.   p Subtlext::View[:all]
  551.   puts
  552.   p Subtlext::View[3]
  553.   p Subtlext::View[0]
  554.   puts
  555.   p Subtlext::View[3].prev.prev.prev.next
  556.   p Subtlext::View[0].next
  557. end
  558.  
  559. on :client_create do |c|
  560.   p Subtlext::View[1] == Subtlext::View[0].next
  561.   p Subtlext::View[0] == Subtlext::View[1].prev
  562. end
RAW Paste Data
Top