iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
IPlugAPP_dialog.cpp
1/*
2 ==============================================================================
3
4 This file is part of the iPlug 2 library. Copyright (C) the iPlug 2 developers.
5
6 See LICENSE.txt for more info.
7
8 ==============================================================================
9*/
10
11#include "IPlugAPP_host.h"
12#include "config.h"
13#include "resource.h"
14
15#include <ctime>
16
17#ifdef OS_WIN
18#include "asio.h"
19#include "win32_utf8.h"
20extern float GetScaleForHWND(HWND hWnd);
21#define GET_MENU() GetMenu(gHWND)
22extern bool SaveWindowScreenshot(HWND hwnd, const char* path);
23#elif defined OS_MAC
24#define GET_MENU() SWELL_GetCurrentMenu()
25extern "C" bool SaveWindowScreenshot(void* hwnd, const char* path);
26#endif
27
28using namespace iplug;
29
30#if !defined NO_IGRAPHICS
31#include "IGraphics.h"
32using namespace igraphics;
33#endif
34
35#define IDT_SCREENSHOT_TIMER 1001
36
37
38// check the input and output devices, find matching srs
39void IPlugAPPHost::PopulateSampleRateList(HWND hwndDlg, RtAudio::DeviceInfo* inputDevInfo, RtAudio::DeviceInfo* outputDevInfo)
40{
41 WDL_String buf;
42
43 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_SR,CB_RESETCONTENT,0,0);
44
45 std::vector<int> matchedSRs;
46
47 for (int i=0; i<inputDevInfo->sampleRates.size(); i++)
48 {
49 for (int j=0; j<outputDevInfo->sampleRates.size(); j++)
50 {
51 if (inputDevInfo->sampleRates[i] == outputDevInfo->sampleRates[j])
52 matchedSRs.push_back(inputDevInfo->sampleRates[i]);
53 }
54 }
55
56 for (int k=0; k<matchedSRs.size(); k++)
57 {
58 buf.SetFormatted(20, "%i", matchedSRs[k]);
59 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_SR,CB_ADDSTRING,0,(LPARAM)buf.Get());
60 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_SR,CB_SETITEMDATA,k,(LPARAM)matchedSRs[k]);
61 }
62
63 WDL_String str;
64 str.SetFormatted(32, "%i", mState.mAudioSR);
65
66 LRESULT sridx = SendDlgItemMessage(hwndDlg, IDC_COMBO_AUDIO_SR, CB_FINDSTRINGEXACT, -1, (LPARAM) str.Get());
67 SendDlgItemMessage(hwndDlg, IDC_COMBO_AUDIO_SR, CB_SETCURSEL, sridx, 0);
68}
69
70void IPlugAPPHost::PopulateAudioInputList(HWND hwndDlg, RtAudio::DeviceInfo* info)
71{
72 WDL_String buf;
73
74 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_IN_L,CB_RESETCONTENT,0,0);
75 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_IN_R,CB_RESETCONTENT,0,0);
76
77 int i;
78
79 for (i=0; i<info->inputChannels -1; i++)
80 {
81 buf.SetFormatted(20, "%i", i+1);
82 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_IN_L,CB_ADDSTRING,0,(LPARAM)buf.Get());
83 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_IN_R,CB_ADDSTRING,0,(LPARAM)buf.Get());
84 }
85
86 // TEMP
87 buf.SetFormatted(20, "%i", i+1);
88 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_IN_R,CB_ADDSTRING,0,(LPARAM)buf.Get());
89
90 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_IN_L,CB_SETCURSEL, mState.mAudioInChanL - 1, 0);
91 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_IN_R,CB_SETCURSEL, mState.mAudioInChanR - 1, 0);
92}
93
94void IPlugAPPHost::PopulateAudioOutputList(HWND hwndDlg, RtAudio::DeviceInfo* info)
95{
96 WDL_String buf;
97
98 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_OUT_L,CB_RESETCONTENT,0,0);
99 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_OUT_R,CB_RESETCONTENT,0,0);
100
101 int i;
102
103 for (i=0; i<info->outputChannels -1; i++)
104 {
105 buf.SetFormatted(20, "%i", i+1);
106 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_OUT_L,CB_ADDSTRING,0,(LPARAM)buf.Get());
107 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_OUT_R,CB_ADDSTRING,0,(LPARAM)buf.Get());
108 }
109
110 // TEMP
111 buf.SetFormatted(20, "%i", i+1);
112 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_OUT_R,CB_ADDSTRING,0,(LPARAM)buf.Get());
113
114 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_OUT_L,CB_SETCURSEL, mState.mAudioOutChanL - 1, 0);
115 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_OUT_R,CB_SETCURSEL, mState.mAudioOutChanR - 1, 0);
116}
117
118// This has to get called after any change to audio driver/in dev/out dev
119void IPlugAPPHost::PopulateDriverSpecificControls(HWND hwndDlg)
120{
121#ifdef OS_WIN
122 int driverType = (int) SendDlgItemMessage(hwndDlg, IDC_COMBO_AUDIO_DRIVER, CB_GETCURSEL, 0, 0);
123 if (driverType == kDeviceASIO)
124 {
125 ComboBox_Enable(GetDlgItem(hwndDlg, IDC_COMBO_AUDIO_IN_DEV), FALSE);
126 Button_Enable(GetDlgItem(hwndDlg, IDC_BUTTON_OS_DEV_SETTINGS), TRUE);
127 }
128 else
129 {
130 ComboBox_Enable(GetDlgItem(hwndDlg, IDC_COMBO_AUDIO_IN_DEV), TRUE);
131 Button_Enable(GetDlgItem(hwndDlg, IDC_BUTTON_OS_DEV_SETTINGS), FALSE);
132 }
133#endif
134
135 int indevidx = 0;
136 int outdevidx = 0;
137
138 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_IN_DEV,CB_RESETCONTENT,0,0);
139 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_OUT_DEV,CB_RESETCONTENT,0,0);
140
141 for (int i = 0; i<mAudioInputDevIDs.size(); i++)
142 {
143 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_IN_DEV,CB_ADDSTRING,0,(LPARAM)GetAudioDeviceName(mAudioInputDevIDs[i]).c_str());
144
145 if (std::string_view(GetAudioDeviceName(mAudioInputDevIDs[i])) == mState.mAudioInDev.Get())
146 indevidx = i;
147 }
148
149 for (int i = 0; i<mAudioOutputDevIDs.size(); i++)
150 {
151 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_OUT_DEV,CB_ADDSTRING,0,(LPARAM)GetAudioDeviceName(mAudioOutputDevIDs[i]).c_str());
152
153 if (std::string_view(GetAudioDeviceName(mAudioOutputDevIDs[i])) == mState.mAudioOutDev.Get())
154 outdevidx = i;
155 }
156
157#ifdef OS_WIN
158 if (driverType == kDeviceASIO)
159 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_IN_DEV,CB_SETCURSEL, outdevidx, 0);
160 else
161#endif
162 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_IN_DEV,CB_SETCURSEL, indevidx, 0);
163
164 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_OUT_DEV,CB_SETCURSEL, outdevidx, 0);
165
166 RtAudio::DeviceInfo inputDevInfo;
167 RtAudio::DeviceInfo outputDevInfo;
168
169 if (mAudioInputDevIDs.size())
170 {
171 inputDevInfo = mDAC->getDeviceInfo(mAudioInputDevIDs[indevidx]);
172 PopulateAudioInputList(hwndDlg, &inputDevInfo);
173 }
174
175 if (mAudioOutputDevIDs.size())
176 {
177 outputDevInfo = mDAC->getDeviceInfo(mAudioOutputDevIDs[outdevidx]);
178 PopulateAudioOutputList(hwndDlg, &outputDevInfo);
179 }
180
181 PopulateSampleRateList(hwndDlg, &inputDevInfo, &outputDevInfo);
182}
183
184void IPlugAPPHost::PopulateAudioDialogs(HWND hwndDlg)
185{
186 PopulateDriverSpecificControls(hwndDlg);
187
188// if (mState.mAudioInIsMono)
189// {
190// SendDlgItemMessage(hwndDlg,IDC_CB_MONO_INPUT,BM_SETCHECK, BST_CHECKED,0);
191// }
192// else
193// {
194// SendDlgItemMessage(hwndDlg,IDC_CB_MONO_INPUT,BM_SETCHECK, BST_UNCHECKED,0);
195// }
196
197// Populate buffer size combobox
198 for (int i = 0; i< kNumBufferSizeOptions; i++)
199 {
200 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_BUF_SIZE,CB_ADDSTRING,0,(LPARAM)kBufferSizeOptions[i].c_str());
201 }
202
203 WDL_String str;
204 str.SetFormatted(32, "%i", mState.mBufferSize);
205
206 LRESULT iovsidx = SendDlgItemMessage(hwndDlg, IDC_COMBO_AUDIO_BUF_SIZE, CB_FINDSTRINGEXACT, -1, (LPARAM) str.Get());
207 SendDlgItemMessage(hwndDlg, IDC_COMBO_AUDIO_BUF_SIZE, CB_SETCURSEL, iovsidx, 0);
208}
209
210bool IPlugAPPHost::PopulateMidiDialogs(HWND hwndDlg)
211{
212 if ( !mMidiIn || !mMidiOut )
213 return false;
214 else
215 {
216 for (int i=0; i<mMidiInputDevNames.size(); i++)
217 {
218 SendDlgItemMessage(hwndDlg,IDC_COMBO_MIDI_IN_DEV,CB_ADDSTRING,0,(LPARAM)mMidiInputDevNames[i].c_str());
219 }
220
221 LRESULT indevidx = SendDlgItemMessage(hwndDlg,IDC_COMBO_MIDI_IN_DEV,CB_FINDSTRINGEXACT, -1, (LPARAM)mState.mMidiInDev.Get());
222
223 // if the midi port name wasn't found update the ini file, and set to off
224 if (indevidx == -1)
225 {
226 mState.mMidiInDev.Set("off");
227 UpdateINI();
228 indevidx = 0;
229 }
230
231 SendDlgItemMessage(hwndDlg,IDC_COMBO_MIDI_IN_DEV,CB_SETCURSEL, indevidx, 0);
232
233 for (int i=0; i<mMidiOutputDevNames.size(); i++)
234 {
235 SendDlgItemMessage(hwndDlg,IDC_COMBO_MIDI_OUT_DEV,CB_ADDSTRING,0,(LPARAM)mMidiOutputDevNames[i].c_str());
236 }
237
238 LRESULT outdevidx = SendDlgItemMessage(hwndDlg,IDC_COMBO_MIDI_OUT_DEV,CB_FINDSTRINGEXACT, -1, (LPARAM)mState.mMidiOutDev.Get());
239
240 // if the midi port name wasn't found update the ini file, and set to off
241 if (outdevidx == -1)
242 {
243 mState.mMidiOutDev.Set("off");
244 UpdateINI();
245 outdevidx = 0;
246 }
247
248 SendDlgItemMessage(hwndDlg,IDC_COMBO_MIDI_OUT_DEV,CB_SETCURSEL, outdevidx, 0);
249
250 // Populate MIDI channel dialogs
251
252 SendDlgItemMessage(hwndDlg,IDC_COMBO_MIDI_IN_CHAN,CB_ADDSTRING,0,(LPARAM)"all");
253 SendDlgItemMessage(hwndDlg,IDC_COMBO_MIDI_OUT_CHAN,CB_ADDSTRING,0,(LPARAM)"all");
254
255 WDL_String buf;
256
257 for (int i=0; i<16; i++)
258 {
259 buf.SetFormatted(20, "%i", i+1);
260 SendDlgItemMessage(hwndDlg,IDC_COMBO_MIDI_IN_CHAN,CB_ADDSTRING,0,(LPARAM)buf.Get());
261 SendDlgItemMessage(hwndDlg,IDC_COMBO_MIDI_OUT_CHAN,CB_ADDSTRING,0,(LPARAM)buf.Get());
262 }
263
264 SendDlgItemMessage(hwndDlg,IDC_COMBO_MIDI_IN_CHAN,CB_SETCURSEL, (LPARAM)mState.mMidiInChan, 0);
265 SendDlgItemMessage(hwndDlg,IDC_COMBO_MIDI_OUT_CHAN,CB_SETCURSEL, (LPARAM)mState.mMidiOutChan, 0);
266
267 return true;
268 }
269}
270
271#ifdef OS_WIN
272void IPlugAPPHost::PopulatePreferencesDialog(HWND hwndDlg)
273{
274 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_DRIVER,CB_ADDSTRING,0,(LPARAM)"DirectSound");
275 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_DRIVER,CB_ADDSTRING,0,(LPARAM)"ASIO");
276 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_DRIVER,CB_SETCURSEL, mState.mAudioDriverType, 0);
277
278 PopulateAudioDialogs(hwndDlg);
279 PopulateMidiDialogs(hwndDlg);
280}
281
282#elif defined OS_MAC
283void IPlugAPPHost::PopulatePreferencesDialog(HWND hwndDlg)
284{
285 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_DRIVER,CB_ADDSTRING,0,(LPARAM)"CoreAudio");
286 //SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_DRIVER,CB_ADDSTRING,0,(LPARAM)"Jack");
287 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_DRIVER,CB_SETCURSEL, mState.mAudioDriverType, 0);
288
289 PopulateAudioDialogs(hwndDlg);
290 PopulateMidiDialogs(hwndDlg);
291}
292#else
293 #error NOT IMPLEMENTED
294#endif
295
296WDL_DLGRET IPlugAPPHost::PreferencesDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
297{
298 IPlugAPPHost* _this = sInstance.get();
299 AppState& mState = _this->mState;
300 AppState& mTempState = _this->mTempState;
301 AppState& mActiveState = _this->mActiveState;
302
303 auto getComboString = [&](WDL_String& str, int item, WPARAM idx) {
304 std::string tempString;
305 long len = (long) SendDlgItemMessage(hwndDlg, item, CB_GETLBTEXTLEN, idx, 0) + 1;
306 tempString.reserve(len);
307 SendDlgItemMessage(hwndDlg, item, CB_GETLBTEXT, idx, (LPARAM) tempString.data());
308 str.Set(tempString.c_str());
309 };
310
311 int v = 0;
312 switch(uMsg)
313 {
314 case WM_INITDIALOG:
315#ifdef OS_WIN
316 WDL_UTF8_HookComboBox(GetDlgItem(hwndDlg, IDC_COMBO_AUDIO_IN_DEV));
317 WDL_UTF8_HookComboBox(GetDlgItem(hwndDlg, IDC_COMBO_AUDIO_OUT_DEV));
318 WDL_UTF8_HookComboBox(GetDlgItem(hwndDlg, IDC_COMBO_MIDI_IN_DEV));
319 WDL_UTF8_HookComboBox(GetDlgItem(hwndDlg, IDC_COMBO_MIDI_OUT_DEV));
320#endif
321 _this->PopulatePreferencesDialog(hwndDlg);
322 mTempState = mState;
323
324 return TRUE;
325
326 case WM_COMMAND:
327 switch (LOWORD(wParam))
328 {
329 case IDOK:
330 if (mActiveState != mState)
331 _this->TryToChangeAudio();
332
333 EndDialog(hwndDlg, IDOK); // INI file will be changed see MainDialogProc
334 break;
335 case IDAPPLY:
336 _this->TryToChangeAudio();
337 break;
338 case IDCANCEL:
339 EndDialog(hwndDlg, IDCANCEL);
340
341 // if state has been changed reset to previous state, INI file won't be changed
342 if (!_this->AudioSettingsInStateAreEqual(mState, mTempState)
343 || !_this->MIDISettingsInStateAreEqual(mState, mTempState))
344 {
345 mState = mTempState;
346
347 _this->TryToChangeAudioDriverType();
348 _this->ProbeAudioIO();
349 _this->TryToChangeAudio();
350 }
351
352 break;
353
354 case IDC_COMBO_AUDIO_DRIVER:
355 if (HIWORD(wParam) == CBN_SELCHANGE)
356 {
357 v = (int) SendDlgItemMessage(hwndDlg, IDC_COMBO_AUDIO_DRIVER, CB_GETCURSEL, 0, 0);
358
359 if (v != mState.mAudioDriverType)
360 {
361 mState.mAudioDriverType = v;
362
363 _this->TryToChangeAudioDriverType();
364 _this->ProbeAudioIO();
365
366 if (_this->mAudioInputDevIDs.size())
367 mState.mAudioInDev.Set(_this->GetAudioDeviceName(_this->mAudioInputDevIDs[0]).c_str());
368
369 if (_this->mAudioOutputDevIDs.size())
370 mState.mAudioOutDev.Set(_this->GetAudioDeviceName(_this->mAudioOutputDevIDs[0]).c_str());
371
372 // Reset IO
373 mState.mAudioOutChanL = 1;
374 mState.mAudioOutChanR = 2;
375
376 _this->PopulateAudioDialogs(hwndDlg);
377 }
378 }
379 break;
380
381 case IDC_COMBO_AUDIO_IN_DEV:
382 if (HIWORD(wParam) == CBN_SELCHANGE)
383 {
384 int idx = (int) SendDlgItemMessage(hwndDlg, IDC_COMBO_AUDIO_IN_DEV, CB_GETCURSEL, 0, 0);
385 getComboString(mState.mAudioInDev, IDC_COMBO_AUDIO_IN_DEV, idx);
386
387 // Reset IO
388 mState.mAudioInChanL = 1;
389 mState.mAudioInChanR = 2;
390
391 _this->PopulateDriverSpecificControls(hwndDlg);
392 }
393 break;
394
395 case IDC_COMBO_AUDIO_OUT_DEV:
396 if (HIWORD(wParam) == CBN_SELCHANGE)
397 {
398 int idx = (int) SendDlgItemMessage(hwndDlg, IDC_COMBO_AUDIO_OUT_DEV, CB_GETCURSEL, 0, 0);
399 getComboString(mState.mAudioOutDev, IDC_COMBO_AUDIO_OUT_DEV, idx);
400
401 // Reset IO
402 mState.mAudioOutChanL = 1;
403 mState.mAudioOutChanR = 2;
404
405 _this->PopulateDriverSpecificControls(hwndDlg);
406 }
407 break;
408
409 case IDC_COMBO_AUDIO_IN_L:
410 if (HIWORD(wParam) == CBN_SELCHANGE)
411 {
412 mState.mAudioInChanL = (int) SendDlgItemMessage(hwndDlg, IDC_COMBO_AUDIO_IN_L, CB_GETCURSEL, 0, 0) + 1;
413
414 //TEMP
415 mState.mAudioInChanR = mState.mAudioInChanL + 1;
416 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_IN_R,CB_SETCURSEL, mState.mAudioInChanR - 1, 0);
417 //
418 }
419 break;
420
421 case IDC_COMBO_AUDIO_IN_R:
422 if (HIWORD(wParam) == CBN_SELCHANGE)
423 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_IN_R,CB_SETCURSEL, mState.mAudioInChanR - 1, 0); // TEMP
424 mState.mAudioInChanR = (int) SendDlgItemMessage(hwndDlg, IDC_COMBO_AUDIO_IN_R, CB_GETCURSEL, 0, 0);
425 break;
426
427 case IDC_COMBO_AUDIO_OUT_L:
428 if (HIWORD(wParam) == CBN_SELCHANGE)
429 {
430 mState.mAudioOutChanL = (int) SendDlgItemMessage(hwndDlg, IDC_COMBO_AUDIO_OUT_L, CB_GETCURSEL, 0, 0) + 1;
431
432 //TEMP
433 mState.mAudioOutChanR = mState.mAudioOutChanL + 1;
434 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_OUT_R,CB_SETCURSEL, mState.mAudioOutChanR - 1, 0);
435 //
436 }
437 break;
438
439 case IDC_COMBO_AUDIO_OUT_R:
440 if (HIWORD(wParam) == CBN_SELCHANGE)
441 SendDlgItemMessage(hwndDlg,IDC_COMBO_AUDIO_OUT_R,CB_SETCURSEL, mState.mAudioOutChanR - 1, 0); // TEMP
442 mState.mAudioOutChanR = (int) SendDlgItemMessage(hwndDlg, IDC_COMBO_AUDIO_OUT_R, CB_GETCURSEL, 0, 0);
443 break;
444
445// case IDC_CB_MONO_INPUT:
446// if (SendDlgItemMessage(hwndDlg,IDC_CB_MONO_INPUT, BM_GETCHECK, 0, 0) == BST_CHECKED)
447// mState.mAudioInIsMono = 1;
448// else
449// mState.mAudioInIsMono = 0;
450// break;
451
452 case IDC_COMBO_AUDIO_BUF_SIZE: // follow through
453 if (HIWORD(wParam) == CBN_SELCHANGE)
454 {
455 int iovsidx = (int) SendDlgItemMessage(hwndDlg, IDC_COMBO_AUDIO_BUF_SIZE, CB_GETCURSEL, 0, 0);
456 mState.mBufferSize = atoi(kBufferSizeOptions[iovsidx].c_str());
457 }
458 break;
459 case IDC_COMBO_AUDIO_SR:
460 if (HIWORD(wParam) == CBN_SELCHANGE)
461 {
462 int idx = (int) SendDlgItemMessage(hwndDlg, IDC_COMBO_AUDIO_SR, CB_GETCURSEL, 0, 0);
463 mState.mAudioSR = (uint32_t) SendDlgItemMessage(hwndDlg, IDC_COMBO_AUDIO_SR, CB_GETITEMDATA, idx, 0);
464 }
465 break;
466
467 case IDC_BUTTON_OS_DEV_SETTINGS:
468 if (HIWORD(wParam) == BN_CLICKED)
469 {
470 #ifdef OS_WIN
471 if ( (_this->mState.mAudioDriverType == kDeviceASIO) && (_this->mDAC->isStreamRunning() == true)) // TODO: still not right
472 ASIOControlPanel();
473 #elif defined OS_MAC
474 if (SWELL_GetOSXVersion() >= 0x1200)
475 {
476 system("open \"/System/Applications/Utilities/Audio MIDI Setup.app\"");
477 }
478 else
479 {
480 system("open \"/Applications/Utilities/Audio MIDI Setup.app\"");
481 }
482 #else
483 #error NOT IMPLEMENTED
484 #endif
485 }
486 break;
487
488 case IDC_COMBO_MIDI_IN_DEV:
489 if (HIWORD(wParam) == CBN_SELCHANGE)
490 {
491 int idx = (int) SendDlgItemMessage(hwndDlg, IDC_COMBO_MIDI_IN_DEV, CB_GETCURSEL, 0, 0);
492 getComboString(mState.mMidiInDev, IDC_COMBO_MIDI_IN_DEV, idx);
493 _this->SelectMIDIDevice(ERoute::kInput, mState.mMidiInDev.Get());
494 }
495 break;
496
497 case IDC_COMBO_MIDI_OUT_DEV:
498 if (HIWORD(wParam) == CBN_SELCHANGE)
499 {
500 int idx = (int) SendDlgItemMessage(hwndDlg, IDC_COMBO_MIDI_OUT_DEV, CB_GETCURSEL, 0, 0);
501 getComboString(mState.mMidiOutDev, IDC_COMBO_MIDI_OUT_DEV, idx);
502 _this->SelectMIDIDevice(ERoute::kOutput, mState.mMidiOutDev.Get());
503 }
504 break;
505
506 case IDC_COMBO_MIDI_IN_CHAN:
507 if (HIWORD(wParam) == CBN_SELCHANGE)
508 mState.mMidiInChan = (int) SendDlgItemMessage(hwndDlg, IDC_COMBO_MIDI_IN_CHAN, CB_GETCURSEL, 0, 0);
509 break;
510
511 case IDC_COMBO_MIDI_OUT_CHAN:
512 if (HIWORD(wParam) == CBN_SELCHANGE)
513 mState.mMidiOutChan = (int) SendDlgItemMessage(hwndDlg, IDC_COMBO_MIDI_OUT_CHAN, CB_GETCURSEL, 0, 0);
514 break;
515
516 default:
517 break;
518 }
519 break;
520 default:
521 return FALSE;
522 }
523 return TRUE;
524}
525
526static void ClientResize(HWND hWnd, int width, int height)
527{
528 RECT rcClient, rcWindow;
529 POINT ptDiff;
530 int screenwidth, screenheight;
531 int x, y;
532
533 screenwidth = GetSystemMetrics(SM_CXSCREEN);
534 screenheight = GetSystemMetrics(SM_CYSCREEN);
535 x = (screenwidth / 2) - (width / 2);
536 y = (screenheight / 2) - (height / 2);
537
538 GetClientRect(hWnd, &rcClient);
539 GetWindowRect(hWnd, &rcWindow);
540
541 ptDiff.x = (rcWindow.right - rcWindow.left) - rcClient.right;
542 ptDiff.y = (rcWindow.bottom - rcWindow.top) - rcClient.bottom;
543
544 SetWindowPos(hWnd, 0, x, y, width + ptDiff.x, height + ptDiff.y, 0);
545}
546
547//static
548WDL_DLGRET IPlugAPPHost::MainDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
549{
550 IPlugAPPHost* pAppHost = IPlugAPPHost::sInstance.get();
551
552 switch (uMsg)
553 {
554 case WM_INITDIALOG:
555 {
556 gHWND = hwndDlg;
557 IPlugAPP* pPlug = pAppHost->GetPlug();
558
559 if (!pAppHost->OpenWindow(gHWND))
560 {
561 DBGMSG("couldn't attach gui\n");
562 }
563
564 ClientResize(hwndDlg, pPlug->GetEditorWidth(), pPlug->GetEditorHeight());
565
566 ShowWindow(hwndDlg, SW_SHOW);
567
568 // If in screenshot mode, start timer to take screenshot after UI initializes
569 if (pAppHost->IsScreenshotMode())
570 {
571 SetTimer(hwndDlg, IDT_SCREENSHOT_TIMER, 500, nullptr); // 500ms delay
572 }
573
574 return 1;
575 }
576 case WM_TIMER:
577 {
578 if (wParam == IDT_SCREENSHOT_TIMER)
579 {
580 KillTimer(hwndDlg, IDT_SCREENSHOT_TIMER);
581
582 SaveWindowScreenshot(gHWND, pAppHost->GetScreenshotPath());
583
584 // Exit the application
585 DestroyWindow(hwndDlg);
586 return 0;
587 }
588 break;
589 }
590 case WM_DESTROY:
591 pAppHost->CloseWindow();
592 gHWND = NULL;
593 IPlugAPPHost::sInstance = nullptr;
594
595 #ifdef OS_WIN
596 PostQuitMessage(0);
597 #else
598 SWELL_PostQuitMessage(hwndDlg);
599 #endif
600
601 return 0;
602 case WM_CLOSE:
603 DestroyWindow(hwndDlg);
604 return 0;
605 case WM_COMMAND:
606 switch (LOWORD(wParam))
607 {
608 case ID_QUIT:
609 {
610 DestroyWindow(hwndDlg);
611 return 0;
612 }
613 case ID_ABOUT:
614 {
615 IPlugAPP* pPlug = pAppHost->GetPlug();
616
617 bool pluginOpensAboutBox = pPlug->OnHostRequestingAboutBox();
618
619 if (pluginOpensAboutBox == false)
620 {
621 WDL_String info;
622 info.Append(PLUG_COPYRIGHT_STR"\nBuilt on " __DATE__);
623 MessageBox(hwndDlg, info.Get(), PLUG_NAME, MB_OK);
624 }
625
626 return 0;
627 }
628 case ID_HELP:
629 {
630 IPlugAPP* pPlug = pAppHost->GetPlug();
631
632 bool pluginOpensHelp = pPlug->OnHostRequestingProductHelp();
633
634 if (pluginOpensHelp == false)
635 {
636 MessageBox(hwndDlg, "See the manual", PLUG_NAME, MB_OK);
637 }
638 return 0;
639 }
640 case ID_PREFERENCES:
641 {
642 INT_PTR ret = DialogBox(gHINSTANCE, MAKEINTRESOURCE(IDD_DIALOG_PREF), hwndDlg, IPlugAPPHost::PreferencesDlgProc);
643
644 if (ret == IDOK)
645 pAppHost->UpdateINI();
646
647 return 0;
648 }
649#ifdef ID_SCREENSHOT
650 case ID_SCREENSHOT:
651 {
652 // Generate filename with timestamp
653 WDL_String path;
654 char timestamp[32];
655 time_t now = time(nullptr);
656 strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", localtime(&now));
657
658 // Get temp directory
659 #ifdef OS_WIN
660 char tempPath[MAX_PATH];
661 GetTempPathA(MAX_PATH, tempPath);
662 path.SetFormatted(512, "%s%s_screenshot_%s.png",
663 tempPath,
664 PLUG_NAME,
665 timestamp);
666 #elif defined OS_MAC
667 const char* tmpDir = getenv("TMPDIR");
668 path.SetFormatted(512, "%s%s_screenshot_%s.png",
669 tmpDir ? tmpDir : "/tmp/",
670 PLUG_NAME,
671 timestamp);
672 #endif
673
674 if (SaveWindowScreenshot(gHWND, path.Get()))
675 {
676 WDL_String msg;
677 msg.SetFormatted(512, "Screenshot saved to:\n%s\n\nOpen it?", path.Get());
678 int result = MessageBox(hwndDlg, msg.Get(), "Screenshot Saved", MB_YESNO);
679
680 if (result == IDYES)
681 {
682 #ifdef OS_WIN
683 ShellExecuteA(NULL, "open", path.Get(), NULL, NULL, SW_SHOWNORMAL);
684 #elif defined OS_MAC
685 WDL_String cmd;
686 cmd.SetFormatted(1024, "open \"%s\"", path.Get());
687 system(cmd.Get());
688 #endif
689 }
690 }
691 else
692 {
693 MessageBox(hwndDlg, "Failed to save screenshot", "Error", MB_OK);
694 }
695
696 return 0;
697 }
698#endif
699#if defined _DEBUG && !defined NO_IGRAPHICS
700 case ID_LIVE_EDIT:
701 {
702 IGEditorDelegate* pPlug = dynamic_cast<IGEditorDelegate*>(pAppHost->GetPlug());
703
704 if (pPlug)
705 {
706 IGraphics* pGraphics = pPlug->GetUI();
707
708 if (pGraphics)
709 {
710 bool enabled = pGraphics->LiveEditEnabled();
711 pGraphics->EnableLiveEdit(!enabled);
712 CheckMenuItem(GET_MENU(), ID_LIVE_EDIT, (MF_BYCOMMAND | enabled) ? MF_UNCHECKED : MF_CHECKED);
713 }
714 }
715
716 return 0;
717 }
718 case ID_SHOW_DRAWN:
719 {
720 IGEditorDelegate* pPlug = dynamic_cast<IGEditorDelegate*>(pAppHost->GetPlug());
721
722 if (pPlug)
723 {
724 IGraphics* pGraphics = pPlug->GetUI();
725
726 if (pGraphics)
727 {
728 bool enabled = pGraphics->ShowAreaDrawnEnabled();
729 pGraphics->ShowAreaDrawn(!enabled);
730 CheckMenuItem(GET_MENU(), ID_SHOW_DRAWN, (MF_BYCOMMAND | enabled) ? MF_UNCHECKED : MF_CHECKED);
731 }
732 }
733
734 return 0;
735 }
736 case ID_SHOW_BOUNDS:
737 {
738 IGEditorDelegate* pPlug = dynamic_cast<IGEditorDelegate*>(pAppHost->GetPlug());
739
740 if (pPlug)
741 {
742 IGraphics* pGraphics = pPlug->GetUI();
743
744 if (pGraphics)
745 {
746 bool enabled = pGraphics->ShowControlBoundsEnabled();
747 pGraphics->ShowControlBounds(!enabled);
748 CheckMenuItem(GET_MENU(), ID_SHOW_BOUNDS, (MF_BYCOMMAND | enabled) ? MF_UNCHECKED : MF_CHECKED);
749 }
750 }
751
752 return 0;
753 }
754 case ID_SHOW_FPS:
755 {
756 IGEditorDelegate* pPlug = dynamic_cast<IGEditorDelegate*>(pAppHost->GetPlug());
757
758 if (pPlug)
759 {
760 IGraphics* pGraphics = pPlug->GetUI();
761
762 if (pGraphics)
763 {
764 bool enabled = pGraphics->ShowingFPSDisplay();
765 pGraphics->ShowFPSDisplay(!enabled);
766 CheckMenuItem(GET_MENU(), ID_SHOW_FPS, (MF_BYCOMMAND | enabled) ? MF_UNCHECKED : MF_CHECKED);
767 }
768 }
769
770 return 0;
771 }
772#endif
773 }
774 return 0;
775 case WM_GETMINMAXINFO:
776 {
777 if (!pAppHost)
778 return 1;
779
780 IPlugAPP* pPlug = pAppHost->GetPlug();
781
782 MINMAXINFO* mmi = (MINMAXINFO*) lParam;
783 mmi->ptMinTrackSize.x = pPlug->GetMinWidth();
784 mmi->ptMinTrackSize.y = pPlug->GetMinHeight();
785 mmi->ptMaxTrackSize.x = pPlug->GetMaxWidth();
786 mmi->ptMaxTrackSize.y = pPlug->GetMaxHeight();
787
788#ifdef OS_WIN
789 float scale = GetScaleForHWND(hwndDlg);
790 mmi->ptMinTrackSize.x = static_cast<LONG>(static_cast<float>(mmi->ptMinTrackSize.x) * scale);
791 mmi->ptMinTrackSize.y = static_cast<LONG>(static_cast<float>(mmi->ptMinTrackSize.y) * scale);
792 mmi->ptMaxTrackSize.x = static_cast<LONG>(static_cast<float>(mmi->ptMaxTrackSize.x) * scale);
793 mmi->ptMaxTrackSize.y = static_cast<LONG>(static_cast<float>(mmi->ptMaxTrackSize.y) * scale);
794#endif
795
796 return 0;
797 }
798#ifdef OS_WIN
799 case WM_DPICHANGED:
800 {
801 WORD dpi = HIWORD(wParam);
802 RECT* rect = (RECT*)lParam;
803 float scale = GetScaleForHWND(hwndDlg);
804
805 POINT ptDiff;
806 RECT rcClient;
807 RECT rcWindow;
808
809 GetClientRect(hwndDlg, &rcClient);
810 GetWindowRect(hwndDlg, &rcWindow);
811
812 ptDiff.x = (rcWindow.right - rcWindow.left) - rcClient.right;
813 ptDiff.y = (rcWindow.bottom - rcWindow.top) - rcClient.bottom;
814
815#ifndef NO_IGRAPHICS
816 IGEditorDelegate* pPlug = dynamic_cast<IGEditorDelegate*>(pAppHost->GetPlug());
817
818 if (pPlug)
819 {
820 IGraphics* pGraphics = pPlug->GetUI();
821
822 if (pGraphics)
823 {
824 pGraphics->SetScreenScale(scale);
825 }
826 }
827#else
828 IEditorDelegate* pPlug = dynamic_cast<IEditorDelegate*>(pAppHost->GetPlug());
829#endif
830
831 int w = pPlug->GetEditorWidth();
832 int h = pPlug->GetEditorHeight();
833
834 SetWindowPos(hwndDlg, 0, rect->left, rect->top, w + ptDiff.x, h + ptDiff.y, 0);
835
836 return 0;
837 }
838#endif
839 case WM_SIZE:
840 {
841 IPlugAPP* pPlug = pAppHost->GetPlug();
842
843 switch (LOWORD(wParam))
844 {
845 case SIZE_RESTORED:
846 case SIZE_MAXIMIZED:
847 {
848 if (pPlug->GetHostResizeEnabled())
849 {
850 RECT r;
851 GetClientRect(hwndDlg, &r);
852 pPlug->OnParentWindowResize(static_cast<int>(r.right), static_cast<int>(r.bottom));
853 }
854 return 1;
855 }
856 default:
857 return 0;
858 }
859 }
860 }
861 return 0;
862}
This pure virtual interface delegates communication in both directions between a UI editor and someth...
int GetEditorHeight() const
int GetEditorWidth() const
An editor delegate base class that uses IGraphics for the UI.
IGraphics * GetUI()
Get a pointer to the IGraphics context.
The lowest level base class of an IGraphics context.
Definition: IGraphics.h:86
void ShowControlBounds(bool enable)
Definition: IGraphics.h:1190
void EnableLiveEdit(bool enable)
Live edit mode allows you to relocate controls at runtime in debug builds.
Definition: IGraphics.cpp:1549
bool ShowAreaDrawnEnabled() const
Definition: IGraphics.h:1196
bool LiveEditEnabled() const
Definition: IGraphics.h:1206
bool ShowControlBoundsEnabled() const
Definition: IGraphics.h:1199
void ShowFPSDisplay(bool enable)
Shows a control to display the frame rate of drawing.
Definition: IGraphics.cpp:437
bool ShowingFPSDisplay()
Definition: IGraphics.h:1363
void ShowAreaDrawn(bool enable)
Definition: IGraphics.h:1193
void SetScreenScale(float scale)
Called by the platform IGraphics class when moving to a new screen to set DPI.
Definition: IGraphics.cpp:75
A class that hosts an IPlug as a standalone app and provides Audio/Midi I/O.
Definition: IPlugAPP_host.h:83
const char * GetScreenshotPath() const
Get screenshot path (empty if not in screenshot mode)
std::string GetAudioDeviceName(uint32_t deviceID) const
Returns the name of the audio device with a given RTAudio device ID.
bool IsScreenshotMode() const
Check if in screenshot mode.
Standalone application base class for an IPlug plug-in.
Definition: IPlugAPP.h:37
bool GetHostResizeEnabled() const