[hackers] [wmii] Add Suraj's Rumai-based wmiirc. || Kris Maglione

From: <hg_AT_suckless.org>
Date: Tue, 29 Sep 2009 21:45:13 +0000 (UTC)

changeset: 2497:e2f59b70918d
tag: tip
user: Kris Maglione <jg_AT_suckless.org>
date: Tue Sep 29 17:44:54 2009 -0400
files: alternative_wmiircs/Makefile alternative_wmiircs/README alternative_wmiircs/ruby/HISTORY alternative_wmiircs/ruby/LICENSE alternative_wmiircs/ruby/README alternative_wmiircs/ruby/config.rb alternative_wmiircs/ruby/config.yaml alternative_wmiircs/ruby/wmiirc
description:
Add Suraj's Rumai-based wmiirc.

diff -r 772dd6ee7286 -r e2f59b70918d alternative_wmiircs/Makefile
--- a/alternative_wmiircs/Makefile Tue Sep 29 15:47:27 2009 -0400
+++ b/alternative_wmiircs/Makefile Tue Sep 29 17:44:54 2009 -0400
@@ -4,7 +4,8 @@
 
 BIN = $(ETC)/wmii$(CONFVERSION)
 TARG = python \
- plan9port
+ plan9port \
+ ruby
 
 $(TARG:%=%.install):
         echo INSTALL $$($(CLEANNAME) $(BASE)${@:.install=})
diff -r 772dd6ee7286 -r e2f59b70918d alternative_wmiircs/README
--- a/alternative_wmiircs/README Tue Sep 29 15:47:27 2009 -0400
+++ b/alternative_wmiircs/README Tue Sep 29 17:44:54 2009 -0400
@@ -4,10 +4,20 @@
 This folder contains alternative implementations of wmii's rc
 scripts. Each folder contains a different implementation,
 described below, including its own README, wmiirc script, and
-possibly other suppporting files and libraries. It usually
-suffices to copy the entire contents of the directory to
-~/.wmii, but see the accompanying README file for more
-details.
+possibly other suppporting files and libraries. These scripts
+are installed along with wmii to $(ETC) as defined in config.mk.
 
+It usually suffices to start the included `wmiirc` script at
+wmii startup. Invoking wmii with the flag '-r python/wmiirc',
+for instance, will start the python implementation.
+Alternatively, if you use a session manager, you can add this
+line to ~/.wmii/wmiirc (which must be executable):
+
+ wmiir xwrite /ctl spawn python/wmiirc
+
+ Index
+ ------------- ----------------------------------------------------
   python/ A pure Python wmiirc implementation.
+ plan9port/ A Plan 9 Port/rc shell based wmiirc implementation
+ ruby/ A pure-ruby wmiirc implementation, by Suraj Kurapati
 
