mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-06-17 10:15:47 -04:00
Initial community commit
This commit is contained in:
54
Src/nu/Alias.h
Normal file
54
Src/nu/Alias.h
Normal 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> ©)
|
||||
{
|
||||
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
388
Src/nu/AudioOutput.h
Normal 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
189
Src/nu/AutoChar.h
Normal 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
50
Src/nu/AutoCharFn.h
Normal 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
98
Src/nu/AutoHeader.h
Normal 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
379
Src/nu/AutoLock.h
Normal 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 ©) { } // make copy constructor private so it can't be used
|
||||
LockGuard &operator =(const LockGuard ©) {} // 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
141
Src/nu/AutoUrl.h
Normal 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
99
Src/nu/AutoWide.h
Normal 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
191
Src/nu/AutoWideFn.h
Normal 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
45
Src/nu/CCVersion.cpp
Normal 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
6
Src/nu/CCVersion.h
Normal 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
21
Src/nu/CGlobalAtom.h
Normal 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
33
Src/nu/ChildSizer.cpp
Normal 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
38
Src/nu/ChildSizer.h
Normal 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
105
Src/nu/ComboBox.h
Normal 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
188
Src/nu/Config.h
Normal 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
373
Src/nu/ConfigCOM.cpp
Normal 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
51
Src/nu/ConfigCOM.h
Normal 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
8
Src/nu/DialogSkinner.cpp
Normal 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
78
Src/nu/DialogSkinner.h
Normal 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
|
64
Src/nu/GaplessRingBuffer.cpp
Normal file
64
Src/nu/GaplessRingBuffer.cpp
Normal 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();
|
||||
}
|
20
Src/nu/GaplessRingBuffer.h
Normal file
20
Src/nu/GaplessRingBuffer.h
Normal 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
93
Src/nu/GrowBuf.h
Normal 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
839
Src/nu/HTMLContainer.cpp
Normal 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
177
Src/nu/HTMLContainer.h
Normal 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
2108
Src/nu/HTMLContainer2.cpp
Normal file
File diff suppressed because it is too large
Load Diff
292
Src/nu/HTMLContainer2.h
Normal file
292
Src/nu/HTMLContainer2.h
Normal 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
47
Src/nu/LockFreeItem.h
Normal 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
156
Src/nu/LockFreeStack.h
Normal 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
42
Src/nu/MainThread.cpp
Normal 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
82
Src/nu/MainThread.h
Normal 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
95
Src/nu/MakeThunk.h
Normal 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
|
463
Src/nu/MediaLibraryInterface.cpp
Normal file
463
Src/nu/MediaLibraryInterface.cpp
Normal 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);
|
||||
}
|
157
Src/nu/MediaLibraryInterface.h
Normal file
157
Src/nu/MediaLibraryInterface.h
Normal 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
343
Src/nu/NonBlockLock.h
Normal 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
|
0
Src/nu/Nullsoft Utility Library.txt
Normal file
0
Src/nu/Nullsoft Utility Library.txt
Normal file
15
Src/nu/Pairs.h
Normal file
15
Src/nu/Pairs.h
Normal 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
125
Src/nu/ProgressTracker.h
Normal 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
68
Src/nu/ReEntry.h
Normal 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
640
Src/nu/RedBlackTree.cpp
Normal 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
77
Src/nu/RedBlackTree.h
Normal 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
461
Src/nu/RingBuffer.cpp
Normal 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
77
Src/nu/RingBuffer.h
Normal 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
81
Src/nu/SampleQueue.h
Normal 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
106
Src/nu/SendTo.h
Normal 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
40
Src/nu/ServiceBuilder.h
Normal 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
183
Src/nu/ServiceWatcher.cpp
Normal 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
60
Src/nu/ServiceWatcher.h
Normal 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
86
Src/nu/Singleton.h
Normal 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
35
Src/nu/Slider.h
Normal 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
105
Src/nu/SpillBuffer.cpp
Normal 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
23
Src/nu/SpillBuffer.h
Normal 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
66
Src/nu/ThreadQueue.cpp
Normal 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
23
Src/nu/ThreadQueue.h
Normal 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
221
Src/nu/Vectors.h
Normal 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> ©)
|
||||
{
|
||||
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> ©)
|
||||
{
|
||||
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
63
Src/nu/VideoClock.h
Normal 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
71
Src/nu/bitbuffer.cpp
Normal 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
26
Src/nu/bitbuffer.h
Normal 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
41
Src/nu/cast64.h
Normal 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
15
Src/nu/comboskin.cpp
Normal 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
15
Src/nu/comboskin.h
Normal 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
59
Src/nu/cstrlib.cpp
Normal 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
10
Src/nu/cstrlib.h
Normal 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
55
Src/nu/dispatchTable.h
Normal 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
77
Src/nu/factoryt.h
Normal 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
285
Src/nu/listview.cpp
Normal 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
229
Src/nu/listview.h
Normal 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
132
Src/nu/menuHelpers.cpp
Normal 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
18
Src/nu/menuHelpers.h
Normal 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
204
Src/nu/menushortcuts.cpp
Normal 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
20
Src/nu/menushortcuts.h
Normal 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
611
Src/nu/mtbrowser.cpp
Normal 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)¶m, 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
63
Src/nu/mtbrowser.h
Normal 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
28
Src/nu/noarg.c
Normal 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
26
Src/nu/nochkclr.c
Normal 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
27
Src/nu/noenv.c
Normal 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
17
Src/nu/nomsgs.c
Normal 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
18
Src/nu/nonewthrow.c
Normal 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
12
Src/nu/nosec.c
Normal 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
67
Src/nu/ns_wc.h
Normal 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
21
Src/nu/refcount.h
Normal 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
233
Src/nu/regexp.cpp
Normal 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
14
Src/nu/regexp.h
Normal 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
49
Src/nu/simple_rwlock.h
Normal 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
182
Src/nu/smalheap.c
Normal 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
361
Src/nu/sort.cpp
Normal 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
11
Src/nu/sort.h
Normal 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
2
Src/nu/strsafe.c
Normal file
@ -0,0 +1,2 @@
|
||||
#define STRSAFE_LIB_IMPL
|
||||
#include "strsafe.h"
|
6647
Src/nu/strsafe.h
Normal file
6647
Src/nu/strsafe.h
Normal file
File diff suppressed because it is too large
Load Diff
23
Src/nu/strsafe.sln
Normal file
23
Src/nu/strsafe.sln
Normal 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
150
Src/nu/strsafe.vcproj
Normal 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
36
Src/nu/threadname.h
Normal 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
|
79
Src/nu/threadpool/ThreadFunctions.cpp
Normal file
79
Src/nu/threadpool/ThreadFunctions.cpp
Normal 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;
|
||||
}
|
||||
}
|
31
Src/nu/threadpool/ThreadFunctions.h
Normal file
31
Src/nu/threadpool/ThreadFunctions.h
Normal 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;
|
||||
};
|
274
Src/nu/threadpool/ThreadID.cpp
Normal file
274
Src/nu/threadpool/ThreadID.cpp
Normal 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;
|
||||
}
|
56
Src/nu/threadpool/ThreadID.h
Normal file
56
Src/nu/threadpool/ThreadID.h
Normal 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;
|
||||
|
||||
};
|
365
Src/nu/threadpool/ThreadPool.cpp
Normal file
365
Src/nu/threadpool/ThreadPool.cpp
Normal 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
|
98
Src/nu/threadpool/ThreadPool.h
Normal file
98
Src/nu/threadpool/ThreadPool.h
Normal 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;
|
||||
};
|
||||
|
54
Src/nu/threadpool/TimerHandle.hpp
Normal file
54
Src/nu/threadpool/TimerHandle.hpp
Normal 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
|
92
Src/nu/threadpool/api_threadpool.h
Normal file
92
Src/nu/threadpool/api_threadpool.h
Normal 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
Reference in New Issue
Block a user