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

Annotation of /gigedit/trunk/src/gigedit/gigedit.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2689 - (hide annotations) (download)
Sun Jan 4 17:19:19 2015 UTC (9 years, 3 months ago) by schoenebeck
File size: 16737 byte(s)
* Automatically switch the sampler's (audible) instrument (in live-mode)
  whenever another instrument was selected in gigedit (this default
  behavior can be switched off in the settings menu of gigedit).

1 schoenebeck 1225 /*
2 schoenebeck 2689 * Copyright (C) 2007-2015 Andreas Persson
3 schoenebeck 1225 *
4     * This program is free software; you can redistribute it and/or
5     * modify it under the terms of the GNU General Public License as
6     * published by the Free Software Foundation; either version 2, or (at
7     * your option) any later version.
8     *
9     * This program is distributed in the hope that it will be useful, but
10     * WITHOUT ANY WARRANTY; without even the implied warranty of
11     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12     * General Public License for more details.
13     *
14     * You should have received a copy of the GNU General Public License
15     * along with program; see the file COPYING. If not, write to the Free
16     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17     * 02110-1301 USA.
18     */
19    
20     #include "gigedit.h"
21    
22 persson 2325 #include <glibmm/dispatcher.h>
23     #include <glibmm/main.h>
24 schoenebeck 1225 #include <gtkmm/main.h>
25 persson 2325
26 schoenebeck 2474 #if defined(__APPLE__)
27     # include <CoreFoundation/CoreFoundation.h>
28     # include "MacHelper.h"
29     #endif
30    
31 schoenebeck 1225 #include "mainwindow.h"
32    
33 schoenebeck 1396 #include "global.h"
34 schoenebeck 1225
35 persson 2470 #ifdef __APPLE__
36     #include <dlfcn.h>
37     #include <glibmm/fileutils.h>
38     #include <glibmm/miscutils.h>
39     #endif
40    
41 schoenebeck 2474 //TODO: (hopefully) just a temporary nasty hack for launching gigedit on the main thread on Mac (see comments below in this file for details)
42     #if defined(__APPLE__) // the following global external variables are defined in LinuxSampler's global_private.cpp ...
43     extern bool g_mainThreadCallbackSupported;
44     extern void (*g_mainThreadCallback)(void* info);
45     extern void* g_mainThreadCallbackInfo;
46     extern bool g_fireMainThreadCallback;
47     #endif
48    
49 persson 1456 namespace {
50 schoenebeck 1225
51 persson 1456 // State for a gigedit thread.
52     //
53     // This class is only used when gigedit is run as a plugin and makes
54     // sure that there's only one Gtk::Main event loop. The event loop is
55     // started in a separate thread. The plugin thread just dispatches an
56     // event to the main loop to open a window and then goes to sleep
57     // until the window is closed.
58     //
59     class GigEditState : public sigc::trackable {
60     public:
61 schoenebeck 2664 GigEditState(GigEdit* parent) : parent(parent), instrument(NULL) { }
62 persson 1456 void run(gig::Instrument* pInstrument);
63    
64 schoenebeck 1654 MainWindow* window;
65    
66 persson 1456 private:
67    
68     // simple condition variable abstraction
69     class Cond {
70     private:
71     bool pred;
72 persson 2325 Glib::Threads::Mutex mutex;
73     Glib::Threads::Cond cond;
74 persson 1456 public:
75     Cond() : pred(false) { }
76     void signal() {
77 persson 2325 Glib::Threads::Mutex::Lock lock(mutex);
78 persson 1456 pred = true;
79     cond.signal();
80     }
81     void wait() {
82 persson 2325 Glib::Threads::Mutex::Lock lock(mutex);
83 persson 1456 while (!pred) cond.wait(mutex);
84     }
85     };
86    
87 persson 2325 #ifdef OLD_THREADS
88 persson 1456 static Glib::StaticMutex mutex;
89 persson 2325 #else
90     static Glib::Threads::Mutex mutex;
91     #endif
92 persson 1456 static Glib::Dispatcher* dispatcher;
93     static GigEditState* current;
94    
95     static void main_loop_run(Cond* intialized);
96     static void open_window_static();
97    
98     GigEdit* parent;
99     Cond open;
100     Cond close;
101 schoenebeck 2474 Cond initialized;
102 persson 1456 gig::Instrument* instrument;
103    
104     void open_window();
105     void close_window();
106 schoenebeck 2474 #if defined(__APPLE__)
107     static void runInCFMainLoop(void* info);
108     #endif
109 persson 1456 };
110    
111 persson 1898 #ifdef WIN32
112     HINSTANCE gigedit_dll_handle = 0;
113     #endif
114    
115 persson 2470 #ifdef __APPLE__
116     std::string gigedit_localedir;
117     #endif
118    
119 persson 1456 void init_app() {
120 schoenebeck 1333 static bool process_initialized = false;
121     if (!process_initialized) {
122     std::cout << "Initializing 3rd party services needed by gigedit.\n"
123     << std::flush;
124     setlocale(LC_ALL, "");
125 schoenebeck 1396
126 persson 2470 #ifdef __APPLE__
127     // Look for pango.modules, gdk-pixbuf.loaders and locale files
128     // under the same dir as the gigedit dylib is installed in.
129     Dl_info info;
130     if (dladdr((void*)&init_app, &info)) {
131     std::string libdir = Glib::path_get_dirname(info.dli_fname);
132    
133     if (Glib::getenv("PANGO_SYSCONFDIR") == "" &&
134     Glib::file_test(Glib::build_filename(libdir,
135     "pango/pango.modules"),
136     Glib::FILE_TEST_EXISTS)) {
137     Glib::setenv("PANGO_SYSCONFDIR", libdir, true);
138     }
139     if (Glib::getenv("GDK_PIXBUF_MODULE_FILE") == "") {
140     std::string module_file =
141     Glib::build_filename(libdir,
142     "gtk-2.0/gdk-pixbuf.loaders");
143     if (Glib::file_test(module_file, Glib::FILE_TEST_EXISTS)) {
144     Glib::setenv("GDK_PIXBUF_MODULE_FILE", module_file, true);
145     }
146     }
147 schoenebeck 2474 //FIXME: for some reason AC GETTEXT check fails on the Mac cross compiler?
148     //#if HAVE_GETTEXT
149 persson 2470 std::string localedir = Glib::build_filename(libdir, "locale");
150     if (Glib::file_test(localedir, Glib::FILE_TEST_EXISTS)) {
151     gigedit_localedir = localedir;
152     bindtextdomain(GETTEXT_PACKAGE, gigedit_localedir.c_str());
153     } else {
154     bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
155     }
156 schoenebeck 2474 //#endif
157 persson 2470 }
158 persson 1823
159 persson 2470 // The gtk file dialog stores its recent files state in
160     // ~/.local/share
161     g_mkdir_with_parents(
162     Glib::build_filename(Glib::get_home_dir(),
163     ".local/share").c_str(), 0777);
164 schoenebeck 2474 #endif // __APPLE__
165 persson 2470
166 schoenebeck 2474 //FIXME: for some reason AC GETTEXT check fails on the Mac cross compiler?
167     #if (HAVE_GETTEXT || defined(__APPLE__))
168 persson 2470
169 schoenebeck 2474 #ifdef WIN32
170     #if GLIB_CHECK_VERSION(2, 16, 0)
171 persson 1823 gchar* root =
172 persson 1898 g_win32_get_package_installation_directory_of_module(gigedit_dll_handle);
173 schoenebeck 2474 #else
174 persson 1823 gchar* root =
175     g_win32_get_package_installation_directory(NULL, NULL);
176 schoenebeck 2474 #endif
177 persson 1823 gchar* temp = g_build_filename(root, "/share/locale", NULL);
178     g_free(root);
179     gchar* localedir = g_win32_locale_filename_from_utf8(temp);
180     g_free(temp);
181     bindtextdomain(GETTEXT_PACKAGE, localedir);
182     g_free(localedir);
183 schoenebeck 2474 #elif !defined(__APPLE__)
184 schoenebeck 1333 bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
185 schoenebeck 2474 #endif
186 schoenebeck 1333 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
187     textdomain(GETTEXT_PACKAGE);
188 schoenebeck 1396 #endif // HAVE_GETTEXT
189    
190 persson 2325 #ifdef OLD_THREADS
191 schoenebeck 1333 // make sure thread_init() is called once and ONLY once per process
192     if (!Glib::thread_supported()) Glib::thread_init();
193 persson 2325 #endif
194 schoenebeck 1333 process_initialized = true;
195     }
196 schoenebeck 1225 }
197    
198 persson 1456 void connect_signals(GigEdit* gigedit, MainWindow* mainwindow) {
199 schoenebeck 1322 // the signals of the "GigEdit" class are actually just proxies, that
200     // is they simply forward the signals of the internal classes to the
201     // outer world
202     mainwindow->signal_file_structure_to_be_changed().connect(
203     gigedit->signal_file_structure_to_be_changed().make_slot()
204     );
205     mainwindow->signal_file_structure_changed().connect(
206     gigedit->signal_file_structure_changed().make_slot()
207     );
208     mainwindow->signal_samples_to_be_removed().connect(
209     gigedit->signal_samples_to_be_removed().make_slot()
210     );
211     mainwindow->signal_samples_removed().connect(
212     gigedit->signal_samples_removed().make_slot()
213     );
214     mainwindow->signal_region_to_be_changed().connect(
215     gigedit->signal_region_to_be_changed().make_slot()
216     );
217     mainwindow->signal_region_changed().connect(
218     gigedit->signal_region_changed().make_slot()
219     );
220     mainwindow->signal_dimreg_to_be_changed().connect(
221     gigedit->signal_dimreg_to_be_changed().make_slot()
222     );
223     mainwindow->signal_dimreg_changed().connect(
224     gigedit->signal_dimreg_changed().make_slot()
225     );
226 schoenebeck 1853 mainwindow->signal_sample_changed().connect(
227     gigedit->signal_sample_changed().make_slot()
228     );
229 schoenebeck 1322 mainwindow->signal_sample_ref_changed().connect(
230     gigedit->signal_sample_ref_changed().make_slot()
231     );
232 schoenebeck 1660 mainwindow->signal_keyboard_key_hit().connect(
233     gigedit->signal_keyboard_key_hit().make_slot()
234     );
235     mainwindow->signal_keyboard_key_released().connect(
236     gigedit->signal_keyboard_key_released().make_slot()
237     );
238 schoenebeck 2689 mainwindow->signal_switch_sampler_instrument().connect(
239     gigedit->signal_switch_sampler_instrument().make_slot()
240     );
241 schoenebeck 1322 }
242    
243 schoenebeck 1654 } // namespace
244    
245     GigEdit::GigEdit() {
246     state = NULL;
247 schoenebeck 1225 }
248    
249 persson 1456 int GigEdit::run(int argc, char* argv[]) {
250     init_app();
251    
252     Gtk::Main kit(argc, argv);
253 persson 2470
254 schoenebeck 2474 //FIXME: for some reason AC GETTEXT check fails on the Mac cross compiler?
255     #if (/*HAVE_GETTEXT &&*/ defined(__APPLE__))
256 persson 2470 // Gtk::Main binds the gtk locale to a possible non-existent
257     // directory. If we have bundled gtk locale files, we rebind here,
258     // after the Gtk::Main constructor.
259     if (!gigedit_localedir.empty()) {
260     bindtextdomain("gtk20", gigedit_localedir.c_str());
261     }
262     #endif
263    
264 schoenebeck 1225 MainWindow window;
265 persson 1456 connect_signals(this, &window);
266     if (argc >= 2) window.load_file(argv[1]);
267 schoenebeck 1225 kit.run(window);
268     return 0;
269     }
270    
271     int GigEdit::run(gig::Instrument* pInstrument) {
272 persson 1456 init_app();
273    
274     GigEditState state(this);
275 schoenebeck 1654 this->state = &state;
276 persson 1456 state.run(pInstrument);
277 schoenebeck 1654 this->state = NULL;
278 schoenebeck 1225 return 0;
279     }
280 schoenebeck 1322
281 schoenebeck 1654 void GigEdit::on_note_on_event(int key, int velocity) {
282     if (!this->state) return;
283     GigEditState* state = (GigEditState*) this->state;
284     state->window->signal_note_on().emit(key, velocity);
285     }
286    
287     void GigEdit::on_note_off_event(int key, int velocity) {
288     if (!this->state) return;
289     GigEditState* state = (GigEditState*) this->state;
290     state->window->signal_note_off().emit(key, velocity);
291     }
292    
293 schoenebeck 1339 sigc::signal<void, gig::File*>& GigEdit::signal_file_structure_to_be_changed() {
294 schoenebeck 1322 return file_structure_to_be_changed_signal;
295     }
296    
297 schoenebeck 1339 sigc::signal<void, gig::File*>& GigEdit::signal_file_structure_changed() {
298 schoenebeck 1322 return file_structure_changed_signal;
299     }
300    
301 schoenebeck 1339 sigc::signal<void, std::list<gig::Sample*> >& GigEdit::signal_samples_to_be_removed() {
302 schoenebeck 1322 return samples_to_be_removed_signal;
303     }
304    
305 schoenebeck 1339 sigc::signal<void>& GigEdit::signal_samples_removed() {
306 schoenebeck 1322 return samples_removed_signal;
307     }
308    
309 schoenebeck 1339 sigc::signal<void, gig::Region*>& GigEdit::signal_region_to_be_changed() {
310 schoenebeck 1322 return region_to_be_changed_signal;
311     }
312    
313 schoenebeck 1339 sigc::signal<void, gig::Region*>& GigEdit::signal_region_changed() {
314 schoenebeck 1322 return region_changed_signal;
315     }
316    
317 schoenebeck 1339 sigc::signal<void, gig::DimensionRegion*>& GigEdit::signal_dimreg_to_be_changed() {
318 schoenebeck 1322 return dimreg_to_be_changed_signal;
319     }
320    
321 schoenebeck 1339 sigc::signal<void, gig::DimensionRegion*>& GigEdit::signal_dimreg_changed() {
322 schoenebeck 1322 return dimreg_changed_signal;
323     }
324    
325 schoenebeck 1853 sigc::signal<void, gig::Sample*>& GigEdit::signal_sample_changed() {
326     return sample_changed_signal;
327     }
328    
329 schoenebeck 1339 sigc::signal<void, gig::Sample*/*old*/, gig::Sample*/*new*/>& GigEdit::signal_sample_ref_changed() {
330 schoenebeck 1322 return sample_ref_changed_signal;
331     }
332 persson 1456
333 schoenebeck 1660 sigc::signal<void, int/*key*/, int/*velocity*/>& GigEdit::signal_keyboard_key_hit() {
334     return keyboard_key_hit_signal;
335     }
336 persson 1456
337 schoenebeck 1660 sigc::signal<void, int/*key*/, int/*velocity*/>& GigEdit::signal_keyboard_key_released() {
338     return keyboard_key_released_signal;
339     }
340    
341 schoenebeck 2689 sigc::signal<void, gig::Instrument*>& GigEdit::signal_switch_sampler_instrument() {
342     return switch_sampler_instrument_signal;
343     }
344    
345 persson 2325 #ifdef OLD_THREADS
346 persson 1456 Glib::StaticMutex GigEditState::mutex = GLIBMM_STATIC_MUTEX_INIT;
347 persson 2325 #else
348     Glib::Threads::Mutex GigEditState::mutex;
349     #endif
350 persson 1456 Glib::Dispatcher* GigEditState::dispatcher = 0;
351     GigEditState* GigEditState::current = 0;
352    
353     void GigEditState::open_window_static() {
354     GigEditState* c = GigEditState::current;
355     c->open.signal();
356     c->open_window();
357     }
358    
359     void GigEditState::open_window() {
360     window = new MainWindow();
361    
362     connect_signals(parent, window);
363     if (instrument) window->load_instrument(instrument);
364    
365     window->signal_hide().connect(sigc::mem_fun(this,
366     &GigEditState::close_window));
367     window->present();
368     }
369    
370     void GigEditState::close_window() {
371     delete window;
372     close.signal();
373     }
374    
375     void GigEditState::main_loop_run(Cond* initialized) {
376     int argc = 1;
377     const char* argv_c[] = { "gigedit" };
378     char** argv = const_cast<char**>(argv_c);
379     Gtk::Main main_loop(argc, argv);
380 schoenebeck 2474 //FIXME: for some reason AC GETTEXT check fails on the Mac cross compiler?
381     #if (/*HAVE_GETTEXT &&*/ defined(__APPLE__))
382 persson 2470 if (!gigedit_localedir.empty()) {
383     bindtextdomain("gtk20", gigedit_localedir.c_str());
384     }
385     #endif
386 persson 1456
387     dispatcher = new Glib::Dispatcher();
388     dispatcher->connect(sigc::ptr_fun(&GigEditState::open_window_static));
389     initialized->signal();
390    
391     main_loop.run();
392     }
393    
394 schoenebeck 2474 #if defined(__APPLE__)
395    
396     void GigEditState::runInCFMainLoop(void* info) {
397     printf("runInCFMainLoop() entered\n"); fflush(stdout);
398     GigEditState* state = static_cast<GigEditState*>(info);
399     state->main_loop_run(
400     &state->initialized
401     );
402     printf("runInCFMainLoop() left\n"); fflush(stdout);
403     }
404    
405     #endif // __APPLE__
406    
407 persson 1456 void GigEditState::run(gig::Instrument* pInstrument) {
408     mutex.lock(); // lock access to static variables
409    
410     static bool main_loop_started = false;
411 schoenebeck 2664 instrument = pInstrument;
412 persson 1456 if (!main_loop_started) {
413 schoenebeck 2474 #if defined(__APPLE__)
414     // spawn GUI on main thread :
415     // On OS X the Gtk GUI can only be launched on a process's "main"
416     // thread. When trying to launch the Gtk GUI on any other thread,
417     // there will only be a white box, because the GUI would not receive
418     // any events, since it would listen to the wrong system event loop.
419     // So far we haven't investigated whether there is any kind of
420     // circumvention to allow doing that also on other OS X threads.
421     {
422     // In case the sampler was launched as standalone sampler (not as
423     // plugin), use the following global callback variable hack ...
424     if (g_mainThreadCallbackSupported) {
425     printf("Setting callback ...\n"); fflush(stdout);
426     g_mainThreadCallback = runInCFMainLoop;
427     g_mainThreadCallbackInfo = this;
428     g_fireMainThreadCallback = true;
429     printf("Callback variables set.\n"); fflush(stdout);
430     } else { // Sampler was launched as (i.e. AU / VST) plugin ...
431     // When the sampler was launched as plugin, we have no idea
432     // whether any sampler thread is the process's "main" thread.
433     // So that's why we are trying to use Apple's API for trying to
434     // launch our callback function on the process's main thread.
435     // However this will only work, if the plugin host application
436     // established a CF event loop, that is if the application is
437     // using Cocoa for its GUI. For other host applications the
438     // callback will never be executed and thus gigedit would not
439     // popup.
440    
441     // should be pretty much the same as the Objective-C solution below with macHelperRunCFuncOnMainThread()
442     /*CFRunLoopSourceContext sourceContext = CFRunLoopSourceContext();
443     sourceContext.info = this;
444     sourceContext.perform = runInCFMainLoop;
445     printf("CFRunLoopSourceCreate\n"); fflush(stdout);
446     CFRunLoopSourceRef source = CFRunLoopSourceCreate(
447     kCFAllocatorDefault, // allocator
448     1, // priority
449     &sourceContext
450     );
451     printf("CFRunLoopAddSource\n"); fflush(stdout);
452     CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode);
453     CFRelease(source);*/
454    
455     // use Apple's Objective-C API to call our callback function
456     // 'runInCFMainLoop()' on the process's "main" thread
457     macHelperRunCFuncOnMainThread(runInCFMainLoop, this);
458     }
459     }
460     #else
461     #ifdef OLD_THREADS
462 persson 2332 Glib::Thread::create(
463 persson 1456 sigc::bind(sigc::ptr_fun(&GigEditState::main_loop_run),
464     &initialized),
465     false);
466 schoenebeck 2474 #else
467 persson 2332 Glib::Threads::Thread::create(
468     sigc::bind(sigc::ptr_fun(&GigEditState::main_loop_run),
469     &initialized));
470 schoenebeck 2474 #endif
471 persson 2332 #endif
472 schoenebeck 2474 printf("Waiting for GUI being initialized (on main thread) ....\n"); fflush(stdout);
473 persson 1456 initialized.wait();
474 schoenebeck 2474 printf("GUI is now initialized. Everything done.\n"); fflush(stdout);
475 persson 1456 main_loop_started = true;
476     }
477     current = this;
478     dispatcher->emit();
479     open.wait(); // wait until the GUI thread has read current
480     mutex.unlock();
481     close.wait(); // sleep until window is closed
482     }
483 persson 1898
484     #if defined(WIN32)
485     extern "C" {
486     BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
487     {
488     switch (reason) {
489     case DLL_PROCESS_ATTACH:
490     gigedit_dll_handle = instance;
491     break;
492     }
493     return TRUE;
494     }
495     }
496     #endif

  ViewVC Help
Powered by ViewVC