Split up in multiple files
[amazing.git] / lib / amazing / cli.rb
blob3f88745ded0d3309716fa577788a183cada6cb7f
1 # Copyright (C) 2008 Dag Odenhall <dag.odenhall@gmail.com>
2 # Licensed under the Academic Free License version 3.0
4 require 'logger'
5 require 'amazing/options'
6 require 'amazing/x11/display_name'
7 require 'yaml'
8 require 'amazing/widget'
9 require 'amazing/proc_file'
10 require 'amazing/widgets'
11 require 'amazing/awesome'
12 require 'timeout'
13 require 'thread'
15 module Amazing
17   # Command line interface runner
18   #
19   #   CLI.run(ARGV)
20   class CLI
21     def initialize(args)
22       @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     end
36     def run
37       trap("SIGINT") do
38         @log.fatal("Received SIGINT, exiting")
39         exit
40       end
41       @options.parse
42       show_help if @options[:help]
43       set_loglevel
44       parse_config
45       load_scripts
46       list_widgets if @options[:listwidgets]
47       setup_screens
48       wait_for_sockets
49       explicit_updates unless @options[:update].empty?
50       update_non_interval
51       count = 0
52       loop do
53         @config["widgets"].each do |widget_name, settings|
54           if settings["every"] && count % settings["every"] == 0
55             update_widget(widget_name)
56           end
57         end
58         count += 1
59         sleep 1
60       end
61     end
63     private
65     def show_help
66       puts @options.help
67       exit
68     end
70     def set_loglevel
71       begin
72         @log.level = Logger.const_get(@options[:loglevel].upcase)
73       rescue NameError
74         @log.error("Unsupported log level #{@options[:loglevel].inspect}")
75         @log.level = Logger::INFO
76       end
77     end
79     def load_scripts
80       scripts = @options[:include]
81       @config["include"].each do |script|
82         script = "#{File.dirname(@options[:config])}/#{script}" if script[0] != ?/
83         scripts << script
84       end
85       scripts.each do |script|
86         if File.exist?(script)
87           Widgets.module_eval(File.read(script))
88         else
89           @log.error("No such widget script #{script.inspect}")
90         end
91       end
92     end
94     def list_widgets
95       Widgets.constants.each do |widget|
96         if description = Widgets.const_get(widget).description
97           puts "#{widget}: #{description}"
98         else
99           puts widget
100         end
101       end
102       exit
103     end
105     def parse_config
106       @log.debug("Parsing configuration file")
107       begin
108         @config = YAML.load_file(@options[:config])
109       rescue
110         @log.fatal("Unable to parse configuration file, exiting")
111         exit 1
112       end
113       @config["include"] ||= []
114       @config["screens"] ||= []
115     end
117     def setup_screens
118       @screens = {}
119       @options[:screens].each do |screen|
120         @screens[screen.to_i] = Awesome.new(screen, @display.display)
121       end
122       if @screens.empty?
123         @config["screens"].each do |screen|
124           @screens[screen] = Awesome.new(screen, @display.display)
125         end
126       end
127       @screens[0] = Awesome.new if @screens.empty?
128     end
130     def wait_for_sockets
131       @log.debug("Waiting for awesome control socket for display #{@display.display}")
132       begin
133         Timeout.timeout(30) do
134           sleep 1 until File.exist?("#{ENV["HOME"]}/.awesome_ctl.#{@display.display}")
135           @log.debug("Got socket for display #{@display.display}")
136         end
137       rescue Timeout::Error
138         @log.fatal("Socket for display #{@display.display} not created within 30 seconds, exiting")
139         exit 1
140       end
141     end
143     def update_non_interval
144       @config["widgets"].each do |widget_name, settings|
145         next if settings["every"]
146         update_widget(widget_name)
147       end
148     end
150     def explicit_updates
151       @config["widgets"].each_key do |widget_name|
152         next unless @options[:update].include? widget_name
153         update_widget(widget_name, false)
154       end
155       exit
156     end
158     def update_widget(widget_name, threaded=true)
159       settings = @config["widgets"][widget_name]
160       begin
161         @screens.each do |screen, awesome|
162           @log.debug("Updating widget #{widget_name} of type #{settings["type"]} on screen #{screen}")
163           opts = settings["options"] || {}
164           field = settings["field"] || "default"
165           update = Proc.new do
166             widget = Widgets.const_get(settings["type"]).new(widget_name, settings["format"], opts)
167             awesome.widget_tell(widget_name, widget.formatize)
168           end
169           if threaded
170             Thread.new &update
171           else
172             update.call
173           end
174         end
175       rescue WidgetError => e
176         @log.error(settings["type"]) { e.message }
177       end
178     end
179   end