/[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 1656 by schoenebeck, Sat Feb 2 08:18:19 2008 UTC revision 2903 by schoenebeck, Tue May 3 14:08:34 2016 UTC
# Line 1  Line 1 
1  /*  /*
2   * Copyright (C) 2007, 2008 Andreas Persson   * Copyright (C) 2007-2015 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 <glibmmconfig.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  #include <gtkmm/main.h>  #include <gtkmmconfig.h>
31    #if GTKMM_MAJOR_VERSION < 3
32    #include <gdkmm/region.h>
33    #endif
34    #include <glibmm/dispatcher.h>
35  #include <glibmm/main.h>  #include <glibmm/main.h>
36    #include <gtkmm/main.h>
37    
38    #if defined(__APPLE__)
39    # include <CoreFoundation/CoreFoundation.h>
40    # include "MacHelper.h"
41    #endif
42    
43  #include "mainwindow.h"  #include "mainwindow.h"
44    
45  #include "global.h"  #include "global.h"
46    
47    #ifdef __APPLE__
48    #include <dlfcn.h>
49    #include <glibmm/fileutils.h>
50    #include <glibmm/miscutils.h>
51    #endif
52    
53    //TODO: (hopefully) just a temporary nasty hack for launching gigedit on the main thread on Mac (see comments below in this file for details)
54    #if defined(__APPLE__) && HAVE_LINUXSAMPLER // the following global external variables are defined in LinuxSampler's global_private.cpp ...
55    extern bool g_mainThreadCallbackSupported;
56    extern void (*g_mainThreadCallback)(void* info);
57    extern void* g_mainThreadCallbackInfo;
58    extern bool g_fireMainThreadCallback;
59    #endif
60    
61  namespace {  namespace {
62    
63  // State for a gigedit thread.  // State for a gigedit thread.
# Line 37  namespace { Line 70  namespace {
70  //  //
71  class GigEditState : public sigc::trackable {  class GigEditState : public sigc::trackable {
72  public:  public:
73      GigEditState(GigEdit* parent) : parent(parent) { }      GigEditState(GigEdit* parent) :
74            window(0), parent(parent), instrument(0) { }
75      void run(gig::Instrument* pInstrument);      void run(gig::Instrument* pInstrument);
76    
77      MainWindow* window;      MainWindow* window;
# Line 48  private: Line 82  private:
82      class Cond {      class Cond {
83      private:      private:
84          bool pred;          bool pred;
85          Glib::Mutex mutex;          Glib::Threads::Mutex mutex;
86          Glib::Cond cond;          Glib::Threads::Cond cond;
87      public:      public:
88          Cond() : pred(false) { }          Cond() : pred(false) { }
89          void signal() {          void signal() {
90              Glib::Mutex::Lock lock(mutex);              Glib::Threads::Mutex::Lock lock(mutex);
91              pred = true;              pred = true;
92              cond.signal();              cond.signal();
93          }          }
94          void wait() {          void wait() {
95              Glib::Mutex::Lock lock(mutex);              Glib::Threads::Mutex::Lock lock(mutex);
96              while (!pred) cond.wait(mutex);              while (!pred) cond.wait(mutex);
97          }          }
98      };      };
99    
100    #ifdef OLD_THREADS
101      static Glib::StaticMutex mutex;      static Glib::StaticMutex mutex;
102    #else
103        static Glib::Threads::Mutex mutex;
104    #endif
105      static Glib::Dispatcher* dispatcher;      static Glib::Dispatcher* dispatcher;
106      static GigEditState* current;      static GigEditState* current;
107    
# Line 73  private: Line 111  private:
111      GigEdit* parent;      GigEdit* parent;
112      Cond open;      Cond open;
113      Cond close;      Cond close;
114        Cond initialized;
115      gig::Instrument* instrument;      gig::Instrument* instrument;
116    
117      void open_window();      void open_window();
118      void close_window();      void close_window();
119    #if defined(__APPLE__)
120        static void runInCFMainLoop(void* info);
121    #endif
122  };  };
123    
124    #ifdef WIN32
125    HINSTANCE gigedit_dll_handle = 0;
126    #endif
127    
128    #ifdef __APPLE__
129    std::string gigedit_localedir;
130    #endif
131    
132  void init_app() {  void init_app() {
133      static bool process_initialized = false;      static bool process_initialized = false;
134      if (!process_initialized) {      if (!process_initialized) {
# Line 86  void init_app() { Line 136  void init_app() {
136                    << std::flush;                    << std::flush;
137          setlocale(LC_ALL, "");          setlocale(LC_ALL, "");
138    
139  #if HAVE_GETTEXT  #ifdef __APPLE__
140            // Look for pango.modules, gdk-pixbuf.loaders and locale files
141            // under the same dir as the gigedit dylib is installed in.
142            Dl_info info;
143            if (dladdr((void*)&init_app, &info)) {
144                std::string libdir = Glib::path_get_dirname(info.dli_fname);
145    
146                if (Glib::getenv("PANGO_SYSCONFDIR") == "" &&
147                    Glib::file_test(Glib::build_filename(libdir,
148                                                         "pango/pango.modules"),
149                                    Glib::FILE_TEST_EXISTS)) {
150                    Glib::setenv("PANGO_SYSCONFDIR", libdir, true);
151                }
152                if (Glib::getenv("GDK_PIXBUF_MODULE_FILE") == "") {
153                    std::string module_file =
154                        Glib::build_filename(libdir,
155                                             "gtk-2.0/gdk-pixbuf.loaders");
156                    if (Glib::file_test(module_file, Glib::FILE_TEST_EXISTS)) {
157                        Glib::setenv("GDK_PIXBUF_MODULE_FILE", module_file, true);
158                    }
159                }
160      //FIXME: for some reason AC GETTEXT check fails on the Mac cross compiler?
161      //#if HAVE_GETTEXT
162                std::string localedir = Glib::build_filename(libdir, "locale");
163                if (Glib::file_test(localedir, Glib::FILE_TEST_EXISTS)) {
164                    gigedit_localedir = localedir;
165                    bindtextdomain(GETTEXT_PACKAGE, gigedit_localedir.c_str());
166                } else {
167                    bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
168                }
169      //#endif
170            }
171    
172            // The gtk file dialog stores its recent files state in
173            // ~/.local/share
174            g_mkdir_with_parents(
175                Glib::build_filename(Glib::get_home_dir(),
176                                     ".local/share").c_str(), 0777);
177    #endif // __APPLE__
178    
179    //FIXME: for some reason AC GETTEXT check fails on the Mac cross compiler?
180    #if (HAVE_GETTEXT || defined(__APPLE__))
181    
182      #ifdef WIN32
183        #if GLIB_CHECK_VERSION(2, 16, 0)
184            gchar* root =
185                g_win32_get_package_installation_directory_of_module(gigedit_dll_handle);
186        #else
187            gchar* root =
188                g_win32_get_package_installation_directory(NULL, NULL);
189        #endif
190            gchar* temp = g_build_filename(root, "/share/locale", NULL);
191            g_free(root);
192            gchar* localedir = g_win32_locale_filename_from_utf8(temp);
193            g_free(temp);
194            bindtextdomain(GETTEXT_PACKAGE, localedir);
195            g_free(localedir);
196      #elif !defined(__APPLE__)
197          bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);          bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
198      #endif
199          bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");          bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
200          textdomain(GETTEXT_PACKAGE);          textdomain(GETTEXT_PACKAGE);
201  #endif // HAVE_GETTEXT  #endif // HAVE_GETTEXT
202    
203    #ifdef OLD_THREADS
204          // make sure thread_init() is called once and ONLY once per process          // make sure thread_init() is called once and ONLY once per process
205          if (!Glib::thread_supported()) Glib::thread_init();          if (!Glib::thread_supported()) Glib::thread_init();
206    #endif
207          process_initialized = true;          process_initialized = true;
208      }      }
209  }  }
# Line 127  void connect_signals(GigEdit* gigedit, M Line 236  void connect_signals(GigEdit* gigedit, M
236      mainwindow->signal_dimreg_changed().connect(      mainwindow->signal_dimreg_changed().connect(
237          gigedit->signal_dimreg_changed().make_slot()          gigedit->signal_dimreg_changed().make_slot()
238      );      );
239        mainwindow->signal_sample_changed().connect(
240            gigedit->signal_sample_changed().make_slot()
241        );
242      mainwindow->signal_sample_ref_changed().connect(      mainwindow->signal_sample_ref_changed().connect(
243          gigedit->signal_sample_ref_changed().make_slot()          gigedit->signal_sample_ref_changed().make_slot()
244      );      );
245        mainwindow->signal_keyboard_key_hit().connect(
246            gigedit->signal_keyboard_key_hit().make_slot()
247        );
248        mainwindow->signal_keyboard_key_released().connect(
249            gigedit->signal_keyboard_key_released().make_slot()
250        );
251        mainwindow->signal_switch_sampler_instrument().connect(
252            gigedit->signal_switch_sampler_instrument().make_slot()
253        );
254        mainwindow->signal_script_to_be_changed.connect(
255            gigedit->signal_script_to_be_changed.make_slot()
256        );
257        mainwindow->signal_script_changed.connect(
258            gigedit->signal_script_changed.make_slot()
259        );
260  }  }
261    
262  } // namespace  } // namespace
# Line 142  int GigEdit::run(int argc, char* argv[]) Line 269  int GigEdit::run(int argc, char* argv[])
269      init_app();      init_app();
270    
271      Gtk::Main kit(argc, argv);      Gtk::Main kit(argc, argv);
272    
273    //FIXME: for some reason AC GETTEXT check fails on the Mac cross compiler?
274    #if (/*HAVE_GETTEXT &&*/ defined(__APPLE__))
275        // Gtk::Main binds the gtk locale to a possible non-existent
276        // directory. If we have bundled gtk locale files, we rebind here,
277        // after the Gtk::Main constructor.
278        if (!gigedit_localedir.empty()) {
279            bindtextdomain("gtk20", gigedit_localedir.c_str());
280        }
281    #endif
282    
283      MainWindow window;      MainWindow window;
284      connect_signals(this, &window);      connect_signals(this, &window);
285      if (argc >= 2) window.load_file(argv[1]);      if (argc >= 2) window.load_file(argv[1]);
# Line 161  int GigEdit::run(gig::Instrument* pInstr Line 299  int GigEdit::run(gig::Instrument* pInstr
299    
300  void GigEdit::on_note_on_event(int key, int velocity) {  void GigEdit::on_note_on_event(int key, int velocity) {
301      if (!this->state) return;      if (!this->state) return;
302      GigEditState* state = (GigEditState*) this->state;      GigEditState* state = static_cast<GigEditState*>(this->state);
303      state->window->signal_note_on().emit(key, velocity);      state->window->signal_note_on().emit(key, velocity);
304  }  }
305    
306  void GigEdit::on_note_off_event(int key, int velocity) {  void GigEdit::on_note_off_event(int key, int velocity) {
307      if (!this->state) return;      if (!this->state) return;
308      GigEditState* state = (GigEditState*) this->state;      GigEditState* state = static_cast<GigEditState*>(this->state);
309      state->window->signal_note_off().emit(key, velocity);      state->window->signal_note_off().emit(key, velocity);
310  }  }
311    
# Line 203  sigc::signal<void, gig::DimensionRegion* Line 341  sigc::signal<void, gig::DimensionRegion*
341      return dimreg_changed_signal;      return dimreg_changed_signal;
342  }  }
343    
344    sigc::signal<void, gig::Sample*>& GigEdit::signal_sample_changed() {
345        return sample_changed_signal;
346    }
347    
348  sigc::signal<void, gig::Sample*/*old*/, gig::Sample*/*new*/>& GigEdit::signal_sample_ref_changed() {  sigc::signal<void, gig::Sample*/*old*/, gig::Sample*/*new*/>& GigEdit::signal_sample_ref_changed() {
349      return sample_ref_changed_signal;      return sample_ref_changed_signal;
350  }  }
351    
352    sigc::signal<void, int/*key*/, int/*velocity*/>& GigEdit::signal_keyboard_key_hit() {
353        return keyboard_key_hit_signal;
354    }
355    
356    sigc::signal<void, int/*key*/, int/*velocity*/>& GigEdit::signal_keyboard_key_released() {
357        return keyboard_key_released_signal;
358    }
359    
360    sigc::signal<void, gig::Instrument*>& GigEdit::signal_switch_sampler_instrument() {
361        return switch_sampler_instrument_signal;
362    }
363    
364    #ifdef OLD_THREADS
365  Glib::StaticMutex GigEditState::mutex = GLIBMM_STATIC_MUTEX_INIT;  Glib::StaticMutex GigEditState::mutex = GLIBMM_STATIC_MUTEX_INIT;
366    #else
367    Glib::Threads::Mutex GigEditState::mutex;
368    #endif
369  Glib::Dispatcher* GigEditState::dispatcher = 0;  Glib::Dispatcher* GigEditState::dispatcher = 0;
370  GigEditState* GigEditState::current = 0;  GigEditState* GigEditState::current = 0;
371    
# Line 239  void GigEditState::main_loop_run(Cond* i Line 396  void GigEditState::main_loop_run(Cond* i
396      const char* argv_c[] = { "gigedit" };      const char* argv_c[] = { "gigedit" };
397      char** argv = const_cast<char**>(argv_c);      char** argv = const_cast<char**>(argv_c);
398      Gtk::Main main_loop(argc, argv);      Gtk::Main main_loop(argc, argv);
399    //FIXME: for some reason AC GETTEXT check fails on the Mac cross compiler?
400    #if (/*HAVE_GETTEXT &&*/ defined(__APPLE__))
401        if (!gigedit_localedir.empty()) {
402            bindtextdomain("gtk20", gigedit_localedir.c_str());
403        }
404    #endif
405    
406      dispatcher = new Glib::Dispatcher();      dispatcher = new Glib::Dispatcher();
407      dispatcher->connect(sigc::ptr_fun(&GigEditState::open_window_static));      dispatcher->connect(sigc::ptr_fun(&GigEditState::open_window_static));
# Line 247  void GigEditState::main_loop_run(Cond* i Line 410  void GigEditState::main_loop_run(Cond* i
410      main_loop.run();      main_loop.run();
411  }  }
412    
413    #if defined(__APPLE__)
414    
415    void GigEditState::runInCFMainLoop(void* info) {
416        printf("runInCFMainLoop() entered\n"); fflush(stdout);
417        GigEditState* state = static_cast<GigEditState*>(info);
418        state->main_loop_run(
419            &state->initialized
420        );
421        printf("runInCFMainLoop() left\n"); fflush(stdout);
422    }
423    
424    #endif // __APPLE__
425    
426  void GigEditState::run(gig::Instrument* pInstrument) {  void GigEditState::run(gig::Instrument* pInstrument) {
427      mutex.lock(); // lock access to static variables      mutex.lock(); // lock access to static variables
428    
429      static bool main_loop_started = false;      static bool main_loop_started = false;
430        instrument = pInstrument;
431      if (!main_loop_started) {      if (!main_loop_started) {
432          Cond initialized;  #if defined(__APPLE__) && HAVE_LINUXSAMPLER
433            // spawn GUI on main thread :
434            //     On OS X the Gtk GUI can only be launched on a process's "main"
435            //     thread. When trying to launch the Gtk GUI on any other thread,
436            //     there will only be a white box, because the GUI would not receive
437            //     any events, since it would listen to the wrong system event loop.
438            //     So far we haven't investigated whether there is any kind of
439            //     circumvention to allow doing that also on other OS X threads.
440            {
441                // In case the sampler was launched as standalone sampler (not as
442                // plugin), use the following global callback variable hack ...
443                if (g_mainThreadCallbackSupported) {
444                    printf("Setting callback ...\n"); fflush(stdout);
445                    g_mainThreadCallback = runInCFMainLoop;
446                    g_mainThreadCallbackInfo = this;
447                    g_fireMainThreadCallback = true;
448                    printf("Callback variables set.\n"); fflush(stdout);
449                } else { // Sampler was launched as (i.e. AU / VST) plugin ...
450                    // When the sampler was launched as plugin, we have no idea
451                    // whether any sampler thread is the process's "main" thread.
452                    // So that's why we are trying to use Apple's API for trying to
453                    // launch our callback function on the process's main thread.
454                    // However this will only work, if the plugin host application
455                    // established a CF event loop, that is if the application is
456                    // using Cocoa for its GUI. For other host applications the
457                    // callback will never be executed and thus gigedit would not
458                    // popup.
459                    
460                    // should be pretty much the same as the Objective-C solution below with macHelperRunCFuncOnMainThread()
461                    /*CFRunLoopSourceContext sourceContext = CFRunLoopSourceContext();
462                    sourceContext.info = this;
463                    sourceContext.perform = runInCFMainLoop;
464                    printf("CFRunLoopSourceCreate\n"); fflush(stdout);
465                    CFRunLoopSourceRef source = CFRunLoopSourceCreate(
466                        kCFAllocatorDefault, // allocator
467                        1, // priority
468                        &sourceContext
469                    );
470                    printf("CFRunLoopAddSource\n"); fflush(stdout);
471                    CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode);
472                    CFRelease(source);*/
473                    
474                    // use Apple's Objective-C API to call our callback function
475                    // 'runInCFMainLoop()' on the process's "main" thread
476                    macHelperRunCFuncOnMainThread(runInCFMainLoop, this);
477                }
478            }
479    #else
480      #ifdef OLD_THREADS
481          Glib::Thread::create(          Glib::Thread::create(
482              sigc::bind(sigc::ptr_fun(&GigEditState::main_loop_run),              sigc::bind(sigc::ptr_fun(&GigEditState::main_loop_run),
483                         &initialized),                         &initialized),
484              false);              false);
485      #else
486            Glib::Threads::Thread::create(
487                sigc::bind(sigc::ptr_fun(&GigEditState::main_loop_run),
488                           &initialized));
489      #endif
490    #endif
491            printf("Waiting for GUI being initialized (on main thread) ....\n"); fflush(stdout);
492          initialized.wait();          initialized.wait();
493            printf("GUI is now initialized. Everything done.\n"); fflush(stdout);
494          main_loop_started = true;          main_loop_started = true;
495      }      }
     instrument = pInstrument;  
496      current = this;      current = this;
497      dispatcher->emit();      dispatcher->emit();
498      open.wait(); // wait until the GUI thread has read current      open.wait(); // wait until the GUI thread has read current
499      mutex.unlock();      mutex.unlock();
500      close.wait(); // sleep until window is closed      close.wait(); // sleep until window is closed
501  }  }
502    
503    #if defined(WIN32)
504    extern "C" {
505        BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
506        {
507            switch (reason) {
508            case DLL_PROCESS_ATTACH:
509                gigedit_dll_handle = instance;
510                break;
511            }
512            return TRUE;
513        }
514    }
515    #endif

Legend:
Removed from v.1656  
changed lines
  Added in v.2903

  ViewVC Help
Powered by ViewVC