/[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 2841 - (hide annotations) (download)
Sun Aug 30 10:00:49 2015 UTC (8 years, 7 months ago) by persson
File size: 17151 byte(s)
* allow building with G_DISABLE_DEPRECATED
* fixed building without liblinuxsampler on Mac
* fixed some compiler and cppcheck warnings

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

  ViewVC Help
Powered by ViewVC