1 /*
2 * Copyright 2012 Julian Harnath
3 *
4 * This file is part of Milkytracker.
5 *
6 * Milkytracker is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Milkytracker is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Milkytracker. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 #include "MilkyApplication.h"
22
23 #include "KeyCodeMap.h"
24 #include "MidiSetup.h"
25 #include "MilkyWindow.h"
26 #include "Screen.h"
27 #include "Tracker.h"
28 #include "MilkySettings/SettingsMessages.h"
29
30 #include <Deskbar.h>
31 #include <Entry.h>
32 #include <File.h>
33 #include <FindDirectory.h>
34 #include <Node.h>
35 #include <NodeInfo.h>
36 #include <Path.h>
37 #include <String.h>
38
39 #include <stdio.h>
40 #include <string.h>
41
42
43 const char* kMilkySignature = "application/x-vnd.Milky-MilkyTracker";
44
45
MilkyApplication()46 MilkyApplication::MilkyApplication()
47 :
48 BApplication(kMilkySignature),
49 fMilkyWindow(NULL),
50 fTracker(NULL),
51 fTrackerLock(0),
52 fTrackerListening(false),
53 fLaunchOpenFile(NULL),
54 fClockThread(0)
55 {
56 }
57
58
~MilkyApplication()59 MilkyApplication::~MilkyApplication()
60 {
61
62 }
63
64
65 void
ReadyToRun()66 MilkyApplication::ReadyToRun()
67 {
68 status_t status;
69
70 // Setup tracker ----------------------------------------------------------
71 fTracker = new Tracker();
72 PPSize windowSize = fTracker->getWindowSizeFromDatabase();
73 int32 scaleFactor = fTracker->getScreenScaleFactorFromDatabase();
74 bool fullScreen = fTracker->getFullScreenFlagFromDatabase();
75 fTrackerLock = create_sem(1, "MilkyTracker lock");
76
77 if (fTrackerLock < B_OK)
78 debugger("Could not create tracker lock");
79
80 // Setup main window ------------------------------------------------------
81 fMilkyWindow = new MilkyWindow(BRect(100, 100,
82 100 + windowSize.width, 100 + windowSize.height), scaleFactor,
83 fullScreen, fTracker, fTrackerLock);
84 fMilkyWindow->Show();
85
86 // Start tracker ----------------------------------------------------------
87 fTracker->startUp();
88 fTrackerListening = true;
89
90 if (fLaunchOpenFile != NULL) {
91 _LoadFile(fLaunchOpenFile->String());
92 delete fLaunchOpenFile;
93 fLaunchOpenFile = NULL;
94 }
95
96 // Start main clock -------------------------------------------------------
97 fClockThread = spawn_thread(&_ClockThread, "Milky clock",
98 B_URGENT_DISPLAY_PRIORITY, this);
99
100 if (fClockThread < B_OK)
101 debugger("Could not setup clock thread");
102
103 status = resume_thread(fClockThread);
104
105 if (status < B_OK)
106 debugger("Could not start clock thread");
107
108 // Start listening for MIDI input -----------------------------------------
109 MidiSetup::StartMidi(fTracker, fTrackerLock);
110
111 // Load platform-specific settings from file ------------------------------
112 _LoadPlatformSettings();
113 }
114
115
116 bool
QuitRequested()117 MilkyApplication::QuitRequested()
118 {
119 // Stop raising events to the tracker
120 fTrackerListening = false;
121
122 // Stop clock
123 kill_thread(fClockThread);
124
125 // Stop listening to MIDI input
126 MidiSetup::StopMidi();
127
128 // Nobody should be using the tracker now anymore
129 delete_sem(fTrackerLock);
130 delete fTracker;
131
132 return true;
133 }
134
135
136 // #pragma mark - Platform-specific settings
137
138
139 void
MessageReceived(BMessage * message)140 MilkyApplication::MessageReceived(BMessage* message)
141 {
142 switch (message->what) {
143 case kMsg_SwitchCommandControlToggled:
144 _SetSwapCommandControl(message->FindBool("on"));
145 break;
146
147 case kMsg_ChangeFullscreenResolutionToggled:
148 fMilkyWindow->SetSwitchFullscreenResolution(
149 message->FindBool("on"));
150 break;
151
152 case kMsg_MidiInputSelected:
153 MidiSetup::ConnectProducer(message->FindInt32("id"));
154 break;
155
156 case kMsg_MidiRecordVelocityToggled:
157 MidiSetup::SetRecordVelocity(message->FindBool("on"));
158 break;
159
160 case kMsg_MidiVelocityAmplificationChanged:
161 MidiSetup::SetVelocityAmplify(message->FindInt32("value"));
162 break;
163
164 default:
165 BApplication::MessageReceived(message);
166 }
167 }
168
169
170 void
_LoadPlatformSettings()171 MilkyApplication::_LoadPlatformSettings()
172 {
173 // Build path to settings file and check if it exists ---------------------
174 BPath path;
175 find_directory(B_USER_SETTINGS_DIRECTORY, &path);
176 path.Append("MilkyTracker/platform_settings");
177
178 BEntry settingsEntry(path.Path());
179 if (!settingsEntry.Exists())
180 return;
181
182 // Settings file exists, try to open and unflatten into message -----------
183 BFile settingsFile(path.Path(), B_READ_ONLY);
184 if (settingsFile.InitCheck() != B_OK)
185 return;
186
187 BMessage settings;
188 status_t status = settings.Unflatten(&settingsFile);
189 if (status != B_OK)
190 return;
191
192 // Look for known settings and apply them --------------------------------
193 bool switchCommandControl;
194 status = settings.FindBool("switch_command_control",
195 &switchCommandControl);
196 if (status == B_OK)
197 _SetSwapCommandControl(switchCommandControl);
198
199 bool changeFullscreenResolution;
200 status = settings.FindBool("change_fullscreen_resolution",
201 &changeFullscreenResolution);
202 if (status == B_OK)
203 fMilkyWindow->SetSwitchFullscreenResolution(
204 changeFullscreenResolution);
205
206 bool recordVelocity;
207 status = settings.FindBool("record_velocity",
208 &recordVelocity);
209 if (status == B_OK)
210 MidiSetup::SetRecordVelocity(recordVelocity);
211
212 int16 velocityAmplify;
213 status = settings.FindInt16("velocity_amplify",
214 &velocityAmplify);
215 if (status == B_OK)
216 MidiSetup::SetVelocityAmplify(velocityAmplify);
217
218 BString midiInputName;
219 status = settings.FindString("midi_input", &midiInputName);
220 if (status == B_OK)
221 MidiSetup::ConnectProducer(midiInputName);
222 }
223
224
225 void
_SetSwapCommandControl(bool swap)226 MilkyApplication::_SetSwapCommandControl(bool swap)
227 {
228 gSwapCommandControl = swap;
229 if (swap) {
230 gModifierDataCommand.modifier = B_CONTROL_KEY;
231 gModifierDataCommand.modifierLeft = B_LEFT_CONTROL_KEY;
232 gModifierDataCommand.modifierRight = B_RIGHT_CONTROL_KEY;
233 gModifierDataControl.modifier = B_COMMAND_KEY;
234 gModifierDataControl.modifierLeft = B_LEFT_COMMAND_KEY;
235 gModifierDataControl.modifierRight = B_RIGHT_COMMAND_KEY;
236 } else {
237 gModifierDataCommand.modifier = B_COMMAND_KEY;
238 gModifierDataCommand.modifierLeft = B_LEFT_COMMAND_KEY;
239 gModifierDataCommand.modifierRight = B_RIGHT_COMMAND_KEY;
240 gModifierDataControl.modifier = B_CONTROL_KEY;
241 gModifierDataControl.modifierLeft = B_LEFT_CONTROL_KEY;
242 gModifierDataControl.modifierRight = B_RIGHT_CONTROL_KEY;
243 }
244 }
245
246
247 // #pragma mark - Loading files from argv and icon drop
248
249
250 void
ArgvReceived(int32 argc,char ** argv)251 MilkyApplication::ArgvReceived(int32 argc, char** argv)
252 {
253 // We only care for the first command line argument, which can be a file
254 // which we're going to try to open
255 BEntry entry(argv[1]);
256 if (entry.InitCheck() != B_OK)
257 return;
258 if (!entry.Exists()) {
259 fprintf(stderr, "ERROR: file not found '%s'\n", argv[1]);
260 return;
261 }
262
263 entry_ref ref;
264 entry.GetRef(&ref);
265
266 BMessage message(B_REFS_RECEIVED);
267 message.AddRef("refs", &ref);
268 RefsReceived(&message);
269 }
270
271
272 void
RefsReceived(BMessage * message)273 MilkyApplication::RefsReceived(BMessage* message)
274 {
275 status_t status;
276
277 entry_ref ref;
278 status = message->FindRef("refs", &ref);
279 if (status != B_OK)
280 return;
281
282 BPath path(&ref);
283 if (path.InitCheck() != B_OK)
284 return;
285
286 // Check that it's a file with a MIME type of the audio group
287 BNode node(&ref);
288 if (node.InitCheck() != B_OK)
289 return;
290 BNodeInfo nodeInfo(&node);
291 char mimeType[B_MIME_TYPE_LENGTH];
292 nodeInfo.GetType(mimeType);
293 BString mimeTypeString(mimeType);
294 if (mimeTypeString.FindFirst("audio/") != 0)
295 return;
296
297 // If we are just launching the application (and thus the Tracker
298 // is not initialized yet), defer the file load
299 if (IsLaunching())
300 fLaunchOpenFile = new BString(path.Path());
301 else
302 _LoadFile(path.Path());
303 }
304
305
306 void
_LoadFile(const char * path)307 MilkyApplication::_LoadFile(const char* path)
308 {
309 fDragDroppedFile = PPSystemString(path);
310 PPSystemString* pathStringPointer = &fDragDroppedFile;
311 PPEvent event(eFileDragDropped, &pathStringPointer,
312 sizeof(PPSystemString*));
313
314 fMilkyWindow->RaiseEvent(&event);
315 }
316
317
318 // #pragma mark - Clock generator
319
320
321 void
GenerateClock()322 MilkyApplication::GenerateClock()
323 {
324 PPEvent event(eTimer);
325 bigtime_t beginTime;
326
327 // Send clock events to the tracker every 15ms
328 for(;;) {
329 beginTime = system_time();
330
331 if (fTrackerListening) {
332 acquire_sem(fTrackerLock);
333 fMilkyWindow->TrackerScreen()->raiseEvent(&event);
334 release_sem(fTrackerLock);
335 }
336
337 snooze_until(beginTime + 20000, B_SYSTEM_TIMEBASE);
338 }
339 }
340
341
342 status_t
_ClockThread(void * data)343 MilkyApplication::_ClockThread(void* data)
344 {
345 MilkyApplication* app = (MilkyApplication*)data;
346 app->GenerateClock();
347
348 return B_OK;
349 }
350