Use an ARGB window for the Ecore Evas.
[euphoria.git] / bin / euphoria
1 #!/usr/bin/ruby -w
2
3 require "ecore"
4 require "ecore_x"
5 require "evas"
6 require "ecore_evas"
7 require "edje"
8 require "esmart"
9 require "esmart_draggies"
10 require "esmart_container"
11 require "xmmsclient"
12 require "xmmsclient_ecore"
13 require "singleton"
14 require "yaml"
15 require "find"
16 require "fileutils"
17
18 require "euphoria/playlist"
19 require "euphoria/playlist_item"
20 require "euphoria/slider"
21 require "euphoria/volume_box"
22 require "euphoria/playtime_updater"
23 require "euphoria/playback_status_handler"
24 #require "euphoria/file_dialog"
25
26 Config = Struct.new("Config", :ipc_path, :theme, :time_display_elapsed)
27
28 # backwards compatibility w/ DrFeelgood and earlier
29 unless Xmms.respond_to?(:userconfdir)
30         def Xmms.userconfdir
31                 File.join(File.expand_path("~"), ".xmms2")
32         end
33 end
34
35 class Euphoria
36         DATA_DIR = "/usr/local/share/euphoria"
37         DATA_DIR_PRIV = File.join(Xmms.userconfdir,
38                                   "clients",
39                                   "euphoria")
40
41         include Singleton
42
43         attr_reader :xmms, :ee, :edje
44
45         def initialize
46                 @edje = nil
47                 @xmms = nil
48                 @volume_box = nil
49
50                 # settings
51                 unless File.directory?(DATA_DIR_PRIV)
52                         FileUtils.mkdir_p(DATA_DIR_PRIV)
53                 end
54
55                 @config_file = DATA_DIR_PRIV + "/config.yaml"
56
57                 load_config
58                 init_xmms
59                 init_gui
60
61                 blk = lambda { |item| ui_fill_track_info(item) }
62                 @playlist = Playlist.new(@xmms, blk)
63
64                 setup_callbacks
65
66                 @playtime_updater = PlaytimeUpdater.new(xmms) do |current_pos|
67                         ui_refresh_time(current_pos)
68
69                         # refresh the seeker
70                         if !@playlist.current_item.nil?
71                                 d = @playlist.current_item.duration
72                                 if d > 0
73                                         ui_refresh_seeker(current_pos / d.to_f)
74                                 end
75                         end
76                 end
77
78                 PlaybackStatusHandler.new(xmms) do |state|
79                         @playback_state = state
80
81                         signal_playback_state
82                         @playtime_updater.enabled =
83                                 (@playback_state == Xmms::Client::PLAY)
84                 end
85
86                 # is repeat enabled?
87                 s = @xmms.configval_get("playlist.repeat_all").wait.value
88                 @repeat = (s == "1")
89                 @edje.emit_signal("TOGGLE_REPEAT_MODE", "Euphoria") if @repeat
90
91                 @sliders = {}
92
93                 unless @edje.data("has_volume_slider").nil?
94                         @sliders["volume"] = VolumeSlider.new(@edje, @xmms,
95                                                               "player.slider.volume")
96                 end
97
98                 if @edje.part_exists?("vol_display_text")
99                         # FIXME Maybe rename the part to volume_box
100                         @volume_box = VolumeBox.new(@edje, @xmms, "player.volumeBox")
101                 end
102
103 =begin
104                 unless @edje.data("has_eq_slider").nil?
105                         1.upto(10) do |i|
106                                 p = "player.slider.eq.band%02i" % i
107                                 k = "equalizer.gain#{i - 1}"
108                                 @sliders[k] = EqSlider.new(@edje, @xmms, p, k)
109                         end
110                 end
111 =end
112
113                 handle_current_pos(@xmms.playlist.current_pos.wait)
114         end
115
116         def show
117                 if @edje.data("has_separate_playlist").nil?
118                         @playlist.show(@theme, false)
119                 end
120
121                 @ee.show
122         end
123
124         def save_config
125                 File.open(@config_file, "w") { |fo| YAML.dump(@config, fo) }
126         end
127
128         def show_file_dialog
129                 if @file_dialog_theme.nil?
130                         puts "file dialog edje not found"
131                         return
132                 end
133
134                 FileDialog.open(@file_dialog_theme) do |sel|
135                         if File.directory?(sel)
136                                 files = []
137                                 Find.find(sel) { |f| files << f if File.file?(f) }
138
139                                 files.sort.each do |f|
140                                         @xmms.playlist_add("file://" + f).wait
141                                 end
142                         elsif File.exists?(sel)
143                                 @xmms.playlist_add("file://" + sel).wait
144                         end
145                 end
146         end
147
148 private
149         def init_gui
150                 @ee = Ecore::Evas::SoftwareX11.new
151                 @ee.title = "Euphoria"
152                 @ee.name_class = ["Euphoria", "Euphoria"]
153                 @ee.has_alpha = true
154                 @ee.borderless = true
155
156                 #@ee.evas.font_path_append("/usr/X11R6/lib/X11/fonts/TTF")
157                 #@ee.evas.font_path_append(ENV["HOME"] + "/.fonts")
158                 @ee.evas.font_hinting = Evas::Evas::AUTO
159
160                 @ee.on_pre_render { Edje::thaw }
161                 @ee.on_post_render { Edje::freeze }
162
163                 @ee.on_resize do
164                         x, y, w, h = @ee.geometry
165
166                         @edje.resize(w, h)
167                         @dragger.resize(w, h)
168                 end
169
170                 @dragger = Esmart::Draggies.new(@ee)
171                 @dragger.name = "dragger"
172                 @dragger.button = 1
173                 @dragger.show
174
175                 @theme = find_theme(@config.theme)
176                 raise "Cannot find theme - #{@config.theme}" if @theme.nil?
177
178                 if Edje.collections(@theme).include?("esmart.filedialog")
179                         @file_dialog_theme = @theme
180                 else
181                         @file_dialog_theme = find_theme("file_dialog")
182                 end
183
184                 @edje = Edje::Edje.new(@ee.evas)
185                 @edje.load(@theme, "euphoria")
186                 @edje.name = "edje"
187                 @edje.show
188
189                 w, h = @edje.get_size_max
190                 @ee.set_size_max(w, h)
191
192                 w, h = @edje.get_size_min
193                 @ee.set_size_min(w, h)
194
195                 @ee.resize(w, h)
196
197                 ui_refresh_seeker(0)
198         end
199
200         def init_xmms
201                 @xmms = Xmms::Client.new("Euphoria")
202                 @xmms.connect(ENV["XMMS_PATH"] || @config.ipc_path)
203                 @xmms.add_to_ecore_mainloop
204
205                 @xmms.on_disconnect do
206                         puts "daemon exited, shutting down"
207                         Ecore.main_loop_quit
208                 end
209         end
210
211         def setup_callbacks
212                 @edje.on_signal("quit") do
213                         Ecore::main_loop_quit
214                 end
215
216                 @edje.on_signal("close") do
217                         Ecore::main_loop_quit
218                 end
219
220                 @edje.on_signal("playback.play") do
221                         case @playback_state
222                                 when Xmms::Client::PLAY
223                                         @xmms.playback_stop.wait
224                                         sleep(0.5) # evil hack, fix xmms2d instead
225                                         @xmms.playback_start.wait
226                                 else
227                                         @xmms.playback_start.wait
228                         end
229                 end
230
231                 @edje.on_signal("playback.stop") do
232                         @xmms.playback_stop.wait
233                 end
234
235                 @edje.on_signal("playback.pause") do
236                         case @playback_state
237                                 when Xmms::Client::PLAY
238                                         @xmms.playback_pause.wait
239                                 when Xmms::Client::PAUSE
240                                         @xmms.playback_start.wait
241                         end
242                 end
243
244                 @edje.on_signal("playback.next") do
245                         @xmms.playlist_set_next_rel(1).wait
246                         @xmms.playback_tickle.wait
247
248                         if @playback_state != Xmms::Client::PLAY
249                                 @xmms.playback_start.wait
250                         end
251                 end
252
253                 @edje.on_signal("playback.prev") do
254                         @xmms.playlist_set_next_rel(-1).wait
255                         @xmms.playback_tickle.wait
256
257                         if @playback_state != Xmms::Client::PLAY
258                                 @xmms.playback_start.wait
259                         end
260                 end
261
262                 @edje.on_signal("TOGGLE_TIME_DISPLAY_MODE") do
263                         @config.time_display_elapsed = !@config.time_display_elapsed
264                 end
265
266                 @edje.on_signal("DEBUG") do |sig, source|
267                         puts source
268                 end
269
270                 @edje.on_signal("playlist.open") { @playlist.show(@theme, true) }
271
272                 @edje.on_signal("file_dialog.open") do
273                         show_file_dialog
274                 end
275
276                 @edje.on_signal("TOGGLE_REPEAT_MODE") do |sig, source|
277                         # don't react on our own emission
278                         next if source == "Euphoria"
279
280                         @repeat = !@repeat
281
282                         v = @repeat ? "1" : "0"
283                         @xmms.configval_set("playlist.repeat_all", v).wait
284                 end
285
286                 @xmms.broadcast_configval_changed.notifier do |res|
287                         k = res.value.keys.first
288                         v = res.value.values.first
289
290                         case k
291                                 when "playlist.repeat_all"
292                                         tmp = (v == "1")
293                                         if tmp != @repeat
294                                                 @repeat = tmp
295                                                 @edje.emit_signal("TOGGLE_REPEAT_MODE", "Euphoria")
296                                         end
297                                 when /equalizer\.gain(\d+)/
298                                         @sliders[k].value = v.to_f
299                         end
300                 end
301
302                 @xmms.broadcast_playback_volume_changed.notifier do |res|
303                         if @edje.data("has_volume_slider")
304                                 @sliders["volume"].value = res.value
305                         end
306
307                         if @edje.part_exists?("vol_display_text")
308                                 @volume_box.value = res.value
309                         end
310                 end
311
312                 @xmms.broadcast_playlist_current_pos.notifier do |res|
313                         handle_current_pos(res)
314                 end
315         end
316
317         def handle_current_pos(res)
318                 begin
319                         pos = res.value
320                 rescue Xmms::Result::ValueError
321                 else
322                         ui_fill_track_info(@playlist[pos])
323                 end
324         end
325
326         def ui_fill_track_info(item)
327                 unless item.nil?
328                         @edje.send_message(item.track_info_message)
329                 end
330         end
331
332         def ui_refresh_time(time)
333                 return unless @edje.part_exists?("player.time")
334
335                 fmt = "%i:%02i"
336
337                 unless @config.time_display_elapsed
338                         time = @playlist.current_item.duration - time
339                         fmt = "-" + fmt
340                 end
341
342                 tmp = fmt % [time / 60, time % 60]
343                 @edje.part("player.time").text = tmp
344         end
345
346         def ui_refresh_seeker(pos)
347                 if @edje.part_exists?("seeker")
348                         @edje.part("seeker").set_drag_value(pos, 0.1)
349                         #@edje.thaw
350                         #@edje.evas.render
351                 end
352         end
353
354         def signal_playback_state
355                 sig = case @playback_state
356                         when Xmms::Client::PAUSE
357                                 "PLAYBACK_STATE_PAUSED"
358                         when Xmms::Client::PLAY
359                                 "PLAYBACK_STATE_PLAYING"
360                         else
361                                 "PLAYBACK_STATE_STOPPED"
362                 end
363
364                 @edje.emit_signal(sig, "Euphoria")
365         end
366
367         def load_config
368                 @config = nil
369
370                 if File.exist?(@config_file)
371                         File.open(@config_file) { |fo| @config = YAML.load(fo) }
372                 end
373
374                 if @config.nil? || !@config.is_a?(Config)
375                         @config = Config.new
376                         @config.ipc_path = nil
377                         @config.theme = "winter"
378                         @config.time_display_elapsed = true
379                 end
380         end
381
382         def find_theme(name)
383                 dirs = [DATA_DIR_PRIV, DATA_DIR]
384                 dirs.each do |dir|
385                         tmp = File.join(dir, "themes/#{name}.edj")
386                         return tmp if File.exist?(tmp)
387                 end
388
389                 nil
390         end
391 end
392
393 $x = Euphoria.instance
394 Euphoria.instance.show
395 Ecore::main_loop_begin
396 Euphoria.instance.save_config