wmiirc-rumai

git clone git://oldgit.suckless.org/wmiirc-rumai/
Log | Files | Refs | README | LICENSE

commit 75fb28c290e3d318a028e0c01545995eeb82747f
parent 6fdafbe03c6cdc15f8f4cd1f70b2003f846f5df4
Author: Suraj N. Kurapati <sunaku@gmail.com>
Date:   Sun, 25 Feb 2007 16:13:56 -0800

move bare wmii-ruby interface into wmii-irb project

Diffstat:
.hgignore | 2--
fs.rb | 130-------------------------------------------------------------------------------
rc.rb | 191-------------------------------------------------------------------------------
wm.rb | 579-------------------------------------------------------------------------------
wmiish | 72------------------------------------------------------------------------
5 files changed, 0 insertions(+), 974 deletions(-)

diff --git a/.hgignore b/.hgignore @@ -1,2 +0,0 @@ -(^|/)\.svn($|/) -(^|/)\.hg($|/) diff --git a/fs.rb b/fs.rb @@ -1,130 +0,0 @@ -# Abstractions for wmii's {IXP file system}[http://wmii.de/contrib/guide/wmii-3/guide-en/guide_en/node9.html] interface. -=begin - Copyright 2006 Suraj N. Kurapati - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -=end - -$:.unshift File.join(File.dirname(__FILE__), 'ruby-ixp', 'lib') -require 'ixp' - -# Encapsulates access to the IXP file system. -module Ixp - Client = IXP::Client.new - - # An entry in the IXP file system. - class Node - attr_reader :path - - # Obtains the IXP node at the given path. Unless it already exists, the given path is created when aCreateIt is asserted. - def initialize aPath, aCreateIt = false - @path = aPath.to_s.squeeze('/') - create! if aCreateIt && !exist? - end - - # Open this node for IO operation. - def open *aArgs, &aBlock # :yields: IO - Client.open @path, *aArgs, &aBlock - end - - # Creates this node. - def create! - Client.create @path - end - - # Deletes this node. - def remove! - Client.remove @path - end - - # Writes the given content to this node. - def write! aContent - Client.write @path, aContent - end - - # Returns the contents of this node or the names of all entries if this is a directory. - def read - cont = Client.read(@path) - - if cont.respond_to? :to_ary - cont.map {|stat| stat.name} - else - cont - end - end - - # Tests if this node is a file. - def file? - Client.file? @path - end - - # Tests if this node is a directory. - def directory? - Client.directory? @path - end - - # Tests if this node exists in the file system. - def exist? - Client.exist? @path - end - - # Returns the basename of this file's path. - def basename - File.basename @path - end - - # Returns the dirname of this file's path. - def dirname - File.dirname @path - end - - # Accesses the given sub-path and dereferences it (reads its contents) if specified. - def [] aSubPath, aDeref = false - child = Node.new("#{@path}/#{aSubPath}") - - if aDeref - child.read - else - child - end - end - - # Writes the given content to the given sub-path. - def []= aSubPath, aContent - self[aSubPath].write! aContent - end - - # Provides access to sub-nodes through method calls. - # - # :call-seq: - # node.child = value -> value - # node.child (tree) -> Node - # node.child (leaf) -> child.read - # - def method_missing aMeth, *aArgs - case aMeth.to_s - when /=$/ - self[$`] = *aArgs - - else - if (n = self[aMeth]).file? - n.read - else - n - end - end - end - end -end diff --git a/rc.rb b/rc.rb @@ -1,191 +0,0 @@ -# Utility methods used by wmiirc. -=begin - Copyright 2006 Suraj N. Kurapati - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -=end - -require 'wm' -require 'find' - -# Returns a list of program names available in the given paths. -def find_programs *aPaths - aPaths.flatten! - aPaths.map! {|p| File.expand_path p} - list = [] - - Find.find(*aPaths) do |f| - if File.file?(f) && File.executable?(f) - list << File.basename(f) - end - end - - list.uniq! - list.sort! - list -end - -# Shows a menu with the given items and returns the chosen item. If nothing was chosen, *nil* is returned. -def show_menu *aChoices - IO.popen('wmiimenu', 'r+') do |menu| - menu.puts aChoices - menu.close_write - - if (choice = menu.read).empty? - nil - else - choice - end - end -end - -# Focuses the client chosen from a menu. -def focus_client_from_menu - choices = Wmii.clients.map do |c| - format "%d. [%s] %s", c.index, c.tags, c.name.downcase - end - - if target = show_menu(choices) - Wmii.focus_client target.scan(/\d+/).first - end -end - -# Changes the tag, chosen from a menu, of each selected client and returns the chosen tag. -# 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 = Wmii.tags.map {|t| [t, "+#{t}", "-#{t}"]}.flatten - - if target = show_menu(choices) - Wmii.selected_clients.each do |c| - case target - when /^\+/ - c.tag! $' - - when /^\-/ - c.untag! $' - - else - c.tags = target - end - end - - target - end -end - -# Send selected clients to temporary view or switch back again. -def toggle_temp_view - curTag = Wmii.current_view.name - - if curTag =~ /~\d+$/ - Wmii.selected_clients.each do |c| - c.with_tags do - delete curTag - push $` if empty? - end - end - - Wmii.focus_view $` - - else - tmpTag = "#{curTag}~#{Time.now.to_i}" - - Wmii.selected_clients.each do |c| - c.tag! tmpTag - end - - Wmii.focus_view tmpTag - Wmii.current_view.grid! - end -end - -# Puts focus on an adjacent view (:left or :right). -def cycle_view aTarget - tags = Wmii.tags - curTag = Wmii.current_view.name - curIndex = tags.index(curTag) - - newIndex = - case aTarget - when :right - curIndex + 1 - - when :left - curIndex - 1 - - else - return - - end % tags.length - - Wmii.focus_view tags[newIndex] -end - -# Toggles maximization of the currently focused client. -def toggle_maximize - src = Wmii.current_client - srcId = src.index - - src.ctl = 'sendto toggle' - dst = Wmii.current_view[0].sel - - if dst.exist? && dst.index == srcId - dst.geom = '0 0 east south' - end -end - -# Focuses the view whose name matches the given pattern. If more than one view matches, then they are cycled (adapted from Fredrik Ternerot). -def focus_view_matching aPattern - choices = Wmii.tags.grep(aPattern) - - unless choices.empty? - curIdx = choices.index(Wmii.current_view.name) - maxIdx = choices.length - - idx = curIdx.next % maxIdx rescue 0 - - Wmii.focus_view choices[idx] - end -end - -# Numbers the view buttons displayed on the bar, from left to right. -def number_view_buttons - bar = Wmii.fs.bar - - bar.read.each_with_index do |b, i| - bar[b].data = "#{i + 1}:#{b}" - end -end - - -## wmii-2 style client detaching - -DETACHED_TAG = 'status' - -# Detach the current selection. -def detach_selection - Wmii.selected_clients.each do |c| - c.tags = DETACHED_TAG - end -end - -# Attach the most recently detached client -def attach_last_client - if a = Wmii.get_view(DETACHED_TAG).areas.last - if c = a.clients.last - c.tags = Wmii.current_view.name - end - end -end diff --git a/wm.rb b/wm.rb @@ -1,579 +0,0 @@ -# Abstractions for the window manager. -=begin - Copyright 2006 Suraj N. Kurapati - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -=end - -require 'fs' - -# Encapsulates access to the window manager. -module Wmii - ## state access - - # Returns the root of IXP file system hierarchy. - def Wmii.fs - Ixp::Node.new '/' - end - - # Returns the currently focused client. - def Wmii.current_client - Client.new("/view/sel/sel") - end - - # Returns the currently focused area. - def Wmii.current_area - Area.new("/view/sel") - end - - # Returns the currently focused view. - def Wmii.current_view - View.new("/view") - end - - # Returns the current set of tags. - def Wmii.tags - Ixp::Client.read('/tags').split - end - - # Returns the current set of views. - def Wmii.views - tags.map {|v| get_view v} - end - - # Returns the current set of clients. - def Wmii.clients - Area.new("/client").clients - end - - # Returns the client which has the given ID. - def Wmii.get_client aId - Client.new("/client/#{aId}") - end - - # Returns the view which has the given name. - def Wmii.get_view aName - View.new("/#{aName}") - end - - # Searches for a client, which has the given ID, in the given places. If no places are specified, then all views are searched. If the client is not found, *nil* is returned. - def Wmii.find_client aClientId, *aPlaces - aClientId = aClientId.to_i - needle = Wmii.get_client(aClientId) - - if needle.exist? - haystack = [] - - # populate the haystack (places to be searched) - aPlaces.select {|p| p.exist?}.each do |place| - if place.respond_to? :clients - haystack << place - end - - if place.respond_to? :areas - haystack.concat place.areas - end - end - - if haystack.empty? - needle.tags.map {|t| get_view t}.each do |v| - haystack.concat v.areas - end - end - - haystack.each do |a| - if a.indices.detect {|i| i == aClientId} - return a[aClientId] - end - end - end - - puts "could not find client #{aClientId} in area #{aArea.inspect} or view #{aView.inspect}" if $DEBUG - - nil - end - - - ## state manipulation - - # Focuses the view with the given name. - def Wmii.focus_view aName - get_view(aName).focus! - end - - # Focuses the area with the given ID in the current view. - def Wmii.focus_area aId - Wmii.current_view[aId].focus! - end - - # Focuses the client which has the given ID. - def Wmii.focus_client aId - if c = find_client(aId) - v = (a = c.parent).parent - - v.focus! - a.focus! - c.focus! - end - end - - - ## Multiple client selection - - SELECTION_TAG = 'SEL' - - # 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 Wmii.selected_clients - list = current_view.areas.map do |a| - a.clients.select {|c| c.selected?} - end - list.flatten! - - if list.empty? - list << current_client - end - - list - end - - # Un-selects all selected clients so that there is nothing selected. - def Wmii.select_none! - get_view(SELECTION_TAG).unselect! - end - - - ## subclasses for abstraction - - # A region in the window manager's hierarchy. - class Node < Ixp::Node - def initialize aParentClass, aChildClass, aFocusCommand, *aArgs - @parentClass = aParentClass - @childClass = aChildClass - @focusCmd = aFocusCommand - super(*aArgs) - end - - # Returns a child with the given sub-path. - def [] *args - child = super - - if child.respond_to? :path - child = @childClass.new(child.path) - end - - child - end - - # Returns the parent of this region. - def parent - @parentClass.new File.dirname(@path) - end - - # Returns the index of this region in the parent. - def index - basename.to_i - end - - # Returns the next region in the parent. - def next - parent[self.index + 1] - end - - # Returns a list of indices of items in this region. - def indices - read.grep(/^\d+$/).map {|s| s.to_i} rescue [] - end - - # Returns the number of items in this region. - def length - indices.length - end - - # Returns a list of items in this region. - def children - indices.map {|i| @childClass.new "#{@path}/#{i}"} - end - - # Adds all clients in this region to the selection. - def select! - children.each do |s| - s.select! - end - end - - # Removes all clients in this region from the selection. - def unselect! - children.each do |s| - s.unselect! - end - end - - # Inverts the selection of clients in this region. - def invert_selection! - children.each do |s| - s.invert_selection! - end - end - - # Puts focus on this region. - def focus! - parent.ctl = "#{@focusCmd} #{basename}" - end - end - - class Client < Node - def initialize *aArgs - super Area, Ixp::Node, :select, *aArgs - end - - def index - self[:index, true].to_i - end - - TAG_DELIMITER = "+" - - # Returns the tags associated with this client. - def tags - self[:tags, true].split(TAG_DELIMITER) - end - - # Modifies the tags associated with this client. - def tags= *aTags - t = aTags.flatten.uniq - self[:tags] = t.join(TAG_DELIMITER) unless t.empty? - end - - # Evaluates the given block within the context of this client's list of tags. - def with_tags &aBlock - t = self.tags - t.instance_eval(&aBlock) - self.tags = t - end - - # Adds the given tags to this client. - def tag! *aTags - with_tags do - push(*aTags.flatten.map {|t| t.to_s}) - end - end - - # Removes the given tags from this client. - def untag! *aTags - with_tags do - aTags.flatten.each do |tag| - delete tag.to_s - end - end - end - - # Checks if this client is included in the current selection. - def selected? - tags.include? SELECTION_TAG - end - - def select! - with_tags do - unshift SELECTION_TAG - end - end - - def unselect! - untag! SELECTION_TAG - end - - def invert_selection! - if selected? - unselect! - else - select! - end - end - end - - class Area < Node - def initialize *aArgs - super View, Client, :select, *aArgs - end - - alias clients children - - # Tests if this area is empty (has no clients). - def empty? - length < 1 - end - - # Inserts the given clients at the bottom of this area. - def push! *aClients - if target = clients.last - target.focus! - end - - insert! aClients - end - - # Inserts the given clients after the currently focused client in this area. - def insert! *aClients - aClients.flatten! - return if aClients.empty? - - setup_for_insertion! aClients.shift - - dst = self.index - aClients.each do |c| - c.ctl = "sendto #{dst}" - end - end - - # Inserts the given clients at the top of this area. - def unshift! *aClients - aClients.flatten! - return if aClients.empty? - - if target = clients.first - target.focus! - end - - setup_for_insertion! aClients.shift - - if top = clients.first - top.ctl = 'swap down' - end - - dst = self.index - aClients.each do |c| - c.ctl = "sendto #{dst}" - end - end - - # Concatenates the given area to the bottom of this area. - def concat! aArea - push! aArea.clients - end - - # Ensures that this area has at most the given number of clients. Areas to the right of this one serve as a buffer into which excess clients are evicted and from which deficit clients are imported. - def length= aMaxClients - return if aMaxClients < 0 - len = self.length - - if len > aMaxClients - self.next.unshift! clients[aMaxClients..-1] - - elsif len < aMaxClients - until (diff = aMaxClients - length) == 0 - immigrants = self.next.clients[0...diff] - break if immigrants.empty? - - push! immigrants - end - end - end - - - private - - # Updates the path of this area for proper insertion and inserts the given client. - def setup_for_insertion! aFirstClient - raise ArgumentError, 'nonexistent client' unless aFirstClient.exist? - - dstIdx = self.index - maxIdx = parent.indices.last - - if dstIdx > maxIdx - # move *near* final destination - clientId = aFirstClient.index - aFirstClient.ctl = "sendto #{maxIdx}" - - # recalculate b/c sendto can be destructive - maxIdx = parent.indices.last - maxCol = parent[maxIdx] - - aFirstClient = Wmii.find_client(clientId, maxCol) - - # move *into* final destination - if maxCol.indices.length > 1 - aFirstClient.ctl = "sendto next" - dstIdx = maxIdx + 1 - else - dstIdx = maxIdx - end - - @path = "#{dirname}/#{dstIdx}" - - else - aFirstClient.ctl = "sendto #{dstIdx}" - end - end - end - - class View < Node - def initialize *aArgs - super Ixp::Node, Area, :view, *aArgs - end - - alias areas children - - # Tests if this view is empty (has no clients). - def empty? - length < 3 && - self[0].length < 1 && - self[1].length < 1 - end - - # Iterates over columns in this view such that destructive operations are supported. If specified, the iteration starts with the column which has the given index. - # Note that the floating area is not considered to be a column. - def each_column aStartIdx = 1 # :yields: area - return unless block_given? - - if (i = aStartIdx.to_i) < 1 - i = 1 - end - - until i >= (areaList = self.areas).length - yield areaList[i] - i += 1 - end - end - - # Arranges the clients in this view, while maintaining their relative order, in the tiling fashion of LarsWM. Only the first client in the primary column is kept; all others are evicted to the *top* of the secondary column. Any subsequent columns are squeezed into the *bottom* of the secondary column. - def tile! - priCol, secCol, extCol = self[1], self[2], self[3] - - unless priCol.empty? - # keep only the first client in primary column - priClient, *rest = priCol.clients - secCol.unshift! rest - - # squeeze extra columns into secondary column - if (numAreas = self.indices.length) > 3 - (numAreas - 2).times do - secCol.concat! extCol - end - end - - secCol.mode = :default - #priCol.mode = :max - priClient.focus! - end - end - - # Arranges the clients in this view, while maintaining their relative order, in a (at best) square grid. - def grid! aMaxClientsPerColumn = nil - # determine client distribution - unless aMaxClientsPerColumn - numClients = num_grounded_clients - return unless numClients > 1 - - numColumns = Math.sqrt(numClients) - aMaxClientsPerColumn = (numClients / numColumns).round - end - - # distribute the clients - if aMaxClientsPerColumn <= 0 - # squeeze all clients into a single column - areaList = self.areas - - (areaList.length - 2).times do - areaList[1].concat! areaList[2] - end - - else - each_column do |a| - a.length = aMaxClientsPerColumn - a.mode = :default - end - end - end - - # Arranges the clients in this view, while maintaining their relative order, in a (at best) equilateral triangle. However, the resulting arrangement appears like a diamond because wmii does not waste screen space. - def diamond! - if (numClients = num_grounded_clients) > 0 - subtriArea = numClients / 2 - crestArea = numClients % subtriArea - - # build fist sub-triangle upwards - height = area = 0 - lastCol = nil - - each_column do |col| - if area < subtriArea - height += 1 - - col.length = height - area += height - - col.mode = :default - lastCol = col - else - break - end - end - - # build crest of overall triangle - if crestArea > 0 - lastCol.length = height + crestArea - end - - # build second sub-triangle downwards - each_column(lastCol.index + 1) do |col| - if area > 0 - col.length = height - area -= height - - height -= 1 - else - break - end - end - end - end - - - private - - # Returns the number of clients in the non-floating areas of this view. - def num_grounded_clients - if ground = areas[1..-1] - ground.inject(0) do |count, area| - count + area.length - end - else - 0 - end - end - end -end - -class Array - alias original_each each - - # Supports destructive operations on each client in this array. - def each - if block_given? - original_each do |c| - # resolve stale paths caused by destructive operations - if c.is_a?(Wmii::Client) && !c.exist? - puts "\n trying to resolve nonexistent client: #{c.path}" if $DEBUG - - c = Wmii.find_client(c.basename, Wmii.current_view) - next unless c - - puts "resolution OK: #{c.path}" if $DEBUG - end - - yield c - end - end - end -end diff --git a/wmiish b/wmiish @@ -1,72 +0,0 @@ -#!/usr/bin/ruby -w -# This is an interactive Ruby shell for wmii. -# - Press the TAB key for command completion. - -=begin - Copyright 2004 Joel VanderWerf - Copyright 2006 Suraj N. Kurapati - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -=end - - -## This code was adapted from ruby-talk:99201. - -require 'irb' -require 'irb/completion' - -module IRB - # Starts IRB within the context of the given object. - def IRB.start_session aContextObj - IRB.setup __FILE__ unless defined? $irb - - ws = WorkSpace.new(aContextObj) - - $irb = if @CONF[:SCRIPT] # normally, set by parse_opts - Irb.new ws, @CONF[:SCRIPT] - else - Irb.new ws - end - - @CONF[:IRB_RC].call($irb.context) if @CONF[:IRB_RC] - @CONF[:MAIN_CONTEXT] = $irb.context - - trap 'INT' do - $irb.signal_handle - end - - custom_configuration if defined? IRB.custom_configuration - - catch :IRB_EXIT do - $irb.eval_input - end - end -end - -class Object - include IRB::ExtendCommandBundle # so that Marshal.dump works -end - - -if __FILE__ == $0 - # show usage info - require 'rdoc/usage' - RDoc::usage_no_exit - - $:.unshift File.dirname(__FILE__) - require 'rc' - - IRB.start_session Wmii -end