mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-07 04:10:08 +00:00
441 lines
12 KiB
C++
441 lines
12 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
|
Copyright 2004-11 by Raw Material Software Ltd.
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
JUCE can be redistributed and/or modified under the terms of the GNU General
|
|
Public License (Version 2), as published by the Free Software Foundation.
|
|
A copy of the license is included in the JUCE distribution, or can be found
|
|
online at www.gnu.org/licenses.
|
|
|
|
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.
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
To release a closed-source product which uses JUCE, commercial licenses are
|
|
available: visit www.rawmaterialsoftware.com/juce for more information.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
// (This file gets included by juce_win32_NativeCode.cpp, rather than being
|
|
// compiled on its own).
|
|
#if JUCE_INCLUDED_FILE
|
|
|
|
#if ! JUCE_ONLY_BUILD_CORE_LIBRARY
|
|
extern HWND juce_messageWindowHandle;
|
|
#endif
|
|
|
|
//==============================================================================
|
|
#if ! JUCE_USE_INTRINSICS
|
|
// In newer compilers, the inline versions of these are used (in juce_Atomic.h), but in
|
|
// older ones we have to actually call the ops as win32 functions..
|
|
long juce_InterlockedExchange (volatile long* a, long b) noexcept { return InterlockedExchange (a, b); }
|
|
long juce_InterlockedIncrement (volatile long* a) noexcept { return InterlockedIncrement (a); }
|
|
long juce_InterlockedDecrement (volatile long* a) noexcept { return InterlockedDecrement (a); }
|
|
long juce_InterlockedExchangeAdd (volatile long* a, long b) noexcept { return InterlockedExchangeAdd (a, b); }
|
|
long juce_InterlockedCompareExchange (volatile long* a, long b, long c) noexcept { return InterlockedCompareExchange (a, b, c); }
|
|
|
|
__int64 juce_InterlockedCompareExchange64 (volatile __int64* value, __int64 newValue, __int64 valueToCompare) noexcept
|
|
{
|
|
jassertfalse; // This operation isn't available in old MS compiler versions!
|
|
|
|
__int64 oldValue = *value;
|
|
if (oldValue == valueToCompare)
|
|
*value = newValue;
|
|
|
|
return oldValue;
|
|
}
|
|
|
|
#endif
|
|
|
|
//==============================================================================
|
|
CriticalSection::CriticalSection() noexcept
|
|
{
|
|
// (just to check the MS haven't changed this structure and broken things...)
|
|
#if JUCE_VC7_OR_EARLIER
|
|
static_jassert (sizeof (CRITICAL_SECTION) <= 24);
|
|
#else
|
|
static_jassert (sizeof (CRITICAL_SECTION) <= sizeof (internal));
|
|
#endif
|
|
|
|
InitializeCriticalSection ((CRITICAL_SECTION*) internal);
|
|
}
|
|
|
|
CriticalSection::~CriticalSection() noexcept
|
|
{
|
|
DeleteCriticalSection ((CRITICAL_SECTION*) internal);
|
|
}
|
|
|
|
void CriticalSection::enter() const noexcept
|
|
{
|
|
EnterCriticalSection ((CRITICAL_SECTION*) internal);
|
|
}
|
|
|
|
bool CriticalSection::tryEnter() const noexcept
|
|
{
|
|
return TryEnterCriticalSection ((CRITICAL_SECTION*) internal) != FALSE;
|
|
}
|
|
|
|
void CriticalSection::exit() const noexcept
|
|
{
|
|
LeaveCriticalSection ((CRITICAL_SECTION*) internal);
|
|
}
|
|
|
|
//==============================================================================
|
|
WaitableEvent::WaitableEvent (const bool manualReset) noexcept
|
|
: internal (CreateEvent (0, manualReset ? TRUE : FALSE, FALSE, 0))
|
|
{
|
|
}
|
|
|
|
WaitableEvent::~WaitableEvent() noexcept
|
|
{
|
|
CloseHandle (internal);
|
|
}
|
|
|
|
bool WaitableEvent::wait (const int timeOutMillisecs) const noexcept
|
|
{
|
|
return WaitForSingleObject (internal, timeOutMillisecs) == WAIT_OBJECT_0;
|
|
}
|
|
|
|
void WaitableEvent::signal() const noexcept
|
|
{
|
|
SetEvent (internal);
|
|
}
|
|
|
|
void WaitableEvent::reset() const noexcept
|
|
{
|
|
ResetEvent (internal);
|
|
}
|
|
|
|
//==============================================================================
|
|
void JUCE_API juce_threadEntryPoint (void*);
|
|
|
|
static unsigned int __stdcall threadEntryProc (void* userData)
|
|
{
|
|
#if ! JUCE_ONLY_BUILD_CORE_LIBRARY
|
|
AttachThreadInput (GetWindowThreadProcessId (juce_messageWindowHandle, 0),
|
|
GetCurrentThreadId(), TRUE);
|
|
#endif
|
|
|
|
juce_threadEntryPoint (userData);
|
|
|
|
_endthreadex (0);
|
|
return 0;
|
|
}
|
|
|
|
void Thread::launchThread()
|
|
{
|
|
unsigned int newThreadId;
|
|
threadHandle_ = (void*) _beginthreadex (0, 0, &threadEntryProc, this, 0, &newThreadId);
|
|
threadId_ = (ThreadID) newThreadId;
|
|
}
|
|
|
|
void Thread::closeThreadHandle()
|
|
{
|
|
CloseHandle ((HANDLE) threadHandle_);
|
|
threadId_ = 0;
|
|
threadHandle_ = 0;
|
|
}
|
|
|
|
void Thread::killThread()
|
|
{
|
|
if (threadHandle_ != 0)
|
|
{
|
|
#if JUCE_DEBUG
|
|
OutputDebugString (_T("** Warning - Forced thread termination **\n"));
|
|
#endif
|
|
TerminateThread (threadHandle_, 0);
|
|
}
|
|
}
|
|
|
|
void Thread::setCurrentThreadName (const String& name)
|
|
{
|
|
#if JUCE_DEBUG && JUCE_MSVC
|
|
struct
|
|
{
|
|
DWORD dwType;
|
|
LPCSTR szName;
|
|
DWORD dwThreadID;
|
|
DWORD dwFlags;
|
|
} info;
|
|
|
|
info.dwType = 0x1000;
|
|
info.szName = name.toUTF8();
|
|
info.dwThreadID = GetCurrentThreadId();
|
|
info.dwFlags = 0;
|
|
|
|
__try
|
|
{
|
|
RaiseException (0x406d1388 /*MS_VC_EXCEPTION*/, 0, sizeof (info) / sizeof (ULONG_PTR), (ULONG_PTR*) &info);
|
|
}
|
|
__except (EXCEPTION_CONTINUE_EXECUTION)
|
|
{}
|
|
#else
|
|
(void) name;
|
|
#endif
|
|
}
|
|
|
|
Thread::ThreadID Thread::getCurrentThreadId()
|
|
{
|
|
return (ThreadID) (pointer_sized_int) GetCurrentThreadId();
|
|
}
|
|
|
|
bool Thread::setThreadPriority (void* handle, int priority)
|
|
{
|
|
int pri = THREAD_PRIORITY_TIME_CRITICAL;
|
|
|
|
if (priority < 1) pri = THREAD_PRIORITY_IDLE;
|
|
else if (priority < 2) pri = THREAD_PRIORITY_LOWEST;
|
|
else if (priority < 5) pri = THREAD_PRIORITY_BELOW_NORMAL;
|
|
else if (priority < 7) pri = THREAD_PRIORITY_NORMAL;
|
|
else if (priority < 9) pri = THREAD_PRIORITY_ABOVE_NORMAL;
|
|
else if (priority < 10) pri = THREAD_PRIORITY_HIGHEST;
|
|
|
|
if (handle == 0)
|
|
handle = GetCurrentThread();
|
|
|
|
return SetThreadPriority (handle, pri) != FALSE;
|
|
}
|
|
|
|
void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask)
|
|
{
|
|
SetThreadAffinityMask (GetCurrentThread(), affinityMask);
|
|
}
|
|
|
|
//==============================================================================
|
|
struct SleepEvent
|
|
{
|
|
SleepEvent()
|
|
: handle (CreateEvent (0, 0, 0,
|
|
#if JUCE_DEBUG
|
|
_T("Juce Sleep Event")))
|
|
#else
|
|
0))
|
|
#endif
|
|
{
|
|
}
|
|
|
|
HANDLE handle;
|
|
};
|
|
|
|
static SleepEvent sleepEvent;
|
|
|
|
void JUCE_CALLTYPE Thread::sleep (const int millisecs)
|
|
{
|
|
if (millisecs >= 10)
|
|
{
|
|
Sleep (millisecs);
|
|
}
|
|
else
|
|
{
|
|
// unlike Sleep() this is guaranteed to return to the current thread after
|
|
// the time expires, so we'll use this for short waits, which are more likely
|
|
// to need to be accurate
|
|
WaitForSingleObject (sleepEvent.handle, millisecs);
|
|
}
|
|
}
|
|
|
|
void Thread::yield()
|
|
{
|
|
Sleep (0);
|
|
}
|
|
|
|
//==============================================================================
|
|
static int lastProcessPriority = -1;
|
|
|
|
// called by WindowDriver because Windows does wierd things to process priority
|
|
// when you swap apps, and this forces an update when the app is brought to the front.
|
|
void juce_repeatLastProcessPriority()
|
|
{
|
|
if (lastProcessPriority >= 0) // (avoid changing this if it's not been explicitly set by the app..)
|
|
{
|
|
DWORD p;
|
|
|
|
switch (lastProcessPriority)
|
|
{
|
|
case Process::LowPriority: p = IDLE_PRIORITY_CLASS; break;
|
|
case Process::NormalPriority: p = NORMAL_PRIORITY_CLASS; break;
|
|
case Process::HighPriority: p = HIGH_PRIORITY_CLASS; break;
|
|
case Process::RealtimePriority: p = REALTIME_PRIORITY_CLASS; break;
|
|
default: jassertfalse; return; // bad priority value
|
|
}
|
|
|
|
SetPriorityClass (GetCurrentProcess(), p);
|
|
}
|
|
}
|
|
|
|
void Process::setPriority (ProcessPriority prior)
|
|
{
|
|
if (lastProcessPriority != (int) prior)
|
|
{
|
|
lastProcessPriority = (int) prior;
|
|
juce_repeatLastProcessPriority();
|
|
}
|
|
}
|
|
|
|
JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger()
|
|
{
|
|
return IsDebuggerPresent() != FALSE;
|
|
}
|
|
|
|
bool JUCE_CALLTYPE Process::isRunningUnderDebugger()
|
|
{
|
|
return juce_isRunningUnderDebugger();
|
|
}
|
|
|
|
|
|
//==============================================================================
|
|
void Process::raisePrivilege()
|
|
{
|
|
jassertfalse; // xxx not implemented
|
|
}
|
|
|
|
void Process::lowerPrivilege()
|
|
{
|
|
jassertfalse; // xxx not implemented
|
|
}
|
|
|
|
void Process::terminate()
|
|
{
|
|
#if JUCE_MSVC && JUCE_CHECK_MEMORY_LEAKS
|
|
_CrtDumpMemoryLeaks();
|
|
#endif
|
|
|
|
// bullet in the head in case there's a problem shutting down..
|
|
ExitProcess (0);
|
|
}
|
|
|
|
//==============================================================================
|
|
void* PlatformUtilities::loadDynamicLibrary (const String& name)
|
|
{
|
|
void* result = nullptr;
|
|
|
|
JUCE_TRY
|
|
{
|
|
result = LoadLibrary (name.toWideCharPointer());
|
|
}
|
|
JUCE_CATCH_ALL
|
|
|
|
return result;
|
|
}
|
|
|
|
void PlatformUtilities::freeDynamicLibrary (void* h)
|
|
{
|
|
JUCE_TRY
|
|
{
|
|
if (h != nullptr)
|
|
FreeLibrary ((HMODULE) h);
|
|
}
|
|
JUCE_CATCH_ALL
|
|
}
|
|
|
|
void* PlatformUtilities::getProcedureEntryPoint (void* h, const String& name)
|
|
{
|
|
return (h != nullptr) ? (void*) GetProcAddress ((HMODULE) h, name.toUTF8()) : nullptr; // (void* cast is required for mingw)
|
|
}
|
|
|
|
|
|
//==============================================================================
|
|
class InterProcessLock::Pimpl
|
|
{
|
|
public:
|
|
Pimpl (String name, const int timeOutMillisecs)
|
|
: handle (0), refCount (1)
|
|
{
|
|
name = name.replaceCharacter ('\\', '/');
|
|
handle = CreateMutexW (0, TRUE, ("Global\\" + name).toWideCharPointer());
|
|
|
|
// Not 100% sure why a global mutex sometimes can't be allocated, but if it fails, fall back to
|
|
// a local one. (A local one also sometimes fails on other machines so neither type appears to be
|
|
// universally reliable)
|
|
if (handle == 0)
|
|
handle = CreateMutexW (0, TRUE, ("Local\\" + name).toWideCharPointer());
|
|
|
|
if (handle != 0 && GetLastError() == ERROR_ALREADY_EXISTS)
|
|
{
|
|
if (timeOutMillisecs == 0)
|
|
{
|
|
close();
|
|
return;
|
|
}
|
|
|
|
switch (WaitForSingleObject (handle, timeOutMillisecs < 0 ? INFINITE : timeOutMillisecs))
|
|
{
|
|
case WAIT_OBJECT_0:
|
|
case WAIT_ABANDONED:
|
|
break;
|
|
|
|
case WAIT_TIMEOUT:
|
|
default:
|
|
close();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
~Pimpl()
|
|
{
|
|
close();
|
|
}
|
|
|
|
void close()
|
|
{
|
|
if (handle != 0)
|
|
{
|
|
ReleaseMutex (handle);
|
|
CloseHandle (handle);
|
|
handle = 0;
|
|
}
|
|
}
|
|
|
|
HANDLE handle;
|
|
int refCount;
|
|
};
|
|
|
|
InterProcessLock::InterProcessLock (const String& name_)
|
|
: name (name_)
|
|
{
|
|
}
|
|
|
|
InterProcessLock::~InterProcessLock()
|
|
{
|
|
}
|
|
|
|
bool InterProcessLock::enter (const int timeOutMillisecs)
|
|
{
|
|
const ScopedLock sl (lock);
|
|
|
|
if (pimpl == nullptr)
|
|
{
|
|
pimpl = new Pimpl (name, timeOutMillisecs);
|
|
|
|
if (pimpl->handle == 0)
|
|
pimpl = nullptr;
|
|
}
|
|
else
|
|
{
|
|
pimpl->refCount++;
|
|
}
|
|
|
|
return pimpl != nullptr;
|
|
}
|
|
|
|
void InterProcessLock::exit()
|
|
{
|
|
const ScopedLock sl (lock);
|
|
|
|
// Trying to release the lock too many times!
|
|
jassert (pimpl != nullptr);
|
|
|
|
if (pimpl != nullptr && --(pimpl->refCount) == 0)
|
|
pimpl = nullptr;
|
|
}
|
|
|
|
|
|
#endif
|