| |   |
| 2 | 2 | # Licensed under the Academic Free License version 3.0 |
| 3 | 3 | |
| 4 | 4 | require 'amazing' |
| 5 | require 'amazing/cli/commands' |
| 6 | require 'amazing/cli/helpers' |
| 7 | require 'amazing/cli/initializers' |
| 5 | 8 | require 'fileutils' |
| 6 | 9 | require 'logger' |
| 7 | 10 | require 'thread' |
| … | … | |
| 15 | 15 | |
| 16 | 16 | # Command line interface runner |
| 17 | 17 | # |
| 18 | | # CLI.run(ARGV) |
| 18 | # CLI.new(ARGV).run |
| 19 | 19 | class CLI |
| 20 | | def initialize(args) |
| 21 | | $KCODE = "utf-8" |
| 20 | include Initializers |
| 21 | include Helpers |
| 22 | include Commands |
| 23 | |
| 24 | def initialize(args=ARGV) |
| 22 | 25 | @args = args |
| 23 | | @log = Logger.new(STDOUT) |
| 24 | | @options = Options.new(@args) |
| 25 | | begin |
| 26 | | @display = X11::DisplayName.new |
| 27 | | rescue X11::EmptyDisplayName => e |
| 28 | | @log.warn("#{e.message}, falling back on :0") |
| 29 | | @display = X11::DisplayName.new(":0") |
| 30 | | rescue X11::InvalidDisplayName => e |
| 31 | | @log.fatal("#{e.message}, exiting") |
| 32 | | exit 1 |
| 33 | | end |
| 34 | | @threads = [] |
| 26 | initialize_threads |
| 27 | initialize_encoding |
| 28 | initialize_logger |
| 29 | initialize_options |
| 30 | initialize_display |
| 31 | initialize_awesome |
| 32 | initialize_exit |
| 35 | 33 | end |
| 36 | 34 | |
| 37 | 35 | def run |
| 38 | | at_exit { Thread.list.each {|t| t.exit } } |
| 39 | | trap("SIGINT") do |
| 40 | | @log.fatal("Received SIGINT, exiting") |
| 41 | | remove_pid |
| 42 | | exit |
| 43 | | end |
| 44 | | @options.parse |
| 45 | | show_help if @options[:help] |
| 36 | parse_options |
| 37 | cmd_show_help |
| 46 | 38 | set_loglevel |
| 47 | | stop_process(true) if @options[:stop] |
| 39 | cmd_stop_process |
| 48 | 40 | load_scripts |
| 49 | | list_widgets if @options[:listwidgets] |
| 50 | | test_widget if @options[:test] |
| 41 | cmd_list_widgets |
| 42 | cmd_test_widget |
| 51 | 43 | parse_config |
| 52 | 44 | wait_for_sockets |
| 53 | | @awesome = Awesome.new(@display.display) |
| 54 | | explicit_updates unless @options[:update] == [] |
| 45 | cmd_explicit_updates |
| 55 | 46 | stop_process |
| 56 | 47 | save_pid |
| 57 | | @threads << Thread.new { update_non_interval } |
| 58 | | @config[:awesome].each do |awesome| |
| 59 | | awesome[:widgets].each do |widget| |
| 60 | | if widget[:interval] |
| 61 | | @threads << Thread.new(awesome, widget) do |awesome, widget| |
| 62 | | iteration = 1 |
| 63 | | loop do |
| 64 | | Thread.new { update_widget(awesome[:screen], awesome[:statusbar], widget, iteration) } |
| 65 | | iteration += 1 |
| 66 | | sleep widget[:interval] |
| 67 | | end |
| 68 | | end |
| 69 | | end |
| 70 | | end |
| 71 | | end |
| 72 | | @threads.each {|t| t.join } |
| 73 | | end |
| 74 | | |
| 75 | | private |
| 76 | | |
| 77 | | def show_help |
| 78 | | puts @options.help |
| 79 | | exit |
| 80 | | end |
| 81 | | |
| 82 | | def set_loglevel |
| 83 | | begin |
| 84 | | @log.level = Logger.const_get(@options[:loglevel].upcase) |
| 85 | | rescue NameError |
| 86 | | @log.error("Unsupported log level #{@options[:loglevel].inspect}") |
| 87 | | @log.level = Logger::INFO |
| 88 | | end |
| 89 | | end |
| 90 | | |
| 91 | | def stop_process(quit=false) |
| 92 | | begin |
| 93 | | Process.kill("SIGINT", File.read("#{ENV["HOME"]}/.amazing/pids/#{@display.display}.pid").to_i) |
| 94 | | @log.warn("Killed older process") unless quit |
| 95 | | rescue |
| 96 | | end |
| 97 | | exit if quit |
| 98 | | end |
| 99 | | |
| 100 | | def load_scripts |
| 101 | | scripts = @options[:include] |
| 102 | | if @options[:autoinclude] |
| 103 | | scripts << Dir["#{ENV["HOME"]}/.amazing/widgets/*"] |
| 104 | | end |
| 105 | | scripts.flatten.each do |script| |
| 106 | | if File.exist?(script) |
| 107 | | @log.debug("Loading script #{script.inspect}") |
| 108 | | begin |
| 109 | | Widgets.module_eval(File.read(script), script) |
| 110 | | rescue SyntaxError => e |
| 111 | | @log.error("Bad syntax in #{script} at line #{e.to_s.scan(/:(\d+)/)}") |
| 112 | | end |
| 113 | | else |
| 114 | | @log.error("No such widget script #{script.inspect}") |
| 115 | | end |
| 116 | | end |
| 117 | | end |
| 118 | | |
| 119 | | def parse_config |
| 120 | | @log.debug("Parsing configuration file") |
| 121 | | begin |
| 122 | | @config = Config.new(@options[:config]) |
| 123 | | rescue |
| 124 | | @log.fatal("Unable to parse configuration file, exiting") |
| 125 | | exit 1 |
| 126 | | end |
| 127 | | end |
| 128 | | |
| 129 | | def list_widgets |
| 130 | | if @options[:listwidgets] == true |
| 131 | | longest_widget_name = Widgets.constants.inject {|a,b| a.length > b.length ? a : b }.length |
| 132 | | Widgets.constants.sort.each do |widget| |
| 133 | | widget_class = Widgets.const_get(widget) |
| 134 | | puts "%-#{longest_widget_name}s : %s" % [widget, widget_class.description] |
| 135 | | end |
| 136 | | else |
| 137 | | widget_class = Widgets.const_get(@options[:listwidgets].camel_case) |
| 138 | | puts |
| 139 | | puts "#{@options[:listwidgets].camel_case} - #{widget_class.description}" |
| 140 | | puts |
| 141 | | dependencies = widget_class.dependencies |
| 142 | | unless dependencies.empty? |
| 143 | | longest_dependency_name = dependencies.keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length |
| 144 | | longest_dependency_name = 10 if longest_dependency_name < 10 |
| 145 | | longest_description = dependencies.values.inject {|a,b| a.length > b.length ? a : b }.length |
| 146 | | longest_description = 11 if longest_description < 11 |
| 147 | | puts " %-#{longest_dependency_name}s | DESCRIPTION" % "DEPENDENCY" |
| 148 | | puts "-" * (longest_dependency_name + longest_description + 5) |
| 149 | | dependencies.keys.sort.each do |dependency| |
| 150 | | puts " %-#{longest_dependency_name}s | #{dependencies[dependency]}" % dependency |
| 151 | | end |
| 152 | | puts |
| 153 | | end |
| 154 | | options = widget_class.options |
| 155 | | unless options.empty? |
| 156 | | longest_option_name = options.keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length |
| 157 | | longest_option_name = 6 if longest_option_name < 6 |
| 158 | | longest_description = options.values.inject {|a,b| a[:description].length > b[:description].length ? a : b }[:description].length |
| 159 | | longest_description = 11 if longest_description < 11 |
| 160 | | longest_default = options.values.inject {|a,b| a[:default].inspect.length > b[:default].inspect.length ? a : b }[:default].inspect.length |
| 161 | | longest_default = 7 if longest_default < 7 |
| 162 | | puts " %-#{longest_option_name}s | %-#{longest_description}s | DEFAULT" % ["OPTION", "DESCRIPTION"] |
| 163 | | puts "-" * (longest_option_name + longest_description + longest_default + 8) |
| 164 | | options.keys.sort_by {|option| option.to_s }.each do |option| |
| 165 | | puts " %-#{longest_option_name}s | %-#{longest_description}s | %s" % [option, options[option][:description], options[option][:default].inspect] |
| 166 | | end |
| 167 | | puts |
| 168 | | end |
| 169 | | fields = widget_class.fields |
| 170 | | unless fields.empty? |
| 171 | | longest_field_name = fields.keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length |
| 172 | | longest_field_name = 5 if longest_field_name < 5 |
| 173 | | longest_description = fields.values.inject {|a,b| a[:description].length > b[:description].length ? a : b }[:description].length |
| 174 | | longest_description = 11 if longest_description < 11 |
| 175 | | longest_default = fields.values.inject {|a,b| a[:default].inspect.length > b[:default].inspect.length ? a : b }[:default].inspect.length |
| 176 | | longest_default = 7 if longest_default < 7 |
| 177 | | puts " %-#{longest_field_name + 1}s | %-#{longest_description}s | DEFAULT" % ["FIELD", "DESCRIPTION"] |
| 178 | | puts "-" * (longest_field_name + longest_description + longest_default + 9) |
| 179 | | fields.keys.sort_by {|field| field.to_s }.each do |field| |
| 180 | | puts " @%-#{longest_field_name}s | %-#{longest_description}s | %s" % [field, fields[field][:description], fields[field][:default].inspect] |
| 181 | | end |
| 182 | | puts |
| 183 | | end |
| 184 | | end |
| 185 | | exit |
| 186 | | end |
| 187 | | |
| 188 | | def test_widget |
| 189 | | widget = Widgets.const_get(@options[:test].camel_case) |
| 190 | | settings = YAML.load("{#{ARGV[0]}}") |
| 191 | | instance = widget.new(settings) |
| 192 | | longest_field_name = widget.fields.merge({:default => nil}).keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length |
| 193 | | puts "@%-#{longest_field_name}s = %s" % [:default, instance.instance_variable_get(:@default).inspect] |
| 194 | | widget.fields.keys.sort_by {|field| field.to_s }.each do |field| |
| 195 | | puts "@%-#{longest_field_name}s = %s" % [field, instance.instance_variable_get("@#{field}".to_sym).inspect] |
| 196 | | end |
| 197 | | exit |
| 198 | | end |
| 199 | | |
| 200 | | def wait_for_sockets |
| 201 | | @log.debug("Waiting for awesome control socket for display #{@display.display}") |
| 202 | | begin |
| 203 | | Timeout.timeout(30) do |
| 204 | | sleep 1 until File.exist?("#{ENV["HOME"]}/.awesome_ctl.#{@display.display}") |
| 205 | | @log.debug("Got socket for display #{@display.display}") |
| 206 | | end |
| 207 | | rescue Timeout::Error |
| 208 | | @log.fatal("Socket for display #{@display.display} not created within 30 seconds, exiting") |
| 209 | | exit 1 |
| 210 | | end |
| 211 | | end |
| 212 | | |
| 213 | | def explicit_updates |
| 214 | | @config[:awesome].each do |awesome| |
| 215 | | awesome[:widgets].each do |widget| |
| 216 | | locator = "%s/%s/%s" % [widget[:identifier], awesome[:statusbar], awesome[:screen]] |
| 217 | | next unless @options[:update] == :all || @options[:update].include?(locator) |
| 218 | | @threads << Thread.new(awesome, widget) do |awesome, widget| |
| 219 | | update_widget(awesome[:screen], awesome[:statusbar], widget) |
| 220 | | end |
| 221 | | end |
| 222 | | end |
| 223 | | @threads.each {|t| t.join } |
| 224 | | exit |
| 225 | | end |
| 226 | | |
| 227 | | def save_pid |
| 228 | | path = "#{ENV["HOME"]}/.amazing/pids" |
| 229 | | FileUtils.makedirs(path) |
| 230 | | File.open("#{path}/#{@display.display}.pid", "w+") do |f| |
| 231 | | f.write($$) |
| 232 | | end |
| 233 | | end |
| 234 | | |
| 235 | | def remove_pid |
| 236 | | File.delete("#{ENV["HOME"]}/.amazing/pids/#{@display.display}.pid") rescue Errno::ENOENT |
| 237 | | end |
| 238 | | |
| 239 | | def update_non_interval |
| 240 | | @config[:awesome].each do |awesome| |
| 241 | | awesome[:widgets].each do |widget| |
| 242 | | next if widget[:interval] |
| 243 | | @threads << Thread.new(awesome, widget) do |awesome, widget| |
| 244 | | update_widget(awesome[:screen], awesome[:statusbar], widget) |
| 245 | | end |
| 246 | | end |
| 247 | | end |
| 248 | | end |
| 249 | | |
| 250 | | def update_widget(screen, statusbar, widget, iteration=0) |
| 251 | | threads = [] |
| 252 | | @log.debug("Updating widget #{widget[:identifier]} of type #{widget[:module]} on screen #{screen}") |
| 253 | | begin |
| 254 | | mod = Widgets.const_get(widget[:module]).new(widget.merge(:iteration => iteration)) |
| 255 | | if widget[:properties].empty? |
| 256 | | threads << Thread.new(screen, statusbar, widget, mod) do |screen, statusbar, widget, mod| |
| 257 | | @awesome.widget_tell(screen, statusbar, widget[:identifier], widget[:property], mod.formatize) |
| 258 | | end |
| 259 | | end |
| 260 | | widget[:properties].each do |property, format| |
| 261 | | threads << Thread.new(screen, statusbar, widget, property, mod, format) do |screen, statusbar, widget, property, mod, format| |
| 262 | | @awesome.widget_tell(screen, statusbar, widget[:identifier], property, mod.formatize(format)) |
| 263 | | end |
| 264 | | end |
| 265 | | rescue WidgetError => e |
| 266 | | @log.error(widget[:module]) { e.message } |
| 267 | | end |
| 268 | | threads.each {|t| t.join } |
| 48 | set_traps |
| 49 | update_non_interval |
| 50 | cmd_main |
| 51 | join_threads |
| 269 | 52 | end |
| 270 | 53 | end |
| 271 | 54 | end |
| toggle raw diff |
--- a/lib/amazing/cli.rb
+++ b/lib/amazing/cli.rb
@@ -2,6 +2,9 @@
# Licensed under the Academic Free License version 3.0
require 'amazing'
+require 'amazing/cli/commands'
+require 'amazing/cli/helpers'
+require 'amazing/cli/initializers'
require 'fileutils'
require 'logger'
require 'thread'
@@ -12,257 +15,40 @@ module Amazing
# Command line interface runner
#
- # CLI.run(ARGV)
+ # CLI.new(ARGV).run
class CLI
- def initialize(args)
- $KCODE = "utf-8"
+ include Initializers
+ include Helpers
+ include Commands
+
+ def initialize(args=ARGV)
@args = args
- @log = Logger.new(STDOUT)
- @options = Options.new(@args)
- begin
- @display = X11::DisplayName.new
- rescue X11::EmptyDisplayName => e
- @log.warn("#{e.message}, falling back on :0")
- @display = X11::DisplayName.new(":0")
- rescue X11::InvalidDisplayName => e
- @log.fatal("#{e.message}, exiting")
- exit 1
- end
- @threads = []
+ initialize_threads
+ initialize_encoding
+ initialize_logger
+ initialize_options
+ initialize_display
+ initialize_awesome
+ initialize_exit
end
def run
- at_exit { Thread.list.each {|t| t.exit } }
- trap("SIGINT") do
- @log.fatal("Received SIGINT, exiting")
- remove_pid
- exit
- end
- @options.parse
- show_help if @options[:help]
+ parse_options
+ cmd_show_help
set_loglevel
- stop_process(true) if @options[:stop]
+ cmd_stop_process
load_scripts
- list_widgets if @options[:listwidgets]
- test_widget if @options[:test]
+ cmd_list_widgets
+ cmd_test_widget
parse_config
wait_for_sockets
- @awesome = Awesome.new(@display.display)
- explicit_updates unless @options[:update] == []
+ cmd_explicit_updates
stop_process
save_pid
- @threads << Thread.new { update_non_interval }
- @config[:awesome].each do |awesome|
- awesome[:widgets].each do |widget|
- if widget[:interval]
- @threads << Thread.new(awesome, widget) do |awesome, widget|
- iteration = 1
- loop do
- Thread.new { update_widget(awesome[:screen], awesome[:statusbar], widget, iteration) }
- iteration += 1
- sleep widget[:interval]
- end
- end
- end
- end
- end
- @threads.each {|t| t.join }
- end
-
- private
-
- def show_help
- puts @options.help
- exit
- end
-
- def set_loglevel
- begin
- @log.level = Logger.const_get(@options[:loglevel].upcase)
- rescue NameError
- @log.error("Unsupported log level #{@options[:loglevel].inspect}")
- @log.level = Logger::INFO
- end
- end
-
- def stop_process(quit=false)
- begin
- Process.kill("SIGINT", File.read("#{ENV["HOME"]}/.amazing/pids/#{@display.display}.pid").to_i)
- @log.warn("Killed older process") unless quit
- rescue
- end
- exit if quit
- end
-
- def load_scripts
- scripts = @options[:include]
- if @options[:autoinclude]
- scripts << Dir["#{ENV["HOME"]}/.amazing/widgets/*"]
- end
- scripts.flatten.each do |script|
- if File.exist?(script)
- @log.debug("Loading script #{script.inspect}")
- begin
- Widgets.module_eval(File.read(script), script)
- rescue SyntaxError => e
- @log.error("Bad syntax in #{script} at line #{e.to_s.scan(/:(\d+)/)}")
- end
- else
- @log.error("No such widget script #{script.inspect}")
- end
- end
- end
-
- def parse_config
- @log.debug("Parsing configuration file")
- begin
- @config = Config.new(@options[:config])
- rescue
- @log.fatal("Unable to parse configuration file, exiting")
- exit 1
- end
- end
-
- def list_widgets
- if @options[:listwidgets] == true
- longest_widget_name = Widgets.constants.inject {|a,b| a.length > b.length ? a : b }.length
- Widgets.constants.sort.each do |widget|
- widget_class = Widgets.const_get(widget)
- puts "%-#{longest_widget_name}s : %s" % [widget, widget_class.description]
- end
- else
- widget_class = Widgets.const_get(@options[:listwidgets].camel_case)
- puts
- puts "#{@options[:listwidgets].camel_case} - #{widget_class.description}"
- puts
- dependencies = widget_class.dependencies
- unless dependencies.empty?
- longest_dependency_name = dependencies.keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length
- longest_dependency_name = 10 if longest_dependency_name < 10
- longest_description = dependencies.values.inject {|a,b| a.length > b.length ? a : b }.length
- longest_description = 11 if longest_description < 11
- puts " %-#{longest_dependency_name}s | DESCRIPTION" % "DEPENDENCY"
- puts "-" * (longest_dependency_name + longest_description + 5)
- dependencies.keys.sort.each do |dependency|
- puts " %-#{longest_dependency_name}s | #{dependencies[dependency]}" % dependency
- end
- puts
- end
- options = widget_class.options
- unless options.empty?
- longest_option_name = options.keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length
- longest_option_name = 6 if longest_option_name < 6
- longest_description = options.values.inject {|a,b| a[:description].length > b[:description].length ? a : b }[:description].length
- longest_description = 11 if longest_description < 11
- longest_default = options.values.inject {|a,b| a[:default].inspect.length > b[:default].inspect.length ? a : b }[:default].inspect.length
- longest_default = 7 if longest_default < 7
- puts " %-#{longest_option_name}s | %-#{longest_description}s | DEFAULT" % ["OPTION", "DESCRIPTION"]
- puts "-" * (longest_option_name + longest_description + longest_default + 8)
- options.keys.sort_by {|option| option.to_s }.each do |option|
- puts " %-#{longest_option_name}s | %-#{longest_description}s | %s" % [option, options[option][:description], options[option][:default].inspect]
- end
- puts
- end
- fields = widget_class.fields
- unless fields.empty?
- longest_field_name = fields.keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length
- longest_field_name = 5 if longest_field_name < 5
- longest_description = fields.values.inject {|a,b| a[:description].length > b[:description].length ? a : b }[:description].length
- longest_description = 11 if longest_description < 11
- longest_default = fields.values.inject {|a,b| a[:default].inspect.length > b[:default].inspect.length ? a : b }[:default].inspect.length
- longest_default = 7 if longest_default < 7
- puts " %-#{longest_field_name + 1}s | %-#{longest_description}s | DEFAULT" % ["FIELD", "DESCRIPTION"]
- puts "-" * (longest_field_name + longest_description + longest_default + 9)
- fields.keys.sort_by {|field| field.to_s }.each do |field|
- puts " @%-#{longest_field_name}s | %-#{longest_description}s | %s" % [field, fields[field][:description], fields[field][:default].inspect]
- end
- puts
- end
- end
- exit
- end
-
- def test_widget
- widget = Widgets.const_get(@options[:test].camel_case)
- settings = YAML.load("{#{ARGV[0]}}")
- instance = widget.new(settings)
- longest_field_name = widget.fields.merge({:default => nil}).keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length
- puts "@%-#{longest_field_name}s = %s" % [:default, instance.instance_variable_get(:@default).inspect]
- widget.fields.keys.sort_by {|field| field.to_s }.each do |field|
- puts "@%-#{longest_field_name}s = %s" % [field, instance.instance_variable_get("@#{field}".to_sym).inspect]
- end
- exit
- end
-
- def wait_for_sockets
- @log.debug("Waiting for awesome control socket for display #{@display.display}")
- begin
- Timeout.timeout(30) do
- sleep 1 until File.exist?("#{ENV["HOME"]}/.awesome_ctl.#{@display.display}")
- @log.debug("Got socket for display #{@display.display}")
- end
- rescue Timeout::Error
- @log.fatal("Socket for display #{@display.display} not created within 30 seconds, exiting")
- exit 1
- end
- end
-
- def explicit_updates
- @config[:awesome].each do |awesome|
- awesome[:widgets].each do |widget|
- locator = "%s/%s/%s" % [widget[:identifier], awesome[:statusbar], awesome[:screen]]
- next unless @options[:update] == :all || @options[:update].include?(locator)
- @threads << Thread.new(awesome, widget) do |awesome, widget|
- update_widget(awesome[:screen], awesome[:statusbar], widget)
- end
- end
- end
- @threads.each {|t| t.join }
- exit
- end
-
- def save_pid
- path = "#{ENV["HOME"]}/.amazing/pids"
- FileUtils.makedirs(path)
- File.open("#{path}/#{@display.display}.pid", "w+") do |f|
- f.write($$)
- end
- end
-
- def remove_pid
- File.delete("#{ENV["HOME"]}/.amazing/pids/#{@display.display}.pid") rescue Errno::ENOENT
- end
-
- def update_non_interval
- @config[:awesome].each do |awesome|
- awesome[:widgets].each do |widget|
- next if widget[:interval]
- @threads << Thread.new(awesome, widget) do |awesome, widget|
- update_widget(awesome[:screen], awesome[:statusbar], widget)
- end
- end
- end
- end
-
- def update_widget(screen, statusbar, widget, iteration=0)
- threads = []
- @log.debug("Updating widget #{widget[:identifier]} of type #{widget[:module]} on screen #{screen}")
- begin
- mod = Widgets.const_get(widget[:module]).new(widget.merge(:iteration => iteration))
- if widget[:properties].empty?
- threads << Thread.new(screen, statusbar, widget, mod) do |screen, statusbar, widget, mod|
- @awesome.widget_tell(screen, statusbar, widget[:identifier], widget[:property], mod.formatize)
- end
- end
- widget[:properties].each do |property, format|
- threads << Thread.new(screen, statusbar, widget, property, mod, format) do |screen, statusbar, widget, property, mod, format|
- @awesome.widget_tell(screen, statusbar, widget[:identifier], property, mod.formatize(format))
- end
- end
- rescue WidgetError => e
- @log.error(widget[:module]) { e.message }
- end
- threads.each {|t| t.join }
+ set_traps
+ update_non_interval
+ cmd_main
+ join_threads
end
end
end |
| |   |
| 1 | # Copyright (C) 2008 Dag Odenhall <dag.odenhall@gmail.com> |
| 2 | # Licensed under the Academic Free License version 3.0 |
| 3 | |
| 4 | module Amazing |
| 5 | class CLI |
| 6 | module Commands |
| 7 | private |
| 8 | |
| 9 | def cmd_show_help |
| 10 | if @options[:help] |
| 11 | puts @options.help |
| 12 | exit |
| 13 | end |
| 14 | end |
| 15 | |
| 16 | def cmd_stop_process |
| 17 | if @options[:stop] |
| 18 | stop_process(true) |
| 19 | exit |
| 20 | end |
| 21 | end |
| 22 | |
| 23 | def cmd_list_widgets |
| 24 | if @options[:listwidgets] |
| 25 | if @options[:listwidgets] == true |
| 26 | longest_widget_name = Widgets.constants.inject {|a,b| a.length > b.length ? a : b }.length |
| 27 | |
| 28 | Widgets.constants.sort.each do |widget| |
| 29 | widget_class = Widgets.const_get(widget) |
| 30 | puts "%-#{longest_widget_name}s : %s" % [widget, widget_class.description] |
| 31 | end |
| 32 | |
| 33 | else |
| 34 | widget_class = Widgets.const_get(@options[:listwidgets].camel_case) |
| 35 | |
| 36 | puts |
| 37 | puts "#{@options[:listwidgets].camel_case} - #{widget_class.description}" |
| 38 | puts |
| 39 | |
| 40 | dependencies = widget_class.dependencies |
| 41 | unless dependencies.empty? |
| 42 | longest_dependency_name = dependencies.keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length |
| 43 | longest_dependency_name = 10 if longest_dependency_name < 10 |
| 44 | longest_description = dependencies.values.inject {|a,b| a.length > b.length ? a : b }.length |
| 45 | longest_description = 11 if longest_description < 11 |
| 46 | |
| 47 | puts " %-#{longest_dependency_name}s | DESCRIPTION" % "DEPENDENCY" |
| 48 | puts "-" * (longest_dependency_name + longest_description + 5) |
| 49 | |
| 50 | dependencies.keys.sort.each do |dependency| |
| 51 | puts " %-#{longest_dependency_name}s | #{dependencies[dependency]}" % dependency |
| 52 | end |
| 53 | |
| 54 | puts |
| 55 | end |
| 56 | |
| 57 | options = widget_class.options |
| 58 | |
| 59 | unless options.empty? |
| 60 | longest_option_name = options.keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length |
| 61 | longest_option_name = 6 if longest_option_name < 6 |
| 62 | longest_description = options.values.inject {|a,b| a[:description].length > b[:description].length ? a : b }[:description].length |
| 63 | longest_description = 11 if longest_description < 11 |
| 64 | longest_default = options.values.inject {|a,b| a[:default].inspect.length > b[:default].inspect.length ? a : b }[:default].inspect.length |
| 65 | longest_default = 7 if longest_default < 7 |
| 66 | |
| 67 | puts " %-#{longest_option_name}s | %-#{longest_description}s | DEFAULT" % ["OPTION", "DESCRIPTION"] |
| 68 | puts "-" * (longest_option_name + longest_description + longest_default + 8) |
| 69 | |
| 70 | options.keys.sort_by {|option| option.to_s }.each do |option| |
| 71 | puts " %-#{longest_option_name}s | %-#{longest_description}s | %s" % [option, options[option][:description], options[option][:default].inspect] |
| 72 | end |
| 73 | |
| 74 | puts |
| 75 | end |
| 76 | |
| 77 | fields = widget_class.fields |
| 78 | |
| 79 | unless fields.empty? |
| 80 | longest_field_name = fields.keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length |
| 81 | longest_field_name = 5 if longest_field_name < 5 |
| 82 | longest_description = fields.values.inject {|a,b| a[:description].length > b[:description].length ? a : b }[:description].length |
| 83 | longest_description = 11 if longest_description < 11 |
| 84 | longest_default = fields.values.inject {|a,b| a[:default].inspect.length > b[:default].inspect.length ? a : b }[:default].inspect.length |
| 85 | longest_default = 7 if longest_default < 7 |
| 86 | |
| 87 | puts " %-#{longest_field_name + 1}s | %-#{longest_description}s | DEFAULT" % ["FIELD", "DESCRIPTION"] |
| 88 | puts "-" * (longest_field_name + longest_description + longest_default + 9) |
| 89 | |
| 90 | fields.keys.sort_by {|field| field.to_s }.each do |field| |
| 91 | puts " @%-#{longest_field_name}s | %-#{longest_description}s | %s" % [field, fields[field][:description], fields[field][:default].inspect] |
| 92 | end |
| 93 | |
| 94 | puts |
| 95 | end |
| 96 | end |
| 97 | |
| 98 | exit |
| 99 | end |
| 100 | end |
| 101 | |
| 102 | def cmd_test_widget |
| 103 | if @options[:test] |
| 104 | widget = Widgets.const_get(@options[:test].camel_case) |
| 105 | settings = YAML.load("{#{ARGV[0]}}") |
| 106 | instance = widget.new(settings) |
| 107 | longest_field_name = widget.fields.merge({:default => nil}).keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length |
| 108 | |
| 109 | puts "@%-#{longest_field_name}s = %s" % [:default, instance.instance_variable_get(:@default).inspect] |
| 110 | |
| 111 | widget.fields.keys.sort_by {|field| field.to_s }.each do |field| |
| 112 | puts "@%-#{longest_field_name}s = %s" % [field, instance.instance_variable_get("@#{field}".to_sym).inspect] |
| 113 | end |
| 114 | |
| 115 | exit |
| 116 | end |
| 117 | end |
| 118 | |
| 119 | def cmd_explicit_updates |
| 120 | if @options[:update] != [] |
| 121 | @config[:awesome].each do |awesome| |
| 122 | awesome[:widgets].each do |widget| |
| 123 | locator = "%s/%s/%s" % [widget[:identifier], awesome[:statusbar], awesome[:screen]] |
| 124 | next unless @options[:update] == :all || @options[:update].include?(locator) |
| 125 | |
| 126 | @threads << Thread.new(awesome, widget) do |awesome, widget| |
| 127 | update_widget(awesome[:screen], awesome[:statusbar], widget) |
| 128 | end |
| 129 | end |
| 130 | end |
| 131 | |
| 132 | @threads.each {|t| t.join } |
| 133 | exit |
| 134 | end |
| 135 | end |
| 136 | |
| 137 | def cmd_main |
| 138 | @config[:awesome].each do |awesome| |
| 139 | awesome[:widgets].each do |widget| |
| 140 | if widget[:interval] |
| 141 | @threads << Thread.new(awesome, widget) do |awesome, widget| |
| 142 | iteration = 1 |
| 143 | |
| 144 | loop do |
| 145 | Thread.new { update_widget(awesome[:screen], awesome[:statusbar], widget, iteration) } |
| 146 | |
| 147 | iteration += 1 |
| 148 | sleep widget[:interval] |
| 149 | end |
| 150 | end |
| 151 | end |
| 152 | end |
| 153 | end |
| 154 | end |
| 155 | end |
| 156 | end |
| 157 | end |
| toggle raw diff |
--- /dev/null
+++ b/lib/amazing/cli/commands.rb
@@ -0,0 +1,157 @@
+# Copyright (C) 2008 Dag Odenhall <dag.odenhall@gmail.com>
+# Licensed under the Academic Free License version 3.0
+
+module Amazing
+ class CLI
+ module Commands
+ private
+
+ def cmd_show_help
+ if @options[:help]
+ puts @options.help
+ exit
+ end
+ end
+
+ def cmd_stop_process
+ if @options[:stop]
+ stop_process(true)
+ exit
+ end
+ end
+
+ def cmd_list_widgets
+ if @options[:listwidgets]
+ if @options[:listwidgets] == true
+ longest_widget_name = Widgets.constants.inject {|a,b| a.length > b.length ? a : b }.length
+
+ Widgets.constants.sort.each do |widget|
+ widget_class = Widgets.const_get(widget)
+ puts "%-#{longest_widget_name}s : %s" % [widget, widget_class.description]
+ end
+
+ else
+ widget_class = Widgets.const_get(@options[:listwidgets].camel_case)
+
+ puts
+ puts "#{@options[:listwidgets].camel_case} - #{widget_class.description}"
+ puts
+
+ dependencies = widget_class.dependencies
+ unless dependencies.empty?
+ longest_dependency_name = dependencies.keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length
+ longest_dependency_name = 10 if longest_dependency_name < 10
+ longest_description = dependencies.values.inject {|a,b| a.length > b.length ? a : b }.length
+ longest_description = 11 if longest_description < 11
+
+ puts " %-#{longest_dependency_name}s | DESCRIPTION" % "DEPENDENCY"
+ puts "-" * (longest_dependency_name + longest_description + 5)
+
+ dependencies.keys.sort.each do |dependency|
+ puts " %-#{longest_dependency_name}s | #{dependencies[dependency]}" % dependency
+ end
+
+ puts
+ end
+
+ options = widget_class.options
+
+ unless options.empty?
+ longest_option_name = options.keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length
+ longest_option_name = 6 if longest_option_name < 6
+ longest_description = options.values.inject {|a,b| a[:description].length > b[:description].length ? a : b }[:description].length
+ longest_description = 11 if longest_description < 11
+ longest_default = options.values.inject {|a,b| a[:default].inspect.length > b[:default].inspect.length ? a : b }[:default].inspect.length
+ longest_default = 7 if longest_default < 7
+
+ puts " %-#{longest_option_name}s | %-#{longest_description}s | DEFAULT" % ["OPTION", "DESCRIPTION"]
+ puts "-" * (longest_option_name + longest_description + longest_default + 8)
+
+ options.keys.sort_by {|option| option.to_s }.each do |option|
+ puts " %-#{longest_option_name}s | %-#{longest_description}s | %s" % [option, options[option][:description], options[option][:default].inspect]
+ end
+
+ puts
+ end
+
+ fields = widget_class.fields
+
+ unless fields.empty?
+ longest_field_name = fields.keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length
+ longest_field_name = 5 if longest_field_name < 5
+ longest_description = fields.values.inject {|a,b| a[:description].length > b[:description].length ? a : b }[:description].length
+ longest_description = 11 if longest_description < 11
+ longest_default = fields.values.inject {|a,b| a[:default].inspect.length > b[:default].inspect.length ? a : b }[:default].inspect.length
+ longest_default = 7 if longest_default < 7
+
+ puts " %-#{longest_field_name + 1}s | %-#{longest_description}s | DEFAULT" % ["FIELD", "DESCRIPTION"]
+ puts "-" * (longest_field_name + longest_description + longest_default + 9)
+
+ fields.keys.sort_by {|field| field.to_s }.each do |field|
+ puts " @%-#{longest_field_name}s | %-#{longest_description}s | %s" % [field, fields[field][:description], fields[field][:default].inspect]
+ end
+
+ puts
+ end
+ end
+
+ exit
+ end
+ end
+
+ def cmd_test_widget
+ if @options[:test]
+ widget = Widgets.const_get(@options[:test].camel_case)
+ settings = YAML.load("{#{ARGV[0]}}")
+ instance = widget.new(settings)
+ longest_field_name = widget.fields.merge({:default => nil}).keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length
+
+ puts "@%-#{longest_field_name}s = %s" % [:default, instance.instance_variable_get(:@default).inspect]
+
+ widget.fields.keys.sort_by {|field| field.to_s }.each do |field|
+ puts "@%-#{longest_field_name}s = %s" % [field, instance.instance_variable_get("@#{field}".to_sym).inspect]
+ end
+
+ exit
+ end
+ end
+
+ def cmd_explicit_updates
+ if @options[:update] != []
+ @config[:awesome].each do |awesome|
+ awesome[:widgets].each do |widget|
+ locator = "%s/%s/%s" % [widget[:identifier], awesome[:statusbar], awesome[:screen]]
+ next unless @options[:update] == :all || @options[:update].include?(locator)
+
+ @threads << Thread.new(awesome, widget) do |awesome, widget|
+ update_widget(awesome[:screen], awesome[:statusbar], widget)
+ end
+ end
+ end
+
+ @threads.each {|t| t.join }
+ exit
+ end
+ end
+
+ def cmd_main
+ @config[:awesome].each do |awesome|
+ awesome[:widgets].each do |widget|
+ if widget[:interval]
+ @threads << Thread.new(awesome, widget) do |awesome, widget|
+ iteration = 1
+
+ loop do
+ Thread.new { update_widget(awesome[:screen], awesome[:statusbar], widget, iteration) }
+
+ iteration += 1
+ sleep widget[:interval]
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end |
| |   |
| 1 | # Copyright (C) 2008 Dag Odenhall <dag.odenhall@gmail.com> |
| 2 | # Licensed under the Academic Free License version 3.0 |
| 3 | |
| 4 | module Amazing |
| 5 | class CLI |
| 6 | module Helpers |
| 7 | private |
| 8 | |
| 9 | def parse_options |
| 10 | @options.parse |
| 11 | end |
| 12 | |
| 13 | def set_loglevel |
| 14 | begin |
| 15 | @log.level = Logger.const_get(@options[:loglevel].upcase) |
| 16 | |
| 17 | rescue NameError |
| 18 | @log.error("Unsupported log level #{@options[:loglevel].inspect}") |
| 19 | @log.level = Logger::INFO |
| 20 | end |
| 21 | end |
| 22 | |
| 23 | def stop_process(log=true) |
| 24 | Process.kill("SIGINT", File.read("#{ENV["HOME"]}/.amazing/pids/#{@display.display}.pid").to_i) |
| 25 | @log.warn("Killed older process") if log |
| 26 | rescue |
| 27 | end |
| 28 | |
| 29 | def load_scripts |
| 30 | scripts = @options[:include] |
| 31 | |
| 32 | if @options[:autoinclude] |
| 33 | scripts << Dir["#{ENV["HOME"]}/.amazing/widgets/*"] |
| 34 | end |
| 35 | |
| 36 | scripts.flatten.each do |script| |
| 37 | if File.exist?(script) |
| 38 | @log.debug("Loading script #{script.inspect}") |
| 39 | |
| 40 | begin |
| 41 | Widgets.module_eval(File.read(script), script) |
| 42 | |
| 43 | rescue SyntaxError => e |
| 44 | @log.error("Bad syntax in #{script} at line #{e.to_s.scan(/:(\d+)/)}") |
| 45 | end |
| 46 | |
| 47 | else |
| 48 | @log.error("No such widget script #{script.inspect}") |
| 49 | end |
| 50 | end |
| 51 | end |
| 52 | |
| 53 | def parse_config |
| 54 | @log.debug("Parsing configuration file") |
| 55 | |
| 56 | begin |
| 57 | @config = Config.new(@options[:config]) |
| 58 | |
| 59 | rescue |
| 60 | @log.fatal("Unable to parse configuration file, exiting") |
| 61 | |
| 62 | exit 1 |
| 63 | end |
| 64 | end |
| 65 | |
| 66 | def wait_for_sockets |
| 67 | @log.debug("Waiting for awesome control socket for display #{@display.display}") |
| 68 | |
| 69 | begin |
| 70 | Timeout.timeout(30) do |
| 71 | sleep 1 until File.exist?("#{ENV["HOME"]}/.awesome_ctl.#{@display.display}") |
| 72 | @log.debug("Got socket for display #{@display.display}") |
| 73 | end |
| 74 | |
| 75 | rescue Timeout::Error |
| 76 | @log.fatal("Socket for display #{@display.display} not created within 30 seconds, exiting") |
| 77 | |
| 78 | exit 1 |
| 79 | end |
| 80 | end |
| 81 | |
| 82 | def save_pid |
| 83 | path = "#{ENV["HOME"]}/.amazing/pids" |
| 84 | FileUtils.makedirs(path) |
| 85 | |
| 86 | File.open("#{path}/#{@display.display}.pid", "w+") do |f| |
| 87 | f.write($$) |
| 88 | end |
| 89 | end |
| 90 | |
| 91 | def remove_pid |
| 92 | File.delete("#{ENV["HOME"]}/.amazing/pids/#{@display.display}.pid") rescue Errno::ENOENT |
| 93 | end |
| 94 | |
| 95 | def set_traps |
| 96 | trap("SIGINT") do |
| 97 | @log.fatal("Received SIGINT, exiting") |
| 98 | remove_pid |
| 99 | exit |
| 100 | end |
| 101 | end |
| 102 | |
| 103 | def update_non_interval |
| 104 | @threads << Thread.new do |
| 105 | @config[:awesome].each do |awesome| |
| 106 | awesome[:widgets].each do |widget| |
| 107 | next if widget[:interval] |
| 108 | |
| 109 | @threads << Thread.new(awesome, widget) do |awesome, widget| |
| 110 | update_widget(awesome[:screen], awesome[:statusbar], widget) |
| 111 | end |
| 112 | end |
| 113 | end |
| 114 | end |
| 115 | end |
| 116 | |
| 117 | def update_widget(screen, statusbar, widget, iteration=0) |
| 118 | threads = [] |
| 119 | @log.debug("Updating widget #{widget[:identifier]} of type #{widget[:module]} on screen #{screen}") |
| 120 | |
| 121 | begin |
| 122 | mod = Widgets.const_get(widget[:module]).new(widget.merge(:iteration => iteration)) |
| 123 | |
| 124 | if widget[:properties].empty? |
| 125 | threads << Thread.new(screen, statusbar, widget, mod) do |screen, statusbar, widget, mod| |
| 126 | @awesome.widget_tell(screen, statusbar, widget[:identifier], widget[:property], mod.formatize) |
| 127 | end |
| 128 | end |
| 129 | |
| 130 | widget[:properties].each do |property, format| |
| 131 | threads << Thread.new(screen, statusbar, widget, property, mod, format) do |screen, statusbar, widget, property, mod, format| |
| 132 | @awesome.widget_tell(screen, statusbar, widget[:identifier], property, mod.formatize(format)) |
| 133 | end |
| 134 | end |
| 135 | |
| 136 | rescue WidgetError => e |
| 137 | @log.error(widget[:module]) { e.message } |
| 138 | end |
| 139 | |
| 140 | threads.each {|t| t.join } |
| 141 | end |
| 142 | |
| 143 | def join_threads |
| 144 | @threads.each {|t| t.join } |
| 145 | end |
| 146 | end |
| 147 | end |
| 148 | end |
| toggle raw diff |
--- /dev/null
+++ b/lib/amazing/cli/helpers.rb
@@ -0,0 +1,148 @@
+# Copyright (C) 2008 Dag Odenhall <dag.odenhall@gmail.com>
+# Licensed under the Academic Free License version 3.0
+
+module Amazing
+ class CLI
+ module Helpers
+ private
+
+ def parse_options
+ @options.parse
+ end
+
+ def set_loglevel
+ begin
+ @log.level = Logger.const_get(@options[:loglevel].upcase)
+
+ rescue NameError
+ @log.error("Unsupported log level #{@options[:loglevel].inspect}")
+ @log.level = Logger::INFO
+ end
+ end
+
+ def stop_process(log=true)
+ Process.kill("SIGINT", File.read("#{ENV["HOME"]}/.amazing/pids/#{@display.display}.pid").to_i)
+ @log.warn("Killed older process") if log
+ rescue
+ end
+
+ def load_scripts
+ scripts = @options[:include]
+
+ if @options[:autoinclude]
+ scripts << Dir["#{ENV["HOME"]}/.amazing/widgets/*"]
+ end
+
+ scripts.flatten.each do |script|
+ if File.exist?(script)
+ @log.debug("Loading script #{script.inspect}")
+
+ begin
+ Widgets.module_eval(File.read(script), script)
+
+ rescue SyntaxError => e
+ @log.error("Bad syntax in #{script} at line #{e.to_s.scan(/:(\d+)/)}")
+ end
+
+ else
+ @log.error("No such widget script #{script.inspect}")
+ end
+ end
+ end
+
+ def parse_config
+ @log.debug("Parsing configuration file")
+
+ begin
+ @config = Config.new(@options[:config])
+
+ rescue
+ @log.fatal("Unable to parse configuration file, exiting")
+
+ exit 1
+ end
+ end
+
+ def wait_for_sockets
+ @log.debug("Waiting for awesome control socket for display #{@display.display}")
+
+ begin
+ Timeout.timeout(30) do
+ sleep 1 until File.exist?("#{ENV["HOME"]}/.awesome_ctl.#{@display.display}")
+ @log.debug("Got socket for display #{@display.display}")
+ end
+
+ rescue Timeout::Error
+ @log.fatal("Socket for display #{@display.display} not created within 30 seconds, exiting")
+
+ exit 1
+ end
+ end
+
+ def save_pid
+ path = "#{ENV["HOME"]}/.amazing/pids"
+ FileUtils.makedirs(path)
+
+ File.open("#{path}/#{@display.display}.pid", "w+") do |f|
+ f.write($$)
+ end
+ end
+
+ def remove_pid
+ File.delete("#{ENV["HOME"]}/.amazing/pids/#{@display.display}.pid") rescue Errno::ENOENT
+ end
+
+ def set_traps
+ trap("SIGINT") do
+ @log.fatal("Received SIGINT, exiting")
+ remove_pid
+ exit
+ end
+ end
+
+ def update_non_interval
+ @threads << Thread.new do
+ @config[:awesome].each do |awesome|
+ awesome[:widgets].each do |widget|
+ next if widget[:interval]
+
+ @threads << Thread.new(awesome, widget) do |awesome, widget|
+ update_widget(awesome[:screen], awesome[:statusbar], widget)
+ end
+ end
+ end
+ end
+ end
+
+ def update_widget(screen, statusbar, widget, iteration=0)
+ threads = []
+ @log.debug("Updating widget #{widget[:identifier]} of type #{widget[:module]} on screen #{screen}")
+
+ begin
+ mod = Widgets.const_get(widget[:module]).new(widget.merge(:iteration => iteration))
+
+ if widget[:properties].empty?
+ threads << Thread.new(screen, statusbar, widget, mod) do |screen, statusbar, widget, mod|
+ @awesome.widget_tell(screen, statusbar, widget[:identifier], widget[:property], mod.formatize)
+ end
+ end
+
+ widget[:properties].each do |property, format|
+ threads << Thread.new(screen, statusbar, widget, property, mod, format) do |screen, statusbar, widget, property, mod, format|
+ @awesome.widget_tell(screen, statusbar, widget[:identifier], property, mod.formatize(format))
+ end
+ end
+
+ rescue WidgetError => e
+ @log.error(widget[:module]) { e.message }
+ end
+
+ threads.each {|t| t.join }
+ end
+
+ def join_threads
+ @threads.each {|t| t.join }
+ end
+ end
+ end
+end |