/[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 1660 by schoenebeck, Sun Feb 3 00:19:55 2008 UTC revision 3033 by persson, Sun Oct 30 17:02:51 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 <glibmm/miscutils.h>
37    #include <gtkmm/main.h>
38    
39    #ifdef WIN32
40    #include <gtkmm/icontheme.h>
41    #endif
42    
43    #if defined(__APPLE__)
44    # include <CoreFoundation/CoreFoundation.h>
45    # include "MacHelper.h"
46    #endif
47    
48  #include "mainwindow.h"  #include "mainwindow.h"
49    
50  #include "global.h"  #include "global.h"
51    
52    #ifdef __APPLE__
53    #include <dlfcn.h>
54    #include <glibmm/fileutils.h>
55    #endif
56    
57    //TODO: (hopefully) just a temporary nasty hack for launching gigedit on the main thread on Mac (see comments below in this file for details)
58    #if defined(__APPLE__) && HAVE_LINUXSAMPLER // the following global external variables are defined in LinuxSampler's global_private.cpp ...
59    extern bool g_mainThreadCallbackSupported;
60    extern void (*g_mainThreadCallback)(void* info);
61    extern void* g_mainThreadCallbackInfo;
62    extern bool g_fireMainThreadCallback;
63    #endif
64    
65  namespace {  namespace {
66    
67  // State for a gigedit thread.  // State for a gigedit thread.
# Line 37  namespace { Line 74  namespace {
74  //  //
75  class GigEditState : public sigc::trackable {  class GigEditState : public sigc::trackable {
76  public:  public:
77      GigEditState(GigEdit* parent) : parent(parent) { }      GigEditState(GigEdit* parent) :
78            window(0), parent(parent), instrument(0) { }
79      void run(gig::Instrument* pInstrument);      void run(gig::Instrument* pInstrument);
80    
81      MainWindow* window;      MainWindow* window;
# Line 48  private: Line 86  private:
86      class Cond {      class Cond {
87      private:      private:
88          bool pred;          bool pred;
89          Glib::Mutex mutex;          Glib::Threads::Mutex mutex;
90          Glib::Cond cond;          Glib::Threads::Cond cond;
91      public:      public:
92          Cond() : pred(false) { }          Cond() : pred(false) { }
93          void signal() {          void signal() {
94              Glib::Mutex::Lock lock(mutex);              Glib::Threads::Mutex::Lock lock(mutex);
95              pred = true;              pred = true;
96              cond.signal();              cond.signal();
97          }          }
98          void wait() {          void wait() {
99              Glib::Mutex::Lock lock(mutex);              Glib::Threads::Mutex::Lock lock(mutex);
100              while (!pred) cond.wait(mutex);              while (!pred) cond.wait(mutex);
101          }          }
102      };      };
103    
104    #ifdef OLD_THREADS
105      static Glib::StaticMutex mutex;      static Glib::StaticMutex mutex;
106    #else
107        static Glib::Threads::Mutex mutex;
108    #endif
109      static Glib::Dispatcher* dispatcher;      static Glib::Dispatcher* dispatcher;
110      static GigEditState* current;      static GigEditState* current;
111    
# Line 73  private: Line 115  private:
115      GigEdit* parent;      GigEdit* parent;
116      Cond open;      Cond open;
117      Cond close;      Cond close;
118        Cond initialized;
119      gig::Instrument* instrument;      gig::Instrument* instrument;
120    
121      void open_window();      void open_window();
122      void close_window();      void close_window();
123    #if defined(__APPLE__)
124        static void runInCFMainLoop(void* info);
125    #endif
126  };  };
127    
128    #ifdef WIN32
129    HINSTANCE gigedit_dll_handle = 0;
130    std::string gigedit_datadir;
131    bool gigedit_installdir_is_parent = false;
132    #endif
133    
134    #ifdef __APPLE__
135    std::string gigedit_localedir;
136    #endif
137    
138  void init_app() {  void init_app() {
139      static bool process_initialized = false;      static bool process_initialized = false;
140      if (!process_initialized) {      if (!process_initialized) {
# Line 86  void init_app() { Line 142  void init_app() {
142                    << std::flush;                    << std::flush;
143          setlocale(LC_ALL, "");          setlocale(LC_ALL, "");
144    
145  #if HAVE_GETTEXT  #ifdef __APPLE__
146            // Look for pango.modules, gdk-pixbuf.loaders and locale files
147            // under the same dir as the gigedit dylib is installed in.
148            Dl_info info;
149            if (dladdr((void*)&init_app, &info)) {
150                std::string libdir = Glib::path_get_dirname(info.dli_fname);
151    
152                if (Glib::getenv("PANGO_SYSCONFDIR") == "" &&
153                    Glib::file_test(Glib::build_filename(libdir,
154                                                         "pango/pango.modules"),
155                                    Glib::FILE_TEST_EXISTS)) {
156                    Glib::setenv("PANGO_SYSCONFDIR", libdir, true);
157                }
158                if (Glib::getenv("GDK_PIXBUF_MODULE_FILE") == "") {
159                    std::string module_file =
160                        Glib::build_filename(libdir,
161                                             "gtk-2.0/gdk-pixbuf.loaders");
162                    if (Glib::file_test(module_file, Glib::FILE_TEST_EXISTS)) {
163                        Glib::setenv("GDK_PIXBUF_MODULE_FILE", module_file, true);
164                    }
165                }
166      //FIXME: for some reason AC GETTEXT check fails on the Mac cross compiler?
167      //#if HAVE_GETTEXT
168                std::string localedir = Glib::build_filename(libdir, "locale");
169                if (Glib::file_test(localedir, Glib::FILE_TEST_EXISTS)) {
170                    gigedit_localedir = localedir;
171                    bindtextdomain(GETTEXT_PACKAGE, gigedit_localedir.c_str());
172                } else {
173                    bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
174                }
175      //#endif
176            }
177    
178            // The gtk file dialog stores its recent files state in
179            // ~/.local/share
180            g_mkdir_with_parents(
181                Glib::build_filename(Glib::get_home_dir(),
182                                     ".local/share").c_str(), 0777);
183    #endif // __APPLE__
184    
185    #ifdef WIN32
186            // Find the data directory: the linuxsampler installer puts
187            // the binaries in sub directories "32" and "64", so the share
188            // directory is located in the parent of the directory of the
189            // binaries.
190    
191      #if GLIB_CHECK_VERSION(2, 16, 0)
192            gchar* root =
193                g_win32_get_package_installation_directory_of_module(gigedit_dll_handle);
194      #else
195            gchar* root =
196                g_win32_get_package_installation_directory(NULL, NULL);
197      #endif
198            std::string installdir(root);
199            g_free(root);
200            std::string basename = Glib::path_get_basename(installdir);
201            if (basename == "32" || basename == "64") {
202                installdir = Glib::path_get_dirname(installdir);
203                gigedit_installdir_is_parent = true;
204            }
205            gigedit_datadir = Glib::build_filename(installdir, "share");
206    
207            // the file dialogs need glib-2.0/schemas/gschemas.compiled
208            if (gigedit_installdir_is_parent) {
209                Glib::setenv("GSETTINGS_SCHEMA_DIR",
210                             Glib::build_filename(gigedit_datadir,
211                                                  "glib-2.0/schemas"));
212            }
213    #endif
214    
215    //FIXME: for some reason AC GETTEXT check fails on the Mac cross compiler?
216    #if (HAVE_GETTEXT || defined(__APPLE__))
217      #ifdef WIN32
218            std::string temp = Glib::build_filename(gigedit_datadir, "locale");
219            gchar* localedir = g_win32_locale_filename_from_utf8(temp.c_str());
220            bindtextdomain(GETTEXT_PACKAGE, localedir);
221            g_free(localedir);
222      #elif !defined(__APPLE__)
223          bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);          bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
224      #endif
225          bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");          bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
226          textdomain(GETTEXT_PACKAGE);          textdomain(GETTEXT_PACKAGE);
227  #endif // HAVE_GETTEXT  #endif // HAVE_GETTEXT
228    
229    #ifdef OLD_THREADS
230          // make sure thread_init() is called once and ONLY once per process          // make sure thread_init() is called once and ONLY once per process
231          if (!Glib::thread_supported()) Glib::thread_init();          if (!Glib::thread_supported()) Glib::thread_init();
232    #endif
233          process_initialized = true;          process_initialized = true;
234      }      }
235  }  }
236    
237    void init_app_after_gtk_init() {
238    //FIXME: for some reason AC GETTEXT check fails on the Mac cross compiler?
239    #if (/*HAVE_GETTEXT &&*/ defined(__APPLE__))
240        // Gtk::Main binds the gtk locale to a possible non-existent
241        // directory. If we have bundled gtk locale files, we rebind here,
242        // after the Gtk::Main constructor.
243        if (!gigedit_localedir.empty()) {
244            bindtextdomain("gtk20", gigedit_localedir.c_str());
245        }
246    #endif
247    
248    #ifdef WIN32
249        if (gigedit_installdir_is_parent) {
250            std::string icon_dir = Glib::build_filename(gigedit_datadir, "icons");
251            Gtk::IconTheme::get_default()->append_search_path(icon_dir);
252        }
253    #endif
254    }
255    
256  void connect_signals(GigEdit* gigedit, MainWindow* mainwindow) {  void connect_signals(GigEdit* gigedit, MainWindow* mainwindow) {
257      // the signals of the "GigEdit" class are actually just proxies, that      // the signals of the "GigEdit" class are actually just proxies, that
258      // 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 127  void connect_signals(GigEdit* gigedit, M Line 281  void connect_signals(GigEdit* gigedit, M
281      mainwindow->signal_dimreg_changed().connect(      mainwindow->signal_dimreg_changed().connect(
282          gigedit->signal_dimreg_changed().make_slot()          gigedit->signal_dimreg_changed().make_slot()
283      );      );
284        mainwindow->signal_sample_changed().connect(
285            gigedit->signal_sample_changed().make_slot()
286        );
287      mainwindow->signal_sample_ref_changed().connect(      mainwindow->signal_sample_ref_changed().connect(
288          gigedit->signal_sample_ref_changed().make_slot()          gigedit->signal_sample_ref_changed().make_slot()
289      );      );
# Line 136  void connect_signals(GigEdit* gigedit, M Line 293  void connect_signals(GigEdit* gigedit, M
293      mainwindow->signal_keyboard_key_released().connect(      mainwindow->signal_keyboard_key_released().connect(
294          gigedit->signal_keyboard_key_released().make_slot()          gigedit->signal_keyboard_key_released().make_slot()
295      );      );
296        mainwindow->signal_switch_sampler_instrument().connect(
297            gigedit->signal_switch_sampler_instrument().make_slot()
298        );
299        mainwindow->signal_script_to_be_changed.connect(
300            gigedit->signal_script_to_be_changed.make_slot()
301        );
302        mainwindow->signal_script_changed.connect(
303            gigedit->signal_script_changed.make_slot()
304        );
305  }  }
306    
307  } // namespace  } // namespace
# Line 148  int GigEdit::run(int argc, char* argv[]) Line 314  int GigEdit::run(int argc, char* argv[])
314      init_app();      init_app();
315    
316      Gtk::Main kit(argc, argv);      Gtk::Main kit(argc, argv);
317        init_app_after_gtk_init();
318    
319      MainWindow window;      MainWindow window;
320      connect_signals(this, &window);      connect_signals(this, &window);
321      if (argc >= 2) window.load_file(argv[1]);      if (argc >= 2) window.load_file(argv[1]);
# Line 167  int GigEdit::run(gig::Instrument* pInstr Line 335  int GigEdit::run(gig::Instrument* pInstr
335    
336  void GigEdit::on_note_on_event(int key, int velocity) {  void GigEdit::on_note_on_event(int key, int velocity) {
337      if (!this->state) return;      if (!this->state) return;
338      GigEditState* state = (GigEditState*) this->state;      GigEditState* state = static_cast<GigEditState*>(this->state);
339      state->window->signal_note_on().emit(key, velocity);      state->window->signal_note_on().emit(key, velocity);
340  }  }
341    
342  void GigEdit::on_note_off_event(int key, int velocity) {  void GigEdit::on_note_off_event(int key, int velocity) {
343      if (!this->state) return;      if (!this->state) return;
344      GigEditState* state = (GigEditState*) this->state;      GigEditState* state = static_cast<GigEditState*>(this->state);
345      state->window->signal_note_off().emit(key, velocity);      state->window->signal_note_off().emit(key, velocity);
346  }  }
347    
# Line 209  sigc::signal<void, gig::DimensionRegion* Line 377  sigc::signal<void, gig::DimensionRegion*
377      return dimreg_changed_signal;      return dimreg_changed_signal;
378  }  }
379    
380    sigc::signal<void, gig::Sample*>& GigEdit::signal_sample_changed() {
381        return sample_changed_signal;
382    }
383    
384  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() {
385      return sample_ref_changed_signal;      return sample_ref_changed_signal;
386  }  }
# Line 221  sigc::signal<void, int/*key*/, int/*velo Line 393  sigc::signal<void, int/*key*/, int/*velo
393      return keyboard_key_released_signal;      return keyboard_key_released_signal;
394  }  }
395    
396    sigc::signal<void, gig::Instrument*>& GigEdit::signal_switch_sampler_instrument() {
397        return switch_sampler_instrument_signal;
398    }
399    
400    #ifdef OLD_THREADS
401  Glib::StaticMutex GigEditState::mutex = GLIBMM_STATIC_MUTEX_INIT;  Glib::StaticMutex GigEditState::mutex = GLIBMM_STATIC_MUTEX_INIT;
402    #else
403    Glib::Threads::Mutex GigEditState::mutex;
404    #endif
405  Glib::Dispatcher* GigEditState::dispatcher = 0;  Glib::Dispatcher* GigEditState::dispatcher = 0;
406  GigEditState* GigEditState::current = 0;  GigEditState* GigEditState::current = 0;
407    
# Line 247  void GigEditState::close_window() { Line 427  void GigEditState::close_window() {
427      close.signal();      close.signal();
428  }  }
429    
430    #if defined(WIN32) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
431    // make sure stack is 16-byte aligned for SSE instructions
432    __attribute__((force_align_arg_pointer))
433    #endif
434  void GigEditState::main_loop_run(Cond* initialized) {  void GigEditState::main_loop_run(Cond* initialized) {
435      int argc = 1;      int argc = 1;
436      const char* argv_c[] = { "gigedit" };      const char* argv_c[] = { "gigedit" };
437      char** argv = const_cast<char**>(argv_c);      char** argv = const_cast<char**>(argv_c);
438      Gtk::Main main_loop(argc, argv);      Gtk::Main main_loop(argc, argv);
439        init_app_after_gtk_init();
440    
441      dispatcher = new Glib::Dispatcher();      dispatcher = new Glib::Dispatcher();
442      dispatcher->connect(sigc::ptr_fun(&GigEditState::open_window_static));      dispatcher->connect(sigc::ptr_fun(&GigEditState::open_window_static));
# Line 260  void GigEditState::main_loop_run(Cond* i Line 445  void GigEditState::main_loop_run(Cond* i
445      main_loop.run();      main_loop.run();
446  }  }
447    
448    #if defined(__APPLE__)
449    
450    void GigEditState::runInCFMainLoop(void* info) {
451        printf("runInCFMainLoop() entered\n"); fflush(stdout);
452        GigEditState* state = static_cast<GigEditState*>(info);
453        state->main_loop_run(
454            &state->initialized
455        );
456        printf("runInCFMainLoop() left\n"); fflush(stdout);
457    }
458    
459    #endif // __APPLE__
460    
461  void GigEditState::run(gig::Instrument* pInstrument) {  void GigEditState::run(gig::Instrument* pInstrument) {
462      mutex.lock(); // lock access to static variables      mutex.lock(); // lock access to static variables
463    
464      static bool main_loop_started = false;      static bool main_loop_started = false;
465        instrument = pInstrument;
466      if (!main_loop_started) {      if (!main_loop_started) {
467          Cond initialized;  #if defined(__APPLE__) && HAVE_LINUXSAMPLER
468            // spawn GUI on main thread :
469            //     On OS X the Gtk GUI can only be launched on a process's "main"
470            //     thread. When trying to launch the Gtk GUI on any other thread,
471            //     there will only be a white box, because the GUI would not receive
472            //     any events, since it would listen to the wrong system event loop.
473            //     So far we haven't investigated whether there is any kind of
474            //     circumvention to allow doing that also on other OS X threads.
475            {
476                // In case the sampler was launched as standalone sampler (not as
477                // plugin), use the following global callback variable hack ...
478                if (g_mainThreadCallbackSupported) {
479                    printf("Setting callback ...\n"); fflush(stdout);
480                    g_mainThreadCallback = runInCFMainLoop;
481                    g_mainThreadCallbackInfo = this;
482                    g_fireMainThreadCallback = true;
483                    printf("Callback variables set.\n"); fflush(stdout);
484                } else { // Sampler was launched as (i.e. AU / VST) plugin ...
485                    // When the sampler was launched as plugin, we have no idea
486                    // whether any sampler thread is the process's "main" thread.
487                    // So that's why we are trying to use Apple's API for trying to
488                    // launch our callback function on the process's main thread.
489                    // However this will only work, if the plugin host application
490                    // established a CF event loop, that is if the application is
491                    // using Cocoa for its GUI. For other host applications the
492                    // callback will never be executed and thus gigedit would not
493                    // popup.
494                    
495                    // should be pretty much the same as the Objective-C solution below with macHelperRunCFuncOnMainThread()
496                    /*CFRunLoopSourceContext sourceContext = CFRunLoopSourceContext();
497                    sourceContext.info = this;
498                    sourceContext.perform = runInCFMainLoop;
499                    printf("CFRunLoopSourceCreate\n"); fflush(stdout);
500                    CFRunLoopSourceRef source = CFRunLoopSourceCreate(
501                        kCFAllocatorDefault, // allocator
502                        1, // priority
503                        &sourceContext
504                    );
505                    printf("CFRunLoopAddSource\n"); fflush(stdout);
506                    CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode);
507                    CFRelease(source);*/
508                    
509                    // use Apple's Objective-C API to call our callback function
510                    // 'runInCFMainLoop()' on the process's "main" thread
511                    macHelperRunCFuncOnMainThread(runInCFMainLoop, this);
512                }
513            }
514    #else
515      #ifdef OLD_THREADS
516          Glib::Thread::create(          Glib::Thread::create(
517              sigc::bind(sigc::ptr_fun(&GigEditState::main_loop_run),              sigc::bind(sigc::ptr_fun(&GigEditState::main_loop_run),
518                         &initialized),                         &initialized),
519              false);              false);
520      #else
521            Glib::Threads::Thread::create(
522                sigc::bind(sigc::ptr_fun(&GigEditState::main_loop_run),
523                           &initialized));
524      #endif
525    #endif
526            printf("Waiting for GUI being initialized (on main thread) ....\n"); fflush(stdout);
527          initialized.wait();          initialized.wait();
528            printf("GUI is now initialized. Everything done.\n"); fflush(stdout);
529          main_loop_started = true;          main_loop_started = true;
530      }      }
     instrument = pInstrument;  
531      current = this;      current = this;
532      dispatcher->emit();      dispatcher->emit();
533      open.wait(); // wait until the GUI thread has read current      open.wait(); // wait until the GUI thread has read current
534      mutex.unlock();      mutex.unlock();
535      close.wait(); // sleep until window is closed      close.wait(); // sleep until window is closed
536  }  }
537    
538    #if defined(WIN32)
539    extern "C" {
540        BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
541        {
542            switch (reason) {
543            case DLL_PROCESS_ATTACH:
544                gigedit_dll_handle = instance;
545                break;
546            }
547            return TRUE;
548        }
549    }
550    #endif

Legend:
Removed from v.1660  
changed lines
  Added in v.3033

  ViewVC Help
Powered by ViewVC