Initial community commit

This commit is contained in:
Jef
2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit 20d28e80a5
16810 changed files with 4640254 additions and 2 deletions

54
Src/nu/Alias.h Normal file
View File

@ -0,0 +1,54 @@
#ifndef NULLSOFT_UTILITY_ALIASH
#define NULLSOFT_UTILITY_ALIASH
template <class ptr_t>
class Alias
{
public:
Alias()
:pointer(0)
{
}
Alias(ptr_t *_pointer) : pointer(_pointer)
{
}
Alias(const Alias<ptr_t> &copy)
{
pointer=copy.pointer;
}
~Alias()
{
pointer=0;
}
ptr_t *operator = (ptr_t *copy)
{
pointer=copy;
return copy;
}
ptr_t *operator ->()
{
return pointer;
}
operator bool()
{
return !!pointer;
}
bool operator == (ptr_t *compare)
{
return pointer == compare;
}
/*
operator ptr_t *()
{
return pointer;
}
*/
private:
ptr_t *pointer;
};
#endif

388
Src/nu/AudioOutput.h Normal file
View File

@ -0,0 +1,388 @@
#pragma once
#include <bfc/platform/types.h>
#include "../Winamp/in2.h"
#include "../Winamp/out.h"
#include "SpillBuffer.h"
#include <assert.h>
/* A class to manage Winamp input plugin audio output
** It handles the following for you:
** * Ensuring that Vis data is sent in chunks of 576
** * Dealing with gapless audio
** (you need to pass in the number of pre-delay and post-delay samples)
** * dealing with the DSP plugin
** * Waiting for CanWrite()
** * dealing with inter-timestamps
** e.g. you pass it >576 samples and it can give you a timestamp based on the divided chunk position
to use, you need to derive from a class that declares
int WaitOrAbort(int time_in_ms);
return 0 on success, non-zero when you need to abort. the return value is passed back through Write()
*/
namespace nu // namespace it since "AudioOutput" isn't a unique enough name
{
template <class wait_t>
class AudioOutput : public wait_t
{
public:
AudioOutput( In_Module *plugin ) : plugin( plugin )
{
Init( nullptr );
}
~AudioOutput()
{
post_buffer.reset();
buffer576.reset();
}
/* Initializes and sets the output plugin pointer
** for most input plugins, the nu::AudioOutput object will be a global,
** so this will be necessary to call at the start of Play thread */
void Init( Out_Module *_output )
{
output = _output;
audio_opened = false;
first_timestamp = 0;
sample_size = 0;
output_latency = 0;
post_buffer.reset();
buffer576.reset();
cut_size = 0;
pre_cut_size = 0;
pre_cut = 0;
decoder_delay = 0;
channels = 0;
sample_rate = 0;
bps = 0;
}
/* sets end-of-stream delay (in samples)
** WITHOUT componesating for post-delay.
** some filetypes (e.g. iTunes MP4) store gapless info this way */
void SetPostDelay(int postSize)
{
if (postSize < 0)
{
postSize = 0;
}
else if (postSize)
{
if (sample_size)
post_buffer.reserve(postSize*sample_size);
cut_size = postSize;
}
}
/* set end-of-stream zero padding, in samples
** compensates for decoder delay */
void SetZeroPadding(int postSize)
{
postSize -= decoder_delay;
if (postSize < 0)
{
postSize = 0;
}
SetPostDelay(postSize);
}
/* set decoder delay, initial zero samples and end-of-stream zero samples, all in one shot
** adjusts zero samples for decoder delay. call SetDelays() if your zero samples are already compensated */
void SetGapless(int decoderDelaySize, int preSize, int postSize)
{
decoder_delay = decoderDelaySize;
SetZeroPadding(postSize);
pre_cut_size = preSize;
pre_cut = pre_cut_size + decoder_delay;
}
/* set decoder delay, initial delay and end-of-stream delay, all in one shot
** WITHOUT componesating for post-delay.
** some filetypes (e.g. iTunes MP4) store gapless info this way */
void SetDelays(int decoderDelaySize, int preSize, int postSize)
{
decoder_delay = decoderDelaySize;
SetPostDelay(postSize);
pre_cut_size = preSize;
pre_cut = pre_cut_size;
}
/* Call on seek */
void Flush(int time_in_ms)
{
if (audio_opened)
{
pre_cut = pre_cut_size;
output->Flush(time_in_ms);
first_timestamp = 0; // once we've flushed, we should be accurate so no need for this anymore
buffer576.clear();
post_buffer.clear();
}
else
first_timestamp = time_in_ms;
}
bool Opened() const
{
return audio_opened;
}
int GetLatency() const
{
return output_latency;
}
int GetFirstTimestamp() const
{
return first_timestamp;
}
/* timestamp is meant to be the first timestamp according to the containing file format
** e.g. many MP4 videos start on 12ms or something, for accurate a/v syncing */
bool Open(int timestamp, int channels, int sample_rate, int bps, int buffer_len_ms=-1, int pre_buffer_ms=-1)
{
if (!audio_opened)
{
int latency = output->Open(sample_rate, channels, bps, buffer_len_ms, pre_buffer_ms);
if (latency < 0)
return false;
plugin->SAVSAInit(latency, sample_rate);
plugin->VSASetInfo(sample_rate, channels);
output->SetVolume(-666);
plugin->SetInfo(-1, sample_rate / 1000, channels, /* TODO? 0*/1);
output_latency = latency;
first_timestamp = timestamp;
sample_size = channels*bps / 8;
this->channels=channels;
this->sample_rate=sample_rate;
this->bps=bps;
SetPostDelay((int)cut_size); // set this again now that we know sample_size, so buffers get allocated correctly
buffer576.reserve(576*sample_size);
audio_opened=true;
}
return audio_opened;
}
void Close()
{
if (audio_opened && output)
{
output->Close();
plugin->SAVSADeInit();
}
output = 0;
first_timestamp = 0;
}
/* outSize is in bytes
** */
int Write(char *out, size_t outSize)
{
if (!out && !outSize)
{
/* --- write contents of buffered audio (end-zero-padding buffer) */
if (!post_buffer.empty())
{
void *buffer = 0;
size_t len = 0;
if (post_buffer.get(&buffer, &len))
{
int ret = Write576((char *)buffer, len);
if (ret != 0)
return ret;
}
}
/* --- write any remaining data in 576 spill buffer (skip vis) */
if (!buffer576.empty())
{
void *buffer = 0;
size_t len = 0;
if (buffer576.get(&buffer, &len))
{
int ret = WriteOutput((char *)buffer, len);
if (ret != 0)
return ret;
}
}
output->Write(0, 0);
return 0;
}
// this probably should not happen but have seen it in some crash reports
if (!sample_size)
return 0;
assert((outSize % sample_size) == 0);
size_t outSamples = outSize / sample_size;
/* --- cut pre samples, if necessary --- */
size_t pre = min(pre_cut, outSamples);
out += pre * sample_size;
outSize -= pre * sample_size;
pre_cut -= pre;
//outSize = outSamples * sample_size;
// do we will have samples to output after cutting pre-delay?
if (!outSize)
return 0;
/* --- if we don't have enough to fully fill the end-zero-padding buffer, go ahead and fill --- */
if (outSize < post_buffer.length())
{
size_t bytes_written = post_buffer.write(out, outSize);
out+=bytes_written;
outSize-=bytes_written;
}
// if we're out of samples, go ahead and bail
if (!outSize)
return 0;
/* --- write contents of buffered audio (end-zero-padding buffer) */
if (!post_buffer.empty())
{
void *buffer = 0;
size_t len = 0;
if (post_buffer.get(&buffer, &len))
{
int ret = Write576((char *)buffer, len);
if (ret != 0)
return ret;
}
}
/* --- make sure we have enough samples left over to fill our post-zero-padding buffer --- */
size_t remainingFill = /*cut_size - */post_buffer.remaining();
int outWrite = max(0, (int)outSize - (int)remainingFill);
/* --- write the output that doesn't end up in the post buffer */
if (outWrite)
{
int ret = Write576(out, outWrite);
if (ret != 0)
return ret;
}
out += outWrite;
outSize -= outWrite;
/* --- write whatever is left over into the end-zero-padding buffer --- */
if (outSize)
{
post_buffer.write(out, outSize);
}
return 0;
}
/* meant to be called after Write(0,0) */
int WaitWhilePlaying()
{
while (output->IsPlaying())
{
int ret = WaitOrAbort(10);
if (ret != 0)
return ret;
output->CanWrite(); // some output drivers need CanWrite
// to be called on a regular basis.
}
return 0;
}
private:
/* helper methods */
int WaitForOutput(int write_size_bytes)
{
while (output->CanWrite() < write_size_bytes)
{
int ret = WaitOrAbort(55);
if (ret != 0)
return ret;
}
return 0;
}
/* writes one chunk (576 samples) to the output plugin, waiting as necessary */
int WriteOutput(char *buffer, size_t len)
{
int ret = WaitForOutput((int)len);
if (ret != 0)
return ret;
// write vis data before so we guarantee 576 samples
if (len == 576*sample_size)
{
plugin->SAAddPCMData(buffer, channels, bps, output->GetWrittenTime() + first_timestamp);
plugin->VSAAddPCMData(buffer, channels, bps, output->GetWrittenTime() + first_timestamp);
}
if (plugin->dsp_isactive())
len = sample_size * plugin->dsp_dosamples((short *)buffer, (int)(len / sample_size), bps, channels, sample_rate);
output->Write(buffer, (int)len);
return 0;
}
/* given a large buffer, writes 576 sample chunks to the vis, dsp and output plugin */
int Write576(char *buffer, size_t out_size)
{
/* if we have some stuff leftover in the 576 sample spill buffer, fill it up */
if (!buffer576.empty())
{
size_t bytes_written = buffer576.write(buffer, out_size);
out_size -= bytes_written;
buffer += bytes_written;
}
if (buffer576.full())
{
void *buffer = 0;
size_t len = 0;
if (buffer576.get(&buffer, &len))
{
int ret = WriteOutput((char *)buffer, len);
if (ret != 0)
return ret;
}
}
while (out_size >= 576*sample_size)
{
int ret = WriteOutput(buffer, 576*sample_size);
if (ret != 0)
return ret;
out_size -= 576*sample_size;
buffer+=576*sample_size;
}
if (out_size)
{
assert(out_size < 576*sample_size);
buffer576.write(buffer, out_size);
}
return 0;
}
private:
Out_Module *output;
In_Module *plugin;
SpillBuffer post_buffer, buffer576;
size_t cut_size;
size_t pre_cut, pre_cut_size, decoder_delay;
bool audio_opened;
int first_timestamp; /* timestamp of the first decoded audio frame, necessary for accurate video syncing */
size_t sample_size; /* size, in bytes, of one sample of audio (channels*bps/8) */
int output_latency; /* as returned from Out_Module::Open() */
int channels, sample_rate, bps;
};
}

189
Src/nu/AutoChar.h Normal file
View File

@ -0,0 +1,189 @@
#ifndef NULLSOFT_AUTOCHARH
#define NULLSOFT_AUTOCHARH
#ifdef WIN32
#include <windows.h>
inline char *AutoCharDupN(const wchar_t *convert, size_t len, UINT codePage = CP_ACP, UINT flags=0)
{
if (!convert)
return 0;
int size = WideCharToMultiByte(codePage, flags, convert, (int)len, 0, 0, NULL, NULL);
if (!size)
return 0;
char *narrow = (char *)malloc((size+1)*sizeof(char));
if ( narrow == 0 )
return 0;
if (!WideCharToMultiByte(codePage, flags, convert, (int)len, narrow, size, NULL, NULL))
{
free(narrow);
narrow=0;
}
else
narrow[size]=0;
return narrow;
}
inline char *AutoCharDup(const wchar_t *convert, UINT codePage = CP_ACP, UINT flags=0)
{
if (!convert)
return 0;
int size = WideCharToMultiByte(codePage, flags, convert, -1, 0, 0, NULL, NULL);
if (!size)
return 0;
char *narrow = (char *)malloc(size*sizeof(char));
if ( narrow == 0 )
return 0;
if (!WideCharToMultiByte(codePage, flags, convert, -1, narrow, size, NULL, NULL))
{
free(narrow);
narrow=0;
}
return narrow;
}
class AutoChar
{
public:
AutoChar(const wchar_t *convert, UINT codePage = CP_ACP, UINT flags=0) : narrow(0)
{
narrow = AutoCharDup(convert, codePage, flags);
}
~AutoChar()
{
free(narrow);
narrow=0;
}
operator const char *()
{
return narrow;
}
operator char *()
{
return narrow;
}
protected:
AutoChar() : narrow(0)
{
}
char *narrow;
};
class AutoCharGrow
{
public:
AutoCharGrow()
{
narrow=0;
size=0;
}
~AutoCharGrow()
{
free(narrow);
}
const char *Convert(const wchar_t *convert, UINT codePage = CP_ACP, UINT flags=0, size_t *cch=0)
{
size_t new_size = WideCharToMultiByte(codePage, flags, convert, -1, 0, 0, NULL, NULL);
if (!new_size)
return 0;
if ((size_t)new_size > size)
{
free(narrow);
narrow = (char *)malloc(new_size * sizeof(char));
if (!narrow)
{
size=0;
return 0;
}
size=(size_t)new_size;
}
if (!WideCharToMultiByte(codePage, flags, convert, -1, narrow, (int)new_size, NULL, NULL))
{
return 0;
}
if (cch)
*cch=new_size-1;
return narrow;
}
protected:
char *narrow;
size_t size;
};
class AutoCharN : public AutoChar
{
public:
AutoCharN(const wchar_t *convert, size_t len, UINT codePage = CP_ACP, UINT flags=0)
{
narrow = AutoCharDupN(convert, len, codePage, flags);
}
};
#else
#include <stdlib.h>
#include <wchar.h>
inline char *AutoCharDup(const wchar_t *convert)
{
if (!convert)
return 0;
size_t size = wcslen(convert)+1;
if (!size)
return 0;
char *narrow = (char *)malloc(size*sizeof(char));
if ( narrow == 0 )
return 0;
if (wcstombs(narrow, convert, size) == (size_t)-1)
{
free(narrow);
narrow=0;
}
return narrow;
}
class AutoChar
{
public:
AutoChar(const wchar_t *convert) : narrow(0)
{
narrow = AutoCharDup(convert);
}
~AutoChar()
{
free(narrow);
narrow=0;
}
operator const char *()
{
return narrow;
}
operator char *()
{
return narrow;
}
private:
char *narrow;
};
#endif
#endif

50
Src/nu/AutoCharFn.h Normal file
View File

@ -0,0 +1,50 @@
#ifndef NULLSOFT_UTILITY_AUTOCHARFN_H
#define NULLSOFT_UTILITY_AUTOCHARFN_H
/* Winamp defines this, but this little block lets us use this thing outside of Winamp */
#ifndef FILENAME_SIZE
#define FILENAME_SIZE (MAX_PATH*4)
#define REMOVE_FILENAME_SIZE
#endif
#include <windows.h>
#include <shlwapi.h>
class AutoCharFn
{
public:
AutoCharFn(const wchar_t *filename)
{
out[0]=0;
if (!filename)
return;
if (PathIsURLW(filename))
{
WideCharToMultiByte(CP_ACP, 0, filename, -1, out, FILENAME_SIZE, NULL, NULL);
return ;
}
BOOL unconvertable = FALSE;
WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, filename, -1, out, FILENAME_SIZE, NULL, &unconvertable);
if (unconvertable)
{
wchar_t temp[MAX_PATH];
if (GetShortPathNameW(filename, temp, MAX_PATH))
WideCharToMultiByte(CP_ACP, 0, temp, -1, out, FILENAME_SIZE, NULL, NULL);
}
}
operator char *() { return out; }
private:
char out[FILENAME_SIZE];
};
#ifdef REMOVE_FILENAME_SIZE
#undef FILENAME_SIZE
#endif
#endif

98
Src/nu/AutoHeader.h Normal file
View File

@ -0,0 +1,98 @@
#ifndef NULLSOFT_AUTOHEADERH
#define NULLSOFT_AUTOHEADERH
#include <windows.h>
#include "AutoChar.h"
/* encodes a UTF-8 string into a buffer, returns a pointer to the end of the string */
inline char *AutoHeader_Encode(const char *in, char *out, size_t len)
{
if (!len)
return 0;
char *dest=out;
const unsigned char *src = (const unsigned char *)in;
while (*src && --len)
{
if ((*src >= 'A' && *src <= 'Z') ||
(*src >= 'a' && *src <= 'z') ||
(*src >= '0' && *src <= '9') || *src == '.' || *src == '-' || *src == '~')
{
*dest++=*src++;
}
else if (len > 2)
{
int i = *src++;
*dest++ = '=';
int b = (i >> 4) & 15;
if (b < 10) *dest++ = '0' + b;
else *dest++ = 'A' + b - 10;
b = i & 15;
if (b < 10) *dest++ = '0' + b;
else *dest++ = 'A' + b - 10;
}
else
break;
}
*dest=0;
return dest;
}
inline char *AutoHeaderDup(const wchar_t *convert)
{
if (!convert)
return 0;
BOOL failed=FALSE;
int n = WideCharToMultiByte(28591, WC_NO_BEST_FIT_CHARS, convert, -1, 0, 0, 0, &failed);
if (n && !failed)
{
char *url = (char *)malloc(n + 1);
WideCharToMultiByte(28591, WC_NO_BEST_FIT_CHARS, convert, -1, url, n+1, 0, 0);
return url;
}
AutoChar utf8(convert, CP_UTF8);
size_t size = strlen(utf8)*3+1; // one byte might get encoded to 3 bytes, so we'll malloc for worst-case
char *url= (char *)malloc((9 /*?utf-8?q?*/+ size + 2 /*?=*/)*sizeof(char));
memcpy(url, "?utf-8?q?", 9);
char *end = AutoHeader_Encode(utf8, url+9, size);
strcpy(end, "?=");
return url;
}
class AutoHeader
{
public:
AutoHeader(const wchar_t *convert) : narrow(0)
{
narrow = AutoHeaderDup(convert);
}
AutoHeader(const AutoHeader &convert) : narrow(0)
{
if (convert.narrow)
narrow = _strdup(convert.narrow);
}
~AutoHeader()
{
free(narrow);
narrow=0;
}
operator const char *()
{
return narrow;
}
operator char *()
{
return narrow;
}
private:
char *narrow;
};
#endif

379
Src/nu/AutoLock.h Normal file
View File

@ -0,0 +1,379 @@
#pragma warning (disable:4786)
#ifndef AUTOLOCKH
#define AUTOLOCKH
#ifdef _WIN32
#include <windows.h>
#else
#include <CoreServices/CoreServices.h>
#endif
/*
NULLSOFT_LOCK_OUTPUT_STATUS turns on/off debugging output
this can be VERY useful if you are trying to find a deadlock
each time the guard is locked or unlocked, it outputs a list of
any threads using the mutex, and their function stack
*/
//#define NULLSOFT_LOCK_OUTPUT_STATS
#ifdef NULLSOFT_LOCK_OUTPUT_STATS
#include <string> // we save each function name as a string
#include <deque> // we make a list of the recursive function stack for each thread
#include <map> // and map
#include <iostream> // we output to std::cerr
#include <windows.h>
/*****
Description:
This class uses scoping to wrap a critical section (lightweight in-process mutex)
The constructor enters the mutex and the destructor leaves it. This allows it to
take advantage of automatic scoping in C++, because C++ automatically calls the destructor
when an object leaves scope.
This is _especially_ useful when you have multiple return paths, since you don't have to
repeat mutex-leaving code.
To use:
Make a LockGuard for a resource you want to protect. The guard is shared, so make it part
of your class, or a global, or whatever. The LockGuard is essentially a "token", equivalent
to your mutex handle or critical section handle.
Make an AutoLock object on the stack to lock. It will unlock automatically when the object
leaves scope.
Note: You'll want to make an object on the stack - don't use a heap object (new/delete)
unless you have weird requirements and know what you are doing.
Example:
class MyClass
{
LockGuard fileGuard;
fstream file;
void DumpSomeData() //
{
AutoLock lock(fileGuard);
file << GetData();
}
void CALLBACK NewData() // potentially called by another thread
{
AutoLock lock(fileGuard)
file << newData;
}
};
Tip: You can use "false scoping" to tweak the mutex lifetime, for example:
void DoStuff()
{
a = GetData();
{ // false scope
AutoLock lock(dataGuard);
DoCalculationsWith(a);
} // mutex will release here
SetData(a);
}
Tip: A common mistake is making a temporary object.
i.e.
CORRECT: AutoLock lock(fileGuard); // an AutoLock object called "lock" is put on the stack
INCORRECT: AutoLock(fileGuard); // An unnamed temporary is created which will be destroyed IMMEDIATELY
*******/
#define MANUALLOCKNAME(x) x
#define LOCKNAME(x) ,x
#define GUARDNAME(x) (x)
namespace Nullsoft
{
namespace Utility
{
/* the token which represents a resource to be locked */
class LockGuard
{
public:
inline LockGuard(char *name = "Unnamed Guard") : lockName(name), owner(0)
{
InitializeCriticalSection(&cerr_cs);
InitializeCriticalSection(&map_cs);
InitializeCriticalSection(&m_cs);
}
inline LockGuard(DWORD spin_count,char *name = "Unnamed Guard") : lockName(name), owner(0)
{
InitializeCriticalSection(&cerr_cs);
InitializeCriticalSection(&map_cs);
InitializeCriticalSection(&m_cs);
}
inline ~LockGuard()
{
DeleteCriticalSection(&cerr_cs);
DeleteCriticalSection(&map_cs);
DeleteCriticalSection(&m_cs);
}
inline void Lock()
{
EnterCriticalSection(&m_cs);
}
inline void Unlock()
{
LeaveCriticalSection(&m_cs);
}
int ThreadCount()
{
EnterCriticalSection(&map_cs);
int count = 0;
for (ThreadMap::iterator itr = threads.begin(); itr != threads.end(); itr++)
{
if (!itr->second.empty())
count++;
}
LeaveCriticalSection(&map_cs);
return count;
}
void Display()
{
EnterCriticalSection(&map_cs);
EnterCriticalSection(&cerr_cs);
if (ThreadCount() > 1 && owner)
{
wchar_t disp[256];
wsprintfW(disp, L"Guard: %S\r\n", lockName.c_str());
OutputDebugStringW(disp);
for (ThreadMap::iterator itr = threads.begin(); itr != threads.end(); itr++)
{
if (itr->second.empty())
continue;
wsprintfW(disp, L" Thread ID: %x", itr->first);
if (owner == itr->first)
wcscat(disp, L" [holding the mutex] *****\r\n");
else
wcscat(disp, L" [blocked]\r\n");
OutputDebugStringW(disp);
for (FunctionStack::iterator fitr = itr->second.begin(); fitr != itr->second.end(); fitr++)
{
wsprintfW(disp, L" %S();\r\n", fitr->c_str());
OutputDebugStringW(disp);
}
}
}
LeaveCriticalSection(&cerr_cs);
LeaveCriticalSection(&map_cs);
}
void In(DWORD thread, char *functionName)
{
EnterCriticalSection(&map_cs);
threads[thread].push_back(functionName);
LeaveCriticalSection(&map_cs);
}
void Out(DWORD thread)
{
EnterCriticalSection(&map_cs);
threads[thread].pop_back();
LeaveCriticalSection(&map_cs);
}
std::string lockName;
CRITICAL_SECTION cerr_cs, map_cs;
typedef std::deque<std::string> FunctionStack; // this typedef reduce ugly c++ <>::<>::<> overkill
typedef std::map<DWORD, FunctionStack> ThreadMap;
ThreadMap threads;
DWORD owner;
private:
CRITICAL_SECTION m_cs;
};
/* an AutoLock locks a resource (represented by a LockGuard) for the duration of its lifetime */
class AutoLock
{
public:
/*
@param functionName The function name which wants the mutex
we pass it in as a char * even though it'll be converted to a std::string
to reduce overhead when OUTPUT_STATS is off
*/
inline AutoLock(LockGuard &_guard, char *functionName = "function name not passed") : guard(&_guard)
{
ManualLock(functionName);
}
inline void ManualLock(char *functionName = "manual lock")
{
thisThread = GetCurrentThreadId();
guard->In(thisThread, functionName);
guard->Display();
guard->Lock();
guard->owner = thisThread;
guard->Display();
}
inline void ManualUnlock()
{
guard->Display();
guard->Unlock();
InterlockedCompareExchange((LONG volatile *)&guard->owner, 0, (LONG)thisThread);
/* above line is functionally equivalent to:
if (guard->owner == thisThread)
guard->owner=0;
*/
guard->Out(thisThread);
guard->Display();
}
inline ~AutoLock()
{
ManualUnlock();
}
LockGuard *guard;
DWORD thisThread;
};
}
}
#else
#define MANUALLOCKNAME(x)
#define LOCKNAME(x)
#define GUARDNAME(x)
namespace Nullsoft
{
namespace Utility
{
/* the token which represents a resource to be locked */
class LockGuard
{
public:
inline LockGuard(char *guardName = "")
{
#ifdef _WIN32
InitializeCriticalSection(&m_cs);
#else
MPCreateCriticalRegion(&cr);
#endif
}
#if _WIN32_WINNT >= 0x403
inline LockGuard(DWORD spin_count, char *guardName = "")
{
if (spin_count)
InitializeCriticalSectionAndSpinCount(&m_cs, spin_count);
else
InitializeCriticalSection(&m_cs);
}
#endif
inline ~LockGuard()
{
#ifdef _WIN32
DeleteCriticalSection(&m_cs);
#else
MPDeleteCriticalRegion(cr);
#endif
}
inline void Lock()
{
#ifdef _WIN32
EnterCriticalSection(&m_cs);
#else
MPEnterCriticalRegion(cr, kDurationForever);
#endif
}
inline void Unlock()
{
#ifdef _WIN32
LeaveCriticalSection(&m_cs);
#else
MPExitCriticalRegion(cr);
#endif
}
private:
#ifdef _WIN32
CRITICAL_SECTION m_cs;
#else
MPCriticalRegionID cr;
#endif
LockGuard(const LockGuard &copy) { } // make copy constructor private so it can't be used
LockGuard &operator =(const LockGuard &copy) {} // same with operator=
};
/* an AutoLock locks a resource (represented by a LockGuard) for the duration of its lifetime */
class AutoLock
{
public:
inline AutoLock(LockGuard &_guard) : guard(&_guard)
{
guard->Lock();
}
inline AutoLock(LockGuard *_guard) : guard(_guard)
{
guard->Lock();
}
inline void ManualLock()
{
guard->Lock();
}
inline void ManualUnlock()
{
guard->Unlock();
}
inline ~AutoLock()
{
guard->Unlock();
}
LockGuard *guard;
};
// will lock anything that implements Lock() and Unlock()
template <class LockGuard_t>
class AutoLockT
{
public:
inline AutoLockT(LockGuard_t &_guard) : guard(&_guard)
{
guard->Lock();
}
inline AutoLockT(LockGuard_t *_guard) : guard(_guard)
{
guard->Lock();
}
inline void ManualLock()
{
guard->Lock();
}
inline void ManualUnlock()
{
guard->Unlock();
}
inline ~AutoLockT()
{
guard->Unlock();
}
LockGuard_t *guard;
};
}
}
#endif
#endif

141
Src/nu/AutoUrl.h Normal file
View File

@ -0,0 +1,141 @@
#ifndef NULLSOFT_AUTOURLH
#define NULLSOFT_AUTOURLH
#include <windows.h>
#include "AutoChar.h"
/* benski> i'm sure there's a nice optimized way of doing this, but I need to implement it _right now_ */
#define HEXCASE(d) case 0x##d: return #@d
inline char quickhex(unsigned char in)
{
switch (in)
{
HEXCASE(0);
HEXCASE(1);
HEXCASE(2);
HEXCASE(3);
HEXCASE(4);
HEXCASE(5);
HEXCASE(6);
HEXCASE(7);
HEXCASE(8);
HEXCASE(9);
HEXCASE(A);
HEXCASE(B);
HEXCASE(C);
HEXCASE(D);
HEXCASE(E);
HEXCASE(F);
}
return 0;
}
/* encodes a UTF-8 string into a buffer */
inline void AutoUrl_Encode(const char *in, char *out, size_t len)
{
if (!len)
return;
char *dest=out;
const unsigned char *src = (const unsigned char *)in;
while (*src && --len)
{
if ((*src >= 'A' && *src <= 'Z') ||
(*src >= 'a' && *src <= 'z') ||
(*src >= '0' && *src <= '9') || *src == '.' || *src == '_' || *src == '-' || *src == '~')
{
*dest++=*src++;
}
else if (len > 2)
{
int i = *src++;
*dest++ = '%';
int b = (i >> 4) & 15;
if (b < 10) *dest++ = (char)('0' + b);
else *dest++ = (char)('A' + b - 10);
b = i & 15;
if (b < 10) *dest++ = (char)('0' + b);
else *dest++ = (char)('A' + b - 10);
}
else
break;
}
*dest=0;
}
inline char *AutoUrlDupN(const wchar_t *convert, size_t len)
{
if (!convert)
return 0;
AutoCharN utf8(convert, len, CP_UTF8);
size_t size = strlen(utf8)*3+1; // one byte might get encoded to 3 bytes, so we'll malloc for worst-case
char *url= (char *)malloc(size*sizeof(char));
AutoUrl_Encode(utf8, url, size);
return url;
}
inline char *AutoUrlDup(const wchar_t *convert)
{
if (!convert)
return 0;
AutoChar utf8(convert, CP_UTF8);
size_t size = strlen(utf8)*3+1; // one byte might get encoded to 3 bytes, so we'll malloc for worst-case
char *url= (char *)malloc(size*sizeof(char));
AutoUrl_Encode(utf8, url, size);
return url;
}
inline char *AutoUrlDup(const char *utf8)
{
if (!utf8)
return 0;
size_t size = strlen(utf8)*3+1; // one byte might get encoded to 3 bytes, so we'll malloc for worst-case
char *url= (char *)malloc(size*sizeof(char));
AutoUrl_Encode(utf8, url, size);
return url;
}
class AutoUrl
{
public:
AutoUrl(const wchar_t *convert) : narrow(0)
{
narrow = AutoUrlDup(convert);
}
AutoUrl(const wchar_t *convert, size_t len) : narrow(0)
{
narrow = AutoUrlDupN(convert, len);
}
AutoUrl(const char *convert) : narrow(0)
{
narrow = AutoUrlDup(convert);
}
AutoUrl(const AutoUrl &convert) : narrow(0)
{
if (convert.narrow)
narrow = _strdup(convert.narrow);
}
~AutoUrl()
{
free(narrow);
narrow=0;
}
operator const char *()
{
return narrow;
}
operator char *()
{
return narrow;
}
private:
char *narrow;
};
#endif

99
Src/nu/AutoWide.h Normal file
View File

@ -0,0 +1,99 @@
#ifndef AUTOWIDEH
#define AUTOWIDEH
#ifdef WIN32
#include <windows.h>
inline wchar_t *AutoWideDup( const char *convert, UINT codePage = CP_ACP )
{
if ( !convert )
return 0;
int size = MultiByteToWideChar( codePage, 0, convert, -1, 0, 0 );
if ( !size )
return 0;
wchar_t *wide = (wchar_t *)malloc( size * sizeof( wchar_t ) );
if ( !MultiByteToWideChar( codePage, 0, convert, -1, wide, size ) )
{
free( wide );
wide = 0;
}
return wide;
}
class AutoWide
{
public:
AutoWide( const char *convert, UINT codePage = CP_ACP )
{
wide = AutoWideDup( convert, codePage );
}
~AutoWide()
{
free( wide );
wide = 0;
}
operator wchar_t *( )
{
return wide;
}
operator const wchar_t *( )
{
return wide;
}
operator bool()
{
return !!wide;
}
private:
wchar_t *wide = 0;
};
#elif defined(__APPLE__)
#include <string.h>
inline wchar_t *AutoWideDup( const char *convert )
{
if ( !convert )
return 0;
int size = strlen( convert ) + 1;
if ( !size )
return 0;
wchar_t *wide = (wchar_t *)malloc( size * sizeof( wchar_t ) );
if ( mbstowcs( wide, convert, size ) == (size_t)-1 )
{
free( wide );
wide = 0;
}
return wide;
}
class AutoWide
{
public:
AutoWide( const char *convert )
{
wide = AutoWideDup( convert );
}
~AutoWide()
{
free( wide );
wide = 0;
}
operator wchar_t *( )
{
return wide;
}
private:
wchar_t *wide = 0;
};
#endif
#endif

191
Src/nu/AutoWideFn.h Normal file
View File

@ -0,0 +1,191 @@
#ifndef NULLSOFT_UTILITY_AUTOWIDEFN_H
#define NULLSOFT_UTILITY_AUTOWIDEFN_H
/* Winamp defines this, but this little block lets us use this thing outside of Winamp */
#ifndef FILENAME_SIZE
#define FILENAME_SIZE (MAX_PATH*4)
#define REMOVE_FILENAME_SIZE
#endif
#include <windows.h>
#include "AutoWide.h"
#include "AutoChar.h"
#include <shlwapi.h>
/*
Tries to find a filename that underwent a destructive Unicode-to-ANSI conversion
*/
#pragma warning(push)
#pragma warning(disable:4995)
class AutoWideFn
{
public:
AutoWideFn(const char *narrowFn)
{
wideFn[0]=0;
if (!narrowFn)
return;
CreateFile_HACK(narrowFn, wideFn);
}
operator wchar_t *() { return wideFn; }
bool unicode_find(/*char *path,*/ char *pattern, wchar_t *out, UINT out_ptr, bool dir, HANDLE *f)
{
WIN32_FIND_DATAW fd = {0};
if (*f == INVALID_HANDLE_VALUE)
{
lstrcpyW(out + out_ptr, L"*");
*f = FindFirstFileW(out, &fd);
out[out_ptr] = 0;
if (*f == INVALID_HANDLE_VALUE) return 0;
}
else
{
if (!FindNextFileW(*f, &fd))
{
FindClose(*f);
*f = INVALID_HANDLE_VALUE;
return 0;
}
}
if (*f == INVALID_HANDLE_VALUE) return 0;
char temp[MAX_PATH*2 + 1] = {0};
do
{
if (dir)
{
if (!(fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)) continue;
}
else
{
if (fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) continue;
}
WideCharToMultiByte(CP_ACP, 0, fd.cFileName, -1, temp, sizeof(temp), 0, 0);
//wcstombs(temp,fd.cFileName,sizeof(temp));
if (!_stricmp(temp, pattern))
{ //found
lstrcpyW(out + out_ptr, fd.cFileName);
return 1;
}
WideCharToMultiByte(CP_ACP, 0, fd.cAlternateFileName, -1, temp, sizeof(temp), 0, 0);
if (!_stricmp(temp, pattern))
{ //found
lstrcpyW(out + out_ptr, fd.cFileName);
return 1;
}
}
while (FindNextFileW(*f, &fd));
FindClose(*f);
*f = INVALID_HANDLE_VALUE;
return 0;
}
bool unicode_open_recur(char *path, char *ptr, wchar_t *out, UINT out_ptr)
{
char * next = strchr(ptr, '\\');
if (next)
{ //dig another dir
HANDLE f = INVALID_HANDLE_VALUE;
bool found;
do
{
next[0] = 0;
char * zz = _strdup(ptr);
char bk = *ptr;
*ptr = 0;
found = unicode_find(/*path,*/ zz, out, out_ptr, 1, &f);
free(zz);
*ptr = bk;
next[0] = '\\';
if (found)
{
UINT op_bk = out_ptr;
while (out_ptr < FILENAME_SIZE && out[out_ptr]) out_ptr++;
out[out_ptr++] = '\\';
if (unicode_open_recur(path, next + 1, out, out_ptr))
{
if (f != INVALID_HANDLE_VALUE) FindClose(f);
return 1;
}
out_ptr = op_bk;
out[out_ptr] = 0;
}
} while (found);
}
else
{ //final dir
HANDLE f = INVALID_HANDLE_VALUE;
char * zz = _strdup(ptr);
char bk = *ptr;
*ptr = 0;
bool found = unicode_find(/*path,*/ zz, out, out_ptr, 0, &f);
if (!found)
{
if (f != INVALID_HANDLE_VALUE)
{
FindClose(f);
f = INVALID_HANDLE_VALUE;
}
found = unicode_find(/*path,*/ zz, out, out_ptr, 1, &f);
}
free(zz);
*ptr = bk;
if (f != INVALID_HANDLE_VALUE) FindClose(f);
return found;
}
return 0;
}
void CreateFile_HACK(const char * path, wchar_t out[FILENAME_SIZE])
{
MultiByteToWideChar(CP_ACP, 0, path, -1, out, FILENAME_SIZE);
if (PathIsURLW(out))
return ;
if (!StrChrW(out, L'?'))
return ; // no unconvertables? Great!
// if (PathFileExistsW(out))
// return ; // no unconvertables? Great!
bool found = false;
memset(out, 0, FILENAME_SIZE * sizeof(wchar_t));
char * _p = _strdup(path);
char * t = strchr(_p, '\\');
if (t)
{
char bk = t[1];
t[1] = 0;
UINT o = MultiByteToWideChar(CP_ACP, 0, _p, -1, out, FILENAME_SIZE);
o--;
t[1] = bk;
found = unicode_open_recur(_p, t + 1, out, o);
}
else
found = unicode_open_recur(_p, _p, out, 0);
free(_p);
if (!found)
MultiByteToWideChar(CP_ACP, 0, path, -1, out, FILENAME_SIZE);
}
private:
wchar_t wideFn[FILENAME_SIZE];
};
#pragma warning(pop)
#ifdef REMOVE_FILENAME_SIZE
#undef FILENAME_SIZE
#endif
#endif

45
Src/nu/CCVersion.cpp Normal file
View File

@ -0,0 +1,45 @@
#include "CCVersion.h"
#include <windows.h>
#include <commctrl.h>
#include <shlwapi.h>
DWORD GetCommCtrlDllVersion(LPCTSTR lpszDllName)
{
DWORD dwVersion = 0;
/* In theory, we should limit the search path to only the system folder
at this point, I don't care */
HINSTANCE hinstDll = LoadLibraryW(lpszDllName);
if(hinstDll)
{
DLLGETVERSIONPROC pDllGetVersion;
pDllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hinstDll,
"DllGetVersion");
/* Because some DLLs might not implement this function, you
must test for it explicitly. Depending on the particular
DLL, the lack of a DllGetVersion function can be a useful
indicator of the version. */
if(pDllGetVersion)
{
DLLVERSIONINFO dvi;
HRESULT hr;
ZeroMemory(&dvi, sizeof(dvi));
dvi.cbSize = sizeof(dvi);
hr = (*pDllGetVersion)(&dvi);
if(SUCCEEDED(hr))
{
dwVersion = PACKVERSION(dvi.dwMajorVersion, dvi.dwMinorVersion);
}
}
FreeLibrary(hinstDll);
}
return dwVersion;
}

6
Src/nu/CCVersion.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef NULLSOFT_CCVERSIONH
#define NULLSOFT_CCVERSIONH
#include <windows.h>
DWORD GetCommCtrlDllVersion(LPCTSTR);
#define PACKVERSION(major,minor) MAKELONG(minor,major)
#endif

21
Src/nu/CGlobalAtom.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <windows.h>
class CGlobalAtom
{
public:
CGlobalAtom(LPCWSTR name)
{
prop = GlobalAddAtomW(name);
}
~CGlobalAtom()
{
if (prop)
GlobalDeleteAtom(prop);
prop=0;
}
operator ATOM() { return prop; }
operator LPCWSTR() { return (LPCWSTR) prop; }
private:
ATOM prop;
};

33
Src/nu/ChildSizer.cpp Normal file
View File

@ -0,0 +1,33 @@
#include "ChildSizer.h"
ChildSizer childSizer;
ChildSizer::ChildSizer()
: childresize_init(0),
childresize_resize(0)
{}
void ChildSizer::Init(HWND dlg, ChildWndResizeItem *list, int count)
{
if (!childresize_init)
childresize_init = (ChildResizeFunc)mediaLibrary.GetWADLGFunc(32);
childresize_init(dlg, list, count);
}
void ChildSizer::Resize(HWND dlg, ChildWndResizeItem *list, int count)
{
if (!childresize_resize)
childresize_resize = (ChildResizeFunc)mediaLibrary.GetWADLGFunc(33);
childresize_resize(dlg, list, count);
}
ChildWndResizeItem *ChildSizer::Lookup(int id, ChildWndResizeItem *list, size_t numElements)
{
for (size_t i=0;i!=numElements;i++)
{
if (list[i].id == id)
return &list[i];
}
return 0;
}

38
Src/nu/ChildSizer.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef NULLSOFT_CHILDSIZERH
#define NULLSOFT_CHILDSIZERH
#include "MediaLibraryInterface.h"
typedef struct
{
int id;
int type; // 0xLTRB
RECT rinfo;
}
ChildWndResizeItem;
enum
{
Stationary = 0x0000,
ResizeBottom = 0x0001,
ResizeRight = 0x0010,
ResizeTop = 0x0100,
ResizeLeft=0x1000,
DockToBottom = 0x0101,
DockToBottomRight = 0x1111,
};
class ChildSizer
{
typedef void (*ChildResizeFunc)(HWND, ChildWndResizeItem*, int);
public:
ChildSizer();
void Init(HWND dlg, ChildWndResizeItem *list, int count);
void Resize(HWND dlg, ChildWndResizeItem *list, int count);
static ChildWndResizeItem *Lookup(int id, ChildWndResizeItem *list, size_t numElements);
ChildResizeFunc childresize_init, childresize_resize;
};
extern ChildSizer childSizer;
#endif

105
Src/nu/ComboBox.h Normal file
View File

@ -0,0 +1,105 @@
#ifndef NULLSOFT_UTILITY_COMBOBOX_H
#define NULLSOFT_UTILITY_COMBOBOX_H
#include <windows.h>
class ComboBox
{
public:
ComboBox(HWND hwndDlg, int id)
{
cbHwnd = GetDlgItem(hwndDlg, id);
}
ComboBox(HWND control)
{
cbHwnd = control;
}
operator HWND()
{
return cbHwnd;
}
LRESULT AddString(const wchar_t *string)
{
return SendMessageW(cbHwnd, CB_ADDSTRING, 0, (LPARAM)string);
}
LRESULT AddString(const wchar_t *string, LPARAM data)
{
LRESULT index = SendMessageW(cbHwnd, CB_ADDSTRING, 0, (LPARAM)string);
SendMessage(cbHwnd, CB_SETITEMDATA, index, data);
return index;
}
LRESULT AddString(const char *string)
{
return SendMessageA(cbHwnd, CB_ADDSTRING, 0, (LPARAM)string);
}
void SetItemData(LPARAM index, LPARAM data)
{
SendMessage(cbHwnd, CB_SETITEMDATA, index, data);
}
int GetCount()
{
return (int)SendMessage(cbHwnd, CB_GETCOUNT, 0, 0);
}
LRESULT GetItemData(LPARAM index)
{
return SendMessage(cbHwnd, CB_GETITEMDATA, index, 0);
}
void Clear()
{
SendMessage(cbHwnd, CB_RESETCONTENT, 0, 0);
}
void SelectItem(LPARAM index)
{
SendMessage(cbHwnd, CB_SETCURSEL, index, 0);
}
LPARAM GetSelection()
{
return SendMessage(cbHwnd, CB_GETCURSEL, 0, 0);
}
LRESULT SelectString(const wchar_t *str)
{
return SendMessageW(cbHwnd, CB_SELECTSTRING, (WPARAM)-1, (LPARAM)str);
}
LRESULT GetTextLen(int index)
{
return SendMessageW(cbHwnd, CB_GETLBTEXTLEN, (WPARAM)index, 0);
}
void GetText(int index, wchar_t *str)
{
SendMessageW(cbHwnd, CB_GETLBTEXT, (WPARAM)index, (LPARAM)str);
}
#if (_WIN32_WINNT >= 0x0501)
void SetCueBanner(const wchar_t *str)
{
SendMessageW(cbHwnd, CB_SETCUEBANNER, 0, (LPARAM)str);
//CB_SETCUEBANNER;
}
#endif
void SetEditText(const wchar_t *str)
{
SendMessageW(cbHwnd, WM_SETTEXT, 0, (LPARAM)str);
}
unsigned int GetEditText(wchar_t *str, unsigned int cch)
{
return (unsigned int)SendMessageW(cbHwnd, WM_GETTEXT, (WPARAM)cch, (LPARAM)str);
}
HWND cbHwnd;
};
#endif

188
Src/nu/Config.h Normal file
View File

@ -0,0 +1,188 @@
#ifndef NULLSOFT_UTILITY_CONFIGH
#define NULLSOFT_UTILITY_CONFIGH
#include <string>
#include <map>
#include <windows.h>
typedef std::wstring tstring;
namespace Nullsoft
{
namespace Utility
{
class ConfigItemBase
{
public:
ConfigItemBase(const wchar_t *_appName, const wchar_t * _fileName, LPCTSTR _keyName)
: appName(nullptr), fileName(nullptr), keyName(nullptr)
{
appName = _appName;
fileName = _fileName;
keyName = _keyName;
}
~ConfigItemBase()
{
}
const wchar_t *appName;
const wchar_t *fileName;
const wchar_t *keyName;
};
template <class config_t>
class ConfigItem : public ConfigItemBase
{
public:
ConfigItem(const wchar_t *_appName, const wchar_t * _fileName, LPCTSTR _keyName) : ConfigItemBase(_appName, _fileName, _keyName)
{
}
~ConfigItem() {}
void operator =(config_t input)
{
WritePrivateProfileStruct(appName,
keyName,
(void *) & input,
sizeof(input),
fileName);
}
operator config_t()
{
config_t temp;
memset(&temp, 0, sizeof(temp));
GetPrivateProfileStruct(appName,
keyName,
&temp,
sizeof(temp),
fileName);
return temp;
}
};
template <>
class ConfigItem<TCHAR *> : public ConfigItemBase
{
public:
ConfigItem(const wchar_t *_appName, const wchar_t * _fileName, LPCTSTR _keyName) : ConfigItemBase(_appName, _fileName, _keyName)
{
}
~ConfigItem(){}
void operator =(LPCTSTR input)
{
WritePrivateProfileString(appName,
keyName,
input,
fileName);
}
void GetString(LPTSTR str, size_t len)
{
GetPrivateProfileString(appName, keyName, TEXT(""), str, len, fileName);
}
};
template <>
class ConfigItem<int> : public ConfigItemBase
{
public:
ConfigItem(const wchar_t *_appName, const wchar_t * _fileName, LPCTSTR _keyName) : ConfigItemBase(_appName, _fileName, _keyName), def(0)
{
}
~ConfigItem() {}
void operator =(int input)
{
TCHAR tmp[(sizeof(int) / 2) * 5 + 1]; // enough room to hold for 16,32 or 64 bit ints, plus null terminator
wsprintf(tmp, TEXT("%d"), input);
WritePrivateProfileString(appName,
keyName,
tmp,
fileName);
}
operator int ()
{
return GetPrivateProfileInt(appName, keyName, def, fileName);
}
void SetDefault(int _def)
{
def = _def;
}
int def;
};
class Config
{
public:
Config() : appName(nullptr),fileName(nullptr)
{
}
~Config()
{
if (appName != nullptr)
{
free(appName);
appName = nullptr;
}
if (fileName != nullptr)
{
free(fileName);
fileName = nullptr;
}
}
void SetFile(LPCTSTR iniFile, LPCTSTR _appName)
{
if (appName != nullptr)
{
free(appName);
appName = nullptr;
}
if (fileName != nullptr)
{
free(fileName);
fileName = nullptr;
}
appName = _wcsdup(_appName);
fileName = _wcsdup(iniFile);
}
ConfigItem<int> cfg_int(LPCTSTR keyName, int def)
{
ConfigItem<int> item(appName, fileName, keyName);
item.SetDefault(def);
return item;
}
ConfigItem<TCHAR *> cfg_str(LPCTSTR keyName)
{
return ConfigItem<TCHAR *>(appName, fileName, keyName);
}
ConfigItem<GUID> cfg_guid(LPCTSTR keyName)
{
return ConfigItem<GUID>(appName, fileName, keyName);
}
ConfigItem<__int64> cfg_int64(LPCTSTR keyName)
{
ConfigItem<__int64> item(appName, fileName, keyName);
return item;
}
wchar_t *appName, *fileName;
};
}
}
#endif

373
Src/nu/ConfigCOM.cpp Normal file
View File

@ -0,0 +1,373 @@
#include "ConfigCOM.h"
#include "AutoChar.h"
#include "../Winamp/JSAPI.h"
#include <shlwapi.h>
#include <strsafe.h>
#define CSTR_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
ConfigCOM::ConfigCOM()
: ref(1), index(31337), pathA(NULL), nameA(NULL)
{
}
ConfigCOM::~ConfigCOM()
{
ConfigMap::iterator config_it;
for(config_it = config_map.begin(); config_it != config_map.end(); config_it++)
{
free(config_it->second);
}
config_map.clear();
if (NULL != pathA) free(pathA);
if (NULL != nameA) free(nameA);
}
HRESULT ConfigCOM::CreateInstanceW(const wchar_t *pszName, const char *pszPath, ConfigCOM **config)
{
if (NULL == config) return E_POINTER;
*config = NULL;
if (NULL == pszName)
return E_INVALIDARG;
*config = new ConfigCOM();
if (NULL == *config) return E_OUTOFMEMORY;
char *buffer = NULL;
int cbBuffer = WideCharToMultiByte(CP_UTF8, 0, pszName, -1, 0, 0, NULL, NULL);
if (0 != cbBuffer)
{
buffer = (char*)calloc(cbBuffer, sizeof(char));
if (NULL != buffer && 0 == WideCharToMultiByte(CP_UTF8, 0, pszName, -1, buffer, cbBuffer, NULL, NULL))
{
free(buffer);
buffer = NULL;
}
}
if (NULL == buffer)
{
(*config)->Release();
*config = NULL;
return E_OUTOFMEMORY;
}
(*config)->nameA = buffer;
if (NULL != pszPath)
(*config)->SetPathA(pszPath);
return S_OK;
}
HRESULT ConfigCOM::CreateInstanceA(const char *pszName, const char *pszPath, ConfigCOM **config)
{
if (NULL == config) return E_POINTER;
*config = NULL;
if (NULL == pszName)
return E_INVALIDARG;
*config = new ConfigCOM();
if (NULL == *config) return E_OUTOFMEMORY;
char *nameA = _strdup(pszName);
if (NULL == nameA)
{
(*config)->Release();
*config = NULL;
return E_OUTOFMEMORY;
}
(*config)->nameA = nameA;
if (NULL != pszPath)
(*config)->SetPathA(pszPath);
return S_OK;
}
STDMETHODIMP_(ULONG) ConfigCOM::AddRef(void)
{
return InterlockedIncrement((LONG*)&ref);
}
STDMETHODIMP_(ULONG) ConfigCOM::Release(void)
{
if (0 == ref)
return ref;
LONG r = InterlockedDecrement((LONG*)&ref);
if (0 == r)
delete(this);
return r;
}
STDMETHODIMP ConfigCOM::QueryInterface(REFIID riid, PVOID *ppvObject)
{
if (!ppvObject)
return E_POINTER;
else if (IsEqualIID(riid, IID_IDispatch))
*ppvObject = (IDispatch *)this;
else if (IsEqualIID(riid, IID_IUnknown))
*ppvObject = this;
else
{
*ppvObject = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
STDMETHODIMP ConfigCOM::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)
{
UNREFERENCED_PARAMETER(riid);
bool unknowns = false;
for (unsigned int i = 0;i != cNames;i++)
{
bool found = false;
AutoChar item(rgszNames[i], CP_UTF8);
ConfigMap::iterator config_it;
for(config_it = config_map.begin();config_it != config_map.end(); config_it++)
{
if (config_it->second &&
CSTR_EQUAL == CompareStringA(lcid, NORM_IGNORECASE, config_it->second, -1, item, -1))
{
found = true;
rgdispid[i] = config_it->first;
}
}
if (!found) // if they reference a config item, well by golly they want that config item.
{
config_map[++index] = _strdup(item);
rgdispid[i] = index;
}
}
if (unknowns)
return DISP_E_UNKNOWNNAME;
return S_OK;
}
STDMETHODIMP ConfigCOM::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
{
UNREFERENCED_PARAMETER(itinfo);
UNREFERENCED_PARAMETER(lcid);
UNREFERENCED_PARAMETER(pptinfo);
return E_NOTIMPL;
}
STDMETHODIMP ConfigCOM::GetTypeInfoCount(unsigned int FAR * pctinfo)
{
UNREFERENCED_PARAMETER(pctinfo);
return E_NOTIMPL;
}
STDMETHODIMP ConfigCOM::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr)
{
UNREFERENCED_PARAMETER(riid);
UNREFERENCED_PARAMETER(lcid);
UNREFERENCED_PARAMETER(pexecinfo);
ConfigMap::iterator config_it = config_map.find(dispid);
if (config_it == config_map.end())
return DISP_E_MEMBERNOTFOUND;
if (0 != (DISPATCH_PROPERTYPUT & wFlags))
{
VARIANTARG *varArg;
JSAPI_VERIFY_PARAMCOUNT(pdispparams, 1);
varArg = &pdispparams->rgvarg[0];
switch(V_VT(varArg))
{
case VT_BSTR:
WriteString(config_it->second, V_BSTR(varArg));
break;
case VT_BOOL:
{
BOOL boolVal;
switch(V_BOOL(varArg))
{
case VARIANT_TRUE:
boolVal = TRUE;
break;
case VARIANT_FALSE:
boolVal = FALSE;
break;
default:
*puArgErr = 0;
return DISP_E_BADVARTYPE;
}
WriteBool(config_it->second, boolVal);
}
break;
case VT_I4:
WriteLong(config_it->second, V_I4(varArg));
break;
default:
*puArgErr = 0;
return DISP_E_TYPEMISMATCH;
}
return S_OK;
}
if (0 != (DISPATCH_PROPERTYGET & wFlags))
{
JSAPI_VERIFY_PARAMCOUNT(pdispparams, 0);
if (NULL == pvarResult)
return DISP_E_PARAMNOTOPTIONAL;
VariantInit(pvarResult);
BSTR tag = ReadBSTR(config_it->second, NULL);
if (NULL != tag)
{
V_VT(pvarResult) = VT_BSTR;
V_BSTR(pvarResult) = tag;
}
else
{
V_VT(pvarResult) = VT_NULL;
}
return S_OK;
}
return ResultFromScode(E_INVALIDARG);
}
BOOL ConfigCOM::WriteStringAnsi(const char *key, const char* string)
{
return WritePrivateProfileStringA(nameA, key, string, pathA);
}
BOOL ConfigCOM::WriteString(const char *key, const wchar_t *string)
{
AutoChar buffer(string, CP_UTF8);
return WriteStringAnsi(key, buffer);
}
BOOL ConfigCOM::WriteBool(const char *key, BOOL value)
{
return WriteStringAnsi(key, (FALSE != value) ? "true" : "false");
}
BOOL ConfigCOM::WriteLong(const char *key, long value)
{
char item[64] = {0};
if (FAILED(StringCchPrintfA(item, ARRAYSIZE(item), "%ld", value)))
return FALSE;
return WriteStringAnsi(key, item);
}
DWORD ConfigCOM::ReadString(const char *key, const char *defaultVal, char *buffer, int bufferMax)
{
return GetPrivateProfileStringA(nameA, key, defaultVal, buffer, bufferMax, pathA);
}
LONG ConfigCOM::ReadLong(const char *key, long defaultVal)
{
return GetPrivateProfileIntA(nameA, key, defaultVal, pathA);
}
BOOL ConfigCOM::ReadBool(const char *key, BOOL defaultVal)
{
char szBuffer[32] = {0};
INT cchLen = ReadString(key, NULL, szBuffer, ARRAYSIZE(szBuffer));
if (0 == cchLen) return defaultVal;
if (1 == cchLen)
{
switch(*szBuffer)
{
case '0':
case 'n':
case 'f':
return FALSE;
case '1':
case 'y':
case 't':
return TRUE;
}
}
else
{
if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, "yes", -1, szBuffer, cchLen) ||
CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, "true", -1, szBuffer, cchLen))
{
return TRUE;
}
if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, "no", -1, szBuffer, cchLen) ||
CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, "false", -1, szBuffer, cchLen))
{
return FALSE;
}
}
INT v;
if (FALSE != StrToIntExA(szBuffer, STIF_SUPPORT_HEX, &v))
return (0 != v);
return defaultVal;
}
BSTR ConfigCOM::ReadBSTR(const char *key, const wchar_t *defaultVal)
{
char szBuffer[16384] = {0};
ReadString(key, "__emptee__", szBuffer, ARRAYSIZE(szBuffer));
if (CSTR_EQUAL != CompareStringA(CSTR_INVARIANT, 0, szBuffer, -1, "__emptee__", -1))
{
int size = MultiByteToWideChar(CP_UTF8, 0, szBuffer, -1, 0,0);
if (0 != size)
{
BSTR result;
result = SysAllocStringLen(0, size-1);
if (NULL == result) return NULL;
if (0 != MultiByteToWideChar(CP_UTF8, 0, szBuffer, -1, result, size))
return result;
SysFreeString(result);
}
}
return (NULL != defaultVal) ? SysAllocString(defaultVal) : NULL;
}
void ConfigCOM::SetPathA(const char *pszPath)
{
if (NULL != pathA)
{
free(pathA);
pathA = NULL;
}
if (NULL == pszPath)
{
pathA = NULL;
return;
}
pathA = _strdup(pszPath);
}
BOOL ConfigCOM::IsEqual(const char *pszName)
{
if (NULL == pszName) return FALSE;
return (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, 0, nameA, -1, pszName, -1));
}

51
Src/nu/ConfigCOM.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef NULLSOFT_NU_CONFIGCOMH
#define NULLSOFT_NU_CONFIGCOMH
#include <ocidl.h>
#include <map>
class ConfigCOM : public IDispatch
{
protected:
ConfigCOM();
~ConfigCOM();
public:
static HRESULT CreateInstanceW(const wchar_t *pszName, const char *pszPath, ConfigCOM **config);
static HRESULT CreateInstanceA(const char *pszName, const char *pszPath, ConfigCOM **config);
public:
/* IUnknown */
STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject);
STDMETHOD_(ULONG, AddRef)(void);
STDMETHOD_(ULONG, Release)(void);
/* IDispatch */
STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid);
STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo);
STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo);
STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr);
void SetPathA(const char *pszPath);
BOOL IsEqual(const char *pszName);
BOOL WriteStringAnsi(const char *key, const char *string);
BOOL WriteString(const char *key, const wchar_t *string);
BOOL WriteBool(const char *key, BOOL value);
BOOL WriteLong(const char *key, long value);
DWORD ReadString(const char *key, const char *defaultVal, char *buffer, int bufferMax);
LONG ReadLong(const char *key, long defaultVal);
BOOL ReadBool(const char *key, BOOL defaultVal);
BSTR ReadBSTR(const char *key, const wchar_t *defaultVal);
private:
typedef std::map<long, char*> ConfigMap;
ConfigMap config_map;
long index;
char *pathA;
char *nameA;
LONG ref;
};
#endif

8
Src/nu/DialogSkinner.cpp Normal file
View File

@ -0,0 +1,8 @@
#include "DialogSkinner.h"
DialogSkinner dialogSkinner;
COLORREF GetHTMLColor(int color)
{
return ( ( color >> 16 ) & 0xff | ( color & 0xff00 ) | ( ( color << 16 ) & 0xff0000 ) );
}

78
Src/nu/DialogSkinner.h Normal file
View File

@ -0,0 +1,78 @@
#ifndef DIALOGSKINNERH
#define DIALOGSKINNERH
#include "MediaLibraryInterface.h"
#include "../winamp/wa_dlg.h"
COLORREF GetHTMLColor( int color );
class DialogSkinner
{
typedef HBITMAP( *BitmapFunc )( );
typedef int ( *ColorFunc )( int idx ); // pass this an index, returns a RGB value (passing 0 or > 3 returns NULL)
typedef int ( *HandleFunc )( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam );
typedef void ( *DrawFunc )( HWND hwndDlg, int *tab, int tabsize ); // each entry in tab would be the id | DCW_*
public:
DialogSkinner()
{}
int Color( int index )
{
if ( !color )
color = (ColorFunc)mediaLibrary.GetWADLGFunc( 1 );
return color( index );
}
RGBQUAD GetRGB( int index )
{
COLORREF color = Color( index );
RGBQUAD rgb;
rgb.rgbReserved = 0;
rgb.rgbBlue = GetBValue( color );
rgb.rgbGreen = GetGValue( color );
rgb.rgbRed = GetRValue( color );
return rgb;
}
INT_PTR Handle( HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam )
{
if ( !handle )
handle = (HandleFunc)mediaLibrary.GetWADLGFunc( 2 );
return handle( dlg, msg, wParam, lParam );
}
void Draw( HWND dlg, int *tab, int tabSize )
{
if ( !draw )
draw = (DrawFunc)mediaLibrary.GetWADLGFunc( 3 );
draw( dlg, tab, tabSize );
}
HFONT GetFont()
{
return (HFONT)mediaLibrary.GetWADLGFunc( 66 );
}
HBITMAP GetBitmap()
{
if ( !bitmap )
bitmap = (BitmapFunc)mediaLibrary.GetWADLGFunc( 4 );
return bitmap();
}
ColorFunc color = 0;
HandleFunc handle = 0;
DrawFunc draw = 0;
BitmapFunc bitmap = 0;
};
extern DialogSkinner dialogSkinner;
#endif

View File

@ -0,0 +1,64 @@
#include "GaplessRingBuffer.h"
#include <bfc/platform/types.h>
#include <bfc/error.h>
#include <algorithm>
GaplessRingBuffer::GaplessRingBuffer()
{
pregapBytes = 0;
frameBytes = 0;
postgapBytes = 0;
currentPregapBytes = 0;
}
GaplessRingBuffer::~GaplessRingBuffer()
{
}
int GaplessRingBuffer::Initialize(size_t samples, size_t bps, size_t channels, size_t pregap, size_t postgap)
{
this->frameBytes = channels * bps / 8;
this->currentPregapBytes = this->pregapBytes = pregap * frameBytes;
this->postgapBytes = postgap * frameBytes;
ring_buffer.reserve(samples * frameBytes + pregapBytes);
return NErr_Success;
}
bool GaplessRingBuffer::Empty() const
{
return (ring_buffer.size() <= pregapBytes);
}
size_t GaplessRingBuffer::Read(void *destination, size_t destination_bytes)
{
// make sure we've filled enough of the buffer to satisfy the postgap
if (Empty()) {
return 0;
}
// don't read into postgap area
size_t remaining = ring_buffer.size() - postgapBytes;
destination_bytes = min(remaining, destination_bytes);
return ring_buffer.read(destination, destination_bytes);
}
size_t GaplessRingBuffer::Write(const void *input, size_t input_bytes)
{
// cut pregap if necessary
if (currentPregapBytes) {
size_t cut = min(input_bytes, currentPregapBytes);
currentPregapBytes -= cut;
input_bytes -= cut;
input = (const uint8_t *)input + cut;
}
return ring_buffer.write(input, input_bytes);
}
void GaplessRingBuffer::Reset()
{
currentPregapBytes = pregapBytes;
ring_buffer.clear();
}

View File

@ -0,0 +1,20 @@
#pragma once
#include "RingBuffer.h"
class GaplessRingBuffer
{
public:
GaplessRingBuffer();
~GaplessRingBuffer();
int Initialize(size_t samples, size_t bps, size_t channels, size_t pregap, size_t postgap);
size_t Read(void *destination, size_t destination_bytes);
bool Empty() const;
size_t Write(const void *input, size_t input_bytes);
void Reset();
private:
RingBuffer ring_buffer;
size_t frameBytes; // byte size of one frame (channels*bps/8)
size_t currentPregapBytes;
size_t pregapBytes;
size_t postgapBytes;
};

93
Src/nu/GrowBuf.h Normal file
View File

@ -0,0 +1,93 @@
#ifndef NULLSOFT_UTILITY_GROWBUF_H
#define NULLSOFT_UTILITY_GROWBUF_H
#include <memory.h>
#include <stdlib.h>
class GrowBuf
{
public:
GrowBuf() {}
~GrowBuf()
{
if ( m_s )
free( m_s );
m_s = NULL;
}
void reserve( size_t len )
{
if ( len > m_alloc )
{
void *ne;
m_alloc = len;
ne = realloc( m_s, m_alloc );
if ( !ne )
{
ne = malloc( m_alloc );
memcpy( ne, m_s, m_used );
free( m_s );
}
m_s = ne;
}
}
size_t add( void *data, size_t len )
{
if ( !len )
return 0;
resize( m_used + len );
memcpy( (char *)get() + m_used - len, data, len );
return m_used - len;
}
void set( void *data, size_t len )
{
resize( len );
memcpy( (char *)get(), data, len );
}
void resize( size_t newlen )
{
m_used = newlen;
if ( newlen > m_alloc )
{
m_alloc = newlen * 2;
if ( m_alloc < 1024 )
m_alloc = 1024;
void *ne = realloc( m_s, m_alloc );
if ( !ne )
{
ne = malloc( m_alloc );
if ( !ne )
*( (char *)ne ) = NULL;
memcpy( ne, m_s, m_used );
free( m_s );
}
m_s = ne;
}
}
size_t getlen()
{
return m_used;
}
void *get()
{
return m_s;
}
private:
void *m_s = NULL;
size_t m_alloc = 0;
size_t m_used = 0;
};
#endif

839
Src/nu/HTMLContainer.cpp Normal file
View File

@ -0,0 +1,839 @@
#include "HTMLContainer.h"
#include <exdisp.h>
#include <mshtmdid.h>
#include <mshtml.h>
#include <exdispid.h>
#include <strsafe.h>
#ifndef DISPID_NEWWINDOW3
#define DISPID_NEWWINDOW3 273
#endif
// ---------------------------------------------------------------
IConnectionPoint *HTMLContainer::GetConnectionPoint (REFIID riid)
{
IUnknown *punk = getUnknown ();
if (!punk)
return 0;
IConnectionPointContainer *pcpc;
IConnectionPoint *pcp = 0;
HRESULT hr = punk->QueryInterface (IID_IConnectionPointContainer, (void **) & pcpc);
if (SUCCEEDED (hr))
{
pcpc->FindConnectionPoint (riid, &pcp);
pcpc->Release();
}
punk->Release();
return pcp;
}
void HTMLContainer::SyncSizeToWindow(HWND hwnd)
{
RECT rect;
GetWindowRect(hwnd, &rect);
int height = (rect.bottom - rect.top);
// if we get a null height then hide the html control (after limiting to 1px)
// and also hide it's parent window - is mainly for ml_wire to prevent display
// glitches when resizing the bottom segment all the way to the bottom
ShowWindow(m_hwnd,height?SW_SHOWNA:SW_HIDE);
ShowWindow(hwnd,height?SW_SHOWNA:SW_HIDE);
setLocation(0, 0, rect.right - rect.left, height?height:1);
}
// uncomment if you ever want to use mozilla instead of IE
// change the CLSID_WebBrowser in the constructor below to CLSID_MozillaBrowser
// but window.external from javascript doesn't work :(
static const CLSID CLSID_MozillaBrowser=
{ 0x1339B54C, 0x3453, 0x11D2, { 0x93, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
HTMLContainer::HTMLContainer(HWND hwnd)
: m_pweb (0), pszHostCSS(NULL), m_cRefs(1), m_hwnd(hwnd), m_punk(NULL)
{
bInitialized = (S_OK == CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)) ? true : false;
dwHostInfoFlags = DOCHOSTUIFLAG_NO3DOUTERBORDER | DOCHOSTUIFLAG_ENABLE_INPLACE_NAVIGATION | DOCHOSTUIFLAG_NO3DBORDER | DOCHOSTUIDBLCLK_DEFAULT;
dwDownloadFlags = DLCTL_DLIMAGES | DLCTL_VIDEOS | DLCTL_PRAGMA_NO_CACHE;
memset(&m_rect, 0, sizeof(m_rect));
add(CLSID_WebBrowser);
IUnknown *punk = getUnknown();
if (punk)
{
if (SUCCEEDED(punk->QueryInterface (IID_IWebBrowser2, (void **) & m_pweb))
|| SUCCEEDED(punk->QueryInterface (IID_IWebBrowser, (void **) & m_pweb)))
{
IConnectionPoint *icp = GetConnectionPoint(DIID_DWebBrowserEvents2);
if (icp)
{
m_dwCookie = 0;
icp->Advise(static_cast<IDispatch *>(this), &m_dwCookie);
icp->Release();
}
}
else
m_pweb=0;
punk->Release();
}
}
HTMLContainer::HTMLContainer()
: m_pweb (0), pszHostCSS(NULL), m_cRefs(1), m_hwnd(NULL), m_punk(NULL)
{
bInitialized = (S_OK == CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)) ? true : false;
dwHostInfoFlags = DOCHOSTUIFLAG_NO3DOUTERBORDER | DOCHOSTUIFLAG_ENABLE_INPLACE_NAVIGATION | DOCHOSTUIFLAG_NO3DBORDER | DOCHOSTUIDBLCLK_DEFAULT;
dwDownloadFlags = DLCTL_DLIMAGES | DLCTL_VIDEOS | DLCTL_PRAGMA_NO_CACHE;
memset(&m_rect, 0, sizeof(m_rect));
add(CLSID_WebBrowser);
IUnknown *punk = getUnknown();
if (punk)
{
if (SUCCEEDED(punk->QueryInterface (IID_IWebBrowser2, (void **) & m_pweb))
|| SUCCEEDED(punk->QueryInterface (IID_IWebBrowser, (void **) & m_pweb)))
{
IConnectionPoint *icp = GetConnectionPoint(DIID_DWebBrowserEvents2);
if (icp)
{
m_dwCookie = 0;
icp->Advise(static_cast<IDispatch *>(this), &m_dwCookie);
icp->Release();
}
}
else
m_pweb=0;
punk->Release();
}
}
HTMLContainer::~HTMLContainer()
{
close();
if (pszHostCSS) { free(pszHostCSS); pszHostCSS = NULL; }
if (bInitialized) CoUninitialize();
}
void HTMLContainer::close()
{
IOleObject *pioo;
if ( m_punk )
{
HRESULT hr = m_punk->QueryInterface(IID_IOleObject, (PVOID *) & pioo);
if (SUCCEEDED(hr))
{
pioo->Close(OLECLOSE_NOSAVE);
pioo->Release();
}
}
if (m_punk)
{
m_punk->Release();
m_punk = NULL;
}
if (m_pweb)
{
m_pweb->Quit();
m_pweb->Release();
m_pweb = 0;
}
}
STDMETHODIMP HTMLContainer::QueryInterface(REFIID riid, PVOID *ppvObject)
{
if (!ppvObject)
return E_POINTER;
if (IsEqualIID(riid, IID_IOleClientSite))
*ppvObject = (IOleClientSite *)this;
else if (IsEqualIID(riid, IID_IOleInPlaceSite))
*ppvObject = (IOleInPlaceSite *)this;
else if (IsEqualIID(riid, IID_IOleInPlaceFrame))
*ppvObject = (IOleInPlaceFrame *)this;
else if (IsEqualIID(riid, IID_IOleInPlaceUIWindow))
*ppvObject = (IOleInPlaceUIWindow *)this;
else if (IsEqualIID(riid, IID_IOleControlSite))
*ppvObject = (IOleControlSite *)this;
else if (IsEqualIID(riid, IID_IOleWindow))
*ppvObject = this;
else if (IsEqualIID(riid, IID_IDispatch))
*ppvObject = (IDispatch *)this;
else if (IsEqualIID(riid, IID_IUnknown))
*ppvObject = this;
else if (IsEqualIID(riid, IID_IDocHostUIHandler))
*ppvObject = (IDocHostUIHandler *)this;
else
{
*ppvObject = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
ULONG HTMLContainer::AddRef(void)
{
return ++m_cRefs;
}
ULONG HTMLContainer::Release(void)
{
if (--m_cRefs)
return m_cRefs;
return 0;
}
HRESULT HTMLContainer::SaveObject()
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER * ppMk)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::GetContainer(LPOLECONTAINER * ppContainer)
{
return E_NOINTERFACE;
}
HRESULT HTMLContainer::ShowObject()
{
return S_OK;
}
HRESULT HTMLContainer::OnShowWindow(BOOL fShow)
{
return S_OK;
}
HRESULT HTMLContainer::RequestNewObjectLayout()
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::GetWindow(HWND * lphwnd)
{
if (!IsWindow(m_hwnd))
return S_FALSE;
*lphwnd = m_hwnd;
return S_OK;
}
HRESULT HTMLContainer::ContextSensitiveHelp(BOOL fEnterMode)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::CanInPlaceActivate(void)
{
return S_OK;
}
HRESULT HTMLContainer::OnInPlaceActivate(void)
{
return S_OK;
}
HRESULT HTMLContainer::OnUIActivate(void)
{
return S_OK;
}
HRESULT HTMLContainer::GetWindowContext(IOleInPlaceFrame ** ppFrame, IOleInPlaceUIWindow ** ppIIPUIWin,
LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo)
{
*ppFrame = (IOleInPlaceFrame *)this;
*ppIIPUIWin = NULL;
RECT rect;
GetClientRect(m_hwnd, &rect);
lprcPosRect->left = 0;
lprcPosRect->top = 0;
lprcPosRect->right = rect.right;
lprcPosRect->bottom = rect.bottom;
CopyRect(lprcClipRect, lprcPosRect);
lpFrameInfo->cb = sizeof(OLEINPLACEFRAMEINFO);
lpFrameInfo->fMDIApp = FALSE;
lpFrameInfo->hwndFrame = m_hwnd;
lpFrameInfo->haccel = 0;
lpFrameInfo->cAccelEntries = 0;
(*ppFrame)->AddRef();
return S_OK;
}
HRESULT HTMLContainer::Scroll(SIZE scrollExtent)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::OnUIDeactivate(BOOL fUndoable)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::OnInPlaceDeactivate(void)
{
return S_OK;
}
HRESULT HTMLContainer::DiscardUndoState(void)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::DeactivateAndUndo(void)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::OnPosRectChange(LPCRECT lprcPosRect)
{
return S_OK;
}
HRESULT HTMLContainer::InsertMenus(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::SetMenu(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::RemoveMenus(HMENU hmenuShared)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::SetStatusText(LPCOLESTR pszStatusText)
{
return S_OK;
}
HRESULT HTMLContainer::TranslateAccelerator(LPMSG lpmsg, WORD wID)
{
return S_OK;
}
HRESULT HTMLContainer::EnableModeless(BOOL fEnable)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::OnControlInfoChanged()
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::LockInPlaceActive(BOOL fLock)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::GetExtendedControl(IDispatch **ppDisp)
{
if (ppDisp == NULL)
return E_INVALIDARG;
*ppDisp = (IDispatch *)this;
(*ppDisp)->AddRef();
return S_OK;
}
HRESULT HTMLContainer::TransformCoords(POINTL *pptlHimetric, POINTF *pptfContainer, DWORD dwFlags)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::TranslateAccelerator(LPMSG pMsg, DWORD grfModifiers)
{
return S_FALSE;
}
HRESULT HTMLContainer::OnFocus(BOOL fGotFocus)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::ShowPropertyFrame(void)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)
{
*rgdispid = DISPID_UNKNOWN;
return DISP_E_UNKNOWNNAME;
}
HRESULT HTMLContainer::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::GetTypeInfoCount(unsigned int FAR * pctinfo)
{
return E_NOTIMPL;
}
void HTMLContainer::OnBeforeNavigate(IDispatch *pDispatch, LPCWSTR pszURL, DWORD dwFlags, LPCWSTR pszTargetFrameName, VARIANT *vtPostData, LPCWSTR pszHeaders, VARIANT_BOOL *Cancel)
{
}
void HTMLContainer::OnNavigateError(IDispatch *pDispatch, LPCWSTR pszURL, LPCWSTR pszTargetFrameName, INT nStatusCode, VARIANT_BOOL *Cancel)
{
}
void HTMLContainer::OnNavigateComplete(IDispatch *pDispatch, LPCWSTR pszURL)
{
}
void HTMLContainer::OnDocumentComplete(IDispatch *pDisp, LPCWSTR pszURL)
{
}
void HTMLContainer::OnDownloadBegin(void)
{
}
void HTMLContainer::OnDownloadComplete(void)
{
}
void HTMLContainer::OnFileDownload(VARIANT_BOOL *ActiveDocument, VARIANT_BOOL *Cancel)
{
}
void HTMLContainer::OnNewWindow2(IDispatch **ppDisp, VARIANT_BOOL *Cancel)
{
}
void HTMLContainer::OnNewWindow3(IDispatch **ppDisp, VARIANT_BOOL *Cancel, DWORD dwFlags, LPCWSTR pszUrlContext, LPCWSTR pszUrl)
{
}
void HTMLContainer::OnProgressChange(long Progress, long ProgressMax)
{
}
void HTMLContainer::OnStatusTextChange(LPCWSTR pszText)
{
}
#define GET_SAFE_DISP_BSTR(_val) ((_val.pvarVal && VT_BSTR == _val.pvarVal->vt) ? _val.pvarVal->bstrVal : NULL)
#define GET_SAFE_DISP_I4(_val) ((_val.pvarVal && VT_I4 == _val.pvarVal->vt) ? _val.pvarVal->intVal : 0)
HRESULT HTMLContainer::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr)
{
switch (dispid)
{
case DISPID_BEFORENAVIGATE2:
OnBeforeNavigate();
OnBeforeNavigate( pdispparams->rgvarg[6].pdispVal, GET_SAFE_DISP_BSTR(pdispparams->rgvarg[5]),
GET_SAFE_DISP_I4(pdispparams->rgvarg[4]), GET_SAFE_DISP_BSTR(pdispparams->rgvarg[3]),
pdispparams->rgvarg[2].pvarVal, GET_SAFE_DISP_BSTR(pdispparams->rgvarg[1]), pdispparams->rgvarg[0].pboolVal);
break;
case DISPID_NAVIGATEERROR:
{
VARIANT * vt_statuscode = pdispparams->rgvarg[1].pvarVal;
DWORD dwStatusCode = vt_statuscode->lVal;
if (dwStatusCode == 200)
{
*pdispparams->rgvarg[0].pboolVal = VARIANT_TRUE;
break;
}
OnNavigateError();
OnNavigateError(pdispparams->rgvarg[4].pdispVal, GET_SAFE_DISP_BSTR(pdispparams->rgvarg[3]),
GET_SAFE_DISP_BSTR(pdispparams->rgvarg[2]), GET_SAFE_DISP_I4(pdispparams->rgvarg[1]), pdispparams->rgvarg[0].pboolVal);
}
break;
case DISPID_NAVIGATECOMPLETE2:
OnNavigateComplete();
OnNavigateComplete(pdispparams->rgvarg[1].pdispVal, GET_SAFE_DISP_BSTR(pdispparams->rgvarg[0]));
break;
case DISPID_DOCUMENTCOMPLETE:
OnDocumentComplete(pdispparams->rgvarg[1].pdispVal, GET_SAFE_DISP_BSTR(pdispparams->rgvarg[0]));
break;
case DISPID_DOWNLOADBEGIN:
OnDownloadBegin();
break;
case DISPID_DOWNLOADCOMPLETE:
OnDownloadComplete();
break;
case DISPID_FILEDOWNLOAD:
OnFileDownload(pdispparams->rgvarg[1].pboolVal, pdispparams->rgvarg[0].pboolVal);
break;
case DISPID_NEWWINDOW2:
OnNewWindow2(pdispparams->rgvarg[1].ppdispVal, pdispparams->rgvarg[0].pboolVal);
break;
case DISPID_NEWWINDOW3:
OnNewWindow3(pdispparams->rgvarg[4].ppdispVal, pdispparams->rgvarg[3].pboolVal,
pdispparams->rgvarg[2].intVal, pdispparams->rgvarg[1].bstrVal, pdispparams->rgvarg[0].bstrVal);
break;
case DISPID_PROGRESSCHANGE:
OnProgressChange(pdispparams->rgvarg[1].lVal, pdispparams->rgvarg[0].lVal);
break;
case DISPID_STATUSTEXTCHANGE:
OnStatusTextChange(GET_SAFE_DISP_BSTR(pdispparams->rgvarg[0]));
break;
case DISPID_AMBIENT_USERAGENT:
/* TODO:
pvar->vt = VT_BSTR;
pvar->bstrVal = SysAllocString("...");
return S_OK;
*/
break;
case DISPID_AMBIENT_DLCONTROL:
pvarResult->vt = VT_I4;
pvarResult->lVal = dwDownloadFlags;
return S_OK;
}
return DISP_E_MEMBERNOTFOUND;
}
void HTMLContainer::add(CLSID clsid)
{
HRESULT hr; // return code
CoCreateInstance(clsid,
NULL,
CLSCTX_INPROC_SERVER/* | CLSCTX_LOCAL_SERVER*/,
IID_IUnknown,
(PVOID *)&m_punk);
if (!m_punk)
return ;
IOleObject *pioo;
hr = m_punk->QueryInterface(IID_IOleObject, (PVOID *) & pioo);
if (FAILED(hr))
return ;
pioo->SetClientSite(this);
pioo->Release();
IPersistStreamInit *ppsi;
hr = m_punk->QueryInterface(IID_IPersistStreamInit, (PVOID *) & ppsi);
if (SUCCEEDED(hr))
{
ppsi->InitNew();
ppsi->Release();
}
}
void HTMLContainer::remove()
{
if (!m_punk)
return ;
HRESULT hr;
IOleObject *pioo;
IOleInPlaceObject *pipo;
/*
benski> enabling this makes everything lock up!
IConnectionPoint *icp = GetConnectionPoint(DIID_DWebBrowserEvents2);
if (icp)
{
// m_dwCookie = 0;
HRESULT hr = icp->Unadvise(m_dwCookie);
icp->Release();
}
*/
hr = m_punk->QueryInterface(IID_IOleObject, (PVOID *) & pioo);
if (SUCCEEDED(hr))
{
pioo->Close(OLECLOSE_NOSAVE);
pioo->SetClientSite(NULL);
pioo->Release();
}
hr = m_punk->QueryInterface(IID_IOleInPlaceObject, (PVOID *) & pipo);
if (SUCCEEDED(hr))
{
pipo->UIDeactivate();
pipo->InPlaceDeactivate();
pipo->Release();
}
m_punk->Release();
m_punk = NULL;
}
void HTMLContainer::setLocation(int x, int y, int width, int height)
{
m_rect.left = x;
m_rect.top = y;
m_rect.right = x + width;
m_rect.bottom = y + height;
if (!m_punk)
return ;
HRESULT hr;
IOleInPlaceObject *pipo;
hr = m_punk->QueryInterface(IID_IOleInPlaceObject, (PVOID *) & pipo);
if (FAILED(hr))
return ;
pipo->SetObjectRects(&m_rect, &m_rect);
pipo->Release();
}
HRESULT HTMLContainer::GetBorder(LPRECT lprectBorder)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::RequestBorderSpace(LPCBORDERWIDTHS lpborderwidths)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::SetBorderSpace(LPCBORDERWIDTHS lpborderwidths)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::SetActiveObject(IOleInPlaceActiveObject * pActiveObject, LPCOLESTR lpszObjName)
{
return E_NOTIMPL;
}
void HTMLContainer::setVisible(BOOL fVisible)
{
if (!m_punk)
return ;
HRESULT hr;
IOleObject *pioo;
hr = m_punk->QueryInterface(IID_IOleObject, (PVOID *) & pioo);
if (FAILED(hr))
return ;
if (fVisible)
{
pioo->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, this, 0, m_hwnd, &m_rect);
pioo->DoVerb(OLEIVERB_SHOW, NULL, this, 0, m_hwnd, &m_rect);
}
else
pioo->DoVerb(OLEIVERB_HIDE, NULL, this, 0, m_hwnd, NULL);
pioo->Release();
}
void HTMLContainer::setFocus(BOOL fFocus)
{
if (!m_punk)
return ;
HRESULT hr;
IOleObject *pioo;
if (fFocus)
{
hr = m_punk->QueryInterface(IID_IOleObject, (PVOID *) & pioo);
if (FAILED(hr))
return ;
pioo->DoVerb(OLEIVERB_UIACTIVATE, NULL, this, 0, m_hwnd, &m_rect);
pioo->Release();
}
}
bool HTMLContainer::translateKey(LPMSG pMsg)
{
if (!m_punk)
return false;
HRESULT hr;
IOleInPlaceActiveObject *pao;
hr = m_punk->QueryInterface(IID_IOleInPlaceActiveObject, (PVOID *) & pao);
if (FAILED(hr))
return false;
HRESULT res = pao->TranslateAccelerator(pMsg);
pao->Release();
return res == S_OK;
}
/**************************************************************************
* adContainer::getDispatch()
**************************************************************************/
IDispatch * HTMLContainer::getDispatch()
{
if (!m_punk)
return NULL;
IDispatch *pdisp = NULL;
m_punk->QueryInterface(IID_IDispatch, (PVOID *) & pdisp);
return pdisp;
}
/**************************************************************************
* adContainer::getUnknown()
**************************************************************************/
IUnknown * HTMLContainer::getUnknown()
{
if (!m_punk)
return NULL;
m_punk->AddRef();
return m_punk;
}
// ***********************************************************************
// IDocHostUIHandler
// ***********************************************************************
HRESULT HTMLContainer::ShowContextMenu(DWORD dwID, POINT __RPC_FAR *ppt, IUnknown __RPC_FAR *pcmdtReserved, IDispatch __RPC_FAR *pdispReserved)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::GetHostInfo(DOCHOSTUIINFO __RPC_FAR *pInfo)
{
pInfo->cbSize = sizeof(DOCHOSTUIINFO);
pInfo->dwFlags = dwHostInfoFlags;
if (pszHostCSS)
{
INT strlen;
OLECHAR *pocCSS;
strlen = lstrlenW(pszHostCSS);
if (strlen)
{
strlen++;
pocCSS = (OLECHAR*)CoTaskMemAlloc(strlen * sizeof(OLECHAR));
if (pocCSS && S_OK== StringCchCopyW(pocCSS, strlen, pszHostCSS)) pInfo->pchHostCss = pocCSS;
}
}
return S_OK;
}
HRESULT HTMLContainer::ShowUI(DWORD dwID, IOleInPlaceActiveObject __RPC_FAR *pActiveObject, IOleCommandTarget __RPC_FAR *pCommandTarget, IOleInPlaceFrame __RPC_FAR *pFrame, IOleInPlaceUIWindow __RPC_FAR *pDoc)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::HideUI(void)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::UpdateUI(void)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::OnDocWindowActivate(BOOL fActivate)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::OnFrameWindowActivate(BOOL fActivate)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::ResizeBorder(LPCRECT prcBorder, IOleInPlaceUIWindow __RPC_FAR *pUIWindow, BOOL fRameWindow)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::TranslateAccelerator(LPMSG lpMsg, const GUID __RPC_FAR *pguidCmdGroup, DWORD nCmdID)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::GetOptionKeyPath(LPOLESTR __RPC_FAR *pchKey, DWORD dw)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::GetDropTarget(IDropTarget __RPC_FAR *pDropTarget, IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::GetExternal(IDispatch __RPC_FAR *__RPC_FAR *ppDispatch)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::TranslateUrl(DWORD dwTranslate, OLECHAR __RPC_FAR *pchURLIn, OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut)
{
return E_NOTIMPL;
}
HRESULT HTMLContainer::FilterDataObject(IDataObject __RPC_FAR *pDO, IDataObject __RPC_FAR *__RPC_FAR *ppDORet)
{
return E_NOTIMPL;
}
BOOL HTMLContainer::SetHostCSS(LPCWSTR pszHostCSS)
{
if (this->pszHostCSS) { free(this->pszHostCSS); this->pszHostCSS = NULL; }
if (pszHostCSS && *pszHostCSS) this->pszHostCSS = _wcsdup(pszHostCSS);
return TRUE;
}
HWND HTMLContainer::GetHostHWND(void)
{
if (m_punk)
{
IOleInPlaceObject *pipo;
m_punk->QueryInterface(IID_IOleInPlaceObject, (PVOID *)&pipo);
if (pipo)
{
HWND hwndHost;
pipo->GetWindow(&hwndHost);
pipo->Release();
return hwndHost;
}
}
return NULL;
}
DWORD HTMLContainer::SetDownloadFlags(DWORD dwFlags)
{
DWORD temp;
temp = dwDownloadFlags;
dwDownloadFlags = dwFlags;
return temp;
}
DWORD HTMLContainer::SetHostInfoFlags(DWORD dwFlags)
{
DWORD temp;
temp = dwHostInfoFlags;
dwHostInfoFlags = dwFlags;
return temp;
}

177
Src/nu/HTMLContainer.h Normal file
View File

@ -0,0 +1,177 @@
#ifndef NULLSOFT_HTMLCONTAINERH
#define NULLSOFT_HTMLCONTAINERH
#include <ocidl.h>
#include <mshtmhst.h>
#include <mshtmdid.h>
#include <shlobj.h>
/**************************************************************************
class definitions
**************************************************************************/
#ifndef DOCHOSTUIFLAG_HOST_NAVIGATES
#define DOCHOSTUIFLAG_HOST_NAVIGATES 0x02000000
#endif
#ifndef DOCHOSTUIFLAG_ENABLE_REDIRECT_NOTIFICATION
#define DOCHOSTUIFLAG_ENABLE_REDIRECT_NOTIFICATION 0x04000000
#endif
#ifndef DOCHOSTUIFLAG_USE_WINDOWLESS_SELECTCONTROL
#define DOCHOSTUIFLAG_USE_WINDOWLESS_SELECTCONTROL 0x08000000
#endif
#ifndef DOCHOSTUIFLAG_USE_WINDOWED_SELECTCONTROL
#define DOCHOSTUIFLAG_USE_WINDOWED_SELECTCONTROL 0x10000000
#endif
#ifndef DOCHOSTUIFLAG_ENABLE_ACTIVEX_INACTIVATE_MODE
#define DOCHOSTUIFLAG_ENABLE_ACTIVEX_INACTIVATE_MODE 0x20000000
#endif
class HTMLContainer : public IOleClientSite,
public IOleInPlaceSite,
public IOleInPlaceFrame,
public IOleControlSite,
public IDocHostUIHandler,
public IDispatch
{
protected:
ULONG m_cRefs; // ref count
IUnknown *m_punk; // IUnknown of contained object
RECT m_rect; // size of control
bool bInitialized;
public:
HWND m_hwnd; // window handle of the container
HTMLContainer();
HTMLContainer(HWND hwnd);
virtual ~HTMLContainer();
public:
// *** IUnknown Methods ***
STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject);
STDMETHOD_(ULONG, AddRef)(void);
STDMETHOD_(ULONG, Release)(void);
// *** IOleInPlaceUIWindow Methods ***
STDMETHOD (GetBorder)(LPRECT lprectBorder);
STDMETHOD (RequestBorderSpace)(LPCBORDERWIDTHS lpborderwidths);
STDMETHOD (SetBorderSpace)(LPCBORDERWIDTHS lpborderwidths);
STDMETHOD (SetActiveObject)(IOleInPlaceActiveObject * pActiveObject,
LPCOLESTR lpszObjName);
// *** IOleClientSite Methods ***
STDMETHOD (SaveObject)();
STDMETHOD (GetMoniker)(DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER *ppMk);
STDMETHOD (GetContainer)(LPOLECONTAINER *ppContainer);
STDMETHOD (ShowObject)();
STDMETHOD (OnShowWindow)(BOOL fShow);
STDMETHOD (RequestNewObjectLayout)();
// *** IOleWindow Methods ***
STDMETHOD (GetWindow) (HWND * phwnd);
STDMETHOD (ContextSensitiveHelp) (BOOL fEnterMode);
// *** IOleInPlaceSite Methods ***
STDMETHOD (CanInPlaceActivate) (void);
STDMETHOD (OnInPlaceActivate) (void);
STDMETHOD (OnUIActivate) (void);
STDMETHOD (GetWindowContext) (IOleInPlaceFrame ** ppFrame, IOleInPlaceUIWindow ** ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo);
STDMETHOD (Scroll) (SIZE scrollExtent);
STDMETHOD (OnUIDeactivate) (BOOL fUndoable);
STDMETHOD (OnInPlaceDeactivate) (void);
STDMETHOD (DiscardUndoState) (void);
STDMETHOD (DeactivateAndUndo) (void);
STDMETHOD (OnPosRectChange) (LPCRECT lprcPosRect);
// *** IOleInPlaceFrame Methods ***
STDMETHOD (InsertMenus)(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths);
STDMETHOD (SetMenu)(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject);
STDMETHOD (RemoveMenus)(HMENU hmenuShared);
STDMETHOD (SetStatusText)(LPCOLESTR pszStatusText);
STDMETHOD (EnableModeless)(BOOL fEnable);
STDMETHOD (TranslateAccelerator)(LPMSG lpmsg, WORD wID);
// *** IOleControlSite Methods ***
STDMETHOD (OnControlInfoChanged)(void);
STDMETHOD (LockInPlaceActive)(BOOL fLock);
STDMETHOD (GetExtendedControl)(IDispatch **ppDisp);
STDMETHOD (TransformCoords)(POINTL *pptlHimetric, POINTF *pptfContainer, DWORD dwFlags);
STDMETHOD (TranslateAccelerator)(LPMSG pMsg, DWORD grfModifiers);
STDMETHOD (OnFocus)(BOOL fGotFocus);
STDMETHOD (ShowPropertyFrame)(void);
// *** IDispatch Methods ***
STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid);
STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo);
STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo);
STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr);
// *** IDocHostUIHandler Methods ***
STDMETHOD (ShowContextMenu)(DWORD dwID, POINT __RPC_FAR *ppt, IUnknown __RPC_FAR *pcmdtReserved, IDispatch __RPC_FAR *pdispReserved);
STDMETHOD (GetHostInfo)(DOCHOSTUIINFO __RPC_FAR *pInfo);
STDMETHOD (ShowUI)(DWORD dwID, IOleInPlaceActiveObject __RPC_FAR *pActiveObject, IOleCommandTarget __RPC_FAR *pCommandTarget, IOleInPlaceFrame __RPC_FAR *pFrame, IOleInPlaceUIWindow __RPC_FAR *pDoc);
STDMETHOD (HideUI)(void);
STDMETHOD (UpdateUI)(void);
STDMETHOD (OnDocWindowActivate)(BOOL fActivate);
STDMETHOD (OnFrameWindowActivate)(BOOL fActivate);
STDMETHOD (ResizeBorder)(LPCRECT prcBorder, IOleInPlaceUIWindow __RPC_FAR *pUIWindow, BOOL fRameWindow);
STDMETHOD (TranslateAccelerator)(LPMSG lpMsg, const GUID __RPC_FAR *pguidCmdGroup, DWORD nCmdID);
STDMETHOD (GetOptionKeyPath)(LPOLESTR __RPC_FAR *pchKey, DWORD dw);
STDMETHOD (GetDropTarget)(IDropTarget __RPC_FAR *pDropTarget, IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget);
STDMETHOD (GetExternal)(IDispatch __RPC_FAR *__RPC_FAR *ppDispatch);
STDMETHOD (TranslateUrl)(DWORD dwTranslate, OLECHAR __RPC_FAR *pchURLIn, OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut);
STDMETHOD (FilterDataObject)(IDataObject __RPC_FAR *pDO, IDataObject __RPC_FAR *__RPC_FAR *ppDORet);
// STDMETHOD (EnableModeless)(BOOL fEnable);
public:
void add(CLSID clsid);
void remove();
void setLocation(int x, int y, int width, int height);
void setVisible(BOOL fVisible);
void setFocus(BOOL fFocus);
void setStatusWindow(HWND hwndStatus);
bool translateKey(LPMSG pMsg);
virtual void OnBeforeNavigate(void) {} // deprecated
virtual void OnNavigateError(void) {} // deprecated
virtual void OnNavigateComplete(void) {} // deprecated
virtual void OnBeforeNavigate(IDispatch *pDispatch, LPCWSTR pszURL, DWORD dwFlags, LPCWSTR pszTargetFrameName, VARIANT *vtPostData, LPCWSTR pszHeaders, VARIANT_BOOL *Cancel);
virtual void OnNavigateError(IDispatch *pDispatch, LPCWSTR pszURL, LPCWSTR pszTargetFrameName, INT nStatusCode, VARIANT_BOOL *Cancel);
virtual void OnNavigateComplete(IDispatch *pDispatch, LPCWSTR pszURL);
virtual void OnDocumentComplete(IDispatch *pDisp, LPCWSTR pszURL);
virtual void OnDownloadBegin(void);
virtual void OnDownloadComplete(void);
virtual void OnFileDownload(VARIANT_BOOL *ActiveDocument, VARIANT_BOOL *Cancel);
virtual void OnNewWindow2(IDispatch **ppDisp, VARIANT_BOOL *Cancel);
virtual void OnNewWindow3(IDispatch **ppDisp, VARIANT_BOOL *Cancel, DWORD dwFlags, LPCWSTR pszUrlContext, LPCWSTR pszUrl);
virtual void OnProgressChange(long Progress, long ProgressMax);
virtual void OnStatusTextChange(LPCWSTR pszText);
void close();
virtual BOOL SetHostCSS(LPCWSTR pszHostCSS);
virtual HWND GetHostHWND(void);
DWORD SetDownloadFlags(DWORD dwFlags);
DWORD SetHostInfoFlags(DWORD dwFlags);
IDispatch *getDispatch();
IUnknown * getUnknown();
IWebBrowser2 *m_pweb;
void SyncSizeToWindow(HWND window);
IConnectionPoint *GetConnectionPoint(REFIID riid);
DWORD m_dwCookie;
private:
wchar_t *pszHostCSS;
DWORD dwDownloadFlags;
DWORD dwHostInfoFlags;
};
#endif

2108
Src/nu/HTMLContainer2.cpp Normal file

File diff suppressed because it is too large Load Diff

292
Src/nu/HTMLContainer2.h Normal file
View File

@ -0,0 +1,292 @@
#ifndef NULLSOFT_HTMLCONTAINERH
#define NULLSOFT_HTMLCONTAINERH
#include <ocidl.h>
#include <mshtmhst.h>
#include <mshtmdid.h>
#include <shlobj.h>
#include <urlmon.h>
/**************************************************************************
class definitions
**************************************************************************/
#ifndef DOCHOSTUIFLAG_HOST_NAVIGATES
#define DOCHOSTUIFLAG_HOST_NAVIGATES 0x02000000
#endif
#ifndef DOCHOSTUIFLAG_ENABLE_REDIRECT_NOTIFICATION
#define DOCHOSTUIFLAG_ENABLE_REDIRECT_NOTIFICATION 0x04000000
#endif
#ifndef DOCHOSTUIFLAG_USE_WINDOWLESS_SELECTCONTROL
#define DOCHOSTUIFLAG_USE_WINDOWLESS_SELECTCONTROL 0x08000000
#endif
#ifndef DOCHOSTUIFLAG_USE_WINDOWED_SELECTCONTROL
#define DOCHOSTUIFLAG_USE_WINDOWED_SELECTCONTROL 0x10000000
#endif
#ifndef DOCHOSTUIFLAG_ENABLE_ACTIVEX_INACTIVATE_MODE
#define DOCHOSTUIFLAG_ENABLE_ACTIVEX_INACTIVATE_MODE 0x20000000
#endif
class HTMLContainer2;
typedef BOOL( CALLBACK *BROWSERCB )( HTMLContainer2 *pContiner, DISPID dispId, DISPPARAMS FAR *pDispParams, LPVOID pUser ); // return TRUE to block normal processing
typedef enum tagCONTAINERSTYLE
{
CSTYLE_NORMAL = 0x00, // nothing
CSTYLE_NAVIGATE2_NOCLICKSOUND = 0x01, // prevents click sound in Nvigate2 calls
CSTYLE_NOCLICKSOUND = 0x02, // prevents all click sounds ( requires testing )
} CONTAINERSTYLE;
BOOL HTMLContainer2_Initialize();
BOOL HTMLContainer2_Uninitialize();
class HTMLContainer2 : public IOleClientSite,
public IOleInPlaceSite,
public IOleInPlaceFrame,
public IOleControlSite,
public IDocHostUIHandler2,
public IDocHostShowUI,
public IOleCommandTarget,
public IServiceProvider,
public IDispatch
{
public:
typedef enum
{
uiToolbar = 1,
uiStatusbar = 2,
uiMenubar = 3,
uiAddressbar = 4,
} uiElement;
typedef enum
{
wndLeft = 0x0001,
wndTop = 0x0002,
wndWidth = 0x0004,
wndHeight = 0x0008,
wndRelative = 0x0010,
} windowPosFlags;
typedef enum
{
msgNavigate2 = 0,
msgNavigateToName = 1,
} redirectedMessage;
protected:
HTMLContainer2( HWND waWindow, HWND hwndParent );
virtual ~HTMLContainer2( void );
public:
// *** IUnknown Methods ***
STDMETHOD( QueryInterface )( REFIID riid, PVOID *ppvObject );
STDMETHOD_( ULONG, AddRef )( void );
STDMETHOD_( ULONG, Release )( void );
protected:
// *** IOleInPlaceUIWindow Methods ***
STDMETHOD( GetBorder )( LPRECT lprectBorder );
STDMETHOD( RequestBorderSpace )( LPCBORDERWIDTHS lpborderwidths );
STDMETHOD( SetBorderSpace )( LPCBORDERWIDTHS lpborderwidths );
STDMETHOD( SetActiveObject )( IOleInPlaceActiveObject *pActiveObject,
LPCOLESTR lpszObjName );
// *** IOleClientSite Methods ***
STDMETHOD( SaveObject )( );
STDMETHOD( GetMoniker )( DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER *ppMk );
STDMETHOD( GetContainer )( LPOLECONTAINER *ppContainer );
STDMETHOD( ShowObject )( );
STDMETHOD( OnShowWindow )( BOOL fShow );
STDMETHOD( RequestNewObjectLayout )( );
// *** IOleWindow Methods ***
STDMETHOD( GetWindow ) ( HWND *phwnd );
STDMETHOD( ContextSensitiveHelp ) ( BOOL fEnterMode );
// *** IOleInPlaceSite Methods ***
STDMETHOD( CanInPlaceActivate ) ( void );
STDMETHOD( OnInPlaceActivate ) ( void );
STDMETHOD( OnUIActivate ) ( void );
STDMETHOD( GetWindowContext ) ( IOleInPlaceFrame **ppFrame, IOleInPlaceUIWindow **ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo );
STDMETHOD( Scroll ) ( SIZE scrollExtent );
STDMETHOD( OnUIDeactivate ) ( BOOL fUndoable );
STDMETHOD( OnInPlaceDeactivate ) ( void );
STDMETHOD( DiscardUndoState ) ( void );
STDMETHOD( DeactivateAndUndo ) ( void );
STDMETHOD( OnPosRectChange ) ( LPCRECT lprcPosRect );
// *** IOleInPlaceFrame Methods ***
STDMETHOD( InsertMenus )( HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths );
STDMETHOD( SetMenu )( HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject );
STDMETHOD( RemoveMenus )( HMENU hmenuShared );
STDMETHOD( SetStatusText )( LPCOLESTR pszStatusText );
STDMETHOD( EnableModeless )( BOOL fEnable );
STDMETHOD( TranslateAccelerator )( LPMSG lpmsg, WORD wID );
// *** IOleControlSite Methods ***
STDMETHOD( OnControlInfoChanged )( void );
STDMETHOD( LockInPlaceActive )( BOOL fLock );
STDMETHOD( GetExtendedControl )( IDispatch **ppDisp );
STDMETHOD( TransformCoords )( POINTL *pptlHimetric, POINTF *pptfContainer, DWORD dwFlags );
STDMETHOD( TranslateAccelerator )( LPMSG pMsg, DWORD grfModifiers );
STDMETHOD( OnFocus )( BOOL fGotFocus );
STDMETHOD( ShowPropertyFrame )( void );
// *** IDispatch Methods ***
STDMETHOD( GetIDsOfNames )( REFIID riid, OLECHAR FAR *FAR *rgszNames, unsigned int cNames, LCID lcid, DISPID FAR *rgdispid );
STDMETHOD( GetTypeInfo )( unsigned int itinfo, LCID lcid, ITypeInfo FAR *FAR *pptinfo );
STDMETHOD( GetTypeInfoCount )( unsigned int FAR *pctinfo );
STDMETHOD( Invoke )( DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR *pexecinfo, unsigned int FAR *puArgErr );
// *** IDocHostUIHandler Methods ***
STDMETHOD( ShowContextMenu )( DWORD dwID, POINT __RPC_FAR *ppt, IUnknown __RPC_FAR *pcmdtReserved, IDispatch __RPC_FAR *pdispReserved );
STDMETHOD( GetHostInfo )( DOCHOSTUIINFO __RPC_FAR *pInfo );
STDMETHOD( ShowUI )( DWORD dwID, IOleInPlaceActiveObject __RPC_FAR *pActiveObject, IOleCommandTarget __RPC_FAR *pCommandTarget, IOleInPlaceFrame __RPC_FAR *pFrame, IOleInPlaceUIWindow __RPC_FAR *pDoc );
STDMETHOD( HideUI )( void );
STDMETHOD( UpdateUI )( void );
STDMETHOD( OnDocWindowActivate )( BOOL fActivate );
STDMETHOD( OnFrameWindowActivate )( BOOL fActivate );
STDMETHOD( ResizeBorder )( LPCRECT prcBorder, IOleInPlaceUIWindow __RPC_FAR *pUIWindow, BOOL fRameWindow );
STDMETHOD( TranslateAccelerator )( LPMSG lpMsg, const GUID __RPC_FAR *pguidCmdGroup, DWORD nCmdID );
STDMETHOD( GetOptionKeyPath )( LPOLESTR __RPC_FAR *pchKey, DWORD dw );
STDMETHOD( GetDropTarget )( IDropTarget __RPC_FAR *pDropTarget, IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget );
STDMETHOD( GetExternal )( IDispatch __RPC_FAR *__RPC_FAR *ppDispatch );
STDMETHOD( TranslateUrl )( DWORD dwTranslate, OLECHAR __RPC_FAR *pchURLIn, OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut );
STDMETHOD( FilterDataObject )( IDataObject __RPC_FAR *pDO, IDataObject __RPC_FAR *__RPC_FAR *ppDORet );
// STDMETHOD (EnableModeless)(BOOL fEnable);
// *** IDocHostUIHandler2 Methods ***
STDMETHOD( GetOverrideKeyPath )( LPOLESTR __RPC_FAR *pchKey, DWORD dw );
// *** IDocHostShowUI ***
STDMETHOD( ShowHelp )( HWND hwnd, LPOLESTR pszHelpFile, UINT uCommand, DWORD dwData, POINT ptMouse, IDispatch *pDispatchObjectHit );
STDMETHOD( ShowMessage )( HWND hwnd, LPOLESTR lpstrText, LPOLESTR lpstrCaption, DWORD dwType, LPOLESTR lpstrHelpFile, DWORD dwHelpContext, LRESULT *plResult );
/*** IOleCommandTarget ***/
STDMETHOD( QueryStatus )( const GUID *pguidCmdGroup, ULONG cCmds, OLECMD *prgCmds, OLECMDTEXT *pCmdText );
STDMETHOD( Exec )( const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut );
/*** IServiceProvider ***/
STDMETHOD( QueryService )( REFGUID guidService, REFIID riid, void **ppv );
public:
STDMETHOD( Initialize )( void );
STDMETHOD( Finish )( void );
STDMETHOD( UnadviseBrowserEvents )( void );
STDMETHOD( SetLocation )( int x, int y, int width, int height );
STDMETHOD( SetFocus )( BOOL fFocused );
virtual BOOL TranslateKey( LPMSG pMsg );
HWND GetHostHWND( void );
HWND GetParentHWND( void );
HRESULT NavigateEx( IWebBrowser2 *pWeb2, VARIANT *URL, VARIANT *Flags, VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers );
HRESULT Navigate2( VARIANT *URL, VARIANT *Flags, VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers );
HRESULT PostNavigate2( VARIANT *URL, VARIANT *Flags, VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers ); // navigate using postmessage API
HRESULT NavigateToName( LPCWSTR pszUrl, UINT fFlags );
HRESULT NavigateToNameEx( IWebBrowser2 *pWeb2, LPCWSTR pszUrl, UINT fFlags );
HRESULT PostNavigateToName( LPCWSTR pszUrl, UINT fFlags );
HRESULT WriteHTML( LPCWSTR pszHTML );
HRESULT WriteDocument( BSTR data ); // if succeeded will free bstr
HRESULT InvokeScriptFunction( LPCWSTR pszFuncName, LCID lcid, DISPPARAMS FAR *pDispParams, VARIANT FAR *pVarResult, EXCEPINFO FAR *pExcepInfo, UINT FAR *puArgErr );
HRESULT GetIDispatch( IDispatch **pDisp );
HRESULT GetIUnknown( IUnknown **pUnk );
HRESULT GetIWebBrowser2( IWebBrowser2 **pWeb2 );
// Registers cursors to use with browser// set hCurToUse = NULL to remove
// hCurToUse will be destryoed using DestroyCursor, make sure that this is not shared resource (use CopyCursor)
STDMETHOD( RegisterBrowserCursor )( INT nSysCurID, HCURSOR hCurToUse );
// used by MTBrowser
BROWSERCB RegisterBrowserEventCB( BROWSERCB fnBrowserCB, LPVOID pUserData );
static HRESULT InternetSetFeatureEnabled( INTERNETFEATURELIST FeatureEntry, DWORD dwFlags, BOOL fEnable );
static HRESULT InternetIsFeatureEnabled( INTERNETFEATURELIST FeatureEntry, DWORD dwFlags );
protected:
virtual void OnBeforeNavigate( IDispatch *pDispatch, VARIANT *URL, VARIANT *Flags, VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers, VARIANT_BOOL *Cancel );
virtual void OnNavigateError( IDispatch *pDispatch, VARIANT *URL, VARIANT *TargetFrameName, VARIANT *StatusCode, VARIANT_BOOL *Cancel ){}
virtual void OnNavigateComplete( IDispatch *pDispatch, VARIANT *URL ) {}
virtual void OnDocumentComplete( IDispatch *pDispatch, VARIANT *URL ) {}
virtual void OnDocumentReady( IDispatch *pDispatch, VARIANT *URL ) {} // top frame OnDocumentComplete
virtual void OnDownloadBegin( void ) {}
virtual void OnDownloadComplete( void ) {}
virtual void OnFileDownload( VARIANT_BOOL *ActiveDocument, VARIANT_BOOL *Cancel ) {}
virtual void OnNewWindow2( IDispatch **ppDisp, VARIANT_BOOL *Cancel ) {}
virtual void OnNewWindow3( IDispatch **ppDisp, VARIANT_BOOL *Cancel, DWORD dwFlags, BSTR bstrUrlContext, BSTR bstrUrl ) {}
virtual void OnProgressChange( long Progress, long ProgressMax ) {}
virtual void OnStatusTextChange( LPCWSTR pszText ) {}
virtual void OnCommandStateChange( LONG commandId, VARIANT_BOOL Enable ) {}
virtual void OnSetSecureLockIcon( UINT secureLockIcon ) {}
virtual void OnNavigateCancelled( LPCWSTR pszUrl, VARIANT_BOOL *Cancel ) {}
virtual void OnTitleChange( BSTR pszText ) {}
virtual void OnVisibleChange( VARIANT_BOOL fVisible ) {}
virtual void OnWindowClosing( VARIANT_BOOL IsChildWindow, VARIANT_BOOL *Cancel ) {}
virtual void OnShowUiElement( UINT elementId, VARIANT_BOOL fSHow ) {}
virtual void OnWindowSetResizable( VARIANT_BOOL Enable ) {}
virtual void OnClientToHostWindow( LONG *CX, LONG *CY ) {}
virtual void OnSetWindowPos( UINT flags, LONG x, LONG y, LONG cx, LONG cy ) {}
virtual void OnEnableFullscreen( VARIANT_BOOL Enable ) {}
virtual COLORREF OnGetHostBkColor( void );
virtual DWORD OnGetHostInfoFlags( void );
virtual OLECHAR *OnGetHostCSS( void ); // use CoTaskMemAlloc to allocate string
virtual OLECHAR *OnGetHostNamespace( void ); // use CoTaskMemAlloc to allocate string
virtual DWORD OnGetDownlodFlags( void );
virtual LPCWSTR OnGetUserAgent( void );
virtual DWORD GetContainerStyle( void );
BOOL ValidateURLHost( LPCWSTR pszUrl );
HRESULT IsFrameset( IWebBrowser2 *pWeb2 );
HRESULT GetFramesCount( IWebBrowser2 *pWeb2, INT *frameCount );
HRESULT GetAppVersion( BSTR *p );
HRESULT GetUserAgent( BSTR *p );
virtual HANDLE InitializePopupHook( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { return NULL; }
virtual void DeletePopupHook( HANDLE hHoook ) {}
virtual void InitializeMenuPopup( HWND hwnd, HMENU hMenu, INT iPos, BOOL fWindowMenu ) {}
virtual void ProcessRedirectedMessage( HWND hwnd, UINT messageId, LPARAM param );
HRESULT PostRedirectMessage( UINT messageId, LPARAM param );
virtual BOOL InputLangChangeRequest( HWND hwnd, UINT flags, HKL hkl ) { return FALSE; }
virtual void InputLangChange( UINT charset, HKL hkl ) {}
virtual void OnClosePopupInternal() {}
private:
/// helpers
friend static HRESULT HTMLContainer2_OnShowUiElementHelper( HTMLContainer2 *instance, UINT elementId, DISPPARAMS *pDispParams );
protected:
LONG ref; // ref count
IUnknown *pUnk; // IUnknown of contained object
RECT rect; //
HWND hParent; // window handle of the container
private:
DWORD dwCookie;
DWORD dwFlags;
BROWSERCB fnBrwoserCB;
LPVOID userData;
BOOL bNavigating;
VOID *hCursors;
INT nCursors;
BOOL ensureChakraLoaded;
HWND winampWindow;
friend static LRESULT SubclassControl_WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
};
#endif

47
Src/nu/LockFreeItem.h Normal file
View File

@ -0,0 +1,47 @@
#pragma once
#include <windows.h>
/* This data structure holds one item, and returns you the old item when you replace it
it's sort of a "stack of 1" */
template <class value_t>
class LockFreeItem
{
public:
typedef value_t *ptr_t;
LockFreeItem() { item = 0; }
value_t *GetItem()
{
for(;;) // keep trying until we get this right
{
volatile ptr_t ret;
InterlockedExchangePointer(&ret, item);
if (InterlockedCompareExchangePointer((volatile PVOID *)&item, 0, ret) == ret)
return ret;
}
}
// returns the previous value
value_t *SetItem(value_t *new_item)
{
for(;;) // keep trying until we get this right
{
volatile ptr_t ret;
InterlockedExchangePointer(&ret, item);
if (InterlockedCompareExchangePointer((volatile PVOID *)&item, new_item, ret) == ret)
return ret;
}
}
// if there's already a value, returns what you passed in
value_t *SetItemIfZero(value_t *new_item)
{
if (InterlockedCompareExchangePointer((volatile PVOID *)&item, new_item, 0) == 0)
return 0;
else
return new_item;
}
volatile ptr_t item;
};

156
Src/nu/LockFreeStack.h Normal file
View File

@ -0,0 +1,156 @@
#pragma once
#include <windows.h>
/* lock free stack object
multiple threads can push and pop without locking
note that order is not guaranteed. that is, if Thread 1 calls Insert before Thread 2, Thread 2's item might still make it in before.
since it uses malloc/free for the linked list nodes, it might not be considered truely lock-free.
use LockFreeStack2 if you can assure that you have a 'next' pointer in your class/struct
*/
template <class value_t>
class LockFreeStackNode
{
public:
LockFreeStackNode(value_t _data)
{
data = _data;
next = 0;
}
value_t data;
LockFreeStackNode *next;
};
template <class value_t>
class LockFreeStack
{
public:
LockFreeStack()
{
head = 0;
}
void push(value_t data)
{
LockFreeStackNode<value_t> *new_head = new LockFreeStackNode<value_t>(data);
LockFreeStackNode<value_t> *old_head = 0;
do
{
old_head = (LockFreeStackNode<value_t> *)head;
new_head->next = old_head;
} while (InterlockedCompareExchangePointer((volatile PVOID *)&head, new_head, old_head) != old_head);
}
value_t pop()
{
LockFreeStackNode<value_t> *new_head = 0, *old_head = 0;
do
{
old_head = (LockFreeStackNode<value_t> *)head;
if (old_head)
{
new_head = old_head->next;
}
else
{
new_head = 0;
}
} while (InterlockedCompareExchangePointer((volatile PVOID *)&head, new_head, old_head) != old_head);
value_t ret = old_head?old_head->data:0;
delete old_head;
return ret;
}
bool pop_ref(value_t *val)
{
LockFreeStackNode<value_t> *new_head = 0, *old_head = 0;
do
{
old_head = (LockFreeStackNode<value_t> *)head;
if (old_head)
{
new_head = old_head->next;
}
else
{
new_head = 0;
}
} while (InterlockedCompareExchangePointer((volatile PVOID *)&head, new_head, old_head) != old_head);
if (old_head)
{
*val = old_head->data;
delete old_head;
return true;
}
return false;
}
volatile LockFreeStackNode<value_t> *head;
};
template <class value_t>
class LockFreeStack2
{
public:
LockFreeStack()
{
head = 0;
}
void push(value_t *data)
{
value_t *old_head = 0;
do
{
old_head = head;
data->next = old_head;
} while (InterlockedCompareExchangePointer((volatile PVOID *)&head, data, old_head) != old_head);
}
value_t *pop()
{
value_t *new_head = 0, *old_head = 0;
do
{
old_head = head;
if (old_head)
{
new_head = old_head->next;
}
else
{
new_head = 0;
}
} while (InterlockedCompareExchangePointer((volatile PVOID *)&head, new_head, old_head) != old_head);
return old_head;
}
bool pop_ref(value_t **val)
{
value_t *new_head = 0, *old_head = 0;
do
{
old_head = head;
if (old_head)
{
new_head = old_head->next;
}
else
{
new_head = 0;
}
} while (InterlockedCompareExchangePointer((volatile PVOID *)&head, new_head, old_head) != old_head);
if (old_head)
{
*val = old_head;
return true;
}
return false;
}
volatile value_t *head;
};

42
Src/nu/MainThread.cpp Normal file
View File

@ -0,0 +1,42 @@
#include "MainThread.h"
LRESULT CALLBACK MarshallProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_USER:
{
Lambda *lambda = (Lambda *)wParam;
lambda->Run();
delete lambda;
return 0;
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
MainThread::MainThread()
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = 0;
wcex.lpfnWndProc = (WNDPROC)MarshallProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = GetModuleHandle(0);
wcex.hIcon = 0;
wcex.hCursor = 0;
wcex.hbrBackground = 0;
wcex.lpszMenuName = 0;
wcex.lpszClassName = "MainWindowMarshaller";
wcex.hIconSm = 0;
RegisterClassEx(&wcex);
mainWindow = CreateWindow( "MainWindowMarshaller", "MainWindowMarshaller", WS_DISABLED, 0, 0, 0, 0, NULL, NULL, GetModuleHandle(0), NULL);
}
MainThread mainThread;

82
Src/nu/MainThread.h Normal file
View File

@ -0,0 +1,82 @@
#ifndef NULLSOFT_MAINTHREADH
#define NULLSOFT_MAINTHREADH
#include <windows.h>
class Lambda
{
public:
virtual void Run() = 0;
};
template <class func_t, class param_t>
class LambdaC : public Lambda
{
public:
LambdaC(func_t _func, param_t _param)
: func(_func), param(_param)
{
event = CreateEvent(0, FALSE, FALSE, 0);
}
~LambdaC()
{
CloseHandle(event);
}
void Run()
{
func(param);
SetEvent(event);
}
private:
HANDLE event;
func_t func;
param_t param;
};
template <class object_t, class func_t, class param_t>
class LambdaCPP
{
public:
LambdaCPP(object_t *_object, func_t *_func, param_t _param)
: func(_func), param(_param)
{
event = CreateEvent(0, FALSE, FALSE, 0);
}
~LambdaCPP()
{
CloseHandle(event);
}
void Run()
{
object->*func(param);
SetEvent(event);
}
private:
HANDLE event;
object_t *object;
func_t *func;
param_t param;
};
class MainThread
{
public:
MainThread();
template <class func_t, class param_t>
void Run(func_t *func, param_t param)
{
Lambda *lambda = new LambdaC(func, param);
PostMessage(mainWindow, WM_USER, lambda, 0);
}
template <class object_t, class func_t, class param_t>
void Run(object_t *object, func_t *func, param_t param)
{
Lambda *lambda = new LambdaCPP(object, func, param);
PostMessage(mainWindow, WM_USER, lambda, 0);
}
private:
HWND mainWindow;
};
extern MainThread mainThread;
#endif

95
Src/nu/MakeThunk.h Normal file
View File

@ -0,0 +1,95 @@
#ifndef NULLSOFT_MAKETHUNKH
#define NULLSOFT_MAKETHUNKH
#include <vector>
class ThunkHolder
{
private:
#pragma pack(push,1)
class ThisThunk
{
private:
unsigned __int8 mov_eax_imm32;
unsigned __int32 save_ebx;
unsigned __int16 mov_reax_ebx;
unsigned __int8 pop_ebx;
unsigned __int8 push_imm32;
unsigned __int32 m_this;
unsigned __int8 m_call_rel32;
unsigned __int32 m_rel_proc;
unsigned __int8 m_pop_eax;
unsigned __int8 m_push_ebx;
unsigned __int8 m_mov_ecx_imm32_2;
unsigned __int32 m_restore_ebx;
unsigned __int16 m_mov_ebx_recx;
unsigned __int8 m_ret;
unsigned __int32 m_ebx;
public:
template <class class_t, class proc_t>
ThisThunk(class_t *pThis, proc_t proc)
{
__int32 procAdr = *(__int32 *) & proc;
/* first, save ebx to memory,
effectively: save_ebx = ebx;
*/
mov_eax_imm32 = 0xB8;
save_ebx = (__int32) & m_ebx;
mov_reax_ebx = 0x1889;
pop_ebx = 0x5B;
push_imm32 = 0x68;
m_this = (__int32)pThis;
m_call_rel32 = 0xE8;
m_rel_proc = procAdr - (__int32) & m_pop_eax;
m_pop_eax = 0x59;
m_push_ebx = 0x53;
m_mov_ecx_imm32_2 = 0xB9;
m_restore_ebx = (__int32) & m_ebx;
m_mov_ebx_recx = 0x198B;
m_ret = 0xC3;
}
/*
mov eax, &save_ebx
mov [eax], ebx
pop ebx
push pThis
call rel32 m_relproc
pop ecx
push ebx
mov ecx, &save_ebx
mov ebx, [ecx]
ret
*/
};
#pragma pack(pop)
public:
template <class class_t, class proc_t, class this_proc_t>
void operator ()(class_t *pThis, proc_t &proc, this_proc_t thisProc)
{
ThisThunk *newThunk = new ThisThunk(pThis, thisProc);
thunks.push_back(newThunk);
proc = (proc_t)newThunk;
}
~ThunkHolder()
{
std::vector<ThisThunk *>::iterator itr;
for (itr = thunks.begin();itr != thunks.end();itr++)
{
delete (*itr);
*itr = 0;
}
thunks.clear();
}
std::vector<ThisThunk *> thunks;
};
#endif

View File

@ -0,0 +1,463 @@
#include "MediaLibraryInterface.h"
#include <commctrl.h>
#include "../nu/ns_wc.h"
#include "../winamp/wa_ipc.h"
#include "../Agave/Language/api_language.h"
#define _ML_HEADER_IMPMLEMENT
#include "..\Plugins\General\gen_ml/ml_ipc_0313.h"
#undef _ML_HEADER_IMPMLEMENT
#include "..\Plugins\General\gen_ml/ml_imageloader.h"
#include <shlwapi.h>
#include <strsafe.h>
MediaLibraryInterface mediaLibrary;
void MediaLibraryInterface::AddTreeItem(MLTREEITEM &newItem)
{
SendMessage(library, WM_ML_IPC, (WPARAM) &newItem, ML_IPC_TREEITEM_ADD);
}
void MediaLibraryInterface::AddTreeItem(MLTREEITEMW &newItem)
{
SendMessage(library, WM_ML_IPC, (WPARAM) &newItem, ML_IPC_TREEITEM_ADDW);
}
int MediaLibraryInterface::AddTreeImage(int resourceId)
{
return AddTreeImage(resourceId, -1, (BMPFILTERPROC)FILTER_DEFAULT1);
}
int MediaLibraryInterface::AddTreeImage(int resourceId, int imageIndex, BMPFILTERPROC filter)
{
MLTREEIMAGE img = {instance, resourceId, imageIndex, filter, 0, 0};
return (int)(INT_PTR)SendMessage(library, WM_ML_IPC, (WPARAM) &img, ML_IPC_TREEIMAGE_ADD);
}
int MediaLibraryInterface::AddTreeImageBmp(int resourceId)
{
HMLIMGLST hmlilNavigation = MLNavCtrl_GetImageList(library);
MLIMAGESOURCE mlis = {sizeof(MLIMAGESOURCE),0};
MLIMAGELISTITEM item = {0};
item.cbSize = sizeof( MLIMAGELISTITEM );
item.hmlil = hmlilNavigation;
item.filterUID = MLIF_FILTER1_UID;
item.pmlImgSource = &mlis;
mlis.hInst = instance;
mlis.bpp = 24;
mlis.lpszName = MAKEINTRESOURCEW(resourceId);
mlis.type = SRC_TYPE_BMP;
mlis.flags = ISF_FORCE_BPP;
return MLImageList_Add(library, &item);
}
void MediaLibraryInterface::SetTreeItem(MLTREEITEM &item)
{
MLTREEITEMINFO ii = {0};
ii.item = item;
ii.mask = MLTI_IMAGE | MLTI_CHILDREN | MLTI_TEXT | MLTI_ID;
SendMessage(library, WM_ML_IPC, (WPARAM) &ii, ML_IPC_TREEITEM_SETINFO);
}
void MediaLibraryInterface::SetTreeItem(MLTREEITEMW &item)
{
MLTREEITEMINFOW ii = {0};
ii.item = item;
ii.mask = MLTI_IMAGE | MLTI_CHILDREN | MLTI_TEXT | MLTI_ID;
SendMessage(library, WM_ML_IPC, (WPARAM) &ii, ML_IPC_TREEITEM_SETINFOW);
}
void MediaLibraryInterface::InsertTreeItem(MLTREEITEM &newItem)
{
SendMessage(library, WM_ML_IPC, (WPARAM) &newItem, ML_IPC_TREEITEM_INSERT);
}
void MediaLibraryInterface::InsertTreeItem(MLTREEITEMW &newItem)
{
SendMessage(library, WM_ML_IPC, (WPARAM) &newItem, ML_IPC_TREEITEM_INSERTW);
}
void MediaLibraryInterface::RemoveTreeItem(INT_PTR treeId)
{
SendMessage(library, WM_ML_IPC, (WPARAM)treeId, ML_IPC_DELTREEITEM);
}
void MediaLibraryInterface::SelectTreeItem(INT_PTR treeId)
{
SendMessage(library, WM_ML_IPC, (WPARAM)treeId, ML_IPC_SETCURTREEITEM);
}
INT_PTR MediaLibraryInterface::GetSelectedTreeItem(void)
{
return (INT_PTR)SendMessage(library, WM_ML_IPC, 0, ML_IPC_GETCURTREEITEM);
}
void MediaLibraryInterface::UpdateTreeItem(MLTREEITEMINFO &newItem)
{
SendMessage(library, WM_ML_IPC, (WPARAM) &newItem, ML_IPC_TREEITEM_SETINFO);
}
void MediaLibraryInterface::UpdateTreeItem(MLTREEITEMINFOW &newItem)
{
SendMessage(library, WM_ML_IPC, (WPARAM) &newItem, ML_IPC_TREEITEM_SETINFOW);
}
int MediaLibraryInterface::SkinList(HWND list)
{
return (int)(INT_PTR)SendMessage(library, WM_ML_IPC, (WPARAM)list, ML_IPC_SKIN_LISTVIEW);
}
void MediaLibraryInterface::UnskinList(int token)
{
SendMessage(library, WM_ML_IPC, (WPARAM)token, ML_IPC_UNSKIN_LISTVIEW);
}
void MediaLibraryInterface::ListViewShowSort(int token, BOOL show)
{
LV_SKIN_SHOWSORT lvs = {0};
lvs.listView = token;
lvs.showSort = show;
SendMessage(library, WM_ML_IPC, (WPARAM)&lvs, ML_IPC_LISTVIEW_SHOWSORT);
}
void MediaLibraryInterface::ListViewSort(int token, int columnIndex, BOOL ascending)
{
LV_SKIN_SORT lvs = {0};
lvs.listView = token;
lvs.ascending = ascending;
lvs.columnIndex = columnIndex;
SendMessage(library, WM_ML_IPC, (WPARAM)&lvs, ML_IPC_LISTVIEW_SORT);
}
void MediaLibraryInterface::ListSkinUpdateView(int listSkin)
{
SendMessage(library, WM_ML_IPC, listSkin, ML_IPC_LISTVIEW_UPDATE);
}
void *MediaLibraryInterface::GetWADLGFunc(int num)
{
return (void *)SendMessage(library, WM_ML_IPC, (WPARAM)num, ML_IPC_SKIN_WADLG_GETFUNC);
}
void MediaLibraryInterface::PlayStream(wchar_t *url, bool force)
{
wchar_t temp[ 2048 ] = { 0 };
wchar_t *end = 0;
StringCchCopyExW( temp, 2047, url, &end, 0, 0 );
if ( end )
{
end[ 1 ] = 0; // double null terminate
mlSendToWinampStruct send;
send.type = ML_TYPE_STREAMNAMESW;
send.enqueue = force ? ( 0 | 2 ) : 0;
send.data = (void *)temp;
SendMessage( library, WM_ML_IPC, (WPARAM)&send, ML_IPC_SENDTOWINAMP );
}
}
void MediaLibraryInterface::PlayStreams(std::vector<const wchar_t*> &urls, bool force)
{
size_t totalSize = 0;
std::vector<const wchar_t*>::iterator itr;
for (itr = urls.begin();itr != urls.end();itr++)
{
totalSize += lstrlenW(*itr) + 1;
}
totalSize++;
wchar_t *sendTo = new wchar_t[totalSize];
wchar_t *ptr = sendTo;
for (itr = urls.begin();itr != urls.end();itr++)
{
//AutoChar narrow(*itr);
StringCchCopyExW(ptr, totalSize, *itr, &ptr, &totalSize, 0);
ptr++;
if (totalSize)
totalSize--;
else
break;
}
ptr[0] = 0;
mlSendToWinampStruct send;
send.type = ML_TYPE_STREAMNAMESW;
send.enqueue = force?(0 | 2):0;
send.data = sendTo;
SendMessage(library, WM_ML_IPC, (WPARAM)&send, ML_IPC_SENDTOWINAMP);
delete [] sendTo;
}
void MediaLibraryInterface::EnqueueStream(wchar_t *url, bool force)
{
wchar_t temp[2048] = {0};
wchar_t *end=0;
StringCchCopyExW(temp, 2047, url, &end, 0, 0);
if (end)
{
end[1]=0; // double null terminate
mlSendToWinampStruct send;
send.type = ML_TYPE_STREAMNAMESW;
send.enqueue = force?(1 | 2):1;
send.data = (void *)temp;
SendMessage(library, WM_ML_IPC, (WPARAM)&send, ML_IPC_SENDTOWINAMP);
}
}
int MediaLibraryInterface::SkinComboBox(HWND comboBox)
{
return (int)(INT_PTR)SendMessage(library, WM_ML_IPC, (WPARAM)comboBox, ML_IPC_SKIN_COMBOBOX);
}
void MediaLibraryInterface::UnskinComboBox(int token)
{
SendMessage(library, WM_ML_IPC, (WPARAM)token, ML_IPC_UNSKIN_COMBOBOX);
}
const char *MediaLibraryInterface::GetIniDirectory()
{
if (!iniDirectory)
{
iniDirectory = (const char*)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETINIDIRECTORY);
if (((unsigned int)(ULONG_PTR)iniDirectory) < 65536)
iniDirectory=0;
}
return iniDirectory;
}
const wchar_t *MediaLibraryInterface::GetIniDirectoryW()
{
if (!iniDirectoryW)
{
iniDirectoryW = (const wchar_t*)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETINIDIRECTORYW);
if (((unsigned int)(ULONG_PTR)iniDirectoryW) < 65536)
iniDirectoryW=0;
}
return iniDirectoryW;
}
void MediaLibraryInterface::BuildPath(const wchar_t *pathEnd, wchar_t *path, size_t numChars)
{
PathCombineW(path, GetIniDirectoryW(), pathEnd);
}
void MediaLibraryInterface::AddToSendTo(char description[], INT_PTR context, INT_PTR unique)
{
mlAddToSendToStruct s;
s.context = context;
s.desc = description;
s.user32 = unique;
SendMessage(library, WM_ML_IPC, (WPARAM)&s, ML_IPC_ADDTOSENDTO);
}
void MediaLibraryInterface::AddToSendTo(wchar_t description[], INT_PTR context, INT_PTR unique)
{
mlAddToSendToStructW s;
s.context = context;
s.desc = description;
s.user32 = unique;
SendMessage(library, WM_ML_IPC, (WPARAM)&s, ML_IPC_ADDTOSENDTOW);
}
void MediaLibraryInterface::BranchSendTo(INT_PTR context)
{
mlAddToSendToStructW s = {0};
s.context = context;
SendMessage(library, WM_ML_IPC, (WPARAM)&s, ML_IPC_BRANCHSENDTO);
}
void MediaLibraryInterface::AddToBranchSendTo(const wchar_t description[], INT_PTR context, INT_PTR unique)
{
mlAddToSendToStructW s;
s.context = context;
s.desc = const_cast<wchar_t *>(description);
s.user32 = unique;
SendMessage(library, WM_ML_IPC, (WPARAM)&s, ML_IPC_ADDTOBRANCH);
}
void MediaLibraryInterface::EndBranchSendTo(const wchar_t description[], INT_PTR context)
{
mlAddToSendToStructW s = {0};
s.context = context;
s.desc = const_cast<wchar_t *>(description);
SendMessage(library, WM_ML_IPC, (WPARAM)&s, ML_IPC_BRANCHSENDTO);
}
void MediaLibraryInterface::PlayFile(const wchar_t *url)
{
wchar_t temp[2048] = {0};
wchar_t *end=0;
StringCchCopyExW(temp, 2047, url, &end, 0, 0);
if (end)
{
end[1]=0; // double null terminate
mlSendToWinampStruct send;
send.type = ML_TYPE_FILENAMESW;
send.enqueue = 0 | 2;
send.data = (void *)temp;
SendMessage(library, WM_ML_IPC, (WPARAM)&send, ML_IPC_SENDTOWINAMP);
}
}
void MediaLibraryInterface::EnqueueFile(const wchar_t *url)
{
wchar_t temp[2048] = {0};
wchar_t *end=0;
StringCchCopyExW(temp, 2047, url, &end, 0, 0);
if (end)
{
end[1]=0; // double null terminate
mlSendToWinampStruct send;
send.type = ML_TYPE_FILENAMESW;
send.enqueue = 1 | 2;
send.data = (void *)temp;
SendMessage(library, WM_ML_IPC, (WPARAM)&send, ML_IPC_SENDTOWINAMP);
}
}
void MediaLibraryInterface::BuildPluginPath(const TCHAR *filename, TCHAR *path, size_t pathSize)
{
if (!pluginDirectory)
pluginDirectory = (const char *)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETPLUGINDIRECTORY);
#ifdef UNICODE
StringCchPrintf(path, pathSize, L"%S\\%s", pluginDirectory, filename);
#else
StringCchPrintf(path, pathSize, "%s\\%s", pluginDirectory, filename);
#endif
}
void MediaLibraryInterface::AddToMediaLibrary(const char *filename)
{
LMDB_FILE_ADD_INFO fi = {const_cast<char *>(filename), -1, -1};
SendMessage(library, WM_ML_IPC, (WPARAM)&fi, ML_IPC_DB_ADDORUPDATEFILE);
PostMessage(library, WM_ML_IPC, 0, ML_IPC_DB_SYNCDB);
}
void MediaLibraryInterface::AddToMediaLibrary(const wchar_t *filename)
{
LMDB_FILE_ADD_INFOW fi = {const_cast<wchar_t *>(filename), -1, -1};
SendMessage(library, WM_ML_IPC, (WPARAM)&fi, ML_IPC_DB_ADDORUPDATEFILEW);
PostMessage(library, WM_ML_IPC, 0, ML_IPC_DB_SYNCDB);
}
IDispatch *MediaLibraryInterface::GetDispatchObject()
{
IDispatch *dispatch = (IDispatch *)SendMessage(winamp, WM_WA_IPC, 0, IPC_GET_DISPATCH_OBJECT);
if (dispatch == (IDispatch *)1)
return 0;
else
return dispatch;
}
int MediaLibraryInterface::GetUniqueDispatchId()
{
return (int)(INT_PTR)SendMessage(winamp, WM_WA_IPC, 0, IPC_GET_UNIQUE_DISPATCH_ID);
}
void MediaLibraryInterface::ListSkinDisableHorizontalScrollbar(int listSkin)
{
SendMessage(library, WM_ML_IPC, listSkin, ML_IPC_LISTVIEW_DISABLEHSCROLL);
}
DWORD MediaLibraryInterface::AddDispatch(wchar_t *name, IDispatch *object)
{
DispatchInfo dispatchInfo;
dispatchInfo.name = name;
dispatchInfo.dispatch = object;
if (SendMessage(winamp, WM_WA_IPC, (WPARAM)&dispatchInfo, IPC_ADD_DISPATCH_OBJECT) == 0)
return dispatchInfo.id;
else
return 0;
}
void MediaLibraryInterface::GetFileInfo(const char *filename, char *title, int titleCch, int *length)
{
basicFileInfoStruct infoStruct = {0};
infoStruct.filename = filename;
infoStruct.title = title;
infoStruct.titlelen = titleCch;
SendMessage(winamp, WM_WA_IPC, (WPARAM)&infoStruct, IPC_GET_BASIC_FILE_INFO);
*length = infoStruct.length;
}
void MediaLibraryInterface::GetFileInfo(const wchar_t *filename, wchar_t *title, int titleCch, int *p_length)
{
if (filename)
{
basicFileInfoStructW infoStruct = {0};
infoStruct.filename = filename;
infoStruct.title = title;
infoStruct.titlelen = titleCch;
SendMessage(winamp, WM_WA_IPC, (WPARAM)&infoStruct, IPC_GET_BASIC_FILE_INFOW);
if ( p_length != NULL )
*p_length = infoStruct.length;
}
else
{
title[0] = 0;
*p_length = -1;
}
}
const char *MediaLibraryInterface::GetWinampIni()
{
if (winampIni && *winampIni)
return winampIni;
winampIni = (const char *)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETINIFILE);
return winampIni;
}
const wchar_t *MediaLibraryInterface::GetWinampIniW()
{
if (winampIniW && *winampIniW)
return winampIniW;
winampIniW = (const wchar_t *)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETINIFILEW);
return winampIniW;
}
INT_PTR MediaLibraryInterface::GetChildId(INT_PTR id)
{
return SendMessage(library, WM_ML_IPC, id, ML_IPC_TREEITEM_GETCHILD_ID);
}
INT_PTR MediaLibraryInterface::GetNextId(INT_PTR id)
{
return SendMessage(library, WM_ML_IPC, id, ML_IPC_TREEITEM_GETNEXT_ID);
}
void MediaLibraryInterface::RenameTreeId(INT_PTR treeId, const wchar_t *newName)
{
MLTREEITEMINFOW info = {0};
info.mask = MLTI_TEXT;
info.item.size = sizeof(info.item);
info.item.id = treeId;
info.item.title = const_cast<wchar_t *>(newName);
SendMessage(library, WM_ML_IPC, (WPARAM) &info, ML_IPC_TREEITEM_SETINFOW);
}

View File

@ -0,0 +1,157 @@
#ifndef MEDIALIBRARYINTERFACEH
#define MEDIALIBRARYINTERFACEH
#include <windows.h>
#include <tchar.h>
#include "..\Plugins\General\gen_ml/ml.h"
#include "../winamp/wa_ipc.h"
#ifndef REPLICANT_COMPAT
#include <vector>
#endif
class MediaLibraryInterface
{
public:
MediaLibraryInterface() : httpRetrieveFile( 0 ) {}
// children communicate back to the media library by SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,param,ML_IPC_X);
int AddTreeImage( int resourceId ); // returns index of the image.
int AddTreeImage( int resourceId, int imageIndex, BMPFILTERPROC filter );
int AddTreeImageBmp( int resourceId );
void AddTreeItem( MLTREEITEM &newItem );
void AddTreeItem( MLTREEITEMW &newItem );
void SetTreeItem( MLTREEITEM &item );
void SetTreeItem( MLTREEITEMW &item );
void RemoveTreeItem( INT_PTR treeId );
void SelectTreeItem( INT_PTR treeId );
void UpdateTreeItem( MLTREEITEMINFO &newItem );
void UpdateTreeItem( MLTREEITEMINFOW &newItem );
INT_PTR GetSelectedTreeItem( void );
void RenameTreeId( INT_PTR treeId, const wchar_t *newName );
INT_PTR GetChildId( INT_PTR id );
INT_PTR GetNextId( INT_PTR id );
void AddToSendTo( char description[], INT_PTR context, INT_PTR unique );
void AddToSendTo( wchar_t description[], INT_PTR context, INT_PTR unique );
void BranchSendTo( INT_PTR context );
void AddToBranchSendTo( const wchar_t description[], INT_PTR context, INT_PTR unique );
void EndBranchSendTo( const wchar_t description[], INT_PTR context );
const char *GetIniDirectory();
const wchar_t *GetIniDirectoryW();
const char *GetWinampIni();
const wchar_t *GetWinampIniW();
void BuildPath( const wchar_t *pathEnd, wchar_t *path, size_t numChars );
int SkinList( HWND list );
void UnskinList( int token );
void ListViewShowSort( int token, BOOL show );
void ListViewSort( int token, int columnIndex, BOOL ascending );
void ListSkinUpdateView( int listSkin );
void OpenURL( const wchar_t *url ) { SendMessage( winamp, WM_WA_IPC, (WPARAM)url, IPC_OPEN_URL ); }
int SkinComboBox( HWND comboBox );
void UnskinComboBox( int token );
void *GetWADLGFunc( int num );
#ifndef REPLICANT_COMPAT
void PlayStreams( std::vector<const wchar_t *> &urls, bool force = false );
#endif
void PlayStream( wchar_t *url, bool force = false );
void PlayFile( const wchar_t *url );
void EnqueueFile( const wchar_t *url );
void EnqueueStream( wchar_t *url, bool force = true );
void GetFileInfo( const char *filename, char *title, int titleCch, int *length );
void GetFileInfo( const wchar_t *filename, wchar_t *title, int titleCch, int *length );
void ClearPlaylist() { SendMessage( winamp, WM_WA_IPC, 0, IPC_DELETE ); }
void SwitchToPluginView( int itemId ) { SendMessage( library, WM_ML_IPC, itemId, ML_IPC_SETCURTREEITEM ); }
int GetWinampVersion() { return (int)(INT_PTR)SendMessage( winamp, WM_WA_IPC, 0, IPC_GETVERSION ); }
HWND library = 0;
HWND winamp = 0;
HINSTANCE instance = 0;
const char *iniDirectory = 0;
const wchar_t *iniDirectoryW = 0;
const char *winampIni = 0;
const wchar_t *winampIniW = 0;
const char *pluginDirectory = 0;
void AddPreferences( prefsDlgRec &prefs ) { SendMessage( winamp, WM_WA_IPC, (WPARAM)&prefs, IPC_ADD_PREFS_DLG ); }
void AddPreferences( prefsDlgRecW &prefs ) { SendMessage( winamp, WM_WA_IPC, (WPARAM)&prefs, IPC_ADD_PREFS_DLGW ); }
void InsertTreeItem( MLTREEITEM &newItem );
void InsertTreeItem( MLTREEITEMW &newItem );
char *GetProxy()
{
char *proxy = (char *)SendMessage( winamp, WM_WA_IPC, 0, IPC_GET_PROXY_STRING );
if ( proxy == (char *)1 || strlen(proxy) == 0 )
return 0;
else
return proxy;
}
void BuildPluginPath( const TCHAR *filename, TCHAR *path, size_t pathSize );
int ( *httpRetrieveFile )( HWND hwnd, const char *url, const char *file, const char *dlgtitle );
IDispatch *GetDispatchObject();
int GetUniqueDispatchId();
void ListSkinDisableHorizontalScrollbar( int listSkin );
void AddToMediaLibrary( const char *filename );
void AddToMediaLibrary( const wchar_t *filename );
void GoToPreferences( int id ) { SendMessage( winamp, WM_WA_IPC, id, IPC_OPENPREFSTOPAGE ); }
void GoToPreferences( prefsDlgRec &prefs ) { SendMessage( winamp, WM_WA_IPC, (WPARAM)&prefs, IPC_OPENPREFSTOPAGE ); }
void GoToPreferences( prefsDlgRecW &prefs ) { SendMessage( winamp, WM_WA_IPC, (WPARAM)&prefs, IPC_OPENPREFSTOPAGE ); }
const char *GetFontName() { return (const char *)SendMessage( winamp, WM_WA_IPC, 1, IPC_GET_GENSKINBITMAP ); }
int GetFontSize() { return (int)SendMessage( winamp, WM_WA_IPC, 3, IPC_GET_GENSKINBITMAP ); }
void AddBookmark( char *bookmark )
{
COPYDATASTRUCT cds;
cds.dwData = IPC_ADDBOOKMARK;
cds.lpData = bookmark;
cds.cbData = (int)strlen( bookmark ) + 1;
SendMessage( winamp, WM_COPYDATA, NULL, (LPARAM)&cds );
}
void AddBookmarkW( wchar_t *bookmark )
{
COPYDATASTRUCT cds;
cds.dwData = IPC_ADDBOOKMARKW;
cds.lpData = bookmark;
cds.cbData = (int)( sizeof( wchar_t ) * wcslen( bookmark ) + sizeof( wchar_t ) );
SendMessage( winamp, WM_COPYDATA, NULL, (LPARAM)&cds );
}
void ShowMediaLibrary() { SendMessage( library, WM_ML_IPC, 0, ML_IPC_ENSURE_VISIBLE ); }
const char *GetExtensionList() { return (const char *)SendMessage( winamp, WM_WA_IPC, 0, IPC_GET_EXTLIST ); }
bool IsShowing() { return !!SendMessage( library, WM_ML_IPC, 0, ML_IPC_IS_VISIBLE ); }
void RefreshPrefs( int prefs ) { SendMessage( library, WM_ML_IPC, prefs, ML_IPC_REFRESH_PREFS ); }
void GracenoteCancelRequest() { SendMessage( library, WM_ML_IPC, GRACENOTE_CANCEL_REQUEST, ML_IPC_GRACENOTE ); }// TODO: should we post this?
DWORD AddDispatch( wchar_t *name, IDispatch *object );
};
extern MediaLibraryInterface mediaLibrary;
#endif

343
Src/nu/NonBlockLock.h Normal file
View File

@ -0,0 +1,343 @@
#pragma warning (disable:4786)
#ifndef NONBLOCKLOCKH
#define NONBLOCKLOCKH
#include <windows.h>
/*
NULLSOFT_LOCK_OUTPUT_STATUS turns on/off debugging output
this can be VERY useful if you are trying to find a deadlock
each time the guard is locked or unlocked, it outputs a list of
any threads using the mutex, and their function stack
*/
#define NULLSOFT_LOCK_OUTPUT_STATS
#ifdef NULLSOFT_LOCK_OUTPUT_STATS
#include <string> // we save each function name as a string
#include <deque> // we make a list of the recursive function stack for each thread
#include <map> // and map
#include <iostream> // we output to std::cerr
#include <windows.h>
#endif
/*****
Description:
This class uses scoping to wrap a critical section (lightweight in-process mutex)
The constructor enters the mutex and the destructor leaves it. This allows it to
take advantage of automatic scoping in C++, because C++ automatically calls the destructor
when an object leaves scope.
This is _especially_ useful when you have multiple return paths, since you don't have to
repeat mutex-leaving code.
To use:
Make a LockGuard for a resource you want to protect. The guard is shared, so make it part
of your class, or a global, or whatever. The LockGuard is essentially a "token", equivalent
to your mutex handle or critical section handle.
Make an AutoLock object on the stack to lock. It will unlock automatically when the object
leaves scope.
Note: You'll want to make an object on the stack - don't use a heap object (new/delete)
unless you have weird requirements and know what you are doing.
Example:
class MyClass
{
LockGuard fileGuard;
fstream file;
void DumpSomeData() //
{
AutoLock lock(fileGuard);
file << GetData();
}
void CALLBACK NewData() // potentially called by another thread
{
AutoLock lock(fileGuard)
file << newData;
}
};
Tip: You can use "false scoping" to tweak the mutex lifetime, for example:
void DoStuff()
{
a = GetData();
{ // false scope
AutoLock lock(dataGuard);
DoCalculationsWith(a);
} // mutex will release here
SetData(a);
}
Tip: A common mistake is making a temporary object.
i.e.
CORRECT: AutoLock lock(fileGuard); // an AutoLock object called "lock" is put on the stack
INCORRECT: AutoLock(fileGuard); // An unnamed temporary is created which will be destroyed IMMEDIATELY
*******/
namespace Nullsoft
{
namespace Utility
{
class NonBlockLock;
/* the token which represents a resource to be locked */
class NonBlockLockGuard
{
public:
friend class NonBlockLock;
inline NonBlockLockGuard(char *name = "Unnamed Guard")
{
#ifdef NULLSOFT_LOCK_OUTPUT_STATS
lockName = name;
InitializeCriticalSection(&cerr_cs);
InitializeCriticalSection(&map_cs);
#endif
event=CreateEvent(NULL, FALSE, TRUE, NULL);
ownerThread=-1;
InitializeCriticalSection(&threads_cs);
}
inline ~NonBlockLockGuard()
{
#ifdef NULLSOFT_LOCK_OUTPUT_STATS
DeleteCriticalSection(&cerr_cs);
DeleteCriticalSection(&map_cs);
#endif
CloseHandle(event);
DeleteCriticalSection(&threads_cs);
}
private:
inline bool Lock()
{
HRESULT hr;
EnterCriticalSection(&threads_cs);
hr=WaitForSingleObject(event, 0);
if (hr == WAIT_TIMEOUT && ownerThread==GetCurrentThreadId())
{
LeaveCriticalSection(&threads_cs);
return false;
}
else if (hr == WAIT_OBJECT_0)
{
ownerThread=GetCurrentThreadId();
LeaveCriticalSection(&threads_cs);
return true;
}
LeaveCriticalSection(&threads_cs);
do
{
EnterCriticalSection(&threads_cs);
if (WaitForSingleObject(event, 3)==WAIT_OBJECT_0)
{
ownerThread=GetCurrentThreadId();
LeaveCriticalSection(&threads_cs);
break;
}
else
{
LeaveCriticalSection(&threads_cs);
MSG msg;
while(PeekMessage(&msg, NULL, 0, 0, 1))
{
//TranslateMessage(&msg);
DispatchMessage(&msg);
}
Sleep(3);
}
} while(true);
return true;
}
inline void Unlock()
{
//LeaveCriticalSection(&m_cs);
EnterCriticalSection(&threads_cs);
ownerThread=-1;
SetEvent(event);
LeaveCriticalSection(&threads_cs);
}
#ifdef NULLSOFT_LOCK_OUTPUT_STATS
int ThreadCount()
{
EnterCriticalSection(&map_cs);
int count = 0;
for (ThreadMap::iterator itr = threads.begin(); itr != threads.end(); itr++)
{
if (!itr->second.empty())
count++;
}
LeaveCriticalSection(&map_cs);
return count;
}
void Display()
{
EnterCriticalSection(&map_cs);
EnterCriticalSection(&cerr_cs);
if (ThreadCount() > 1 && owner)
{
std::cerr << "Guard: " << lockName << std::endl;
for (ThreadMap::iterator itr = threads.begin(); itr != threads.end(); itr++)
{
if (itr->second.empty())
continue;
std::cerr << " Thread ID: " << std::hex << itr->first << std::dec;
if (owner == itr->first)
std::cerr << " [holding the mutex] *****";
else
std::cerr << " [blocked]";
std::cerr << std::endl;
for (FunctionStack::iterator fitr = itr->second.begin(); fitr != itr->second.end(); fitr++)
{
std::cerr << " " << *fitr << "();" << std::endl;
}
}
}
LeaveCriticalSection(&cerr_cs);
LeaveCriticalSection(&map_cs);
}
void In(DWORD thread, char *functionName)
{
EnterCriticalSection(&map_cs);
threads[thread].push_back(functionName);
LeaveCriticalSection(&map_cs);
}
void Out(DWORD thread)
{
EnterCriticalSection(&map_cs);
threads[thread].pop_back();
LeaveCriticalSection(&map_cs);
}
std::string lockName;
CRITICAL_SECTION cerr_cs, map_cs;
typedef std::deque<std::string> FunctionStack; // this typedef reduce ugly c++ <>::<>::<> overkill
typedef std::map<DWORD, FunctionStack> ThreadMap;
ThreadMap threads;
DWORD owner;
#endif
private:
//CRITICAL_SECTION m_cs;
CRITICAL_SECTION threads_cs;
HANDLE event;
DWORD ownerThread;
};
/* an AutoLock locks a resource (represented by a LockGuard) for the duration of its lifetime */
class NonBlockLock
{
public:
/*
@param functionName The function name which wants the mutex
we pass it in as a char * even though it'll be converted to a std::string
to reduce overhead when OUTPUT_STATS is off
*/
inline NonBlockLock(NonBlockLockGuard &_guard, char *functionName = "function name not passed") : guard(&_guard), owner(false)
{
#ifdef NULLSOFT_LOCK_OUTPUT_STATS
thisThread = GetCurrentThreadId();
guard->In(thisThread, functionName);
guard->Display();
#endif
owner=guard->Lock();
#ifdef NULLSOFT_LOCK_OUTPUT_STATS
guard->owner = thisThread;
guard->Display();
#endif
}
inline void ManualLock(char *functionName = "manual lock")
{
#ifdef NULLSOFT_LOCK_OUTPUT_STATS
thisThread = GetCurrentThreadId();
guard->In(thisThread,functionName);
guard->Display();
#endif
owner=guard->Lock();
#ifdef NULLSOFT_LOCK_OUTPUT_STATS
guard->owner = thisThread;
guard->Display();
#endif
}
inline void ManualUnlock()
{
#ifdef NULLSOFT_LOCK_OUTPUT_STATS
guard->Display();
#endif
if (owner)
guard->Unlock();
owner=false;
#ifdef NULLSOFT_LOCK_OUTPUT_STATS
InterlockedCompareExchange((LONG *)(void *)&guard->owner, 0, (LONG)thisThread);
/* above line is functionally equivalent to:
if (guard->owner == thisThread)
guard->owner=0;
*/
guard->Out(thisThread);
guard->Display();
#endif
}
inline ~NonBlockLock()
{
#ifdef NULLSOFT_LOCK_OUTPUT_STATS
guard->Display();
#endif
if (owner)
guard->Unlock();
#ifdef NULLSOFT_LOCK_OUTPUT_STATS
InterlockedCompareExchange((LONG *)(void *)&guard->owner, 0, (LONG)thisThread);
/* above line is functionally equivalent to:
if (guard->owner == thisThread)
guard->owner=0;
*/
guard->Out(thisThread);
guard->Display();
#endif
}
NonBlockLockGuard *guard;
bool owner;
#ifdef NULLSOFT_LOCK_OUTPUT_STATS
DWORD thisThread;
#endif
};
}
}
#endif

View File

15
Src/nu/Pairs.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
namespace nu
{
template <class A, class B>
class Pair
{
public:
Pair() {}
Pair(const A &_a, const B &_b) : first(_a), second(_b) {}
A first;
B second;
};
}

125
Src/nu/ProgressTracker.h Normal file
View File

@ -0,0 +1,125 @@
#include <map>
#include "AutoLock.h"
#include <bfc/platform/types.h>
class ProgressTracker
{
public:
static const uint64_t null_position;
ProgressTracker()
{
current_position=0;
current_chunk=0;
chunks[0]=0;
chunks[null_position]=null_position;
};
void Write(uint64_t bytes_written)
{
Nullsoft::Utility::AutoLock list_lock(list_guard);
ChunkList::iterator next, itr = chunks.find(current_chunk);
current_position += bytes_written;
if (itr->second < current_position)
itr->second = current_position;
for (;;)
{
next = itr;
++next;
if (next != chunks.end() && (next->first <= itr->second))
{
itr->second = next->second;
chunks.erase(next);
}
else
break;
}
}
bool Valid(uint64_t requested_position, uint64_t requested_end)
{
Nullsoft::Utility::AutoLock list_lock(list_guard);
for (ChunkList::iterator itr=chunks.begin();itr!=chunks.end();itr++)
{
if (requested_position >= itr->first)
{
if (requested_position < itr->second)
{
if (requested_end <= itr->second)
{
return true;
}
else
{
return false;
}
}
}
}
return false;
}
bool Seek(uint64_t requested_position, uint64_t requested_end, uint64_t *new_start, uint64_t *new_end)
{
Nullsoft::Utility::AutoLock list_lock(list_guard);
uint64_t last_good_start=0;
ChunkList::iterator itr;
for (itr=chunks.begin();itr!=chunks.end();itr++)
{
if (requested_position >= itr->first)
{
current_chunk = itr->first;
if (requested_position <= itr->second)
{
ChunkList::iterator next = itr;
++next;
*new_end = next->first;
*new_start = current_position = itr->second;
if (requested_end <= itr->second)
{
return true;
}
else
{
return false;
}
}
else
{
last_good_start = itr->second;
}
}
}
if (last_good_start > requested_position)
*new_start = current_position = last_good_start;
else
{
*new_start = current_chunk = current_position = requested_position;
chunks[current_chunk] = current_chunk;
}
*new_end = null_position;
return false;
}
#if 0
void Dump()
{
ChunkList::iterator itr;
for (itr=chunks.begin();itr!=chunks.end();itr++)
{
printf("%I64u - %I64u\n", itr->first, itr->second);
}
}
#endif
typedef std::map<uint64_t, uint64_t> ChunkList;
ChunkList chunks;
uint64_t current_chunk;
uint64_t current_position;
Nullsoft::Utility::LockGuard list_guard;
};
const uint64_t ProgressTracker::null_position = (uint64_t)-1;

68
Src/nu/ReEntry.h Normal file
View File

@ -0,0 +1,68 @@
#ifndef NULLSOFT_UTILITY_RENTRYH
#define NULLSOFT_UTILITY_RENTRYH
#include <string>
namespace Nullsoft
{
namespace Utility
{
class ReEntryGuard
{
public:
ReEntryGuard() : entered(false)
{}
bool FunctionCall(std::string funcName = "Unknown")
{
if (entered)
{
char errorMsg[256];
sprintf(errorMsg, "%s branched to %s", firstFunc.c_str(), funcName.c_str());
::MessageBox(NULL, errorMsg, "Class ReEntry error", MB_OK);
return false;
}
else
{
firstFunc = funcName;
entered = true;
return true;
}
}
void LeaveFunction()
{
entered = false;
firstFunc = "";
}
private:
bool entered;
std::string firstFunc;
};
class ReEntry
{
public:
ReEntry(ReEntryGuard &_entry, std::string funcName = "Unknown") : entry(&_entry)
{
entry->FunctionCall(funcName);
}
~ReEntry()
{
entry->LeaveFunction();
}
private:
ReEntryGuard *entry;
};
}
}
#endif

640
Src/nu/RedBlackTree.cpp Normal file
View File

@ -0,0 +1,640 @@
#include "RedBlackTree.h"
#include <limits>
#include "PtrList.h"
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
void RedBlackTreeIterator::next()
{
if (node)
node = tree->GetSuccessorOf(node);
}
bool RedBlackTreeIterator::get(RedBlackTreeIterator::val_t *val)
{
if (node)
{
*val = node->GetEntry();
return true;
}
return false;
}
RedBlackTreeNode::RedBlackTreeNode()
{
}
RedBlackTreeNode::RedBlackTreeNode(key_t _key, val_t newEntry)
: storedEntry(newEntry) , key(_key)
{
}
RedBlackTreeNode::~RedBlackTreeNode()
{
}
RedBlackTreeNode::val_t RedBlackTreeNode::GetEntry() const
{
return storedEntry;
}
RedBlackTree::RedBlackTree()
{
nil = new RedBlackTreeNode;
nil->left = nil->right = nil->parent = nil;
nil->red = 0;
nil->key = std::numeric_limits<key_t>::min();
nil->storedEntry = NULL;
root = new RedBlackTreeNode;
root->parent = root->left = root->right = nil;
root->key = std::numeric_limits<key_t>::max();
root->red=0;
root->storedEntry = NULL;
numElements = 0;
}
RedBlackTreeIterator RedBlackTree::end()
{
RedBlackTreeIterator temp;
return temp;
}
RedBlackTreeIterator RedBlackTree::begin()
{
return RedBlackTreeIterator(root->left, this);
}
/***********************************************************************/
/* FUNCTION: LeftRotate */
/**/
/* INPUTS: the node to rotate on */
/**/
/* OUTPUT: None */
/**/
/* Modifies Input: this, x */
/**/
/* EFFECTS: Rotates as described in _Introduction_To_Algorithms by */
/* Cormen, Leiserson, Rivest (Chapter 14). Basically this */
/* makes the parent of x be to the left of x, x the parent of */
/* its parent before the rotation and fixes other pointers */
/* accordingly. */
/***********************************************************************/
void RedBlackTree::LeftRotate(RedBlackTreeNode* x)
{
RedBlackTreeNode* y;
/* I originally wrote this function to use the sentinel for */
/* nil to avoid checking for nil. However this introduces a */
/* very subtle bug because sometimes this function modifies */
/* the parent pointer of nil. This can be a problem if a */
/* function which calls LeftRotate also uses the nil sentinel */
/* and expects the nil sentinel's parent pointer to be unchanged */
/* after calling this function. For example, when DeleteFixUP */
/* calls LeftRotate it expects the parent pointer of nil to be */
/* unchanged. */
y=x->right;
x->right=y->left;
if (y->left != nil) y->left->parent=x; /* used to use sentinel here */
/* and do an unconditional assignment instead of testing for nil */
y->parent=x->parent;
/* instead of checking if x->parent is the root as in the book, we */
/* count on the root sentinel to implicitly take care of this case */
if (x == x->parent->left)
{
x->parent->left=y;
}
else
{
x->parent->right=y;
}
y->left=x;
x->parent=y;
#ifdef CHECK_RB_TREE_ASSUMPTIONS
CheckAssumptions();
#elif defined(DEBUG_ASSERT)
Assert(!nil->red,"nil not red in RedBlackTree::LeftRotate");
#endif
}
/***********************************************************************/
/* FUNCTION: RighttRotate */
/**/
/* INPUTS: node to rotate on */
/**/
/* OUTPUT: None */
/**/
/* Modifies Input?: this, y */
/**/
/* EFFECTS: Rotates as described in _Introduction_To_Algorithms by */
/* Cormen, Leiserson, Rivest (Chapter 14). Basically this */
/* makes the parent of x be to the left of x, x the parent of */
/* its parent before the rotation and fixes other pointers */
/* accordingly. */
/***********************************************************************/
void RedBlackTree::RightRotate(RedBlackTreeNode* y)
{
RedBlackTreeNode* x;
/* I originally wrote this function to use the sentinel for */
/* nil to avoid checking for nil. However this introduces a */
/* very subtle bug because sometimes this function modifies */
/* the parent pointer of nil. This can be a problem if a */
/* function which calls LeftRotate also uses the nil sentinel */
/* and expects the nil sentinel's parent pointer to be unchanged */
/* after calling this function. For example, when DeleteFixUP */
/* calls LeftRotate it expects the parent pointer of nil to be */
/* unchanged. */
x=y->left;
y->left=x->right;
if (nil != x->right) x->right->parent=y; /*used to use sentinel here */
/* and do an unconditional assignment instead of testing for nil */
/* instead of checking if x->parent is the root as in the book, we */
/* count on the root sentinel to implicitly take care of this case */
x->parent=y->parent;
if (y == y->parent->left)
{
y->parent->left=x;
}
else
{
y->parent->right=x;
}
x->right=y;
y->parent=x;
#ifdef CHECK_RB_TREE_ASSUMPTIONS
CheckAssumptions();
#elif defined(DEBUG_ASSERT)
Assert(!nil->red,"nil not red in RedBlackTree::RightRotate");
#endif
}
/***********************************************************************/
/* FUNCTION: TreeInsertHelp */
/**/
/* INPUTS: z is the node to insert */
/**/
/* OUTPUT: none */
/**/
/* Modifies Input: this, z */
/**/
/* EFFECTS: Inserts z into the tree as if it were a regular binary tree */
/* using the algorithm described in _Introduction_To_Algorithms_ */
/* by Cormen et al. This funciton is only intended to be called */
/* by the Insert function and not by the user */
/***********************************************************************/
void RedBlackTree::TreeInsertHelp(RedBlackTreeNode* z)
{
/* This function should only be called by RedBlackTree::Insert */
RedBlackTreeNode* x;
RedBlackTreeNode* y;
z->left=z->right=nil;
y=root;
x=root->left;
while (x != nil)
{
y=x;
if (x->key > z->key)
{
x=x->left;
}
else /* x->key <= z->key */
{
x=x->right;
}
}
z->parent=y;
if ((y == root) ||
(y->key > z->key))
{
y->left=z;
}
else
{
y->right=z;
}
#if defined(DEBUG_ASSERT)
Assert(!nil->red,"nil not red in RedBlackTree::TreeInsertHelp");
#endif
}
RedBlackTreeIterator RedBlackTree::Search(key_t key)
{
RedBlackTreeNode* x;
x=root->left;
while (x != nil)
{
if (x->key > key)
{
x=x->left;
}
else if (x->key < key)
{
x=x->right;
}
else
{
return RedBlackTreeIterator(x, this);
}
}
return end();
}
/* Before calling InsertNode the node x should have its key set */
/***********************************************************************/
/* FUNCTION: InsertNode */
/**/
/* INPUTS: newEntry is the entry to insert*/
/**/
/* OUTPUT: This function returns a pointer to the newly inserted node */
/* which is guarunteed to be valid until this node is deleted. */
/* What this means is if another data structure stores this */
/* pointer then the tree does not need to be searched when this */
/* is to be deleted. */
/**/
/* Modifies Input: tree */
/**/
/* EFFECTS: Creates a node node which contains the appropriate key and */
/* info pointers and inserts it into the tree. */
/***********************************************************************/
RedBlackTreeIterator RedBlackTree::Insert(key_t _key, val_t newEntry)
{
RedBlackTreeNode * y;
RedBlackTreeNode * x;
RedBlackTreeNode * newNode;
x = new RedBlackTreeNode(_key, newEntry);
TreeInsertHelp(x);
newNode = x;
x->red=1;
while (x->parent->red) /* use sentinel instead of checking for root */
{
if (x->parent == x->parent->parent->left)
{
y=x->parent->parent->right;
if (y->red)
{
x->parent->red=0;
y->red=0;
x->parent->parent->red=1;
x=x->parent->parent;
}
else
{
if (x == x->parent->right)
{
x=x->parent;
LeftRotate(x);
}
x->parent->red=0;
x->parent->parent->red=1;
RightRotate(x->parent->parent);
}
}
else /* case for x->parent == x->parent->parent->right */
{
/* this part is just like the section above with */
/* left and right interchanged */
y=x->parent->parent->left;
if (y->red)
{
x->parent->red=0;
y->red=0;
x->parent->parent->red=1;
x=x->parent->parent;
}
else
{
if (x == x->parent->left)
{
x=x->parent;
RightRotate(x);
}
x->parent->red=0;
x->parent->parent->red=1;
LeftRotate(x->parent->parent);
}
}
}
root->left->red=0;
numElements++;
return RedBlackTreeIterator(newNode, this);
#ifdef CHECK_RB_TREE_ASSUMPTIONS
CheckAssumptions();
#elif defined(DEBUG_ASSERT)
Assert(!nil->red,"nil not red in RedBlackTree::Insert");
Assert(!root->red,"root not red in RedBlackTree::Insert");
#endif
}
RedBlackTree::~RedBlackTree()
{
RedBlackTreeNode * x = root->left;
nu::PtrList<RedBlackTreeNode> stuffToFree;
if (x != nil)
{
if (x->left != nil)
{
stuffToFree.push_back(x->left);
}
if (x->right != nil)
{
stuffToFree.push_back(x->right);
}
// delete x->storedEntry;
delete x;
while (!stuffToFree.empty())
{
x = stuffToFree.back();
stuffToFree.pop_back();
if (x->left != nil)
{
stuffToFree.push_back(x->left);
}
if (x->right != nil)
{
stuffToFree.push_back(x->right);
}
// delete x->storedEntry;
delete x;
}
}
delete nil;
delete root;
}
void RedBlackTree::DeleteFixUp(RedBlackTreeNode* x)
{
RedBlackTreeNode * w;
RedBlackTreeNode * rootLeft = root->left;
while ((!x->red) && (rootLeft != x))
{
if (x == x->parent->left)
{
w=x->parent->right;
if (w->red)
{
w->red=0;
x->parent->red=1;
LeftRotate(x->parent);
w=x->parent->right;
}
if ((!w->right->red) && (!w->left->red))
{
w->red=1;
x=x->parent;
}
else
{
if (!w->right->red)
{
w->left->red=0;
w->red=1;
RightRotate(w);
w=x->parent->right;
}
w->red=x->parent->red;
x->parent->red=0;
w->right->red=0;
LeftRotate(x->parent);
x=rootLeft; /* this is to exit while loop */
}
}
else /* the code below is has left and right switched from above */
{
w=x->parent->left;
if (w->red)
{
w->red=0;
x->parent->red=1;
RightRotate(x->parent);
w=x->parent->left;
}
if ((!w->right->red) && (!w->left->red))
{
w->red=1;
x=x->parent;
}
else
{
if (!w->left->red)
{
w->right->red=0;
w->red=1;
LeftRotate(w);
w=x->parent->left;
}
w->red=x->parent->red;
x->parent->red=0;
w->left->red=0;
RightRotate(x->parent);
x=rootLeft; /* this is to exit while loop */
}
}
}
x->red=0;
#ifdef CHECK_RB_TREE_ASSUMPTIONS
CheckAssumptions();
#elif defined(DEBUG_ASSERT)
Assert(!nil->red,"nil not black in RedBlackTree::DeleteFixUp");
#endif
}
void RedBlackTree::Delete(RedBlackTree::key_t key)
{
RedBlackTreeIterator itr = Search(key);
DeleteNode(itr.node);
}
/***********************************************************************/
/* FUNCTION: DeleteNode */
/**/
/* INPUTS: tree is the tree to delete node z from */
/**/
/* OUTPUT: returns the RedBlackEntry stored at deleted node */
/**/
/* EFFECT: Deletes z from tree and but don't call destructor */
/**/
/* Modifies Input: z */
/**/
/* The algorithm from this function is from _Introduction_To_Algorithms_ */
/***********************************************************************/
RedBlackTree::val_t RedBlackTree::DeleteNode(RedBlackTreeNode * z)
{
RedBlackTreeNode* y;
RedBlackTreeNode* x;
val_t returnValue = z->storedEntry;
y= ((z->left == nil) || (z->right == nil)) ? z : GetSuccessorOf(z);
x= (y->left == nil) ? y->right : y->left;
if (root == (x->parent = y->parent)) /* assignment of y->p to x->p is intentional */
{
root->left=x;
}
else
{
if (y == y->parent->left)
{
y->parent->left=x;
}
else
{
y->parent->right=x;
}
}
if (y != z) /* y should not be nil in this case */
{
#ifdef DEBUG_ASSERT
Assert((y!=nil),"y is nil in DeleteNode \n");
#endif
/* y is the node to splice out and x is its child */
y->left=z->left;
y->right=z->right;
y->parent=z->parent;
z->left->parent=z->right->parent=y;
if (z == z->parent->left)
{
z->parent->left=y;
}
else
{
z->parent->right=y;
}
if (!(y->red))
{
y->red = z->red;
DeleteFixUp(x);
}
else
y->red = z->red;
delete z;
#ifdef CHECK_RB_TREE_ASSUMPTIONS
CheckAssumptions();
#elif defined(DEBUG_ASSERT)
Assert(!nil->red,"nil not black in RedBlackTree::Delete");
#endif
}
else
{
if (!(y->red)) DeleteFixUp(x);
delete y;
#ifdef CHECK_RB_TREE_ASSUMPTIONS
CheckAssumptions();
#elif defined(DEBUG_ASSERT)
Assert(!nil->red,"nil not black in RedBlackTree::Delete");
#endif
}
numElements--;
return returnValue;
}
size_t RedBlackTree::size() const
{
return numElements;
}
/***********************************************************************/
/* FUNCTION: GetPredecessorOf */
/**/
/* INPUTS: x is the node to get predecessor of */
/**/
/* OUTPUT: This function returns the predecessor of x or NULL if no */
/* predecessor exists. */
/**/
/* Modifies Input: none */
/**/
/* Note: uses the algorithm in _Introduction_To_Algorithms_ */
/***********************************************************************/
RedBlackTreeNode *RedBlackTree::GetPredecessorOf(RedBlackTreeNode * x) const
{
RedBlackTreeNode* y;
if (nil != (y = x->left)) /* assignment to y is intentional */
{
while (y->right != nil) /* returns the maximum of the left subtree of x */
{
y=y->right;
}
return(y);
}
else
{
y=x->parent;
while (x == y->left)
{
if (y == root) return(nil);
x=y;
y=y->parent;
}
return(y);
}
}
/***********************************************************************/
/* FUNCTION: GetSuccessorOf */
/**/
/* INPUTS: x is the node we want the succesor of */
/**/
/* OUTPUT: This function returns the successor of x or NULL if no */
/* successor exists. */
/**/
/* Modifies Input: none */
/**/
/* Note: uses the algorithm in _Introduction_To_Algorithms_ */
/***********************************************************************/
RedBlackTreeNode *RedBlackTree::GetSuccessorOf(RedBlackTreeNode * x) const
{
RedBlackTreeNode* y;
if (nil != (y = x->right)) /* assignment to y is intentional */
{
while (y->left != nil) /* returns the minium of the right subtree of x */
{
y=y->left;
}
return(y);
}
else
{
y=x->parent;
while (x == y->right) /* sentinel used instead of checking for nil */
{
x=y;
y=y->parent;
}
if (y == root) return(nil);
return(y);
}
}

77
Src/nu/RedBlackTree.h Normal file
View File

@ -0,0 +1,77 @@
#pragma once
// http://web.mit.edu/~emin/www/source_code/cpp_trees/index.html
class RedBlackTreeNode
{
public:
typedef int key_t;
typedef void *val_t;
friend class RedBlackTree;
public:
RedBlackTreeNode();
RedBlackTreeNode(key_t, val_t);
val_t GetEntry() const;
~RedBlackTreeNode();
protected:
val_t storedEntry;
key_t key;
int red; /* if red=0 then the node is black */
RedBlackTreeNode *left;
RedBlackTreeNode *right;
RedBlackTreeNode *parent;
};
class RedBlackTree;
class RedBlackTreeIterator
{
public:
friend RedBlackTree;
typedef int key_t;
typedef void *val_t;
RedBlackTreeIterator() : node(0), tree(0) {}
RedBlackTreeIterator(RedBlackTreeNode *_node, RedBlackTree *_tree) : node(_node), tree(_tree) {}
void next();
bool get(val_t *val);
private:
RedBlackTreeNode *node;
RedBlackTree *tree;
};
class RedBlackTree
{
public:
typedef int key_t;
typedef void *val_t;
public:
RedBlackTree();
~RedBlackTree();
RedBlackTreeIterator end();
RedBlackTreeIterator Insert(key_t, val_t);
RedBlackTreeIterator Search(key_t key);
RedBlackTreeIterator begin();
// semi-public:
void Delete(key_t key);
val_t DeleteNode(RedBlackTreeNode *);
RedBlackTreeNode *GetPredecessorOf(RedBlackTreeNode *) const;
RedBlackTreeNode *GetSuccessorOf(RedBlackTreeNode *) const;
size_t size() const;
//TemplateStack<RedBlackTreeNode *> * Enumerate(int low, int high) ;
protected:
/* A sentinel is used for root and for nil. These sentinels are */
/* created when RedBlackTreeCreate is caled. root->left should always */
/* point to the node which is the root of the tree. nil points to a */
/* node which should always be black but has aribtrary children and */
/* parent and no key or info. The point of using these sentinels is so */
/* that the root and nil nodes do not require special cases in the code */
RedBlackTreeNode *root;
RedBlackTreeNode *nil;
void LeftRotate(RedBlackTreeNode *);
void RightRotate(RedBlackTreeNode *);
void TreeInsertHelp(RedBlackTreeNode *);
void TreePrintHelper(RedBlackTreeNode *) const;
void FixUpMaxHigh(RedBlackTreeNode *);
void DeleteFixUp(RedBlackTreeNode *);
size_t numElements;
};

461
Src/nu/RingBuffer.cpp Normal file
View File

@ -0,0 +1,461 @@
/*
* RingBuffer.cpp
* simple_mp3_playback
*
* Created by Ben Allison on 11/10/07.
* Copyright 2007 Nullsoft, Inc. All rights reserved.
*
*/
#include "RingBuffer.h"
#include "../replicant/foundation/error.h"
#include "bfc/platform/types.h"
#include "bfc/platform/minmax.h"
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#ifdef MIN
#undef MIN
#endif // MIN
#define MIN(a,b) ((a<b)?(a):(b))
RingBuffer::~RingBuffer()
{
if ( ringBuffer )
free( ringBuffer );
ringBuffer = 0;
}
void RingBuffer::Reset()
{
if ( ringBuffer )
free( ringBuffer );
ringBuffer = 0;
}
bool RingBuffer::reserve( size_t bytes )
{
Reset();
ringBufferSize = bytes;
ringBuffer = (char *)calloc( ringBufferSize, sizeof( char ) );
if ( !ringBuffer )
return false;
clear();
return true;
}
int RingBuffer::expand( size_t bytes )
{
if ( bytes > ringBufferSize )
{
char *new_buffer = (char *)realloc( ringBuffer, bytes );
if ( !new_buffer )
return NErr_OutOfMemory;
size_t write_offset = ringReadPosition - ringBuffer;
size_t read_offset = ringWritePosition - ringBuffer;
/* update write pointer for the new buffer */
ringWritePosition = new_buffer + write_offset;
if ( write_offset > read_offset || !ringBufferUsed ) /* ringBufferUsed will resolve the ambiguity when ringWritePosition == ringReadPosition */
{
/* the ring buffer looks like [ RXXXW ], so we don't need to move anything.
Just update the read pointer */
ringReadPosition = new_buffer + write_offset;
}
else
{
/* [XXW RXX] needs to become [XXW RXX] */
size_t end_bytes = ringBufferSize - read_offset; // number of bytes that we need to relocate (the RXX portion)
char *new_read_pointer = &new_buffer[ bytes - end_bytes ];
memmove( new_read_pointer, ringReadPosition, end_bytes );
ringReadPosition = new_read_pointer; /* update read pointer */
}
ringBufferSize = bytes;
ringBuffer = new_buffer;
return NErr_Success;
}
else
return NErr_NoAction;
}
bool RingBuffer::empty() const
{
return ( ringBufferUsed == 0 );
}
size_t RingBuffer::read( void *dest, size_t len )
{
int8_t *out = (int8_t *)dest; // lets us do pointer math easier
size_t toCopy = MIN( ringBufferUsed, len );
size_t copied = 0;
len -= toCopy;
// read to the end of the ring buffer
size_t end = ringBufferSize - ( ringReadPosition - ringBuffer );
size_t read1 = MIN( end, toCopy );
memcpy( out, ringReadPosition, read1 );
copied += read1;
ringReadPosition += read1;
if ( ringReadPosition == ringBuffer + ringBufferSize )
ringReadPosition = ringBuffer;
// update positions
ringBufferUsed -= read1;
toCopy -= read1;
out = (int8_t *)out + read1;
// see if we still have more to read after wrapping around
if ( toCopy )
{
memcpy( out, ringReadPosition, toCopy );
copied += toCopy;
ringReadPosition += toCopy;
ringBufferUsed -= toCopy;
if ( ringReadPosition == ringBuffer + ringBufferSize )
ringReadPosition = ringBuffer;
}
return copied;
}
size_t RingBuffer::at(size_t offset, void *dest, size_t len) const
{
size_t toCopy = ringBufferUsed;
// make a local copy of this so we don't blow the original
char *ringReadPosition = this->ringReadPosition;
/* --- do a "dummy read" to deal with the offset request --- */
size_t dummy_end = ringBufferSize-(ringReadPosition-ringBuffer);
offset = MIN(toCopy, offset);
size_t read0 = MIN(dummy_end, offset);
ringReadPosition+=read0;
if (ringReadPosition == ringBuffer + ringBufferSize)
ringReadPosition = ringBuffer;
// update positions
toCopy -= read0;
offset -= read0;
// do second-half read (wraparound)
if ( offset )
{
ringReadPosition += offset;
toCopy -= offset;
}
// dummy read done
/* --- set up destination buffer and copy size --- */
int8_t *out = (int8_t *)dest; // lets us do pointer math easier
if ( toCopy > len )
toCopy = len;
size_t copied=0;
/* --- read to the end of the ring buffer --- */
size_t end = ringBufferSize - ( ringReadPosition - ringBuffer );
size_t read1 = MIN( end, toCopy );
memcpy( out, ringReadPosition, read1 );
copied += read1;
ringReadPosition += read1;
if (ringReadPosition == ringBuffer + ringBufferSize)
ringReadPosition = ringBuffer;
// update positions
toCopy -= read1;
out = (int8_t *)out + read1;
/* --- see if we still have more to read after wrapping around --- */
if (toCopy)
{
memcpy(out, ringReadPosition, toCopy);
copied += toCopy;
ringReadPosition += toCopy;
}
return copied;
}
size_t RingBuffer::peek( void *dest, size_t len ) const
{
int8_t *out = (int8_t *)dest; // lets us do pointer math easier
size_t toCopy = MIN( ringBufferUsed, len );
size_t copied = 0;
// make a local copy of this so we don't blow the original
char *ringReadPosition = this->ringReadPosition;
// read to the end of the ring buffer
size_t end = ringBufferSize - ( ringReadPosition - ringBuffer );
size_t read1 = MIN( end, toCopy );
memcpy( out, ringReadPosition, read1 );
copied += read1;
ringReadPosition += read1;
if ( ringReadPosition == ringBuffer + ringBufferSize )
ringReadPosition = ringBuffer;
// update positions
toCopy -= read1;
out = (int8_t *)out + read1;
// see if we still have more to read after wrapping around
if ( toCopy )
{
memcpy( out, ringReadPosition, toCopy );
copied += toCopy;
ringReadPosition += toCopy;
}
return copied;
}
size_t RingBuffer::advance( size_t len )
{
size_t toCopy = MIN( ringBufferUsed, len );
size_t copied = 0;
len -= toCopy;
// read to the end of the ring buffer
size_t end = ringBufferSize - ( ringReadPosition - ringBuffer );
size_t read1 = MIN( end, toCopy );
copied += read1;
ringReadPosition += read1;
if ( ringReadPosition == ringBuffer + ringBufferSize )
ringReadPosition = ringBuffer;
// update positions
toCopy -= read1;
ringBufferUsed -= read1;
// see if we still have more to read after wrapping around
if ( toCopy )
{
copied += toCopy;
ringReadPosition += toCopy;
ringBufferUsed -= toCopy;
if ( ringReadPosition == ringBuffer + ringBufferSize )
ringReadPosition = ringBuffer;
}
return copied;
}
size_t RingBuffer::avail() const
{
return ringBufferSize - ringBufferUsed;
}
size_t RingBuffer::write( const void *buffer, size_t bytes )
{
size_t used = ringBufferUsed;
size_t avail = ringBufferSize - used;
bytes = MIN( avail, bytes );
// write to the end of the ring buffer
size_t end = ringBufferSize - ( ringWritePosition - ringBuffer );
size_t copied = 0;
size_t write1 = MIN( end, bytes );
memcpy( ringWritePosition, buffer, write1 );
copied += write1;
ringWritePosition += write1;
if ( ringWritePosition == ringBuffer + ringBufferSize )
ringWritePosition = ringBuffer;
// update positions
ringBufferUsed += write1;
bytes -= write1;
buffer = (const int8_t *)buffer + write1;
// see if we still have more to write after wrapping around
if ( bytes )
{
memcpy( ringWritePosition, buffer, bytes );
copied += bytes;
ringWritePosition += bytes;
ringBufferUsed += bytes;
if ( ringWritePosition == ringBuffer + ringBufferSize )
ringWritePosition = ringBuffer;
}
return copied;
}
size_t RingBuffer::drain( Drainer *drainer, size_t max_bytes )
{
// read to the end of the ring buffer
size_t used = ringBufferUsed;
size_t bytes = used;
bytes = MIN(bytes, max_bytes);
size_t copied = 0;
size_t end = ringBufferSize-(ringReadPosition-ringBuffer);
size_t drain1 = MIN(end, bytes);
if (!drain1)
return 0;
size_t read1 = drainer->Write(ringReadPosition, drain1);
if (read1 == 0)
return 0;
copied+=read1;
ringReadPosition+=read1;
if (ringReadPosition == ringBuffer + ringBufferSize)
ringReadPosition=ringBuffer;
// update positions
ringBufferUsed -= read1;
bytes-=read1;
// see if we still have more to read after wrapping around
if (drain1 == read1 && bytes)
{
size_t read2 = drainer->Write(ringReadPosition, bytes);
copied += read2;
ringReadPosition += read2;
ringBufferUsed -= read2;
if (ringReadPosition == ringBuffer + ringBufferSize)
ringReadPosition=ringBuffer;
}
return copied;
}
size_t RingBuffer::fill(Filler *filler, size_t max_bytes)
{
// write to the end of the ring buffer
size_t used = ringBufferUsed;
size_t bytes = ringBufferSize - used;
bytes = MIN(bytes, max_bytes);
size_t copied = 0;
size_t end = ringBufferSize-(ringWritePosition-ringBuffer);
size_t fill1 = MIN(end, bytes);
if (!fill1)
return 0;
size_t write1 = filler->Read(ringWritePosition, fill1);
if (write1 == 0)
return 0;
copied+=write1;
ringWritePosition+=write1;
if (ringWritePosition == ringBuffer + ringBufferSize)
ringWritePosition=ringBuffer;
// update positions
ringBufferUsed += write1;
bytes-=write1;
// see if we still have more to write after wrapping around
if (fill1 == write1 && bytes)
{
size_t write2 = filler->Read(ringWritePosition, bytes);
copied += write2;
ringWritePosition += write2;
ringBufferUsed += write2;
if (ringWritePosition == ringBuffer + ringBufferSize)
ringWritePosition=ringBuffer;
}
return copied;
}
size_t RingBuffer::size() const
{
return ringBufferUsed;
}
void RingBuffer::clear()
{
ringBufferUsed = 0;
ringWritePosition = ringBuffer;
ringReadPosition = ringBuffer;
}
void *RingBuffer::LockBuffer()
{
return ringBuffer;
}
void RingBuffer::UnlockBuffer( size_t written )
{
ringWritePosition = ringBuffer+written;
ringBufferUsed = written;
}
size_t RingBuffer::write_position() const
{
return (size_t)ringWritePosition;
}
size_t RingBuffer::read_position() const
{
return (size_t)ringReadPosition;
}
void RingBuffer::get_read_buffer(size_t bytes, const void **buffer, size_t *bytes_available) const
{
size_t toCopy = MIN( ringBufferUsed, bytes );
// read to the end of the ring buffer
size_t end = ringBufferSize-(ringReadPosition-ringBuffer);
*bytes_available = MIN(end, toCopy);
*buffer = ringReadPosition;
}

77
Src/nu/RingBuffer.h Normal file
View File

@ -0,0 +1,77 @@
/*
* RingBuffer.h
* simple_mp3_playback
*
* Created by Ben Allison on 11/10/07.
* Copyright 2007 Nullsoft, Inc. All rights reserved.
*
* Ring Buffer class
* Thread safety:
* This class can be used from exactly two simultaneous threads without locking
* as long as one thread only writes and the other thread only reads
* the writer thread may call empty(), avail(), size(), write(), fill(
* the reader thread my call empty(), avail(), size(), read(), peek(), advance()
*
* two (or more) readers or two (or more) writers requires external locking
*
* Reset(), reserve(), clear(), LockBuffer(), UnlockBuffer() are not thread-safe
*/
#pragma once
#include <stddef.h>
class Filler
{
public:
virtual size_t Read(void *dest, size_t len)=0;
};
class Drainer
{
public:
virtual size_t Write(const void *dest, size_t len)=0;
};
class RingBuffer
{
public:
RingBuffer() {}
~RingBuffer();
void Reset();
bool reserve( size_t bytes ); // destructive.
int expand( size_t bytes ); // like reserve, but only expands upward. non-destructive. returns an NError
bool empty() const;
size_t avail() const; // how much available for writing
size_t size() const; // how much available for reading
void clear();
size_t read( void *dest, size_t len ); // returns amount actually read
size_t advance( size_t len ); // same as read() but doesn't write the data any where.
size_t peek( void *dest, size_t len ) const; // same as read() but doesn't advance the read pointer
size_t write( const void *src, size_t len );
size_t fill( Filler *filler, size_t max_bytes );
size_t drain( Drainer *drainer, size_t max_bytes );
size_t at( size_t offset, void *dest, size_t len ) const; // peeks() from offset. returns bytes read
size_t write_position() const; // returns an integer representing a write position
size_t read_position() const; // returns an integer representing the read position
void get_read_buffer( size_t bytes, const void **buffer, size_t *bytes_available ) const; /* returns a pointer that you can read data from, call advance() when you are done */
/* DO NOT USING THIS UNLESS YOU KNOW WHAT YOU'RE DOING
you should only use it when the ring buffer is empty
1) call clear() beforehand - very important!
2) call LockBuffer(), it'll give you a buffer
3) call UnlockBufer() with how much you've written
4) you catch the man
*/
void *LockBuffer();
void UnlockBuffer( size_t written );
private:
volatile size_t ringBufferUsed = 0;
size_t ringBufferSize = 0;
char *ringBuffer = 0;
char *ringWritePosition = 0;
char *ringReadPosition = 0;
};

81
Src/nu/SampleQueue.h Normal file
View File

@ -0,0 +1,81 @@
#pragma once
#include <bfc/platform/types.h>
#include <deque>
#include "../nu/AutoLock.h"
template <class SampleData>
class SampleQueue
{
public:
void PushFree(SampleData *new_sample)
{
queue_guard.Lock();
free_queue.push_front(new_sample);
queue_guard.Unlock();
}
void PushProcessed(SampleData *new_sample)
{
queue_guard.Lock();
processed_queue.push_front(new_sample);
queue_guard.Unlock();
}
// will return 0 if none ready
SampleData *PopProcessed()
{
SampleData *sample=0;
queue_guard.Lock();
if (!processed_queue.empty())
{
sample = processed_queue.back();
processed_queue.pop_back();
}
queue_guard.Unlock();
return sample;
}
SampleData *PopFree()
{
SampleData *sample=0;
queue_guard.Lock();
if (!free_queue.empty())
{
sample = free_queue.back();
free_queue.pop_back();
}
queue_guard.Unlock();
if (!sample)
sample = new SampleData;
return sample;
}
void Trim()
{
queue_guard.Lock();
//free_queue.deleteAll();
auto it_f = free_queue.begin();
while (it_f != free_queue.end())
{
SampleData* p = *it_f;
delete p;
it_f = free_queue.erase(it_f);
}
//processed_queue.deleteAll();
auto it_p = processed_queue.begin();
while (it_p != processed_queue.end())
{
SampleData* p = *it_p;
delete p;
it_p = processed_queue.erase(it_p);
}
queue_guard.Unlock();
}
private:
std::deque<SampleData*> free_queue;
std::deque<SampleData*> processed_queue;
Nullsoft::Utility::LockGuard queue_guard;
};

106
Src/nu/SendTo.h Normal file
View File

@ -0,0 +1,106 @@
#ifndef NULLSOFT_SEND_TO_HELPER_CLASS_H
#define NULLSOFT_SEND_TO_HELPER_CLASS_H
#include "../gen_ml/ml_ipc.h"
#include "../gen_ml/ml.h"
#include "../winamp/wa_ipc.h"
class SendToMenu
{
public:
SendToMenu(winampMediaLibraryPlugin &_plugin) : IPC_LIBRARY_SENDTOMENU(0), plugin(&_plugin)
{
memset(&sendTo, 0, sizeof(sendTo));
}
void AddHere(HWND hwnd, HMENU hMenu, int type)
{
sendTo.mode = 0;
sendTo.hwnd = 0;
sendTo.build_hMenu = 0;
IPC_LIBRARY_SENDTOMENU = SendMessage(plugin->hwndWinampParent, WM_WA_IPC, (WPARAM)&"LibrarySendToMenu", IPC_REGISTER_WINAMP_IPCMESSAGE);
if (IPC_LIBRARY_SENDTOMENU > 65536 && SendMessage(plugin->hwndWinampParent, WM_WA_IPC, (WPARAM)0, IPC_LIBRARY_SENDTOMENU) == (LRESULT)-1)
{
sendTo.mode = 1;
sendTo.hwnd = hwnd;
sendTo.data_type = type; //ML_TYPE_ITEMRECORDLIST;
sendTo.build_hMenu = hMenu;
}
}
bool WasClicked(int popUpReturnVal)
{
if (sendTo.mode == 2)
{
sendTo.menu_id = popUpReturnVal;
if (SendMessage(plugin->hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU) == (LRESULT)-1)
return true;
}
return false;
}
void Cleanup()
{
if (sendTo.mode)
{
sendTo.mode = 4;
SendMessage(plugin->hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU); // cleanup
}
sendTo.build_hMenu = 0;
}
bool InitPopupMenu(WPARAM wParam)
{
if (wParam && (HMENU)wParam == sendTo.build_hMenu && sendTo.mode == 1)
{
if (SendMessage(plugin->hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU) == (LRESULT)-1)
sendTo.mode = 2;
return true;
}
return false;
}
// still need to free it on your own
void SendItemRecordList(itemRecordList *obj)
{
sendTo.data_type = ML_TYPE_ITEMRECORDLIST;
sendTo.mode = 3;
sendTo.data = (void*) & obj;
SendMessage(plugin->hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU);
}
void SendFilenames(const char *filenames)
{
sendTo.data_type = ML_TYPE_FILENAMES;
sendTo.mode = 3;
sendTo.data = (void*)filenames;
SendMessage(plugin->hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU);
}
void SendFilenames(const wchar_t *filenames)
{
sendTo.data_type = ML_TYPE_FILENAMESW;
sendTo.mode = 3;
sendTo.data = (void*)filenames;
SendMessage(plugin->hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU);
}
LRESULT SendPlaylist(mlPlaylist *playlist)
{
sendTo.data_type = ML_TYPE_PLAYLIST;
sendTo.mode = 3;
sendTo.data = (void*)playlist;
return SendMessage(plugin->hwndWinampParent, WM_WA_IPC, (WPARAM)&sendTo, IPC_LIBRARY_SENDTOMENU);
}
private:
LRESULT IPC_LIBRARY_SENDTOMENU;
librarySendToMenuStruct sendTo;
winampMediaLibraryPlugin *plugin;
};
#endif

40
Src/nu/ServiceBuilder.h Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#ifndef NULLSOFT_UTILITY_SERVICE_BUILDER_H
#define NULLSOFT_UTILITY_SERVICE_BUILDER_H
#include <api/service/waservicefactoryi.h>
#include <api/service/services.h>
#ifndef WASABI_API_SVC
#define WASABI_API_SVC serviceManager
#endif
template <class api_T>
static void ServiceBuild(api_service *service, api_T *&api_t, GUID factoryGUID_t)
{
if (service)
{
waServiceFactory *factory = service->service_getServiceByGuid(factoryGUID_t);
if (factory)
{
api_t = reinterpret_cast<api_T *>( factory->getInterface() );
}
}
}
template <class api_T>
static void ServiceRelease(api_service *service, api_T *&api_t, GUID factoryGUID_t)
{
if (service && api_t)
{
waServiceFactory *factory = service->service_getServiceByGuid(factoryGUID_t);
if (factory)
{
factory->releaseInterface(api_t);
}
}
api_t = NULL;
}
#endif

183
Src/nu/ServiceWatcher.cpp Normal file
View File

@ -0,0 +1,183 @@
#include "ServiceWatcher.h"
#include <api/service/waservicefactory.h>
static void *GetService(api_service *p_serviceManager, GUID p_serviceGUID)
{
waServiceFactory *sf = p_serviceManager->service_getServiceByGuid( p_serviceGUID);
if (sf)
return sf->getInterface();
else
return 0;
}
static void ReleaseService(api_service *p_serviceManager, GUID p_serviceGUID, void *p_service)
{
waServiceFactory *sf = p_serviceManager->service_getServiceByGuid( p_serviceGUID);
if (sf)
sf->releaseInterface( p_service);
}
void ServiceWatcher::WatchWith(api_service *_serviceApi)
{
serviceManager =_serviceApi;
systemCallbacks=(api_syscb*)GetService( serviceManager, syscbApiServiceGuid);
}
void ServiceWatcher::StopWatching()
{
if (systemCallbacks)
{
systemCallbacks->syscb_deregisterCallback(this);
ReleaseService( serviceManager, syscbApiServiceGuid, systemCallbacks);
}
systemCallbacks=0;
}
void ServiceWatcher::Clear()
{
//watchList.Reset();
watchList.clear();
}
ServiceWatcher::~ServiceWatcher()
{
//StopWatching();
}
void ServiceWatcher::WatchForT(void **ptr, GUID watchGUID)
{
watchList[watchGUID]=ptr;
if (!*ptr) // try to get it if we need it
{
*ptr = GetService( serviceManager, watchGUID);
}
}
int ServiceWatcher::Notify(int msg, intptr_t param1, intptr_t param2)
{
switch (msg)
{
case SvcCallback::ONREGISTER:
{
waServiceFactory *sf = reinterpret_cast<waServiceFactory*>(param2);
GUID serviceGUID = sf->getGuid();
if (serviceGUID != INVALID_GUID)
{
WatchList::iterator itr = watchList.find(serviceGUID);
if (itr!=watchList.end())
{
void **ptr = itr->second;
if (ptr && !*ptr) // don't re-retrieve service if we already have it
{
*ptr = sf->getInterface();
}
}
}
}
break;
case SvcCallback::ONDEREGISTER:
{
waServiceFactory *sf = reinterpret_cast<waServiceFactory*>(param2);
GUID serviceGUID = sf->getGuid();
if (serviceGUID != INVALID_GUID)
{
WatchList::iterator itr = watchList.find(serviceGUID);
if (itr!=watchList.end())
{
void **ptr = itr->second;
if (ptr && *ptr)
{
// benski> probably not safe to do, so i'll leave it commented out: sf->releaseInterface(*ptr);
*ptr = 0;
}
}
}
}
break;
default: return 0;
}
return 1;
}
#define CBCLASS ServiceWatcher
START_DISPATCH;
CB(SYSCALLBACK_GETEVENTTYPE, GetEventType);
CB(SYSCALLBACK_NOTIFY, Notify);
END_DISPATCH;
#undef CBCLASS
ServiceWatcherSingle::~ServiceWatcherSingle()
{
//StopWatching();
}
void ServiceWatcherSingle::StopWatching()
{
if (systemCallbacks)
{
systemCallbacks->syscb_deregisterCallback(this);
ReleaseService( serviceManager, syscbApiServiceGuid, systemCallbacks);
}
systemCallbacks=0;
}
void ServiceWatcherSingle::WatchWith(api_service *_serviceApi)
{
serviceManager =_serviceApi;
systemCallbacks=(api_syscb*)GetService( serviceManager, syscbApiServiceGuid);
}
void ServiceWatcherSingle::WatchForT(void **ptr, GUID watchGUID)
{
service=ptr;
serviceGUID=watchGUID;
if (ptr && !*ptr) // try to get it if we need it
{
*ptr = GetService( serviceManager, watchGUID);
if (*ptr)
OnRegister();
}
}
int ServiceWatcherSingle::Notify(int msg, intptr_t param1, intptr_t param2)
{
switch (msg)
{
case SvcCallback::ONREGISTER:
{
if (service && !*service) // don't re-retrieve service if we already have it
{
waServiceFactory *sf = reinterpret_cast<waServiceFactory*>(param2);
if (sf && sf->getGuid() == serviceGUID)
{
*service = sf->getInterface();
if (*service)
OnRegister();
}
}
}
break;
case SvcCallback::ONDEREGISTER:
{
if (service && *service)
{
waServiceFactory *sf = reinterpret_cast<waServiceFactory*>(param2);
if (serviceGUID == sf->getGuid())
{
OnDeregister();
*service=0;
}
}
}
break;
default: return 0;
}
return 1;
}
#define CBCLASS ServiceWatcherSingle
START_DISPATCH;
CB(SYSCALLBACK_GETEVENTTYPE, GetEventType);
CB(SYSCALLBACK_NOTIFY, Notify);
END_DISPATCH;
#undef CBCLASS

60
Src/nu/ServiceWatcher.h Normal file
View File

@ -0,0 +1,60 @@
#pragma once
#include <api/service/api_service.h>
#include <api/syscb/callbacks/svccb.h>
#include <map>
#include <api/syscb/api_syscb.h>
class ServiceWatcher : public SysCallback
{
public:
ServiceWatcher() : serviceManager(0),systemCallbacks(0) {}
~ServiceWatcher();
void WatchWith(api_service *_serviceApi);
template <class T>
void WatchFor(T **ptr, GUID watchGUID)
{
WatchForT((void **)ptr, watchGUID);
}
void StopWatching();
void Clear();
private:
void WatchForT(void **ptr, GUID watchGUID);
typedef std::map<GUID, void **> WatchList;
WatchList watchList;
FOURCC GetEventType() { return SysCallback::SERVICE; }
int Notify(int msg, intptr_t param1, intptr_t param2);
api_service *serviceManager;
api_syscb *systemCallbacks;
protected:
RECVS_DISPATCH;
};
class ServiceWatcherSingle : public SysCallback
{
public:
ServiceWatcherSingle() : serviceManager(0),systemCallbacks(0),service(0) {}
virtual ~ServiceWatcherSingle();
void WatchWith(api_service *_serviceApi);
template <class T>
void WatchFor(T **ptr, GUID watchGUID)
{
WatchForT((void **)ptr, watchGUID);
}
virtual void OnRegister() {}
virtual void OnDeregister()=0;
void StopWatching();
private:
void WatchForT(void **ptr, GUID watchGUID);
FOURCC GetEventType() { return SysCallback::SERVICE; }
int Notify(int msg, intptr_t param1, intptr_t param2);
api_service *serviceManager;
api_syscb *systemCallbacks;
void **service;
GUID serviceGUID;
protected:
RECVS_DISPATCH;
};

86
Src/nu/Singleton.h Normal file
View File

@ -0,0 +1,86 @@
#pragma once
#ifndef NULLSOFT_UTILITY_SINGLETON_H
#define NULLSOFT_UTILITY_SINGLETON_H
#include <api/service/waservicefactoryi.h>
#include <api/service/services.h>
/*
====== Usage ======
disp_t: your Dispatchable base class
implt_t: your implementation class
SingletonServiceFactory<disp_t, impl_t> myFactory;
impl_t myImplementation;
//....
//during service registration
myFactory.Register(WASABI_API_SVC, &myImplementation);
//during service deregistration
myFactory.Deregister(WASABI_API_SVC, &myImplementation);
==== Class requirements ====
your base or implementation class requires the following three static methods
static FOURCC getServiceType(); // return your type (e.g. WaSvc::UNIQUE)... might already be defined in the dispatchable base class
static const char *getServiceName(); // return your service name
static GUID getServiceGuid(); // return your service GUID
*/
template <class disp_t, class impl_t>
class SingletonServiceFactory : public waServiceFactory
{
public:
SingletonServiceFactory() : impl(0)
{
}
~SingletonServiceFactory()
{
}
void Register(api_service *serviceManager, impl_t *new_impl)
{
impl=new_impl;
serviceManager->service_register(this);
}
void Deregister(api_service *serviceManager)
{
if (impl)
{
serviceManager->service_deregister(this);
impl=0;
}
}
private:
FOURCC svc_serviceType() { return impl_t::getServiceType(); }
const char *svc_getServiceName() { return impl_t::getServiceName(); }
GUID svc_getGuid() { return impl_t::getServiceGuid(); } // GUID per service factory, can be INVALID_GUID
void *svc_getInterface(int global_lock = TRUE) { return static_cast<disp_t *>(impl); }
int svc_supportNonLockingGetInterface() { return 1; }
int svc_releaseInterface(void *ifc) { return 0; }
const wchar_t *svc_getTestString() { return 0; }
int svc_notify(int msg, int param1 = 0, int param2 = 0) { return 0; }
private:
impl_t *impl;
protected:
#define CBCLASS SingletonServiceFactory<disp_t, impl_t>
START_DISPATCH_INLINE;
CB(WASERVICEFACTORY_GETSERVICETYPE, svc_serviceType);
CB(WASERVICEFACTORY_GETSERVICENAME, svc_getServiceName);
CB(WASERVICEFACTORY_GETGUID, svc_getGuid);
CB(WASERVICEFACTORY_GETINTERFACE, svc_getInterface);
CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, svc_supportNonLockingGetInterface);
CB(WASERVICEFACTORY_RELEASEINTERFACE, svc_releaseInterface);
CB(WASERVICEFACTORY_GETTESTSTRING, svc_getTestString);
CB(WASERVICEFACTORY_SERVICENOTIFY, svc_notify);
END_DISPATCH;
#undef CBCLASS
};
#endif

35
Src/nu/Slider.h Normal file
View File

@ -0,0 +1,35 @@
#pragma once
#include <windows.h>
#include <commctrl.h>
class Slider
{
public:
Slider(HWND hwndDlg, int id)
{
slider_hwnd = GetDlgItem(hwndDlg, id);
}
void SetRange(WORD low, WORD high, BOOL redraw = TRUE)
{
SendMessage(slider_hwnd, TBM_SETRANGE, redraw, MAKELONG(low,high));
}
void SetPosition(LPARAM position, BOOL redraw = TRUE)
{
SendMessage(slider_hwnd, TBM_SETPOS, redraw, position);
}
void SetTickFrequency(LPARAM frequency)
{
SendMessage(slider_hwnd, TBM_SETTICFREQ, frequency, 0);
}
enum
{
NO_REDRAW = FALSE,
REDRAW = TRUE,
};
private:
HWND slider_hwnd;
};

105
Src/nu/SpillBuffer.cpp Normal file
View File

@ -0,0 +1,105 @@
#include "SpillBuffer.h"
#include <stdlib.h>
#include <string.h>
#ifndef min
#define min(a,b) ((a<b)?(a):(b))
#endif
SpillBuffer::SpillBuffer()
{
spillBufferUsed=0;
spillBufferSize=0;
spillBuffer=0;
}
SpillBuffer::~SpillBuffer()
{
free(spillBuffer);
}
void SpillBuffer::reset()
{
free(spillBuffer);
spillBuffer=0;
spillBufferUsed=0;
spillBufferSize=0;
}
bool SpillBuffer::reserve(size_t bytes)
{
size_t old_spillBufferSize = spillBufferSize;
spillBufferSize=bytes;
char *new_spillBuffer = (char *)realloc(spillBuffer, spillBufferSize*2);
if (new_spillBuffer) spillBuffer = new_spillBuffer;
else
{
new_spillBuffer = (char *)calloc(spillBufferSize*2, sizeof(char));
if (new_spillBuffer)
{
memcpy(new_spillBuffer, spillBuffer, old_spillBufferSize * 2);
free(spillBuffer);
spillBuffer = new_spillBuffer;
}
else
{
spillBufferSize = old_spillBufferSize;
return false;
}
}
if (!spillBuffer) return false;
clear();
return true;
}
void SpillBuffer::clear()
{
spillBufferUsed=0;
}
size_t SpillBuffer::write(const void *buffer, size_t bytes)
{
size_t avail = spillBufferSize - spillBufferUsed;
bytes = min(avail, bytes);
memcpy(spillBuffer + spillBufferUsed, buffer, bytes);
spillBufferUsed+=bytes;
return bytes;
}
bool SpillBuffer::get(void **buffer, size_t *len)
{
*buffer = spillBuffer;
*len = spillBufferUsed;
spillBufferUsed = 0;
return true;
}
bool SpillBuffer::full() const
{
return spillBufferUsed == spillBufferSize;
}
bool SpillBuffer::empty() const
{
return spillBufferUsed == 0;
}
void SpillBuffer::remove(size_t len)
{
if (len > spillBufferUsed)
len=spillBufferUsed;
if (len)
{
memmove(spillBuffer, spillBuffer + len, spillBufferUsed - len);
}
}
size_t SpillBuffer::remaining() const
{
return spillBufferSize - spillBufferUsed;
}
size_t SpillBuffer::length() const
{
return spillBufferSize;
}

23
Src/nu/SpillBuffer.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
class SpillBuffer
{
public:
SpillBuffer();
~SpillBuffer();
bool reserve(size_t bytes);
void clear();
void reset();
size_t write(const void *src, size_t len);
bool get(void **buffer, size_t *len);
bool full() const;
bool empty() const;
void remove(size_t len); // removes len bytes from the start of the spill buffer
size_t remaining() const; // how many bytes to fill it up
size_t length() const; /* buffer length when full */
private:
volatile size_t spillBufferUsed;
size_t spillBufferSize;
char *spillBuffer;
};

66
Src/nu/ThreadQueue.cpp Normal file
View File

@ -0,0 +1,66 @@
#include "ThreadQueue.h"
#include <assert.h>
#include <time.h>
static inline __attribute__((always_inline))
void get_exceed_time(struct timespec* ptime, long delay)
{
clock_gettime(CLOCK_REALTIME, ptime);
ptime->tv_nsec += delay;
if (ptime->tv_nsec >= 1000000000L) // overflow
{
ptime->tv_nsec -= 1000000000L;
++ptime->tv_sec;
}
}
ThreadQueue::ThreadQueue()
{
buffer.reserve(256 * sizeof(void *));
sem_init(&event, 0, 0);
}
ThreadQueue::~ThreadQueue()
{
sem_destroy(&event);
}
void ThreadQueue::Queue(const void *in)
{
buffer.write(&in, sizeof(in));
sem_post(&event);
}
void *ThreadQueue::Get()
{
sem_wait(&event);
void *out=0;
size_t read = buffer.read(&out, sizeof(out));
assert(read == sizeof(out));
return out;
}
int ThreadQueue::Wait(long delay, void **val)
{
timespec t;
get_exceed_time(&t, delay);
int ret = sem_timedwait(&event, &t);
if (ret == 0)
{
size_t read = buffer.read(val, sizeof(*val));
assert(read == sizeof(*val));
}
return ret;
}
int ThreadQueue::Try(void **val)
{
int ret = sem_trywait(&event);
if (ret == 0)
{
size_t read = buffer.read(val, sizeof(*val));
assert(read == sizeof(*val));
}
return ret;
}

23
Src/nu/ThreadQueue.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include "RingBuffer.h"
#include <semaphore.h>
class ThreadQueue
{
public:
ThreadQueue();
~ThreadQueue();
void Queue(const void *);
// Get() blocks until there's something in the queue
void *Get();
// return value is same as sem_wait
// delay is in nanoseconds
int Wait(long delay, void **val);
// kind of like sem_trywait
int Try(void **val);
private:
// TODO: need to use something safer than RingBuffer, preferably a lock-free linked list so we can grow unlimited
RingBuffer buffer;
sem_t event;
};

221
Src/nu/Vectors.h Normal file
View File

@ -0,0 +1,221 @@
#ifndef NULLSOFT_VECTOR_H
#define NULLSOFT_VECTOR_H
#include <assert.h>
#include <bfc/platform/types.h>
template <class Type, int INCREMENT = 32, int MULTIPLIER=1>
class Vector
{
public:
typedef Type *iterator;
typedef const Type *const_iterator;
public:
Vector() {}
virtual ~Vector()
{
delete [] values;
}
Vector(const Vector<Type, INCREMENT, MULTIPLIER> &copy)
{
if (copy.numPtrs)
{
values = new Type[copy.numPtrs];
allocSize = copy.numPtrs;
numPtrs = copy.numPtrs;
for (size_t i = 0;i != numPtrs;i++)
{
values[i] = copy.values[i];
}
}
}
void operator=(const Vector<Type, INCREMENT, MULTIPLIER> &copy)
{
Reset();
if (copy.numPtrs)
{
values = new Type[copy.numPtrs];
allocSize = copy.numPtrs;
numPtrs = copy.numPtrs;
for (size_t i = 0;i != numPtrs;i++)
{
values[i] = copy.values[i];
}
}
}
Type &operator[](size_t index)
{
return values[index];
}
Type *data()
{
return values;
}
Type *begin() const
{
return values;
}
Type *end() const
{
if (values) return values + numPtrs; else return 0;
}
void Reset()
{
delete [] values; values = 0; numPtrs = 0; allocSize=0;
}
void clear()
{
numPtrs = 0;
}
size_t size() const
{
return numPtrs;
}
size_t capacity()
{
return allocSize;
}
Type &back()
{
return values[numPtrs-1];
}
Type &at(size_t index) const
{
return values[index];
}
void reserve(size_t size)
{
if (size <= numPtrs)
return;
Type *newTable = new Type[size];
for (size_t i = 0;i != numPtrs;i++)
{
newTable[i] = values[i];
}
allocSize = size;
delete[] values;
values = newTable;
}
void push_back(Type t)
{
if (numPtrs == allocSize)
reserve(allocSize*MULTIPLIER + INCREMENT);
values[numPtrs++] = t;
}
void insert(size_t index, const Type &value)
{
if (numPtrs == allocSize)
reserve(allocSize*MULTIPLIER + INCREMENT);
for (size_t i = numPtrs;i != index;i--)
{
values[i] = values[i-1];
}
values[index] = value;
numPtrs++;
}
void append(size_t size, Type *t)
{
reserve(numPtrs + size + INCREMENT);
for (size_t i = 0;i != size;i++)
{
push_back(t[i]);
}
}
void pop_back()
{
if (numPtrs)
{
numPtrs--;
// next line removed to allow structs and classes
// values[numPtrs] = 0; // TODO: an inplace delete might be better?
}
}
void erase(iterator itr)
{
size_t index = itr - values;
eraseAt(index);
}
void eraseAt(size_t index)
{
if (numPtrs > index)
{
for (size_t k = index + 1; k < numPtrs; k++)
values[k-1] = values[k];
--numPtrs;
}
}
/* Removes an item by swapping it with the last item in the list. faster but can ruin order */
void eraseAtFast(size_t index)
{
if (index < numPtrs)
{
values[index] = values[--numPtrs];
// if (numPtrs != index)
// values[numPtrs]=0;
}
}
bool empty() const
{
return numPtrs == 0;
}
void resize(size_t newSize, Type val)
{
if (newSize < numPtrs)
{
numPtrs = newSize;
}
else if (newSize > numPtrs)
{
reserve(allocSize + (newSize - numPtrs) + INCREMENT);
while(numPtrs < newSize)
{
values[numPtrs] = val;
numPtrs++;
}
}
}
void resize(size_t newSize)
{
if (newSize < numPtrs)
{
numPtrs = newSize;
}
else if (newSize > numPtrs)
{
reserve(allocSize + (newSize - numPtrs) + INCREMENT);
numPtrs = newSize;
}
}
void set(Type *ptr, size_t num)
{
delete [] values;
values=ptr;
numPtrs=num;
}
private:
size_t numPtrs = 0;
size_t allocSize = 0;
Type *values = 0;
};
#endif

63
Src/nu/VideoClock.h Normal file
View File

@ -0,0 +1,63 @@
#pragma once
#include <windows.h>
namespace nu
{
class VideoClock
{
public:
VideoClock()
{
video_sync_start_time=0;
pause_start_time=0;
length_paused=0;
paused=0;
}
void Reset()
{
length_paused = 0;
paused=0;
}
void Pause()
{
paused=1;
pause_start_time = GetTickCount();
}
void Unpause()
{
paused=0;
length_paused += (GetTickCount() - pause_start_time);
}
int GetOutputTime()
{
if (paused)
{
return pause_start_time - video_sync_start_time - length_paused;
}
else
{
return GetTickCount() - video_sync_start_time - length_paused;
}
}
void Seek(int time_ms)
{
video_sync_start_time = GetTickCount() - time_ms;
length_paused = 0;
}
void Start()
{
video_sync_start_time = GetTickCount();
}
private:
DWORD video_sync_start_time;
DWORD pause_start_time;
DWORD length_paused;
int paused;
};
}

71
Src/nu/bitbuffer.cpp Normal file
View File

@ -0,0 +1,71 @@
#include "bitbuffer.h"
#include <memory.h>
#include <stdlib.h>
BitBuffer::BitBuffer()
{
buffer=0;
length=0;
bits=0;
}
void BitBuffer::WriteBit(char bit)
{
if (bits == 0)
Resize(length+1);
bit = !!bit;
unsigned char mask = 1 << (7-bits);
buffer[length-1] &= ~mask;
buffer[length-1] |= (bit << (7-bits));
bits=(bits+1)%8;
}
void BitBuffer::Resize(size_t newlen)
{
if (newlen > length)
{
unsigned char *new_buffer = (unsigned char *)realloc(buffer, newlen);
if (new_buffer)
{
buffer = new_buffer;
memset(buffer+length, 0, newlen-length); // zero out new data
length=newlen;
}
else
{
new_buffer = (unsigned char *)malloc(newlen);
if (new_buffer)
{
memcpy(new_buffer, buffer, length);
free(buffer);
buffer = new_buffer;
memset(buffer+length, 0, newlen-length); // zero out new data
length=newlen;
}
}
}
}
void BitBuffer::WriteBits(uintptr_t num, size_t bitlen)
{
for (size_t i=0;i!=bitlen;i++)
{
WriteBit((num >> (bitlen-i-1))&1);
}
}
void BitBuffer::WriteBytes(void *buffer, size_t bytes)
{
unsigned char *b = (unsigned char *)buffer;
for (size_t i=0;i!=bytes;i++)
WriteBits(b[i], 8);
}
void BitBuffer::WriteByte(unsigned char byte)
{
for (size_t i=0;i!=8;i++)
{
WriteBit((byte >> (7-i))&1);
}
}

26
Src/nu/bitbuffer.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef NULLSOFT_UTILITY_BITBUFFER_H
#define NULLSOFT_UTILITY_BITBUFFER_H
#include <stddef.h>
#ifdef _WIN32
#include <stddef.h>
#else
#include <inttypes.h>
#endif
class BitBuffer
{
public:
BitBuffer();
void WriteBit(char bit);
void WriteBits(uintptr_t num, size_t bitlen);
void WriteBytes(void *buffer, size_t bytes);
void WriteByte(unsigned char byte);
unsigned char *Get() { return buffer; }
size_t GetLength() { return length; }
private:
void Resize(size_t newlen);
unsigned char *buffer;
size_t length;
size_t bits;
};
#endif

41
Src/nu/cast64.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef NULLSOFT_UTILITY_CAST_64_H
#define NULLSOFT_UTILITY_CAST_64_H
#include <limits>
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
namespace nu
{
template<class dest, class src>
dest saturate_cast(src srcVal)
{
if (std::numeric_limits<dest>::is_bounded && srcVal > std::numeric_limits<dest>::max())
return (dest)std::numeric_limits<dest>::min);
else
return (dest)srcVal;
}
template<class dest, class src>
bool checked_cast_to(src srcVal, dest *dstVal)
{
if (!std::numeric_limits<dest>::is_bounded ||
(srcVal >= std::numeric_limits<dest>::min() && srcVal <= std::numeric_limits<dest>::max()))
{
*dstVal = (dest)srcVal;
return true;
}
else
{
return false;
}
}
}
#endif

15
Src/nu/comboskin.cpp Normal file
View File

@ -0,0 +1,15 @@
#include "comboskin.h"
#include "../nu/MediaLibraryInterface.h"
ComboSkin::ComboSkin(HWND hwnd)
: token(0)
{
token = mediaLibrary.SkinComboBox(hwnd);
}
ComboSkin::~ComboSkin()
{
mediaLibrary.UnskinComboBox(token);
}

15
Src/nu/comboskin.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef _COMBOSKIN_H
#define _COMBOSKIN_H
#include <windows.h>
class ComboSkin
{
public:
ComboSkin(HWND hwnd);
~ComboSkin();
int token;
};
#endif

59
Src/nu/cstrlib.cpp Normal file
View File

@ -0,0 +1,59 @@
/* Utility library for C strings */
#include <windows.h>
extern "C"
char *scanstr_back(char *str, char *toscan, char *defval)
{
char *s=str+strlen(str)-1;
if (strlen(str) < 1) return defval;
if (strlen(toscan) < 1) return defval;
while (1)
{
char *t=toscan;
while (*t)
if (*t++ == *s) return s;
t=CharPrev(str,s);
if (t==s) return defval;
s=t;
}
}
extern "C"
char *extension(char *fn)
{
char *s = scanstr_back(fn,".\\",fn-1);
if (s < fn) return "";
if (*s == '\\') return "";
return (s+1);
}
void CleanDirectory(char *str)
{
if (!str)
return;
int l = strlen(str);
while (l--)
{
if (str[l] == ' '
|| str[l] == '.')
str[l]=0;
else
break;
}
}
void FormatSizeStr64(char *out, __int64 size)
{
if (size < 1024*1024) wsprintf(out, "%u KB", (DWORD)(size >> 10));
else if (size < 1024*1024*1024)
{
wsprintf(out, "%u.%02u MB", (DWORD)(size >> 20), ((((DWORD)(size >> 10))&1023)*100) >> 10);
}
else
{
wsprintf(out, "%u.%02u GB", (DWORD)(size >> 30), ((((DWORD)(size >> 20))&1023)*100) >> 10);
}
}

10
Src/nu/cstrlib.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef NULLSOFT_NU_CSTRLIBH
#define NULLSOFT_NU_CSTRLIBH
extern "C"
{
char *scanstr_back(char *str, char *toscan, char *defval);
char *extension(char *fn);
}
void CleanDirectory(char *str);
void FormatSizeStr64(char *out, __int64 size);
#endif

55
Src/nu/dispatchTable.h Normal file
View File

@ -0,0 +1,55 @@
#ifndef NULLSOFT_ORB_PLUGIN_DISPATCHTABLE_HEADER
#define NULLSOFT_ORB_PLUGIN_DISPATCHTABLE_HEADER
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif
#define DISPTABLE_INCLUDE() public:\
STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid);\
STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo);\
STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo);\
STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr);\
private:\
typedef struct __DISPATCHENTRY DISPATCHENTRY;\
const static DISPATCHENTRY szDispatchTable[];
#define DISPTABLE_BEGIN()\
typedef HRESULT (DISPTABLE_CLASS::*DISPATCHHANDLER)(WORD /*wFlags*/, DISPPARAMS FAR* /*pdispparams*/, VARIANT FAR* /*pvarResult*/, UINT FAR* /*puArgErr*/);\
typedef struct __DISPATCHENTRY {\
UINT id;\
LPCWSTR name;\
DISPATCHHANDLER handler;\
} DISPATCHENTRY;\
const DISPTABLE_CLASS::DISPATCHENTRY DISPTABLE_CLASS::szDispatchTable[] = {
#define DISPTABLE_END\
};\
HRESULT DISPTABLE_CLASS::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid) {\
UINT unknowns = 0;\
for (unsigned int i = 0; i != cNames; i++) {\
rgdispid[i] = DISPID_UNKNOWN;\
for (INT j =0; j < ARRAYSIZE(szDispatchTable); j++) {\
if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, 0, rgszNames[i], -1, szDispatchTable[j].name, -1)) {\
rgdispid[i] = szDispatchTable[j].id; break; }\
}\
if (DISPID_UNKNOWN == rgdispid[i]) { unknowns++; }\
}\
return (0 != unknowns) ? DISP_E_UNKNOWNNAME : S_OK;\
}\
HRESULT DISPTABLE_CLASS::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo) { return E_NOTIMPL; }\
HRESULT DISPTABLE_CLASS::GetTypeInfoCount(unsigned int FAR * pctinfo) { return E_NOTIMPL; }\
HRESULT DISPTABLE_CLASS::Invoke(DISPID dispId, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr) {\
for (INT i = 0; i < ARRAYSIZE(szDispatchTable); i++) {\
if (dispId == szDispatchTable[i].id) { return (this->*szDispatchTable[i].handler)(wFlags, pdispparams, pvarResult, puArgErr); }\
}\
return DISP_E_MEMBERNOTFOUND;\
}
#define DISPENTRY_ADD(__id, __name, __handler)\
{ DISPTABLE_CLASS::__id, __name, &DISPTABLE_CLASS::__handler },
#define DISPHANDLER_REGISTER(__handler)\
HRESULT __handler(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr);
#endif //NULLSOFT_ORB_PLUGIN_DISPATCHTABLE_HEADER

77
Src/nu/factoryt.h Normal file
View File

@ -0,0 +1,77 @@
#pragma once
#include <api/service/waservicefactoryi.h>
#include <api/service/services.h>
/*
====== Usage ======
disp_t: your Dispatchable base class
implt_t: your implementation class
ServiceFactoryT<disp_t, impl_t> myFactory;
impl_t myImplementation;
//....
//during service registration
myFactory.Register(WASABI_API_SVC);
//during service deregistration
myFactory.Deregister(WASABI_API_SVC);
==== Class requirements ====
your base or implementation class requires the following three static methods
static FOURCC getServiceType(); // return your type (e.g. WaSvc::UNIQUE)... might already be defined in the dispatchable base class
static const char *getServiceName(); // return your service name
static GUID getServiceGuid(); // return your service GUID
must implementation a constructor that requires no parameters
*/
template <class disp_t, class impl_t>
class ServiceFactoryT : public waServiceFactory
{
public:
ServiceFactoryT()
{
}
~ServiceFactoryT()
{
}
void Register(api_service *serviceManager)
{
serviceManager->service_register(this);
}
void Deregister(api_service *serviceManager)
{
serviceManager->service_deregister(this);
}
private:
FOURCC svc_serviceType() { return impl_t::getServiceType(); }
const char *svc_getServiceName() { return impl_t::getServiceName(); }
GUID svc_getGuid() { return impl_t::getServiceGuid(); } // GUID per service factory, can be INVALID_GUID
void *svc_getInterface(int global_lock = TRUE) { return static_cast<disp_t *>(new impl_t); }
int svc_supportNonLockingGetInterface() { return 1; }
int svc_releaseInterface(void *ifc) { disp_t *disp = static_cast<disp_t *>(ifc); impl_t *impl = static_cast<impl_t *>(disp); delete impl; return 1; }
const wchar_t *svc_getTestString() { return 0; }
int svc_notify(int msg, int param1 = 0, int param2 = 0) { return 0; }
protected:
#define CBCLASS ServiceFactoryT<disp_t, impl_t>
START_DISPATCH_INLINE;
CB(WASERVICEFACTORY_GETSERVICETYPE, svc_serviceType);
CB(WASERVICEFACTORY_GETSERVICENAME, svc_getServiceName);
CB(WASERVICEFACTORY_GETGUID, svc_getGuid);
CB(WASERVICEFACTORY_GETINTERFACE, svc_getInterface);
CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, svc_supportNonLockingGetInterface);
CB(WASERVICEFACTORY_RELEASEINTERFACE, svc_releaseInterface);
CB(WASERVICEFACTORY_GETTESTSTRING, svc_getTestString);
CB(WASERVICEFACTORY_SERVICENOTIFY, svc_notify);
END_DISPATCH;
#undef CBCLASS
};

285
Src/nu/listview.cpp Normal file
View File

@ -0,0 +1,285 @@
/*
** Copyright (C) 2003 Nullsoft, Inc.
**
** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
** liable for any damages arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
** alter it and redistribute it freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
**
** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
**
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#include <windows.h>
#include <commctrl.h>
#include "listview.h"
void W_ListView::AddImageCol( int w )
{
LVCOLUMN lvc = { 0, };
lvc.mask = LVCF_WIDTH | LVCF_FMT | LVCF_SUBITEM;
lvc.fmt = LVCFMT_IMAGE;
lvc.iSubItem = m_col;
if ( w )
lvc.cx = w;
ListView_InsertColumn( m_hwnd, m_col, &lvc );
++m_col;
}
int W_ListView::GetColumnWidth( int col )
{
if ( col < 0 || col >= m_col )
return 0;
return ListView_GetColumnWidth( m_hwnd, col );
}
int W_ListView::GetParam( int p )
{
LVITEM lvi = { 0, };
lvi.mask = LVIF_PARAM;
lvi.iItem = p;
ListView_GetItem( m_hwnd, &lvi );
return (int)(INT_PTR)lvi.lParam;
}
void W_ListView::SetItemParam( int p, int param )
{
LVITEM lvi = { 0, };
lvi.iItem = p;
lvi.mask = LVIF_PARAM;
lvi.lParam = param;
ListView_SetItem( m_hwnd, &lvi );
}
void W_ListView::SetFont( HFONT newFont )
{
if ( m_font )
{
if ( m_hwnd )
SetWindowFont( m_hwnd, NULL, FALSE );
DeleteFont( m_font );
}
m_font = NULL;
if ( m_hwnd )
{
SetWindowFont( m_hwnd, newFont, FALSE );
InvalidateRect( m_hwnd, NULL, TRUE );
}
}
int W_ListView::FindItemByPoint( int x, int y )
{
int l = GetCount();
for ( int i = 0; i < l; i++ )
{
RECT r;
GetItemRect( i, &r );
if ( r.left <= x && r.right >= x && r.top <= y && r.bottom >= y )
return i;
}
return -1;
}
W_ListView::W_ListView()
{
m_hwnd = NULL;
m_col = 0;
m_font = NULL;
}
W_ListView::W_ListView( HWND hwnd )
{
m_hwnd = NULL;
m_col = 0;
m_font = NULL;
if ( IsWindow( hwnd ) )
{
m_hwnd = hwnd;
ListView_SetExtendedListViewStyleEx( m_hwnd, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP );
}
}
W_ListView::W_ListView( HWND hwndDlg, int resourceId )
{
m_hwnd = NULL;
m_col = 0;
m_font = NULL;
m_hwnd = GetDlgItem( hwndDlg, resourceId );
if ( IsWindow( m_hwnd ) )
{
ListView_SetExtendedListViewStyleEx( m_hwnd, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP );
}
}
W_ListView::~W_ListView()
{
if ( m_font )
DeleteFont( m_font );
m_font = 0;
}
void W_ListView::SetTextColors(COLORREF foregroundColor, COLORREF backgroundColor)
{
ListView_SetTextColor(m_hwnd, foregroundColor);
ListView_SetTextBkColor(m_hwnd, backgroundColor);
}
void W_ListView::InvertSelection()
{
int n = GetCount();
for (int i = 0; i < n; i++)
{
if (GetSelected(i))
Unselect(i);
else
SetSelected(i);
}
}
/* unicode / ansi trouble spots go below this line */
void W_ListView::AddAutoCol(LPTSTR text)
{
LVCOLUMN lvc = {0};
lvc.mask = LVCF_TEXT;
lvc.pszText = text;
ListView_InsertColumn(m_hwnd, m_col, &lvc);
m_col++;
}
void W_ListView::AddCol(const wchar_t *text, int w)
{
LVCOLUMN lvc = {0};
lvc.mask = LVCF_TEXT | LVCF_WIDTH;
lvc.pszText = (LPTSTR)text;
if (w)
lvc.cx = w;
SendMessageW(m_hwnd, LVM_INSERTCOLUMNW, (WPARAM)m_col, (LPARAM)&lvc);
m_col++;
}
void W_ListView::AddCol(const char *text, int w)
{
LVCOLUMN lvc = {0};
lvc.mask = LVCF_TEXT | LVCF_WIDTH;
lvc.pszText = (LPTSTR) text;
if (w) lvc.cx = w;
SendMessageA(m_hwnd, LVM_INSERTCOLUMNA, (WPARAM)m_col, (LPARAM)&lvc);
m_col++;
}
int W_ListView::AppendItem(LPCWSTR text, LPARAM param)
{
LVITEM lvi = {0};
lvi.mask = LVIF_TEXT | LVIF_PARAM;
lvi.iItem = GetCount();
lvi.pszText = (LPTSTR) text;
lvi.cchTextMax = wcslen(text);
lvi.lParam = param;
return (int)(INT_PTR)SendMessageW(m_hwnd, LVM_INSERTITEMW, 0, (LPARAM)&lvi);
}
int W_ListView::InsertItem(int p, const wchar_t *text, LPARAM param)
{
LVITEM lvi = {0};
lvi.mask = LVIF_TEXT | LVIF_PARAM;
lvi.iItem = p;
lvi.pszText = (LPTSTR) text;
lvi.cchTextMax = wcslen(text);
lvi.lParam = param;
return (int)(INT_PTR)SendMessageW(m_hwnd, LVM_INSERTITEMW, 0, (LPARAM)&lvi);
}
int W_ListView::InsertItem(int p, const char *text, LPARAM param)
{
LVITEM lvi = {0};
lvi.mask = LVIF_TEXT | LVIF_PARAM;
lvi.iItem = p;
lvi.pszText = (LPTSTR) text;
lvi.cchTextMax = lstrlenA(text);
lvi.lParam = param;
return (int)(INT_PTR)SendMessageA(m_hwnd, LVM_INSERTITEMA, 0, (LPARAM)&lvi);
}
void W_ListView::SetItemText(int p, int si, const wchar_t *text)
{
LVITEM lvi = {0};
lvi.iItem = p;
lvi.iSubItem = si;
lvi.mask = LVIF_TEXT;
lvi.pszText = (LPTSTR)text;
lvi.cchTextMax = wcslen(text);
SendMessageW(m_hwnd, LVM_SETITEMW, 0, (LPARAM)&lvi);
}
void W_ListView::SetItemText(int p, int si, const char *text)
{
LVITEM lvi = {0};
lvi.iItem = p;
lvi.iSubItem = si;
lvi.mask = LVIF_TEXT;
lvi.pszText = (LPTSTR)text;
lvi.cchTextMax = lstrlenA(text);
SendMessageA(m_hwnd, LVM_SETITEMA, 0, (LPARAM)&lvi);
}
void W_ListView::setwnd (HWND hwnd)
{
m_hwnd = hwnd;
if (IsWindow(hwnd))
{
ListView_SetExtendedListViewStyleEx(hwnd, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP);
#if defined(_UNICODE) || defined(UNICODE)
SendMessageW(hwnd, CCM_SETUNICODEFORMAT, TRUE, 0);
#endif
}
}
void W_ListView::GetText(int p, int si, wchar_t *text, int maxlen)
{
LVITEM lvi = {0};
lvi.iItem = p;
lvi.iSubItem = si;
lvi.mask = LVIF_TEXT;
lvi.pszText = (LPTSTR)text;
lvi.cchTextMax = maxlen;
SendMessageW(m_hwnd, LVM_GETITEMTEXTW, p, (LPARAM)&lvi);
}
void W_ListView::GetText(int p, int si, char *text, int maxlen)
{
LVITEM lvi = {0};
lvi.iItem = p;
lvi.iSubItem = si;
lvi.mask = LVIF_TEXT;
lvi.pszText = (LPTSTR)text;
lvi.cchTextMax = maxlen;
SendMessageA(m_hwnd, LVM_GETITEMTEXTA, p, (LPARAM)&lvi);
}

229
Src/nu/listview.h Normal file
View File

@ -0,0 +1,229 @@
/*
** Copyright (C) 2003 Nullsoft, Inc.
**
** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
** liable for any damages arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
** alter it and redistribute it freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
**
** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
**
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#ifndef _LISTVIEW_H_
#define _LISTVIEW_H_
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#ifndef LVS_EX_DOUBLEBUFFER //this will work XP only
#define LVS_EX_DOUBLEBUFFER 0x00010000
#endif
class W_ListView
{
public:
W_ListView();
W_ListView( HWND hwndView );
W_ListView( HWND hwndDlg, int resourceId );
~W_ListView();
void InvertSelection();
void SetTextColors( COLORREF foregroundColor, COLORREF backgroundColor );
void SetFont( HFONT newFont );
void setwnd( HWND hwnd );
void AddCol( const wchar_t *text, int w );
void AddCol( const char *text, int w );
void AddAutoCol( LPTSTR text );
void AddImageCol( int w );
void JustifyColumn( int column, int justificationFlag )
{
LVCOLUMN col;
col.mask = LVCF_FMT;
col.fmt = justificationFlag;
ListView_SetColumn( m_hwnd, column, &col );
}
void SetColumnWidth( int column, int width )
{
ListView_SetColumnWidth( m_hwnd, column, width );
}
int GetCount( void )
{
return ListView_GetItemCount( m_hwnd );
}
int GetParam( int p );
void DeleteItem( int n )
{
ListView_DeleteItem( m_hwnd, n );
}
void RefreshItem( int item )
{
ListView_RedrawItems( m_hwnd, item, item );
}
void RefreshAll()
{
ListView_RedrawItems( m_hwnd, 0, GetCount() );
}
void Clear( void )
{
ListView_DeleteAllItems( m_hwnd );
}
int GetSelected( int x )
{
return( ListView_GetItemState( m_hwnd, x, LVIS_SELECTED ) & LVIS_SELECTED ) ? 1 : 0;
}
int GetSelectedCount()
{
return ListView_GetSelectedCount( m_hwnd );
}
int GetNextSelected( int start = -1 )
{
return ListView_GetNextItem( m_hwnd, start, LVNI_ALL | LVNI_SELECTED );
}
int GetSelectionMark()
{
return ListView_GetSelectionMark( m_hwnd );
}
void SetSelected( int x )
{
ListView_SetItemState( m_hwnd, x, LVIS_SELECTED, LVIS_SELECTED );
}
void SelectAll()
{
ListView_SetItemState( m_hwnd, -1, LVIS_SELECTED, LVIS_SELECTED );
}
void UnselectAll()
{
ListView_SetItemState( m_hwnd, -1, 0, LVIS_SELECTED );
}
void Unselect( int x )
{
ListView_SetItemState( m_hwnd, x, 0, LVIS_SELECTED );
}
void EditItem( int x )
{
SetFocus( m_hwnd );
ListView_EditLabel( m_hwnd, x );
}
int AppendItem( LPCWSTR text, LPARAM param );
int InsertItem( int p, const wchar_t *text, LPARAM param );
int InsertItem( int p, const char *text, LPARAM param );
void GetItemRect( int i, RECT *r )
{
ListView_GetItemRect( m_hwnd, i, r, LVIR_BOUNDS );
}
void SetItemText( int p, int si, const wchar_t *text );
void SetItemText( int p, int si, const char *text );
void SetItemParam( int p, int param );
void GetText( int p, int si, char *text, int maxlen );
void GetText( int p, int si, wchar_t *text, int maxlen );
size_t GetTextLength( int p, int si )
{
LVITEM lvItem;
lvItem.cchTextMax = 0;
lvItem.pszText = 0;
lvItem.iSubItem = si;
lvItem.iItem = p;
return SendMessage( m_hwnd, LVM_GETITEMTEXT, p, (LPARAM)&lvItem );
}
int FindItemByParam( int param )
{
LVFINDINFO fi = { LVFI_PARAM,0,param };
return ListView_FindItem( m_hwnd, -1, &fi );
}
int FindItemByPoint( int x, int y );
void SetVirtualCount( int count, DWORD flags = 0 )
{
ListView_SetItemCountEx( m_hwnd, count, flags );
}
void SetVirtualCountAsync( int count, DWORD flags = 0 )
{
if ( m_hwnd )
PostMessage( m_hwnd, LVM_SETITEMCOUNT, count, flags );
}
int GetColumnWidth( int col );
void AutoColumnWidth( int col )
{
ListView_SetColumnWidth( m_hwnd, col, LVSCW_AUTOSIZE_USEHEADER );
}
void AutoSizeColumn( int col )
{
ListView_SetColumnWidth( m_hwnd, col, LVSCW_AUTOSIZE );
}
HWND getwnd( void )
{
return m_hwnd;
}
void ScrollTo( int index )
{
ListView_EnsureVisible( m_hwnd, index, FALSE );
}
void SetDoubleBuffered( bool buffered = true )
{
ListView_SetExtendedListViewStyleEx( m_hwnd, LVS_EX_DOUBLEBUFFER, buffered ? LVS_EX_DOUBLEBUFFER : 0 );
}
bool ColumnExists( int columnNum )
{
LVCOLUMN col;
col.mask = LVCF_WIDTH;
return ListView_GetColumn( m_hwnd, columnNum, &col );
}
void ForceUnicode()
{
SendMessage( m_hwnd, CCM_SETUNICODEFORMAT, TRUE, 0 );
}
protected:
HWND m_hwnd;
HFONT m_font;
int m_col;
};
#endif//_LISTVIEW_H_

132
Src/nu/menuHelpers.cpp Normal file
View File

@ -0,0 +1,132 @@
#include "./menuHelpers.h"
#include <strsafe.h>
INT MenuHelper_CopyMenuEx(HMENU hDest, INT iDstStart, HMENU hSource, INT iSrcStart, INT iSrcCount)
{
if (!hDest || !hSource) return 0;
TCHAR szText[1024] = {0};
MENUITEMINFO mii = {sizeof(MENUITEMINFO), };
mii.fMask = MIIM_BITMAP | MIIM_CHECKMARKS | MIIM_DATA | MIIM_FTYPE | MIIM_ID | MIIM_STATE | MIIM_STRING | MIIM_SUBMENU;
mii.dwTypeData = szText;
if (iDstStart < 0) iDstStart = 0;
if (iSrcStart < 0) iSrcStart = 0;
INT pos = iDstStart;
if ( 0 != iSrcCount)
{
INT c = GetMenuItemCount(hSource);
if (-1 == c || iSrcStart > c) return 0;
if (iSrcCount < 0)
iSrcCount = c - iSrcStart;
else if (iSrcCount < (c - iSrcStart))
c = iSrcCount + iSrcStart;
for (int i = iSrcStart; i < c; i++)
{
mii.cch = ARRAYSIZE(szText);
if (GetMenuItemInfo(hSource, i, TRUE, &mii))
{
if(InsertMenuItem(hDest, pos, TRUE, &mii))
{
pos++;
}
}
}
}
else
{
mii.cch = ARRAYSIZE(szText);
if (GetMenuItemInfo(hSource, iSrcStart, FALSE, &mii))
{
if (InsertMenuItem(hDest, pos, TRUE, &mii))
{
pos++;
}
}
}
return pos - iDstStart;
}
INT MenuHelper_CopyMenu(HMENU hDest, INT iDstStart, HMENU hSource)
{
return MenuHelper_CopyMenuEx(hDest, iDstStart, hSource, 0, -1);
}
HMENU MenuHelper_DuplcateMenu(HMENU hMenu)
{
HMENU hDest = CreatePopupMenu();
if (NULL == hMenu)
return NULL;
MenuHelper_CopyMenu(hDest, 0, hMenu);
return hDest;
}
INT MenuHelper_InsertSeparator(HMENU hMenu, INT iPos)
{
if (!hMenu) return FALSE;
MENUITEMINFO mii = {sizeof(MENUITEMINFO), };
mii.fMask = MIIM_FTYPE;
mii.fType = MFT_SEPARATOR;
return InsertMenuItem(hMenu, iPos, TRUE, &mii);
}
void MenuHelper_RemoveRange(HMENU hMenu, UINT min, UINT max)
{
MENUITEMINFO mii = {sizeof(MENUITEMINFO), };
mii.fMask = MIIM_ID;
INT count = GetMenuItemCount(hMenu);
if (-1 == count)
return;
while(count--)
{
if (GetMenuItemInfo(hMenu, count, TRUE, &mii) &&
mii.wID >= min && mii.wID <= max)
{
RemoveMenu(hMenu, count, MF_BYPOSITION);
}
}
}
void MenuHelper_EnableGroup(HMENU hMenu, UINT *pszItems, INT cchItems, BOOL fByPos, BOOL bEnable)
{
UINT enableGroup = (bEnable) ? MF_ENABLED : (MF_DISABLED | MF_GRAYED);
if (!fByPos)
enableGroup |= MF_BYCOMMAND;
for (INT i = 0; i < cchItems; i++)
{
EnableMenuItem(hMenu, pszItems[i], enableGroup);
}
}
BOOL MenuHelper_GetMenuItemPos(HMENU hMenu, UINT itemId, INT *pPos)
{
if (pPos)
*pPos = -1;
MENUITEMINFO mii = {sizeof(MENUITEMINFO), };
mii.fMask = MIIM_ID;
INT count = GetMenuItemCount(hMenu);
if (count < 1)
return FALSE;
while(count--)
{
if (GetMenuItemInfo(hMenu, count, TRUE, &mii) &&
mii.wID == itemId)
{
if (pPos)
*pPos = count;
return TRUE;
}
}
return FALSE;
}

18
Src/nu/menuHelpers.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef NULLOSFT_MENUHELPERS_HEADER
#define NULLOSFT_MENUHELPERS_HEADER
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif
#include <wtypes.h>
INT MenuHelper_CopyMenuEx(HMENU hDest, INT iDstStart, HMENU hSource, INT iSrcStart, INT iSrcCount);
INT MenuHelper_CopyMenu(HMENU hDest, INT iDstStart, HMENU hSource);
HMENU MenuHelper_DuplcateMenu(HMENU hMenu);
INT MenuHelper_InsertSeparator(HMENU hMenu, INT iPos);
void MenuHelper_RemoveRange(HMENU hMenu, UINT min, UINT max);
void MenuHelper_EnableGroup(HMENU hMenu, UINT *pszItems, INT cchItems, BOOL fByPos, BOOL bEnable);
BOOL MenuHelper_GetMenuItemPos(HMENU hMenu, UINT itemId, INT *pPos);
#endif // NULLOSFT_MENUHELPERS_HEADER

204
Src/nu/menushortcuts.cpp Normal file
View File

@ -0,0 +1,204 @@
#include "./menushortcuts.h"
#include <strsafe.h>
// change key string to capitalised only so it's more UI consistant (otherwise messes up the localisation look)
wchar_t* CapitaliseKeyName(wchar_t* szAccelName){
if(szAccelName) {
wchar_t *p = &szAccelName[1];
int space = 0;
while(p && *p){
if(*p == L' ') space = 1;
else {
if(!space) {
*p = tolower(*p);
}
else {
space = 0;
}
}
p = CharNextW(p);
}
}
return szAccelName;
}
BOOL AppendShortcutTextEx(LPWSTR pszItemText, INT cchTextMax, WORD wID, ACCEL *pszAccel, INT cchAccel, UINT uMode)
{
BOOL bDirty = FALSE;
if (!pszItemText) return FALSE;
UINT cchLen = lstrlenW(pszItemText);
if (MSF_REPLACE == (0x0F & uMode))
{
UINT len;
for (len = 0; len < cchLen && L'\t' != pszItemText[len]; len++);
if (cchLen != len)
{
cchLen = len;
pszItemText[len] = L'\0';
bDirty = TRUE;
}
}
if (wID > 0)
{
wchar_t szAccelName[64] = {0};
BOOL bFirst = TRUE;
pszItemText += cchLen;
size_t cchText = cchTextMax - cchLen;
for(int k = 0; k < cchAccel; k++)
{
if (wID == pszAccel[k].cmd)
{
HRESULT hr = StringCchCopyExW(pszItemText, cchText, ((bFirst) ? L"\t" : L", "), &pszItemText, &cchText, STRSAFE_IGNORE_NULLS);
if (S_OK == hr && (FCONTROL & pszAccel[k].fVirt)) {
GetKeyNameTextW(MapVirtualKey(VK_CONTROL, 0)<<16, szAccelName, sizeof(szAccelName)/sizeof(szAccelName[0]));
hr = StringCchCopyExW(pszItemText, cchText, CapitaliseKeyName(szAccelName), &pszItemText, &cchText, STRSAFE_IGNORE_NULLS);
if(S_OK == hr) StringCchCatExW(pszItemText, cchText, L"+", &pszItemText, &cchText, STRSAFE_IGNORE_NULLS);
}
if (S_OK == hr && (FALT & pszAccel[k].fVirt)) {
GetKeyNameTextW(MapVirtualKey(VK_MENU, 0)<<16, szAccelName, sizeof(szAccelName)/sizeof(szAccelName[0]));
hr = StringCchCopyExW(pszItemText, cchText, CapitaliseKeyName(szAccelName), &pszItemText, &cchText, STRSAFE_IGNORE_NULLS);
if(S_OK == hr) hr = StringCchCatExW(pszItemText, cchText, L"+", &pszItemText, &cchText, STRSAFE_IGNORE_NULLS);
}
if (S_OK == hr && (FSHIFT & pszAccel[k].fVirt)) {
GetKeyNameTextW(MapVirtualKey(VK_SHIFT, 0)<<16, szAccelName, sizeof(szAccelName)/sizeof(szAccelName[0]));
hr = StringCchCopyExW(pszItemText, cchText, CapitaliseKeyName(szAccelName), &pszItemText, &cchText, STRSAFE_IGNORE_NULLS);
if(S_OK == hr) hr = StringCchCatExW(pszItemText, cchText, L"+", &pszItemText, &cchText, STRSAFE_IGNORE_NULLS);
}
if (S_OK == hr)
{
if (FVIRTKEY & pszAccel[k].fVirt)
{
szAccelName[0] = L'\0';
UINT vkey = MapVirtualKey(pszAccel[k].key, 0);
/* benski> this removes "NUM" from the descriptions of certain characters */
switch(pszAccel[k].key)
{
case VK_INSERT:
case VK_DELETE:
case VK_HOME:
case VK_END:
case VK_NEXT: // Page down
case VK_PRIOR: // Page up
case VK_LEFT:
case VK_RIGHT:
case VK_UP:
case VK_DOWN:
vkey |= 0x100; // Add extended bit
}
vkey<<=16;
if (GetKeyNameTextW(vkey, szAccelName, sizeof(szAccelName)/sizeof(szAccelName[0]))) {
hr = StringCchCopyExW(pszItemText, cchText, CapitaliseKeyName(szAccelName), &pszItemText, &cchText, STRSAFE_IGNORE_NULLS);
}
}
else if (cchText > 1)
{
pszItemText[0] = (wchar_t)pszAccel[k].key;
pszItemText[1] = L'\0';
pszItemText += 2;
cchText -= 2;
}
}
bFirst = FALSE;
bDirty = (S_OK == hr);
}
}
}
return bDirty;
}
BOOL AppendMenuShortcutsEx(HMENU hMenu, ACCEL *pszAccel, INT cchAccel, UINT uMode)
{
wchar_t szText[4096] = {0};
if (!hMenu) return FALSE;
MENUITEMINFOW mii = { sizeof(MENUITEMINFOW), };
INT c = GetMenuItemCount(hMenu);
if (0 == c || -1 == c) return TRUE;
for (int i = 0; i < c; i++)
{
mii.fMask = MIIM_ID | MIIM_STRING | MIIM_FTYPE | MIIM_SUBMENU;
mii.cch = sizeof(szText)/sizeof(szText[0]);
mii.dwTypeData = szText;
if (GetMenuItemInfoW(hMenu, i, TRUE, &mii) &&
0 == ((MFT_SEPARATOR | MFT_MENUBREAK |MFT_MENUBARBREAK) & mii.fType))
{
if (AppendShortcutTextEx(mii.dwTypeData, sizeof(szText)/sizeof(szText[0]), (WORD)mii.wID, pszAccel, cchAccel, uMode))
{
mii.fMask = MIIM_STRING;
SetMenuItemInfoW(hMenu, i, TRUE, &mii);
}
if (NULL != mii.hSubMenu && (MSF_WALKSUBMENU & uMode))
{
AppendMenuShortcutsEx(mii.hSubMenu, pszAccel, cchAccel, uMode);
}
}
}
return TRUE;
}
BOOL AppendMenuShortcuts(HMENU hMenu, HACCEL *phAccel, INT count, UINT uMode)
{
ACCEL *pszAccel = NULL;
INT c = 0;
if (!hMenu) return FALSE;
if (phAccel)
{
for (int i = 0; i < count; i++)
{
c += CopyAcceleratorTable(phAccel[i], NULL, 0);
}
if (c)
{
pszAccel = (ACCEL*)calloc(c, sizeof(ACCEL));
if (!pszAccel) return FALSE;
}
int k = 0;
for (int i = 0; i < count; i++)
{
k += CopyAcceleratorTable(phAccel[i], &pszAccel[k], c - k);
}
}
BOOL r = AppendMenuShortcutsEx(hMenu, pszAccel, c, uMode);
if (pszAccel) free(pszAccel);
return r;
}
BOOL AppendShortcutText(LPWSTR pszItemText, INT cchTextMax, WORD wID, HACCEL *phAccel, INT count, UINT uMode)
{
ACCEL *pszAccel = NULL;
INT c = 0;
if (!pszItemText) return FALSE;
if (phAccel)
{
for (int i = 0; i < count; i++)
{
c += CopyAcceleratorTable(phAccel[i], NULL, 0);
}
if (c)
{
pszAccel = (ACCEL*)calloc(c, sizeof(ACCEL));
if (!pszAccel) return FALSE;
}
int k = 0;
for (int i = 0; i < count; i++)
{
k += CopyAcceleratorTable(phAccel[i], &pszAccel[k], c - k);
}
}
BOOL r = AppendShortcutTextEx(pszItemText, cchTextMax, wID, pszAccel, c, uMode);
if (pszAccel) free(pszAccel);
return r;
}

20
Src/nu/menushortcuts.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef NULLOSFT_MEDIALIBRARY_MENU_SHORTCUTS_HEADER
#define NULLOSFT_MEDIALIBRARY_MENU_SHORTCUTS_HEADER
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif
#include <windows.h>
#define MSF_REPLACE 0x0000
#define MSF_APPEND 0x0001
#define MSF_WALKSUBMENU 0x0100
BOOL AppendMenuShortcuts(HMENU hMenu, HACCEL *phAccel, INT count, UINT uMode);
BOOL AppendMenuShortcutsEx(HMENU hMenu, ACCEL *pszAccel, INT count, UINT uMode);
BOOL AppendShortcutText(LPWSTR pszItemText, INT cchTextMax, WORD wID, HACCEL *phAccel, INT count, UINT uMode);
BOOL AppendShortcutTextEx(LPWSTR pszItemText, INT cchTextMax, WORD wID, ACCEL *pszAccel, INT cchAccel, UINT uMode);
#endif // NULLOSFT_MEDIALIBRARY_MENU_SHORTCUTS_HEADER

611
Src/nu/mtbrowser.cpp Normal file
View File

@ -0,0 +1,611 @@
#include "./mtbrowser.h"
#include <exdispid.h>
#define QUIT_FORCE 0x00FF
#define THREAD_QUITING 0x0001
#define THREAD_COMINIT 0x1000
#define MEMMMGR_RECALLOCSTEP 8
#define GetThreadBrowserInstance() ((HTMLContainer2*)TlsGetValue(threadStorage))
typedef struct _THREAD_START_PARAM
{
HANDLE hEvent;
MTBROWSER *pmtb;
} THREAD_START_PARAM;
typedef struct _MEMREC
{
HANDLE handle;
FREEPROC freeproc;
} MEMREC;
typedef struct _MEMMNGR
{
CRITICAL_SECTION cs;
MEMREC *pRec;
INT cCount;
INT cAlloc;
} MEMMNGR;
typedef struct _APCPARAM
{
MEMMNGR *pmm;
VARIANTARG *pArgs;
INT cArgs;
HWND hwndNotify;
UINT uMsgNotify;
INT nNotifyCode;
APCPROC fnAPC;
HANDLE hThread;
} APCPARAM;
typedef struct _CALLBACKPARAM
{
HWND hwndNotify;
UINT uMsgNotify;
BOOL bDestroyed;
BOOL bQuiting;
BOOL bReady;
} CALLBACKPARAM;
// forward declarations
static DWORD CALLBACK BrowserThread(LPVOID param);
static HRESULT MTBrowser_Quit(HTMLContainer2 *pContainer);
static BOOL MemRec_Add(MEMMNGR *pmm, void *pMem, FREEPROC fnFreeProc);
static BOOL MemRec_Free(MEMMNGR *pmm, void *pMem);
static APCPARAM *AllocAPCParam(MTBROWSER *pmtb, INT cArgs, INT nNotifyCode, APCPROC fnAPC);
static void CALLBACK FreeAPCParam(void *pMem);
// APC
static void CALLBACK APC_FunctionCall(ULONG_PTR param);
static void CALLBACK APC_NavigateToName(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult);
static void CALLBACK APC_SetLocation(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult);
static void CALLBACK APC_Refresh2(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult);
static DWORD threadStorage = TLS_OUT_OF_INDEXES;
BOOL MTBrowser_Init(MTBROWSER *pmtb)
{
if (!pmtb) return FALSE;
ZeroMemory(pmtb, sizeof(MTBROWSER));
if (TLS_OUT_OF_INDEXES == threadStorage)
{
threadStorage = TlsAlloc();
if (TLS_OUT_OF_INDEXES == threadStorage) return FALSE;
}
return TRUE;
}
BOOL MTBrowser_Clear(MTBROWSER *pmtb)
{
if (pmtb)
{
if (pmtb->hThread) CloseHandle(pmtb->hThread);
if (pmtb->hMemMngr)
{
MEMMNGR *pmm;
pmm = (MEMMNGR*)pmtb->hMemMngr;
EnterCriticalSection(&pmm->cs);
for(int i = 0; i < pmm->cCount; i++)
{
if (pmm->pRec[i].handle)
{
(pmm->pRec[i].freeproc) ? pmm->pRec[i].freeproc(pmm->pRec[i].handle) : free(pmm->pRec[i].handle);
}
}
pmm->cCount = 0;
LeaveCriticalSection(&pmm->cs);
DeleteCriticalSection(&pmm->cs);
free(pmtb->hMemMngr);
}
ZeroMemory(pmtb, sizeof(MTBROWSER));
}
return TRUE;
}
BOOL MTBrowser_Start(MTBROWSER *pmtb, HTMLContainer2 *pContainer, UINT uMsgNotify)
{
THREAD_START_PARAM param = {0};
if (!pmtb || !pContainer || TLS_OUT_OF_INDEXES == threadStorage) return FALSE;
pmtb->hMemMngr = calloc(1, sizeof(MEMMNGR));
if (!pmtb->hMemMngr) return FALSE;
InitializeCriticalSection(&((MEMMNGR*)pmtb->hMemMngr)->cs);
param.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
param.pmtb = pmtb;
pmtb->uMsgNotify = uMsgNotify;
pmtb->pContainer = pContainer;
pmtb->hThread = CreateThread(NULL, 0, BrowserThread, (LPVOID)&param, 0, &pmtb->dwThreadId);
if (pmtb->hThread)
{
WaitForSingleObject(param.hEvent, INFINITE);
SetThreadPriority(pmtb->hThread, THREAD_PRIORITY_NORMAL);
}
CloseHandle(param.hEvent);
return (NULL != pmtb->hThread);
}
BOOL MTBrowser_Kill(MTBROWSER *pmtb, UINT nTerminateDelay)
{
MSG msg;
if (pmtb)
{
pmtb->bQuiting = TRUE;
if (pmtb && pmtb->hThread)
{
PostThreadMessage(pmtb->dwThreadId, WM_QUIT, QUIT_FORCE, 0L);
while(WAIT_TIMEOUT == WaitForSingleObject(pmtb->hThread, 0))
{
DWORD dwStatus;
dwStatus = MsgWaitForMultipleObjectsEx(1, &pmtb->hThread, nTerminateDelay, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
if (WAIT_OBJECT_0 + 1 == dwStatus || WAIT_OBJECT_0 == dwStatus)
{
while (PeekMessageW(&msg, NULL, 0xc0d6, 0xc0d6, PM_NOREMOVE)) if (NULL == msg.hwnd) DispatchMessageW(&msg);
}
else if (WAIT_TIMEOUT == dwStatus)
{
TerminateThread(pmtb->hThread, 3);
break;
}
}
CloseHandle(pmtb->hThread);
pmtb->hThread = NULL;
}
return TRUE;
}
return FALSE;
}
BOOL MTBrowser_QuitAPC(MTBROWSER *pmtb)
{
if (!pmtb) return FALSE;
if (pmtb->bQuiting) return TRUE;
pmtb->bQuiting = (pmtb->dwThreadId && PostThreadMessage(pmtb->dwThreadId, WM_QUIT, 0, 0L));
return pmtb->bQuiting;
}
BOOL MTBrowser_NavigateToNameAPC(MTBROWSER *pmtb, LPCWSTR pszURL, UINT fFlags)
{
HAPC hAPC;
VARIANTARG *pArgs;
hAPC = MTBrowser_InitializeAPC(pmtb, 2, MTBC_APC_NAVIGATE, APC_NavigateToName, &pArgs);
if (!hAPC) return FALSE;
pArgs[0].vt = VT_BSTR;
pArgs[0].bstrVal= SysAllocString(pszURL);
pArgs[1].vt = VT_I4;
pArgs[1].intVal = fFlags;
return MTBrowser_CallAPC(hAPC);
}
BOOL MTBrowser_SetLocationAPC(MTBROWSER *pmtb, RECT *pRect)
{
HAPC hAPC;
VARIANTARG *pArgs;
hAPC = MTBrowser_InitializeAPC(pmtb, 4, MTBC_APC_SETLOCATION, APC_SetLocation, &pArgs);
if (!hAPC) return FALSE;
pArgs[0].vt = VT_I4;
pArgs[0].intVal = pRect->left;
pArgs[1].vt = VT_I4;
pArgs[1].intVal = pRect->top;
pArgs[2].vt = VT_I4;
pArgs[2].intVal = pRect->right - pRect->left;
pArgs[3].vt = VT_I4;
pArgs[3].intVal = pRect->bottom - pRect->top;
return MTBrowser_CallAPC(hAPC);
}
BOOL MTBrowser_Refresh2APC(MTBROWSER *pmtb, INT nRefreshMode)
{
HAPC hAPC;
VARIANTARG *pArgs;
hAPC = MTBrowser_InitializeAPC(pmtb, 1, MTBC_APC_REFRESH2, APC_Refresh2, &pArgs);
if (!hAPC) return FALSE;
pArgs[0].vt = VT_I4;
pArgs[0].intVal = nRefreshMode;
return MTBrowser_CallAPC(hAPC);
}
HAPC MTBrowser_InitializeAPC(MTBROWSER *pmtb, INT nCount, UINT nCmdCode, APCPROC fnAPC, VARIANTARG **pArgs)
{
APCPARAM *pParam;
if (!pmtb || pmtb->bQuiting || !pmtb->hThread) return FALSE;
pParam = AllocAPCParam(pmtb, nCount, nCmdCode, fnAPC);
if (pParam && pArgs) *pArgs = pParam->pArgs;
return (HAPC) pParam;
}
BOOL MTBrowser_CallAPC(HAPC hAPC)
{
BOOL result;
if (!hAPC) return FALSE;
result = QueueUserAPC(APC_FunctionCall, ((APCPARAM*)hAPC)->hThread, (ULONG_PTR)hAPC);
if (!result) MemRec_Free(((APCPARAM*)hAPC)->pmm, hAPC);
return result;
}
BOOL MTBrowser_AddMemRec(MTBROWSER *pmtb, void *pMem, FREEPROC fnFreeProc)
{
return (pmtb && pmtb->hMemMngr) ? MemRec_Add((MEMMNGR*)pmtb->hMemMngr, pMem, fnFreeProc) : FALSE;
}
BOOL MTBrowser_FreeMemRec(MTBROWSER *pmtb, void *pMem)
{
return (pmtb && pmtb->hMemMngr) ? MemRec_Free((MEMMNGR*)pmtb->hMemMngr, pMem) : FALSE;
}
static BOOL MemRec_Add(MEMMNGR *pmm, void *pMem, FREEPROC fnFreeProc)
{
if (!pmm || !pMem) return FALSE;
EnterCriticalSection(&pmm->cs);
if (pmm->cCount == pmm->cAlloc)
{
LPVOID pData;
pData = realloc(pmm->pRec, sizeof(MEMREC)*(pmm->cCount + MEMMMGR_RECALLOCSTEP));
if (!pData)
{
LeaveCriticalSection(&pmm->cs);
return FALSE;
}
pmm->pRec = (MEMREC*)pData;
pmm->cAlloc = pmm->cCount + MEMMMGR_RECALLOCSTEP;
}
pmm->pRec[pmm->cCount].handle = pMem;
pmm->pRec[pmm->cCount].freeproc = fnFreeProc;
pmm->cCount++;
LeaveCriticalSection(&pmm->cs);
return TRUE;
}
static BOOL MemRec_Free(MEMMNGR *pmm, void *pMem)
{
INT index;
MEMREC rec;
if (!pmm || !pMem) return FALSE;
EnterCriticalSection(&pmm->cs);
ZeroMemory(&rec, sizeof(MEMREC));
for(index = 0; index < pmm->cCount; index++)
{
if (pmm->pRec[index].handle == pMem)
{
rec.freeproc= pmm->pRec[index].freeproc;
rec.handle = pmm->pRec[index].handle;
break;
}
}
if (index < (pmm->cCount -1)) MoveMemory(&pmm->pRec[index], &pmm->pRec[index + 1], sizeof(MEMREC)*(pmm->cCount - index - 1));
if (index < pmm->cCount) pmm->cCount--;
if(rec.handle)
{
(rec.freeproc) ? rec.freeproc(rec.handle) : free(rec.handle);
}
LeaveCriticalSection(&pmm->cs);
return (NULL != rec.handle);
}
#define POSTNOTIFY(_hwnd, _msg, _code, _param) ((_msg && IsWindow(_hwnd)) ? PostMessageW(_hwnd, _msg, (WPARAM)_code, (LPARAM)_param) : FALSE)
static BOOL CALLBACK OnBrowserEvent(HTMLContainer2 *pContainer, DISPID dispId, DISPPARAMS FAR *pDispParams, LPVOID pUser)
{
CALLBACKPARAM *pcb;
pcb = (CALLBACKPARAM*)pUser;
if (!pcb) return FALSE;
switch(dispId)
{
case DISPID_DESTRUCTOR:
pcb->bDestroyed = TRUE;
PostThreadMessage(GetCurrentThreadId(), WM_NULL, 0, 0L);
break;
case DISPID_NAVIGATECOMPLETE2:
if (pcb->bReady)
{
}
else
{
DWORD_PTR dwRedrawOFF;
HWND hwndParent;
hwndParent = pContainer->GetParentHWND();
if (hwndParent) SendMessageTimeout(hwndParent, WM_SETREDRAW, FALSE, 0L, SMTO_NORMAL, 100, &dwRedrawOFF);
pContainer->SetLocation(-2000, 0, 100, 100);
if (hwndParent) SendMessageTimeout(hwndParent, WM_SETREDRAW, TRUE, 0L, SMTO_NORMAL, 100, &dwRedrawOFF);
}
break;
case DISPID_DOCUMENTCOMPLETE:
{
HRESULT hr;
IUnknown *pUnk, *pUnkDisp;
BOOL bDocReady;
bDocReady = FALSE;
hr = pContainer->GetIUnknown(&pUnk);
if (SUCCEEDED(hr))
{
hr = pDispParams->rgvarg[1].pdispVal->QueryInterface(IID_IUnknown, (void**)&pUnkDisp);
if (SUCCEEDED(hr)) pUnkDisp->Release();
if (pUnk == pUnkDisp) bDocReady = TRUE;
if (!pcb->bReady && pDispParams->rgvarg[0].pvarVal->bstrVal &&
0 == lstrcmpW(L"about:blank", pDispParams->rgvarg[0].pvarVal->bstrVal))
{
pcb->bReady = TRUE;
POSTNOTIFY(pcb->hwndNotify, pcb->uMsgNotify, MTBC_READY, (LPARAM)pContainer);
break;
}
}
if (pcb->bReady) POSTNOTIFY(pcb->hwndNotify, pcb->uMsgNotify, MTBC_DOCUMENTCOMPLETE, bDocReady);
}
}
return pcb->bQuiting;
}
static DWORD CALLBACK BrowserThread(LPVOID param)
{
HRESULT hr;
HTMLContainer2 *pInstance;
DWORD dwInterval;
UINT uState;
MSG msg;
CALLBACKPARAM cbParam;
uState = 0;
TlsSetValue(threadStorage, 0);
pInstance = ((THREAD_START_PARAM*)param)->pmtb->pContainer;
ZeroMemory(&cbParam, sizeof(CALLBACKPARAM));
cbParam.uMsgNotify = ((THREAD_START_PARAM*)param)->pmtb->uMsgNotify;
cbParam.hwndNotify = pInstance->GetParentHWND();
pInstance->RegisterBrowserEventCB(OnBrowserEvent, (LPVOID)&cbParam);
((THREAD_START_PARAM*)param)->pmtb->hwndNotify = cbParam.hwndNotify;
SetEvent(((THREAD_START_PARAM*)param)->hEvent);
hr = OleInitialize(NULL);//hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hr))
{
uState |= THREAD_COMINIT;
hr = pInstance->Initialize();
if(SUCCEEDED(hr)) TlsSetValue(threadStorage, pInstance);
else
{
pInstance->Finish();
pInstance->Release();
}
}
// force message queue
PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
if (FAILED(hr) || !IsWindow(cbParam.hwndNotify))
{
TlsSetValue(threadStorage, 0);
pInstance->Finish();
pInstance->Release();
}
else
{
pInstance->NavigateToName(L"about:blank", 0);
}
dwInterval = INFINITE;
while (!cbParam.bDestroyed)
{
DWORD dwStatus = MsgWaitForMultipleObjectsEx(0, NULL, dwInterval, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
if (WAIT_OBJECT_0 == dwStatus)
{
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
{
if (WM_QUIT == msg.message)
{
cbParam.bQuiting = TRUE;
TlsSetValue(threadStorage, 0);
if (QUIT_FORCE == msg.wParam)
{
if (!cbParam.bDestroyed)
{
pInstance->Finish();
while(pInstance->Release() > 0);
}
break;
}
else if (0 == (THREAD_QUITING & uState) && SUCCEEDED(MTBrowser_Quit(pInstance)))
{
uState |= THREAD_QUITING;
dwInterval = 200;
}
POSTNOTIFY(cbParam.hwndNotify, cbParam.uMsgNotify, MTBC_APC_QUIT, (LPARAM)(THREAD_QUITING & uState));
}
else if ((WM_KEYFIRST > msg.message || WM_KEYLAST < msg.message ||
(THREAD_QUITING & uState) || !pInstance->TranslateKey(&msg)) &&
!IsDialogMessageW(cbParam.hwndNotify, &msg))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
}
else if (WAIT_TIMEOUT == dwStatus) // quiting - check readystatus
{
if (!cbParam.bDestroyed)
{
READYSTATE state;
IWebBrowser2 *pWeb2;
hr = pInstance->GetIWebBrowser2(&pWeb2);
if (SUCCEEDED(hr))
{
hr = pWeb2->get_ReadyState(&state);
pWeb2->Release();
}
else state = READYSTATE_UNINITIALIZED;
if (FAILED(hr) || READYSTATE_UNINITIALIZED == state || READYSTATE_INTERACTIVE <= state)
{
pInstance->Finish();
pInstance->Release();
}
}
}
}
if (THREAD_COMINIT & uState) OleUninitialize();//CoUninitialize();
POSTNOTIFY(cbParam.hwndNotify, cbParam.uMsgNotify, MTBC_DESTROYED, (LPARAM)pInstance);
return 0;
}
static HRESULT MTBrowser_Quit(HTMLContainer2 *pContainer)
{
HRESULT hr;
IWebBrowser2 *pWeb2;
hr = pContainer->GetIWebBrowser2(&pWeb2);
if (SUCCEEDED(hr))
{
hr = pWeb2->Stop();
pWeb2->Release();
if (SUCCEEDED(hr)) hr = pContainer->NavigateToName(L"about:blank", navNoHistory | navNoReadFromCache | navNoWriteToCache);
}
return hr;
}
static APCPARAM* AllocAPCParam(MTBROWSER *pmtb, INT cArgs, INT nNotifyCode, APCPROC fnAPC)
{
if (!pmtb || !pmtb->hMemMngr || !fnAPC) return NULL;
APCPARAM *pParam = (APCPARAM*)calloc(1, sizeof(APCPARAM));
if (!pParam) return NULL;
if (cArgs)
{
INT i;
pParam->pArgs = (VARIANTARG*)calloc(cArgs, sizeof(VARIANTARG));
if (!pParam->pArgs)
{
free(pParam);
return NULL;
}
for (i = 0; i < cArgs; i++) VariantInit(&pParam->pArgs[i]);
}
else pParam->pArgs = NULL;
pParam->cArgs = cArgs;
pParam->pmm = (MEMMNGR*)pmtb->hMemMngr;
pParam->hwndNotify = pmtb->hwndNotify;
pParam->uMsgNotify = pmtb->uMsgNotify;
pParam->nNotifyCode = nNotifyCode;
pParam->fnAPC = fnAPC;
pParam->hThread = pmtb->hThread;
if (!MemRec_Add(pParam->pmm, pParam, FreeAPCParam))
{
FreeAPCParam(pParam);
pParam = NULL;
}
return pParam;
}
static void CALLBACK FreeAPCParam(void *pMem)
{
if (pMem)
{
APCPARAM* pParam;
pParam = (APCPARAM*)pMem;
if (pParam->pArgs)
{
for(INT i = 0; i < pParam->cArgs; i++) VariantClear(&pParam->pArgs[i]);
free(pParam->pArgs);
}
free(pMem);
}
}
static void CALLBACK APC_FunctionCall(ULONG_PTR param)
{
LPARAM result;
APCPARAM *pParam;
pParam = (APCPARAM*)param;
if (!pParam) return;
HTMLContainer2 *pContainer;
pContainer = GetThreadBrowserInstance();
if (pContainer)
{
result = 0L;
if (pParam->fnAPC) pParam->fnAPC(pContainer, pParam->pArgs, pParam->cArgs, &result);
if (pParam->nNotifyCode && pParam->hwndNotify && IsWindow(pParam->hwndNotify))
{
PostMessageW(pParam->hwndNotify, pParam->uMsgNotify, pParam->nNotifyCode, result);
}
}
MemRec_Free(pParam->pmm, pParam);
}
static void CALLBACK APC_NavigateToName(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult)
{
*pResult = (2 == cArgs) ? pContainer->NavigateToName(pArgs[0].bstrVal, pArgs[1].intVal) : E_INVALIDARG;
}
static void CALLBACK APC_SetLocation(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult)
{
*pResult = (4 == cArgs) ? pContainer->SetLocation(pArgs[0].intVal, pArgs[1].intVal, pArgs[2].intVal, pArgs[3].intVal) : E_INVALIDARG;
}
static void CALLBACK APC_Refresh2(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult)
{
if (1 != cArgs) *pResult = E_INVALIDARG;
else
{
HRESULT hr;
IWebBrowser2 *pWeb2;
hr = pContainer->GetIWebBrowser2(&pWeb2);
if (SUCCEEDED(hr))
{
hr = pWeb2->Refresh2(&pArgs[0]);
pWeb2->Release();
}
*pResult = (LPARAM)hr;
}
}

63
Src/nu/mtbrowser.h Normal file
View File

@ -0,0 +1,63 @@
#ifndef NULLSOFT_MULTITHREADED_BROWSER_HEADER
#define NULLSOFT_MULTITHREADED_BROWSER_HEADER
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif
#include "../nu/HTMLContainer2.h"
typedef struct _MTBROWSER
{
HTMLContainer2 *pContainer; // HTMLContainer2 object
HANDLE hThread; // Browser object thread
DWORD dwThreadId; // Browser object thread Id
BOOL bQuiting; // Browser quiting (do not schedule any othe APC)
HANDLE hMemMngr; // handle to the browser memory meanger.
HWND hwndNotify; // set from pContainer->GetParentHWND();
UINT uMsgNotify; // if 0 != uMsgNotify you will be notified that browser finished operation with wParam set to cmd code, and lParam result.
} MTBROWSER;
typedef void* HAPC;
typedef void (CALLBACK *FREEPROC)(void* pMemFree);
typedef void (CALLBACK *APCPROC)(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult);
// messages
#define MTBC_FIRST (0x0000)
#define MTBC_LAST (0x00FF)
#define MTBC_READY (MTBC_FIRST) // Posted when browser thread and browser object initialization completed //lParam = (HTMLContainer2*)
#define MTBC_DESTROYED (MTBC_LAST) // Posted when browser object destroyed itself and browser thread about to exit. //lParam = (HTMLContainer2*)- destroyed!!!
#define MTBC_APC_QUIT (MTBC_FIRST + 20) // Posted when browser executed quit APC
#define MTBC_APC_NAVIGATE (MTBC_FIRST + 21) // Posted when browser executed navigate APC. lParam = (HRESULT)
#define MTBC_APC_SETLOCATION (MTBC_FIRST + 22) // Posted when browser executed SetLocation APC. lParam = (HRESULT)
#define MTBC_APC_REFRESH2 (MTBC_FIRST + 23) // Posted when browser executed Refresh2 APC. lParam = (HRESULT)
#define MTBC_DOCUMENTCOMPLETE (MTBC_FIRST + 41) // lParam = (BOOL)bDocumentReady
BOOL MTBrowser_Init(MTBROWSER *pmtb);
BOOL MTBrowser_Clear(MTBROWSER *pmtb);
BOOL MTBrowser_Start(MTBROWSER *pmtb, HTMLContainer2 *pContainer, UINT uMsgNotify);
BOOL MTBrowser_Kill(MTBROWSER *pmtb, UINT nTerminateDelay); // use in WM_DESTROY
// Async calls
HAPC MTBrowser_InitializeAPC(MTBROWSER *pmtb, INT nCount, UINT nCmdCode, APCPROC fnAPC, VARIANTARG **pArgs);
BOOL MTBrowser_CallAPC(HAPC hAPC);
BOOL MTBrowser_QuitAPC(MTBROWSER *pmtb);
BOOL MTBrowser_NavigateToNameAPC(MTBROWSER *pmtb, LPCWSTR pszURL, UINT fFlags);
BOOL MTBrowser_SetLocationAPC(MTBROWSER *pmtb, RECT *pRect);
BOOL MTBrowser_SetVisibleAPC(MTBROWSER *pmtb, BOOL bVisible);
BOOL MTBrowser_Refresh2APC(MTBROWSER *pmtb, INT nRefreshMode);
/// Thread Memory manager
BOOL MTBrowser_AddMemRec(MTBROWSER *pmtb, void *pMem, FREEPROC fnFreeProc); // if fnFreeProc == NULL - standart free() will be used
BOOL MTBrowser_FreeMemRec(MTBROWSER *pmtb, void *pMem);
#endif //NULLSOFT_MULTITHREADED_BROWSER_HEADER

28
Src/nu/noarg.c Normal file
View File

@ -0,0 +1,28 @@
/***
*noarg.c - stub out CRT's processing of command line arguments
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
* Stub out the processing of the command line into argv[], normally
* carried out at during startup. Note, the argc and argv arguments to
* main are not meaningful if this object is used. Nor are __argc and
* __argv.
*
*******************************************************************************/
#include <tchar.h>
#ifdef __cplusplus
extern "C" {
#endif
int __cdecl _setargv() { return 0; }
int __cdecl _wsetargv() { return 0; }
_TUCHAR * __cdecl _wincmdln() { return NULL; }
_TUCHAR * __cdecl _wwincmdln() { return NULL; }
#ifdef __cplusplus
}
#endif

26
Src/nu/nochkclr.c Normal file
View File

@ -0,0 +1,26 @@
/***
* nochkclr.c - Dummy non-version-checking CLR call
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
*
*******************************************************************************/
/***
*_check_commonlanguageruntime_version
*
*Purpose:
* If you don't link to the CRT, you use this obj to fill the compiler's need for this symbol
*******************************************************************************/
#ifdef __cplusplus
extern "C" {
#endif
void __cdecl _check_commonlanguageruntime_version()
{
}
#ifdef __cplusplus
}
#endif

27
Src/nu/noenv.c Normal file
View File

@ -0,0 +1,27 @@
/***
*noenv.c - stub out CRT's environment string processing
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
* Stub out the environment string processing normally carried out at
* during startup. Note, getenv, _putenv and _environ are not supported
* if this object is used. Nor is the third argument to main.
*
*******************************************************************************/
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
int __cdecl _setenvp(void) { return 0; }
void * __cdecl __crtGetEnvironmentStringsA(void) { return NULL; }
int __cdecl _wsetenvp(void) { return 0; }
void * __cdecl __crtGetEnvironmentStringsW(void) { return NULL; }
#ifdef __cplusplus
}
#endif

17
Src/nu/nomsgs.c Normal file
View File

@ -0,0 +1,17 @@
#ifdef __cplusplus
extern "C" {
#endif
void __cdecl _FF_MSGBANNER (
void
)
{}
void __cdecl _NMSG_WRITE (
int rterrnum
)
{}
#ifdef __cplusplus
}
#endif

18
Src/nu/nonewthrow.c Normal file
View File

@ -0,0 +1,18 @@
void *operator new(size_t size)
{
return malloc(size);
}
void *operator new[](size_t size)
{
return malloc(size);
}
void operator delete(void *ptr)
{
free(ptr);
}
void operator delete[](void *ptr)
{
free(ptr);
}

12
Src/nu/nosec.c Normal file
View File

@ -0,0 +1,12 @@
#ifdef __cplusplus
extern "C" {
#endif
void __cdecl __security_error_handler(
int code,
void *data)
{
_exit(3);
}
#ifdef __cplusplus
}
#endif

67
Src/nu/ns_wc.h Normal file
View File

@ -0,0 +1,67 @@
#pragma once
#include <windows.h>
#ifdef __cplusplus
extern "C"
{
#endif
__inline int MultiByteToWideCharSZ(
UINT CodePage, // code page
DWORD dwFlags, // character-type options
LPCSTR lpMultiByteStr, // string to map
int cbMultiByte, // number of bytes in string
LPWSTR lpWideCharStr, // wide-character buffer
int cchWideChar // size of buffer
)
{
int converted=0;
if (cchWideChar == 0)
return MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, lpWideCharStr, cchWideChar);
converted = MultiByteToWideChar(CodePage, dwFlags, lpMultiByteStr, cbMultiByte, lpWideCharStr, cchWideChar-1);
if (!converted)
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
lpWideCharStr[cchWideChar-1]=0;
return cchWideChar;
}
else
return 0;
}
lpWideCharStr[converted]=0;
return converted+1;
}
__inline int WideCharToMultiByteSZ(
UINT CodePage, // code page
DWORD dwFlags, // performance and mapping flags
LPCWSTR lpWideCharStr, // wide-character string
int cchWideChar, // number of chars in string
LPSTR lpMultiByteStr, // buffer for new string
int cbMultiByte, // size of buffer
LPCSTR lpDefaultChar, // default for unmappable chars
LPBOOL lpUsedDefaultChar // set when default char used
)
{
int converted=0;
if (cbMultiByte == 0)
return WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar);
converted= WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte-1, lpDefaultChar, lpUsedDefaultChar);
if (!converted)
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
lpMultiByteStr[cbMultiByte-1]=0;
return cbMultiByte;
}
else
return 0;
}
lpMultiByteStr[converted]=0;
return converted+1;
}
#ifdef __cplusplus
}
#endif

21
Src/nu/refcount.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
template <class ifc_t>
class Countable : public ifc_t
{
public:
Countable()
{
ref_count=1;
}
// this needs to be done like this otherwise the destructor doesn't get called properly (we don't want virtual destructor for various reasons)
#define REFERENCE_COUNT_IMPLEMENTATION size_t AddRef() { return InterlockedIncrement((LONG*)&ref_count); }\
size_t Release() { if (!ref_count) return ref_count; LONG r = InterlockedDecrement((LONG*)&ref_count); if (!r) delete(this); return r; }
protected:
size_t ref_count;
};
#define REFERENCE_COUNTED CB(ADDREF, AddRef); CB(RELEASE, Release);

233
Src/nu/regexp.cpp Normal file
View File

@ -0,0 +1,233 @@
#include "regexp.h"
// TODO: make a little more multi-byte safe
// regexp match functions
// A match means the entire string TEXT is used up in matching.
// In the pattern string:
// `*' matches any sequence of characters (zero or more)
// `?' matches any character
// [SET] matches any character in the specified set,
// [!SET] or [^SET] matches any character not in the specified set.
// A set is composed of characters or ranges; a range looks like
// character hyphen character (as in 0-9 or A-Z). [0-9a-zA-Z_] is the
// minimal set of characters allowed in the [..] pattern construct.
// Other characters are allowed (ie. 8 bit characters) if your system
// will support them.
// To suppress the special syntactic significance of any of `[]*?!^-\',
// and match the character exactly, precede it with a `\'.
enum {
MATCH_VALID = 1, /* valid match */
MATCH_END, /* premature end of pattern string */
MATCH_ABORT, /* premature end of text string */
MATCH_RANGE, /* match failure on [..] construct */
MATCH_LITERAL, /* match failure on literal match */
MATCH_PATTERN, /* bad pattern */
};
enum {
PATTERN_VALID = 0, /* valid pattern */
PATTERN_ESC = -1, /* literal escape at end of pattern */
PATTERN_RANGE = -2, /* malformed range in [..] construct */
PATTERN_CLOSE = -3, /* no end bracket in [..] construct */
PATTERN_EMPTY = -4, /* [..] contstruct is empty */
};
int Matche(const regchar_t *p, const regchar_t *t);
// TODO: make this multi-byte aware
int matche_after_star(const regchar_t *p, const regchar_t *t)
{
register int match = 0;
register regchar_t nextp;
/* pass over existing ? and * in pattern */
while ( *p == '?' || *p == '*' )
{
/* take one char for each ? and + */
if (*p == '?')
{
/* if end of text then no match */
if (!*t++) return MATCH_ABORT;
}
/* move to next char in pattern */
p++;
}
/* if end of pattern we have matched regardless of text left */
if (!*p) return MATCH_VALID;
/* get the next character to match which must be a literal or '[' */
nextp = *p;
if (nextp == '\\')
{
nextp = p[1];
/* if end of text then we have a bad pattern */
if (!nextp) return MATCH_PATTERN;
}
/* Continue until we run out of text or definite result seen */
do
{
/* a precondition for matching is that the next character
in the pattern match the next character in the text or that
the next pattern char is the beginning of a range. Increment
text pointer as we go here */
if (nextp == *t || nextp == '[') match = Matche(p, t);
/* if the end of text is reached then no match */
if (!*t++) match = MATCH_ABORT;
}
while ( match != MATCH_VALID && match != MATCH_ABORT && match != MATCH_PATTERN);
/* return result */
return match;
}
int Matche(const regchar_t *p, const regchar_t *t)
{
regchar_t range_start, range_end; /* start and end in range */
bool invert; /* is this [..] or [!..] */
bool member_match; /* have I matched the [..] construct? */
bool loop; /* should I terminate? */
for ( ; *p; p++, t++)
{
/* if this is the end of the text then this is the end of the match */
if (!*t)
{
return (*p == '*' && *++p == '\0') ? MATCH_VALID : MATCH_ABORT;
}
/* determine and react to pattern type */
switch (*p)
{
case '?': /* single any character match */
break;
case '*': /* multiple any character match */
return matche_after_star (p, t);
/* [..] construct, single member/exclusion character match */
case '[':
{
/* move to beginning of range */
p++;
/* check if this is a member match or exclusion match */
invert = false;
if (*p == '!' || *p == '^')
{
invert = true;
p++;
}
/* if closing bracket here or at range start then we have a malformed pattern */
if (*p == ']')
return MATCH_PATTERN;
member_match = false;
loop = true;
while (loop)
{
/* if end of construct then loop is done */
if (*p == ']')
{
loop = false;
continue;
}
/* matching a '!', '^', '-', '\' or a ']' */
if (*p == '\\')
range_start = range_end = *++p;
else
range_start = range_end = *p;
/* if end of pattern then bad pattern (Missing ']') */
if (!*p)
return MATCH_PATTERN;
/* check for range bar */
if (*++p == '-')
{
/* get the range end */
range_end = *++p;
/* if end of pattern or construct then bad pattern */
if (range_end == '\0' || range_end == ']') return MATCH_PATTERN;
/* special character range end */
if (range_end == '\\')
{
range_end = *++p;
/* if end of text then we have a bad pattern */
if (!range_end) return MATCH_PATTERN;
}
/* move just beyond this range */
p++;
}
/* if the text character is in range then match found.
make sure the range letters have the proper
relationship to one another before comparison */
if (range_start < range_end)
{
if (*t >= range_start && *t <= range_end)
{
member_match = true;
loop = false;
}
}
else
{
if (*t >= range_end && *t <= range_start)
{
member_match = true;
loop = false;
}
}
}
/* if there was a match in an exclusion set then no match */
/* if there was no match in a member set then no match */
if ((invert && member_match) || !(invert || member_match))
return MATCH_RANGE;
/* if this is not an exclusion then skip the rest of the [...] construct that already matched. */
if (member_match)
{
while (p && *p != ']')
{
/* bad pattern (Missing ']') */
if (!*p)
return MATCH_PATTERN;
/* skip exact match */
if (*p == '\\')
{
p++;
/* if end of text then we have a bad pattern */
if (!*p)
return MATCH_PATTERN;
}
/* move to next pattern char */
p++;
}
}
break;
}
case '\\': /* next character is quoted and must match exactly */
/* move pattern pointer to quoted char and fall through */
p++;
/* if end of text then we have a bad pattern */
if (!*p)
return MATCH_PATTERN;
/* must match this character exactly */
default:
if (*p != *t)
return MATCH_LITERAL;
}
}
/* if end of text not reached then the pattern fails */
if (*t)
return MATCH_END;
else return MATCH_VALID;
}
bool Match(const regchar_t *match, const regchar_t *string)
{
if (!match)
return true;
int error_type;
error_type = Matche(match, string);
return (error_type == MATCH_VALID);
}

14
Src/nu/regexp.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef NULLSOFT_UTILITY_REGEXP_H
#define NULLSOFT_UTILITY_REGEXP_H
#pragma once
#ifdef _WIN32
#include <wchar.h>
typedef wchar_t regchar_t;
#else
typedef char regchar_t;
#endif
// strings must be in ALL CAPS. sorry.
bool Match(const regchar_t *match, const regchar_t *string);
#endif

49
Src/nu/simple_rwlock.h Normal file
View File

@ -0,0 +1,49 @@
#pragma once
/*
Simple Reader/Writer lock. Lets unlimited readers through but a writer will lock exclusively
not meant for high-throughput uses
this is useful when writes are very infrequent
*/
#include <bfc/platform/types.h>
#include <windows.h>
typedef size_t simple_rwlock_t;
static const size_t simple_rwlock_writer_active = 1; // writer active flag
static const size_t simple_rwlock_reader_increment= 2; // to adjust reader count
static inline void simple_rwlock_write_lock(simple_rwlock_t *lock)
{
while (InterlockedCompareExchangePointer((PVOID volatile*)lock, (PVOID)simple_rwlock_writer_active, 0))
{
// nop
}
}
static inline void simple_rwlock_write_unlock(simple_rwlock_t *lock)
{
#ifdef _WIN64
InterlockedExchangeAdd64((LONGLONG volatile*)lock, -simple_rwlock_writer_active);
#else
InterlockedExchangeAdd((LONG volatile*)lock, -simple_rwlock_writer_active);
#endif
}
static inline void simple_rwlock_read_lock(simple_rwlock_t *lock)
{
InterlockedExchangeAdd((LONG volatile*)lock, simple_rwlock_reader_increment);
while ((*lock & simple_rwlock_writer_active))
{
// nope
}
}
static inline void simple_rwlock_read_unlock(simple_rwlock_t *lock)
{
#ifdef _WIN64
InterlockedExchangeAdd64((LONGLONG volatile*)lock, -simple_rwlock_reader_increment);
#else
InterlockedExchangeAdd((LONG volatile*)lock, -simple_rwlock_reader_increment);
#endif
}

182
Src/nu/smalheap.c Normal file
View File

@ -0,0 +1,182 @@
/***
*smalheap.c - small, simple heap manager
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
*
*
*******************************************************************************/
#include <stdlib.h>
#include <malloc.h>
#include <windows.h>
#define BYTES_PER_PARA 16
#define DWORDS_PER_PARA 4
#define PARAS_PER_PAGE 256 // tunable value
#define PAGES_PER_GROUP 8 // tunable value
#define GROUPS_PER_REGION 32 // tunable value (max 32)
#define BYTES_PER_PAGE (BYTES_PER_PARA * PARAS_PER_PAGE)
#define BYTES_PER_GROUP (BYTES_PER_PAGE * PAGES_PER_GROUP)
#define BYTES_PER_REGION (BYTES_PER_GROUP * GROUPS_PER_REGION)
#ifdef __cplusplus
extern "C" {
#endif
HANDLE _crtheap;
/*
* Primary heap routines (Initialization, termination, malloc and free).
*/
void __cdecl free (
void * pblock
)
{
if ( pblock == NULL )
return;
HeapFree(_crtheap, 0, pblock);
}
int __cdecl _heap_init (
int mtflag
)
{
if ( (_crtheap = HeapCreate( mtflag ? 0 : HEAP_NO_SERIALIZE,
BYTES_PER_PAGE, 0 )) == NULL )
return 0;
return 1;
}
void __cdecl _heap_term (
void
)
{
HeapDestroy( _crtheap );
}
void * __cdecl _nh_malloc (
size_t size,
int nhFlag
)
{
void * retp;
retp = HeapAlloc( _crtheap, 0, size );
/*
* if successful allocation, return pointer to memory
* if new handling turned off altogether, return NULL
*/
return retp;
}
void * __cdecl malloc (
size_t size
)
{
return _nh_malloc( size, 0 );
}
/*
* Secondary heap routines.
*/
void * __cdecl calloc (
size_t num,
size_t size
)
{
void * retp;
size *= num;
retp = HeapAlloc( _crtheap, HEAP_ZERO_MEMORY, size );
return retp;
/* new handler was successful -- try to allocate again */
}
void * __cdecl _expand (
void * pblock,
size_t newsize
)
{
return HeapReAlloc( _crtheap,
HEAP_REALLOC_IN_PLACE_ONLY,
pblock,
newsize );
}
int __cdecl _heapchk(void)
{
int retcode = _HEAPOK;
if ( !HeapValidate( _crtheap, 0, NULL ) &&
(GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) )
retcode = _HEAPBADNODE;
return retcode;
}
int __cdecl _heapmin(void)
{
if ( (HeapCompact( _crtheap, 0 ) == 0) &&
(GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) )
return -1;
return 0;
}
size_t __cdecl _msize (
void * pblock
)
{
return (size_t)HeapSize( _crtheap, 0, pblock );
}
void * __cdecl realloc (
void * pblock,
size_t newsize
)
{
void * retp;
/* if pblock is NULL, call malloc */
if ( pblock == (void *) NULL )
return malloc( newsize );
/* if pblock is !NULL and size is 0, call free and return NULL */
if ( newsize == 0 ) {
free( pblock );
return NULL;
}
retp = HeapReAlloc( _crtheap, 0, pblock, newsize );
return retp;
}
#ifdef __cplusplus
}
#endif

361
Src/nu/sort.cpp Normal file
View File

@ -0,0 +1,361 @@
#include <bfc/platform/types.h>
#include "sort.h"
#include <assert.h>
/***
*qsort.c - quicksort algorithm; qsort() library function for sorting arrays
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
*Purpose:
* To implement the qsort() routine for sorting arrays.
*
*******************************************************************************/
/* Always compile this module for speed, not size */
#pragma optimize("t", on)
/* prototypes for local routines */
static void shortsort(uint8_t *lo, uint8_t *hi, size_t width, const void *context,
int (__fastcall *comp)(const void *, const void *, const void *));
static void swap(uint8_t *p, uint8_t *q, size_t width);
/* this parameter defines the cutoff between using quick sort and
insertion sort for arrays; arrays with lengths shorter or equal to the
below value use insertion sort */
#define CUTOFF 8 /* testing shows that this is good value */
/***
*qsort(base, num, wid, context, comp) - quicksort function for sorting arrays
*
*Purpose:
* quicksort the array of elements
* side effects: sorts in place
* maximum array size is number of elements times size of elements,
* but is limited by the virtual address space of the processor
*
*Entry:
* char *base = pointer to base of array
* size_t num = number of elements in the array
* size_t width = width in bytes of each array element
* int (*comp)() = pointer to function returning analog of strcmp for
* strings, but supplied by user for comparing the array elements.
* it accepts 2 pointers to elements and returns neg if 1<2, 0 if
* 1=2, pos if 1>2.
*
*Exit:
* returns void
*
*Exceptions:
*
*******************************************************************************/
/* sort the array between lo and hi (inclusive) */
#define STKSIZ (8*sizeof(void*) - 2)
void __cdecl nu::qsort (
void *base,
size_t num,
size_t width,
const void *context,
int (__fastcall *comp)(const void *, const void *, const void *)
)
{
/* Note: the number of stack entries required is no more than
1 + log2(num), so 30 is sufficient for any array */
uint8_t *lo, *hi; /* ends of sub-array currently sorting */
uint8_t *mid; /* points to middle of subarray */
uint8_t *loguy, *higuy; /* traveling pointers for partition step */
size_t size; /* size of the sub-array */
uint8_t *lostk[STKSIZ] = {0}, *histk[STKSIZ] = {0};
int stkptr; /* stack for saving sub-array to be processed */
assert((width % sizeof(void *)) == 0);
if (num < 2 || width == 0)
return; /* nothing to do */
stkptr = 0; /* initialize stack */
lo = static_cast<uint8_t *>(base);
hi = (uint8_t *)base + width * (num-1); /* initialize limits */
/* this entry point is for pseudo-recursion calling: setting
lo and hi and jumping to here is like recursion, but stkptr is
preserved, locals aren't, so we preserve stuff on the stack */
recurse:
size = (hi - lo) / width + 1; /* number of el's to sort */
/* below a certain size, it is faster to use a O(n^2) sorting method */
if (size <= CUTOFF) {
shortsort(lo, hi, width, context, comp);
}
else {
/* First we pick a partitioning element. The efficiency of the
algorithm demands that we find one that is approximately the median
of the values, but also that we select one fast. We choose the
median of the first, middle, and last elements, to avoid bad
performance in the face of already sorted data, or data that is made
up of multiple sorted runs appended together. Testing shows that a
median-of-three algorithm provides better performance than simply
picking the middle element for the latter case. */
mid = lo + (size / 2) * width; /* find middle element */
/* Sort the first, middle, last elements into order */
if (comp(lo, mid, context) > 0) {
swap(lo, mid, width);
}
if (comp(lo, hi, context) > 0) {
swap(lo, hi, width);
}
if (comp(mid, hi, context) > 0) {
swap(mid, hi, width);
}
/* We now wish to partition the array into three pieces, one consisting
of elements <= partition element, one of elements equal to the
partition element, and one of elements > than it. This is done
below; comments indicate conditions established at every step. */
loguy = lo;
higuy = hi;
/* Note that higuy decreases and loguy increases on every iteration,
so loop must terminate. */
for (;;) {
/* lo <= loguy < hi, lo < higuy <= hi,
A[i] <= A[mid] for lo <= i <= loguy,
A[i] > A[mid] for higuy <= i < hi,
A[hi] >= A[mid] */
/* The doubled loop is to avoid calling comp(mid,mid), since some
existing comparison funcs don't work when passed the same
value for both pointers. */
if (mid > loguy) {
do {
loguy += width;
} while (loguy < mid && comp(loguy, mid, context) <= 0);
}
if (mid <= loguy) {
do {
loguy += width;
} while (loguy <= hi && comp(loguy, mid, context) <= 0);
}
/* lo < loguy <= hi+1, A[i] <= A[mid] for lo <= i < loguy,
either loguy > hi or A[loguy] > A[mid] */
do {
higuy -= width;
} while (higuy > mid && comp(higuy, mid, context) > 0);
/* lo <= higuy < hi, A[i] > A[mid] for higuy < i < hi,
either higuy == lo or A[higuy] <= A[mid] */
if (higuy < loguy)
break;
/* if loguy > hi or higuy == lo, then we would have exited, so
A[loguy] > A[mid], A[higuy] <= A[mid],
loguy <= hi, higuy > lo */
swap(loguy, higuy, width);
/* If the partition element was moved, follow it. Only need
to check for mid == higuy, since before the swap,
A[loguy] > A[mid] implies loguy != mid. */
if (mid == higuy)
mid = loguy;
/* A[loguy] <= A[mid], A[higuy] > A[mid]; so condition at top
of loop is re-established */
}
/* A[i] <= A[mid] for lo <= i < loguy,
A[i] > A[mid] for higuy < i < hi,
A[hi] >= A[mid]
higuy < loguy
implying:
higuy == loguy-1
or higuy == hi - 1, loguy == hi + 1, A[hi] == A[mid] */
/* Find adjacent elements equal to the partition element. The
doubled loop is to avoid calling comp(mid,mid), since some
existing comparison funcs don't work when passed the same value
for both pointers. */
higuy += width;
if (mid < higuy) {
do {
higuy -= width;
} while (higuy > mid && comp(higuy, mid, context) == 0);
}
if (mid >= higuy) {
do {
higuy -= width;
} while (higuy > lo && comp(higuy, mid, context) == 0);
}
/* OK, now we have the following:
higuy < loguy
lo <= higuy <= hi
A[i] <= A[mid] for lo <= i <= higuy
A[i] == A[mid] for higuy < i < loguy
A[i] > A[mid] for loguy <= i < hi
A[hi] >= A[mid] */
/* We've finished the partition, now we want to sort the subarrays
[lo, higuy] and [loguy, hi].
We do the smaller one first to minimize stack usage.
We only sort arrays of length 2 or more.*/
if ( higuy - lo >= hi - loguy ) {
if (lo < higuy) {
lostk[stkptr] = lo;
histk[stkptr] = higuy;
++stkptr;
} /* save big recursion for later */
if (loguy < hi) {
lo = loguy;
goto recurse; /* do small recursion */
}
}
else {
if (loguy < hi) {
lostk[stkptr] = loguy;
histk[stkptr] = hi;
++stkptr; /* save big recursion for later */
}
if (lo < higuy) {
hi = higuy;
goto recurse; /* do small recursion */
}
}
}
/* We have sorted the array, except for any pending sorts on the stack.
Check if there are any, and do them. */
--stkptr;
if (stkptr >= 0) {
lo = lostk[stkptr];
hi = histk[stkptr];
goto recurse; /* pop subarray from stack */
}
else
return; /* all subarrays done */
}
/***
*shortsort(hi, lo, width, comp) - insertion sort for sorting short arrays
*
*Purpose:
* sorts the sub-array of elements between lo and hi (inclusive)
* side effects: sorts in place
* assumes that lo < hi
*
*Entry:
* char *lo = pointer to low element to sort
* char *hi = pointer to high element to sort
* size_t width = width in bytes of each array element
* int (*comp)() = pointer to function returning analog of strcmp for
* strings, but supplied by user for comparing the array elements.
* it accepts 2 pointers to elements and returns neg if 1<2, 0 if
* 1=2, pos if 1>2.
*
*Exit:
* returns void
*
*Exceptions:
*
*******************************************************************************/
static void __cdecl shortsort (
uint8_t *lo,
uint8_t *hi,
size_t width,
const void *context,
int (__fastcall *comp)(const void *, const void *, const void *)
)
{
uint8_t *p;
/* Note: in assertions below, i and j are alway inside original bound of
array to sort. */
while (hi > lo) {
/* A[i] <= A[j] for i <= j, j > hi */
uint8_t *max = lo;
for (p = lo+width; p <= hi; p += width) {
/* A[i] <= A[max] for lo <= i < p */
if (comp(p, max, context) > 0) {
max = p;
}
/* A[i] <= A[max] for lo <= i <= p */
}
/* A[i] <= A[max] for lo <= i <= hi */
swap(max, hi, width);
/* A[i] <= A[hi] for i <= hi, so A[i] <= A[j] for i <= j, j >= hi */
hi -= width;
/* A[i] <= A[j] for i <= j, j > hi, loop top condition established */
}
/* A[i] <= A[j] for i <= j, j > lo, which implies A[i] <= A[j] for i < j,
so array is sorted */
}
/***
*swap(a, b, width) - swap two elements
*
*Purpose:
* swaps the two array elements of size width
*
*Entry:
* char *a, *b = pointer to two elements to swap
* size_t width = width in bytes of each array element
*
*Exit:
* returns void
*
*Exceptions:
*
*******************************************************************************/
static void swap (
uint8_t *_a,
uint8_t *_b,
size_t width
)
{
#if 1
void *tmp;
void **a = (void **)_a;
void **b = (void **)_b;
if ( a != b )
/* Do the swap one character at a time to avoid potential alignment
problems. */
do {
tmp = *a;
*a++ = *b;
*b++ = tmp;
width-=sizeof(void *);
} while (width);
#else
//void *temp = alloca(width);
memcpy(temp, a, width);
memcpy(a, b, width);
memcpy(b, temp, width);
#endif
}

11
Src/nu/sort.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
namespace nu
{
void __cdecl qsort (
void *base,
size_t num,
size_t width,
const void *context,
int (__fastcall *comp)(const void *, const void *, const void *));
};

2
Src/nu/strsafe.c Normal file
View File

@ -0,0 +1,2 @@
#define STRSAFE_LIB_IMPL
#include "strsafe.h"

6647
Src/nu/strsafe.h Normal file

File diff suppressed because it is too large Load Diff

23
Src/nu/strsafe.sln Normal file
View File

@ -0,0 +1,23 @@
Microsoft Visual Studio Solution File, Format Version 8.00
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "strsafe", "strsafe.vcproj", "{8402F30F-5EFE-4066-9D94-0B47B6C83D43}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfiguration) = preSolution
Debug = Debug
Release = Release
EndGlobalSection
GlobalSection(ProjectDependencies) = postSolution
EndGlobalSection
GlobalSection(ProjectConfiguration) = postSolution
{8402F30F-5EFE-4066-9D94-0B47B6C83D43}.Debug.ActiveCfg = Debug|Win32
{8402F30F-5EFE-4066-9D94-0B47B6C83D43}.Debug.Build.0 = Debug|Win32
{8402F30F-5EFE-4066-9D94-0B47B6C83D43}.Release.ActiveCfg = Release|Win32
{8402F30F-5EFE-4066-9D94-0B47B6C83D43}.Release.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection
GlobalSection(ExtensibilityAddIns) = postSolution
EndGlobalSection
EndGlobal

150
Src/nu/strsafe.vcproj Normal file
View File

@ -0,0 +1,150 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.10"
Name="strsafe"
ProjectGUID="{8402F30F-5EFE-4066-9D94-0B47B6C83D43}"
Keyword="Win32Proj">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="Debug"
IntermediateDirectory="Debug"
ConfigurationType="2"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;STRSAFE_EXPORTS"
MinimalRebuild="TRUE"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="4"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
OutputFile="$(OutDir)/strsafe.dll"
LinkIncremental="2"
ModuleDefinitionFile="strsafe.def"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile="$(OutDir)/strsafe.pdb"
SubSystem="2"
ImportLibrary="$(OutDir)/strsafe.lib"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="Release"
IntermediateDirectory="Release"
ConfigurationType="2"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
GlobalOptimizations="TRUE"
EnableIntrinsicFunctions="TRUE"
FavorSizeOrSpeed="1"
OmitFramePointers="TRUE"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;STRSAFE_EXPORTS"
StringPooling="TRUE"
RuntimeLibrary="2"
EnableFunctionLevelLinking="TRUE"
TreatWChar_tAsBuiltInType="TRUE"
ForceConformanceInForLoopScope="TRUE"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="3"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="unicows.lib"
OutputFile="$(ProgramFiles)/Winamp/strsafe.dll"
LinkIncremental="1"
ModuleDefinitionFile="strsafe.def"
GenerateDebugInformation="TRUE"
SubSystem="2"
OptimizeReferences="2"
EnableCOMDATFolding="2"
OptimizeForWindows98="1"
ImportLibrary="$(OutDir)/strsafe.lib"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
<File
RelativePath=".\strsafe.c">
</File>
<File
RelativePath=".\strsafe.h">
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

36
Src/nu/threadname.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef NULLSOFT_UTILITY_THREADNAME_H
#define NULLSOFT_UTILITY_THREADNAME_H
#ifdef _DEBUG
#include <windows.h>
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // must be 0x1000
LPCSTR szName; // pointer to name (in user addr space)
DWORD dwThreadID; // thread ID (-1=caller thread)
DWORD dwFlags; // reserved for future use, must be zero
}
THREADNAME_INFO;
__inline void SetThreadName(DWORD dwThreadID, const char *szThreadName)
{
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = szThreadName;
info.dwThreadID = dwThreadID;
info.dwFlags = 0;
__try
{
RaiseException( 0x406D1388, 0, sizeof(info) / sizeof(DWORD), (const ULONG_PTR *)&info );
}
__except(EXCEPTION_CONTINUE_EXECUTION)
{}
}
#else
#define SetThreadName(x,y)
#endif
#endif

View File

@ -0,0 +1,79 @@
#include "ThreadFunctions.h"
#include "threadpool_types.h"
ThreadFunctions::ThreadFunctions(int create_function_list)
{
if (create_function_list)
{
functions_semaphore = CreateSemaphore(0, 0, ThreadPoolTypes::MAX_SEMAPHORE_VALUE, 0);
InitializeCriticalSectionAndSpinCount(&functions_guard, 200);
}
else
functions_semaphore = 0;
}
ThreadFunctions::~ThreadFunctions()
{
if (functions_semaphore)
{
CloseHandle(functions_semaphore);
DeleteCriticalSection(&functions_guard);
}
}
void ThreadFunctions::Add(HANDLE handle, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id)
{
Nullsoft::Utility::AutoLock l(guard);
Data *new_data = (Data *)calloc(1, sizeof(Data));
new_data->func = func;
new_data->user_data = user_data;
new_data->id = id;
data[handle] = new_data;
}
bool ThreadFunctions::Get(HANDLE handle, api_threadpool::ThreadPoolFunc *func, void **user_data, intptr_t *id)
{
Nullsoft::Utility::AutoLock l(guard);
DataMap::iterator found = data.find(handle);
if (found == data.end())
return false;
const Data *d = found->second;
*func = d->func;
*user_data = d->user_data;
*id = d->id;
return true;
}
void ThreadFunctions::QueueFunction(api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id)
{
Data *new_data = (Data *)calloc(1, sizeof(Data));
new_data->func = func;
new_data->user_data = user_data;
new_data->id = id;
EnterCriticalSection(&functions_guard);
functions_list.push_front(new_data);
LeaveCriticalSection(&functions_guard); // unlock before releasing the semaphore early so we don't lock convoy
ReleaseSemaphore(functions_semaphore, 1, 0);
}
bool ThreadFunctions::PopFunction(api_threadpool::ThreadPoolFunc *func, void **user_data, intptr_t *id)
{
EnterCriticalSection(&functions_guard);
if (!functions_list.empty())
{
ThreadFunctions::Data *data = functions_list.back();
functions_list.pop_back();
LeaveCriticalSection(&functions_guard);
*func = data->func;
*user_data = data->user_data;
*id = data->id;
free(data);
return true;
}
else
{
LeaveCriticalSection(&functions_guard);
return false;
}
}

View File

@ -0,0 +1,31 @@
#pragma once
#include "api_threadpool.h"
#include <map>
#include <deque>
#include "../AutoLock.h"
class ThreadFunctions
{
public:
struct Data
{
api_threadpool::ThreadPoolFunc func;
void *user_data;
intptr_t id;
};
ThreadFunctions(int create_function_list=1);
~ThreadFunctions();
void Add(HANDLE handle, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id);
bool Get(HANDLE handle, api_threadpool::ThreadPoolFunc *func, void **user_data, intptr_t *id);
void QueueFunction(api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id);
bool PopFunction(api_threadpool::ThreadPoolFunc *func, void **user_data, intptr_t *id);
typedef std::map<HANDLE, const ThreadFunctions::Data*> DataMap;
DataMap data;
Nullsoft::Utility::LockGuard guard;
typedef std::deque<ThreadFunctions::Data*> FuncList;
FuncList functions_list;
CRITICAL_SECTION functions_guard;
HANDLE functions_semaphore;
};

View File

@ -0,0 +1,274 @@
#include "ThreadID.h"
DWORD ThreadID::thread_func_stub(LPVOID param)
{
ThreadID *t = static_cast<ThreadID*>(param);
if (t != NULL)
{
return t->ThreadFunction();
}
else return 0;
}
void ThreadID::Kill()
{
if (threadHandle && threadHandle != INVALID_HANDLE_VALUE)
{
//cut: WaitForSingleObject(threadHandle, INFINITE);
while (WaitForMultipleObjectsEx(1, &threadHandle, FALSE, INFINITE, TRUE) != WAIT_OBJECT_0)
{
}
}
}
ThreadID::ThreadID(ThreadFunctions *t_f, HANDLE killswitch, HANDLE global_functions_semaphore,
ThreadPoolTypes::HandleList &inherited_handles,
volatile LONG *thread_count, HANDLE _max_load_event,
int _reserved, int _com_type) : ThreadFunctions(_reserved)
{
/* initialize values */
released = false;
InitializeCriticalSection(&handle_lock);
/* grab values passed to us */
reserved = _reserved;
com_type = _com_type;
max_load_event = _max_load_event;
global_functions = t_f;
num_threads_available = thread_count;
/* wait_handles[0] is kill switch */
wait_handles.push_back(killswitch);
/* wait_handles[1] is wake switch */
wakeHandle = CreateSemaphore(0, 0, ThreadPoolTypes::MAX_SEMAPHORE_VALUE, 0);
wait_handles.push_back(wakeHandle);
if (reserved)
{
/* if thread is reserved,
wait_handles[2] is a Funcion Call wake semaphore
for this thread only. */
wait_handles.push_back(functions_semaphore); // WAIT_OBJECT_0+1 == per-thread queued functions
}
else
{
/* if thread is not reserved,
wait_handles[2] is a Function Call wake semaphore
global to all threads */
wait_handles.push_back(global_functions_semaphore); // WAIT_OBJECT_0+2 == any-thread queued functions
}
/* add inherited handles
(handles added to thread pool before this thread was created) */
for ( ThreadPoolTypes::HandleList::iterator itr = inherited_handles.begin(); itr != inherited_handles.end(); itr++ )
{
wait_handles.push_back( *itr );
}
/* start thread */
threadHandle = CreateThread(0, 0, thread_func_stub, this, 0, 0);
}
ThreadID::~ThreadID()
{
CloseHandle(threadHandle);
CloseHandle(wakeHandle);
DeleteCriticalSection(&handle_lock);
}
bool ThreadID::TryAddHandle(HANDLE new_handle)
{
// let's see if we get lucky and can access the handle list directly
if (TryEnterCriticalSection(&handle_lock))
{
// made it
wait_handles.push_back(new_handle);
LeaveCriticalSection(&handle_lock);
return true;
}
else
{
ReleaseSemaphore(wakeHandle, 1, 0); // kick the thread out of WaitForMultiple...
return false;
}
}
void ThreadID::WaitAddHandle(HANDLE handle)
{
// wakeHandle already got released once by nature of this function being called
EnterCriticalSection(&handle_lock);
wait_handles.push_back(handle);
LeaveCriticalSection(&handle_lock);
ReleaseSemaphore(wakeHandle, 1, 0); // kick out the second wait
}
void ThreadID::AddHandle(HANDLE new_handle)
{
if (!TryAddHandle(new_handle))
WaitAddHandle(new_handle);
}
bool ThreadID::TryRemoveHandle(HANDLE handle)
{
// let's see if we get lucky and can access the handle list directly
if (TryEnterCriticalSection(&handle_lock))
{
RemoveHandle_Internal(handle);
LeaveCriticalSection(&handle_lock);
return true;
}
else
{
ReleaseSemaphore(wakeHandle, 1, 0); // kick the thread out of WaitForMultiple...
return false;
}
return false;
}
void ThreadID::WaitRemoveHandle(HANDLE handle)
{
// wakeHandle already got released once by nature of this function being called
EnterCriticalSection(&handle_lock);
RemoveHandle_Internal(handle);
LeaveCriticalSection(&handle_lock);
ReleaseSemaphore(wakeHandle, 1, 0); // kick out the second wait
}
void ThreadID::RemoveHandle(HANDLE handle)
{
if (!TryRemoveHandle(handle))
WaitRemoveHandle(handle);
}
void ThreadID::RemoveHandle_Internal(HANDLE handle)
{
// first three handles are reserved, so start after that
for (size_t i=3;i<wait_handles.size();i++)
{
if (wait_handles[i] == handle)
{
wait_handles.erase(wait_handles.begin() + i);
i--;
}
}
}
bool ThreadID::IsReserved() const
{
return !!reserved;
}
DWORD CALLBACK ThreadID::ThreadFunction()
{
switch(com_type)
{
case api_threadpool::FLAG_REQUIRE_COM_MT:
CoInitializeEx(0, COINIT_MULTITHREADED);
break;
case api_threadpool::FLAG_REQUIRE_COM_STA:
CoInitialize(0);
break;
}
while (1)
{
InterlockedIncrement(num_threads_available);
EnterCriticalSection(&handle_lock);
DWORD ret = WaitForMultipleObjectsEx((DWORD)wait_handles.size(), wait_handles.data(), FALSE, INFINITE, TRUE);
// cut: LeaveCriticalSection(&handle_lock);
if (InterlockedDecrement(num_threads_available) == 0 && !reserved)
SetEvent(max_load_event); // notify the watch dog if all the threads are used up
if (ret == WAIT_OBJECT_0)
{
// killswitch
LeaveCriticalSection(&handle_lock);
break;
}
else if (ret == WAIT_OBJECT_0 + 1)
{
// we got woken up to release the handles lock
// wait for the second signal
LeaveCriticalSection(&handle_lock);
InterlockedIncrement(num_threads_available);
WaitForSingleObject(wakeHandle, INFINITE);
InterlockedDecrement(num_threads_available);
}
else if (ret == WAIT_OBJECT_0 + 2)
{
LeaveCriticalSection(&handle_lock);
api_threadpool::ThreadPoolFunc func;
void *user_data;
intptr_t id;
if (reserved)
{
// per-thread queued functions
if (PopFunction(&func, &user_data, &id))
{
func(0, user_data, id);
}
}
else
{
// global queued functions
if (global_functions->PopFunction(&func, &user_data, &id))
{
func(0, user_data, id);
}
}
}
else if (ret > WAIT_OBJECT_0 && ret < (WAIT_OBJECT_0 + wait_handles.size()))
{
DWORD index = ret - WAIT_OBJECT_0;
HANDLE handle = wait_handles[index];
LeaveCriticalSection(&handle_lock);
/* !!! race condition here if someone calls ThreadPool::RemoveHandle and then CloseHandle() !!!
before calling RemoveHandle, caller needs to either
ensure that Event is unsignalled (And won't be signalled)
or call RemoveHandle from within the function callback */
api_threadpool::ThreadPoolFunc func;
void *user_data;
intptr_t id;
if (global_functions->Get(handle, &func, &user_data, &id))
{
func(handle, user_data, id);
}
}
else
{
LeaveCriticalSection(&handle_lock);
}
}
if (com_type & api_threadpool::MASK_COM_FLAGS)
CoUninitialize();
return 0;
}
bool ThreadID::CanRunCOM(int flags) const
{
switch(com_type)
{
case api_threadpool::FLAG_REQUIRE_COM_MT: // if we're a CONIT_MULTITHREADEX thread (default)
return !(flags & api_threadpool::FLAG_REQUIRE_COM_STA); // don't let STA stuff run
case api_threadpool::FLAG_REQUIRE_COM_STA: // if we're a CoInitialize(0) thread
return !(flags & api_threadpool::FLAG_REQUIRE_COM_MT); // don't let MT stuff run
}
return false; // shouldn't get here
}
bool ThreadID::IsReleased() const
{
return released;
}
void ThreadID::Reserve()
{
released=false;
}
void ThreadID::Release()
{
released=true;
}

View File

@ -0,0 +1,56 @@
#pragma once
#include <windows.h>
#include "ThreadFunctions.h"
#include "threadpool_types.h"
#include <vector>
class ThreadID : private ThreadFunctions
{
public:
static DWORD CALLBACK thread_func_stub(LPVOID param);
ThreadID(ThreadFunctions *t_f, HANDLE killswitch, HANDLE global_functions_semaphore, ThreadPoolTypes::HandleList &inherited_handles, volatile LONG *thread_count, HANDLE _max_load_event, int _reserved, int _com_type);
~ThreadID();
void Kill();
/* Try and Wait must be paired!!! */
bool TryAddHandle(HANDLE new_handle);
void WaitAddHandle(HANDLE new_handle);
void AddHandle(HANDLE new_handle);
/* Try and Wait must be paired!!! */
bool TryRemoveHandle(HANDLE handle);
void WaitRemoveHandle(HANDLE handle);
void RemoveHandle(HANDLE handle);
using ThreadFunctions::QueueFunction;
bool IsReserved() const;
bool IsReleased() const;
bool CanRunCOM(int flags) const;
void Reserve(); // re-reserves a released thread
void Release(); // release a reversed thread
private:
void RemoveHandle_Internal(HANDLE handle);
DWORD CALLBACK ThreadFunction();
int reserved;
ThreadFunctions *global_functions;
volatile LONG *num_threads_available;
int com_type;
bool released;
ThreadFunctions local_functions;
// list of handles we're waiting on
typedef std::vector<HANDLE> HandleList;
HandleList wait_handles;
CRITICAL_SECTION handle_lock;
// handles we create/own
HANDLE threadHandle;
HANDLE wakeHandle;
// handles given to us
HANDLE max_load_event;
};

View File

@ -0,0 +1,365 @@
#include "ThreadPool.h"
ThreadPool::ThreadPool()
{
for ( int i = 0; i < THREAD_TYPES; i++ )
{
num_threads_available[ i ] = 0;
max_load_event[ i ] = CreateEvent( NULL, TRUE, FALSE, NULL );
}
killswitch = CreateEvent( NULL, TRUE, FALSE, NULL );
// one thread of each type to start
for ( int i = 0; i < 2; i++ )
CreateNewThread_Internal( i );
watchdog_thread_handle = CreateThread( 0, 0, WatchDogThreadProcedure_stub, this, 0, 0 );
}
void ThreadPool::Kill()
{
SetEvent( killswitch );
WaitForSingleObject( watchdog_thread_handle, INFINITE );
CloseHandle( watchdog_thread_handle );
for ( ThreadID *l_thread : threads )
{
l_thread->Kill();
delete l_thread;
}
CloseHandle( killswitch );
for ( int i = 0; i < THREAD_TYPES; i++ )
CloseHandle( max_load_event[ i ] );
}
DWORD ThreadPool::WatchDogThreadProcedure_stub( LPVOID param )
{
ThreadPool *_this = (ThreadPool *)param;
return _this->WatchDogThreadProcedure();
}
/*
watchdog will get woken up when number of available threads hits zero
it creates a new thread, sleeps for a bit to let things "settle" and then reset the event
it will need a copy of all "any-thread" handles to build the new thread, and will need to manage in a thread safe way
(so a new thread doesn't "miss" a handle that is added in the interim)
*/
DWORD CALLBACK ThreadPool::WatchDogThreadProcedure()
{
// we ignore the max load event for reserved threads
HANDLE events[ 3 ] = { killswitch, max_load_event[ TYPE_MT ], max_load_event[ TYPE_STA ] };
while ( 1 )
{
DWORD ret = WaitForMultipleObjects( 3, events, FALSE, INFINITE );
if ( ret == WAIT_OBJECT_0 )
{
break;
}
else if ( ret == WAIT_OBJECT_0 + 1 || ret == WAIT_OBJECT_0 + 2 )
{
int thread_type = ret - ( WAIT_OBJECT_0 + 1 );
// this signal is for "full thread load reached"
// lets make sure we're actually at max capacity
Sleep( 10 ); // sleep a bit
if ( num_threads_available[ thread_type ] != 0 ) // see if we're still fully-loaded
continue;
guard.Lock();
CreateNewThread_Internal( thread_type );
guard.Unlock();
Sleep( 250 ); // give the system time to 'settle down' so we don't spawn a ton of threads in a row
ResetEvent( max_load_event[ thread_type ] );
}
}
return 0;
}
ThreadID *ThreadPool::ReserveThread( int flags )
{
// first, check to see if there's any released threads we can grab
Nullsoft::Utility::AutoLock threadlock( guard );
for ( ThreadID *t : threads )
{
if ( t->IsReserved() && t->IsReleased() && t->CanRunCOM( flags ) )
{
t->Reserve();
return t;
}
}
// TODO: if there are enough free threads available, mark one as reserved
// this will involve signalling the thread to switch to 'reserved' mode
// swapping out the 'function list' semaphore with a local one
// and removing all 'busy handles' from the queue
// can probably use the 'wake' handle to synchronize this
/*
int thread_type = GetThreadType(flags);
if (num_threads_available[thread_type > 2])
{
for (size_t i=0;i!=threads.size();i++)
{
if (threads[i]->IsReserved() == false && threads[i]->CanRunCOM(flags))
{
}
}
}
*/
ThreadID *new_thread = CreateNewThread_Internal( GetThreadType( flags, 1 ) );
return new_thread;
}
void ThreadPool::ReleaseThread( ThreadID *thread_id )
{
if ( thread_id )
{
// lock so there's no race condition between ReserveThread() and ReleaseThread()
Nullsoft::Utility::AutoLock threadlock( guard );
thread_id->Release();
}
}
int ThreadPool::AddHandle( ThreadID *thread_id, HANDLE handle, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id, int flags )
{
// TODO: need to ensure that handles are not duplicated
thread_functions.Add( handle, func, user_data, id );
if ( thread_id )
{
if ( thread_id->CanRunCOM( flags ) )
thread_id->AddHandle( handle );
else
return 1;
return 0;
}
else
{
/* increment thread counts temporarily - because the massive wake-up
causes thread counts to go to 0 */
for ( int i = 0; i < THREAD_TYPES; i++ )
InterlockedIncrement( &num_threads_available[ i ] );
guard.Lock();
AddHandle_Internal( 0, handle, flags );
bool thread_types[ THREAD_TYPES ];
GetThreadTypes( flags, thread_types );
for ( int i = 0; i < THREAD_TYPES; i++ )
{
if ( thread_types[ i ] )
any_thread_handles[ i ].push_back( handle );
}
guard.Unlock();
for ( int i = 0; i < THREAD_TYPES; i++ )
InterlockedDecrement( &num_threads_available[ i ] );
}
return 0;
}
/* helper functions for adding/removing handles,
we keep going through the list as long as we can add/remove immediately.
once we have to block, we recurse the function starting at the next handle
when the function returns, we wait.
this lets us do some work rather than sit and wait for each thread's lock */
void ThreadPool::RemoveHandle_Internal(size_t start, HANDLE handle)
{
for (;start!=threads.size();start++)
{
ThreadID *t = threads[start];
if (!t->TryRemoveHandle(handle)) // try to remove
{
// have to wait
RemoveHandle_Internal(start+1, handle); // recurse start with the next thread
t->WaitRemoveHandle(handle); // finish the job
return;
}
}
}
void ThreadPool::AddHandle_Internal(size_t start, HANDLE handle, int flags)
{
for (;start<threads.size();start++)
{
ThreadID *t = threads[start];
if ((flags & api_threadpool::FLAG_LONG_EXECUTION) && t->IsReserved())
continue;
if (!t->CanRunCOM(flags))
continue;
if (!t->TryAddHandle(handle)) // try to add
{
// have to wait,
AddHandle_Internal(start+1, handle, flags); // recurse start with the next thread
t->WaitAddHandle(handle); // finish the job
return;
}
}
}
void ThreadPool::RemoveHandle(ThreadID *thread_id, HANDLE handle)
{
if (thread_id)
{
thread_id->RemoveHandle(handle);
}
else
{
/* increment thread counts temporarily - because the massive wake-up
causes thread counts to go to 0 */
for (int i=0;i<THREAD_TYPES;i++)
InterlockedIncrement(&num_threads_available[i]);
guard.Lock();
RemoveHandle_Internal(0, handle);
for (int j=0;j<THREAD_TYPES;j++)
{
//for (ThreadPoolTypes::HandleList::iterator itr = any_thread_handles[j].begin();
// itr != any_thread_handles[j].end();
// itr++)
ThreadPoolTypes::HandleList::iterator itr = any_thread_handles[j].begin();
while(itr != any_thread_handles[j].end())
{
if (*itr == handle)
{
itr = any_thread_handles[j].erase(itr);
}
else
{
itr++;
}
}
}
guard.Unlock();
for (int i=0;i<THREAD_TYPES;i++)
InterlockedDecrement(&num_threads_available[i]);
}
}
int ThreadPool::RunFunction(ThreadID *threadid, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id, int flags)
{
if (threadid)
threadid->QueueFunction(func, user_data, id);
else
thread_functions.QueueFunction(func, user_data, id);
return 0;
}
ThreadID *ThreadPool::CreateNewThread_Internal(int thread_type)
{
int reserved=0;
int com_type = api_threadpool::FLAG_REQUIRE_COM_MT; // default
switch(thread_type)
{
case TYPE_STA_RESERVED:
reserved=1;
case TYPE_STA:
com_type = api_threadpool::FLAG_REQUIRE_COM_STA;
break;
case TYPE_MT_RESERVED:
reserved=1;
case TYPE_MT:
com_type = api_threadpool::FLAG_REQUIRE_COM_MT;
break;
}
Nullsoft::Utility::AutoLock threadlock(guard); // lock here (rather than after new ThreadID) to protect any_thread_handles
ThreadID *new_thread = new ThreadID(&thread_functions, killswitch, thread_functions.functions_semaphore,
any_thread_handles[thread_type],
&num_threads_available[thread_type], max_load_event[thread_type],
reserved, com_type);
threads.push_back(new_thread);
return new_thread;
}
size_t ThreadPool::GetNumberOfThreads()
{
Nullsoft::Utility::AutoLock threadlock(guard);
return threads.size();
}
size_t ThreadPool::GetNumberOfActiveThreads()
{
size_t numThreads = GetNumberOfThreads();
for (int i=0;i<THREAD_TYPES;i++)
numThreads -= num_threads_available[i];
return numThreads;
}
int ThreadPool::GetThreadType(int flags, int reserved)
{
flags &= api_threadpool::MASK_COM_FLAGS;
int thread_type=TYPE_MT;
switch(flags)
{
case api_threadpool::FLAG_REQUIRE_COM_STA:
thread_type = reserved?TYPE_STA_RESERVED:TYPE_STA;
break;
case 0: // default
case api_threadpool::FLAG_REQUIRE_COM_MT:
thread_type = reserved?TYPE_MT_RESERVED:TYPE_MT;
break;
}
return thread_type;
}
void ThreadPool::GetThreadTypes(int flags, bool types[THREAD_TYPES])
{
for (int i=0;i<THREAD_TYPES;i++)
{
types[i]=true;
}
if (flags & api_threadpool::FLAG_REQUIRE_COM_STA)
{
types[TYPE_MT] = false;
types[TYPE_MT] = false;
}
if (flags & api_threadpool::FLAG_REQUIRE_COM_STA)
{
types[TYPE_STA] = false;
types[TYPE_STA_RESERVED] = false;
}
if (flags & api_threadpool::FLAG_LONG_EXECUTION)
{
types[TYPE_STA_RESERVED] = false;
types[TYPE_MT_RESERVED] = false;
}
}
#define CBCLASS ThreadPool
START_DISPATCH;
CB(RESERVETHREAD, ReserveThread)
VCB(RELEASETHREAD, ReleaseThread)
CB(ADDHANDLE, AddHandle)
VCB(REMOVEHANDLE, RemoveHandle)
CB(RUNFUNCTION, RunFunction)
CB(GETNUMBEROFTHREADS, GetNumberOfThreads)
CB(GETNUMBEROFACTIVETHREADS, GetNumberOfActiveThreads)
END_DISPATCH;
#undef CBCLASS

View File

@ -0,0 +1,98 @@
#pragma once
#include <windows.h>
#include <bfc/platform/types.h>
#include <vector>
#include "../autolock.h"
#include "ThreadID.h"
#include "ThreadFunctions.h"
#include "threadpool_types.h"
/* random notes
HANDLEs common to all threads
WaitForMultipleObjectsEx() around these
0 - killswitch
1 - shared APC event. since threads might want to use APCs themselves, we'll use a different mechanism (thread-safe FIFO and an event). the intention is that APCs that can go on any thread will use this handle
2 - per thread APC event.
parameters for "run my function" method
function pointer, user data, flags
flags:
interrupt - for very short non-locking functions where it is safe to interrupt another thread, uses QueueUserAPC
no_wait - spawn a new thread if all threads are busy
com_multithreaded - all threads are created with CoInitialize(0), if you need a COINIT_MULTITHREADED thread, use this flag
parameters for "add my handle" method
handle, function pointer, user data, flags
flags:
single_thread - only one thread in the pool will wait on your object, useful if your handle is not auto-reset
parameters for "function call repeat" - calls your function until you return 0
function pointer, user data, flags
flags:
single_thread - keep calling on the same thread
*/
class ThreadPool : public api_threadpool
{
public:
static const char *getServiceName() { return "Thread Pool API"; }
static const GUID getServiceGuid() { return ThreadPoolGUID; }
public:
// Owner API:
ThreadPool();
void Kill();
// User API:
/* If you have multiple events, APCs, etc and you need them to always run on the same thread
you can reserve one */
ThreadID *ReserveThread(int flags);
/* Release a thread you've previously reserved */
void ReleaseThread(ThreadID *thread_id);
/* adds a waitable handle to the thread pool. when the event is signalled, your function ptr will get called
user_data and id values get passed to your function.
your function should return 1 to indicate that it can be removed
flags, see api_threadpool */
int AddHandle(ThreadID *threadid, HANDLE handle, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id, int flags);
void RemoveHandle(ThreadID *threadid, HANDLE handle);
int RunFunction(ThreadID *threadid, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id, int flags);
size_t GetNumberOfThreads(); // total number of threads in the threadpool
size_t GetNumberOfActiveThreads(); // number of threads that are currently being used (inside user function but not necessarily busy)
private:
enum
{
TYPE_MT = 0,
TYPE_STA = 1,
TYPE_MT_RESERVED = 2,
TYPE_STA_RESERVED = 3,
THREAD_TYPES = 4, // two thread types, single threaded apartment COM and multithreaded COM
};
private:
static DWORD CALLBACK WatchDogThreadProcedure_stub(LPVOID param);
ThreadID *CreateNewThread_Internal(int thread_type = 0);
DWORD CALLBACK WatchDogThreadProcedure();
static int GetThreadType(int flags, int reserved = 0);
static void GetThreadTypes(int flags, bool types[THREAD_TYPES]);
void RemoveHandle_Internal(size_t start, HANDLE handle); // recursive helper function for RemoveHandle()
void AddHandle_Internal(size_t start, HANDLE handle, int flags); // recursive helper function for RemoveHandle()
Nullsoft::Utility::LockGuard guard; // guards threads, any_thread_handles, and non_reserved_handles data structures
typedef std::vector<ThreadID*> ThreadList;
ThreadList threads;
ThreadPoolTypes::HandleList any_thread_handles[THREAD_TYPES];
HANDLE killswitch;
HANDLE watchdog_thread_handle;
volatile LONG num_threads_available[THREAD_TYPES];
ThreadFunctions thread_functions;
HANDLE max_load_event[THREAD_TYPES];
protected:
RECVS_DISPATCH;
};

View File

@ -0,0 +1,54 @@
#ifndef NU_THREADPOOL_TIMERHANDLE_H
#define NU_THREADPOOL_TIMERHANDLE_H
#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x400)
#error Must define _WIN32_WINNT >= 0x400 to use TimerHandle
#endif
#include <windows.h>
#include <bfc/platform/types.h>
/*
TimerHandle() constructor will make a new timer handle
TimerHandle(existing_handle) will "take over" an existing handle
~TimerHandle() DOES NOT CloseHandle as this object is meant as a helper
call Close() to kill the timer handle
The timer will be "one shot" auto-reset.
Because it is meant to be compatible with the threadpool, manual-reset timers and periodic timers
are not recommended!! You will have re-entrancy problems
If you want "periodic" behavior, call Wait() at the end of your ThreadPoolFunc
*/
class TimerHandle
{
public:
TimerHandle() { timerHandle = CreateWaitableTimer( 0, FALSE, 0 ); }
TimerHandle( HANDLE p_handle ) { timerHandle = p_handle; }
void Close() { CloseHandle( timerHandle ); }
void Wait( uint64_t p_milliseconds )
{
/* MSDN notes about SetWaitableTimer: 100 nanosecond resolution, Negative values indicate relative time*/
LARGE_INTEGER timeout = { 0 };
timeout.QuadPart = -( (int64_t)p_milliseconds * 1000LL /*to microseconds*/ * 10LL /* to 100 nanoseconds */ );
SetWaitableTimer( timerHandle, &timeout, 0, 0, 0, FALSE );
}
void Poll( uint64_t p_milliseconds ) // only use on a reserved thread!!!
{
/* MSDN notes about SetWaitableTimer: 100 nanosecond resolution, Negative values indicate relative time*/
LARGE_INTEGER timeout = { 0 };
timeout.QuadPart = -( (int64_t)p_milliseconds * 1000LL /*to microseconds*/ * 10LL /* to 100 nanoseconds */ );
SetWaitableTimer( timerHandle, &timeout, (LONG)p_milliseconds, 0, 0, FALSE );
}
/* TODO: WaitUntil method for absolute times */
void Cancel() { CancelWaitableTimer( timerHandle ); }
operator HANDLE() { return timerHandle; }
private:
HANDLE timerHandle;
};
#endif // !NU_THREADPOOL_TIMERHANDLE_H

View File

@ -0,0 +1,92 @@
#pragma once
#include <windows.h>
#include <bfc/platform/types.h>
#include <bfc/dispatch.h>
class ThreadID;
class api_threadpool : public Dispatchable
{
protected:
api_threadpool() {}
~api_threadpool() {}
public:
typedef int (*ThreadPoolFunc)(HANDLE handle, void *user_data, intptr_t id);
enum
{
// pass this flag to AddHandle or RunFunction indicate that your thread function
// might run for a long time
FLAG_LONG_EXECUTION = 0x1,
FLAG_REQUIRE_COM_STA = 0x2,
FLAG_REQUIRE_COM_MT = 0x4,
MASK_COM_FLAGS = 0x6,
};
public:
ThreadID *ReserveThread(int flags);
/* Release a thread you've previously reserved */
void ReleaseThread(ThreadID *thread_id);
/* adds a waitable handle to the thread pool. when the event is signalled, your function ptr will get called
user_data and id values get passed to your function.
your function should return 1 to indicate that it can be removed
flags, see api_threadpool */
int AddHandle(ThreadID *threadid, HANDLE handle, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id, int flags);
void RemoveHandle(ThreadID *threadid, HANDLE handle);
int RunFunction(ThreadID *threadid, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id, int flags);
size_t GetNumberOfThreads(); // total number of threads in the threadpool
size_t GetNumberOfActiveThreads(); // number of threads that are currently being used (inside user function but not necessarily busy)
enum
{
RESERVETHREAD = 0,
RELEASETHREAD = 1,
ADDHANDLE = 2,
REMOVEHANDLE = 3,
RUNFUNCTION = 4,
GETNUMBEROFTHREADS = 5,
GETNUMBEROFACTIVETHREADS = 6,
};
};
inline ThreadID *api_threadpool::ReserveThread(int flags)
{
return _call(RESERVETHREAD, (ThreadID *)0, flags);
}
inline void api_threadpool::ReleaseThread(ThreadID *thread_id)
{
_voidcall(RELEASETHREAD, thread_id);
}
inline int api_threadpool::AddHandle(ThreadID *threadid, HANDLE handle, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id, int flags)
{
return _call(ADDHANDLE, (int)1, threadid, handle, func, user_data, id, flags);
}
inline void api_threadpool::RemoveHandle(ThreadID *threadid, HANDLE handle)
{
_voidcall(REMOVEHANDLE, threadid, handle);
}
inline int api_threadpool::RunFunction(ThreadID *threadid, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id, int flags)
{
return _call(RUNFUNCTION, (int)1, threadid, func, user_data, id, flags);
}
inline size_t api_threadpool::GetNumberOfThreads()
{
return _call(GETNUMBEROFTHREADS, (size_t)0);
}
inline size_t api_threadpool::GetNumberOfActiveThreads()
{
return _call(GETNUMBEROFACTIVETHREADS, (size_t)0);
}
// {4DE015D3-11D8-4ac6-A3E6-216DF5252107}
static const GUID ThreadPoolGUID =
{ 0x4de015d3, 0x11d8, 0x4ac6, { 0xa3, 0xe6, 0x21, 0x6d, 0xf5, 0x25, 0x21, 0x7 } };

Some files were not shown because too many files have changed in this diff Show More