1 /*
2 Commandline interpreter interface.
3 Copyright (c) 2003-2006 stefan kersten.
4 Copyright (c) 2013 tim blechmann.
5
6 ====================================================================
7
8 SuperCollider real time audio synthesis system
9 Copyright (c) 2002 James McCartney. All rights reserved.
10 http://www.audiosynth.com
11
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 */
26
27 #include "SC_TerminalClient.h"
28 #ifdef SC_QT
29 # include "../../QtCollider/LanguageClient.h"
30 #endif
31
32 #include <boost/bind.hpp>
33
34 #ifdef _WIN32
35 # define __GNU_LIBRARY__
36 # include "getopt.h"
37 # include "SC_Win32Utils.h"
38 # include <io.h>
39 # include <windows.h>
40 # include <ioapiset.h>
41 # include <iostream> // for cerr
42 #endif
43
44 #ifdef __APPLE__
45 # include "../../common/SC_Apple.hpp"
46 #endif
47
48 #ifdef HAVE_READLINE
49 # include <readline/readline.h>
50 # include <readline/history.h>
51 # include <signal.h>
52 #endif
53
54 #include "GC.h"
55 #include "PyrKernel.h"
56 #include "PyrPrimitive.h"
57 #include "PyrLexer.h"
58 #include "PyrSlot.h"
59 #include "VMGlobals.h"
60 #include "SC_Filesystem.hpp"
61 #include "SC_LanguageConfig.hpp"
62 #include "SC_Version.hpp"
63
64 #include <boost/filesystem/operations.hpp>
65
66 static FILE* gPostDest = stdout;
67
68 #ifdef _WIN32
69 static UINT gOldCodePage; // for remembering the old codepage when we switch to UTF-8
70 #endif
71
SC_TerminalClient(const char * name)72 SC_TerminalClient::SC_TerminalClient(const char* name):
73 SC_LanguageClient(name),
74 mReturnCode(0),
75 mUseReadline(false),
76 mWork(mIoService),
77 mTimer(mIoService),
78 #ifndef _WIN32
79 mStdIn(mInputService, STDIN_FILENO)
80 #else
81 mStdIn(mInputService, GetStdHandle(STD_INPUT_HANDLE))
82 #endif
83 {
84 }
85
~SC_TerminalClient()86 SC_TerminalClient::~SC_TerminalClient() {}
87
postText(const char * str,size_t len)88 void SC_TerminalClient::postText(const char* str, size_t len) { fwrite(str, sizeof(char), len, gPostDest); }
89
postFlush(const char * str,size_t len)90 void SC_TerminalClient::postFlush(const char* str, size_t len) {
91 fwrite(str, sizeof(char), len, gPostDest);
92 fflush(gPostDest);
93 }
94
postError(const char * str,size_t len)95 void SC_TerminalClient::postError(const char* str, size_t len) {
96 fprintf(gPostDest, "ERROR: ");
97 fwrite(str, sizeof(char), len, gPostDest);
98 }
99
flush()100 void SC_TerminalClient::flush() { fflush(gPostDest); }
101
printUsage()102 void SC_TerminalClient::printUsage() {
103 Options opt;
104
105 const size_t bufSize = 128;
106 char memGrowBuf[bufSize];
107 char memSpaceBuf[bufSize];
108
109 snprintMemArg(memGrowBuf, bufSize, opt.mMemGrow);
110 snprintMemArg(memSpaceBuf, bufSize, opt.mMemSpace);
111
112 fprintf(stdout, "Usage:\n %s [options] [file..] [-]\n\n", getName());
113 fprintf(
114 stdout,
115 "Options:\n"
116 " -v Print supercollider version and exit\n"
117 " -d <path> Set runtime directory\n"
118 " -D Enter daemon mode (no input)\n"
119 " -g <memory-growth>[km] Set heap growth (default %s)\n"
120 " -h Display this message and exit\n"
121 " -l <path> Set library configuration file\n"
122 " -m <memory-space>[km] Set initial heap size (default %s)\n"
123 " -r Call Main.run on startup\n"
124 " -s Call Main.stop on shutdown\n"
125 " -u <network-port-number> Set UDP listening port (default %d)\n"
126 " -i <ide-name> Specify IDE name (for enabling IDE-specific class code, default \"%s\")\n"
127 " -a Standalone mode (exclude SCClassLibrary and user and system Extensions "
128 "folders from search path)\n",
129 memGrowBuf, memSpaceBuf, opt.mPort, SC_Filesystem::instance().getIdeName().c_str());
130 }
131
parseOptions(int & argc,char ** & argv,Options & opt)132 bool SC_TerminalClient::parseOptions(int& argc, char**& argv, Options& opt) {
133 const char* optstr = ":d:Dg:hl:m:rsu:i:av";
134 int c;
135
136 // inhibit error reporting
137 opterr = 0;
138
139 while ((c = getopt(argc, argv, optstr)) != -1) {
140 switch (c) {
141 case 'd':
142 opt.mRuntimeDir = optarg;
143 break;
144 case 'D':
145 opt.mDaemon = true;
146 break;
147 case 'g':
148 if (!parseMemArg(optarg, &opt.mMemGrow)) {
149 optopt = c;
150 goto optArgInvalid;
151 }
152 break;
153 case 'h':
154 goto help;
155 case 'l':
156 opt.mLibraryConfigFile = optarg;
157 break;
158 case 'm':
159 if (!parseMemArg(optarg, &opt.mMemSpace)) {
160 optopt = c;
161 goto optArgInvalid;
162 }
163 break;
164 case 'r':
165 opt.mCallRun = true;
166 break;
167 case 'v':
168 fprintf(stdout, "sclang %s (%s)\n", SC_VersionString().c_str(), SC_BuildString().c_str());
169 quit(0);
170 return false;
171 break;
172 case 's':
173 opt.mCallStop = true;
174 break;
175 case 'u':
176 if (!parsePortArg(optarg, &opt.mPort)) {
177 optopt = c;
178 goto optArgInvalid;
179 }
180 break;
181 case '?':
182 goto optInvalid;
183 break;
184 case ':':
185 goto optArgExpected;
186 break;
187 case 'i':
188 SC_Filesystem::instance().setIdeName(optarg);
189 break;
190 case 'a':
191 opt.mStandalone = true;
192 break;
193 default:
194 ::post("%s: unknown error (getopt)\n", getName());
195 quit(255);
196 return false;
197 }
198 }
199
200 argv += optind;
201 argc -= optind;
202
203 return true;
204
205 help:
206 printUsage();
207 quit(0);
208 return false;
209
210 optInvalid:
211 ::post("%s: invalid option -%c\n", getName(), optopt);
212 quit(1);
213 return false;
214
215 optArgExpected:
216 ::post("%s: missing argument for option -%c\n", getName(), optopt);
217 quit(1);
218 return false;
219
220 optArgInvalid:
221 ::post("%s: invalid argument for option -%c -- %s\n", getName(), optopt, optarg);
222 quit(1);
223 return false;
224 }
225
run(int argc,char ** argv)226 int SC_TerminalClient::run(int argc, char** argv) {
227 Options& opt = mOptions;
228
229 if (!parseOptions(argc, argv, opt)) {
230 return mReturnCode;
231 }
232
233 // finish argv processing
234 const char* codeFile = nullptr;
235
236 if (argc > 0) {
237 codeFile = argv[0];
238 opt.mDaemon = true;
239 argv++;
240 argc--;
241 }
242
243 opt.mArgc = argc;
244 opt.mArgv = argv;
245
246 // read library configuration file
247 if (opt.mLibraryConfigFile)
248 SC_LanguageConfig::setConfigPath(opt.mLibraryConfigFile);
249 SC_LanguageConfig::readLibraryConfig(opt.mStandalone);
250
251 // initialize runtime
252 initRuntime(opt);
253
254 // Create config directory so that it can be used by Quarks, etc. See #2919.
255 if (!opt.mStandalone && !opt.mLibraryConfigFile)
256 boost::filesystem::create_directories(
257 SC_Filesystem::instance().getDirectory(SC_Filesystem::DirName::UserConfig));
258
259 // startup library
260 compileLibrary(opt.mStandalone);
261
262 // enter main loop
263 if (codeFile)
264 executeFile(codeFile);
265 if (opt.mCallRun)
266 runMain();
267
268 if (opt.mDaemon) {
269 daemonLoop();
270 } else {
271 initInput();
272 startInput();
273 commandLoop();
274 endInput();
275 cleanupInput();
276 }
277
278 if (opt.mCallStop)
279 stopMain();
280
281 // shutdown library
282 shutdownLibrary();
283 flush();
284
285 shutdownRuntime();
286
287 return mReturnCode;
288 }
289
recompileLibrary()290 void SC_TerminalClient::recompileLibrary() { SC_LanguageClient::recompileLibrary(mOptions.mStandalone); }
291
quit(int code)292 void SC_TerminalClient::quit(int code) { mReturnCode = code; }
293
resolveMethodSymbol(bool silent)294 static PyrSymbol* resolveMethodSymbol(bool silent) {
295 if (silent)
296 return s_interpretCmdLine;
297 else
298 return s_interpretPrintCmdLine;
299 }
300
interpretCmdLine(const char * cmdLine,bool silent)301 void SC_TerminalClient::interpretCmdLine(const char* cmdLine, bool silent) {
302 setCmdLine(cmdLine);
303 runLibrary(resolveMethodSymbol(silent));
304 flush();
305 }
306
307
interpretCmdLine(const char * cmdLine,size_t size,bool silent)308 void SC_TerminalClient::interpretCmdLine(const char* cmdLine, size_t size, bool silent) {
309 setCmdLine(cmdLine, size);
310 runLibrary(resolveMethodSymbol(silent));
311 flush();
312 }
313
314 // Note: called only if the input thread does not perform an asynchronous read operation
interpretInput()315 void SC_TerminalClient::interpretInput() {
316 char* data = mInputBuf.getData();
317 int c = mInputBuf.getSize();
318 int i = 0;
319 while (i < c) {
320 switch (data[i]) {
321 case kInterpretCmdLine:
322 interpretCmdLine(data, i, true);
323 break;
324 case kInterpretPrintCmdLine:
325 interpretCmdLine(data, i, false);
326 break;
327
328 case kRecompileLibrary:
329 recompileLibrary();
330 break;
331
332 default:
333 ++i;
334 continue;
335 }
336
337 data += i + 1;
338 c -= i + 1;
339 i = 0;
340 }
341 mInputBuf.reset();
342
343 if (mUseReadline)
344 mReadlineSem.post();
345 else
346 startInputRead();
347 }
348
onLibraryStartup()349 void SC_TerminalClient::onLibraryStartup() {
350 SC_LanguageClient::onLibraryStartup();
351 int base, index = 0;
352 base = nextPrimitiveIndex();
353 definePrimitive(base, index++, "_Argv", &SC_TerminalClient::prArgv, 1, 0);
354 definePrimitive(base, index++, "_Exit", &SC_TerminalClient::prExit, 1, 0);
355 definePrimitive(base, index++, "_AppClock_SchedNotify", &SC_TerminalClient::prScheduleChanged, 1, 0);
356 definePrimitive(base, index++, "_Recompile", &SC_TerminalClient::prRecompile, 1, 0);
357 }
358
sendSignal(Signal sig)359 void SC_TerminalClient::sendSignal(Signal sig) {
360 switch (sig) {
361 case sig_input:
362 mIoService.post(boost::bind(&SC_TerminalClient::interpretInput, this));
363 break;
364
365 case sig_recompile:
366 mIoService.post(boost::bind(&SC_TerminalClient::recompileLibrary, this));
367 break;
368
369 case sig_sched:
370 mIoService.post(boost::bind(&SC_TerminalClient::tick, this, boost::system::error_code()));
371 break;
372
373 case sig_stop:
374 mIoService.post(boost::bind(&SC_TerminalClient::stopMain, this));
375 break;
376 }
377 }
378
onQuit(int exitCode)379 void SC_TerminalClient::onQuit(int exitCode) {
380 postfl("main: quit request %i\n", exitCode);
381 quit(exitCode);
382 stop();
383 }
384
385 extern void ElapsedTimeToChrono(double elapsed, std::chrono::system_clock::time_point& out_time_point);
386
387 /**
388 * \brief Repeatedly calls the main AppClock loop.
389 *
390 * This method does the following:
391 *
392 * - Wraps \c tickLocked (locking \c gLangMutex before and after), which in
393 * turn calls \c runLibrary.
394 * - Schedules to call itself again using a \c boost::asio::basic_waitable_timer
395 * (\c mTimer).
396 *
397 * Since it calls itself, a single call to \c tick will cause the method to
398 * keep calling itself autonomously.
399 *
400 * If \c tick is called again externally while a timer is running, any
401 * previously scheduled \c tick call is canceled. This forces a premature \c
402 * tick, which will schedule itself again, and so on.
403 *
404 * The timed calls to \c tick are used for events scheduled on the AppClock.
405 * The interruption feature is used when sclang receives unanticipated events
406 * such as inbound OSC messages.
407 */
tick(const boost::system::error_code & error)408 void SC_TerminalClient::tick(const boost::system::error_code& error) {
409 /*
410 Even if the timeout is canceled, this callback will always fire (see docs
411 for boost::asio::basic_waitable_timer).
412
413 Distinguishing between a canceled and successful callback is done by
414 inspecting the error. If it turns out this method was called because of a
415 cancelled timer, we need to bail out and let the tick call that
416 interrupted us take over.
417
418 If we aren't the result of a canceled timer call, we're either the result
419 of a scheduled timer expiry, or we *are* the interruption and we need to
420 cancel any scheduled tick calls. Calling mTimer.cancel() if the error is
421 a success error code handles both cases.
422
423 Previously, instead of this if/else block, this was just a call to
424 mTimer.cancel(). This was causing this method to rapidly call itself
425 excessively, hogging the CPU. See discussion at #2144.
426 */
427 if (error == boost::system::errc::success) {
428 mTimer.cancel();
429 } else {
430 return;
431 }
432
433 double secs;
434 lock();
435 bool haveNext = tickLocked(&secs);
436 unlock();
437
438 flush();
439
440 std::chrono::system_clock::time_point nextAbsTime;
441 ElapsedTimeToChrono(secs, nextAbsTime);
442
443 if (haveNext) {
444 mTimer.expires_at(nextAbsTime);
445 mTimer.async_wait(boost::bind(&SC_TerminalClient::tick, this, _1));
446 }
447 }
448
commandLoop()449 void SC_TerminalClient::commandLoop() { mIoService.run(); }
450
daemonLoop()451 void SC_TerminalClient::daemonLoop() { commandLoop(); }
452
453 #ifdef HAVE_READLINE
454
sc_rl_cleanlf(void)455 static void sc_rl_cleanlf(void) {
456 rl_reset_line_state();
457 rl_crlf();
458 rl_redisplay();
459 }
460
sc_rl_signalhandler(int sig)461 static void sc_rl_signalhandler(int sig) {
462 // ensure ctrl-C clears line rather than quitting (ctrl-D will quit nicely)
463 rl_replace_line("", 0);
464 sc_rl_cleanlf();
465 }
466
sc_rl_mainstop(int i1,int i2)467 static int sc_rl_mainstop(int i1, int i2) {
468 static_cast<SC_TerminalClient*>(SC_LanguageClient::instance())->sendSignal(SC_TerminalClient::sig_stop);
469 sc_rl_cleanlf(); // We also push a newline so that there's some UI feedback
470 return 0;
471 }
472
473 /*
474 // Completion from sclang dictionary TODO
475 char ** sc_rl_completion (const char *text, int start, int end);
476 char ** sc_rl_completion (const char *text, int start, int end){
477 char **matches = (char **)NULL;
478 printf("sc_rl_completion(%s, %i, %i)\n", text, start, end);
479 return matches;
480 }
481 */
482
readlineRecompile(int i1,int i2)483 int SC_TerminalClient::readlineRecompile(int i1, int i2) {
484 static_cast<SC_TerminalClient*>(SC_LanguageClient::instance())->sendSignal(sig_recompile);
485 sc_rl_cleanlf();
486 return 0;
487 }
488
readlineCmdLine(char * cmdLine)489 void SC_TerminalClient::readlineCmdLine(char* cmdLine) {
490 SC_TerminalClient* client = static_cast<SC_TerminalClient*>(instance());
491
492 if (cmdLine == nullptr) {
493 postfl("\nExiting sclang (ctrl-D)\n");
494 client->onQuit(0);
495 return;
496 }
497
498 if (*cmdLine != 0) {
499 // If line wasn't empty, store it so that uparrow retrieves it
500 add_history(cmdLine);
501 int len = strlen(cmdLine);
502
503 client->mInputBuf.append(cmdLine, len);
504 client->mInputBuf.append(kInterpretPrintCmdLine);
505 client->sendSignal(sig_input);
506 client->mReadlineSem.wait();
507 }
508 }
509
readlineInit()510 void SC_TerminalClient::readlineInit() {
511 // Setup readline
512 rl_readline_name = "sclang";
513 rl_basic_word_break_characters = " \t\n\"\\'`@><=;|&{}().";
514 // rl_attempted_completion_function = sc_rl_completion;
515 rl_bind_key(CTRL('t'), &sc_rl_mainstop);
516 rl_bind_key(CTRL('x'), &readlineRecompile);
517 rl_callback_handler_install("sc3> ", &readlineCmdLine);
518
519 // FIXME: Implement the code below on Windows
520 # ifndef _WIN32
521 // Set our handler for SIGINT that will clear the line instead of terminating.
522 // NOTE: We prevent readline from setting its own signal handlers,
523 // to not override ours.
524 rl_catch_signals = 0;
525 struct sigaction sact;
526 memset(&sact, 0, sizeof(struct sigaction));
527 sact.sa_handler = &sc_rl_signalhandler;
528 sigaction(SIGINT, &sact, nullptr);
529 # endif
530 }
531
532 #endif // HAVE_READLINE
533
startInputRead()534 void SC_TerminalClient::startInputRead() {
535 #ifndef _WIN32
536 if (mUseReadline)
537 mStdIn.async_read_some(boost::asio::null_buffers(), boost::bind(&SC_TerminalClient::onInputRead, this, _1, _2));
538 else
539 mStdIn.async_read_some(boost::asio::buffer(inputBuffer),
540 boost::bind(&SC_TerminalClient::onInputRead, this, _1, _2));
541 #else
542 mStdIn.async_wait([&](const boost::system::error_code& error) {
543 if (error)
544 onInputRead(error, 0);
545 else {
546 DWORD bytes_transferred;
547
548 ::ReadFile(GetStdHandle(STD_INPUT_HANDLE), inputBuffer.data(), inputBuffer.size(), &bytes_transferred,
549 nullptr);
550
551 onInputRead(error, bytes_transferred);
552 }
553 });
554 #endif
555 }
556
onInputRead(const boost::system::error_code & error,std::size_t bytes_transferred)557 void SC_TerminalClient::onInputRead(const boost::system::error_code& error, std::size_t bytes_transferred) {
558 if (error == boost::asio::error::operation_aborted) {
559 postfl("SCLang Input: Quit requested\n");
560 return;
561 }
562
563 if (error == boost::asio::error::eof) {
564 postfl("SCLang Input: EOF. Will quit.\n");
565 onQuit(0);
566 return;
567 }
568
569 if (error) {
570 postfl("SCLang Input: %s.\n", error.message().c_str());
571 onQuit(1);
572 return;
573 }
574
575 if (!error) {
576 #if HAVE_READLINE
577 if (mUseReadline) {
578 rl_callback_read_char();
579 startInputRead();
580 return;
581 }
582 #endif
583 pushCmdLine(inputBuffer.data(), bytes_transferred);
584 }
585 }
586
inputThreadFn()587 void SC_TerminalClient::inputThreadFn() {
588 #if HAVE_READLINE
589 if (mUseReadline)
590 readlineInit();
591 #endif
592
593 #ifdef _WIN32
594 // make sure there's nothing on stdin before we launch the service
595 // this fixes #4214
596 DWORD bytesRead = 0;
597 auto success = ReadFile(GetStdHandle(STD_INPUT_HANDLE), inputBuffer.data(), inputBuffer.size(), &bytesRead, NULL);
598
599 if (success) {
600 pushCmdLine(inputBuffer.data(), bytesRead);
601 }
602 #endif
603
604 startInputRead();
605
606 boost::asio::io_service::work work(mInputService);
607 mInputService.run();
608 }
609
610
pushCmdLine(const char * newData,size_t size)611 void SC_TerminalClient::pushCmdLine(const char* newData, size_t size) {
612 bool signal = false;
613 while (size--) {
614 char c = *newData++;
615 switch (c) {
616 case kRecompileLibrary:
617 case kInterpretCmdLine:
618 case kInterpretPrintCmdLine:
619 mInputBuf.append(mInputThrdBuf.getData(), mInputThrdBuf.getSize());
620 mInputBuf.append(c);
621 signal = true;
622 mInputThrdBuf.reset();
623 break;
624
625 default:
626 mInputThrdBuf.append(c);
627 }
628 }
629
630 if (signal)
631 sendSignal(sig_input);
632 else
633 startInputRead();
634 }
635
636
initInput()637 void SC_TerminalClient::initInput() {
638 #ifdef HAVE_READLINE
639 if (!SC_Filesystem::instance().usingIde()) {
640 // Other clients (emacs, vim, ...) won't want to interact through rl
641 mUseReadline = true;
642 return;
643 }
644 #endif
645 }
646
647
startInput()648 void SC_TerminalClient::startInput() {
649 SC_Thread thread(std::bind(&SC_TerminalClient::inputThreadFn, this));
650 mInputThread = std::move(thread);
651 }
652
endInput()653 void SC_TerminalClient::endInput() {
654 mInputService.stop();
655 mStdIn.cancel();
656 #ifdef _WIN32
657 // Note this breaks Windows XP compatibility, since this function is only defined in Vista and later
658 ::CancelIoEx(GetStdHandle(STD_INPUT_HANDLE), nullptr);
659 #endif
660 postfl("main: waiting for input thread to join...\n");
661 mInputThread.join();
662 postfl("main: quitting...\n");
663 }
664
cleanupInput()665 void SC_TerminalClient::cleanupInput() {
666 #ifdef HAVE_READLINE
667 if (mUseReadline)
668 rl_callback_handler_remove();
669 #endif
670 }
671
prArgv(struct VMGlobals * g,int)672 int SC_TerminalClient::prArgv(struct VMGlobals* g, int) {
673 int argc = ((SC_TerminalClient*)SC_TerminalClient::instance())->options().mArgc;
674 char** argv = ((SC_TerminalClient*)SC_TerminalClient::instance())->options().mArgv;
675
676 PyrSlot* argvSlot = g->sp;
677
678 PyrObject* argvObj = newPyrArray(g->gc, argc * sizeof(PyrObject), 0, true);
679 SetObject(argvSlot, argvObj); // this is okay here as we don't use the receiver
680
681 for (int i = 0; i < argc; i++) {
682 PyrString* str = newPyrString(g->gc, argv[i], 0, true);
683 SetObject(argvObj->slots + i, str);
684 argvObj->size++;
685 g->gc->GCWriteNew(argvObj, (PyrObject*)str); // we know str is white so we can use GCWriteNew
686 }
687
688 return errNone;
689 }
690
prExit(struct VMGlobals * g,int)691 int SC_TerminalClient::prExit(struct VMGlobals* g, int) {
692 int code;
693
694 int err = slotIntVal(g->sp, &code);
695 if (err)
696 return err;
697
698 ((SC_TerminalClient*)SC_LanguageClient::instance())->onQuit(code);
699
700 return errNone;
701 }
702
prScheduleChanged(struct VMGlobals * g,int numArgsPushed)703 int SC_TerminalClient::prScheduleChanged(struct VMGlobals* g, int numArgsPushed) {
704 static_cast<SC_TerminalClient*>(instance())->sendSignal(sig_sched);
705 return errNone;
706 }
707
prRecompile(struct VMGlobals *,int)708 int SC_TerminalClient::prRecompile(struct VMGlobals*, int) {
709 static_cast<SC_TerminalClient*>(instance())->sendSignal(sig_recompile);
710 return errNone;
711 }
712
createLanguageClient(const char * name)713 SCLANG_DLLEXPORT SC_LanguageClient* createLanguageClient(const char* name) {
714 if (SC_LanguageClient::instance())
715 return nullptr;
716
717 #ifdef __APPLE__
718 SC::Apple::disableAppNap();
719 #endif
720
721 #ifdef _WIN32
722 // set codepage to UTF-8
723 gOldCodePage = GetConsoleOutputCP();
724 if (!SetConsoleOutputCP(65001))
725 std::cerr << "WARNING: could not set codepage to UTF-8" << std::endl;
726 #endif
727
728 #ifdef SC_QT
729 return new QtCollider::LangClient(name);
730 #else
731 return new SC_TerminalClient(name);
732 #endif
733 }
734
destroyLanguageClient(class SC_LanguageClient * languageClient)735 SCLANG_DLLEXPORT void destroyLanguageClient(class SC_LanguageClient* languageClient) {
736 #ifdef _WIN32
737 // reset codepage from UTF-8
738 SetConsoleOutputCP(gOldCodePage);
739 #endif
740 delete languageClient;
741 }
742