commit 9df5e88d8563c897f5cb1c66e49f920064f30943
parent 505f5fcc75f8a643c0d44789c807e9fa3b6ad15b
Author: Suraj N. Kurapati <sunaku@gmail.com>
Date: Mon, 11 Sep 2006 09:08:04 -0700
[project @ 49e4b99b23be694bd735ada0a3a10bca6ca8e967]
[project @ 64]
refactor Wmii into a module and its state ops into Wmii::State
mv detaching stuff into rc
mv big logic from wmiirc into rc
up Wmii methods are now included directly in the shell. no more W#...
Diffstat:
rc.rb | | | 121 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- |
wm.rb | | | 197 | ++++++++++++++++++++++++++++++++----------------------------------------------- |
wmiirc | | | 208 | ++++++++++++++++++++++++++++++++++--------------------------------------------- |
wmiish | | | 6 | +----- |
4 files changed, 288 insertions(+), 244 deletions(-)
diff --git a/rc.rb b/rc.rb
@@ -17,6 +17,12 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
=end
+require 'wm'
+
+include Wmii
+include Wmii::State
+
+
require 'find'
# Returns a list of program names available in the given paths.
@@ -34,13 +40,13 @@ def find_programs *aPaths
list.uniq.sort
end
-# Shows a WM menu with the given content and returns its output.
-def show_menu *aContent
- aContent.flatten!
+# Shows a menu with the given items and returns the chosen item. If nothing was chosen, an empty string is returned.
+def show_menu *aChoices
+ aChoices.flatten!
output = nil
IO.popen('wmiimenu', 'r+') do |menu|
- menu.write aContent.join("\n")
+ menu.write aChoices.join("\n")
menu.close_write
output = menu.read
@@ -48,3 +54,110 @@ def show_menu *aContent
output
end
+
+# Focuses the client chosen from a menu.
+def focus_client_from_menu
+ choices = clients.map do |c|
+ format "%d. [%s] %s", c.index, c.tags, c.name.downcase
+ end
+
+ target = show_menu(choices)
+
+ unless target.empty?
+ focus_client target.scan(/\d+/).first
+ end
+end
+
+# Changes the tag, chosen from a menu, of each selected client.
+# The {+tag -tag idea}[http://zimbatm.oree.ch/articles/2006/06/15/wmii-3-and-ruby] is from Jonas Pfenniger.
+def change_tag_from_menu
+ choices = tags.map {|t| [t, "+#{t}", "-#{t}"]}.flatten
+ target = show_menu(choices)
+
+ with_selection do |c|
+ c.with_tags do
+ case target
+ when /^\+/
+ push $'
+
+ when /^\-/
+ delete $'
+
+ else
+ clear
+ push target
+ end
+ end
+ end
+end
+
+# Send selected clients to temporary view or switch back again.
+def toggle_temporary_view
+ curView = focused_view.name
+
+ if curView =~ /~\d+$/
+ with_selection do |c|
+ c.with_tags do
+ delete curView
+ push $` if empty?
+ end
+ end
+
+ focus_view $`
+
+ else
+ tmpView = "#{curView}~#{Time.now.to_i}"
+
+ with_selection do |c|
+ c.with_tags do
+ push tmpView
+ end
+ end
+
+ focus_view tmpView
+ focused_view.grid!
+ end
+end
+
+# Changes the currently focused view to an adjacent one (:left or :right).
+def cycle_view aTarget
+ tags = self.tags
+ curTag = focused_view.name
+ curIndex = tags.index(curTag)
+
+ newIndex =
+ case aTarget
+ when :right
+ curIndex + 1
+
+ when :left
+ curIndex - 1
+
+ else
+ return
+
+ end % tags.length
+
+ focus_view tags[newIndex]
+end
+
+
+## wmii-2 style client detaching
+
+DETACHED_TAG = 'status'
+
+# Detach the current selection.
+def detach_selection
+ selected_clients.each do |c|
+ c.tags = DETACHED_TAG
+ end
+end
+
+# Attach the most recently detached client
+def attach_last_client
+ if a = View.new("/#{DETACHED_TAG}").areas.last
+ if c = a.clients.last
+ c.tags = focused_view.name
+ end
+ end
+end
diff --git a/wm.rb b/wm.rb
@@ -19,69 +19,11 @@
require 'fs'
-# Encapsulates access to the window manager.
-class Wmii < IxpNode
+module Wmii
SELECTION_TAG = 'SEL'
- DETACHED_TAG = 'status'
- def initialize
- super '/'
- end
-
-
- ## access to WM state
-
- # Returns the currently focused client.
- def focused_client
- Client.new("/view/sel/sel")
- end
-
- # Returns the currently focused area.
- def focused_area
- Area.new("/view/sel")
- end
-
- # Returns the currently focused view.
- def focused_view
- View.new("/view")
- end
-
- # Returns the current set of tags.
- def tags
- self[:tags].split
- end
-
- # Returns the current set of views.
- def views
- tags.map {|v| View.new "/#{v}"}
- end
-
- # Returns the current set of clients.
- def clients
- Area.new("/client").clients
- end
-
-
- ## WM state manipulation
-
- # Focuses the view with the given name.
- def focus_view aName
- View.new("/#{aName}").focus!
- end
-
- # Focuses the client which has the given ID.
- def focus_client aClientId
- if c = find_client(aClientId)
- v = (a = c.parent).parent
-
- v.focus!
- a.focus!
- c.focus!
- end
- end
-
- # Returns the client which has the given ID or +nil+ if not found. The search is performed in the given places if specified.
- def Wmii.find_client aClientId, aArea = nil, aView = nil
+ # Returns the client which has the given ID or +nil+ if not found. The search is performed within the given places if they are specified.
+ def find_client aClientId, aArea = nil, aView = nil
aClientId = aClientId.to_i
needle = Client.new("/client/#{aClientId}")
@@ -108,91 +50,112 @@ class Wmii < IxpNode
nil
end
- # Changes the currently focused view to an adjacent one (:left or :right).
- def cycle_view aTarget
- tags = self.tags
- curTag = focused_view.name
- curIndex = tags.index(curTag)
+ # Encapsulates the window manager's state.
+ module State
+ ## state access
- newIndex =
- case aTarget
- when :right
- curIndex + 1
+ # Returns the currently focused client.
+ def focused_client
+ Client.new("/view/sel/sel")
+ end
- when :left
- curIndex - 1
+ # Returns the currently focused area.
+ def focused_area
+ Area.new("/view/sel")
+ end
- else
- return
+ # Returns the currently focused view.
+ def focused_view
+ View.new("/view")
+ end
- end % tags.length
+ # Returns the current set of tags.
+ def tags
+ IxpFs.read('/tags').split
+ end
- focus_view tags[newIndex]
- end
+ # Returns the current set of views.
+ def views
+ tags.map {|v| View.new "/#{v}"}
+ end
+
+ # Returns the current set of clients.
+ def clients
+ Area.new("/client").clients
+ end
- ## Multiple client selection
+ ## state manipulation
- # Returns a list of all selected clients in the currently focused view. If there are no selected clients, then the currently focused client is returned in the list.
- def selected_clients
- list = focused_view.areas.map do |a|
- a.clients.select {|c| c.selected?}
+ # Focuses the view with the given name.
+ def focus_view aName
+ View.new("/#{aName}").focus!
end
- list.flatten!
- if list.empty?
- list << focused_client
+ # Focuses the client which has the given ID.
+ def focus_client aClientId
+ if c = find_client(aClientId)
+ v = (a = c.parent).parent
+
+ v.focus!
+ a.focus!
+ c.focus!
+ end
end
- list
- end
- # Un-selects all selected clients.
- def select_none
- View.new("/#{SELECTION_TAG}").unselect!
- end
+ ## Multiple client selection
- # Invokes the given block for each #selected_clients in a way that supports destructive operations, which change the number of areas in a view.
- def with_selection # :yields: client
- return unless block_given?
+ # Returns a list of all selected clients in the currently focused view. If there are no selected clients, then the currently focused client is returned in the list.
+ def selected_clients
+ list = focused_view.areas.map do |a|
+ a.clients.select {|c| c.selected?}
+ end
+ list.flatten!
- curView = focused_view
+ if list.empty?
+ list << focused_client
+ end
- selected_clients.each do |c|
- # resolve stale paths caused by destructive operations
- unless c.exist?
- c = find_client(c.basename, nil, curView)
- c || next # skip upon failure
- end
+ list
+ end
- yield c
+ # Un-selects all selected clients so that there is nothing selected.
+ def select_none!
+ View.new("/#{SELECTION_TAG}").unselect!
end
- end
+ # Invokes the given block for each #selected_clients in a way that supports destructive operations, which change the number of areas in a view.
+ def with_selection # :yields: client
+ return unless block_given?
- ## wmii-2 style client detaching
+ curView = focused_view
- # Detach the current selection.
- def detach_selection
- selected_clients.each do |c|
- c.tags = DETACHED_TAG
- end
- end
+ selected_clients.each do |c|
+ # resolve stale paths caused by destructive operations
+ unless c.exist?
+ c = find_client(c.basename, nil, curView)
+ c || next # skip upon failure
+ end
- # Attach the most recently detached client
- def attach_last_client
- if a = View.new("/#{DETACHED_TAG}").areas.last
- if c = a.clients.last
- c.tags = focused_view.name
+ yield c
end
end
end
+ # Represents the window manager at root of the file system.
+ class Root < IxpNode
+ include State
- ## sub classes
+ def initialize
+ super '/'
+ end
+ end
# Encapsulates a graphical region in the window manager.
class Container < IxpNode
+ include Wmii
+
def initialize aParentClass, aChildClass, *aArgs
@parentClass = aParentClass
@childClass = aChildClass
@@ -381,7 +344,7 @@ class Wmii < IxpNode
maxIdx = parent.indices.last
maxCol = parent[maxIdx]
- aFirstClient = Wmii.find_client(aFirstClient.index, maxCol)
+ aFirstClient = find_client(aFirstClient.index, maxCol)
# move *into* final destination
if maxCol.indices.length > 1
diff --git a/wmiirc b/wmiirc
@@ -19,15 +19,16 @@
=end
$: << File.dirname(__FILE__)
-require 'wm'
require 'rc'
## WM STARTUP
-WM = Wmii.new
+WM = Wmii::Root.new
+
+
PROGRAM_MENU = find_programs( ENV['PATH'].squeeze(':').split(':') )
-ACTION_MENU = find_programs('~/dry/apps/wmii/etc/wmii-3', '~/.wmii-3')
+ACTION_MENU = find_programs('~/dry/apps/wmii/etc/wmii-3', File.dirname(__FILE__))
# terminate existing wmiirc processes
sleep 1 until WM.event = "Start wmiirc\n"
@@ -111,208 +112,177 @@ PROGRAM = ACTION
SHORTCUTS = {
# focus previous view
"#{FOCUS}comma" => lambda do
- WM.cycle_view :left
+ cycle_view :left
end,
# focus next view
"#{FOCUS}period" => lambda do
- WM.cycle_view :right
+ cycle_view :right
end,
# focus previous area
"#{FOCUS}#{LEFT}" => lambda do
- WM.focused_view.ctl = 'select prev'
+ focused_view.ctl = 'select prev'
end,
# focus next area
"#{FOCUS}#{RIGHT}" => lambda do
- WM.focused_view.ctl = 'select next'
+ focused_view.ctl = 'select next'
end,
# focus floating area
"#{FOCUS}space" => lambda do
- WM.focused_view.ctl = 'select toggle'
+ focused_view.ctl = 'select toggle'
end,
# focus previous client
"#{FOCUS}#{UP}" => lambda do
- WM.focused_area.ctl = 'select prev'
+ focused_area.ctl = 'select prev'
end,
# focus next client
"#{FOCUS}#{DOWN}" => lambda do
- WM.focused_area.ctl = 'select next'
+ focused_area.ctl = 'select next'
end,
+ # apply equal spacing layout to currently focused column
"#{LAYOUT}w" => lambda do
- WM.focused_area.mode = 'default'
+ focused_area.mode = 'default'
end,
+ # apply stacked layout to currently focused column
"#{LAYOUT}v" => lambda do
- WM.focused_area.mode = 'stack'
+ focused_area.mode = 'stack'
end,
+ # apply maximized layout to currently focused column
"#{LAYOUT}m" => lambda do
- WM.focused_area.mode = 'max'
+ focused_area.mode = 'max'
end,
+ # maximize the floating area's focused client
"#{LAYOUT}z" => lambda do
- WM.focused_view[0].sel.geom = '0 0 east south'
+ focused_view[0].sel.geom = '0 0 east south'
end,
+ # apply tiling layout to the currently focused view
"#{LAYOUT}t" => lambda do
- WM.focused_view.tile!
+ focused_view.tile!
end,
+ # apply gridding layout to the currently focused view
"#{LAYOUT}g" => lambda do
- WM.focused_view.grid!
+ focused_view.grid!
end,
+ # add/remove the currently focused client from the selection
"#{GROUP}g" => lambda do
- WM.focused_client.invert_selection!
+ focused_client.invert_selection!
end,
+ # add all clients in the currently focused view to the selection
"#{GROUP}a" => lambda do
- WM.focused_view.select!
+ focused_view.select!
end,
+ # invert the selection in the currently focused view
"#{GROUP}i" => lambda do
- WM.focused_view.invert_selection!
+ focused_view.invert_selection!
end,
+ # nullify the selection
"#{GROUP}n" => lambda do
- WM.select_none
+ select_none!
end,
+ # launch an internal action by choosing from a menu
"#{MENU}i" => lambda do
action = show_menu(ACTION_MENU)
system(action << '&') unless action.empty?
end,
+ # launch an external program by choosing from a menu
"#{MENU}e" => lambda do
program = show_menu(PROGRAM_MENU)
system(program << '&') unless program.empty?
end,
+ # focus any view by choosing from a menu
"#{MENU}Shift-v" => lambda do
- WM.focus_view(show_menu(WM.tags))
+ focus_view(show_menu(tags))
end,
- # focus any client by choosing from a menu
"#{MENU}a" => lambda do
- choices = WM.clients.map do |c|
- format "%d. [%s] %s", c.index, c.tags, c.name.downcase
- end
+ focus_client_from_menu
+ end,
- target = show_menu(choices)
- unless target.empty?
- WM.focus_client target.scan(/\d+/).first
- end
+ "#{PROGRAM}x" => lambda do
+ system 'terminal &'
end,
+ "#{PROGRAM}k" => lambda do
+ system 'epiphany &'
+ end,
- "#{PROGRAM}x" => lambda do system 'terminal &' end,
- "#{PROGRAM}k" => lambda do system 'epiphany &' end,
- "#{PROGRAM}j" => lambda do system 'nautilus --no-desktop &' end,
+ "#{PROGRAM}j" => lambda do
+ system 'nautilus --no-desktop &'
+ end,
"#{SEND}#{LEFT}" => lambda do
- WM.with_selection do |c|
+ with_selection do |c|
c.ctl = 'sendto prev'
end
end,
"#{SEND}#{RIGHT}" => lambda do
- WM.with_selection do |c|
+ with_selection do |c|
c.ctl = 'sendto next'
end
end,
"#{SEND}space" => lambda do
- WM.with_selection do |c|
+ with_selection do |c|
c.ctl = 'sendto toggle'
end
end,
"#{SEND}Delete" => lambda do
- WM.with_selection do |c|
+ with_selection do |c|
c.ctl = 'kill'
end
end,
- # change the tag of the currently focused client
- # +tag -tag idea from Jonas Pfenniger <http://zimbatm.oree.ch/articles/2006/06/15/wmii-3-and-ruby>
"#{SEND}t" => lambda do
- choices = WM.tags.map {|t| [t, "+#{t}", "-#{t}"]}.flatten
- target = show_menu(choices)
-
- WM.with_selection do |c|
- c.with_tags do
- case target
- when /^\+/
- push $'
-
- when /^\-/
- delete $'
-
- else
- clear
- push target
- end
- end
- end
+ change_tag_from_menu
end,
# remove currently focused view from current selection's tags
"#{SEND}Shift-minus" => lambda do
- WM.with_selection do |c|
+ with_selection do |c|
c.with_tags do
- delete WM.focused_view.name
+ delete focused_view.name
end
end
end,
- # send to temporary view or switch back again
"#{ACTION}b" => lambda do
- curView = WM.focused_view.name
-
- if curView =~ /~\d+$/
- WM.with_selection do |c|
- c.with_tags do
- delete curView
- push $` if empty?
- end
- end
-
- WM.focus_view $`
-
- else
- tmpView = "#{curView}~#{Time.now.to_i}"
-
- WM.with_selection do |c|
- c.with_tags do
- push tmpView
- end
- end
-
- WM.focus_view tmpView
- WM.focused_view.grid!
- end
+ toggle_temporary_view
end,
# wmii-2 style detaching
"#{ACTION}d" => lambda do
- WM.detach_selection
+ detach_selection
end,
# wmii-2 style detaching
"#{ACTION}Shift-d" => lambda do
- WM.attach_last_client
+ attach_last_client
end,
# toggle maximizing the currently focused client to full screen
@@ -323,22 +293,22 @@ SHORTCUTS = {
# swap the currently focused client with the one to its left
"#{SWAP}#{LEFT}" => lambda do
- WM.focused_client.ctl = 'swap prev'
+ focused_client.ctl = 'swap prev'
end,
# swap the currently focused client with the one to its right
"#{SWAP}#{RIGHT}" => lambda do
- WM.focused_client.ctl = 'swap next'
+ focused_client.ctl = 'swap next'
end,
# swap the currently focused client with the one below it
"#{SWAP}#{DOWN}" => lambda do
- WM.focused_client.ctl = 'swap down'
+ focused_client.ctl = 'swap down'
end,
# swap the currently focused client with the one above it
"#{SWAP}#{UP}" => lambda do
- WM.focused_client.ctl = 'swap up'
+ focused_client.ctl = 'swap up'
end,
}
@@ -347,45 +317,45 @@ SHORTCUTS = {
# focus _i_th view
SHORTCUTS["#{FOCUS}#{i}"] = lambda do
- WM.focus_view(WM.tags[k] || i)
+ focus_view(tags[k] || i)
end
# send selection to _i_th view
SHORTCUTS["#{SEND}#{i}"] = lambda do
- WM.with_selection do |c|
- c.tags = (WM.tags[k] || i)
+ with_selection do |c|
+ c.tags = (tags[k] || i)
end
end
# send selection to _i_th area
SHORTCUTS["#{SEND}Shift-#{i}"] = lambda do
- dst = WM.focused_view[i]
+ dst = focused_view[i]
- WM.with_selection do |c|
+ with_selection do |c|
dst.insert! c
end
end
# apply grid layout with _i_ clients per column
SHORTCUTS["#{LAYOUT}#{i}"] = lambda do
- WM.focused_view.grid! i
+ focused_view.grid! i
end
# add _i_th view to current selection's tags
SHORTCUTS["#{SEND}equal,#{i}"] =
SHORTCUTS["#{SEND}Shift-equal,#{i}"] = lambda do
- WM.with_selection do |c|
+ with_selection do |c|
c.with_tags do
- push(WM.tags[k] || i)
+ push(tags[k] || i)
end
end
end
# remove _i_th view from current selection's tags
SHORTCUTS["#{SEND}minus,#{i}"] = lambda do
- WM.with_selection do |c|
+ with_selection do |c|
c.with_tags do
- delete(WM.tags[k] || i)
+ delete(tags[k] || i)
end
end
end
@@ -394,11 +364,11 @@ end
# jump to view whose name begins with the pressed key
('a'..'z').each do |char|
SHORTCUTS["#{MENU}v,#{char}"] = lambda do
- choices = WM.tags
- choices.delete WM.focused_view.name
+ choices = self.tags
+ choices.delete focused_view.name
if view = choices.select {|t| t =~ /^#{char}/i}.first
- WM.focus_view view
+ focus_view view
end
end
end
@@ -407,23 +377,22 @@ WM.def.grabmod = MODKEY
WM.def.keys = SHORTCUTS.keys.join("\n")
-## MINI SCRIPTS
+## STATUS BAR
-# display time and system status in the bar
- Thread.new do
- status = IxpNode.new("/bar/status", true)
- status.colors = ENV['WMII_NORMCOLORS']
+Thread.new do
+ status = IxpNode.new("/bar/status", true)
+ status.colors = ENV['WMII_NORMCOLORS']
- loop do
- upTime = `uptime`.scan(/\d+\.\d+/).join(' ')
- diskUsage = `df -h ~`.split[-3..-1].join(' ')
+ loop do
+ cpuLoad = `uptime`.scan(/\d+\.\d+/).join(' ')
+ diskSpace = `df -h ~`.split[-3..-1].join(' ')
- 5.times do
- status.data = "#{Time.now.to_s} | #{upTime} | #{diskUsage}"
- sleep 1
- end
+ 5.times do
+ status.data = "#{Time.now.to_s} | #{cpuLoad} | #{diskSpace}"
+ sleep 1
end
end
+end
## EVENT LOOP
@@ -442,17 +411,19 @@ begin
case mouseBtn.to_i
when PRIMARY
- WM.focus_view viewId
+ focus_view viewId
when MIDDLE
- WM.with_selection do |c|
+ # add view to selection's tags
+ with_selection do |c|
c.with_tags do
push viewId
end
end
when SECONDARY
- WM.with_selection do |c|
+ # remove view from selection's tags
+ with_selection do |c|
c.with_tags do
delete viewId
end
@@ -464,6 +435,7 @@ begin
case mouseBtn.to_i
when MIDDLE, SECONDARY
+ # add/remove client from selection
Wmii::Client.new("/client/#{clientId}").invert_selection!
end
@@ -473,5 +445,5 @@ begin
end
end
rescue EOFError
- exit # wmiiwm has quit
+ exit 1 # wmiiwm has quit
end
diff --git a/wmiish b/wmiish
@@ -1,6 +1,5 @@
#!/usr/bin/ruby
# This is an interactive Ruby shell for wmii.
-# - Access & manipulate wmii via the *W* constant.
# - Press the TAB key for command completion.
=begin
Copyright 2006 Suraj N. Kurapati
@@ -22,14 +21,11 @@
require 'rdoc/usage'
-
RDoc::usage_no_exit
$:.unshift File.dirname(__FILE__)
-require 'wm'
-
-W = Wmii.new
+require 'rc'
require 'irb'