Mercurial > ~darius > hgwebdir.cgi > mservtk
annotate mservtk.tcl @ 7:abe05fb9c2a6
Better debugging.
Add "Awful and Skip" option.
author | darius |
---|---|
date | Mon, 16 Sep 2002 12:19:01 +0000 |
parents | b370e0bbe050 |
children | 81b36e5b725b |
rev | line source |
---|---|
5 | 1 #!/usr/bin/env wish8.2 |
1 | 2 |
3 # | |
4 # This software is copyright Daniel O'Connor (darius@dons.net.au) 2000 | |
5 # | |
6 # Redistribution and use in source and binary forms, with or without | |
7 # modification, are permitted provided that the following conditions | |
8 # are met: | |
9 # 1. Redistributions of source code must retain the above copyright | |
10 # notice, this list of conditions and the following disclaimer. | |
11 # 2. Redistributions in binary form must reproduce the above copyright | |
12 # notice, this list of conditions and the following disclaimer in the | |
13 # documentation and/or other materials provided with the distribution. | |
14 # 3. Neither the name Daniel O'Connor nor the names of its contributors | |
15 # may be used to endorse or promote products derived from this software | |
16 # without specific prior written permission. | |
17 # | |
18 # THIS SOFTWARE IS PROVIDED BY DANIEL O'CONNOR AND CONTRIBUTORS ``AS IS'' AND | |
19 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
20 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
21 # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
22 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
23 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
24 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
25 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
26 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
27 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
28 # SUCH DAMAGE. | |
29 # | |
30 | |
31 proc main {} { | |
3 | 32 global argv0 argv state albums songs tcl_platform; |
1 | 33 |
3 | 34 if {[string first "Windows" $tcl_platform(os)] == -1} { |
35 set state(conffile) "~/.mservtk"; | |
36 set state(windows) 0; | |
37 } else { | |
38 package require registry 1.0; | |
39 set state(windows) 1; | |
40 } | |
41 | |
7 | 42 set state(loglevel) 0; |
1 | 43 set state(port) "4444"; |
3 | 44 set state(exit) 0; |
1 | 45 set state(tmpphrase) ""; |
3 | 46 set state(sortmode) "Title"; |
1 | 47 |
4 | 48 set state(rtlist) ""; |
49 | |
3 | 50 wm withdraw .; |
51 | |
52 f_readconf; | |
53 gui_conf; | |
1 | 54 |
3 | 55 if {$state(host) == "NONE"} { |
7 | 56 log 0 "Login cancelled"; |
3 | 57 exit; |
58 } else { | |
7 | 59 log 0 "Login OK'd"; |
3 | 60 } |
61 | |
62 f_writeconf; | |
63 | |
64 wm deiconify .; | |
1 | 65 con_mserv; |
66 | |
67 con_getalbums albums; | |
68 con_getsongs songs albums; | |
69 | |
3 | 70 gui_build; |
71 focus -force .; | |
72 | |
1 | 73 gui_updatesongs; |
74 gui_updatequeue; | |
75 | |
6
b370e0bbe050
Remove the check every second, since it breaks stuff :-/
darius
parents:
5
diff
changeset
|
76 gui_updateinfo; |
1 | 77 update_timer; |
78 | |
79 while {1} { | |
4 | 80 vwait state; |
81 | |
82 if {$state(rtlist) != ""} { | |
83 # Copy it so we don't stomp any new additions | |
84 set tmp $state(rtlist); | |
85 set state(rtlist) ""; | |
86 | |
5 | 87 foreach t $tmp { |
7 | 88 # log 0 "Handle $t"; |
4 | 89 n_rthandler [lindex $t 0] [lindex $t 1]; |
90 } | |
91 } | |
1 | 92 |
93 if {$state(exit) == 1} { | |
94 exit; | |
95 } | |
96 } | |
97 } | |
98 | |
3 | 99 proc f_readconf {} { |
100 global state; | |
101 | |
102 set state(host) "NONE"; | |
103 set state(user) "NONE"; | |
104 set state(pass) ""; | |
105 | |
106 if {$state(windows) == 1} { | |
107 if {[catch { | |
108 set state(host) [registry get {HKEY_CURRENT_USER\Software\MServTk} host]; | |
109 set state(user) [registry get {HKEY_CURRENT_USER\Software\MServTk} user]; | |
110 set state(pass) [registry get {HKEY_CURRENT_USER\Software\MServTk} pass]; | |
111 } msg]} { | |
7 | 112 log 0 "Failed to read registry keys - $msg"; |
3 | 113 } |
114 } else { | |
115 if {![catch { | |
116 set fh [open $state(conffile)]; | |
117 } msg]} { | |
118 if {[gets $fh] != "mservtk-0.1"} { | |
7 | 119 log 0 "Conf file has the wrong version"; |
3 | 120 } else { |
121 set state(host) [gets $fh]; | |
122 set state(user) [gets $fh]; | |
123 set state(pass) [gets $fh]; | |
124 close $fh; | |
125 } | |
126 } else { | |
7 | 127 log 0 "Failed to open $state(conffile) - $msg"; |
3 | 128 } |
129 } | |
130 } | |
131 | |
132 proc f_writeconf {} { | |
133 global state; | |
134 | |
135 if {$state(windows) == 1} { | |
136 if {[catch { | |
137 registry set {HKEY_CURRENT_USER\Software\MServTk} host $state(host); | |
138 registry set {HKEY_CURRENT_USER\Software\MServTk} user $state(user); | |
139 registry set {HKEY_CURRENT_USER\Software\MServTk} pass $state(pass); | |
140 } msg]} { | |
7 | 141 log 0 "Failed to set registry keys - $msg"; |
3 | 142 } |
143 } else { | |
144 if {![catch { | |
145 set fh [open $state(conffile) w]; | |
146 } msg]} { | |
147 puts $fh "mservtk-0.1"; | |
148 puts $fh $state(host); | |
149 puts $fh $state(user); | |
150 puts $fh $state(pass); | |
151 | |
152 close $fh; | |
153 } else { | |
7 | 154 log 0 "Failed to open $state(conffile) - $msg"; |
3 | 155 } |
156 } | |
157 } | |
158 | |
159 proc gui_conf {} { | |
160 global state; | |
161 | |
162 catch {destroy .conf}; | |
163 | |
164 toplevel .conf -class Dialog; | |
165 wm title .conf "Authentication"; | |
166 wm iconname .conf "Authentication"; | |
167 | |
168 frame .conf.host; | |
169 pack .conf.host -side top -pady 2m -fill x; | |
170 label .conf.host.label -text "Host:" -width 10 -anchor e; | |
171 | |
172 entry .conf.host.entry -relief sunken -width 30 -textvariable state(host) | |
173 pack .conf.host.label .conf.host.entry -side left -padx 1m | |
174 | |
175 frame .conf.user | |
176 pack .conf.user -side top -pady 2m -fill x | |
177 label .conf.user.label -text "User:" -width 10 -anchor e | |
178 entry .conf.user.entry -relief sunken -width 8 -textvariable state(user) | |
179 pack .conf.user.label .conf.user.entry -side left -padx 1m | |
180 | |
181 frame .conf.password | |
182 pack .conf.password -side top -pady 2m -fill x | |
183 label .conf.password.label -text "Password:" -width 10 -anchor e | |
184 entry .conf.password.entry -relief sunken -width 8 -textvariable state(pass) -show *; | |
185 bind .conf.password.entry <Return> ".conf.buttons.ok invoke"; | |
186 pack .conf.password.label .conf.password.entry -side left -padx 1m | |
187 | |
188 frame .conf.buttons | |
189 pack .conf.buttons -side top -pady 1m -fill x | |
190 button .conf.buttons.ok -text OK -width 6 -command { | |
191 destroy .conf; | |
192 } | |
193 button .conf.buttons.cancel -text Cancel -width 6 -command { | |
194 set state(host) "NONE"; | |
195 set state(user) "NONE"; | |
196 set state(pass) "NONE"; | |
197 destroy .conf; | |
198 } | |
199 pack .conf.buttons.ok -side left -padx 1m; | |
200 pack .conf.buttons.cancel -side right -padx 1m; | |
201 | |
202 | |
203 # Withdraw the window, then update all the geometry information | |
204 # so we know how big it wants to be, then center the window in the | |
205 # display and de-iconify it. | |
206 | |
207 wm withdraw .conf | |
208 update idletasks | |
209 set x [expr [winfo screenwidth .conf]/2 - [winfo reqwidth .conf]/2 \ | |
210 - [winfo vrootx [winfo parent .conf]]] | |
211 set y [expr [winfo screenheight .conf]/2 - [winfo reqheight .conf]/2 \ | |
212 - [winfo vrooty [winfo parent .conf]]] | |
213 wm geom .conf +$x+$y | |
214 wm deiconify .conf | |
215 | |
216 # Set a grab and claim the focus too. | |
217 | |
218 set oldFocus [focus] | |
219 set oldGrab [grab current .conf] | |
220 if {$oldGrab != ""} { | |
221 set grabStatus [grab status $oldGrab] | |
222 } | |
223 grab .conf | |
224 tkwait visibility .conf | |
225 if {$state(host) == "NONE"} { | |
226 focus .conf.host.entry; | |
227 } else { | |
228 focus .conf.password.entry; | |
229 } | |
230 tkwait window .conf; | |
231 if {$oldGrab != ""} { | |
232 if {$grabStatus == "global"} { | |
233 grab -global $oldGrab | |
234 } else { | |
235 grab $oldGrab | |
236 } | |
237 } | |
238 | |
7 | 239 log 0 "Host $state(host)"; |
3 | 240 } |
241 | |
1 | 242 proc quit_now {} { |
243 global state; | |
244 | |
245 set state(exit) 1; | |
246 } | |
247 | |
248 proc gui_build {} { | |
249 # create the toplevel | |
250 eval destroy [winfo child .]; | |
251 wm title . "MServ-Tk"; | |
252 wm minsize . 600 500; | |
253 wm geometry . 600x500; | |
254 | |
255 # Let's have a menubar | |
256 frame .menubar -relief raised -bd 2; | |
257 pack .menubar -side top -fill x; | |
258 | |
259 # Add the File menu | |
260 menubutton .menubar.file -text "File" -menu .menubar.file.m -underline 0; | |
261 menu .menubar.file.m -tearoff 0; | |
3 | 262 .menubar.file.m add command -label " Top" -command "gui_top" |
5 | 263 .menubar.file.m add command -label " Update Queue" -command "gui_updatequeue" |
3 | 264 .menubar.file.m add separator; |
1 | 265 .menubar.file.m add command -label " Quit" -command "quit_now" \ |
266 -underline 2 -accelerator "Alt-q"; | |
267 pack .menubar.file -side left; | |
268 | |
269 # Add the Rate menu | |
270 menubutton .menubar.rate -text "Rate" -menu .menubar.rate.m -underline 0; | |
271 menu .menubar.rate.m -tearoff 0; | |
272 .menubar.rate.m add command -label " Superb" -command "rate_song SUPERB"; | |
273 .menubar.rate.m add command -label " Good" -command "rate_song GOOD"; | |
274 .menubar.rate.m add command -label " Neutral" -command "rate_song NEUTRAL"; | |
275 .menubar.rate.m add command -label " Bad" -command "rate_song BAD"; | |
276 .menubar.rate.m add command -label " Awful" -command "rate_song AWFUL"; | |
7 | 277 .menubar.rate.m add command -label " Awful and Skip" -command "rate_song AWFUL ; control_player NEXT"; |
1 | 278 |
279 pack .menubar.rate -side left; | |
280 | |
281 # Add the Control menu | |
282 menubutton .menubar.control -text "Control" -menu .menubar.control.m -underline 0; | |
283 menu .menubar.control.m -tearoff 0; | |
284 .menubar.control.m add command -label " Next" -command "control_player NEXT"; | |
285 .menubar.control.m add command -label " Pause" -command "control_player PAUSE"; | |
286 .menubar.control.m add command -label " Stop" -command "control_player STOP" | |
287 .menubar.control.m add command -label " Play" -command "control_player PLAY"; | |
288 | |
289 pack .menubar.control -side left; | |
290 | |
291 # Add the Volume menu | |
292 menubutton .menubar.vol -text "Volume" -menu .menubar.vol.m -underline 0; | |
293 menu .menubar.vol.m -tearoff 0; | |
294 .menubar.vol.m add command -label " Increase" -command "set_vol +3" \ | |
295 -accelerator "+"; | |
296 .menubar.vol.m add command -label " Decrease" -command "set_vol -3" \ | |
297 -accelerator "-"; | |
298 .menubar.vol.m add separator; | |
299 .menubar.vol.m add command -label " 100%" -command "set_vol 100"; | |
300 .menubar.vol.m add command -label " 90%" -command "set_vol 90"; | |
301 .menubar.vol.m add command -label " 80%" -command "set_vol 80"; | |
302 .menubar.vol.m add command -label " 70%" -command "set_vol 70"; | |
303 .menubar.vol.m add command -label " 60%" -command "set_vol 60"; | |
304 .menubar.vol.m add command -label " 50%" -command "set_vol 50"; | |
305 .menubar.vol.m add command -label " 40%" -command "set_vol 40"; | |
306 .menubar.vol.m add command -label " 30%" -command "set_vol 30"; | |
307 .menubar.vol.m add command -label " 20%" -command "set_vol 20"; | |
308 .menubar.vol.m add command -label " 10%" -command "set_vol 10"; | |
309 .menubar.vol.m add command -label " 0%" -command "set_vol 0"; | |
310 | |
311 pack .menubar.vol -side left; | |
312 | |
313 # Add the Sort menu | |
314 menubutton .menubar.sort -text "Sort" -menu .menubar.sort.m -underline 0; | |
315 menu .menubar.sort.m -tearoff 0; | |
316 .menubar.sort.m add command -label " Artist" \ | |
317 -command "global state; set state(sortmode) Artist; gui_updatesongs"; | |
318 .menubar.sort.m add command -label " Title" \ | |
319 -command "global state; set state(sortmode) Title; gui_updatesongs"; | |
320 .menubar.sort.m add command -label " Album" \ | |
321 -command "global state; set state(sortmode) Album; gui_updatesongs"; | |
322 | |
323 pack .menubar.sort -side left; | |
7 | 324 |
325 # Add the debug menu | |
326 menubutton .menubar.debug -text "Debug" -menu .menubar.debug.m -underline 0; | |
327 menu .menubar.debug.m -tearoff 0; | |
328 .menubar.debug.m add command -label " Debug 0" -command "global state; set state(loglevel) 0"; | |
329 .menubar.debug.m add command -label " Debug 1" -command "global state; set state(loglevel) 1"; | |
330 pack .menubar.debug -side left; | |
1 | 331 |
332 # Add the Help menu | |
333 menubutton .menubar.help -text "Help" -menu .menubar.help.m -underline 0; | |
334 menu .menubar.help.m -tearoff 0; | |
335 .menubar.help.m add command -label " About" -command "about_box" \ | |
336 -underline 2; | |
337 pack .menubar.help -side right; | |
338 | |
339 # Top frame holding tracklist and trackinfo frames | |
340 frame .top -relief raised -bd 1; | |
341 pack .top -fill both -expand 1; | |
342 | |
343 # Tracklist | |
344 frame .top.tlist -relief raised -bd 1; | |
345 pack .top.tlist -side left -fill both -expand 1; | |
346 label .top.tlist.label -text "Track List"; | |
347 pack .top.tlist.label -side top -expand 0; | |
348 listbox .top.tlist.list -relief raised -borderwidth 2 \ | |
349 -yscrollcommand ".top.tlist.scr set"; | |
350 pack .top.tlist.list -side left -fill both -expand 1; | |
351 scrollbar .top.tlist.scr -command ".top.tlist.list yview"; | |
352 pack .top.tlist.scr -side right -fill y; | |
353 bind .top.tlist.list <Double-Button-1> { | |
354 queue_song [.top.tlist.list curselection]; | |
5 | 355 gui_updatequeue; |
1 | 356 } |
357 | |
358 # Trackinfo | |
359 frame .top.tinfo -relief raised -bd 1; | |
360 pack .top.tinfo -side right -fill both -expand 0; | |
361 label .top.tinfo.label -text "Track Info"; | |
362 pack .top.tinfo.label -side top -expand 0; | |
363 frame .top.tinfo.sub -relief raised -bd 1; | |
364 pack .top.tinfo.sub -side right -fill both -expand 0; | |
365 label .top.tinfo.sub.author -text "Author:"; | |
366 pack .top.tinfo.sub.author -side top -expand 0; | |
367 label .top.tinfo.sub.title -text "Title:"; | |
368 pack .top.tinfo.sub.title -side top -expand 0; | |
369 label .top.tinfo.sub.length -text "Length:"; | |
370 pack .top.tinfo.sub.length -side top -expand 0; | |
6
b370e0bbe050
Remove the check every second, since it breaks stuff :-/
darius
parents:
5
diff
changeset
|
371 # label .top.tinfo.sub.time -text "Time:"; |
b370e0bbe050
Remove the check every second, since it breaks stuff :-/
darius
parents:
5
diff
changeset
|
372 # pack .top.tinfo.sub.time -side top -expand 0; |
1 | 373 label .top.tinfo.sub.album -text "Album:"; |
374 pack .top.tinfo.sub.album -side top -expand 0; | |
3 | 375 label .top.tinfo.sub.misc -text "Misc:"; |
376 pack .top.tinfo.sub.misc -side top -expand 0; | |
377 label .top.tinfo.sub.rate1 -text "Rating:"; | |
378 pack .top.tinfo.sub.rate1 -side top -expand 0; | |
379 label .top.tinfo.sub.rate2 -text "Temporally Adjusted:"; | |
380 pack .top.tinfo.sub.rate2 -side top -expand 0; | |
1 | 381 label .top.tinfo.sub.vol -text "Volume:"; |
382 pack .top.tinfo.sub.vol -side top -expand 0; | |
383 | |
384 # Queue (and the frame holding it) | |
385 frame .bot -relief raised -bd 1; | |
386 pack .bot -fill both -expand 1; | |
387 label .bot.qlabel -text "Queue"; | |
388 pack .bot.qlabel -side top -expand 0; | |
389 frame .bot.queue; | |
390 pack .bot.queue -fill both -expand 1; | |
391 listbox .bot.queue.list -relief raised -borderwidth 2 \ | |
392 -yscrollcommand ".bot.queue.scr set"; | |
393 pack .bot.queue.list -side left -fill both -expand 1; | |
394 scrollbar .bot.queue.scr -command ".bot.queue.list yview"; | |
395 pack .bot.queue.scr -side right -fill y; | |
396 bind .bot.queue.list <Double-Button-1> { | |
397 gui_delqueue [.bot.queue.list curselection]; | |
398 } | |
399 | |
400 bind . <Destroy> {quit_now}; | |
401 bind all <Alt-q> {quit_now}; | |
402 | |
403 bind all <KP_Add> {set_vol +3}; | |
404 bind all <KP_Subtract> {set_vol -3}; | |
405 bind all <plus> {set_vol +3}; | |
406 bind all <equal> {set_vol +3}; | |
407 bind all <minus> {set_vol -3}; | |
408 bind all <F1> {set_vol 10}; | |
409 bind all <F2> {set_vol 20}; | |
410 bind all <F3> {set_vol 30}; | |
411 bind all <F4> {set_vol 40}; | |
412 bind all <F5> {set_vol 50}; | |
413 bind all <F6> {set_vol 60}; | |
414 bind all <F7> {set_vol 70}; | |
415 bind all <F8> {set_vol 80}; | |
416 bind all <F9> {set_vol 90}; | |
417 bind all <F10> {set_vol 100}; | |
418 | |
419 bind all <Pause> {control_player PAUSE}; | |
420 bind all <End> {control_player NEXT}; | |
421 bind all <Delete> {control_player STOP}; | |
422 bind all <Home> {control_player PLAY}; | |
423 | |
424 update; | |
425 } | |
426 | |
427 proc queue_song {id} { | |
428 global songs state; | |
429 | |
430 set tmp [lindex [lindex [array get songs *:listid:$id] 1] 0]; | |
7 | 431 log 0 "Queue - '$songs($tmp:name)' by '$songs($tmp:author)' ($tmp)"; |
1 | 432 |
6
b370e0bbe050
Remove the check every second, since it breaks stuff :-/
darius
parents:
5
diff
changeset
|
433 set cookie [acquire_lock]; |
1 | 434 n_write "QUEUE [split $tmp {:}]"; |
435 n_getrtn rtn; | |
6
b370e0bbe050
Remove the check every second, since it breaks stuff :-/
darius
parents:
5
diff
changeset
|
436 release_lock $cookie; |
b370e0bbe050
Remove the check every second, since it breaks stuff :-/
darius
parents:
5
diff
changeset
|
437 |
1 | 438 if {$rtn(code) != 247} { |
439 if {$rtn(code) == 510} { | |
440 msg_box "Queue" "You can't have the same\nsong in the queue twice!"; | |
441 } else { | |
7 | 442 log 0 "Failed to queue track ($rtn(code) $rtn(data))"; |
1 | 443 } |
444 } | |
445 } | |
446 | |
447 proc msg_box {title msg} { | |
448 global state; | |
449 | |
450 catch {destroy .msg}; | |
451 | |
452 toplevel .msg -class Dialog; | |
453 wm title .msg $title; | |
454 wm iconname .msg $title; | |
455 | |
456 # text region | |
457 frame .msg.frame; | |
458 pack .msg.frame -side top -fill both -expand yes; | |
3 | 459 text .msg.frame.text -font fixed -yscroll ".msg.frame.scroll set" -wrap none; |
1 | 460 scrollbar .msg.frame.scroll -command ".msg.frame.text yview"; |
461 pack .msg.frame.text -side left -expand y -fill both; | |
462 pack .msg.frame.scroll -side right -fill y; | |
463 | |
464 # close button | |
465 button .msg.close -text "Close" -command "destroy .msg"; | |
466 pack .msg.close -side bottom -fill x; | |
467 | |
468 # read text into the text widget | |
469 .msg.frame.text insert end $msg; | |
470 } | |
471 | |
472 proc about_box {} { | |
473 global state; | |
474 | |
475 catch {destroy .about}; | |
476 | |
477 toplevel .about -class Dialog; | |
478 wm title .about "About..."; | |
479 wm iconname .about "About"; | |
480 | |
481 # text region | |
482 frame .about.frame; | |
483 pack .about.frame -side top -fill both -expand yes; | |
484 text .about.frame.text -font fixed -height 10 -width 40 -yscroll ".about.frame.scroll set" \ | |
485 -wrap none; | |
486 scrollbar .about.frame.scroll -command ".about.frame.text yview"; | |
487 pack .about.frame.text -side left -expand y; | |
488 pack .about.frame.scroll -side right -fill y; | |
489 | |
490 # close button | |
491 button .about.close -text "Close" -command "destroy .about"; | |
492 pack .about.close -side bottom -fill x; | |
493 | |
494 # read text into the text widget | |
495 .about.frame.text insert end "Mserv Client\n"; | |
496 .about.frame.text insert end "Copyright Daniel O'Connor 2000\n"; | |
497 .about.frame.text insert end "\n"; | |
498 .about.frame.text insert end "http://www.dons.net.au/~darius/\n"; | |
499 } | |
500 | |
501 proc set_vol {vol} { | |
502 global state; | |
503 | |
5 | 504 set cookie [acquire_lock]; |
1 | 505 n_write "VOLUME $vol" |
506 n_getrtn rtn; | |
5 | 507 release_lock $cookie; |
508 | |
1 | 509 if {$rtn(code) != 255} { |
7 | 510 log 0 "Couldn't set volume ($rtn(code) $rtn(data))"; |
1 | 511 } |
512 } | |
513 | |
514 proc rate_song {rate} { | |
515 global state; | |
516 | |
5 | 517 set cookie [acquire_lock]; |
1 | 518 n_write "RATE $rate"; |
519 n_getrtn rtn; | |
5 | 520 release_lock $cookie; |
1 | 521 |
522 if {$rtn(code) != 270} { | |
7 | 523 log 0 "Failed to get rate song ($rtn(code) $rtn(data))"; |
1 | 524 } |
525 | |
526 } | |
527 | |
528 proc control_player {cmd} { | |
529 global state; | |
530 | |
7 | 531 log 0 "acquiring lock"; |
5 | 532 set cookie [acquire_lock]; |
7 | 533 log 0 "Writing $cmd"; |
1 | 534 n_write "$cmd"; |
7 | 535 log 0 "Wrote $cmd"; |
1 | 536 n_getrtn rtn; |
7 | 537 log 0 "Got rtn"; |
5 | 538 release_lock $cookie; |
7 | 539 log 0 "Lock freed"; |
1 | 540 |
7 | 541 # log 0 "Control Got $rtn(code) $rtn(data)"; |
1 | 542 } |
543 | |
3 | 544 proc gui_top {} { |
545 global state; | |
546 | |
5 | 547 set cookie [acquire_lock]; |
3 | 548 n_write "TOP" |
549 n_getrtn rtn; | |
5 | 550 release_lock $cookie; |
3 | 551 |
552 set msg "List of songs most likely to be played next\n\n"; | |
553 | |
554 foreach t $rtn(lines) { | |
555 set tmp [split $t \011]; | |
556 append msg "[lindex $tmp 0]%\t[lindex $tmp 4] by [lindex $tmp 3]\n"; | |
557 } | |
558 | |
559 msg_box "Top Listing" $msg; | |
560 } | |
561 | |
1 | 562 proc gui_updatesongs {} { |
563 global state songs; | |
564 | |
565 .top.tlist.list delete 0 end; | |
566 | |
567 set tmp ""; | |
568 | |
569 foreach tag [array names songs "*:id"] { | |
570 set a $songs($tag); | |
571 lappend tmp [list $a $songs($a:name) $songs($a:author) $songs($a:albumname)]; | |
572 } | |
573 | |
574 switch -- $state(sortmode) { | |
575 "Title" { | |
576 set idx 1; | |
577 } | |
578 | |
579 "Artist" { | |
580 set idx 2; | |
581 } | |
582 | |
583 "Album" { | |
584 set idx 3; | |
585 } | |
586 | |
587 default { | |
588 set idx 1; | |
589 } | |
590 } | |
591 set tmp [lsort -dictionary -index $idx $tmp]; | |
592 | |
593 foreach a [array names songs *:listid:*] { | |
594 unset songs($a); | |
595 } | |
596 | |
597 set i 0; | |
598 foreach a $tmp { | |
599 .top.tlist.list insert end "'[lindex $a 1]' by '[lindex $a 2]' on '[lindex $a 3]'" | |
600 set songs([lindex $a 0]:listid:$i) $a; | |
601 incr i; | |
602 } | |
603 } | |
604 | |
605 proc gui_updatequeue {} { | |
606 global state songs queue; | |
607 | |
7 | 608 # log 0 "Updating queue"; |
1 | 609 |
610 .bot.queue.list delete 0 end; | |
611 | |
612 con_getqueue queue; | |
613 | |
614 foreach tag [lsort [array names queue]] { | |
615 .bot.queue.list insert end "'$songs($queue($tag):name)' by '$songs($queue($tag):author)' on '$songs($queue($tag):albumname)'"; | |
616 } | |
617 } | |
618 | |
619 proc gui_delqueue {id} { | |
620 global queue; | |
621 | |
622 if {$id == ""} { | |
623 return; | |
624 } | |
625 | |
5 | 626 set cookie [acquire_lock]; |
1 | 627 n_write "UNQUEUE [split $queue($id) {:}]"; |
628 n_getrtn rtn; | |
5 | 629 release_lock $cookie; |
630 | |
1 | 631 if {$rtn(code) != 254} { |
7 | 632 log 0 "Failed to remove $id ($queue($id))"; |
1 | 633 msg_box "Queue" "Failed to dequeue the song"; |
634 } | |
635 } | |
636 | |
637 proc gui_updateinfo {} { | |
638 global state; | |
639 | |
5 | 640 set cookie [acquire_lock]; |
1 | 641 n_write "VOLUME"; |
642 n_getrtn rtn; | |
5 | 643 release_lock $cookie; |
1 | 644 |
645 if {$rtn(code) != 235} { | |
646 set vol "??"; | |
647 } else { | |
648 set vol "[lindex [lindex $rtn(lines) 0] 0]"; | |
649 } | |
650 | |
5 | 651 set cookie [acquire_lock]; |
1 | 652 n_write "INFO"; |
653 n_getrtn rtn; | |
5 | 654 release_lock $cookie; |
1 | 655 |
656 if {$rtn(code) == 246} { | |
657 set data [split [lindex $rtn(lines) 0] "\t"]; | |
658 set author [lindex $data 4]; | |
659 set title [lindex $data 5]; | |
660 set length [lindex $data 14]; | |
661 set album [lindex $data 3]; | |
3 | 662 set rate1 [lindex $data 9]; |
663 set rate2 [lindex $data 10]; | |
664 set misc [lindex $data 15]; | |
1 | 665 } else { |
666 set author "N/A"; | |
667 set title "N/A"; | |
668 set length "N/A"; | |
669 set album "N/A"; | |
3 | 670 set rate1 "N/A"; |
671 set rate2 "N/A"; | |
672 set misc "N/A"; | |
1 | 673 if {$rtn(code) != 401} { |
7 | 674 log 0 "Failed to get track info ($rtn(code) $rtn(data))"; |
1 | 675 } |
676 } | |
677 | |
5 | 678 set cookie [acquire_lock]; |
1 | 679 n_write "STATUS"; |
680 n_getrtn rtn; | |
5 | 681 release_lock $cookie; |
1 | 682 |
683 if {$rtn(code) != 222} { | |
684 set played "x:xx"; | |
685 } else { | |
686 set played [lindex [split [lindex $rtn(lines) 0] "\t"] 8]; | |
687 } | |
688 | |
689 .top.tinfo.sub.author configure -text "Author: $author"; | |
690 .top.tinfo.sub.title configure -text "Title: $title"; | |
691 .top.tinfo.sub.length configure -text "Length: $length"; | |
6
b370e0bbe050
Remove the check every second, since it breaks stuff :-/
darius
parents:
5
diff
changeset
|
692 # .top.tinfo.sub.time configure -text "Time: $played"; |
1 | 693 .top.tinfo.sub.album configure -text "Album: $album"; |
3 | 694 .top.tinfo.sub.misc configure -text "Misc: $misc"; |
695 .top.tinfo.sub.rate1 configure -text "Rating: $rate1"; | |
696 .top.tinfo.sub.rate2 configure -text "Temporally Adjusted: $rate2"; | |
1 | 697 .top.tinfo.sub.vol configure -text "Volume: $vol"; |
5 | 698 |
1 | 699 } |
700 | |
701 proc con_getqueue {queuevar} { | |
702 upvar $queuevar queue; | |
703 | |
704 global state; | |
705 | |
706 catch {unset queue}; | |
707 | |
5 | 708 set cookie [acquire_lock]; |
1 | 709 n_write "QUEUE" |
710 n_getrtn rtn; | |
5 | 711 release_lock $cookie; |
712 | |
1 | 713 if {$rtn(code) == 225} { |
714 set i 0; | |
715 foreach line $rtn(lines) { | |
716 set foo [split $line \011]; | |
717 set id "[lindex $foo 1]:[lindex $foo 2]"; | |
718 | |
719 set queue($i) $id; | |
720 incr i; | |
721 } | |
722 } elseif {$rtn(code) == 404} { | |
7 | 723 # log 0 "Queue empty"; |
1 | 724 } else { |
7 | 725 log 0 "Failed to get queue ($rtn(code) $rtn(data))"; |
1 | 726 } |
727 } | |
728 | |
729 proc con_getsongs {songsvar albumsvar} { | |
730 upvar $songsvar songs; | |
731 upvar $albumsvar albums; | |
732 | |
733 global state; | |
734 | |
735 catch { unset songs }; | |
736 | |
737 foreach i [array names albums "*:"] { | |
5 | 738 set cookie [acquire_lock]; |
1 | 739 n_write "TRACKS $albums($i)"; |
740 n_getrtn rtn; | |
5 | 741 release_lock $cookie; |
742 | |
1 | 743 if {$rtn(code) != "228"} { |
744 error "Got bogus response to track request ($rtn(code) $rtn(data))"; | |
745 } | |
746 | |
747 foreach trk $rtn(lines) { | |
748 set foo [split $trk \011]; | |
749 if {[llength $foo] != 6} { | |
750 continue; | |
751 } | |
752 | |
753 set albid [lindex $foo 0]; | |
754 set num [lindex $foo 1] | |
755 set songs($albid:$num:id) "$albid:$num"; | |
756 set songs($albid:$num:author) [lindex $foo 2]; | |
757 set songs($albid:$num:name) [lindex $foo 3]; | |
758 set songs($albid:$num:rating) [lindex $foo 4]; | |
759 set songs($albid:$num:length) [lindex $foo 5]; | |
760 set songs($albid:$num:albumname) $albums($albid:name); | |
761 } | |
762 } | |
763 } | |
764 | |
765 proc con_getalbums {albumsvar} { | |
766 upvar $albumsvar albums; | |
767 | |
768 global state; | |
769 | |
770 catch {unset albums}; | |
771 | |
5 | 772 set cookie [acquire_lock]; |
1 | 773 n_write "ALBUMS"; |
774 n_getrtn rtn; | |
5 | 775 release_lock $cookie; |
776 | |
1 | 777 if {$rtn(code) != 227} { |
778 error "Server gave bogus response to album request ($rtn(code) $rtn(data))"; | |
779 } | |
780 | |
781 foreach alb $rtn(lines) { | |
782 set foo [split $alb \011]; | |
783 set id [lindex $foo 0]; | |
5 | 784 if {$id == ""} { |
785 continue; | |
786 } | |
787 set albums($id:) $id; | |
1 | 788 set albums($id:author) [lindex $foo 1]; |
789 set albums($id:name) [lindex $foo 2]; | |
790 | |
7 | 791 # log 0 "Album $id, ID '$albums($id:)' called '$albums($id:name)' by '$albums($id:author)'"; |
1 | 792 } |
793 } | |
794 | |
795 proc update_timer {} { | |
796 | |
6
b370e0bbe050
Remove the check every second, since it breaks stuff :-/
darius
parents:
5
diff
changeset
|
797 # gui_updateinfo; |
1 | 798 |
799 after 900 update_timer; | |
800 } | |
801 | |
802 proc con_mserv {} { | |
803 global state; | |
804 | |
805 # Close old FD | |
806 catch {close state(serv_fd)}; | |
807 catch {unset state(serv_fd)}; | |
808 | |
809 catch {fileevent $state(serv_fd) readable ""}; | |
810 set state(serv_fd) [ socket $state(host) $state(port) ]; | |
811 set state(pushbuf) ""; | |
812 fileevent $state(serv_fd) readable n_rtinput; | |
813 fconfigure $state(serv_fd) -blocking 0; | |
814 | |
815 # Greeting from server | |
816 n_getrtn rtn; | |
7 | 817 log 0 "$rtn(data)"; |
1 | 818 if {$rtn(code) != "200"} { |
819 error "Server failed to send greeting"; | |
820 } | |
821 | |
822 # Login | |
823 n_write "USER $state(user)" | |
824 n_getrtn rtn; | |
825 if {$rtn(code) != "201"} { | |
826 error "Server failed to send password request"; | |
827 } | |
828 | |
829 n_write "PASS $state(pass) RTCOMPUTER"; | |
830 n_getrtn rtn; | |
831 if {$rtn(code) == "507"} { | |
832 error "Server rejected our credentials"; | |
833 } | |
834 | |
835 if {$rtn(code) != "202"} { | |
836 error "Unknown response to PASS command - $rtn(code) $rtn(data)" | |
837 } | |
5 | 838 |
839 set state(lock) ""; | |
840 # trace variable state(lock) rw foobar; | |
841 | |
7 | 842 log 0 "Logged in"; |
1 | 843 } |
844 | |
845 proc n_write {text} { | |
846 global state; | |
847 | |
848 puts $state(serv_fd) $text; | |
5 | 849 flush $state(serv_fd); |
1 | 850 |
5 | 851 if {[eof $state(serv_fd)]} { |
7 | 852 log 0 "Server went away on write"; |
5 | 853 exit 1; |
854 } | |
7 | 855 # log 0 "Wrote - $text"; |
1 | 856 } |
857 | |
858 proc n_rthandler {code data} { | |
859 global songs; | |
860 | |
7 | 861 # log 0 "Got RT - $code $data"; |
1 | 862 |
863 switch -- $code { | |
864 600 { | |
7 | 865 log 0 "User '$data' connected"; |
1 | 866 } |
867 | |
868 601 { | |
7 | 869 log 0 "User '$data' disconnected"; |
1 | 870 } |
871 | |
5 | 872 240 - |
6
b370e0bbe050
Remove the check every second, since it breaks stuff :-/
darius
parents:
5
diff
changeset
|
873 602 - |
b370e0bbe050
Remove the check every second, since it breaks stuff :-/
darius
parents:
5
diff
changeset
|
874 615 - |
1 | 875 618 - |
876 619 - | |
5 | 877 620 - |
1 | 878 622 - |
5 | 879 623 - |
1 | 880 627 - |
5 | 881 628 - |
882 629 { | |
7 | 883 # log 0 "Updating queue on idle"; |
6
b370e0bbe050
Remove the check every second, since it breaks stuff :-/
darius
parents:
5
diff
changeset
|
884 gui_updateinfo; |
5 | 885 gui_updatequeue; |
886 } | |
887 | |
888 default { | |
7 | 889 log 0 "Got unhandled RT event $code $data"; |
1 | 890 } |
891 } | |
892 } | |
893 | |
894 proc n_rtinput {} { | |
895 global state; | |
896 | |
897 set rth ""; | |
898 | |
5 | 899 while {1} { |
900 set line [gets $state(serv_fd)]; | |
901 if {[eof $state(serv_fd)]} { | |
7 | 902 log 0 "Server went away on read"; |
5 | 903 exit 1; |
904 } | |
7 | 905 log 0 "Read - $line"; |
5 | 906 if {$line == ""} { |
907 return; | |
1 | 908 } |
5 | 909 # Check for RT text |
910 set foo [split $line "\t"]; | |
911 if {[string index $line 0] == "="} { | |
912 lappend state(rtlist) [list [string range [lindex $foo 0] 1 3] [lrange $foo 1 end]]; | |
7 | 913 log 0 "RT event"; |
5 | 914 } else { |
915 lappend state(tmpphrase) $line | |
916 if {$line == "."} { | |
917 set state(pushbuf) [linsert $state(pushbuf) 0 $state(tmpphrase)]; | |
7 | 918 log 0 "push buffer - '$state(tmpphrase)'"; |
5 | 919 set state(tmpphrase) ""; |
920 } | |
921 } | |
922 } | |
1 | 923 } |
924 | |
925 proc n_getrtn {var} { | |
926 upvar $var rtn; | |
927 global state; | |
928 | |
929 set gotcode 0; | |
930 catch {unset rtn(code)}; | |
931 catch {unset rtn(data)}; | |
932 catch {unset rtn(lines)} | |
933 | |
934 while {[llength $state(pushbuf)] == 0} { | |
7 | 935 log 0 "Sleeping for data"; |
1 | 936 vwait state(pushbuf); |
937 } | |
5 | 938 |
7 | 939 log 0 "Waking up, got $state(pushbuf)"; |
5 | 940 |
1 | 941 set buf [lindex $state(pushbuf) 0]; |
942 set state(pushbuf) [lrange $state(pushbuf) 1 end]; | |
943 | |
944 while {1} { | |
945 if {[llength $buf] == 0} { | |
946 break; | |
947 } | |
948 | |
949 set line [lindex $buf 0]; | |
950 set buf [lrange $buf 1 end]; | |
951 | |
952 if {[string index $line 0] == "."} { | |
953 break; | |
954 } | |
955 | |
956 if {$gotcode == 0} { | |
957 set rtn(code) [string range $line 0 2]; | |
958 set rtn(data) [string range $line 4 end]; | |
959 set gotcode 1; | |
960 continue; | |
961 } | |
962 | |
963 lappend rtn(lines) $line; | |
964 } | |
965 | |
966 if {$gotcode == 0} { | |
7 | 967 log 0 "Failed to parse phrase (got . before server response)"; |
1 | 968 } |
969 } | |
970 | |
971 ################################################################## | |
972 # Log a message to stderr | |
973 # | |
7 | 974 proc log {level message} { |
975 global state; | |
976 | |
1 | 977 # Extract the calling function's name |
3 | 978 if {[catch {set fname [lindex [info level -1] 0]}]} { |
979 set fname "unknown"; | |
980 } | |
1 | 981 |
7 | 982 if {$state(loglevel) > $level} { |
983 # Emit the message | |
984 catch { | |
985 puts stderr "[clock format [clock seconds] -format {%y/%m/%d %H:%M:%S} -gmt yes]:$fname: $message"; | |
986 flush stderr; | |
987 } | |
5 | 988 } |
989 } | |
990 | |
991 proc acquire_lock {} { | |
992 global state; | |
993 | |
994 # Extract the calling function's name | |
995 if {[catch {set fname [lindex [info level -1] 0]}]} { | |
996 set fname "unknown"; | |
997 } | |
998 | |
7 | 999 log 0 "Acquiring lock for $fname"; |
5 | 1000 |
1001 set foo 0; | |
1002 | |
1003 if {[info exists state(lock)]} { | |
1004 while {$state(lock) != ""} { | |
1005 set foo 1; | |
7 | 1006 log 0 "$fname waiting for lock (held by [lindex $state(lock) 1])"; |
5 | 1007 vwait state(lock); |
1008 } | |
1009 | |
1010 if {$foo == 1} { | |
7 | 1011 log 0 "Lock released"; |
5 | 1012 } |
1013 | |
1014 } | |
1015 | |
1016 set cookie [clock clicks]; | |
1017 set state(lock) [list $cookie $fname]; | |
7 | 1018 log 0 "Lock acquired"; |
5 | 1019 return $cookie; |
1 | 1020 } |
1021 | |
5 | 1022 proc release_lock {cookie} { |
1023 global state; | |
1024 | |
1025 # Extract the calling function's name | |
1026 if {[catch {set fname [lindex [info level -1] 0]}]} { | |
1027 set fname "unknown"; | |
1028 } | |
1029 | |
1030 if {$cookie == ""} { | |
7 | 1031 log 0 "$fname trying to unlock without being locked"; |
5 | 1032 exit 1; |
1033 } | |
1034 | |
1035 if {$cookie != [lindex $state(lock) 0]} { | |
7 | 1036 log 0 "Lock cookie not matched!"; |
5 | 1037 exit 1; |
1038 } | |
1039 | |
1040 if {$fname != [lindex $state(lock) 1]} { | |
7 | 1041 log 0 "$fname tried to free [lindex $state(lock) 1]'s lock!"; |
5 | 1042 exit 1; |
1043 } | |
1044 | |
1045 set state(lock) ""; | |
7 | 1046 log 0 "Lock for $fname now free"; |
5 | 1047 } |
1048 | |
1049 proc foobar {n1 n2 op} { | |
1050 global state; | |
1051 | |
7 | 1052 log 0 "$op, now $state(lock)"; |
5 | 1053 } |
1054 | |
1055 if {[catch {main} msg]} { | |
6
b370e0bbe050
Remove the check every second, since it breaks stuff :-/
darius
parents:
5
diff
changeset
|
1056 catch {tk_dialog .dummy "Error!" [format "%s\n%s" $msg $errorInfo] error 0 "OK"}; |
b370e0bbe050
Remove the check every second, since it breaks stuff :-/
darius
parents:
5
diff
changeset
|
1057 exit 1; |
5 | 1058 } |
1059 |