diff -r 772dd6ee7286 -r e2f59b70918d alternative_wmiircs/ruby/HISTORY
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/alternative_wmiircs/ruby/HISTORY Tue Sep 29 17:44:54 2009 -0400
@@ -0,0 +1,233 @@
+= 2006-09-30
+
+* Included 1.1.0 release of Ruby-IXP.
+
+
+= 2006-09-29
+
+* Fixed bug in toggle_maximize method (in rc.rb) due
+ to accessing a nonexistent file in IXP file system.
+
+ Thanks to Christian von Mueffling for reporting this bug.
+
+* Fixed problem with reading
+ index (Wmii::Client#index) of
+ currently selected client.
+
+* Wmii.find_client now accepts a variable number of places to be searched.
+
+
+= 2006-09-28
+
+* Added number_view_buttons method (in rc.rb) which numbers
+ the view buttons displayed on the bar, from left to right.
+
+
+= 2006-09-27
+
+* Included two main concurrency fixes for Ruby-IXP.
+
+
+= 2006-09-24
+
+* Added two-stage event handling,
+ to minimize the number of events
+ missed while processing an event.
+
+
+= 2006-09-23
+
+* Fixed event & status bar loop. It was forgotten when I transitioned
+ to the new Ixp::Node#method_missing behavior on 2006-09-22.
+
+ Thanks to Fredrik Ternerot for reporting this bug.
+
+* When selecting views based on their first letter: if more than one
+ view matches, then they are cycled (adapted from Fredrik Ternerot).
+
+* Added focus_view_matching method in rc.rb.
+
+* Fixed errors that occurred when the tile and
+ diamond arrangements were applied to empty views.
+
+
+= 2006-09-22
+
+* Ixp::Node#method_missing now only dereferences files. Also,
+ the ! notation has been removed, as you can see below.
+
+ >> Wmii.fs.bar.status
+ => #<Ixp::Node:0xb7b5940c @path="/bar/status">
+ >> Wmii.fs.bar.status.read
+ => ["colors", "data"]
+ >> Wmii.fs.bar.status.data
+ => "Fri Sep 22 18:46:11 PDT 2006 | 0.06 0.10 0.08 | 531M 100% /home"
+ >> Wmii.fs.bar.status.data!
+ => #<Ixp::Node:0xb7b377e4 @path="/bar/status/data!">
+
+
+= 2006-09-21
+
+* Fix some forgotten changes from show_menu() returning *nil*.
+
+* Exception error message (xmessage) now lets you restart *wmiirc*.
+
+* Updated event loop to generate less 9P traffic.
+
+
+= 2006-09-20
+
+* Included code from upcoming Ruby-IXP 1.1.0 release.
+
+* Ixp::Node#method_missing now only dereferences a node
+ if the method is suffixed with an exclamation mark.
+
+* show_menu now returns *nil* if nothing was chosen.
+
+* Updated event loop for {wmii-3.1's /event overload bug
+ fix}[http://wmii.de/pipermail/wmii/2006-September/002718.html].
+
+* Added explicit termination of already running instances
+ in *wmiirc* via Process.kill and `ps`, instead of using
+ /event as a means of coordinating said task.
+
+
+= 2006-09-19
+
+* Included Ruby-IXP 1.0.3 release.
+
+* Added Ixp::Node#open method to reduce 9P traffic.
+
+* Added ability to fetch a sub-node
+ via Ixp::Node#method_missing, while
+ not dereferencing it (reading its
+ contents if it is a file), by adding
+ an exclamation to the file name.
+
+ For example, consider the following output in *wmiish*.
+
+ >> Wmii.fs.bar.status.data
+ => "Tue Sep 19 10:50:41 PDT 2006 | 0.30 0.43 0.29 | 1.7G 98% /home"
+ >> Wmii.fs.bar.status.data!
+ => #<Ixp::Node:0xb7bf1f18 @path="/bar/status/data">
+
+* *wmiirc* no longer automatically resumes from error. Instead,
+ it throws you a terminal and shows you the error details so
+ you have a chance to fix it and restart *wmiirc* yourself.
+
+
+= 2006-09-18
+
+* Included Ruby-IXP 1.0.2 release.
+
+
+= 2006-09-17
+
+* Added Wmii::View#empty? and Wmii::Area#empty? methods.
+
+* change_tag_from_menu now returns the chosen tag.
+
+* Included Ruby-IXP 1.0.1 release.
+
+
+= 2006-09-16
+
+* Fixed toggling of maximization
+ of currently focused client,
+ via toggle_maximize in rc.rb.
+
+ Thanks to Fredrik Ternerot for reporting this bug.
+
+
+= 2006-09-15
+
+* Added Wmii.get_view and Wmii.get_client
+ methods, to further minimize hard-coded
+ IXP file system paths. This will make it
+ easier to upgrade to wmii-4 later on.
+
+* Fixed ruby-ixp to be internally buffered for Ixp#read.
+
+* Event loop now uses Ixp#read instead of *wmiir*.
+
+* Already running configurations now correctly
+ exit when another instance starts up.
+
+
+= 2006-09-14
+
+* Added ability to swap current client with the
+ currently focused client in any other column.
+
+
+= 2006-09-13
+
+* Reverted to *wmiir* for event loop, because
+ Ixp#read isn't internally buffered!
+
+* Changed Wmii::View#each to Wmii::View#each_column because
+ floating area isn't a column (it doesn't have /mode file).
+
+* Added shortcuts for setting layouts of all columns in current view.
+
+* Added shortcuts for selection of current column.
+
+* Fixed ability to terminate multiple clients.
+
+
+= 2006-09-12
+
+* Event loop now uses Ixp#read instead of *wmiir*.
+
+ * Already running configurations now correctly
+ exit when another instance starts up.
+
+* Added Wmii::View#diamond! -- a diamond-shaped automated client arrangement.
+
+* Added Wmii::Area#length= for setting number of clients in a column.
+
+
+= 2006-09-11
+
+* Added exception logging and recovery mechanism.
+
+ * wmiirc is now split into a loader
+ file (wmiirc) and a configuration
+ file (wmiirc-config.rb), just
+ like in the ruby-wmii project.
+
+* IXPException' are no longer hidden away inside Ixp.
+
+* Moved support for destructive area-operations
+ from Wmii#with_selection into Array#each so
+ that it is generally available.
+
+
+= 2006-09-10
+
+* Added wmiish--an interactive Ruby shell for controlling wmii.
+
+* Lots of major refactoring in Ixp and Wmii.
+ * Moved utility methods from wmiirc into rc.rb.
+
+
+= 2006-09-09
+
+* Cleaned up IXP abstraction... now
+ multiple levels of method_missing
+ works, and so does self[sub_path]
+
+* Wmii#with_selection now supports destructive area-operations.
+
+* Update for compliance with new unique-client-id in filesystem patch.
+
+
+= 2006-08-31
+
+* Added facility which sends the selection
+ to temporary view or switches back again.
+
+
+= 2006-08-30
+
+* Add Wmii#with_selection method for operating on all clients in selection.
diff -r 772dd6ee7286 -r e2f59b70918d alternative_wmiircs/ruby/LICENSE
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/alternative_wmiircs/ruby/LICENSE Tue Sep 29 17:44:54 2009 -0400
@@ -0,0 +1,46 @@
+(the ISC license)
+
+Copyright 2006 Suraj N. Kurapati <sunaku_AT_gmail.com>
+Copyright 2007 Kris Maglione <jg_AT_suckless.org>
+Copyright 2007 Nick Stenning <nick_AT_whiteink.com>
+Copyright 2009 Daniel Wäber <waeber_AT_inf.fu-berlin.de>
+Copyright 2009 Michael Andrus <centyx_AT_centyx.net>
+Copyright 2009 Simon Hafner <hafnersimon_AT_gmail.com>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+Portions of this software originate from wmii <http://wmii.suckless.org>:
+
+(the MIT license)
+
+© 2006-2007 Kris Maglione <fbsdaemon_AT_gmail.com>
+© 2003-2006 Anselm R. Garbe <garbeam at suckless dot org>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff -r 772dd6ee7286 -r e2f59b70918d alternative_wmiircs/ruby/README
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/alternative_wmiircs/ruby/README Tue Sep 29 17:44:54 2009 -0400
@@ -0,0 +1,77 @@
+
+This is a modified version of sunaku's wmiirc, designed for
+his Rumai Ruby module. Minor configuration changes, namely to
+the color scheme and default key bindings, as well as the
+configuration search path, exist in this version. Builtin mpd
+support has also been removed. Also added is support for
+string interpolation in key bindings, as should be apparent in
+the included config.yaml.
+
+In particular, not that there is no need to copy any files to
+~/.wmii-hg or ~/.wmii other than config.yaml. The script will
+happily load the requisite files from their default install
+location. They can be loaded either by involing wmii as
+follows:
+
+ wmiir -r ruby/wmiirc
+
+or running the following after startup:
+
+ wmiir xwrite /ctl spawn ruby/wmiirc
+
+The rumai gem is still required, as noted below.
+
+The original readme appears below unmodified:
+
+sunaku's Ruby wmiirc
+====================
+
+This is my wmii configuration, described in these articles:
+
+ http://wmii.suckless.org/alternative_wmiirc_scripts
+
+ http://snk.tuxfamily.org/lib/rumai/
+
+ http://article.gmane.org/gmane.comp.window-managers.wmii/1704
+
+ http://snk.tuxfamily.org/web/2006-07-01-wmii-3-1-configuration-in-ruby.html
+
+Dependencies:
+
+ wmii 3.6 or newer (preferably wmii-hg)
+
+ Ruby 1.8.6 or newer
+
+ RubyGems 1.3.1 or newer
+
+Installation:
+
+ # library
+ gem install rumai # required
+ gem install librmpd # optional
+
+ # install
+ mv ~/.wmii-hg ~/.wmii-hg.backup
+ git clone git://github.com/sunaku/wmiirc.git ~/.wmii-hg
+
+ # run
+ ~/.wmii-hg/wmiirc
+
+Documentation:
+
+ # see list of all key bindings
+ grep 'Mod.*#' ~/.wmii-hg/config.yaml
+
+ # read the configuration file
+ less ~/.wmii-hg/config.yaml
+
+Configuration:
+
+ Edit ~/.wmii-hg/config.yaml to your liking.
+
+ Run ~/.wmii-hg/wmiirc to apply your changes.
+
+Questions:
+
+ Send me an e-mail; see LICENSE for my address.
+
diff -r 772dd6ee7286 -r e2f59b70918d alternative_wmiircs/ruby/config.rb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/alternative_wmiircs/ruby/config.rb Tue Sep 29 17:44:54 2009 -0400
@@ -0,0 +1,422 @@
+# DSL for wmiirc configuration.
+#--
+# Copyright protects this work.
+# See LICENSE file for details.
+#++
+
+require 'shellwords'
+require 'pathname'
+require 'yaml'
+
+require 'rubygems'
+gem 'rumai', '~> 3'
+require 'rumai'
+
+include Rumai
+
+class Handler < Hash
+ def initialize
+ super {|h,k| h[k] = [] }
+ end
+
+ ##
+ # If a block is given, registers a handler
+ # for the given key and returns the handler.
+ #
+ # Otherwise, executes all handlers registered for the given key.
+ #
+ def handle key, *args, &block
+ if block
+ self[key] << block
+
+ elsif key? key
+ self[key].each do |block|
+ block.call(*args)
+ end
+ end
+
+ block
+ end
+end
+
+EVENTS = Handler.new
+ACTIONS = Handler.new
+KEYS = Handler.new
+
+##
+# When a block is given, registers a handler
+# for the given event and returns the handler.
+#
+# Otherwise, executes all handlers for the given event.
+#
+def event *a, &b
+ EVENTS.handle(*a, &b)
+end
+
+##
+# Returns a list of registered event names.
+#
+def events
+ EVENTS.keys
+end
+
+##
+# If a block is given, registers a handler for
+# the given action and returns the handler.
+#
+# Otherwise, executes all handlers for the given action.
+#
+def action *a, &b
+ ACTIONS.handle(*a, &b)
+end
+
+##
+# Returns a list of registered action names.
+#
+def actions
+ ACTIONS.keys
+end
+
+##
+# If a block is given, registers a handler for
+# the given keypress and returns the handler.
+#
+# Otherwise, executes all handlers for the given keypress.
+#
+def key *a, &b
+ KEYS.handle(*a, &b)
+end
+
+##
+# Returns a list of registered action names.
+#
+def keys
+ KEYS.keys
+end
+
+##
+# Shows a menu (where the user must press keys on their keyboard to
+# make a choice) with the given items and returns the chosen item.
+#
+# If nothing was chosen, then nil is returned.
+#
+# ==== Parameters
+#
+# [prompt]
+# Instruction on what the user should enter or choose.
+#
+def key_menu choices, prompt = nil
+ words = %w[dmenu -b -fn].push(CONFIG['display']['font'])
+
+ words.concat %w[-nf -nb -sf -sb].zip(
+ [
+ CONFIG['display']['color']['normal'],
+ CONFIG['display']['color']['focus'],
+
+ ].map {|c| c.to_s.split[0,2] }.flatten
+
+ ).flatten
+
+ words.push '-p', prompt if prompt
+
+ command = words.shelljoin
+ IO.popen(command, 'r+') do |menu|
+ menu.puts choices
+ menu.close_write
+
+ choice = menu.read
+ choice unless choice.empty?
+ end
+end
+
+##
+# Shows a menu (where the user must click a menu
+# item using their mouse to make a choice) with
+# the given items and returns the chosen item.
+#
+# If nothing was chosen, then nil is returned.
+#
+# ==== Parameters
+#
+# [choices]
+# List of choices to display in the menu.
+#
+# [initial]
+# The choice that should be initially selected.
+#
+# If this choice is not included in the list
+# of cohices, then this item will be made
+# into a makeshift title-bar for the menu.
+#
+def click_menu choices, initial = nil
+ words = %w[wmii9menu]
+
+ if initial
+ words << '-i'
+
+ unless choices.include? initial
+ initial = "<<#{initial}>>:"
+ words << initial
+ end
+
+ words << initial
+ end
+
+ words.concat choices
+ command = words.shelljoin
+
+ choice = `#{command}`.chomp
+ choice unless choice.empty?
+end
+
+##
+# Returns the basenames of executable files present in the given directories.
+#
+def find_programs *dirs
+ dirs.flatten.
+ map {|d| Pathname.new(d).expand_path.children rescue [] }.flatten.
+ map {|f| f.basename.to_s if f.file? and f.executable? }.compact.uniq.sort
+end
+
+##
+# Launches the command built from the given words in the background.
+#
+def launch *words
+ command = words.shelljoin
+ system "#{command} &"
+end
+
+##
+# A button on a bar.
+#
+class Button < Thread
+ ##
+ # Creates a new button at the given node and updates its label
+ # according to the given refresh rate (measured in seconds). The
+ # given block is invoked to calculate the label of the button.
+ #
+ # The return value of the given block can be either an
+ # array (whose first item is a wmii color sequence for the
+ # button, and the remaining items compose the label of the
+ # button) or a string containing the label of the button.
+ #
+ # If the given block raises a standard exception, then that will be
+ # rescued and displayed (using error colors) as the button's label.
+ #
+ def initialize fs_bar_node, refresh_rate, &button_label
+ raise ArgumentError, 'block must be given' unless block_given?
+
+ super(fs_bar_node) do |button|
+ while true
+ label =
+ begin
+ Array(button_label.call)
+ rescue Exception => e
+ LOG.error e
+ [CONFIG['display']['color']['error'], e]
+ end
+
+ # provide default color
+ unless label.first =~ /(?:#[[:xdigit:]]{6} ?){3}/
+ label.unshift CONFIG['display']['color']['normal']
+ end
+
+ button.create unless button.exist?
+ button.write label.join(' ')
+ sleep refresh_rate
+ end
+ end
+ end
+
+ ##
+ # Refreshes the label of this button.
+ #
+ alias refresh wakeup
+end
+
+##
+# Loads the given YAML configuration file.
+#
+def load_config config_file
+ Object.const_set :CONFIG, YAML.load_file(config_file)
+
+ # script
+ eval CONFIG['script']['before'].to_s, TOPLEVEL_BINDING,
+ "#{config_file}:script:before"
+
+ # display
+ fo = ENV['WMII_FONT'] = CONFIG['display']['font']
+ fc = ENV['WMII_FOCUSCOLORS'] = CONFIG['display']['color']['focus']
+ nc = ENV['WMII_NORMCOLORS'] = CONFIG['display']['color']['normal']
+
+ settings = {
+ 'font' => fo,
+ 'focuscolors' => fc,
+ 'normcolors' => nc,
+ 'border' => CONFIG['display']['border'],
+ 'bar on' => CONFIG['display']['bar'],
+ 'colmode' => CONFIG['display']['column']['mode'],
+ 'grabmod' => CONFIG['control']['grab'],
+ }
+
+ begin
+ fs.ctl.write settings.map {|pair| pair.join(' ') }.join("\n")
+
+ rescue Rumai::IXP::Error => e
+ #
+ # settings that are not supported in a particular wmii version
+ # are ignored, and those that are supported are (silently)
+ # applied. but a "bad command" error is raised nevertheless!
+ #
+ warn e.inspect
+ warn e.backtrace
+ end
+
+ launch 'xsetroot', '-solid', CONFIG['display']['background']
+
+ # column
+ fs.colrules.write CONFIG['display']['column']['rule']
+
+ # client
+ event 'CreateClient' do |client_id|
+ client = Client.new(client_id)
+
+ unless defined? @client_tags_by_regexp
+ @client_tags_by_regexp = CONFIG['display']['client'].map {|hash|
+ k, v = hash.to_a.first
+ [eval(k, TOPLEVEL_BINDING, "#{config_file}:display:client"), v]
+ }
+ end
+
+ if label = client.props.read rescue nil
+ catch :found do
+ @client_tags_by_regexp.each do |regexp, tags|
+ if label =~ regexp
+ client.tags = tags
+ throw :found
+ end
+ end
+
+ # force client onto current view
+ begin
+ client.tags = curr_tag
+ client.focus
+ rescue
+ # ignore
+ end
+ end
+ end
+ end
+
+ # status
+ action 'status' do
+ fs.rbar.clear
+
+ unless defined? @status_button_by_name
+ @status_button_by_name = {}
+ @status_button_by_file = {}
+ @on_click_by_status_button = {}
+
+ CONFIG['display']['status'].each_with_index do |hash, position|
+ name, defn = hash.to_a.first
+
+ # buttons appear in ASCII order of their IXP file name
+ file = "#{position}-#{name}"
+
+ button = eval(
+ "Button.new(fs.rbar[#{file.inspect}], #{defn['refresh']}) { #{defn['content']} }",
+ TOPLEVEL_BINDING, "#{config_file}:display:status:#{name}"
+ )
+
+ @status_button_by_name[name] = button
+ @status_button_by_file[file] = button
+
+ # mouse click handler
+ if code = defn['click']
+ @on_click_by_status_button[button] = eval(
+ "lambda {|mouse_button| #{code} }", TOPLEVEL_BINDING,
+ "#{config_file}:display:status:#{name}:click"
+ )
+ end
+ end
+ end
+
+ @status_button_by_name.each_value {|b| b.refresh }
+
+ end.call
+
+ ##
+ # Returns the status button associated with the given name.
+ #
+ # ==== Parameters
+ #
+ # [name]
+ # Either the the user-defined name of
+ # the status button or the basename
+ # of the status button's IXP file.
+ #
+ def status_button name
+ @status_button_by_name[name] || @status_button_by_file[name]
+ end
+
+ ##
+ # Refreshes the content of the status button with the given name.
+ #
+ # ==== Parameters
+ #
+ # [name]
+ # Either the the user-defined name of
+ # the status button or the basename
+ # of the status button's IXP file.
+ #
+ def status name
+ if button = status_button(name)
+ button.refresh
+ end
+ end
+
+ ##
+ # Invokes the mouse click handler for the given mouse
+ # button on the status button that has the given name.
+ #
+ # ==== Parameters
+ #
+ # [name]
+ # Either the the user-defined name of
+ # the status button or the basename
+ # of the status button's IXP file.
+ #
+ # [mouse_button]
+ # The identification number of
+ # the mouse button (as defined
+ # by X server) that was clicked.
+ #
+ def status_click name, mouse_button
+ if button = status_button(name) and
+ handle = @on_click_by_status_button[button]
+ then
+ handle.call mouse_button.to_i
+ end
+ end
+
+ # control
+ %w[key action event].each do |param|
+ CONFIG['control'][param].each do |name, code|
+ eval "#{param}(#{name.inspect}) {|*argv| #{code} }",
+ TOPLEVEL_BINDING, "#{config_file}:control:#{param}:#{name}"
+ end
+ end
+
+ # script
+ eval CONFIG['script']['after'].to_s, TOPLEVEL_BINDING,
+ "#{config_file}:script:after"
+
+end
+
+##
+# Reloads the entire wmii configuration.
+#
+def reload_config
+ LOG.info 'reload'
+ launch $0
+end
diff -r 772dd6ee7286 -r e2f59b70918d alternative_wmiircs/ruby/config.yaml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/alternative_wmiircs/ruby/config.yaml Tue Sep 29 17:44:54 2009 -0400
@@ -0,0 +1,804 @@
+#
+# High-level wmii configuration.
+#
+# Ruby code in this file has access
+# to a CONFIG constant which contains
+# the data in this configuration file.
+#
+#--
+# Copyright protects this work.
+# See LICENSE file for details.
+#++
+
+
+##
+# Appearance settings.
+#
+display:
+
+ ##
+ # Where to display the horizontal status bar?
+ #
+ # Possible choices are "top" and "bottom".
+ #
+ bar: bottom
+
+ ##
+ # The font to use in all text drawn by wmii.
+ #
+ font: -*-fixed-medium-r-*-*-18-*-*-*-*-*-*-*
+
+ ##
+ # Thickness of client border (measured in pixels).
+ #
+ border: 1
+
+ ##
+ # Number of seconds a notice should be displayed.
+ #
+ notice: 5
+
+ ##
+ # Color schemes for everything drawn by wmii.
+ #
+ # <scheme>: "<text> <background> <border>"
+ #
+ # You can find more color schemes here:
+ #
+ # http://wmii.suckless.org/scripts_n_snips/themes
+ #
+ color:
+ normal: "#c0c0c0 #0a0a0a #202020"
+ focus: "#ffffff #285577 #4c7899"
+ error: "#8a1f11 #FBE3E4 #FBC2C4" # from http://www.blueprintcss.org
+ notice: "#514721 #FFF6BF #FFD324" # from http://www.blueprintcss.org
+ success: "#264409 #E6EFC2 #C6D880" # from http://www.blueprintcss.org
+
+ ##
+ # Color of desktop background.
+ #
+ background: "#333333"
+
+ ##
+ # Settings for columns drawn by wmii.
+ #
+ # mode: <the wmii "colmode" setting>
+ # rule: <the wmii "colrules" setting>
+ #
+ column:
+ mode: default
+ rule: |
+ /gimp/ -> 17+83+41
+ /.*/ -> 50+50
+
+ ##
+ # Mapping of clients to views they must appear on.
+ #
+ # - <client props regular expression> : <tags to apply>
+ #
+ # These mappings are processed in top-to-bottom order.
+ # Processing stops after the first matching mapping is applied.
+ #
+ client:
+ - /\b(xconsole|alsamixer|XMMS|Sonata)\b/ : 1
+ - /^pidgin:|:WeeChat\b/ : chat
+ - /\b(Liferea|GMail|Thunderbird)\b/ : mail
+ - /:(Firefox|Shiretoko):.*\bRestore\b.*\bSession\b/ : web
+
+ ##
+ # Self-refreshing buttons on the status bar.
+ #
+ # - <button name>:
+ # refresh: <number of seconds to wait before refreshing the content>
+ # content: <Ruby code whose result is displayed as the content>
+ # click: <Ruby code to handle mouse clicks on the status button.
+ # This code has access to a "mouse_button" variable which is
+ # an integer representing the mouse button that was clicked.>
+ #
+ # You can refresh a particular status button in Ruby using:
+ #
+ # status "your button name"
+ #
+ # The horizontal order in which these buttons appear on the status
+ # bar reflects the vertical order in which they are defined below.
+ #
+ status:
+ - music:
+ refresh: 15
+ content: |
+ unless defined? @music
+ require 'rubygems'
+ gem 'librmpd', '~> 0.1'
+ require 'librmpd'
+
+ @music = MPD.new
+ end
+
+ unless @music.connected?
+ @music.connect
+ end
+
+ music_state = (@music.stopped? || @music.paused?) ? '(-)' : '(>)'
+
+ if song = @music.current_song
+ artist = song.artist
+ title = song.title || (f = song.file and File.basename(f))
+ song_name = [artist, title].compact.join(': ')
+ end
+
+ [music_state, song_name].compact
+
+ - volume:
+ refresh: 60
+ content: |
+ ['volume', `amixer get Master`.scan(/\d+%/).first]
+
+ - disk_space:
+ refresh: 600 # 10 minutes
+ content: |
+ free, used, path = `df -h ~`.split.last(3)
+ [path, used, 'used', free, 'free']
+
+ - system_load:
+ refresh: 10
+ content: |
+ load_averages = File.read('/proc/loadavg').split.first(3)
+ current_load = load_averages.first.to_f
+
+ # visually indicate the intensity of system load
+ color = case
+ when current_load > 3.0 then CONFIG['display']['color']['error']
+ when current_load > 1.5 then CONFIG['display']['color']['notice']
+ end
+
+ [color, *load_averages]
+
+ - clock:
+ refresh: 5
+ content: Time.now.to_s
+
+
+##
+# Interaction settings.
+#
+control:
+
+ ##
+ # The wmii "grabmod" setting.
+ #
+ grab: Mod1
+
+ ##
+ # Key bindings.
+ #
+ # <key sequence>: <Ruby code to execute>
+ #
+ key:
+ #---------------------------------------------------------------------------
+ # focus
+ #---------------------------------------------------------------------------
+
+ Mod1-Control-t: | # focus above client
+ curr_view.select(:up) rescue nil
+
+ Mod1-Control-n: | # focus below client
+ curr_view.select(:down) rescue nil
+
+ Mod1-Control-h: | # focus left client
+ curr_view.select(:left) rescue nil
+
+ Mod1-Control-s: | # focus right client
+ curr_view.select(:right) rescue nil
+
+ Mod1-Control-space: | # focus floating area (toggle)
+ curr_view.select(:toggle)
+
+ Mod1-Control-comma: | # focus previous view
+ prev_view.focus
+
+ Mod1-Control-period: | # focus next view
+ next_view.focus
+
+ # focus the view whose index or name equals the pressed number
+ Mod1-Control-1: focus_view( tags[0] || 1 )
+ Mod1-Control-2: focus_view( tags[1] || 2 )
+ Mod1-Control-3: focus_view( tags[2] || 3 )
+ Mod1-Control-4: focus_view( tags[3] || 4 )
+ Mod1-Control-5: focus_view( tags[4] || 5 )
+ Mod1-Control-6: focus_view( tags[5] || 6 )
+ Mod1-Control-7: focus_view( tags[6] || 7 )
+ Mod1-Control-8: focus_view( tags[7] || 8 )
+ Mod1-Control-9: focus_view( tags[8] || 9 )
+ Mod1-Control-0: focus_view( tags[9] || 10 )
+
+ # focus the view whose name begins with the pressed alphabet
+ Mod1-Control-v,a: t = tags.grep(/^a/i).first and focus_view(t)
+ Mod1-Control-v,b: t = tags.grep(/^b/i).first and focus_view(t)
+ Mod1-Control-v,c: t = tags.grep(/^c/i).first and focus_view(t)
+ Mod1-Control-v,d: t = tags.grep(/^d/i).first and focus_view(t)
+ Mod1-Control-v,e: t = tags.grep(/^e/i).first and focus_view(t)
+ Mod1-Control-v,f: t = tags.grep(/^f/i).first and focus_view(t)
+ Mod1-Control-v,g: t = tags.grep(/^g/i).first and focus_view(t)
+ Mod1-Control-v,h: t = tags.grep(/^h/i).first and focus_view(t)
+ Mod1-Control-v,i: t = tags.grep(/^i/i).first and focus_view(t)
+ Mod1-Control-v,j: t = tags.grep(/^j/i).first and focus_view(t)
+ Mod1-Control-v,k: t = tags.grep(/^k/i).first and focus_view(t)
+ Mod1-Control-v,l: t = tags.grep(/^l/i).first and focus_view(t)
+ Mod1-Control-v,m: t = tags.grep(/^m/i).first and focus_view(t)
+ Mod1-Control-v,n: t = tags.grep(/^n/i).first and focus_view(t)
+ Mod1-Control-v,o: t = tags.grep(/^o/i).first and focus_view(t)
+ Mod1-Control-v,p: t = tags.grep(/^p/i).first and focus_view(t)
+ Mod1-Control-v,q: t = tags.grep(/^q/i).first and focus_view(t)
+ Mod1-Control-v,r: t = tags.grep(/^r/i).first and focus_view(t)
+ Mod1-Control-v,s: t = tags.grep(/^s/i).first and focus_view(t)
+ Mod1-Control-v,t: t = tags.grep(/^t/i).first and focus_view(t)
+ Mod1-Control-v,u: t = tags.grep(/^u/i).first and focus_view(t)
+ Mod1-Control-v,v: t = tags.grep(/^v/i).first and focus_view(t)
+ Mod1-Control-v,w: t = tags.grep(/^w/i).first and focus_view(t)
+ Mod1-Control-v,x: t = tags.grep(/^x/i).first and focus_view(t)
+ Mod1-Control-v,y: t = tags.grep(/^y/i).first and focus_view(t)
+ Mod1-Control-v,z: t = tags.grep(/^z/i).first and focus_view(t)
+
+ #---------------------------------------------------------------------------
+ # move
+ #---------------------------------------------------------------------------
+
+ Mod1-Control-m,t: | # move grouping toward the top
+ grouping.each {|c| c.send(:up) rescue nil }
+
+ Mod1-Control-m,n: | # move grouping toward the bottom
+ grouping.each {|c| c.send(:down) rescue nil }
+
+ Mod1-Control-m,h: | # move grouping toward the left
+ grouping.each {|c| c.send(:left) rescue nil }
+
+ Mod1-Control-m,s: | # move grouping toward the right
+ grouping.each {|c| c.send(:right) rescue nil }
+
+ Mod1-Control-m,space: | # move grouping to floating area (toggle)
+ grouping.each {|c| c.send(:toggle) rescue nil }
+
+ Mod1-Control-m,v: | # move grouping to chosen view
+ #
+ # Changes the tag (according to a menu choice) of
+ # each grouped client and returns the chosen tag.
+ #
+ # The +tag -tag idea is from Jonas Pfenniger:
+ #
+ # http://zimbatm.oree.ch/articles/2006/06/15/wmii-3-and-ruby
+ #
+ choices = tags.map {|t| [t, "+#{t}", "-#{t}"] }.flatten
+
+ if target = key_menu(choices, 'tag as:')
+ grouping.each do |c|
+ case target
+ when /^\+/ then c.tag $'
+ when /^\-/ then c.untag $'
+ else c.tags = target
+ end
+ end
+ end
+
+ Mod1-Control-m,Delete: | # kill all clients in grouping
+ grouping.each {|c| c.kill }
+
+ # move grouping to the view whose index or name equals the pressed number
+ Mod1-Control-m,1: grouping.each {|c| c.tags = tags[0] || 1 }
+ Mod1-Control-m,2: grouping.each {|c| c.tags = tags[1] || 2 }
+ Mod1-Control-m,3: grouping.each {|c| c.tags = tags[2] || 3 }
+ Mod1-Control-m,4: grouping.each {|c| c.tags = tags[3] || 4 }
+ Mod1-Control-m,5: grouping.each {|c| c.tags = tags[4] || 5 }
+ Mod1-Control-m,6: grouping.each {|c| c.tags = tags[5] || 6 }
+ Mod1-Control-m,7: grouping.each {|c| c.tags = tags[6] || 7 }
+ Mod1-Control-m,8: grouping.each {|c| c.tags = tags[7] || 8 }
+ Mod1-Control-m,9: grouping.each {|c| c.tags = tags[8] || 9 }
+ Mod1-Control-m,0: grouping.each {|c| c.tags = tags[9] || 10 }
+
+ #---------------------------------------------------------------------------
+ # swap
+ #---------------------------------------------------------------------------
+
+ Mod1-Control-w,t: | # swap with above client
+ curr_client.swap(:up) rescue nil
+
+ Mod1-Control-w,n: | # swap with below client
+ curr_client.swap(:down) rescue nil
+
+ Mod1-Control-w,h: | # swap with left client
+ curr_client.swap(:left) rescue nil
+
+ Mod1-Control-w,s: | # swap with right client
+ curr_client.swap(:right) rescue nil
+
+ # swap current client with the column whose index equals the pressed number
+ Mod1-Control-w,1: curr_client.swap 1
+ Mod1-Control-w,2: curr_client.swap 2
+ Mod1-Control-w,3: curr_client.swap 3
+ Mod1-Control-w,4: curr_client.swap 4
+ Mod1-Control-w,5: curr_client.swap 5
+ Mod1-Control-w,6: curr_client.swap 6
+ Mod1-Control-w,7: curr_client.swap 7
+ Mod1-Control-w,8: curr_client.swap 8
+ Mod1-Control-w,9: curr_client.swap 9
+ Mod1-Control-w,0: curr_client.swap 10
+
+ #---------------------------------------------------------------------------
+ # column
+ #---------------------------------------------------------------------------
+
+ Mod1-Control-z,w: | # apply equal-spacing layout to current column
+ curr_area.layout = :default
+
+ Mod1-Control-z,Shift-w: | # apply equal-spacing layout to all columns
+ curr_view.columns.each do |a|
+ a.layout = :default
+ end
+
+ Mod1-Control-z,v: | # apply stacked layout to current column
+ curr_area.layout = 'stack-max'
+
+ Mod1-Control-z,Shift-v: | # apply stacked layout to all columns
+ curr_view.columns.each do |a|
+ a.layout = 'stack-max'
+ end
+
+ Mod1-Control-z,m: | # apply maximized layout to current column
+ curr_area.layout = 'stack+max'
+
+ Mod1-Control-z,Shift-m: | # apply maximized layout to all columns
+ curr_view.columns.each do |a|
+ a.layout = 'stack+max'
+ end
+
+ #---------------------------------------------------------------------------
+ # group
+ #---------------------------------------------------------------------------
+
+ Mod1-Control-g,g: | # toggle current client from grouping
+ curr_client.group!
+
+ Mod1-Control-g,c: | # add clients in current area to grouping
+ curr_area.group
+
+ Mod1-Control-g,Shift-c: | # remove clients in current area from grouping
+ curr_area.ungroup
+
+ Mod1-Control-g,f: | # add clients in floating area to grouping
+ Area.floating.group
+
+ Mod1-Control-g,Shift-f: | # remove clients in floating area from grouping
+ Area.floating.ungroup
+
+ Mod1-Control-g,m: | # add clients in managed areas to grouping
+ curr_view.managed_areas.each {|a| a.group }
+
+ Mod1-Control-g,Shift-m: | # remove clients in managed areas from grouping
+ curr_view.managed_areas.each {|a| a.ungroup }
+
+ Mod1-Control-g,v: | # add clients in current view to grouping
+ curr_view.group
+
+ Mod1-Control-g,Shift-v: | # remove clients in current view from grouping
+ curr_view.ungroup
+
+ Mod1-Control-g,i: | # invert the grouping in the current view
+ curr_view.group!
+
+ Mod1-Control-g,Shift-i: | # invert the grouping in all views
+ Rumai.group!
+
+ Mod1-Control-g,n: | # remove all clients everywhere from grouping
+ Rumai.ungroup
+
+ #---------------------------------------------------------------------------
+ # detach
+ #---------------------------------------------------------------------------
+
+ Mod1-Control-d: | # detach grouping from current view
+ grouping.each do |c|
+ c.with_tags do
+ delete curr_tag
+ push DETACHED_TAG
+ end
+ end
+
+ Mod1-Control-Shift-d: | # attach most recently detached client
+ v = View.new DETACHED_TAG
+
+ if v.exist? and c = v.clients.last
+ c.with_tags do
+ delete DETACHED_TAG
+ push curr_tag
+ end
+ end
+
+ #---------------------------------------------------------------------------
+ # zoom
+ #---------------------------------------------------------------------------
+
+ Mod1-Control-f: | # zoom client to fullscreen (toggle)
+ curr_client.fullscreen!
+
+ Mod1-Control-b: | # copy grouping to temporary view
+ clients = grouping
+
+ unless clients.empty?
+ # determine new view
+ if curr_tag =~ ZOOMED_SUFFIX
+ src, num = $`, $1.to_i
+ dst = "#{src}~#{num+1}"
+ else
+ dst = "#{curr_tag}~1"
+ end
+
+ # add clients to new view
+ clients.each {|c| c.tag dst }
+
+ # focus new view
+ v = View.new dst
+ v.focus
+ v.arrange_in_grid
+
+ # propagate focus into new view
+ clients.first.focus v
+ end
+
+ Mod1-Control-Shift-b: | # return grouping to original view
+ clients = grouping
+
+ unless clients.empty?
+ src = curr_tag
+
+ if src =~ ZOOMED_SUFFIX
+ # determine new view
+ dst = $`
+
+ # remove clients from old view
+ clients.each do |c|
+ c.with_tags do
+ delete src
+
+ if empty?
+ push dst
+ else
+ dst = last
+ end
+ end
+ end
+
+ # focus new view
+ v = View.new dst
+ v.focus
+
+ # propagate focus into original view
+ clients.first.focus v
+ end
+ end
+
+ #---------------------------------------------------------------------------
+ # arrange
+ #---------------------------------------------------------------------------
+
+ Mod1-Control-z,t: | # arrange clients in current view like LarsWM does
+ curr_view.arrange_as_larswm
+
+ Mod1-Control-z,g: | # arrange clients in current view like a grid
+ curr_view.arrange_in_grid
+
+ Mod1-Control-z,d: | # arrange clients in current view like a diamond
+ curr_view.arrange_in_diamond
+
+ # apply grid layout with the pressed number of clients per column
+ Mod1-Control-z,1: curr_view.arrange_in_grid 1
+ Mod1-Control-z,2: curr_view.arrange_in_grid 2
+ Mod1-Control-z,3: curr_view.arrange_in_grid 3
+ Mod1-Control-z,4: curr_view.arrange_in_grid 4
+ Mod1-Control-z,5: curr_view.arrange_in_grid 5
+ Mod1-Control-z,6: curr_view.arrange_in_grid 6
+ Mod1-Control-z,7: curr_view.arrange_in_grid 7
+ Mod1-Control-z,8: curr_view.arrange_in_grid 8
+ Mod1-Control-z,9: curr_view.arrange_in_grid 9
+ Mod1-Control-z,0: curr_view.arrange_in_grid 9999 # make one giant column
+
+ #---------------------------------------------------------------------------
+ # menu
+ #---------------------------------------------------------------------------
+
+ Mod1-Control-i: | # run internal action chosen from a menu
+ if choice = key_menu(actions, 'run action:')
+ action choice
+ end
+
+ Mod1-Control-e: | # run external program chosen from a menu
+ if choice = key_menu(@programs, 'run program:')
+ launch choice
+ end
+
+ Mod1-Control-u: | # focus view chosen from a menu
+ if choice = key_menu(tags, 'show view:')
+ focus_view choice
+ end
+
+ Mod1-Control-a: | # focus client chosen from a menu
+ choices = []
+
+ clients.each_with_index do |c, i|
+ choices << "%d. [%s] %s" % [i, c[:tags].read, c[:label].read.downcase]
+ end
+
+ if target = key_menu(choices, 'show client:')
+ i = target.scan(/\d+/).first.to_i
+ clients[i].focus
+ end
+
+ #---------------------------------------------------------------------------
+ # launcher
+ #---------------------------------------------------------------------------
+
+ Mod1-Control-x: | # launch a terminal
+ #
+ # Launch a new terminal and set its
+ # working directory to be the same
+ # as the currently focused terminal.
+ #
+ work = ENV['HOME']
+
+ label = curr_client.label.read rescue ''
+
+ # iterate in reverse order because
+ # paths are usually at end of label
+ label.split(' ').reverse_each do |s|
+ path = File.expand_path(s)
+
+ if File.exist? path
+ unless File.directory? path
+ path = File.dirname(path)
+ end
+
+ work = path
+ break
+ end
+ end
+
+ require 'fileutils'
+ FileUtils.cd work do
+ launch 'urxvt'
+ end
+
+ Mod1-Control-k: | # launch a web browser
+ launch 'firefox'
+
+ Mod1-Control-j: | # launch a file manager
+ launch 'thunar'
+
+ Mod1-Control-q: | # launch a note taker
+ launch 'mousepad'
+
+ #---------------------------------------------------------------------------
+ # music
+ #---------------------------------------------------------------------------
+
+ Mod1-Control-Prior: | # previous song
+ @music.previous rescue nil
+ status 'music'
+
+ Mod1-Control-Next: | # next song
+ @music.next rescue nil
+ status 'music'
+
+ Mod1-Control-Return: | # pause song (toggle)
+ begin
+ if @music.stopped?
+ @music.play
+ else
+ @music.pause = !@music.paused?
+ end
+ rescue
+ # ignore
+ end
+
+ status 'music'
+
+ Mod1-Control-Home: | # load a playlist
+ if list = key_menu(@music.playlists, 'load playlist:')
+ @music.clear
+ @music.load list
+ @music.play
+ end
+
+ Mod1-Control-End: | # add current song to a playlist
+ if list = key_menu(@music.playlists, 'add current song to playlist:')
+ file = File.join(File.expand_path('~/.mpd/playlists'), list + '.m3u')
+
+ songs = File.readlines(file) rescue []
+ songs << @music.current_song.file
+ songs.uniq!
+
+ File.open(file, 'w') {|f| f.puts songs }
+ end
+
+ #---------------------------------------------------------------------------
+ # volume
+ #---------------------------------------------------------------------------
+
+ Mod1-Control-Shift-Prior: | # increase volume
+ system 'amixer set Master 3dB+'
+ status 'volume'
+
+ Mod1-Control-Shift-Next: | # decrease volume
+ system 'amixer set Master 3dB-'
+ status 'volume'
+
+ Mod1-Control-Shift-Return: | # mute volume (toggle)
+ system 'amixer set Master toggle'
+ status 'volume'
+
+ ##
+ # Event handlers.
+ #
+ # <event name>: <Ruby code to execute>
+ #
+ # The Ruby code has access to an "argv" variable which
+ # is a list of arguments that were passed to the event.
+ #
+ event:
+ CreateTag: |
+ tag = argv[0]
+ but = fs.lbar[tag]
+ but.create unless but.exist?
+ but.write "#{CONFIG['display']['color']['normal']} #{tag}"
+
+ DestroyTag: |
+ tag = argv[0]
+ but = fs.lbar[tag]
+ but.remove if but.exist?
+
+ FocusTag: |
+ tag = argv[0]
+ but = fs.lbar[tag]
+ but.write "#{CONFIG['display']['color']['focus']} #{tag}" if but.exist?
+
+ UnfocusTag: |
+ tag = argv[0]
+ but = fs.lbar[tag]
+ but.write "#{CONFIG['display']['color']['normal']} #{tag}" if but.exist?
+
+ UrgentTag: |
+ tag = argv[1]
+ but = fs.lbar[tag]
+ but.write "#{CONFIG['display']['color']['notice']} #{tag}" if but.exist?
+
+ NotUrgentTag: |
+ tag = argv[1]
+ but = fs.lbar[tag]
+ color = curr_view.id == tag ? 'focus' : 'normal'
+ but.write "#{CONFIG['display']['color'][color]} #{tag}" if but.exist?
+
+ LeftBarClick: &LeftBarClick |
+ mouse_button, view_id = argv
+
+ if mouse_button == '1' # primary button
+ focus_view view_id
+ end
+
+ ##
+ # allows the user to drag a file over a
+ # view button and activate that view while
+ # still holding on to their dragged file!
+ #
+ LeftBarDND: *LeftBarClick
+
+ RightBarClick: |
+ status_click *argv.reverse
+
+ Unresponsive: |
+ client_id = argv[0]
+ client = Client.new(client_id)
+
+ IO.popen('xmessage -nearmouse -file - -buttons Kill,Wait -print', 'w+') do |f|
+ f.puts 'The following client is not responding.', ''
+ f.puts client.inspect
+ f.puts client.label.read
+
+ f.puts '', 'What would you like to do?'
+ f.close_write
+
+ if f.read.chomp == 'Kill'
+ client.slay
+ end
+ end
+
+ Notice: |
+ unless defined? @notice_mutex
+ require 'thread'
+ @notice_mutex = Mutex.new
+ end
+
+ Thread.new do
+ # prevent notices from overwriting each other
+ @notice_mutex.synchronize do
+ button = fs.rbar['!notice']
+ button.create unless button.exist?
+
+ # display the notice
+ message = argv.join(' ')
+
+ LOG.info message # also log it in case the user is AFK
+ button.write "#{CONFIG['display']['color']['notice']} #{message}"
+
+ # clear the notice
+ sleep [1, CONFIG['display']['notice'].to_i].max
+ button.remove
+ end
+ end
+
+ ClientMouseDown: |
+ client_id, mouse_button = argv
+
+ if mouse_button == '3' # secondary button
+ client = Client.new(client_id)
+
+ case click_menu %w[stick group fullscreen kill slay], 'client'
+ when 'stick' then client.stick!
+ when 'group' then client.group!
+ when 'fullscreen' then client.fullscreen!
+ when 'kill' then client.kill
+ when 'slay' then client.slay
+ end
+ end
+
+ ##
+ # Internal scripts.
+ #
+ # <action name>: <Ruby code to execute>
+ #
+ action:
+ reload: | # reload this wmii configuration
+ reload_config
+
+ rehash: | # scan for available programs and actions
+ @programs = find_programs(ENV['PATH'].squeeze(':').split(':'))
+
+ clear: | # kill all clients
+ # firefox's restore session feature does not
+ # work unless the whole process is killed.
+ system 'killall firefox firefox-bin thunderbird thunderbird-bin'
+
+ # gnome-panel refuses to die by any other means
+ system 'killall -s TERM gnome-panel'
+
+ Thread.pass until clients.each do |c|
+ begin
+ c.focus # XXX: client must be on current view in order to be killed
+ c.kill
+ rescue
+ # ignore
+ end
+ end.empty?
+
+ kill: | # kill the window manager only; do not touch the clients!
+ fs.ctl.write 'quit'
+
+ quit: | # kill both clients and window manager
+ action 'clear'
+ action 'kill'
+
+
+##
+# Arbitrary logic.
+#
+# script:
+# before: <Ruby code to execute before processing this file>
+# after: <Ruby code to execute after processing this file>
+#
+script:
+ before: |
+ DETACHED_TAG = '|'
+ ZOOMED_SUFFIX = /~(\d+)$/
+
+ after: |
+ action 'rehash'
+
+ # desktop wallpaper
+ system 'sh ~/.fehbg'
+
diff -r 772dd6ee7286 -r e2f59b70918d alternative_wmiircs/ruby/wmiirc
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/alternative_wmiircs/ruby/wmiirc Tue Sep 29 17:44:54 2009 -0400
@@ -0,0 +1,83 @@
+#!/usr/bin/env ruby
+# Bootloader for wmii configuration.
+#--
+# Copyright protects this work.
+# See LICENSE file for details.
+#++
+
+# create a logger to aid debugging
+require 'logger'
+LOG = Logger.new(__FILE__ + '.log', 5)
+
+class << LOG
+ # emulate IO.write
+ alias write <<
+
+ def flush
+ # ignore
+ end
+end
+
+# capture standard output in logger
+$stdout = $stderr = LOG
+
+begin
+ LOG.info 'birth'
+
+ # load configuration library
+ config_home = File.dirname(__FILE__)
+ config_libs = File.join(config_home, 'config.rb')
+ config_file = File.join(config_home, 'config.yaml')
+
+ require config_libs
+
+ # terminate any existing wmiirc
+ fs.event.write 'Start wmiirc'
+
+ event 'Start' do |arg|
+ exit if arg == 'wmiirc'
+ end
+
+ # apply user configuration
+ load_config config_file
+
+ # setup tag bar (buttons that correspond to views)
+ fs.lbar.clear
+ tags.each {|t| event 'CreateTag', t }
+ event 'FocusTag', curr_tag
+
+ # register key bindings
+ fs.keys.write keys.join("\n")
+ event('Key') {|*a| key(*a) }
+
+ # the main event loop
+ fs.event.each_line do |line|
+ line.split("\n").each do |call|
+ name, args = call.split(' ', 2)
+
+ argv = args.to_s.split(' ')
+ event name, *argv
+ end
+ end
+
+rescue SystemExit
+ # ignore it; the program wants to terminate
+
+rescue Exception => e
+ LOG.error e
+
+ # allow the user to rescue themselves
+ system 'xterm &'
+
+ IO.popen('xmessage -nearmouse -file - -buttons Recover,Ignore -print', 'w+') do |f|
+ f.puts e.inspect, e.backtrace
+ f.close_write
+
+ if f.read.chomp == 'Recover'
+ reload_config
+ end
+ end
+
+ensure
+ LOG.info 'death'
+end
Received on Tue Sep 29 2009 - 21:45:13 UTC

This archive was generated by hypermail 2.2.0 : Tue Sep 29 2009 - 21:48:05 UTC