1 # Copyright (C) 2008 Dag Odenhall <dag.odenhall@gmail.com>
2 # Licensed under the Academic Free License version 3.0
5 require 'amazing/options'
6 require 'amazing/x11/display_name'
8 require 'amazing/widget'
9 require 'amazing/proc_file'
10 require 'amazing/widgets'
11 require 'amazing/awesome'
18 # Command line interface runner
24 @log = Logger.new(STDOUT)
25 @options = Options.new(@args)
27 @display = X11::DisplayName.new
28 rescue X11::EmptyDisplayName => e
29 @log.warn("#{e.message}, falling back on :0")
30 @display = X11::DisplayName.new(":0")
31 rescue X11::InvalidDisplayName => e
32 @log.fatal("#{e.message}, exiting")
39 @log.fatal("Received SIGINT, exiting")
44 show_help if @options[:help]
46 stop_process(true) if @options[:stop]
49 list_widgets if @options[:listwidgets]
50 test_widget if @options[:test]
52 @awesome = Awesome.new(@display.display)
53 explicit_updates unless @options[:update] == []
59 @config["widgets"].each do |screen, widgets|
60 widgets.each do |widget_name, settings|
61 if settings["every"] && count % settings["every"] == 0
62 update_widget(screen, widget_name)
80 @log.level = Logger.const_get(@options[:loglevel].upcase)
82 @log.error("Unsupported log level #{@options[:loglevel].inspect}")
83 @log.level = Logger::INFO
87 def stop_process(quit=false)
89 Process.kill("SIGINT", File.read("#{ENV["HOME"]}/.amazing/pids/#{@display.display}.pid").to_i)
90 @log.warn("Killed older process") unless quit
97 scripts = @options[:include]
98 @config["include"].each do |script|
99 script = "#{File.dirname(@options[:config])}/#{script}" if script[0] != ?/
102 if @options[:autoinclude]
103 scripts << Dir["#{ENV["HOME"]}/.amazing/widgets/*"]
105 scripts.flatten.each do |script|
106 if File.exist?(script)
107 @log.debug("Loading script #{script.inspect}")
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+)/)}")
114 @log.error("No such widget script #{script.inspect}")
120 if @options[:listwidgets] == true
121 longest_widget_name = Widgets.constants.inject {|a,b| a.length > b.length ? a : b }.length
122 Widgets.constants.sort.each do |widget|
123 widget_class = Widgets.const_get(widget)
124 puts "%-#{longest_widget_name}s : %s" % [widget, widget_class.description]
127 widget_class = Widgets.const_get(@options[:listwidgets])
129 puts "#{@options[:listwidgets]} - #{widget_class.description}"
131 dependencies = widget_class.dependencies
132 unless dependencies.empty?
133 longest_dependency_name = dependencies.keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length
134 longest_dependency_name = 10 if longest_dependency_name < 10
135 longest_description = dependencies.values.inject {|a,b| a.length > b.length ? a : b }.length
136 longest_description = 11 if longest_description < 11
137 puts " %-#{longest_dependency_name}s | DESCRIPTION" % "DEPENDENCY"
138 puts "-" * (longest_dependency_name + longest_description + 5)
139 dependencies.keys.sort.each do |dependency|
140 puts " %-#{longest_dependency_name}s | #{dependencies[dependency]}" % dependency
144 options = widget_class.options
145 unless options.empty?
146 longest_option_name = options.keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length
147 longest_option_name = 6 if longest_option_name < 6
148 longest_description = options.values.inject {|a,b| a[:description].length > b[:description].length ? a : b }[:description].length
149 longest_description = 11 if longest_description < 11
150 longest_default = options.values.inject {|a,b| a[:default].inspect.length > b[:default].inspect.length ? a : b }[:default].inspect.length
151 longest_default = 7 if longest_default < 7
152 puts " %-#{longest_option_name}s | %-#{longest_description}s | DEFAULT" % ["OPTION", "DESCRIPTION"]
153 puts "-" * (longest_option_name + longest_description + longest_default + 8)
154 options.keys.sort_by {|option| option.to_s }.each do |option|
155 puts " %-#{longest_option_name}s | %-#{longest_description}s | %s" % [option, options[option][:description], options[option][:default].inspect]
159 fields = widget_class.fields
161 longest_field_name = fields.keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length
162 longest_field_name = 5 if longest_field_name < 5
163 longest_description = fields.values.inject {|a,b| a[:description].length > b[:description].length ? a : b }[:description].length
164 longest_description = 11 if longest_description < 11
165 longest_default = fields.values.inject {|a,b| a[:default].inspect.length > b[:default].inspect.length ? a : b }[:default].inspect.length
166 longest_default = 7 if longest_default < 7
167 puts " %-#{longest_field_name + 1}s | %-#{longest_description}s | DEFAULT" % ["FIELD", "DESCRIPTION"]
168 puts "-" * (longest_field_name + longest_description + longest_default + 9)
169 fields.keys.sort_by {|field| field.to_s }.each do |field|
170 puts " @%-#{longest_field_name}s | %-#{longest_description}s | %s" % [field, fields[field][:description], fields[field][:default].inspect]
179 widget = Widgets.const_get(@options[:test])
180 settings = YAML.load("{#{ARGV[0]}}")
181 instance = widget.new("test", settings)
182 longest_field_name = widget.fields.keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length
183 widget.fields.keys.sort_by {|field| field.to_s }.each do |field|
184 puts "@%-#{longest_field_name}s = %s" % [field, instance.instance_variable_get("@#{field}".to_sym).inspect]
190 @log.debug("Parsing configuration file")
192 @config = YAML.load_file(@options[:config])
194 @log.fatal("Unable to parse configuration file, exiting")
197 @config["include"] ||= []
201 @log.debug("Waiting for awesome control socket for display #{@display.display}")
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}")
207 rescue Timeout::Error
208 @log.fatal("Socket for display #{@display.display} not created within 30 seconds, exiting")
214 @config["widgets"].each do |screen, widgets|
215 widgets.each_key do |widget_name|
216 next unless @options[:update] == :all || @options[:update].include?(widget_name)
217 update_widget(screen, widget_name, false)
224 path = "#{ENV["HOME"]}/.amazing/pids"
225 FileUtils.makedirs(path)
226 File.open("#{path}/#{@display.display}.pid", "w+") do |f|
232 File.delete("#{ENV["HOME"]}/.amazing/pids/#{@display.display}.pid") rescue Errno::ENOENT
235 def update_non_interval
236 @config["widgets"].each do |screen, widgets|
237 widgets.each do |widget_name, settings|
238 next if settings["every"]
239 update_widget(screen, widget_name)
244 def update_widget(screen, widget_name, threaded=true)
245 settings = @config["widgets"][screen][widget_name]
246 @log.debug("Updating widget #{widget_name} of type #{settings["type"]} on screen #{screen}")
249 widget = Widgets.const_get(settings["type"]).new(widget_name, settings)
250 @awesome.widget_tell(screen, widget_name, widget.formatize)
251 rescue WidgetError => e
252 @log.error(settings["type"]) { e.message }