• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kunitoki / popsicle / 8606821583

08 Apr 2024 09:24PM UTC coverage: 20.182%. Remained the same
8606821583

push

github

kunitoki
Added plugin demo

23834 of 118095 relevant lines covered (20.18%)

3622.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

44.33
/modules/juce_python/bindings/ScriptJuceGuiEntryPointsBindings.cpp
1
/**
2
 * juce_python - Python bindings for the JUCE framework.
3
 *
4
 * This file is part of the popsicle project.
5
 *
6
 * Copyright (c) 2024 - kunitoki <kunitoki@gmail.com>
7
 *
8
 * popsicle is an open source library subject to commercial or open-source licensing.
9
 *
10
 * By using popsicle, you agree to the terms of the popsicle License Agreement, which can
11
 * be found at https://raw.githubusercontent.com/kunitoki/popsicle/master/LICENSE
12
 *
13
 * Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses).
14
 *
15
 * POPSICLE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED
16
 * OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED.
17
 */
18

19
#include "ScriptJuceGuiEntryPointsBindings.h"
20

21
#define JUCE_PYTHON_INCLUDE_PYBIND11_OPERATORS
22
#define JUCE_PYTHON_INCLUDE_PYBIND11_IOSTREAM
23
#include "../utilities/PyBind11Includes.h"
24

25
#if JUCE_WINDOWS
26
#include "../utilities/WindowsIncludes.h"
27
#endif
28

29
#include "ScriptJuceOptionsBindings.h"
30

31
#include <functional>
32
#include <string_view>
33
#include <typeinfo>
34
#include <tuple>
35

36
// =================================================================================================
37

38
namespace juce {
39

40
#if ! JUCE_WINDOWS
41
 extern const char* const* juce_argv;
42
 extern int juce_argc;
43
#endif
44

45
} // namespace juce
46

