From fb815110ca9c35b4fa33c3a09cf91772b3db5373 Mon Sep 17 00:00:00 2001 From: jules Date: Wed, 19 Sep 2007 15:09:36 +0000 Subject: [PATCH] --- .../juce_linux_Threads.cpp | 22 + .../juce_mac_Threads.cpp | 750 +++++++++--------- .../juce_win32_Threads.cpp | 11 + .../plugins/vst/juce_VSTPluginInstance.cpp | 445 ++++++++++- extras/juce demo/src/BinaryData.cpp | 2 +- .../gui/components/controls/juce_ComboBox.h | 25 +- src/juce_core/basics/juce_PlatformDefs.h | 12 +- src/juce_core/basics/juce_StandardHeader.h | 21 +- src/juce_core/io/files/juce_File.cpp | 14 +- src/juce_core/io/files/juce_File.h | 6 +- src/juce_core/threads/juce_Process.h | 5 + 11 files changed, 874 insertions(+), 439 deletions(-) diff --git a/build/linux/platform_specific_code/juce_linux_Threads.cpp b/build/linux/platform_specific_code/juce_linux_Threads.cpp index 1d63c96cc9..d6925ef8db 100644 --- a/build/linux/platform_specific_code/juce_linux_Threads.cpp +++ b/build/linux/platform_specific_code/juce_linux_Threads.cpp @@ -32,6 +32,8 @@ #include "linuxincludes.h" #include #include +#include +#include #include "../../../src/juce_core/basics/juce_StandardHeader.h" BEGIN_JUCE_NAMESPACE @@ -339,6 +341,26 @@ void Process::terminate() exit (0); } +bool juce_isRunningUnderDebugger() throw() +{ + static char testResult = 0; + + if (testResult == 0) + { + testResult = (ptrace (PTRACE_TRACEME, 0, 0, 0) < 0) ? 1 : -1; + + if (testResult < 0) + ptrace (PTRACE_DETACH, 0, (caddr_t) 1, 0); + } + + return testResult > 0; +} + +bool Process::isRunningUnderDebugger() throw() +{ + return juce_isRunningUnderDebugger(); +} + void Process::raisePrivilege() { // If running suid root, change effective user diff --git a/build/macosx/platform_specific_code/juce_mac_Threads.cpp b/build/macosx/platform_specific_code/juce_mac_Threads.cpp index b97bbb2caf..8395d5bd17 100644 --- a/build/macosx/platform_specific_code/juce_mac_Threads.cpp +++ b/build/macosx/platform_specific_code/juce_mac_Threads.cpp @@ -1,364 +1,386 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-7 by Raw Material Software ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the - GNU General Public License, as published by the Free Software Foundation; - either version 2 of the License, or (at your option) any later version. - - JUCE is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with JUCE; if not, visit www.gnu.org/licenses or write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - - ------------------------------------------------------------------------------ - - If you'd like to release a closed-source product which uses JUCE, commercial - licenses are also available: visit www.rawmaterialsoftware.com/juce for - more information. - - ============================================================================== -*/ - -#include "../../../src/juce_core/basics/juce_StandardHeader.h" -#include -#include -#include -#include - -BEGIN_JUCE_NAMESPACE - -#include "../../../src/juce_core/threads/juce_CriticalSection.h" -#include "../../../src/juce_core/threads/juce_WaitableEvent.h" -#include "../../../src/juce_core/threads/juce_Thread.h" -#include "../../../src/juce_core/threads/juce_Process.h" -#include "../../../src/juce_core/threads/juce_InterProcessLock.h" -#include "../../../src/juce_core/misc/juce_PlatformUtilities.h" -#include "../../../src/juce_core/io/files/juce_File.h" - - -//============================================================================== -CriticalSection::CriticalSection() throw() -{ - pthread_mutexattr_t atts; - pthread_mutexattr_init (&atts); - pthread_mutexattr_settype (&atts, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init (&internal, &atts); -} - -CriticalSection::~CriticalSection() throw() -{ - pthread_mutex_destroy (&internal); -} - -void CriticalSection::enter() const throw() -{ - pthread_mutex_lock (&internal); -} - -bool CriticalSection::tryEnter() const throw() -{ - return pthread_mutex_trylock (&internal) == 0; -} - -void CriticalSection::exit() const throw() -{ - pthread_mutex_unlock (&internal); -} - -//============================================================================== -struct EventStruct -{ - pthread_cond_t condition; - pthread_mutex_t mutex; - bool triggered; -}; - -WaitableEvent::WaitableEvent() throw() -{ - EventStruct* const es = new EventStruct(); - es->triggered = false; - - pthread_cond_init (&es->condition, 0); - pthread_mutex_init (&es->mutex, 0); - - internal = es; -} - -WaitableEvent::~WaitableEvent() throw() -{ - EventStruct* const es = (EventStruct*) internal; - - pthread_cond_destroy (&es->condition); - pthread_mutex_destroy (&es->mutex); - - delete es; -} - -bool WaitableEvent::wait (const int timeOutMillisecs) const throw() -{ - EventStruct* const es = (EventStruct*) internal; - - bool ok = true; - pthread_mutex_lock (&es->mutex); - - if (! es->triggered) - { - if (timeOutMillisecs < 0) - { - pthread_cond_wait (&es->condition, &es->mutex); - } - else - { - struct timespec time; - time.tv_sec = timeOutMillisecs / 1000; - time.tv_nsec = (timeOutMillisecs % 1000) * 1000000; - pthread_cond_timedwait_relative_np (&es->condition, &es->mutex, &time); - } - - ok = es->triggered; - } - - es->triggered = false; - - pthread_mutex_unlock (&es->mutex); - return ok; -} - -void WaitableEvent::signal() const throw() -{ - EventStruct* const es = (EventStruct*) internal; - - pthread_mutex_lock (&es->mutex); - es->triggered = true; - pthread_cond_signal (&es->condition); - pthread_mutex_unlock (&es->mutex); -} - -void WaitableEvent::reset() const throw() -{ - EventStruct* const es = (EventStruct*) internal; - - pthread_mutex_lock (&es->mutex); - es->triggered = false; - pthread_mutex_unlock (&es->mutex); -} - -//============================================================================== -void JUCE_API juce_threadEntryPoint (void*); - -void* threadEntryProc (void* userData) throw() -{ - juce_threadEntryPoint (userData); - return 0; -} - -void* juce_createThread (void* userData) throw() -{ - pthread_t handle = 0; - - if (pthread_create (&handle, 0, threadEntryProc, userData) == 0) - { - pthread_detach (handle); - return (void*) handle; - } - - return 0; -} - -void juce_killThread (void* handle) throw() -{ - if (handle != 0) - pthread_cancel ((pthread_t) handle); -} - -void juce_setCurrentThreadName (const String& /*name*/) throw() -{ -} - -int Thread::getCurrentThreadId() throw() -{ - return (int) pthread_self(); -} - -void juce_setThreadPriority (void* handle, int priority) throw() -{ - if (handle == 0) - handle = (void*) pthread_self(); - - struct sched_param param; - int policy; - pthread_getschedparam ((pthread_t) handle, &policy, ¶m); - param.sched_priority = jlimit (1, 127, 1 + (priority * 126) / 11); - pthread_setschedparam ((pthread_t) handle, policy, ¶m); -} - -void Thread::yield() throw() -{ - sched_yield(); -} - -void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) throw() -{ - // xxx - jassertfalse -} - -void JUCE_CALLTYPE Thread::sleep (int millisecs) throw() -{ - struct timespec time; - time.tv_sec = millisecs / 1000; - time.tv_nsec = (millisecs % 1000) * 1000000; - nanosleep (&time, 0); -} - - -//============================================================================== -void Process::raisePrivilege() -{ - jassertfalse -} - -void Process::lowerPrivilege() -{ - jassertfalse -} - -void Process::terminate() -{ - ExitToShell(); -} - -void Process::setPriority (ProcessPriority p) -{ - // xxx -} - -void* Process::loadDynamicLibrary (const String& name) -{ - // xxx needs to use bundles - - FSSpec fs; - if (PlatformUtilities::makeFSSpecFromPath (&fs, name)) - { - CFragConnectionID connID; - Ptr mainPtr; - Str255 errorMessage; - Str63 nm; - PlatformUtilities::copyToStr63 (nm, name); - - const OSErr err = GetDiskFragment (&fs, 0, kCFragGoesToEOF, nm, kReferenceCFrag, &connID, &mainPtr, errorMessage); - if (err == noErr) - return (void*)connID; - } - - return 0; -} - -void Process::freeDynamicLibrary (void* handle) -{ - if (handle != 0) - CloseConnection ((CFragConnectionID*)&handle); -} - -void* Process::getProcedureEntryPoint (void* h, const String& procedureName) -{ - if (h != 0) - { - CFragSymbolClass cl; - Ptr ptr; - Str255 name; - PlatformUtilities::copyToStr255 (name, procedureName); - - if (FindSymbol ((CFragConnectionID) h, name, &ptr, &cl) == noErr) - { - return ptr; - } - } - - return 0; -} - -//============================================================================== -InterProcessLock::InterProcessLock (const String& name_) throw() - : internal (0), - name (name_), - reentrancyLevel (0) -{ - const File tempDir (File::getSpecialLocation (File::tempDirectory)); - const File temp (tempDir.getChildFile (name)); - temp.create(); - - internal = (void*) open (temp.getFullPathName().toUTF8(), O_NONBLOCK | O_RDONLY); -} - -InterProcessLock::~InterProcessLock() throw() -{ - while (reentrancyLevel > 0) - this->exit(); - - close ((int) internal); -} - -bool InterProcessLock::enter (const int timeOutMillisecs) throw() -{ - if (internal == 0) - return false; - - if (reentrancyLevel != 0) - return true; - - if (timeOutMillisecs <= 0) - { - if (flock ((int) internal, - timeOutMillisecs < 0 ? LOCK_EX - : (LOCK_EX | LOCK_NB)) == 0) - { - ++reentrancyLevel; - return true; - } - } - else - { - const int64 endTime = Time::currentTimeMillis() + timeOutMillisecs; - - for (;;) - { - if (flock ((int) internal, LOCK_EX | LOCK_NB) == 0) - { - ++reentrancyLevel; - return true; - } - - if (Time::currentTimeMillis() >= endTime) - break; - - Thread::sleep (10); - } - } - - return false; -} - -void InterProcessLock::exit() throw() -{ - if (reentrancyLevel > 0 && internal != 0) - { - --reentrancyLevel; - - const int result = flock ((int) internal, LOCK_UN); - (void) result; - jassert (result == 0); - } -} - -END_JUCE_NAMESPACE +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "../../../src/juce_core/basics/juce_StandardHeader.h" +#include +#include +#include +#include +#include +#include + +BEGIN_JUCE_NAMESPACE + +#include "../../../src/juce_core/threads/juce_CriticalSection.h" +#include "../../../src/juce_core/threads/juce_WaitableEvent.h" +#include "../../../src/juce_core/threads/juce_Thread.h" +#include "../../../src/juce_core/threads/juce_Process.h" +#include "../../../src/juce_core/threads/juce_InterProcessLock.h" +#include "../../../src/juce_core/misc/juce_PlatformUtilities.h" +#include "../../../src/juce_core/io/files/juce_File.h" + + +//============================================================================== +CriticalSection::CriticalSection() throw() +{ + pthread_mutexattr_t atts; + pthread_mutexattr_init (&atts); + pthread_mutexattr_settype (&atts, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init (&internal, &atts); +} + +CriticalSection::~CriticalSection() throw() +{ + pthread_mutex_destroy (&internal); +} + +void CriticalSection::enter() const throw() +{ + pthread_mutex_lock (&internal); +} + +bool CriticalSection::tryEnter() const throw() +{ + return pthread_mutex_trylock (&internal) == 0; +} + +void CriticalSection::exit() const throw() +{ + pthread_mutex_unlock (&internal); +} + +//============================================================================== +struct EventStruct +{ + pthread_cond_t condition; + pthread_mutex_t mutex; + bool triggered; +}; + +WaitableEvent::WaitableEvent() throw() +{ + EventStruct* const es = new EventStruct(); + es->triggered = false; + + pthread_cond_init (&es->condition, 0); + pthread_mutex_init (&es->mutex, 0); + + internal = es; +} + +WaitableEvent::~WaitableEvent() throw() +{ + EventStruct* const es = (EventStruct*) internal; + + pthread_cond_destroy (&es->condition); + pthread_mutex_destroy (&es->mutex); + + delete es; +} + +bool WaitableEvent::wait (const int timeOutMillisecs) const throw() +{ + EventStruct* const es = (EventStruct*) internal; + + bool ok = true; + pthread_mutex_lock (&es->mutex); + + if (! es->triggered) + { + if (timeOutMillisecs < 0) + { + pthread_cond_wait (&es->condition, &es->mutex); + } + else + { + struct timespec time; + time.tv_sec = timeOutMillisecs / 1000; + time.tv_nsec = (timeOutMillisecs % 1000) * 1000000; + pthread_cond_timedwait_relative_np (&es->condition, &es->mutex, &time); + } + + ok = es->triggered; + } + + es->triggered = false; + + pthread_mutex_unlock (&es->mutex); + return ok; +} + +void WaitableEvent::signal() const throw() +{ + EventStruct* const es = (EventStruct*) internal; + + pthread_mutex_lock (&es->mutex); + es->triggered = true; + pthread_cond_signal (&es->condition); + pthread_mutex_unlock (&es->mutex); +} + +void WaitableEvent::reset() const throw() +{ + EventStruct* const es = (EventStruct*) internal; + + pthread_mutex_lock (&es->mutex); + es->triggered = false; + pthread_mutex_unlock (&es->mutex); +} + +//============================================================================== +void JUCE_API juce_threadEntryPoint (void*); + +void* threadEntryProc (void* userData) throw() +{ + juce_threadEntryPoint (userData); + return 0; +} + +void* juce_createThread (void* userData) throw() +{ + pthread_t handle = 0; + + if (pthread_create (&handle, 0, threadEntryProc, userData) == 0) + { + pthread_detach (handle); + return (void*) handle; + } + + return 0; +} + +void juce_killThread (void* handle) throw() +{ + if (handle != 0) + pthread_cancel ((pthread_t) handle); +} + +void juce_setCurrentThreadName (const String& /*name*/) throw() +{ +} + +int Thread::getCurrentThreadId() throw() +{ + return (int) pthread_self(); +} + +void juce_setThreadPriority (void* handle, int priority) throw() +{ + if (handle == 0) + handle = (void*) pthread_self(); + + struct sched_param param; + int policy; + pthread_getschedparam ((pthread_t) handle, &policy, ¶m); + param.sched_priority = jlimit (1, 127, 1 + (priority * 126) / 11); + pthread_setschedparam ((pthread_t) handle, policy, ¶m); +} + +void Thread::yield() throw() +{ + sched_yield(); +} + +void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) throw() +{ + // xxx + jassertfalse +} + +void JUCE_CALLTYPE Thread::sleep (int millisecs) throw() +{ + struct timespec time; + time.tv_sec = millisecs / 1000; + time.tv_nsec = (millisecs % 1000) * 1000000; + nanosleep (&time, 0); +} + + +//============================================================================== +bool juce_isRunningUnderDebugger() throw() +{ + static char testResult = 0; + + if (testResult == 0) + { + testResult = (ptrace (PT_TRACE_ME, 0, 0, 0) < 0) ? 1 : -1; + + if (testResult < 0) + ptrace (PT_DETACH, 0, (caddr_t) 1, 0); + } + + return testResult > 0; +} + +bool Process::isRunningUnderDebugger() throw() +{ + return juce_isRunningUnderDebugger(); +} + +void Process::raisePrivilege() +{ + jassertfalse +} + +void Process::lowerPrivilege() +{ + jassertfalse +} + +void Process::terminate() +{ + ExitToShell(); +} + +void Process::setPriority (ProcessPriority p) +{ + // xxx +} + +void* Process::loadDynamicLibrary (const String& name) +{ + // xxx needs to use bundles + + FSSpec fs; + if (PlatformUtilities::makeFSSpecFromPath (&fs, name)) + { + CFragConnectionID connID; + Ptr mainPtr; + Str255 errorMessage; + Str63 nm; + PlatformUtilities::copyToStr63 (nm, name); + + const OSErr err = GetDiskFragment (&fs, 0, kCFragGoesToEOF, nm, kReferenceCFrag, &connID, &mainPtr, errorMessage); + if (err == noErr) + return (void*)connID; + } + + return 0; +} + +void Process::freeDynamicLibrary (void* handle) +{ + if (handle != 0) + CloseConnection ((CFragConnectionID*)&handle); +} + +void* Process::getProcedureEntryPoint (void* h, const String& procedureName) +{ + if (h != 0) + { + CFragSymbolClass cl; + Ptr ptr; + Str255 name; + PlatformUtilities::copyToStr255 (name, procedureName); + + if (FindSymbol ((CFragConnectionID) h, name, &ptr, &cl) == noErr) + { + return ptr; + } + } + + return 0; +} + +//============================================================================== +InterProcessLock::InterProcessLock (const String& name_) throw() + : internal (0), + name (name_), + reentrancyLevel (0) +{ + const File tempDir (File::getSpecialLocation (File::tempDirectory)); + const File temp (tempDir.getChildFile (name)); + temp.create(); + + internal = (void*) open (temp.getFullPathName().toUTF8(), O_NONBLOCK | O_RDONLY); +} + +InterProcessLock::~InterProcessLock() throw() +{ + while (reentrancyLevel > 0) + this->exit(); + + close ((int) internal); +} + +bool InterProcessLock::enter (const int timeOutMillisecs) throw() +{ + if (internal == 0) + return false; + + if (reentrancyLevel != 0) + return true; + + if (timeOutMillisecs <= 0) + { + if (flock ((int) internal, + timeOutMillisecs < 0 ? LOCK_EX + : (LOCK_EX | LOCK_NB)) == 0) + { + ++reentrancyLevel; + return true; + } + } + else + { + const int64 endTime = Time::currentTimeMillis() + timeOutMillisecs; + + for (;;) + { + if (flock ((int) internal, LOCK_EX | LOCK_NB) == 0) + { + ++reentrancyLevel; + return true; + } + + if (Time::currentTimeMillis() >= endTime) + break; + + Thread::sleep (10); + } + } + + return false; +} + +void InterProcessLock::exit() throw() +{ + if (reentrancyLevel > 0 && internal != 0) + { + --reentrancyLevel; + + const int result = flock ((int) internal, LOCK_UN); + (void) result; + jassert (result == 0); + } +} + +END_JUCE_NAMESPACE diff --git a/build/win32/platform_specific_code/juce_win32_Threads.cpp b/build/win32/platform_specific_code/juce_win32_Threads.cpp index e71e09cde6..ef42aef8d4 100644 --- a/build/win32/platform_specific_code/juce_win32_Threads.cpp +++ b/build/win32/platform_specific_code/juce_win32_Threads.cpp @@ -299,6 +299,17 @@ void Process::setPriority (ProcessPriority prior) } } +bool juce_isRunningUnderDebugger() throw() +{ + return IsDebuggerPresent() != FALSE; +} + +bool Process::isRunningUnderDebugger() throw() +{ + return juce_isRunningUnderDebugger(); +} + + //============================================================================== void Process::raisePrivilege() { diff --git a/extras/audio plugin host/src/plugins/vst/juce_VSTPluginInstance.cpp b/extras/audio plugin host/src/plugins/vst/juce_VSTPluginInstance.cpp index 69cf03abc7..c9773f9cf4 100644 --- a/extras/audio plugin host/src/plugins/vst/juce_VSTPluginInstance.cpp +++ b/extras/audio plugin host/src/plugins/vst/juce_VSTPluginInstance.cpp @@ -35,12 +35,31 @@ #include #include #pragma warning (disable : 4312) +#elif defined LINUX + #include + #include + #include + #include + #include + #undef Font + #undef KeyPress + #undef Drawable + #undef Time + class VSTPluginWindow; #else #include #endif #include "../../../../../juce.h" #include "juce_VSTPluginInstance.h" + +#if JUCE_LINUX + #define Font JUCE_NAMESPACE::Font + #define KeyPress JUCE_NAMESPACE::KeyPress + #define Drawable JUCE_NAMESPACE::Drawable + #define Time JUCE_NAMESPACE::Time +#endif + #include "../juce_PluginDescription.h" #if ! JUCE_WIN32 @@ -173,6 +192,117 @@ static void* NewCFMFromMachO (void* const machofp) throw() #endif #endif +//============================================================================== +#if JUCE_LINUX + +BEGIN_JUCE_NAMESPACE + extern Display* display; + extern XContext improbableNumber; +END_JUCE_NAMESPACE + +typedef void (*EventProcPtr) (XEvent* ev); + +static bool xErrorTriggered; + +static int temporaryErrorHandler (Display*, XErrorEvent*) +{ + xErrorTriggered = true; + return 0; +} + +static int getPropertyFromXWindow (Window handle, Atom atom) +{ + XErrorHandler oldErrorHandler = XSetErrorHandler (temporaryErrorHandler); + xErrorTriggered = false; + + int userSize; + unsigned long bytes, userCount; + unsigned char* data; + Atom userType; + + XGetWindowProperty (display, handle, atom, 0, 1, false, AnyPropertyType, + &userType, &userSize, &userCount, &bytes, &data); + + XSetErrorHandler (oldErrorHandler); + + return (userCount == 1 && ! xErrorTriggered) ? *(int*) data + : 0; +} + +static Window getChildWindow (Window windowToCheck) +{ + Window rootWindow, parentWindow; + Window* childWindows; + unsigned int numChildren; + + XQueryTree (display, + windowToCheck, + &rootWindow, + &parentWindow, + &childWindows, + &numChildren); + + if (numChildren > 0) + return childWindows [0]; + + return 0; +} + +static void translateJuceToXButtonModifiers (const MouseEvent& e, XEvent& ev) throw() +{ + if (e.mods.isLeftButtonDown()) + { + ev.xbutton.button = Button1; + ev.xbutton.state |= Button1Mask; + } + else if (e.mods.isRightButtonDown()) + { + ev.xbutton.button = Button3; + ev.xbutton.state |= Button3Mask; + } + else if (e.mods.isMiddleButtonDown()) + { + ev.xbutton.button = Button2; + ev.xbutton.state |= Button2Mask; + } +} + +static void translateJuceToXMotionModifiers (const MouseEvent& e, XEvent& ev) throw() +{ + if (e.mods.isLeftButtonDown()) + ev.xmotion.state |= Button1Mask; + else if (e.mods.isRightButtonDown()) + ev.xmotion.state |= Button3Mask; + else if (e.mods.isMiddleButtonDown()) + ev.xmotion.state |= Button2Mask; +} + +static void translateJuceToXCrossingModifiers (const MouseEvent& e, XEvent& ev) throw() +{ + if (e.mods.isLeftButtonDown()) + ev.xcrossing.state |= Button1Mask; + else if (e.mods.isRightButtonDown()) + ev.xcrossing.state |= Button3Mask; + else if (e.mods.isMiddleButtonDown()) + ev.xcrossing.state |= Button2Mask; +} + +static void translateJuceToXMouseWheelModifiers (const MouseEvent& e, const float increment, XEvent& ev) throw() +{ + if (increment < 0) + { + ev.xbutton.button = Button5; + ev.xbutton.state |= Button5Mask; + } + else if (increment > 0) + { + ev.xbutton.button = Button4; + ev.xbutton.state |= Button4Mask; + } +} + +#endif + //============================================================================== static VoidArray activeModules; @@ -217,9 +347,9 @@ public: ModuleHandle (const File& file_) : file (file_), moduleMain (0), -#if JUCE_WIN32 +#if JUCE_WIN32 || JUCE_LINUX hModule (0) -#else +#elif JUCE_MAC fragId (0), resHandle (0), bundleRef (0), @@ -228,9 +358,9 @@ public: { activeModules.add (this); -#if JUCE_WIN32 +#if JUCE_WIN32 || JUCE_LINUX fullParentDirectoryPathName = file_.getParentDirectory().getFullPathName(); -#else +#elif JUCE_MAC PlatformUtilities::makeFSSpecFromPath (&parentDirFSSpec, file_.getParentDirectory().getFullPathName()); #endif } @@ -246,27 +376,13 @@ public: juce_UseDebuggingNewOperator //============================================================================== -#if JUCE_WIN32 - HMODULE hModule; +#if JUCE_WIN32 || JUCE_LINUX + void* hModule; String fullParentDirectoryPathName; - static HMODULE loadDLL (const TCHAR* filename) throw() - { - HMODULE h = 0; - - __try - { - h = LoadLibrary (filename); - } - __finally - { - } - - return h; - } - bool open() { +#if JUCE_WIN32 static bool timePeriodSet = false; if (! timePeriodSet) @@ -274,18 +390,16 @@ public: timePeriodSet = true; timeBeginPeriod (2); } +#endif pluginName = file.getFileNameWithoutExtension(); - hModule = loadDLL (file.getFullPathName()); + hModule = Process::loadDynamicLibrary (file.getFullPathName()); - if (hModule == 0) - return false; - - moduleMain = (MainCall) GetProcAddress (hModule, "VSTPluginMain"); + moduleMain = (MainCall) Process::getProcedureEntryPoint (hModule, "VSTPluginMain"); if (moduleMain == 0) - moduleMain = (MainCall) GetProcAddress (hModule, "main"); + moduleMain = (MainCall) Process::getProcedureEntryPoint (hModule, "main"); return moduleMain != 0; } @@ -294,16 +408,7 @@ public: { _fpreset(); // (doesn't do any harm) - if (hModule != 0) - { - __try - { - FreeLibrary (hModule); - } - __finally - { - } - } + Process::freeDynamicLibrary (hModule); } void closeEffect (AEffect* eff) @@ -758,7 +863,11 @@ void VSTPluginInstance::processBlock (AudioSampleBuffer& buffer, { #if JUCE_WIN32 vstHostTime.nanoSeconds = timeGetTime() * 1000000.0; -#else +#elif JUCE_LINUX + timeval micro; + gettimeofday (µ, 0); + vstHostTime.nanoSeconds = micro.tv_usec * 1000.0; +#elif JUCE_MAC UnsignedWide micro; Microseconds (µ); vstHostTime.nanoSeconds = micro.lo * 1000.0; @@ -944,6 +1053,9 @@ public: #if JUCE_WIN32 sizeCheckCount = 0; pluginHWND = 0; +#elif JUCE_LINUX + pluginWindow = None; + pluginProc = None; #else pluginViewRef = 0; #endif @@ -1014,9 +1126,16 @@ public: { repaint(); } -#else +#elif JUCE_WIN32 if (pluginHWND != 0) MoveWindow (pluginHWND, x, y, getWidth(), getHeight(), TRUE); +#elif JUCE_LINUX + if (pluginWindow != 0) + { + XResizeWindow (display, pluginWindow, getWidth(), getHeight()); + XMoveWindow (display, pluginWindow, x, y); + XMapRaised (display, pluginWindow); + } #endif recursiveResize = false; @@ -1072,6 +1191,23 @@ public: #if JUCE_MAC dispatch (effEditDraw, 0, 0, 0, 0); +#elif JUCE_LINUX + if (pluginWindow != 0) + { + const Rectangle clip (g.getClipBounds()); + + XEvent ev; + zerostruct (ev); + ev.xexpose.type = Expose; + ev.xexpose.display = display; + ev.xexpose.window = pluginWindow; + ev.xexpose.x = clip.getX(); + ev.xexpose.y = clip.getY(); + ev.xexpose.width = clip.getWidth(); + ev.xexpose.height = clip.getHeight(); + + sendEventToChild (&ev); + } #endif } } @@ -1123,6 +1259,29 @@ public: { PostEvent (::mouseDown, 0); } +#elif JUCE_LINUX + + if (pluginWindow == 0) + return; + + toFront (true); + + XEvent ev; + zerostruct (ev); + ev.xbutton.display = display; + ev.xbutton.type = ButtonPress; + ev.xbutton.window = pluginWindow; + ev.xbutton.root = RootWindow (display, DefaultScreen (display)); + ev.xbutton.time = CurrentTime; + ev.xbutton.x = e.x; + ev.xbutton.y = e.y; + ev.xbutton.x_root = e.getScreenX(); + ev.xbutton.y_root = e.getScreenY(); + + translateJuceToXButtonModifiers (e, ev); + + sendEventToChild (&ev); + #else (void) e; @@ -1152,9 +1311,12 @@ private: HWND pluginHWND; void* originalWndProc; int sizeCheckCount; -#else +#elif JUCE_MAC HIViewRef pluginViewRef; WindowRef pluginWindowRef; +#elif JUCE_LINUX + Window pluginWindow; + EventProcPtr pluginProc; #endif //============================================================================== @@ -1231,7 +1393,7 @@ private: } } } -#else +#elif JUCE_MAC HIViewRef root = HIViewGetRoot ((WindowRef) getWindowHandle()); HIViewFindByID (root, kHIViewWindowContentID, &root); pluginViewRef = HIViewGetFirstSubview (root); @@ -1269,6 +1431,30 @@ private: h = 150; } } + +#elif JUCE_LINUX + pluginWindow = getChildWindow ((Window) getWindowHandle()); + + if (pluginWindow != 0) + pluginProc = (EventProcPtr) getPropertyFromXWindow (pluginWindow, + XInternAtom (display, "_XEventProc", False)); + + int w = 250, h = 150; + + if (rect != 0) + { + w = rect->right - rect->left; + h = rect->bottom - rect->top; + + if (w == 0 || h == 0) + { + w = 250; + h = 150; + } + } + + if (pluginWindow != 0) + XMapRaised (display, pluginWindow); #endif // double-check it's not too tiny @@ -1310,10 +1496,14 @@ private: DestroyWindow (pluginHWND); pluginHWND = 0; -#else +#elif JUCE_MAC dispatch (effEditSleep, 0, 0, 0, 0); pluginViewRef = 0; stopTimer(); +#elif JUCE_LINUX + stopTimer(); + pluginWindow = 0; + pluginProc = 0; #endif } } @@ -1408,6 +1598,168 @@ private: return DefWindowProc (hW, message, wParam, lParam); } #endif + +#if JUCE_LINUX + //============================================================================== + // overload mouse/keyboard events to forward them to the plugin's inner window.. + void sendEventToChild (XEvent* event) + { + if (pluginProc != 0) + { + // if the plugin publishes an event procedure, pass the event directly.. + pluginProc (event); + } + else if (pluginWindow != 0) + { + // if the plugin has a window, then send the event to the window so that + // its message thread will pick it up.. + XSendEvent (display, pluginWindow, False, 0L, event); + XFlush (display); + } + } + + void mouseEnter (const MouseEvent& e) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xcrossing.display = display; + ev.xcrossing.type = EnterNotify; + ev.xcrossing.window = pluginWindow; + ev.xcrossing.root = RootWindow (display, DefaultScreen (display)); + ev.xcrossing.time = CurrentTime; + ev.xcrossing.x = e.x; + ev.xcrossing.y = e.y; + ev.xcrossing.x_root = e.getScreenX(); + ev.xcrossing.y_root = e.getScreenY(); + ev.xcrossing.mode = NotifyNormal; // NotifyGrab, NotifyUngrab + ev.xcrossing.detail = NotifyAncestor; // NotifyVirtual, NotifyInferior, NotifyNonlinear,NotifyNonlinearVirtual + + translateJuceToXCrossingModifiers (e, ev); + + sendEventToChild (&ev); + } + } + + void mouseExit (const MouseEvent& e) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xcrossing.display = display; + ev.xcrossing.type = LeaveNotify; + ev.xcrossing.window = pluginWindow; + ev.xcrossing.root = RootWindow (display, DefaultScreen (display)); + ev.xcrossing.time = CurrentTime; + ev.xcrossing.x = e.x; + ev.xcrossing.y = e.y; + ev.xcrossing.x_root = e.getScreenX(); + ev.xcrossing.y_root = e.getScreenY(); + ev.xcrossing.mode = NotifyNormal; // NotifyGrab, NotifyUngrab + ev.xcrossing.detail = NotifyAncestor; // NotifyVirtual, NotifyInferior, NotifyNonlinear,NotifyNonlinearVirtual + ev.xcrossing.focus = hasKeyboardFocus (true); // TODO - yes ? + + translateJuceToXCrossingModifiers (e, ev); + + sendEventToChild (&ev); + } + } + + void mouseMove (const MouseEvent& e) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xmotion.display = display; + ev.xmotion.type = MotionNotify; + ev.xmotion.window = pluginWindow; + ev.xmotion.root = RootWindow (display, DefaultScreen (display)); + ev.xmotion.time = CurrentTime; + ev.xmotion.is_hint = NotifyNormal; + ev.xmotion.x = e.x; + ev.xmotion.y = e.y; + ev.xmotion.x_root = e.getScreenX(); + ev.xmotion.y_root = e.getScreenY(); + + sendEventToChild (&ev); + } + } + + void mouseDrag (const MouseEvent& e) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xmotion.display = display; + ev.xmotion.type = MotionNotify; + ev.xmotion.window = pluginWindow; + ev.xmotion.root = RootWindow (display, DefaultScreen (display)); + ev.xmotion.time = CurrentTime; + ev.xmotion.x = e.x ; + ev.xmotion.y = e.y; + ev.xmotion.x_root = e.getScreenX(); + ev.xmotion.y_root = e.getScreenY(); + ev.xmotion.is_hint = NotifyNormal; + + translateJuceToXMotionModifiers (e, ev); + sendEventToChild (&ev); + } + } + + void mouseUp (const MouseEvent& e) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xbutton.display = display; + ev.xbutton.type = ButtonRelease; + ev.xbutton.window = pluginWindow; + ev.xbutton.root = RootWindow (display, DefaultScreen (display)); + ev.xbutton.time = CurrentTime; + ev.xbutton.x = e.x; + ev.xbutton.y = e.y; + ev.xbutton.x_root = e.getScreenX(); + ev.xbutton.y_root = e.getScreenY(); + + translateJuceToXButtonModifiers (e, ev); + sendEventToChild (&ev); + } + } + + void mouseWheelMove (const MouseEvent& e, + float incrementX, + float incrementY) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xbutton.display = display; + ev.xbutton.type = ButtonPress; + ev.xbutton.window = pluginWindow; + ev.xbutton.root = RootWindow (display, DefaultScreen (display)); + ev.xbutton.time = CurrentTime; + ev.xbutton.x = e.x; + ev.xbutton.y = e.y; + ev.xbutton.x_root = e.getScreenX(); + ev.xbutton.y_root = e.getScreenY(); + + translateJuceToXMouseWheelModifiers (e, incrementY, ev); + sendEventToChild (&ev); + + // TODO - put a usleep here ? + + ev.xbutton.type = ButtonRelease; + sendEventToChild (&ev); + } + } +#endif + }; //============================================================================== @@ -2533,9 +2885,12 @@ bool VSTPluginFormat::fileMightContainThisPluginType (const File& f) #endif return false; -#else +#elif JUCE_WIN32 return f.existsAsFile() && f.hasFileExtension (T(".dll")); +#elif JUCE_LINUX + return f.existsAsFile() + && f.hasFileExtension (T(".so")); #endif } @@ -2543,9 +2898,11 @@ const FileSearchPath VSTPluginFormat::getDefaultLocationsToSearch() { #if JUCE_MAC return FileSearchPath ("~/Library/Audio/Plug-Ins/VST;/Library/Audio/Plug-Ins/VST"); -#else +#elif JUCE_WIN32 const String programFiles (File::getSpecialLocation (File::globalApplicationsDirectory).getFullPathName()); return FileSearchPath (programFiles + "\\Steinberg\\VstPlugins"); +#elif JUCE_LINUX + return FileSearchPath ("/usr/lib/vst"); #endif } diff --git a/extras/juce demo/src/BinaryData.cpp b/extras/juce demo/src/BinaryData.cpp index cda2ed8f88..eb5ff7d860 100644 --- a/extras/juce demo/src/BinaryData.cpp +++ b/extras/juce demo/src/BinaryData.cpp @@ -5160,7 +5160,7 @@ static const unsigned char temp9[] = {47,42,13,10,32,32,61,61,61,61,61,61,61,61, 46,76,105,98,34,41,13,10,32,35,101,110,100,105,102,13,10,35,101,108,105,102,32,100,101,102,105,110,101,100,32,40,76,73,78,85,88,41,13,10, 32,35,105,110,99,108,117,100,101,32,60,71,76,47,103,108,46,104,62,13,10,32,35,105,110,99,108,117,100,101,32,60,71,76,47,103,108,117,116,46, 104,62,13,10,32,35,117,110,100,101,102,32,75,101,121,80,114,101,115,115,13,10,35,101,108,115,101,13,10,32,35,105,110,99,108,117,100,101,32,60, - 97,103,108,47,97,103,108,46,104,62,13,10,32,35,105,110,99,108,117,100,101,32,60,103,108,117,116,47,103,108,117,116,46,104,62,13,10,35,101,110, + 65,71,76,47,97,103,108,46,104,62,13,10,32,35,105,110,99,108,117,100,101,32,60,71,76,85,84,47,103,108,117,116,46,104,62,13,10,35,101,110, 100,105,102,13,10,13,10,35,105,102,110,100,101,102,32,71,76,95,66,71,82,65,95,69,88,84,13,10,32,35,100,101,102,105,110,101,32,71,76,95, 66,71,82,65,95,69,88,84,32,48,120,56,48,101,49,13,10,35,101,110,100,105,102,13,10,13,10,47,47,61,61,61,61,61,61,61,61,61,61,61, 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, diff --git a/src/juce_appframework/gui/components/controls/juce_ComboBox.h b/src/juce_appframework/gui/components/controls/juce_ComboBox.h index f824ef6007..7cc0db62dc 100644 --- a/src/juce_appframework/gui/components/controls/juce_ComboBox.h +++ b/src/juce_appframework/gui/components/controls/juce_ComboBox.h @@ -343,6 +343,22 @@ public: void handleAsyncUpdate(); /** @internal */ const String getTooltip() { return label->getTooltip(); } + /** @internal */ + void mouseDown (const MouseEvent&); + /** @internal */ + void mouseDrag (const MouseEvent&); + /** @internal */ + void mouseUp (const MouseEvent&); + /** @internal */ + void lookAndFeelChanged(); + /** @internal */ + void paint (Graphics&); + /** @internal */ + void resized(); + /** @internal */ + bool keyStateChanged(); + /** @internal */ + bool keyPressed (const KeyPress&); //============================================================================== juce_UseDebuggingNewOperator @@ -367,15 +383,6 @@ private: Label* label; String textWhenNothingSelected, noChoicesMessage; - void mouseDown (const MouseEvent&); - void mouseDrag (const MouseEvent&); - void mouseUp (const MouseEvent&); - void lookAndFeelChanged(); - void paint (Graphics&); - void resized(); - bool keyStateChanged(); - bool keyPressed (const KeyPress&); - void showPopup(); ItemInfo* getItemForId (const int id) const throw(); diff --git a/src/juce_core/basics/juce_PlatformDefs.h b/src/juce_core/basics/juce_PlatformDefs.h index 5d9b8ea33c..795fac039a 100644 --- a/src/juce_core/basics/juce_PlatformDefs.h +++ b/src/juce_core/basics/juce_PlatformDefs.h @@ -190,6 +190,10 @@ //============================================================================== // Assertions.. + BEGIN_JUCE_NAMESPACE + extern bool juce_isRunningUnderDebugger() throw(); + END_JUCE_NAMESPACE + #if JUCE_MSVC || DOXYGEN #if JUCE_USE_INTRINSICS @@ -202,7 +206,7 @@ @see jassert() */ - #define jassertfalse { juce_LogCurrentAssertion; __debugbreak(); } + #define jassertfalse { juce_LogCurrentAssertion; if (juce_isRunningUnderDebugger()) __debugbreak(); } #else /** This will always cause an assertion failure. @@ -210,12 +214,12 @@ @see jassert() */ - #define jassertfalse { juce_LogCurrentAssertion; __asm int 3 } + #define jassertfalse { juce_LogCurrentAssertion; if (juce_isRunningUnderDebugger()) { __asm int 3 } } #endif #elif defined (JUCE_MAC) - #define jassertfalse { juce_LogCurrentAssertion; Debugger(); } + #define jassertfalse { juce_LogCurrentAssertion; if (juce_isRunningUnderDebugger()) Debugger(); } #elif defined (JUCE_GCC) || defined (JUCE_LINUX) - #define jassertfalse { juce_LogCurrentAssertion; asm("int $3"); } + #define jassertfalse { juce_LogCurrentAssertion; if (juce_isRunningUnderDebugger()) asm("int $3"); } #endif //============================================================================== diff --git a/src/juce_core/basics/juce_StandardHeader.h b/src/juce_core/basics/juce_StandardHeader.h index 2c24284c01..deaa5e2b94 100644 --- a/src/juce_core/basics/juce_StandardHeader.h +++ b/src/juce_core/basics/juce_StandardHeader.h @@ -54,12 +54,20 @@ //============================================================================== #include "../../../juce_Config.h" +//============================================================================== +#ifdef JUCE_NAMESPACE + #define BEGIN_JUCE_NAMESPACE namespace JUCE_NAMESPACE { + #define END_JUCE_NAMESPACE } +#else + #define BEGIN_JUCE_NAMESPACE + #define END_JUCE_NAMESPACE +#endif + +//============================================================================== // This sets up the JUCE_WIN32, JUCE_MAC, or JUCE_LINUX macros #include "juce_PlatformDefs.h" -//============================================================================== // Now we'll include any OS headers we need.. (at this point we are outside the Juce namespace). - #if JUCE_MSVC #pragma warning (push) #pragma warning (disable: 4514 4245 4100) @@ -93,15 +101,6 @@ #pragma warning (pop) #endif -//============================================================================== -#ifdef JUCE_NAMESPACE - #define BEGIN_JUCE_NAMESPACE namespace JUCE_NAMESPACE { - #define END_JUCE_NAMESPACE } -#else - #define BEGIN_JUCE_NAMESPACE - #define END_JUCE_NAMESPACE -#endif - //============================================================================== // DLL building settings on Win32 #if JUCE_MSVC diff --git a/src/juce_core/io/files/juce_File.cpp b/src/juce_core/io/files/juce_File.cpp index c4476b3672..77a8d949b6 100644 --- a/src/juce_core/io/files/juce_File.cpp +++ b/src/juce_core/io/files/juce_File.cpp @@ -439,15 +439,19 @@ bool File::isAChildOf (const File& potentialParent) const throw() } //============================================================================== -const File File::getChildFile (String relativePath) const throw() +bool File::isAbsolutePath (const String& path) throw() { - if (relativePath.startsWithChar (T('/')) - || relativePath.startsWithChar (T('\\')) + return path.startsWithChar (T('/')) || path.startsWithChar (T('\\')) #if JUCE_WIN32 - || (relativePath.isNotEmpty() && ((const String&) relativePath)[1] == T(':'))) + || (path.isNotEmpty() && ((const String&) path)[1] == T(':')); #else - || relativePath.startsWithChar (T('~'))) + || path.startsWithChar (T('~')); #endif +} + +const File File::getChildFile (String relativePath) const throw() +{ + if (isAbsolutePath (relativePath)) { // the path is really absolute.. return File (relativePath); diff --git a/src/juce_core/io/files/juce_File.h b/src/juce_core/io/files/juce_File.h index f58c42752b..d3e9a555bd 100644 --- a/src/juce_core/io/files/juce_File.h +++ b/src/juce_core/io/files/juce_File.h @@ -182,7 +182,7 @@ public: be relative to. If this is actually a file rather than a directory, its parent directory will be used instead. If it doesn't exist, it's assumed to be a directory. - @see getChildFile + @see getChildFile, isAbsolutePath */ const String getRelativePathFrom (const File& directoryToBeRelativeTo) const throw(); @@ -834,6 +834,10 @@ public: */ static bool areFileNamesCaseSensitive(); + /** Returns true if the string seems to be a fully-specified absolute path. + */ + static bool isAbsolutePath (const String& path) throw(); + //============================================================================== juce_UseDebuggingNewOperator diff --git a/src/juce_core/threads/juce_Process.h b/src/juce_core/threads/juce_Process.h index bc0cf2a96e..66cac9823e 100644 --- a/src/juce_core/threads/juce_Process.h +++ b/src/juce_core/threads/juce_Process.h @@ -93,6 +93,11 @@ public: */ static void lowerPrivilege(); + //============================================================================== + /** Returns true if this process is being hosted by a debugger. + */ + static bool isRunningUnderDebugger() throw(); + //============================================================================== /** Loads a dynamically-linked library into the process's address space.