1 # Copyright 2008 Dag Odenhall <dag.odenhall@gmail.com>
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
24 # Command line interface runner
31 @log = Logger.new(STDOUT)
32 @options = Options.new(@args)
34 @display = X11::DisplayName.new
35 rescue X11::EmptyDisplayName => e
36 @log.warn("#{e.message}, falling back on :0")
37 @display = X11::DisplayName.new(":0")
38 rescue X11::InvalidDisplayName => e
39 @log.fatal("#{e.message}, exiting")
46 @log.fatal("Received SIGINT, exiting")
51 show_help if @options[:help]
53 stop_process(true) if @options[:stop]
56 list_widgets if @options[:listwidgets]
57 test_widget if @options[:test]
59 @awesome = Awesome.new(@display.display)
60 explicit_updates unless @options[:update] == []
66 @config["awesome"].each do |screen, widgets|
67 widgets.each do |widget_name, settings|
68 if settings["interval"] && count % settings["interval"] == 0
69 update_widget(screen, widget_name)
87 @log.level = Logger.const_get(@options[:loglevel].upcase)
89 @log.error("Unsupported log level #{@options[:loglevel].inspect}")
90 @log.level = Logger::INFO
94 def stop_process(quit=false)
96 Process.kill("SIGINT", File.read("#{ENV["HOME"]}/.amazing/pids/#{@display.display}.pid").to_i)
97 @log.warn("Killed older process") unless quit
104 scripts = @options[:include]
105 @config["include"].each do |script|
106 script = File.expand_path(script, File.dirname(@options[:config]))
109 if @options[:autoinclude]
110 scripts << Dir["#{ENV["HOME"]}/.amazing/widgets/*"]
112 scripts.flatten.each do |script|
113 if File.exist?(script)
114 @log.debug("Loading script #{script.inspect}")
116 Widgets.module_eval(File.read(script), script)
117 rescue SyntaxError => e
118 @log.error("Bad syntax in #{script} at line #{e.to_s.scan(/:(\d+)/)}")
121 @log.error("No such widget script #{script.inspect}")
127 if @options[:listwidgets] == true
128 longest_widget_name = Widgets.constants.inject {|a,b| a.length > b.length ? a : b }.length
129 Widgets.constants.sort.each do |widget|
130 widget_class = Widgets.const_get(widget)
131 puts "%-#{longest_widget_name}s : %s" % [widget, widget_class.description]
134 widget_class = Widgets.const_get(@options[:listwidgets].camel_case)
136 puts "#{@options[:listwidgets].camel_case} - #{widget_class.description}"
138 dependencies = widget_class.dependencies
139 unless dependencies.empty?
140 longest_dependency_name = dependencies.keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length
141 longest_dependency_name = 10 if longest_dependency_name < 10
142 longest_description = dependencies.values.inject {|a,b| a.length > b.length ? a : b }.length
143 longest_description = 11 if longest_description < 11
144 puts " %-#{longest_dependency_name}s | DESCRIPTION" % "DEPENDENCY"
145 puts "-" * (longest_dependency_name + longest_description + 5)
146 dependencies.keys.sort.each do |dependency|
147 puts " %-#{longest_dependency_name}s | #{dependencies[dependency]}" % dependency
151 options = widget_class.options
152 unless options.empty?
153 longest_option_name = options.keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length
154 longest_option_name = 6 if longest_option_name < 6
155 longest_description = options.values.inject {|a,b| a[:description].length > b[:description].length ? a : b }[:description].length
156 longest_description = 11 if longest_description < 11
157 longest_default = options.values.inject {|a,b| a[:default].inspect.length > b[:default].inspect.length ? a : b }[:default].inspect.length
158 longest_default = 7 if longest_default < 7
159 puts " %-#{longest_option_name}s | %-#{longest_description}s | DEFAULT" % ["OPTION", "DESCRIPTION"]
160 puts "-" * (longest_option_name + longest_description + longest_default + 8)
161 options.keys.sort_by {|option| option.to_s }.each do |option|
162 puts " %-#{longest_option_name}s | %-#{longest_description}s | %s" % [option, options[option][:description], options[option][:default].inspect]
166 fields = widget_class.fields
168 longest_field_name = fields.keys.inject {|a,b| a.to_s.length > b.to_s.length ? a : b }.to_s.length
169 longest_field_name = 5 if longest_field_name < 5
170 longest_description = fields.values.inject {|a,b| a[:description].length > b[:description].length ? a : b }[:description].length
171 longest_description = 11 if longest_description < 11
172 longest_default = fields.values.inject {|a,b| a[:default].inspect.length > b[:default].inspect.length ? a : b }[:default].inspect.length
173 longest_default = 7 if longest_default < 7
174 puts " %-#{longest_field_name + 1}s | %-#{longest_description}s | DEFAULT" % ["FIELD", "DESCRIPTION"]
175 puts "-" * (longest_field_name + longest_description + longest_default + 9)
176 fields.keys.sort_by {|field| field.to_s }.each do |field|
177 puts " @%-#{longest_field_name}s | %-#{longest_description}s | %s" % [field, fields[field][:description], fields[field][:default].inspect]
186 widget = Widgets.const_get(@options[:test].camel_case)
187 settings = YAML.load("{#{ARGV[0]}}")
188 instance = widget.new("test", settings)
189 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
190 puts "@%-#{longest_field_name}s = %s" % [:default, instance.instance_variable_get(:@default).inspect]
191 widget.fields.keys.sort_by {|field| field.to_s }.each do |field|
192 puts "@%-#{longest_field_name}s = %s" % [field, instance.instance_variable_get("@#{field}".to_sym).inspect]
198 @log.debug("Parsing configuration file")
200 @config = YAML.load_file(@options[:config])
202 @log.fatal("Unable to parse configuration file, exiting")
205 @config["include"] ||= []
209 @log.debug("Waiting for awesome control socket for display #{@display.display}")
211 Timeout.timeout(30) do
212 sleep 1 until File.exist?("#{ENV["HOME"]}/.awesome_ctl.#{@display.display}")
213 @log.debug("Got socket for display #{@display.display}")
215 rescue Timeout::Error
216 @log.fatal("Socket for display #{@display.display} not created within 30 seconds, exiting")
222 @config["awesome"].each do |screen, widgets|
223 widgets.each_key do |widget_name|
224 next unless @options[:update] == :all || @options[:update].include?(widget_name)
225 update_widget(screen, widget_name, false)
232 path = "#{ENV["HOME"]}/.amazing/pids"
233 FileUtils.makedirs(path)
234 File.open("#{path}/#{@display.display}.pid", "w+") do |f|
240 File.delete("#{ENV["HOME"]}/.amazing/pids/#{@display.display}.pid") rescue Errno::ENOENT
243 def update_non_interval
244 @config["awesome"].each do |screen, widgets|
245 widgets.each do |widget_name, settings|
246 next if settings["interval"]
247 update_widget(screen, widget_name)
252 def update_widget(screen, widget_name, threaded=true)
253 settings = @config["awesome"][screen][widget_name]
254 type = settings["type"].camel_case
255 @log.debug("Updating widget #{widget_name} of type #{type} on screen #{screen}")
258 widget = Widgets.const_get(type).new(widget_name, settings)
259 @awesome.widget_tell(screen, widget_name, widget.formatize)
260 rescue WidgetError => e
261 @log.error(type) { e.message }