/[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 3021 - (hide annotations) (download)
Sun Oct 23 07:24:40 2016 UTC (7 years, 6 months ago) by persson
File size: 17645 byte(s)
* windows, 32-bit: fixed crashes by making sure the stack in sub
  threads is 16-byte aligned

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 persson 2841 #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 schoenebeck 1225 #include "gigedit.h"
29    
30 persson 2844 #include <gtkmmconfig.h>
31     #if GTKMM_MAJOR_VERSION < 3
32     #include <gdkmm/region.h>
33     #endif
34 persson 2325 #include <glibmm/dispatcher.h>
35     #include <glibmm/main.h>
36 schoenebeck 1225 #include <gtkmm/main.h>
37 persson 2325
38 schoenebeck 2474 #if defined(__APPLE__)
39     # include <CoreFoundation/CoreFoundation.h>
40     # include "MacHelper.h"
41     #endif
42    
43 schoenebeck 1225 #include "mainwindow.h"
44    
45 schoenebeck 1396 #include "global.h"
46 schoenebeck 1225
47 persson 2470 #ifdef __APPLE__
48     #include <dlfcn.h>
49     #include <glibmm/fileutils.h>
50     #include <glibmm/miscutils.h>
51     #endif
52    
53 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)
54 persson 2841 #if defined(__APPLE__) && HAVE_LINUXSAMPLER // the following global external variables are defined in LinuxSampler's global_private.cpp ...
55 schoenebeck 2474 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 persson 1456 namespace {
62 schoenebeck 1225
63 persson 1456 // State for a gigedit thread.
64     //
65     // This class is only used when gigedit is run as a plugin and makes
66     // sure that there's only one Gtk::Main event loop. The event loop is
67     // started in a separate thread. The plugin thread just dispatches an
68     // event to the main loop to open a window and then goes to sleep
69     // until the window is closed.
70     //
71     class GigEditState : public sigc::trackable {
72     public:
73 persson 2841 GigEditState(GigEdit* parent) :
74     window(0), parent(parent), instrument(0) { }
75 persson 1456 void run(gig::Instrument* pInstrument);
76    
77 schoenebeck 1654 MainWindow* window;
78    
79 persson 1456 private:
80    
81     // simple condition variable abstraction
82     class Cond {
83     private:
84     bool pred;
85 persson 2325 Glib::Threads::Mutex mutex;
86     Glib::Threads::Cond cond;
87 persson 1456 public:
88     Cond() : pred(false) { }
89     void signal() {
90 persson 2325 Glib::Threads::Mutex::Lock lock(mutex);
91 persson 1456 pred = true;
92     cond.signal();
93     }
94     void wait() {
95 persson 2325 Glib::Threads::Mutex::Lock lock(mutex);
96 persson 1456 while (!pred) cond.wait(mutex);
97     }
98     };
99    
100 persson 2325 #ifdef OLD_THREADS
101 persson 1456 static Glib::StaticMutex mutex;
102 persson 2325 #else
103     static Glib::Threads::Mutex mutex;
104     #endif
105 persson 1456 static Glib::Dispatcher* dispatcher;
106     static GigEditState* current;
107    
108     static void main_loop_run(Cond* intialized);
109     static void open_window_static();
110    
111     GigEdit* parent;
112     Cond open;
113     Cond close;
114 schoenebeck 2474 Cond initialized;
115 persson 1456 gig::Instrument* instrument;
116    
117     void open_window();
118     void close_window();
119 schoenebeck 2474 #if defined(__APPLE__)
120     static void runInCFMainLoop(void* info);
121     #endif
122 persson 1456 };
123    
124 persson 1898 #ifdef WIN32
125     HINSTANCE gigedit_dll_handle = 0;
126     #endif
127    
128 persson 2470 #ifdef __APPLE__
129     std::string gigedit_localedir;
130     #endif
131    
132 persson 1456 void init_app() {
133 schoenebeck 1333 static bool process_initialized = false;
134     if (!process_initialized) {
135     std::cout << "Initializing 3rd party services needed by gigedit.\n"
136     << std::flush;
137     setlocale(LC_ALL, "");
138 schoenebeck 1396
139 persson 2470 #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 schoenebeck 2474 //FIXME: for some reason AC GETTEXT check fails on the Mac cross compiler?
161     //#if HAVE_GETTEXT
162 persson 2470 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 schoenebeck 2474 //#endif
170 persson 2470 }
171 persson 1823
172 persson 2470 // 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 schoenebeck 2474 #endif // __APPLE__
178 persson 2470
179 schoenebeck 2474 //FIXME: for some reason AC GETTEXT check fails on the Mac cross compiler?
180     #if (HAVE_GETTEXT || defined(__APPLE__))
181 persson 2470
182 schoenebeck 2474 #ifdef WIN32
183     #if GLIB_CHECK_VERSION(2, 16, 0)
184 persson 1823 gchar* root =
185 persson 1898 g_win32_get_package_installation_directory_of_module(gigedit_dll_handle);
186 schoenebeck 2474 #else
187 persson 1823 gchar* root =
188     g_win32_get_package_installation_directory(NULL, NULL);
189 schoenebeck 2474 #endif
190 persson 1823 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 schoenebeck 2474 #elif !defined(__APPLE__)
197 schoenebeck 1333 bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
198 schoenebeck 2474 #endif
199 schoenebeck 1333 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
200     textdomain(GETTEXT_PACKAGE);
201 schoenebeck 1396 #endif // HAVE_GETTEXT
202    
203 persson 2325 #ifdef OLD_THREADS
204 schoenebeck 1333 // make sure thread_init() is called once and ONLY once per process
205     if (!Glib::thread_supported()) Glib::thread_init();
206 persson 2325 #endif
207 schoenebeck 1333 process_initialized = true;
208     }
209 schoenebeck 1225 }
210    
211 persson 1456 void connect_signals(GigEdit* gigedit, MainWindow* mainwindow) {
212 schoenebeck 1322 // the signals of the "GigEdit" class are actually just proxies, that
213     // is they simply forward the signals of the internal classes to the
214     // outer world
215     mainwindow->signal_file_structure_to_be_changed().connect(
216     gigedit->signal_file_structure_to_be_changed().make_slot()
217     );
218     mainwindow->signal_file_structure_changed().connect(
219     gigedit->signal_file_structure_changed().make_slot()
220     );
221     mainwindow->signal_samples_to_be_removed().connect(
222     gigedit->signal_samples_to_be_removed().make_slot()
223     );
224     mainwindow->signal_samples_removed().connect(
225     gigedit->signal_samples_removed().make_slot()
226     );
227     mainwindow->signal_region_to_be_changed().connect(
228     gigedit->signal_region_to_be_changed().make_slot()
229     );
230     mainwindow->signal_region_changed().connect(
231     gigedit->signal_region_changed().make_slot()
232     );
233     mainwindow->signal_dimreg_to_be_changed().connect(
234     gigedit->signal_dimreg_to_be_changed().make_slot()
235     );
236     mainwindow->signal_dimreg_changed().connect(
237     gigedit->signal_dimreg_changed().make_slot()
238     );
239 schoenebeck 1853 mainwindow->signal_sample_changed().connect(
240     gigedit->signal_sample_changed().make_slot()
241     );
242 schoenebeck 1322 mainwindow->signal_sample_ref_changed().connect(
243     gigedit->signal_sample_ref_changed().make_slot()
244     );
245 schoenebeck 1660 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 schoenebeck 2689 mainwindow->signal_switch_sampler_instrument().connect(
252     gigedit->signal_switch_sampler_instrument().make_slot()
253     );
254 schoenebeck 2903 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 schoenebeck 1322 }
261    
262 schoenebeck 1654 } // namespace
263    
264     GigEdit::GigEdit() {
265     state = NULL;
266 schoenebeck 1225 }
267    
268 persson 1456 int GigEdit::run(int argc, char* argv[]) {
269     init_app();
270    
271     Gtk::Main kit(argc, argv);
272 persson 2470
273 schoenebeck 2474 //FIXME: for some reason AC GETTEXT check fails on the Mac cross compiler?
274     #if (/*HAVE_GETTEXT &&*/ defined(__APPLE__))
275 persson 2470 // 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 schoenebeck 1225 MainWindow window;
284 persson 1456 connect_signals(this, &window);
285     if (argc >= 2) window.load_file(argv[1]);
286 schoenebeck 1225 kit.run(window);
287     return 0;
288     }
289    
290     int GigEdit::run(gig::Instrument* pInstrument) {
291 persson 1456 init_app();
292    
293     GigEditState state(this);
294 schoenebeck 1654 this->state = &state;
295 persson 1456 state.run(pInstrument);
296 schoenebeck 1654 this->state = NULL;
297 schoenebeck 1225 return 0;
298     }
299 schoenebeck 1322
300 schoenebeck 1654 void GigEdit::on_note_on_event(int key, int velocity) {
301     if (!this->state) return;
302 persson 2841 GigEditState* state = static_cast<GigEditState*>(this->state);
303 schoenebeck 1654 state->window->signal_note_on().emit(key, velocity);
304     }
305    
306     void GigEdit::on_note_off_event(int key, int velocity) {
307     if (!this->state) return;
308 persson 2841 GigEditState* state = static_cast<GigEditState*>(this->state);
309 schoenebeck 1654 state->window->signal_note_off().emit(key, velocity);
310     }
311    
312 schoenebeck 1339 sigc::signal<void, gig::File*>& GigEdit::signal_file_structure_to_be_changed() {
313 schoenebeck 1322 return file_structure_to_be_changed_signal;
314     }
315    
316 schoenebeck 1339 sigc::signal<void, gig::File*>& GigEdit::signal_file_structure_changed() {
317 schoenebeck 1322 return file_structure_changed_signal;
318     }
319    
320 schoenebeck 1339 sigc::signal<void, std::list<gig::Sample*> >& GigEdit::signal_samples_to_be_removed() {
321 schoenebeck 1322 return samples_to_be_removed_signal;
322     }
323    
324 schoenebeck 1339 sigc::signal<void>& GigEdit::signal_samples_removed() {
325 schoenebeck 1322 return samples_removed_signal;
326     }
327    
328 schoenebeck 1339 sigc::signal<void, gig::Region*>& GigEdit::signal_region_to_be_changed() {
329 schoenebeck 1322 return region_to_be_changed_signal;
330     }
331    
332 schoenebeck 1339 sigc::signal<void, gig::Region*>& GigEdit::signal_region_changed() {
333 schoenebeck 1322 return region_changed_signal;
334     }
335    
336 schoenebeck 1339 sigc::signal<void, gig::DimensionRegion*>& GigEdit::signal_dimreg_to_be_changed() {
337 schoenebeck 1322 return dimreg_to_be_changed_signal;
338     }
339    
340 schoenebeck 1339 sigc::signal<void, gig::DimensionRegion*>& GigEdit::signal_dimreg_changed() {
341 schoenebeck 1322 return dimreg_changed_signal;
342     }
343    
344 schoenebeck 1853 sigc::signal<void, gig::Sample*>& GigEdit::signal_sample_changed() {
345     return sample_changed_signal;
346     }
347    
348 schoenebeck 1339 sigc::signal<void, gig::Sample*/*old*/, gig::Sample*/*new*/>& GigEdit::signal_sample_ref_changed() {
349 schoenebeck 1322 return sample_ref_changed_signal;
350     }
351 persson 1456
352 schoenebeck 1660 sigc::signal<void, int/*key*/, int/*velocity*/>& GigEdit::signal_keyboard_key_hit() {
353     return keyboard_key_hit_signal;
354     }
355 persson 1456
356 schoenebeck 1660 sigc::signal<void, int/*key*/, int/*velocity*/>& GigEdit::signal_keyboard_key_released() {
357     return keyboard_key_released_signal;
358     }
359    
360 schoenebeck 2689 sigc::signal<void, gig::Instrument*>& GigEdit::signal_switch_sampler_instrument() {
361     return switch_sampler_instrument_signal;
362     }
363    
364 persson 2325 #ifdef OLD_THREADS
365 persson 1456 Glib::StaticMutex GigEditState::mutex = GLIBMM_STATIC_MUTEX_INIT;
366 persson 2325 #else
367     Glib::Threads::Mutex GigEditState::mutex;
368     #endif
369 persson 1456 Glib::Dispatcher* GigEditState::dispatcher = 0;
370     GigEditState* GigEditState::current = 0;
371    
372     void GigEditState::open_window_static() {
373     GigEditState* c = GigEditState::current;
374     c->open.signal();
375     c->open_window();
376     }
377    
378     void GigEditState::open_window() {
379     window = new MainWindow();
380    
381     connect_signals(parent, window);
382     if (instrument) window->load_instrument(instrument);
383    
384     window->signal_hide().connect(sigc::mem_fun(this,
385     &GigEditState::close_window));
386     window->present();
387     }
388    
389     void GigEditState::close_window() {
390     delete window;
391     close.signal();
392     }
393    
394 persson 3021 #if defined(WIN32) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
395     // make sure stack is 16-byte aligned for SSE instructions
396     __attribute__((force_align_arg_pointer))
397     #endif
398 persson 1456 void GigEditState::main_loop_run(Cond* initialized) {
399     int argc = 1;
400     const char* argv_c[] = { "gigedit" };
401     char** argv = const_cast<char**>(argv_c);
402     Gtk::Main main_loop(argc, argv);
403 schoenebeck 2474 //FIXME: for some reason AC GETTEXT check fails on the Mac cross compiler?
404     #if (/*HAVE_GETTEXT &&*/ defined(__APPLE__))
405 persson 2470 if (!gigedit_localedir.empty()) {
406     bindtextdomain("gtk20", gigedit_localedir.c_str());
407     }
408     #endif
409 persson 1456
410     dispatcher = new Glib::Dispatcher();
411     dispatcher->connect(sigc::ptr_fun(&GigEditState::open_window_static));
412     initialized->signal();
413    
414     main_loop.run();
415     }
416    
417 schoenebeck 2474 #if defined(__APPLE__)
418    
419     void GigEditState::runInCFMainLoop(void* info) {
420     printf("runInCFMainLoop() entered\n"); fflush(stdout);
421     GigEditState* state = static_cast<GigEditState*>(info);
422     state->main_loop_run(
423     &state->initialized
424     );
425     printf("runInCFMainLoop() left\n"); fflush(stdout);
426     }
427    
428     #endif // __APPLE__
429    
430 persson 1456 void GigEditState::run(gig::Instrument* pInstrument) {
431     mutex.lock(); // lock access to static variables
432    
433     static bool main_loop_started = false;
434 schoenebeck 2664 instrument = pInstrument;
435 persson 1456 if (!main_loop_started) {
436 persson 2841 #if defined(__APPLE__) && HAVE_LINUXSAMPLER
437 schoenebeck 2474 // spawn GUI on main thread :
438     // On OS X the Gtk GUI can only be launched on a process's "main"
439     // thread. When trying to launch the Gtk GUI on any other thread,
440     // there will only be a white box, because the GUI would not receive
441     // any events, since it would listen to the wrong system event loop.
442     // So far we haven't investigated whether there is any kind of
443     // circumvention to allow doing that also on other OS X threads.
444     {
445     // In case the sampler was launched as standalone sampler (not as
446     // plugin), use the following global callback variable hack ...
447     if (g_mainThreadCallbackSupported) {
448     printf("Setting callback ...\n"); fflush(stdout);
449     g_mainThreadCallback = runInCFMainLoop;
450     g_mainThreadCallbackInfo = this;
451     g_fireMainThreadCallback = true;
452     printf("Callback variables set.\n"); fflush(stdout);
453     } else { // Sampler was launched as (i.e. AU / VST) plugin ...
454     // When the sampler was launched as plugin, we have no idea
455     // whether any sampler thread is the process's "main" thread.
456     // So that's why we are trying to use Apple's API for trying to
457     // launch our callback function on the process's main thread.
458     // However this will only work, if the plugin host application
459     // established a CF event loop, that is if the application is
460     // using Cocoa for its GUI. For other host applications the
461     // callback will never be executed and thus gigedit would not
462     // popup.
463    
464     // should be pretty much the same as the Objective-C solution below with macHelperRunCFuncOnMainThread()
465     /*CFRunLoopSourceContext sourceContext = CFRunLoopSourceContext();
466     sourceContext.info = this;
467     sourceContext.perform = runInCFMainLoop;
468     printf("CFRunLoopSourceCreate\n"); fflush(stdout);
469     CFRunLoopSourceRef source = CFRunLoopSourceCreate(
470     kCFAllocatorDefault, // allocator
471     1, // priority
472     &sourceContext
473     );
474     printf("CFRunLoopAddSource\n"); fflush(stdout);
475     CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode);
476     CFRelease(source);*/
477    
478     // use Apple's Objective-C API to call our callback function
479     // 'runInCFMainLoop()' on the process's "main" thread
480     macHelperRunCFuncOnMainThread(runInCFMainLoop, this);
481     }
482     }
483     #else
484     #ifdef OLD_THREADS
485 persson 2332 Glib::Thread::create(
486 persson 1456 sigc::bind(sigc::ptr_fun(&GigEditState::main_loop_run),
487     &initialized),
488     false);
489 schoenebeck 2474 #else
490 persson 2332 Glib::Threads::Thread::create(
491     sigc::bind(sigc::ptr_fun(&GigEditState::main_loop_run),
492     &initialized));
493 schoenebeck 2474 #endif
494 persson 2332 #endif
495 schoenebeck 2474 printf("Waiting for GUI being initialized (on main thread) ....\n"); fflush(stdout);
496 persson 1456 initialized.wait();
497 schoenebeck 2474 printf("GUI is now initialized. Everything done.\n"); fflush(stdout);
498 persson 1456 main_loop_started = true;
499     }
500     current = this;
501     dispatcher->emit();
502     open.wait(); // wait until the GUI thread has read current
503     mutex.unlock();
504     close.wait(); // sleep until window is closed
505     }
506 persson 1898
507     #if defined(WIN32)
508     extern "C" {
509     BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
510     {
511     switch (reason) {
512     case DLL_PROCESS_ATTACH:
513     gigedit_dll_handle = instance;
514     break;
515     }
516     return TRUE;
517     }
518     }
519     #endif

  ViewVC Help
Powered by ViewVC