47
namespace popsicle::Bindings {
48

49
using namespace juce;
50

51
namespace py = pybind11;
52
using namespace py::literals;
53

54
namespace {
55

56
// ============================================================================================
57

58
#if ! JUCE_PYTHON_EMBEDDED_INTERPRETER
59
void runApplication (JUCEApplicationBase* application, int milliseconds)
×
60
{
61
    {
62
        py::gil_scoped_release release;
×
63

64
        if (! application->initialiseApp())
×
65
            return;
×
66
    }
67

68
    while (! MessageManager::getInstance()->hasStopMessageBeenSent())
×
69
    {
70
        try
71
        {
72
            py::gil_scoped_release release;
×
73

74
            MessageManager::getInstance()->runDispatchLoopUntil (milliseconds);
×
75
        }
76
        catch (const py::error_already_set& e)
×
77
        {
78
            if (globalOptions().catchExceptionsAndContinue)
×
79
            {
80
                Helpers::printPythonException (e);
×
81
            }
82
            else
83
            {
84
                throw e;
×
85
            }
86
        }
87

88
        if (globalOptions().caughtKeyboardInterrupt)
×
89
            break;
×
90

91
        if (PyErr_CheckSignals() != 0)
×
92
            throw py::error_already_set();
×
93
    }
94
}
95
#endif
96

97
} // namespace
98

99
void registerJuceGuiEntryPointsBindings (py::module_& m)
1✔
100
{
101
#if ! JUCE_PYTHON_EMBEDDED_INTERPRETER
102

103
    // =================================================================================================
104

105
    m.def ("START_JUCE_APPLICATION", [](py::handle applicationType, bool catchExceptionsAndContinue)
×
106
    {
107
#if JUCE_MAC
108
        juce::Process::setDockIconVisible (true);
109
#endif
110

111
        globalOptions().catchExceptionsAndContinue = catchExceptionsAndContinue;
×
112
        globalOptions().caughtKeyboardInterrupt = false;
×
113

114
        py::scoped_ostream_redirect output;
×
115

116
        if (! applicationType)
×
117
            throw py::value_error("Argument must be a JUCEApplication subclass");
×
118

119
        JUCEApplicationBase* application = nullptr;
×
120

121
        auto sys = py::module_::import ("sys");
×
122
        auto systemExit = [sys, &application]
×
123
        {
124
            const int returnValue = application != nullptr ? application->shutdownApp() : 255;
×
125

126
            sys.attr ("exit") (returnValue);
×
127
        };
×
128

129
#if ! JUCE_WINDOWS
130
        StringArray arguments;
×
131
        for (auto arg : sys.attr ("argv"))
×
132
            arguments.add (arg.cast<String>());
×
133

134
        Array<const char*> argv;
×
135
        for (const auto& arg : arguments)
×
136
            argv.add (arg.toRawUTF8());
×
137

138
        juce_argv = argv.getRawDataPointer();
×
139
        juce_argc = argv.size();
×
140
#endif
141

142
        auto pyApplication = applicationType(); // TODO - error checking (python)
×
143

144
        application = pyApplication.cast<JUCEApplication*>();
×
145
        if (application == nullptr)
×
146
        {
147
            systemExit();
×
148
            return;
×
149
        }
150

151
        try
152
        {
153
            runApplication (application, globalOptions().messageManagerGranularityMilliseconds);
×
154
        }
155
        catch (const py::error_already_set& e)
×
156
        {
157
            Helpers::printPythonException (e);
×
158
        }
159

160
        systemExit();
×
161
    }, "applicationType"_a, "catchExceptionsAndContinue"_a = false);
1✔
162

163
#endif
164

165
    // =================================================================================================
166

167
    struct PyTestableApplication
168
    {
169
        struct Scope
170
        {
171
            Scope (py::handle applicationType)
42✔
172
            {
42✔
173
                if (! applicationType)
42✔
174
                    throw py::value_error("Argument must be a JUCEApplication subclass");
×
175

176
                JUCEApplicationBase* application = nullptr;
42✔
177

178
#if ! JUCE_WINDOWS
179
                for (auto arg : py::module_::import ("sys").attr ("argv"))
168✔
180
                    arguments.add (arg.cast<String>());
126✔
181

182
                for (const auto& arg : arguments)
168✔
183
                    argv.add (arg.toRawUTF8());
126✔
184

185
                juce_argv = argv.getRawDataPointer();
42✔
186
                juce_argc = argv.size();
42✔
187
#endif
188

189
                auto pyApplication = applicationType();
42✔
190

191
                application = pyApplication.cast<JUCEApplication*>();
42✔
192
                if (application == nullptr)
42✔
193
                    return;
×
194

195
                if (! application->initialiseApp())
42✔
196
                    return;
×
197
            }
198

199
            ~Scope()
42✔
200
            {
42✔
201
            }
42✔
202

203
        private:
204
#if ! JUCE_WINDOWS
205
            StringArray arguments;
206
            Array<const char*> argv;
207
#endif
208
        };
209

210
        PyTestableApplication (py::handle applicationType)
42✔
211
            : applicationType (applicationType)
42✔
212
        {
213
        }
42✔
214

215
        void processEvents(int milliseconds = 20)
141✔
216
        {
217
            try
218
            {
219
                JUCE_TRY
220
                {
221
                    py::gil_scoped_release release;
141✔
222

223
                    if (MessageManager::getInstance()->hasStopMessageBeenSent())
141✔
224
                        return;
×
225

226
                    MessageManager::getInstance()->runDispatchLoopUntil (milliseconds);
141✔
227
                }
228
                JUCE_CATCH_EXCEPTION
×
229

230
                bool isErrorSignalInFlight = PyErr_CheckSignals() != 0;
141✔
231
                if (isErrorSignalInFlight)
141✔
232
                    throw py::error_already_set();
×
233
            }
234
            catch (const py::error_already_set& e)
×
235
            {
236
                py::print (e.what());
×
237
            }
238
            catch (...)
×
239
            {
240
                py::print ("unhandled runtime error");
×
241
            }
242
        }
243

244
        py::handle applicationType;
245
        std::unique_ptr<Scope> applicationScope;
246
    };
247

248
    py::class_<PyTestableApplication> classTestableApplication (m, "TestApplication");
1✔
249

250
    classTestableApplication
251
        .def (py::init<py::handle>())
1✔
252
        .def ("processEvents", &PyTestableApplication::processEvents, "milliseconds"_a = 20)
2✔
253
        .def ("__enter__", [](PyTestableApplication& self)
42✔
254
        {
255
            self.applicationScope = std::make_unique<PyTestableApplication::Scope> (self.applicationType);
42✔
256
            return std::addressof (self);
42✔
257
        }, py::return_value_policy::reference)
1✔
258
        .def ("__exit__", [](PyTestableApplication& self, const std::optional<py::type>&, const std::optional<py::object>&, const std::optional<py::object>&)
42✔
259
        {
260
            self.applicationScope.reset();
42✔
261
        })
1✔
262
        .def ("__next__", [](PyTestableApplication& self)
126✔
263
        {
264
            self.processEvents();
126✔
265
            return std::addressof (self);
126✔
266
        }, py::return_value_policy::reference)
1✔
267
    ;
268
}
1✔
269

270
} // namespace popsicle::Bindings
271

272
// =================================================================================================
273

274
#if ! JUCE_PYTHON_EMBEDDED_INTERPRETER && JUCE_WINDOWS
275
BOOL APIENTRY DllMain(HANDLE instance, DWORD reason, LPVOID reserved)
276
{
277
    juce::ignoreUnused (reserved);
278

279
    if (reason == DLL_PROCESS_ATTACH)
280
        juce::Process::setCurrentModuleInstanceHandle (instance);
281

282
    return true;
283
}
284
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc