eq and pl buttons
[amper.git] / amper.d
blob29ffb172cdf68843629014b8d18ade52c9407810
1 module amper is aliced;
3 import std.concurrency;
5 import arsd.color;
6 import arsd.image;
7 import arsd.simpledisplay;
9 import iv.cmdcon;
10 import iv.cmdcongl;
11 import iv.sdpyutil;
12 import iv.strex;
13 import iv.vfs;
15 import aplayer;
17 import egfx;
19 import amperskin;
20 import amperopts;
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; }
35 });
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 () {
59 bool doQuit =
60 (glconCtlWindow is null || glconCtlWindow.closed) ||
61 (sdampwin is null || sdampwin.closed);
62 if (doQuit) {
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; }
92 concmd("quit");
93 });
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();
107 //glFlush();
108 flushGui();
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;
120 consoleLock();
121 scope(exit) consoleUnlock();
122 conProcessQueue();
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);
135 } else {
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) {
152 if (!evt.success) {
153 conwriteln("ERROR loading '", evt.filename, "'");
154 //glconCtlWindow.close();
155 concmd("song_next");
156 return;
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 () {
187 ampMain.onPaint();
188 //conwriteln("PAINT: sdampwin");
191 sdampwin.visibleForTheFirstTime = delegate () {
192 vglRegisterWindow!"main"(sdampwin);
193 prepareTrayIcon();
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) {
212 if (mainDrag) {
213 if (event.type == MouseEventType.motion /*&& event.modifierState&ModifierState.leftButtonDown*/) {
214 flushGui();
215 Window dummyw;
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;
222 mainDrawPrevX = nx;
223 mainDrawPrevY = ny;
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);
236 flushGui();
238 if (event.type == MouseEventType.buttonReleased && event.button == MouseButton.left) mainDrag = false;
239 return;
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) {
245 flushGui();
246 Window dummyw;
247 mainDrag = true;
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 () {
285 ampPList.onPaint();
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);
294 flushGui();
296 Window dummyw;
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);
301 flushGui();
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) {
332 //vbNewScale = 2;
333 //vbufEffScale = 2;
334 glconShowKey = "M-Grave";
336 conRegVar!vbNewScale(1, 8, "v_scale", "window scale");
338 aplayStart();
339 scope(exit) aplayShutdown();
341 conProcessQueue(); // load config
342 conProcessArgs!true(args);
344 loadSkin();
346 version(none) {
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));
354 } else {
355 // scan directory
356 import std.file;
357 foreach (DirEntry de; dirEntries("/home/www/dox/muzax/irij/flac/2008_drakon/", SpanMode.shallow)) {
358 if (!de.isFile) continue;
359 try {
360 auto sio = AudioStream.open(VFile(de.name));
361 if (sio.valid) {
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));
364 sio.close();
365 } else {
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);
374 createCtlWindow();
375 createAmpWindow();
376 createPListWindow();
377 dgGreatePListWindow = delegate () { createPListWindow(); };
378 flushGui();
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);
395 flushGui();
396 conProcessQueue(int.max/4);