1 module amper
is aliced
;
3 import std
.concurrency
;
7 import arsd
.simpledisplay
;
23 // ////////////////////////////////////////////////////////////////////////// //
24 __gshared NotificationAreaIcon trayicon
;
25 __gshared Image trayimage
;
26 __gshared MemoryImage icon
; // 0: normal
29 void prepareTrayIcon () {
30 icon
= readPng("skins/notifyicon.png");
31 trayimage
= Image
.fromMemoryImage(icon
);
32 trayicon
= new NotificationAreaIcon("Amper", trayimage
, (MouseButton button
) {
33 //if (button == MouseButton.left) vbwin.switchToWindow();
34 if (button
== MouseButton
.middle
) { trayicon
.close(); trayicon
= null; }
39 // ////////////////////////////////////////////////////////////////////////// //
40 __gshared
ubyte vbNewScale
= 1;
43 // ////////////////////////////////////////////////////////////////////////// //
44 class ScrollTitleEvent
{}
45 __gshared ScrollTitleEvent evScrollTitle
;
47 shared static this () {
48 evScrollTitle
= new ScrollTitleEvent();
52 // ////////////////////////////////////////////////////////////////////////// //
53 __gshared
bool mainDrag
= false;
54 __gshared
int mainDrawPrevX
, mainDrawPrevY
;
57 // ////////////////////////////////////////////////////////////////////////// //
58 void closeAllIfMainIsClosed () {
60 (glconCtlWindow
is null || glconCtlWindow
.closed
) ||
61 (sdampwin
is null || sdampwin
.closed
);
63 if (sdplwin
!is null && !sdplwin
.closed
) sdplwin
.close();
64 if (sdampwin
!is null && !sdampwin
.closed
) sdampwin
.close();
65 if (glconCtlWindow
!is null && !glconCtlWindow
.closed
) glconCtlWindow
.close();
70 // ////////////////////////////////////////////////////////////////////////// //
71 // create hidden control window
72 void createCtlWindow () {
73 sdpyWindowClass
= "AMPER_PLAYER_CTL";
74 glconCtlWindow
= new SimpleWindow(1, 1, "AmperCtl", OpenGlOptions
.no
, Resizability
.fixedSize
, WindowTypes
.hidden
);
76 glconCtlWindow
.onClosing
= delegate () {
77 //conwriteln("closing ctl window...");
78 if (sdplwin
!is null && !sdplwin
.closed
) sdplwin
.close();
79 if (sdampwin
!is null && !sdampwin
.closed
) sdampwin
.close();
82 glconCtlWindow
.onDestroyed
= delegate () {
83 //conwriteln("ctl window destroyed");
84 closeAllIfMainIsClosed();
87 glconCtlWindow
.addEventListener((QuitEvent evt
) {
88 scope(exit
) if (!conQueueEmpty()) glconPostDoConCommands();
89 scope(exit
) closeAllIfMainIsClosed();
90 if (glconCtlWindow
.closed
) return;
91 if (isQuitRequested
) { glconCtlWindow
.close(); return; }
95 void rebuildRepaint () {
96 scope(exit
) if (!conQueueEmpty()) glconPostDoConCommands();
97 scope(exit
) closeAllIfMainIsClosed();
98 if (glconCtlWindow
.closed
) return;
99 if (isQuitRequested
) { glconCtlWindow
.close(); return; }
100 //conwriteln("rebuilding screen");
102 //if (aplayIsPlaying) ampMain.curtime = aplayCurTime;
104 if (sdampwin
!is null && !sdampwin
.closed
) sdampwin
.redrawOpenGlSceneNow();
105 if (sdplwin
!is null && !sdplwin
.closed
) sdplwin
.redrawOpenGlSceneNow();
111 glconCtlWindow
.addEventListener((GLConScreenRebuildEvent evt
) { rebuildRepaint(); });
112 glconCtlWindow
.addEventListener((GLConScreenRepaintEvent evt
) { rebuildRepaint(); });
114 glconCtlWindow
.addEventListener((GLConDoConsoleCommandsEvent evt
) {
115 scope(exit
) if (!conQueueEmpty()) glconPostDoConCommands();
116 scope(exit
) closeAllIfMainIsClosed();
117 bool sendAnother
= false;
118 bool prevVisible
= isConsoleVisible
;
121 scope(exit
) consoleUnlock();
123 sendAnother
= !conQueueEmpty();
125 if (glconCtlWindow
.closed
) return;
126 if (isQuitRequested
) { glconCtlWindow
.close(); return; }
127 if (sendAnother
) glconPostDoConCommands();
129 if (prevVisible || isConsoleVisible) glconPostScreenRepaintDelayed();
130 if (vbNewScale != vbufEffScale) glconPostScreenRepaint();
132 if (vbNewScale
!= vglWindowScale(glconCtlWindow
)) {
133 vbNewScale
= vglScaleWindow(glconCtlWindow
, vbNewScale
);
134 vglScaleWindow(sdplwin
, vbNewScale
);
136 glconPostScreenRebuild();
140 glconCtlWindow
.addEventListener((ScrollTitleEvent evt
) {
141 scope(exit
) if (!conQueueEmpty()) glconPostDoConCommands();
142 scope(exit
) closeAllIfMainIsClosed();
143 if (glconCtlWindow
.closed
) return;
144 if (isQuitRequested
) { glconCtlWindow
.close(); return; }
145 //conwriteln("scrolling title");
146 ampMain
.scrollTitle();
147 if (!glconCtlWindow
.eventQueued
!ScrollTitleEvent
) glconCtlWindow
.postTimeout(evScrollTitle
, 100);
148 glconPostScreenRebuild();
151 glconCtlWindow
.addEventListener((EventFileLoaded evt
) {
153 conwriteln("ERROR loading '", evt
.filename
, "'");
154 //glconCtlWindow.close();
158 //conwriteln("playing '", evt.filename, "': ", evt.artist, " -- ", evt.title);
161 glconCtlWindow
.addEventListener((EventFileComplete evt
) {
162 //glconCtlWindow.close();
163 concmd("song_next tan");
168 // ////////////////////////////////////////////////////////////////////////// //
169 void createAmpWindow () {
170 sdpyWindowClass
= "AMPER_PLAYER";
172 sdampwin
= new SimpleWindow(skinImgMain
.width
*vbufEffScale
, skinImgMain
.height
*vbufEffScale
, "Amper", OpenGlOptions
.yes
, Resizability
.fixedSize
, WindowTypes
.undecorated
);
173 sdampwin
.setMinSize(skinImgMain
.width
*vbufEffScale
, skinImgMain
.height
*vbufEffScale
);
174 sdampwin
.setMaxSize(skinImgMain
.width
*vbufEffScale
, skinImgMain
.height
*vbufEffScale
);
175 //sdwin.hideCursor();
177 static if (is(typeof(&sdampwin
.closeQuery
))) {
178 sdampwin
.closeQuery
= delegate () { concmd("quit"); glconPostDoConCommands(); };
181 sdampwin
.windowResized
= delegate (int wdt
, int hgt
) {
182 sdampwin
.setMinSize(wdt
, hgt
);
183 sdampwin
.setMaxSize(wdt
, hgt
);
186 sdampwin
.redrawOpenGlScene
= delegate () {
188 //conwriteln("PAINT: sdampwin");
191 sdampwin
.visibleForTheFirstTime
= delegate () {
192 vglRegisterWindow
!"main"(sdampwin
);
197 sdampwin.handlePulse = delegate () {
201 sdampwin
.handleKeyEvent
= delegate (KeyEvent event
) {
202 if (event
.pressed
&& event
== "Escape") { concmd("quit"); return; }
203 //if (event.pressed && event == "Plus") { concmd("v_scale 2"); return; }
204 //if (event.pressed && event == "Minus") { concmd("v_scale 1"); return; }
205 //conwriteln(event.toStr);
206 ampMain
.onKey(event
);
207 glconPostScreenRebuild();
208 //sdwin.redrawOpenGlSceneNow();
211 sdampwin
.handleMouseEvent
= delegate (MouseEvent event
) {
213 if (event
.type
== MouseEventType
.motion
/*&& event.modifierState&ModifierState.leftButtonDown*/) {
217 int nx
= event
.x
*vbufEffScale
;
218 int ny
= event
.y
*vbufEffScale
;
219 XTranslateCoordinates(sdampwin
.display
, sdampwin
.window
, RootWindow(sdampwin
.display
, DefaultScreen(sdampwin
.display
)), nx
, ny
, &nx
, &ny
, &dummyw
);
220 int dx
= nx
-mainDrawPrevX
;
221 int dy
= ny
-mainDrawPrevY
;
225 XWindowAttributes xwa
;
226 XGetWindowAttributes(sdampwin
.display
, sdampwin
.window
, &xwa
);
227 XTranslateCoordinates(sdampwin
.display
, sdampwin
.window
, RootWindow(sdampwin
.display
, DefaultScreen(sdampwin
.display
)), xwa
.x
, xwa
.y
, &xwa
.x
, &xwa
.y
, &dummyw
);
228 sdampwin
.move(xwa
.x
+dx
, xwa
.y
+dy
);
230 if (sdplwin
!is null && !sdplwin
.closed
) {
231 XGetWindowAttributes(sdplwin
.display
, sdplwin
.window
, &xwa
);
232 XTranslateCoordinates(sdplwin
.display
, sdplwin
.window
, RootWindow(sdplwin
.display
, DefaultScreen(sdplwin
.display
)), xwa
.x
, xwa
.y
, &xwa
.x
, &xwa
.y
, &dummyw
);
233 sdplwin
.move(xwa
.x
+dx
, xwa
.y
+dy
);
238 if (event
.type
== MouseEventType
.buttonReleased
&& event
.button
== MouseButton
.left
) mainDrag
= false;
241 if (!ampMain
.onMouse(event
)) {
243 if (event.type == MouseEventType.buttonPressed && event.button == MouseButton.left) {
244 if (ampMain.widgetAt(event.x, event.y) is null) {
248 mainDrawPrevX = event.x*vbufEffScale;
249 mainDrawPrevY = event.y*vbufEffScale;
250 XTranslateCoordinates(sdampwin.display, sdampwin.window, RootWindow(sdampwin.display, DefaultScreen(sdampwin.display)), mainDrawPrevX, mainDrawPrevY, &mainDrawPrevX, &mainDrawPrevY, &dummyw);
255 glconPostScreenRebuild();
256 //sdwin.redrawOpenGlSceneNow();
260 sdampwin.handleCharEvent = delegate (dchar ch) {
264 sdampwin
.onFocusChange
= delegate (bool focused
) { ampMain
.focusChanged(focused
); };
266 if (!sdampwin
.eventQueued
!ScrollTitleEvent
) glconCtlWindow
.postEvent(evScrollTitle
);
270 // ////////////////////////////////////////////////////////////////////////// //
271 void createPListWindow () {
272 sdpyWindowClass
= "AMPER_PLAYLIST";
274 sdplwin
= new SimpleWindow(skinImgMain
.width
*vbufEffScale
, skinImgMain
.height
*vbufEffScale
, "Amper Playlist", OpenGlOptions
.yes
, Resizability
.allowResizing
, WindowTypes
.undecorated
);
275 sdplwin
.setMinSize(skinImgMain
.width
*vbufEffScale
, skinImgMain
.height
*vbufEffScale
);
276 sdplwin
.setMaxSize(4096, 4096);
277 //sdplwin.setResizeGranularity(5, 4);
278 sdplwin
.setResizeGranularity(25, 29);
280 static if (is(typeof(&sdplwin
.closeQuery
))) {
281 sdplwin
.closeQuery
= delegate () { /*concmd("quit"); glconPostDoConCommands();*/ };
284 sdplwin
.redrawOpenGlScene
= delegate () {
286 //conwriteln("PAINT: sdplwin");
287 //conwriteln(ampPList.curitem);
290 sdplwin
.visibleForTheFirstTime
= delegate () {
291 vglRegisterWindow
!"normal"(sdplwin
);
292 if (sdampwin
!is null && !sdampwin
.closed
) {
293 switchToWindow(sdampwin
);
297 XWindowAttributes xwa;
298 XGetWindowAttributes(sdampwin.display, sdampwin.window, &xwa);
299 XTranslateCoordinates(sdampwin.display, sdampwin.window, RootWindow(sdampwin.display, DefaultScreen(sdampwin.display)), xwa.x, xwa.y, &xwa.x, &xwa.y, &dummyw);
300 sdplwin.move(xwa.x, xwa.y+sdampwin.height);
306 sdplwin
.handleKeyEvent
= delegate (KeyEvent event
) {
307 ampPList
.onKey(event
);
308 glconPostScreenRebuild();
309 //sdplwin.redrawOpenGlSceneNow();
312 sdplwin
.handleMouseEvent
= delegate (MouseEvent event
) {
313 //conwriteln("PLMOUSE BEFORE: ", ampPList.curitem, " : ", event.type);
314 ampPList
.onMouse(event
);
315 //conwriteln("PLMOUSE AFTER: ", ampPList.curitem, " : ", event.type);
316 glconPostScreenRebuild();
317 //sdplwin.redrawOpenGlSceneNow();
320 sdplwin
.windowResized
= delegate (int wdt
, int hgt
) {
321 ampPList
.imgrc
= GxRect(0, 0, wdt
/vbufEffScale
, hgt
/vbufEffScale
);
322 //vglResizeBuffer(wdt/vbufEffScale, hgt/vbufEffScale, vbufEffScale);
323 //sdplwin.redrawOpenGlSceneNow();
326 sdplwin
.onFocusChange
= delegate (bool focused
) { ampPList
.focusChanged(focused
); };
330 // ////////////////////////////////////////////////////////////////////////// //
331 void main (string
[] args
) {
334 glconShowKey
= "M-Grave";
336 conRegVar
!vbNewScale(1, 8, "v_scale", "window scale");
339 scope(exit
) aplayShutdown();
341 conProcessQueue(); // load config
342 conProcessArgs
!true(args
);
347 ampPList
.appendListItem(PListItem("Sonata Arctica \u2014 Replica", "/mnt/muzax/wtf", 142));
348 ampPList
.appendListItem(PListItem("Zonata \u2014 Geronimo", "/mnt/muzax/wtf", 242));
350 foreach (immutable int idx
; 3..42) {
351 import std
.format
: format
;
352 ampPList
.appendListItem(PListItem("song #%d".format(idx
), "/mnt/muzax/wtf", 100+idx
));
357 foreach (DirEntry
de; dirEntries("/home/www/dox/muzax/irij/flac/2008_drakon/", SpanMode
.shallow
)) {
358 if (!de.isFile
) continue;
360 auto sio
= AudioStream
.open(VFile(de.name
));
362 ampPList
.appendListItem(PListItem(sio
.artist
~" \u2014 "~sio
.title
, de.name
, cast(int)(sio
.timetotal
/1000)));
363 //conwriteln("+++ ", de.name, ": ", sio.artist, " -- ", sio.title, ": ", cast(int)(sio.timetotal/1000));
366 //conwriteln("+++ ", de.name, ": FUUUUUUU");
368 } catch (Exception e
) {}
370 //if (modeShuffle) ampPList.state.curitem = ampPList.findShuffleFirst();
371 //conwriteln(ampPList.findShuffleFirst(), " : ", ampPList.state.shuffleidx, " : ", ampPList.state.curplayingitem);
377 dgGreatePListWindow
= delegate () { createPListWindow(); };
380 conRegFunc
!((int plidx
, bool forcestart
=false) {
381 if (!sdampwin
.closed
) {
382 ampPList
.playSongByIndex(plidx
, forcestart
);
383 glconPostScreenRebuild();
385 })("song_play_by_index", "play song from playlist by index");
387 conRegFunc
!((){ if (!sdampwin
.closed
) ampPList
.playPrevSong(); })("song_prev", "play previous song");
388 conRegFunc
!((bool forceplay
=false){ if (!sdampwin
.closed
) ampPList
.playNextSong(forceplay
); })("song_next", "play next song");
389 conRegFunc
!((){ if (!sdampwin
.closed
) ampPList
.playCurrentSong(); })("song_play", "play current song");
390 conRegFunc
!((){ if (!sdampwin
.closed
) ampPList
.stopSong(); })("song_stop", "stop current song");
391 conRegFunc
!((){ aplayTogglePause(); })("song_pause_toggle", "pause/unpause current song");
393 glconCtlWindow
.eventLoop(0);
396 conProcessQueue(int.max
/4);