daily pastebin goal
62%
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
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top