main.cpp (24037B)
1 /* 2 ZynAddSubFX - a software synthesizer 3 4 main.cpp - Main file of the synthesizer 5 Copyright (C) 2002-2005 Nasca Octavian Paul 6 Copyright (C) 2012-2019 Mark McCurry 7 8 This program is free software; you can redistribute it and/or 9 modify it under the terms of the GNU General Public License 10 as published by the Free Software Foundation; either version 2 11 of the License, or (at your option) any later version. 12 */ 13 14 15 #include <iostream> 16 #include <fstream> 17 #include <map> 18 #include <cmath> 19 #include <cctype> 20 #include <ctime> 21 #include <algorithm> 22 #include <signal.h> 23 24 #ifndef WIN32 25 #include <err.h> 26 #endif 27 #include <unistd.h> 28 29 #include <getopt.h> 30 31 #include <sys/types.h> 32 #include <signal.h> 33 34 #include <rtosc/rtosc.h> 35 #include <rtosc/ports.h> 36 #include "Params/PADnoteParameters.h" 37 38 #include "DSP/FFTwrapper.h" 39 #include "Misc/MemLocker.h" 40 #include "Misc/PresetExtractor.h" 41 #include "Misc/Master.h" 42 #include "Misc/Part.h" 43 #include "Misc/Util.h" 44 #include "zyn-config.h" 45 #include "zyn-version.h" 46 47 //Nio System 48 #include "Nio/Nio.h" 49 #include "Nio/InMgr.h" 50 51 //GUI System 52 #include "UI/Connection.h" 53 GUI::ui_handle_t gui; 54 55 #ifdef ZEST_GUI 56 #ifndef WIN32 57 #include <sys/wait.h> 58 #endif 59 #endif 60 61 //Glue Layer 62 #include "Misc/MiddleWare.h" 63 64 using namespace std; 65 using namespace zyn; 66 67 MiddleWare *middleware; 68 69 Master *master; 70 int swaplr = 0; //1 for left-right swapping 71 bool compr = false; // enables output audio compressor 72 73 // forward declarations of namespace zyn 74 namespace zyn 75 { 76 extern int Pexitprogram; //if the UI set this to 1, the program will exit 77 void dump_json(std::ostream &o, 78 const rtosc::Ports &p); 79 } 80 81 #if LASH 82 #include "Misc/LASHClient.h" 83 LASHClient *lash = NULL; 84 #endif 85 86 #if USE_NSM 87 #include "UI/NSM.H" 88 89 NSM_Client *nsm = 0; 90 #endif 91 92 char *instance_name = 0; 93 94 void exitprogram(const Config &config); 95 96 97 //cleanup on signaled exit 98 void sigterm_exit(int /*sig*/) 99 { 100 if(Pexitprogram) 101 exit(1); 102 Pexitprogram = 1; 103 } 104 105 /* 106 * Program initialisation 107 */ 108 void initprogram(SYNTH_T synth, Config* config, int preferred_port) 109 { 110 middleware = new MiddleWare(std::move(synth), config, preferred_port); 111 master = middleware->spawnMaster(); 112 master->swaplr = swaplr; 113 114 signal(SIGINT, sigterm_exit); 115 signal(SIGTERM, sigterm_exit); 116 Nio::init(master->synth, config->cfg.oss_devs, master); 117 } 118 119 /* 120 * Program exit 121 */ 122 void exitprogram(const Config& config) 123 { 124 Nio::stop(); 125 config.save(); 126 middleware->removeAutoSave(); 127 128 GUI::destroyUi(gui); 129 delete middleware; 130 #if LASH 131 if(lash) 132 delete lash; 133 #endif 134 #if USE_NSM 135 if(nsm) 136 delete nsm; 137 #endif 138 139 FFT_cleanup(); 140 } 141 142 //Windows MIDI OH WHAT A HACK... 143 #ifdef WIN32 144 #include <windows.h> 145 #include <mmsystem.h> 146 namespace zyn{ 147 extern InMgr *in; 148 } 149 HMIDIIN winmidiinhandle = 0; 150 151 void CALLBACK WinMidiInProc(HMIDIIN hMidiIn,UINT wMsg,DWORD dwInstance, 152 DWORD dwParam1,DWORD dwParam2) 153 { 154 int midicommand=0; 155 if (wMsg==MIM_DATA) { 156 int cmd,par1,par2; 157 cmd=dwParam1&0xff; 158 if (cmd==0xfe) return; 159 par1=(dwParam1>>8)&0xff; 160 par2=dwParam1>>16; 161 int cmdchan=cmd&0x0f; 162 int cmdtype=(cmd>>4)&0x0f; 163 164 int tmp=0; 165 MidiEvent ev; 166 switch (cmdtype) { 167 case(0x8)://noteon 168 ev.type = 1; 169 ev.num = par1; 170 ev.channel = cmdchan; 171 ev.value = 0; 172 in->putEvent(ev); 173 break; 174 case(0x9)://noteoff 175 ev.type = 1; 176 ev.num = par1; 177 ev.channel = cmdchan; 178 ev.value = par2&0xff; 179 in->putEvent(ev); 180 break; 181 case(0xb)://controller 182 ev.type = 2; 183 ev.num = par1; 184 ev.channel = cmdchan; 185 ev.value = par2&0xff; 186 in->putEvent(ev); 187 break; 188 case(0xe)://pitch wheel 189 //tmp=(par1+par2*(long int) 128)-8192; 190 //winmaster->SetController(cmdchan,C_pitchwheel,tmp); 191 break; 192 default: 193 break; 194 }; 195 196 }; 197 }; 198 199 void InitWinMidi(int midi) 200 { 201 (void)midi; 202 for(int i=0; i<10; ++i) { 203 long int res=midiInOpen(&winmidiinhandle,i,(DWORD_PTR)(void*)WinMidiInProc,0,CALLBACK_FUNCTION); 204 if(res == MMSYSERR_NOERROR) { 205 res=midiInStart(winmidiinhandle); 206 printf("[INFO] Starting Windows MIDI At %d with code %d(noerror=%d)\n", i, res, MMSYSERR_NOERROR); 207 if(res == 0) 208 return; 209 } else 210 printf("[INFO] No Windows MIDI Device At id %d\n", i); 211 } 212 }; 213 214 //void StopWinMidi() 215 //{ 216 // midiInStop(winmidiinhandle); 217 // midiInClose(winmidiinhandle); 218 //}; 219 #else 220 void InitWinMidi(int) {} 221 #endif 222 223 224 int main(int argc, char *argv[]) 225 { 226 SYNTH_T synth; 227 Config config; 228 int noui = 0; 229 cerr 230 << "\nZynAddSubFX - Copyright (c) 2002-2013 Nasca Octavian Paul and others" 231 << endl; 232 cerr 233 << " Copyright (c) 2009-2019 Mark McCurry [active maintainer]" 234 << endl; 235 cerr << "This program is free software (GNU GPL v2 or later) and \n"; 236 cerr << "it comes with ABSOLUTELY NO WARRANTY.\n" << endl; 237 if(argc == 1) 238 cerr << "Try 'zynaddsubfx --help' for command-line options." << endl; 239 240 /* Get the settings from the Config*/ 241 synth.samplerate = config.cfg.SampleRate; 242 synth.buffersize = config.cfg.SoundBufferSize; 243 synth.oscilsize = config.cfg.OscilSize; 244 swaplr = config.cfg.SwapStereo; 245 compr = config.cfg.AudioOutputCompressor; 246 247 Nio::preferredSampleRate(synth.samplerate); 248 249 synth.alias(); //build aliases 250 251 sprng(time(NULL)); 252 253 // for option entries with the 3rd member (flag) pointing here, 254 // getopt_long*() will return 0 and set this flag to the 4th member (val) 255 int getopt_flag; 256 257 /* Parse command-line options */ 258 struct option opts[] = { 259 // options with single char equivalents 260 { 261 "load", 2, NULL, 'l' 262 }, 263 { 264 "load-instrument", 2, NULL, 'L' 265 }, 266 { 267 "midi-learn", 2, NULL, 'M' 268 }, 269 { 270 "sample-rate", 2, NULL, 'r' 271 }, 272 { 273 "buffer-size", 2, NULL, 'b' 274 }, 275 { 276 "oscil-size", 2, NULL, 'o' 277 }, 278 { 279 "swap", 2, NULL, 'S' 280 }, 281 { 282 "no-gui", 0, NULL, 'U' 283 }, 284 { 285 "dummy", 2, NULL, 'Y' 286 }, 287 { 288 "help", 2, NULL, 'h' 289 }, 290 { 291 "version", 2, NULL, 'v' 292 }, 293 { 294 "named", 1, NULL, 'N' 295 }, 296 { 297 "auto-connect", 0, NULL, 'a' 298 }, 299 { 300 "auto-save", 0, NULL, 'A' 301 }, 302 { 303 "pid-in-client-name", 0, NULL, 'p' 304 }, 305 { 306 "preferred-port", 1, NULL, 'P', 307 }, 308 { 309 "output", 1, NULL, 'O' 310 }, 311 { 312 "input", 1, NULL, 'I' 313 }, 314 { 315 "exec-after-init", 1, NULL, 'e' 316 }, 317 { 318 "dump-oscdoc", 2, NULL, 'd' 319 }, 320 { 321 "dump-json-schema", 2, NULL, 'D' 322 }, 323 // options without single char equivalents ("getopt_flag" compulsory) 324 { 325 "list-inputs", no_argument, &getopt_flag, 'i' 326 }, 327 { 328 "list-outputs", no_argument, &getopt_flag, 'o' 329 }, 330 { 331 0, 0, 0, 0 332 } 333 }; 334 opterr = 0; 335 int option_index = 0, opt; 336 enum class exit_with_t 337 { 338 dont_exit, 339 help, 340 version, 341 list_inputs, 342 list_outputs 343 }; 344 exit_with_t exit_with = exit_with_t::dont_exit; 345 int preferred_port = -1; 346 int auto_save_interval = 0; 347 int wmidi = -1; 348 349 string loadfile, loadinstrument, execAfterInit, loadmidilearn; 350 351 while(1) { 352 int tmp = 0; 353 354 /**\todo check this process for a small memory leak*/ 355 opt = getopt_long(argc, 356 argv, 357 "l:L:M:r:b:o:I:O:N:e:P:A:d:D:hvapSDUYZ", 358 opts, 359 &option_index); 360 char *optarguments = optarg; 361 362 #define GETOP(x) if(optarguments) \ 363 x = optarguments 364 #define GETOPNUM(x) if(optarguments) \ 365 x = atoi(optarguments) 366 367 368 if(opt == -1) 369 break; 370 371 switch(opt) { 372 case 'h': 373 exit_with = exit_with_t::help; 374 break; 375 case 'v': 376 exit_with = exit_with_t::version; 377 break; 378 case 'Y': /* this command a dummy command (has NO effect) 379 and is used because I need for NSIS installer 380 (NSIS sometimes forces a command line for a 381 program, even if I don't need that; eg. when 382 I want to add a icon to a shortcut. 383 */ 384 break; 385 case 'U': 386 noui = 1; 387 break; 388 case 'l': 389 GETOP(loadfile); 390 break; 391 case 'L': 392 GETOP(loadinstrument); 393 break; 394 case 'M': 395 GETOP(loadmidilearn); 396 break; 397 case 'r': 398 GETOPNUM(synth.samplerate); 399 if(synth.samplerate < 4000) { 400 cerr << "ERROR:Incorrect sample rate: " << optarguments 401 << endl; 402 exit(1); 403 } 404 break; 405 case 'b': 406 GETOPNUM(synth.buffersize); 407 if(synth.buffersize < 2) { 408 cerr << "ERROR:Incorrect buffer size: " << optarguments 409 << endl; 410 exit(1); 411 } 412 break; 413 case 'o': 414 if(optarguments) 415 synth.oscilsize = tmp = atoi(optarguments); 416 if(synth.oscilsize < MAX_AD_HARMONICS * 2) 417 synth.oscilsize = MAX_AD_HARMONICS * 2; 418 synth.oscilsize = 419 (int) powf(2, 420 ceil(logf(synth.oscilsize - 1.0f) / logf(2.0f))); 421 if(tmp != synth.oscilsize) 422 cerr 423 << 424 "synth.oscilsize is wrong (must be 2^n) or too small. Adjusting to " 425 << synth.oscilsize << "." << endl; 426 break; 427 case 'S': 428 swaplr = 1; 429 break; 430 case 'N': 431 Nio::setPostfix(optarguments); 432 break; 433 case 'I': 434 if(optarguments) 435 Nio::setDefaultSource(optarguments); 436 break; 437 case 'O': 438 if(optarguments) 439 Nio::setDefaultSink(optarguments); 440 break; 441 case 'a': 442 Nio::autoConnect = true; 443 break; 444 case 'p': 445 Nio::pidInClientName = true; 446 break; 447 case 'P': 448 if(optarguments) 449 preferred_port = atoi(optarguments); 450 break; 451 case 'A': 452 if(optarguments) 453 auto_save_interval = atoi(optarguments); 454 break; 455 case 'e': 456 GETOP(execAfterInit); 457 break; 458 case 'd': 459 if(optarguments) 460 { 461 rtosc::OscDocFormatter s; 462 ofstream outfile(optarguments); 463 s.prog_name = "ZynAddSubFX"; 464 s.p = &MiddleWare::getAllPorts(); 465 s.uri = "http://example.com/fake/"; 466 s.doc_origin = "http://example.com/fake/url.xml"; 467 s.author_first = "Mark"; 468 s.author_last = "McCurry"; 469 outfile << s; 470 } 471 break; 472 case 'D': 473 if(optarguments) 474 { 475 ofstream outfile(optarguments); 476 dump_json(outfile, MiddleWare::getAllPorts()); 477 } 478 break; 479 case 'Z': 480 if(optarguments) 481 wmidi = atoi(optarguments); 482 break; 483 case 0: // catch options without single char equivalent 484 switch(getopt_flag) 485 { 486 case 'i': 487 exit_with = exit_with_t::list_inputs; 488 break; 489 case 'o': 490 exit_with = exit_with_t::list_outputs; 491 break; 492 } 493 break; 494 case '?': 495 cerr << "ERROR:Bad option or parameter.\n" << endl; 496 exit_with = exit_with_t::help; 497 break; 498 } 499 } 500 501 synth.alias(); 502 503 switch (exit_with) 504 { 505 case exit_with_t::version: 506 cout << "Version: " << version << endl; 507 break; 508 case exit_with_t::help: 509 cout << "Usage: zynaddsubfx [OPTION]\n\n" 510 << " -h , --help \t\t\t\t Display command-line help and exit\n" 511 << " -v , --version \t\t\t Display version and exit\n" 512 << " -l file, --load=FILE\t\t\t Loads a .xmz file\n" 513 << " -L file, --load-instrument=FILE\t Loads a .xiz file\n" 514 << " -M file, --midi-learn=FILE\t\t Loads a .xlz file\n" 515 << " -r SR, --sample-rate=SR\t\t Set the sample rate SR\n" 516 << 517 " -b BS, --buffer-size=SR\t\t Set the buffer size (granularity)\n" 518 << " -o OS, --oscil-size=OS\t\t Set the ADsynth oscil. size\n" 519 << " -S , --swap\t\t\t\t Swap Left <--> Right\n" 520 << 521 " -U , --no-gui\t\t\t\t Run ZynAddSubFX without user interface\n" 522 << " -N , --named\t\t\t\t Postfix IO Name when possible\n" 523 << " -a , --auto-connect\t\t\t AutoConnect when using JACK\n" 524 << " -A , --auto-save=INTERVAL\t\t Automatically save at interval\n" 525 << "\t\t\t\t\t (disabled with 0 interval)\n" 526 << " -p , --pid-in-client-name\t\t Append PID to (JACK) " 527 "client name\n" 528 << " -P , --preferred-port\t\t\t Preferred OSC Port\n" 529 << " -O , --output\t\t\t\t Set Output Engine\n" 530 << " -I , --input\t\t\t\t Set Input Engine\n" 531 << " -e , --exec-after-init\t\t Run post-initialization script\n" 532 << " -d , --dump-oscdoc=FILE\t\t Dump oscdoc xml to file\n" 533 << " -D , --dump-json-schema=FILE\t\t Dump osc schema (.json) to file\n" 534 << endl; 535 break; 536 case exit_with_t::list_inputs: 537 case exit_with_t::list_outputs: 538 { 539 Nio::init(synth, config.cfg.oss_devs, nullptr); 540 auto get_func = (getopt_flag == 'i') 541 ? &Nio::getSources 542 : &Nio::getSinks; 543 std::set<std::string> engines = (*get_func)(); 544 for(std::string engine : engines) 545 { 546 std::transform(engine.begin(), engine.end(), engine.begin(), 547 ::tolower); 548 cout << engine << endl; 549 } 550 break; 551 } 552 default: 553 break; 554 } 555 if(exit_with != exit_with_t::dont_exit) 556 return 0; 557 558 cerr.precision(1); 559 cerr << std::fixed; 560 cerr << "\nSample Rate = \t\t" << synth.samplerate << endl; 561 cerr << "Sound Buffer Size = \t" << synth.buffersize << " samples" << endl; 562 cerr << "Internal latency = \t" << synth.dt() * 1000.0f << " ms" << endl; 563 cerr << "ADsynth Oscil.Size = \t" << synth.oscilsize << " samples" << endl; 564 565 initprogram(std::move(synth), &config, preferred_port); 566 567 bool altered_master = false; 568 if(!loadfile.empty()) { 569 altered_master = true; 570 const char *filename = loadfile.c_str(); 571 int tmp = master->loadXML(filename); 572 if(tmp < 0) { 573 cerr << "ERROR: Could not load master file " << loadfile 574 << "." << endl; 575 exit(1); 576 } 577 else { 578 fast_strcpy(master->last_xmz, filename, XMZ_PATH_MAX); 579 master->last_xmz[XMZ_PATH_MAX-1] = 0; 580 master->applyparameters(); 581 cout << "Master file loaded." << endl; 582 } 583 } 584 585 if(!loadinstrument.empty()) { 586 altered_master = true; 587 int loadtopart = 0; 588 int tmp = master->part[loadtopart]->loadXMLinstrument( 589 loadinstrument.c_str()); 590 if(tmp < 0) { 591 cerr << "ERROR: Could not load instrument file " 592 << loadinstrument << '.' << endl; 593 exit(1); 594 } 595 else { 596 master->part[loadtopart]->applyparameters(); 597 master->part[loadtopart]->initialize_rt(); 598 cout << "Instrument file loaded." << endl; 599 } 600 } 601 602 if(!loadmidilearn.empty()) { 603 char msg[1024]; 604 rtosc_message(msg, sizeof(msg), "/load_xlz", 605 "s", loadmidilearn.c_str()); 606 middleware->transmitMsg(msg); 607 } 608 609 if(altered_master) 610 middleware->updateResources(master); 611 612 613 //Run the Nio system 614 printf("[INFO] Nio::start()\n"); 615 bool ioGood = Nio::start(); 616 617 printf("[INFO] exec-after-init\n"); 618 if(!execAfterInit.empty()) { 619 cout << "Executing user supplied command: " << execAfterInit << endl; 620 if(system(execAfterInit.c_str()) == -1) 621 cerr << "Command Failed..." << endl; 622 } 623 624 InitWinMidi(wmidi); 625 master->setAudioCompressor(compr); 626 627 gui = NULL; 628 629 //Capture Startup Responses 630 printf("[INFO] startup OSC\n"); 631 typedef std::vector<const char *> wait_t; 632 wait_t msg_waitlist; 633 middleware->setUiCallback(0, [](void*v,const char*msg) { 634 wait_t &wait = *(wait_t*)v; 635 size_t len = rtosc_message_length(msg, -1); 636 char *copy = new char[len]; 637 memcpy(copy, msg, len); 638 wait.push_back(copy); 639 }, &msg_waitlist); 640 641 printf("[INFO] UI calbacks\n"); 642 if(!noui) 643 gui = GUI::createUi(middleware->spawnUiApi(), &Pexitprogram); 644 middleware->setUiCallback(0, GUI::raiseUi, gui); 645 middleware->setIdleCallback([](void*){GUI::tickUi(gui);}, NULL); 646 647 //Replay Startup Responses 648 printf("[INFO] OSC replay\n"); 649 for(auto msg:msg_waitlist) { 650 GUI::raiseUi(gui, msg); 651 delete [] msg; 652 } 653 654 if(!noui) 655 { 656 GUI::raiseUi(gui, "/show", "i", config.cfg.UserInterfaceMode); 657 if(!ioGood) 658 GUI::raiseUi(gui, "/alert", "s", 659 "Default IO did not initialize.\nDefaulting to NULL backend."); 660 } 661 662 printf("[INFO] auto_save setup\n"); 663 if(auto_save_interval > 0) { 664 int old_save = middleware->checkAutoSave(); 665 if(old_save > 0) 666 GUI::raiseUi(gui, "/alert-reload", "i", old_save); 667 middleware->enableAutoSave(auto_save_interval); 668 } 669 670 //TODO move this stuff into Cmake 671 #if USE_NSM && defined(WIN32) 672 #undef USE_NSM 673 #define USE_NSM 0 674 #endif 675 676 #if LASH && defined(WIN32) 677 #undef LASH 678 #define LASH 0 679 #endif 680 681 #if USE_NSM 682 printf("[INFO] NSM Stuff\n"); 683 char *nsm_url = getenv("NSM_URL"); 684 685 if(nsm_url) { 686 nsm = new NSM_Client(middleware); 687 688 if(!nsm->init(nsm_url)) 689 nsm->announce("ZynAddSubFX", ":switch:", argv[0]); 690 else { 691 delete nsm; 692 nsm = NULL; 693 } 694 } 695 #endif 696 697 #if USE_NSM 698 printf("[INFO] LASH Stuff\n"); 699 if(!nsm) 700 #endif 701 { 702 #if LASH 703 lash = new LASHClient(&argc, &argv); 704 GUI::raiseUi(gui, "/session-type", "s", "LASH"); 705 #endif 706 } 707 708 #ifdef ZEST_GUI 709 #ifndef WIN32 710 pid_t gui_pid = 0; 711 #endif 712 if(!noui) { 713 printf("[INFO] Launching Zyn-Fusion...\n"); 714 char *addr = middleware->getServerAddress(); 715 #ifndef WIN32 716 gui_pid = fork(); 717 if(gui_pid == 0) { 718 auto exec_fusion = [&addr](const char* path) { 719 execlp(path, "zyn-fusion", addr, "--builtin", "--no-hotload", 0); }; 720 #ifndef __APPLE__ 721 if(fusion_dir && *fusion_dir) 722 { 723 std::string fusion = fusion_dir; 724 fusion += "/zest"; 725 if(access(fusion.c_str(), X_OK)) 726 fputs("Warning: CMake's ZynFusionDir does not contain a" 727 "\"zest\" binary - ignoring.", stderr); 728 else { 729 const char* cur = getenv("LD_LIBRARY_PATH"); 730 std::string ld_library_path; 731 if(cur) { 732 ld_library_path += cur; 733 ld_library_path += ":"; 734 } 735 ld_library_path += fusion_dir; 736 setenv("LD_LIBRARY_PATH", ld_library_path.c_str(), 1); 737 exec_fusion(fusion.c_str()); 738 } 739 } 740 #endif 741 exec_fusion("./zyn-fusion"); 742 exec_fusion("/opt/zyn-fusion/zyn-fusion"); 743 exec_fusion("zyn-fusion"); 744 err(1,"Failed to launch Zyn-Fusion"); 745 } 746 #else 747 STARTUPINFO si; 748 PROCESS_INFORMATION pi; 749 memset(&si, 0, sizeof(si)); 750 memset(&pi, 0, sizeof(pi)); 751 char *why_windows = strrchr(addr, ':'); 752 char *seriously_why = why_windows + 1; 753 char start_line[256] = {}; 754 if(why_windows) 755 snprintf(start_line, sizeof(start_line), "zyn-fusion.exe osc.udp://127.0.0.1:%s", seriously_why); 756 else { 757 printf("COULD NOT PARSE <%s>\n", addr); 758 exit(1); 759 } 760 printf("[INFO] starting subprocess via <%s>\n", start_line); 761 if(!CreateProcess(NULL, start_line, 762 NULL, NULL, 0, 0, NULL, NULL, &si, &pi)) { 763 printf("Failed to launch Zyn-Fusion...\n"); 764 exit(1); 765 } 766 #endif 767 free(addr); 768 } 769 #endif 770 771 MemLocker mem_locker; 772 mem_locker.lock(); 773 774 printf("[INFO] Main Loop...\n"); 775 bool already_exited = false; 776 while(Pexitprogram == 0) { 777 #ifndef WIN32 778 #if USE_NSM 779 if(nsm) { 780 nsm->check(); 781 goto done; 782 } 783 #endif 784 #if LASH 785 { 786 string filename; 787 switch(lash->checkevents(filename)) { 788 case LASHClient::Save: 789 GUI::raiseUi(gui, "/save-master", "s", filename.c_str()); 790 lash->confirmevent(LASHClient::Save); 791 break; 792 case LASHClient::Restore: 793 GUI::raiseUi(gui, "/load-master", "s", filename.c_str()); 794 lash->confirmevent(LASHClient::Restore); 795 break; 796 case LASHClient::Quit: 797 Pexitprogram = 1; 798 default: 799 break; 800 } 801 } 802 #endif //LASH 803 804 #if USE_NSM 805 done: 806 #endif 807 GUI::tickUi(gui); 808 #endif // !WIN32 809 middleware->tick(); 810 #ifdef WIN32 811 Sleep(1); 812 #endif 813 814 #ifdef ZEST_GUI 815 #ifndef WIN32 816 if(!noui) { 817 int status = 0; 818 int ret = waitpid(gui_pid, &status, WNOHANG); 819 if(ret == gui_pid) { 820 Pexitprogram = 1; 821 already_exited = true; 822 } 823 } 824 #endif 825 #endif 826 } // while !Pexitprogram 827 828 mem_locker.unlock(); 829 830 #ifdef ZEST_GUI 831 #ifndef WIN32 832 if(!already_exited) { 833 int ret = kill(gui_pid, SIGHUP); 834 if (ret == -1) { 835 err(1, "Failed to terminate Zyn-Fusion...\n"); 836 } 837 } 838 #else 839 (void)already_exited; 840 #endif 841 #else 842 (void)already_exited; 843 #endif 844 exitprogram(config); 845 return 0; 846 }