Skip to content

Commit

Permalink
implement Cocoa event loop and run it in scsynth_main.cpp to show nat…
Browse files Browse the repository at this point in the history
…ive GUI windows

* added "-e" command line option to enable the event loop (disabled by default)
* added "eventLoop" member to sclangs's ServerOption class
* didn't have to touch WorldOptions!
  • Loading branch information
Christof Ressi authored and Spacechild1 committed Dec 6, 2019
1 parent 8fe8f31 commit d47688d
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 4 deletions.
2 changes: 2 additions & 0 deletions HelpSource/Classes/ServerOptions.schelp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ A Boolean indicating whether the server should try to lock its memory into physi
method:: maxLogins
An Integer indicating the maximum number of clients which can simultaneously receive notifications from the server. When using TCP this is also the maximum number of simultaneous connections. This is also used by the language to split ranges of link::Classes/Node##Nodes::, link::Classes/Buffer##Buffers::, or link::Classes/Bus##Busses::. In multi-client situations you will need to set this to at least the number of clients you wish to allow. This must be the same in the Server instances on every client. The default is 1.

method:: eventLoop
A Boolean indicating whether Scsynth should run a Cocoa event loop on its main thread to show native GUI windows on macOS (e.g. VST plugin editors). Default is code::false::.

subsection:: Other Instance Methods
method:: asOptionsString
Expand Down
5 changes: 5 additions & 0 deletions SCClassLibrary/Common/Control/Server.sc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ ServerOptions {
var <>recBufSize;

var <>bindAddress;
var <>eventLoop; // for macOSC

*initClass {
defaultValues = IdentityDictionary.newFrom(
Expand Down Expand Up @@ -98,6 +99,7 @@ ServerOptions {
recChannels: 2,
recBufSize: nil,
bindAddress: "127.0.0.1",
eventLoop: false, // run Cocoa event loop to show native GUI windows
)
)
}
Expand Down Expand Up @@ -222,6 +224,9 @@ ServerOptions {
if (maxLogins.notNil, {
o = o ++ " -l " ++ maxLogins;
});
if (eventLoop, {
o = o ++ " -e";
});
^o
}

Expand Down
13 changes: 13 additions & 0 deletions common/SC_Apple.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,18 @@ namespace SC { namespace Apple {

void disableAppNap();

namespace EventLoop {
// Setup the main application. This function must be called in the
// main thread and before any other calls to Cocoa methods.
void setup();
// Run the event loop. This function must be called in the main thread.
// It blocks until the event loop finishes.
void run();
// Ask the event loop to stop and terminate the program.
// This function can be called from any thread.
void quit();

} // EventLoop

} // namespace Apple
} // namespace SC
41 changes: 41 additions & 0 deletions common/SC_Apple.mm
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,46 @@ void disableAppNap() {
}
}

namespace EventLoop {

void setup() {
// create NSApplication and make it the foreground application
ProcessSerialNumber psn = {0, kCurrentProcess};
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
[NSApplication sharedApplication];
}

void run() {
#if 0
// this doesn't work...
[NSApp run];
#else
// Kudos to https://www.cocoawithlove.com/2009/01/demystifying-nsapplication-by.html
NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];

[NSApp finishLaunching];
while (true) {
[pool release];
pool = [[NSAutoreleasePool alloc] init];
NSEvent *event = [NSApp
nextEventMatchingMask:NSAnyEventMask
untilDate:[NSDate distantFuture]
inMode:NSDefaultRunLoopMode
dequeue:YES];
if (event) {
[NSApp sendEvent:event];
[NSApp updateWindows];
}
}
[pool release];
#endif
}

void quit() {
[NSApp terminate:nil];
}

} // EventLoop

} // namespace Apple
} // namespace SC
3 changes: 2 additions & 1 deletion server/scsynth/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ if (FFTW3F_FOUND)
endif()

if (APPLE)
target_link_libraries(libscsynth "-framework Accelerate -framework CoreServices -framework Foundation")
target_link_libraries(libscsynth "-framework Accelerate -framework CoreServices"
"-framework Foundation -framework Cocoa")
endif()

if(CMAKE_SYSTEM_NAME MATCHES "Linux")
Expand Down
41 changes: 38 additions & 3 deletions server/scsynth/scsynth_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
#else
# include <sys/wait.h>
#endif
#ifdef __APPLE__
#include "SC_Apple.hpp"
#include <thread>
typedef std::thread SC_Thread;
#endif

#ifdef _WIN32

Expand All @@ -42,7 +47,6 @@ inline int setlinebuf(FILE* stream) { return setvbuf(stream, (char*)0, _IONBF, 0

#endif


void Usage();
void Usage() {
WorldOptions defaultOptions;
Expand Down Expand Up @@ -80,6 +84,8 @@ void Usage() {
#ifdef __APPLE__
" -I <input-streams-enabled>\n"
" -O <output-streams-enabled>\n"
" -e <enable Cocoa event loop>\n"
" This allows UGens to show native GUI windows.\n"
#endif
#if (_POSIX_MEMLOCK - 0) >= 200112L
" -L enable memory locking\n"
Expand Down Expand Up @@ -136,6 +142,10 @@ int main(int argc, char* argv[]) {
return 1;
}
#endif
#ifdef __APPLE__
bool eventLoop = false;
#endif


int udpPortNum = -1;
int tcpPortNum = -1;
Expand All @@ -144,7 +154,7 @@ int main(int argc, char* argv[]) {
WorldOptions options;

for (int i = 1; i < argc;) {
if (argv[i][0] != '-' || argv[i][1] == 0 || strchr("utBaioczblndpmwZrCNSDIOMHvVRUhPL", argv[i][1]) == 0) {
if (argv[i][0] != '-' || argv[i][1] == 0 || strchr("utBaioczblndpmwZrCNSDIOeMHvVRUhPL", argv[i][1]) == 0) {
scprintf("ERROR: Invalid option %s\n", argv[i]);
Usage();
}
Expand Down Expand Up @@ -250,6 +260,10 @@ int main(int argc, char* argv[]) {
checkNumArgs(2);
options.mOutputStreamsEnabled = argv[j + 1];
break;
case 'e':
checkNumArgs(1);
eventLoop = true;
break;
case 'M':
#endif
case 'H':
Expand Down Expand Up @@ -315,6 +329,11 @@ int main(int argc, char* argv[]) {
} else
options.mSharedMemoryID = 0;

#ifdef __APPLE__
if (eventLoop) {
SC::Apple::EventLoop::setup();
}
#endif

struct World* world = World_New(&options);
if (!world)
Expand Down Expand Up @@ -357,8 +376,24 @@ int main(int argc, char* argv[]) {
}
fflush(stdout);

#ifdef __APPLE__
if (eventLoop) {
// this thread simply waits for the world to quit,
// after which it will ask the main loop to terminate.
auto thread = SC_Thread([&]() {
World_WaitForQuit(world, true);
scprintf("Quit event loop\n");
SC::Apple::EventLoop::quit();
});
thread.detach();
scprintf("Start event loop\n");
SC::Apple::EventLoop::run();
} else {
World_WaitForQuit(world, true);
}
#else
World_WaitForQuit(world, true);

#endif

#ifdef _WIN32
// clean up winsock
Expand Down

0 comments on commit d47688d

Please sign in to comment.