/[svn]/gigedit/trunk/src/gigedit/gigedit.cpp
ViewVC logotype

Diff of /gigedit/trunk/src/gigedit/gigedit.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 2470 by persson, Sun Sep 15 13:31:04 2013 UTC revision 3364 by schoenebeck, Tue Nov 14 18:07:25 2017 UTC
# Line 1  Line 1 
1  /*  /*
2   * Copyright (C) 2007-2009 Andreas Persson   * Copyright (C) 2007-2017 Andreas Persson
3   *   *
4   * This program is free software; you can redistribute it and/or   * This program is free software; you can redistribute it and/or
5   * modify it under the terms of the GNU General Public License as   * modify it under the terms of the GNU General Public License as
# Line 17  Line 17 
17   * 02110-1301 USA.   * 02110-1301 USA.
18   */   */
19    
20    #include "compat.h"
21    // threads.h must be included first to be able to build with
22    // G_DISABLE_DEPRECATED
23    #if (GLIBMM_MAJOR_VERSION == 2 && GLIBMM_MINOR_VERSION == 31 && GLIBMM_MICRO_VERSION >= 2) || \
24        (GLIBMM_MAJOR_VERSION == 2 && GLIBMM_MINOR_VERSION > 31) || GLIBMM_MAJOR_VERSION > 2
25    #include <glibmm/threads.h>
26    #endif
27    
28  #include "gigedit.h"  #include "gigedit.h"
29    
30    #if GTKMM_MAJOR_VERSION < 3
31    #include <gdkmm/region.h>
32    #endif
33  #include <glibmm/dispatcher.h>  #include <glibmm/dispatcher.h>
34  #include <glibmm/main.h>  #include <glibmm/main.h>
35    #include <glibmm/miscutils.h>
36  #include <gtkmm/main.h>  #include <gtkmm/main.h>
37    
38    #ifdef WIN32
39    #include <gtkmm/icontheme.h>
40    #endif
41    
42    #if defined(__APPLE__)
43    # include <CoreFoundation/CoreFoundation.h>
44    # include "MacHelper.h"
45    #endif
46    
47  #include "mainwindow.h"  #include "mainwindow.h"
48    
49  #include "global.h"  #include "global.h"
# Line 30  Line 51 
51  #ifdef __APPLE__  #ifdef __APPLE__
52  #include <dlfcn.h>  #include <dlfcn.h>
53  #include <glibmm/fileutils.h>  #include <glibmm/fileutils.h>
54  #include <glibmm/miscutils.h>  #endif
55    
56    //TODO: (hopefully) just a temporary nasty hack for launching gigedit on the main thread on Mac (see comments below in this file for details)
57    #if defined(__APPLE__) && HAVE_LINUXSAMPLER // the following global external variables are defined in LinuxSampler's global_private.cpp ...
58    extern bool g_mainThreadCallbackSupported;
59    extern void (*g_mainThreadCallback)(void* info);
60    extern void* g_mainThreadCallbackInfo;
61    extern bool g_fireMainThreadCallback;
62  #endif  #endif
63    
64  namespace {  namespace {
# Line 45  namespace { Line 73  namespace {
73  //  //
74  class GigEditState : public sigc::trackable {  class GigEditState : public sigc::trackable {
75  public:  public:
76      GigEditState(GigEdit* parent) : parent(parent) { }      GigEditState(GigEdit* parent) :
77            window(0), parent(parent), instrument(0) { }
78      void run(gig::Instrument* pInstrument);      void run(gig::Instrument* pInstrument);
79    
80      MainWindow* window;      MainWindow* window;
# Line 85  private: Line 114  private:
114      GigEdit* parent;      GigEdit* parent;
115      Cond open;      Cond open;
116      Cond close;      Cond close;
117        Cond initialized;
118      gig::Instrument* instrument;      gig::Instrument* instrument;
119    
120      void open_window();      void open_window();
121      void close_window();      void close_window();
122    #if defined(__APPLE__)
123        static void runInCFMainLoop(void* info);
124    #endif
125  };  };
126    
127  #ifdef WIN32  #ifdef WIN32
128  HINSTANCE gigedit_dll_handle = 0;  HINSTANCE gigedit_dll_handle = 0;
129    std::string gigedit_datadir;
130    bool gigedit_installdir_is_parent = false;
131  #endif  #endif
132    
133  #ifdef __APPLE__  #ifdef __APPLE__
# Line 111  void init_app() { Line 146  void init_app() {
146          // under the same dir as the gigedit dylib is installed in.          // under the same dir as the gigedit dylib is installed in.
147          Dl_info info;          Dl_info info;
148          if (dladdr((void*)&init_app, &info)) {          if (dladdr((void*)&init_app, &info)) {
149    #ifdef CONFIG_FORCE_GTK_LIBDIR
150                std::string libdir = CONFIG_FORCE_GTK_LIBDIR;
151    #else
152              std::string libdir = Glib::path_get_dirname(info.dli_fname);              std::string libdir = Glib::path_get_dirname(info.dli_fname);
153    #endif
154    
155              if (Glib::getenv("PANGO_SYSCONFDIR") == "" &&              if (Glib::getenv("PANGO_SYSCONFDIR") == "" &&
156                  Glib::file_test(Glib::build_filename(libdir,                  Glib::file_test(Glib::build_filename(libdir,
# Line 127  void init_app() { Line 166  void init_app() {
166                      Glib::setenv("GDK_PIXBUF_MODULE_FILE", module_file, true);                      Glib::setenv("GDK_PIXBUF_MODULE_FILE", module_file, true);
167                  }                  }
168              }              }
169  #if HAVE_GETTEXT    //FIXME: for some reason AC GETTEXT check fails on the Mac cross compiler?
170      //#if HAVE_GETTEXT
171              std::string localedir = Glib::build_filename(libdir, "locale");              std::string localedir = Glib::build_filename(libdir, "locale");
172              if (Glib::file_test(localedir, Glib::FILE_TEST_EXISTS)) {              if (Glib::file_test(localedir, Glib::FILE_TEST_EXISTS)) {
173                  gigedit_localedir = localedir;                  gigedit_localedir = localedir;
# Line 135  void init_app() { Line 175  void init_app() {
175              } else {              } else {
176                  bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);                  bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
177              }              }
178  #endif    //#endif
179          }          }
180    
181          // The gtk file dialog stores its recent files state in          // The gtk file dialog stores its recent files state in
# Line 143  void init_app() { Line 183  void init_app() {
183          g_mkdir_with_parents(          g_mkdir_with_parents(
184              Glib::build_filename(Glib::get_home_dir(),              Glib::build_filename(Glib::get_home_dir(),
185                                   ".local/share").c_str(), 0777);                                   ".local/share").c_str(), 0777);
186  #endif  #endif // __APPLE__
   
 #if HAVE_GETTEXT  
187    
188  #ifdef WIN32  #ifdef WIN32
189  #if GLIB_CHECK_VERSION(2, 16, 0)          // Find the data directory: the linuxsampler installer puts
190            // the binaries in sub directories "32" and "64", so the share
191            // directory is located in the parent of the directory of the
192            // binaries.
193    
194      #if GLIB_CHECK_VERSION(2, 16, 0)
195          gchar* root =          gchar* root =
196              g_win32_get_package_installation_directory_of_module(gigedit_dll_handle);              g_win32_get_package_installation_directory_of_module(gigedit_dll_handle);
197  #else    #else
198          gchar* root =          gchar* root =
199              g_win32_get_package_installation_directory(NULL, NULL);              g_win32_get_package_installation_directory(NULL, NULL);
200  #endif    #endif
201          gchar* temp = g_build_filename(root, "/share/locale", NULL);          std::string installdir(root);
202          g_free(root);          g_free(root);
203          gchar* localedir = g_win32_locale_filename_from_utf8(temp);          std::string basename = Glib::path_get_basename(installdir);
204          g_free(temp);          if (basename == "32" || basename == "64") {
205                installdir = Glib::path_get_dirname(installdir);
206                gigedit_installdir_is_parent = true;
207            }
208            gigedit_datadir = Glib::build_filename(installdir, "share");
209    
210            // the file dialogs need glib-2.0/schemas/gschemas.compiled
211            if (gigedit_installdir_is_parent) {
212                Glib::setenv("GSETTINGS_SCHEMA_DIR",
213                             Glib::build_filename(gigedit_datadir,
214                                                  "glib-2.0/schemas"));
215            }
216    #endif
217    
218    //FIXME: for some reason AC GETTEXT check fails on the Mac cross compiler?
219    #if (HAVE_GETTEXT || defined(__APPLE__))
220      #ifdef WIN32
221            std::string temp = Glib::build_filename(gigedit_datadir, "locale");
222            gchar* localedir = g_win32_locale_filename_from_utf8(temp.c_str());
223          bindtextdomain(GETTEXT_PACKAGE, localedir);          bindtextdomain(GETTEXT_PACKAGE, localedir);
224          g_free(localedir);          g_free(localedir);
225  #elif !defined(__APPLE__)    #elif !defined(__APPLE__)
226          bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);          bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
227  #endif    #endif
228          bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");          bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
229          textdomain(GETTEXT_PACKAGE);          textdomain(GETTEXT_PACKAGE);
230  #endif // HAVE_GETTEXT  #endif // HAVE_GETTEXT
# Line 176  void init_app() { Line 237  void init_app() {
237      }      }
238  }  }
239    
240    void init_app_after_gtk_init() {
241    //FIXME: for some reason AC GETTEXT check fails on the Mac cross compiler?
242    #if (/*HAVE_GETTEXT &&*/ defined(__APPLE__))
243        // Gtk::Main binds the gtk locale to a possible non-existent
244        // directory. If we have bundled gtk locale files, we rebind here,
245        // after the Gtk::Main constructor.
246        if (!gigedit_localedir.empty()) {
247            bindtextdomain("gtk20", gigedit_localedir.c_str());
248        }
249    #endif
250    
251    #ifdef WIN32
252        if (gigedit_installdir_is_parent) {
253            std::string icon_dir = Glib::build_filename(gigedit_datadir, "icons");
254            Gtk::IconTheme::get_default()->append_search_path(icon_dir);
255        }
256    #endif
257    }
258    
259  void connect_signals(GigEdit* gigedit, MainWindow* mainwindow) {  void connect_signals(GigEdit* gigedit, MainWindow* mainwindow) {
260      // the signals of the "GigEdit" class are actually just proxies, that      // the signals of the "GigEdit" class are actually just proxies, that
261      // is they simply forward the signals of the internal classes to the      // is they simply forward the signals of the internal classes to the
# Line 216  void connect_signals(GigEdit* gigedit, M Line 296  void connect_signals(GigEdit* gigedit, M
296      mainwindow->signal_keyboard_key_released().connect(      mainwindow->signal_keyboard_key_released().connect(
297          gigedit->signal_keyboard_key_released().make_slot()          gigedit->signal_keyboard_key_released().make_slot()
298      );      );
299        mainwindow->signal_switch_sampler_instrument().connect(
300            gigedit->signal_switch_sampler_instrument().make_slot()
301        );
302        mainwindow->signal_script_to_be_changed.connect(
303            gigedit->signal_script_to_be_changed.make_slot()
304        );
305        mainwindow->signal_script_changed.connect(
306            gigedit->signal_script_changed.make_slot()
307        );
308  }  }
309    
310  } // namespace  } // namespace
# Line 227  GigEdit::GigEdit() { Line 316  GigEdit::GigEdit() {
316  int GigEdit::run(int argc, char* argv[]) {  int GigEdit::run(int argc, char* argv[]) {
317      init_app();      init_app();
318    
319    #if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION < 89 || (GTKMM_MINOR_VERSION == 89 && GTKMM_MICRO_VERSION < 4))) // GTKMM < 3.89.4
320      Gtk::Main kit(argc, argv);      Gtk::Main kit(argc, argv);
321    #else
322  #ifdef __APPLE__      Glib::RefPtr<Gtk::Application> app =
323      // Gtk::Main binds the gtk locale to a possible non-existent          Gtk::Application::create("org.linuxsampler.gigedit");
     // directory. If we have bundled gtk locale files, we rebind here,  
     // after the Gtk::Main constructor.  
     if (!gigedit_localedir.empty()) {  
         bindtextdomain("gtk20", gigedit_localedir.c_str());  
     }  
324  #endif  #endif
325        init_app_after_gtk_init();
326    
327      MainWindow window;      MainWindow window;
328      connect_signals(this, &window);      connect_signals(this, &window);
329      if (argc >= 2) window.load_file(argv[1]);      if (argc >= 2) window.load_file(argv[1]);
330    #if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION < 89 || (GTKMM_MINOR_VERSION == 89 && GTKMM_MICRO_VERSION < 4))) // GTKMM < 3.89.4
331      kit.run(window);      kit.run(window);
332    #else
333        app->run(window, argc, argv);
334    #endif
335        
336      return 0;      return 0;
337  }  }
338    
# Line 257  int GigEdit::run(gig::Instrument* pInstr Line 348  int GigEdit::run(gig::Instrument* pInstr
348    
349  void GigEdit::on_note_on_event(int key, int velocity) {  void GigEdit::on_note_on_event(int key, int velocity) {
350      if (!this->state) return;      if (!this->state) return;
351      GigEditState* state = (GigEditState*) this->state;      GigEditState* state = static_cast<GigEditState*>(this->state);
352      state->window->signal_note_on().emit(key, velocity);      state->window->signal_note_on().emit(key, velocity);
353  }  }
354    
355  void GigEdit::on_note_off_event(int key, int velocity) {  void GigEdit::on_note_off_event(int key, int velocity) {
356      if (!this->state) return;      if (!this->state) return;
357      GigEditState* state = (GigEditState*) this->state;      GigEditState* state = static_cast<GigEditState*>(this->state);
358      state->window->signal_note_off().emit(key, velocity);      state->window->signal_note_off().emit(key, velocity);
359  }  }
360    
# Line 315  sigc::signal<void, int/*key*/, int/*velo Line 406  sigc::signal<void, int/*key*/, int/*velo
406      return keyboard_key_released_signal;      return keyboard_key_released_signal;
407  }  }
408    
409    sigc::signal<void, gig::Instrument*>& GigEdit::signal_switch_sampler_instrument() {
410        return switch_sampler_instrument_signal;
411    }
412    
413  #ifdef OLD_THREADS  #ifdef OLD_THREADS
414  Glib::StaticMutex GigEditState::mutex = GLIBMM_STATIC_MUTEX_INIT;  Glib::StaticMutex GigEditState::mutex = GLIBMM_STATIC_MUTEX_INIT;
415  #else  #else
# Line 345  void GigEditState::close_window() { Line 440  void GigEditState::close_window() {
440      close.signal();      close.signal();
441  }  }
442    
443    #if defined(WIN32) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
444    // make sure stack is 16-byte aligned for SSE instructions
445    __attribute__((force_align_arg_pointer))
446    #endif
447  void GigEditState::main_loop_run(Cond* initialized) {  void GigEditState::main_loop_run(Cond* initialized) {
448      int argc = 1;      int argc = 1;
449      const char* argv_c[] = { "gigedit" };      const char* argv_c[] = { "gigedit" };
450      char** argv = const_cast<char**>(argv_c);      char** argv = const_cast<char**>(argv_c);
451    #if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION < 89 || (GTKMM_MINOR_VERSION == 89 && GTKMM_MICRO_VERSION < 4))) // GTKMM < 3.89.4
452      Gtk::Main main_loop(argc, argv);      Gtk::Main main_loop(argc, argv);
 #ifdef __APPLE__  
     if (!gigedit_localedir.empty()) {  
         bindtextdomain("gtk20", gigedit_localedir.c_str());  
     }  
453  #endif  #endif
454        init_app_after_gtk_init();
455    
456      dispatcher = new Glib::Dispatcher();      dispatcher = new Glib::Dispatcher();
457      dispatcher->connect(sigc::ptr_fun(&GigEditState::open_window_static));      dispatcher->connect(sigc::ptr_fun(&GigEditState::open_window_static));
458      initialized->signal();      initialized->signal();
459    
460    #if GTKMM_MAJOR_VERSION < 3 || (GTKMM_MAJOR_VERSION == 3 && (GTKMM_MINOR_VERSION < 89 || (GTKMM_MINOR_VERSION == 89 && GTKMM_MICRO_VERSION < 4))) // GTKMM < 3.89.4
461      main_loop.run();      main_loop.run();
462    #else
463        Gtk::Main::run();
464    #endif
465    }
466    
467    #if defined(__APPLE__)
468    
469    void GigEditState::runInCFMainLoop(void* info) {
470        printf("runInCFMainLoop() entered\n"); fflush(stdout);
471        GigEditState* state = static_cast<GigEditState*>(info);
472        state->main_loop_run(
473            &state->initialized
474        );
475        printf("runInCFMainLoop() left\n"); fflush(stdout);
476  }  }
477    
478    #endif // __APPLE__
479    
480  void GigEditState::run(gig::Instrument* pInstrument) {  void GigEditState::run(gig::Instrument* pInstrument) {
481      mutex.lock(); // lock access to static variables      mutex.lock(); // lock access to static variables
482    
483      static bool main_loop_started = false;      static bool main_loop_started = false;
484        instrument = pInstrument;
485      if (!main_loop_started) {      if (!main_loop_started) {
486          Cond initialized;  #if defined(__APPLE__) && HAVE_LINUXSAMPLER
487  #ifdef OLD_THREADS          // spawn GUI on main thread :
488            //     On OS X the Gtk GUI can only be launched on a process's "main"
489            //     thread. When trying to launch the Gtk GUI on any other thread,
490            //     there will only be a white box, because the GUI would not receive
491            //     any events, since it would listen to the wrong system event loop.
492            //     So far we haven't investigated whether there is any kind of
493            //     circumvention to allow doing that also on other OS X threads.
494            {
495                // In case the sampler was launched as standalone sampler (not as
496                // plugin), use the following global callback variable hack ...
497                if (g_mainThreadCallbackSupported) {
498                    printf("Setting callback ...\n"); fflush(stdout);
499                    g_mainThreadCallback = runInCFMainLoop;
500                    g_mainThreadCallbackInfo = this;
501                    g_fireMainThreadCallback = true;
502                    printf("Callback variables set.\n"); fflush(stdout);
503                } else { // Sampler was launched as (i.e. AU / VST) plugin ...
504                    // When the sampler was launched as plugin, we have no idea
505                    // whether any sampler thread is the process's "main" thread.
506                    // So that's why we are trying to use Apple's API for trying to
507                    // launch our callback function on the process's main thread.
508                    // However this will only work, if the plugin host application
509                    // established a CF event loop, that is if the application is
510                    // using Cocoa for its GUI. For other host applications the
511                    // callback will never be executed and thus gigedit would not
512                    // popup.
513                    
514                    // should be pretty much the same as the Objective-C solution below with macHelperRunCFuncOnMainThread()
515                    /*CFRunLoopSourceContext sourceContext = CFRunLoopSourceContext();
516                    sourceContext.info = this;
517                    sourceContext.perform = runInCFMainLoop;
518                    printf("CFRunLoopSourceCreate\n"); fflush(stdout);
519                    CFRunLoopSourceRef source = CFRunLoopSourceCreate(
520                        kCFAllocatorDefault, // allocator
521                        1, // priority
522                        &sourceContext
523                    );
524                    printf("CFRunLoopAddSource\n"); fflush(stdout);
525                    CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode);
526                    CFRelease(source);*/
527                    
528                    // use Apple's Objective-C API to call our callback function
529                    // 'runInCFMainLoop()' on the process's "main" thread
530                    macHelperRunCFuncOnMainThread(runInCFMainLoop, this);
531                }
532            }
533    #else
534      #ifdef OLD_THREADS
535          Glib::Thread::create(          Glib::Thread::create(
536              sigc::bind(sigc::ptr_fun(&GigEditState::main_loop_run),              sigc::bind(sigc::ptr_fun(&GigEditState::main_loop_run),
537                         &initialized),                         &initialized),
538              false);              false);
539  #else    #else
540          Glib::Threads::Thread::create(          Glib::Threads::Thread::create(
541              sigc::bind(sigc::ptr_fun(&GigEditState::main_loop_run),              sigc::bind(sigc::ptr_fun(&GigEditState::main_loop_run),
542                         &initialized));                         &initialized));
543      #endif
544  #endif  #endif
545            printf("Waiting for GUI being initialized (on main thread) ....\n"); fflush(stdout);
546          initialized.wait();          initialized.wait();
547            printf("GUI is now initialized. Everything done.\n"); fflush(stdout);
548          main_loop_started = true;          main_loop_started = true;
549      }      }
     instrument = pInstrument;  
550      current = this;      current = this;
551      dispatcher->emit();      dispatcher->emit();
552      open.wait(); // wait until the GUI thread has read current      open.wait(); // wait until the GUI thread has read current

Legend:
Removed from v.2470  
changed lines
  Added in v.3364

  ViewVC Help
Powered by ViewVC