/[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 3068 - (hide annotations) (download)
Mon Jan 2 22:13:01 2017 UTC (7 years, 3 months ago) by schoenebeck
File size: 18838 byte(s)
- Preparations for Xcode project update.

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

  ViewVC Help
Powered by ViewVC