Umsci
Upmix Spatial Control Interface — OCA/OCP.1 spatial audio utility
Loading...
Searching...
No Matches
Main.cpp
Go to the documentation of this file.
1/* Copyright (c) 2026, Christian Ahrens
2 *
3 * This file is part of Umsci <https://github.com/ChristianAhrens/Umsci>
4 *
5 * This tool is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU Lesser General Public License version 3.0 as published
7 * by the Free Software Foundation.
8 *
9 * This tool is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12 * details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this tool; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19#include <JuceHeader.h>
20
21#include "MainComponent.h"
22#include "DeviceController.h"
23
24#include <CustomLookAndFeel.h>
25
26 //==============================================================================
27class MainApplication : public juce::JUCEApplication
28{
29public:
30 //==============================================================================
32
33 const String getApplicationName() override { return ProjectInfo::projectName; }
34 const String getApplicationVersion() override { return ProjectInfo::versionString; }
35 bool moreThanOneInstanceAllowed() override { return true; }
36
37 //==============================================================================
38 void initialise(const String& commandLine) override
39 {
40 m_mainWindow.reset(std::make_unique<MainWindow>(getApplicationName(), commandLine).release());
41
42#if JUCE_MAC
43 // Ignore SIGPIPE globally, to prevent occasional unexpected app
44 // termination when Umsci instances disconnect while sending by
45 // writing to socket is ongoing
46 signal(SIGPIPE, SIG_IGN);
47#endif
48
49 // a single instance of tooltip window is required and used by JUCE everywhere a tooltip is required.
50 m_toolTipWindowInstance = std::make_unique<TooltipWindow>();
51 }
52
53 void shutdown() override
54 {
55 DeviceController::deleteInstance();
56 m_mainWindow.reset();
57 }
58
59 //==============================================================================
60 void systemRequestedQuit() override
61 {
62 // This is called when the app is being asked to quit: you can ignore this
63 // request and let the app carry on running, or call quit() to allow the app to close.
64 quit();
65 }
66
67 void anotherInstanceStarted(const juce::String& commandLine) override
68 {
69 ignoreUnused(commandLine);
70 }
71
72 //==============================================================================
73 /*
74 This class implements the desktop window that contains an instance of
75 our MainComponent class.
76 */
77 class MainWindow : public juce::DocumentWindow, juce::DarkModeSettingListener
78 {
79 public:
80 MainWindow(const juce::String& name, const juce::String& commandLine) : juce::DocumentWindow(name,
81 juce::Desktop::getInstance().getDefaultLookAndFeel()
82 .findColour(juce::ResizableWindow::backgroundColourId),
83 juce::DocumentWindow::allButtons)
84 {
85 ignoreUnused(commandLine);
86
87 setUsingNativeTitleBar(true);
88 auto mainComponent = std::make_unique<MainComponent>();
89 mainComponent->onPaletteStyleChange = [=](int paletteStyle, bool followLocalStyle) {
90 m_followLocalStyle = followLocalStyle;
91 applyPaletteStyle(static_cast<JUCEAppBasics::CustomLookAndFeel::PaletteStyle>(paletteStyle));
92 };
93 mainComponent->onSetFullscreenWindow = [=](bool fullscreenWindow) { setFullscreenWindow(fullscreenWindow); };
94 setContentOwned(mainComponent.release(), true);
95
96#if JUCE_IOS || JUCE_ANDROID
97 setFullScreen(true);
98 juce::Desktop::getInstance().setScreenSaverEnabled(false);
99#elif JUCE_LINUX
100 juce::Desktop::getInstance().setKioskModeComponent(getTopLevelComponent(), false);
101#else
102 setResizable(true, true);
103 centreWithSize(getWidth(), getHeight());
104#endif
105
106 setVisible(true);
107
108 juce::Desktop::getInstance().addDarkModeSettingListener(this);
109 darkModeSettingChanged(); // initially trigger correct colourscheme
110
111 // use the settings menu item call infrastructure to activate dark mode per default
112 if (auto mc = dynamic_cast<MainComponent*>(getContentComponent()))
114
115 JUCEAppBasics::AppConfigurationBase::getInstance()->triggerWatcherUpdate();
116 }
117
118 void closeButtonPressed() override
119 {
120 juce::JUCEApplication::getInstance()->systemRequestedQuit();
121 }
122
124 {
125 if (!m_followLocalStyle)
126 return;
127
128 if (juce::Desktop::getInstance().isDarkModeActive())
129 {
130 // go dark
131 applyPaletteStyle(JUCEAppBasics::CustomLookAndFeel::PS_Dark);
132 }
133 else
134 {
135 // go light
136 applyPaletteStyle(JUCEAppBasics::CustomLookAndFeel::PS_Light);
137 }
138
139 lookAndFeelChanged();
140 }
141
142 void applyPaletteStyle(JUCEAppBasics::CustomLookAndFeel::PaletteStyle paletteStyle)
143 {
144 m_lookAndFeel = std::make_unique<JUCEAppBasics::CustomLookAndFeel>(paletteStyle);
145 juce::Desktop::getInstance().setDefaultLookAndFeel(m_lookAndFeel.get());
146 }
147
148 void setFullscreenWindow(bool fullscreenWindow)
149 {
150#if JUCE_WINDOWS
151 if (fullscreenWindow)
152 juce::Desktop::getInstance().setKioskModeComponent(getTopLevelComponent(), false);
153 else
154 juce::Desktop::getInstance().setKioskModeComponent(nullptr, false);
155#elif JUCE_MAC
156 if (auto* topLevel = getTopLevelComponent())
157 {
158 if (auto* peer = topLevel->getPeer())
159 {
160 peer->setFullScreen(fullscreenWindow);
161 return;
162 }
163 }
164 jassertfalse;
165#endif
166 }
167
168 private:
169 std::unique_ptr<juce::LookAndFeel> m_lookAndFeel;
170 bool m_followLocalStyle = true;
171
172 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainWindow)
173 };
174
175private:
176 std::unique_ptr<MainWindow> m_mainWindow;
177 std::unique_ptr<juce::TooltipWindow> m_toolTipWindowInstance;
178};
179
180//==============================================================================
181// This macro generates the main() routine that launches the app.
182START_JUCE_APPLICATION(MainApplication)
void closeButtonPressed() override
Definition Main.cpp:118
void setFullscreenWindow(bool fullscreenWindow)
Definition Main.cpp:148
void applyPaletteStyle(JUCEAppBasics::CustomLookAndFeel::PaletteStyle paletteStyle)
Definition Main.cpp:142
MainWindow(const juce::String &name, const juce::String &commandLine)
Definition Main.cpp:80
void darkModeSettingChanged() override
Definition Main.cpp:123
const String getApplicationVersion() override
Definition Main.cpp:34
void initialise(const String &commandLine) override
Definition Main.cpp:38
void shutdown() override
Definition Main.cpp:53
bool moreThanOneInstanceAllowed() override
Definition Main.cpp:35
void systemRequestedQuit() override
Definition Main.cpp:60
void anotherInstanceStarted(const juce::String &commandLine) override
Definition Main.cpp:67
const String getApplicationName() override
Definition Main.cpp:33
Root JUCE component — the top-level UI that wires together the device connection, the visualisation,...
@ LookAndFeel_Dark
Force dark colour scheme.