4#include "Stroika/Frameworks/StroikaPreComp.h"
14#include "Stroika/Foundation/Execution/Throw.h"
17#include "Stroika/Frameworks/Led/Config.h"
19#if qStroika_Foundation_Common_Platform_Windows
22#elif qStroika_FeatureSupported_XWindows
32#include "Stroika/Frameworks/Led/Support.h"
34#if qUseActiveXToOpenURLs
40using namespace Stroika::Frameworks;
41using namespace Stroika::Frameworks::Led;
43#if !qTargetPlatformSDKUseswchar_t
44SDKString Led::Led_tString2SDKString (
const Led_tString& s)
54Led_tString Led::Led_ANSIString2tString (
const string& s)
65string Led::Led_tString2ANSIString (
const Led_tString& s)
80void Led::Led_BeepNotify ()
82#if qStroika_Foundation_Common_Platform_Windows
83 ::MessageBeep (MB_OK);
84#elif qStroika_FeatureSupported_XWindows
85 if (gBeepNotifyCallBackProc !=
nullptr) {
86 (gBeepNotifyCallBackProc) ();
91#if qStroika_FeatureSupported_XWindows
92static unsigned long sLastXWindowsEventTime;
93static double sLastEventReferenceTime;
94static double Led::GetThisMachineCurTime ()
97 memset (&tv, 0,
sizeof (tv));
98 Verify (::gettimeofday (&tv,
nullptr) == 0);
99 Assert (tv.tv_usec < 1000000);
100 double t =
static_cast<double> (tv.tv_sec) + (tv.tv_usec / 1000000.0);
105#if qStroika_FeatureSupported_XWindows
110void (*Led::gBeepNotifyCallBackProc) () =
nullptr;
116void Led::SyncronizeLedXTickCount (
unsigned long xTickCount)
118 sLastEventReferenceTime = GetThisMachineCurTime ();
119 sLastXWindowsEventTime = xTickCount;
127unsigned long Led::LedTickCount2XTime (
float ledTickCount)
129 return static_cast<unsigned long> (ledTickCount * 1000.0f);
140#if qStroika_Foundation_Common_Platform_Windows
147int Led::Led_tStrniCmp (
const Led_tChar* l,
const Led_tChar* r,
size_t n)
154 if (result == strong_ordering::equal)
156 if (result == strong_ordering::less)
158 if (result == strong_ordering::greater)
164int Led::Led_tStriCmp (
const Led_tChar* l,
const Led_tChar* r)
169 if (result == strong_ordering::equal)
171 if (result == strong_ordering::less)
173 if (result == strong_ordering::greater)
185size_t Led::Led_SkrunchOutSpecialChars (Led_tChar* text,
size_t textLen, Led_tChar charToRemove)
187 size_t charsSkipped = 0;
188 Led_tChar* end = text + textLen;
189 for (Led_tChar* p = text; p + charsSkipped < end;) {
190 if (*(p + charsSkipped) == charToRemove) {
194 *p = *(p + charsSkipped);
197 return textLen - charsSkipped;
200#if qStroika_Frameworks_Led_SupportClipboard
207Led_ClipboardObjectAcquire::Led_ClipboardObjectAcquire (Led_ClipFormat clipType)
209#if qStroika_Foundation_Common_Platform_Windows
210 fOSClipHandle (nullptr)
213 fLockedData (nullptr)
215#if qStroika_Foundation_Common_Platform_Windows
218 fOSClipHandle = ::GetClipboardData (clipType);
220 DWORD errResult = ::GetLastError ();
222 if (fOSClipHandle !=
nullptr) {
223 fLockedData = ::GlobalLock (fOSClipHandle);
229#if qStroika_Foundation_Common_Platform_Windows
235VariantArrayPacker::VariantArrayPacker (VARIANT* v, VARTYPE vt,
size_t nElts)
236 : fSafeArrayVariant{v}
240 ::VariantClear (fSafeArrayVariant);
241 fSafeArrayVariant->parray = ::SafeArrayCreateVector (vt, 0,
static_cast<ULONG
> (nElts));
243 fSafeArrayVariant->vt = VT_ARRAY | vt;
247VariantArrayPacker::~VariantArrayPacker ()
251 ::SafeArrayUnaccessData (fSafeArrayVariant->parray);
254void* VariantArrayPacker::PokeAtData ()
const
264VariantArrayUnpacker::VariantArrayUnpacker (
const VARIANT& v)
265 : fSafeArray{v.parray}
267 if (not(v.vt & VT_ARRAY) or fSafeArray ==
nullptr) {
270 if ((v.vt & ~VT_ARRAY) != GetArrayElementType ()) {
274 void* useP =
nullptr;
279VariantArrayUnpacker::~VariantArrayUnpacker ()
282 ::SafeArrayUnaccessData (fSafeArray);
285const void* VariantArrayUnpacker::PeekAtData ()
const
290VARTYPE VariantArrayUnpacker::GetArrayElementType ()
const
293 VARTYPE vt = VT_EMPTY;
298size_t VariantArrayUnpacker::GetLength ()
const
309#if qStroika_Foundation_Common_Platform_Windows
320VARIANT Led::CreateSafeArrayOfBSTR (
const wchar_t*
const* strsStart,
const wchar_t*
const* strsEnd)
322 size_t nElts = strsEnd - strsStart;
324 ::VariantInit (&result);
326 const bool kEncodeAsVariant =
false;
328 if (kEncodeAsVariant) {
329 VariantArrayPacker packer (&result, VT_VARIANT, nElts);
330 VARIANT* vptr =
reinterpret_cast<VARIANT*
> (packer.PokeAtData ());
331 for (
size_t i = 0; i < nElts; ++i) {
332 ::VariantInit (&vptr[i]);
334 vptr[i].vt = VT_VARIANT;
338 VariantArrayPacker packer (&result, VT_BSTR, nElts);
339 BSTR* bptr =
reinterpret_cast<BSTR*
> (packer.PokeAtData ());
340 for (
size_t i = 0; i < nElts; ++i) {
346 ::VariantClear (&result);
352VARIANT Led::CreateSafeArrayOfBSTR (
const vector<const wchar_t*>& v)
354 return CreateSafeArrayOfBSTR (Traversal::Iterator2Pointer (v.begin ()), Traversal::Iterator2Pointer (v.end ()));
357VARIANT Led::CreateSafeArrayOfBSTR (
const vector<wstring>& v)
359 vector<const wchar_t*> tmp;
360 for (
auto i = v.begin (); i != v.end (); ++i) {
362 tmp.push_back ((*i).c_str ());
364 return CreateSafeArrayOfBSTR (tmp);
372vector<wstring> Led::UnpackVectorOfStringsFromVariantArray (
const VARIANT& v)
374 vector<wstring> result;
375 VariantArrayUnpacker up (v);
376 size_t nElts = up.GetLength ();
377 switch (up.GetArrayElementType ()) {
379 const BSTR* bArray =
reinterpret_cast<const BSTR*
> (up.PeekAtData ());
380 for (
size_t i = 0; i < nElts; ++i) {
381 result.push_back (bArray[i]);
385 const VARIANT* vArray =
reinterpret_cast<const VARIANT*
> (up.PeekAtData ());
386 for (
size_t i = 0; i < nElts; ++i) {
387 result.push_back (vArray[i].bstrVal);
391 throw (DISP_E_BADVARTYPE);
397#if qStroika_Foundation_Common_Platform_Windows
405void Led::DumpSupportedInterfaces (IUnknown* obj,
const char* objectName,
const char* levelPrefix)
408 string label = objectName ==
nullptr ?
string{} : Characters::CString::Format (
" (named '%s')", objectName);
409 string msg = Characters::CString::Format (
"Dumping interfaces for object%s at %p:\n", label.c_str (), obj);
410 ::OutputDebugStringA (msg.c_str ());
412 if (levelPrefix ==
nullptr) {
421 HKEY interfaceKey =
nullptr;
423 BSTR tmpStr =
nullptr;
424 IUnknown* test =
nullptr;
426 if (::RegOpenKeyEx (HKEY_CLASSES_ROOT, _T (
"Interface"), 0, KEY_READ, &interfaceKey) != ERROR_SUCCESS) {
427 throw GetLastError ();
429 for (DWORD i = 0;; ++i) {
430 char subKey[MAX_PATH];
431 DWORD subKeySize =
sizeof (subKey);
432 if (::RegEnumKeyExA (interfaceKey, i, subKey, &subKeySize, 0, 0, 0, 0) == ERROR_SUCCESS) {
435 (void)::memset (&iid, 0,
sizeof (iid));
436 tmpStr = ::SysAllocString (String::FromNarrowSDKString (subKey).As<wstring> ().c_str ());
438 if (tmpStr !=
nullptr) {
439 ::SysFreeString (tmpStr);
442 if (SUCCEEDED (obj->QueryInterface (iid,
reinterpret_cast<void**
> (&test)))) {
444 Assert (iKey ==
nullptr);
445 if (::RegOpenKeyExA (interfaceKey, subKey, 0, KEY_READ, &iKey) != ERROR_SUCCESS) {
446 throw GetLastError ();
449 TCHAR interfaceNameBuf[1024];
450 DWORD interfaceNameBufSize =
sizeof (interfaceNameBuf);
451 if (::RegQueryValueEx (iKey, _T(
""),
nullptr, &dwType,
reinterpret_cast<LPBYTE
> (interfaceNameBuf),
452 &interfaceNameBufSize) == ERROR_SUCCESS) {
453 OutputDebugStringA (levelPrefix);
454 OutputDebugString (interfaceNameBuf);
455 OutputDebugStringA (
"\n");
457 if (iKey !=
nullptr) {
458 ::CloseHandle (iKey);
462 if (test !=
nullptr) {
468 OutputDebugStringA (levelPrefix);
469 OutputDebugStringA (
"ERROR LOOKING UP INTERFACE - IGNORING");
470 OutputDebugStringA (
"\n");
477 if (test !=
nullptr) {
481 if (tmpStr !=
nullptr) {
482 ::SysFreeString (tmpStr);
485 if (interfaceKey !=
nullptr) {
486 ::CloseHandle (interfaceKey);
487 interfaceKey =
nullptr;
491 if (iKey !=
nullptr) {
492 ::CloseHandle (iKey);
495 if (interfaceKey !=
nullptr) {
496 ::CloseHandle (interfaceKey);
497 interfaceKey =
nullptr;
499 if (tmpStr !=
nullptr) {
500 ::SysFreeString (tmpStr);
503 if (test !=
nullptr) {
518void Led::DumpObjectsInIterator (IEnumUnknown* iter,
const char* iteratorName,
const char* levelPrefix)
521 string label = iteratorName ==
nullptr ?
string{} : Characters::CString::Format (
" (named '%s')", iteratorName);
522 string msg = Characters::CString::Format (
"Dumping objects (and their interface names) for iterator%s at %p\n", label.c_str (), iter);
523 ::OutputDebugStringA (msg.c_str ());
524 if (levelPrefix ==
nullptr) {
528 if (iter ==
nullptr) {
529 ::OutputDebugStringA (levelPrefix);
530 ::OutputDebugStringA (
"<<nullptr ENUM POINTER>>\n");
533 IUnknown* nextObj =
nullptr;
534 for (
size_t i = 0; SUCCEEDED (iter->Next (1, &nextObj,
nullptr)); ++i) {
536 (void)snprintf (nameBuf, Memory::NEltsOf (nameBuf),
"obj#%d",
static_cast<int> (i));
537 char levelPrefixBuf[1024];
538 Assert (::strlen (levelPrefix) <
sizeof (levelPrefixBuf) / 2);
539 Characters::CString::Copy (levelPrefixBuf, Memory::NEltsOf (levelPrefixBuf), levelPrefix);
540 CString::Cat (levelPrefixBuf, Memory::NEltsOf (levelPrefixBuf),
"\t");
541 DumpSupportedInterfaces (nextObj, nameBuf, levelPrefixBuf);
546 Assert (nextObj ==
nullptr);
555Led_URLD::Led_URLD (
const char* url,
const char* title)
560 size_t urlLen = ::strlen (url);
561 size_t titleLen = ::strlen (title);
563 size_t resultURLDLen = (urlLen + 1 + titleLen + 1);
564 fData.resize (resultURLDLen,
'\0');
566 char* data = &fData.front ();
567 Characters::CString::Copy (data, resultURLDLen, url);
568 CString::Cat (data, fData.size () - 1,
"\r");
569 CString::Cat (data, fData.size () - 1, title);
572Led_URLD::Led_URLD (
const void* urlData,
size_t nBytes)
576 for (
size_t i = 0; i < nBytes; i++) {
577 char c = ((
char*)urlData)[i];
584 if (fData.size () == 0 or fData.back () !=
'\0') {
585 fData.push_back (
'\0');
589size_t Led_URLD::GetURLDLength ()
const
592 return fData.size () - 1;
595size_t Led_URLD::GetURLLength ()
const
597 size_t len = fData.size ();
598 for (
size_t i = 0; i < len; i++) {
599 if (fData[i] ==
'\r') {
606size_t Led_URLD::GetTitleLength ()
const
608 size_t len = fData.size ();
609 for (
size_t i = 0; i < len; i++) {
610 if (fData[i] ==
'\r') {
611 Assert (len >= (i + 1 + 1));
612 return len - (i + 1 + 1);
618char* Led_URLD::PeekAtURLD ()
const
620 return (&
const_cast<Led_URLD*
> (
this)->fData.front ());
623char* Led_URLD::PeekAtURL ()
const
625 return (&
const_cast<Led_URLD*
> (
this)->fData.front ());
628char* Led_URLD::PeekAtTitle ()
const
630 return PeekAtURL () + GetURLLength () + 1;
633string Led_URLD::GetTitle ()
const
635 return string{PeekAtTitle (), GetTitleLength ()};
638string Led_URLD::GetURL ()
const
640 return string{PeekAtURL (), GetURLLength ()};
649 Led_URLManager* sTheURLMgr_;
651Led_URLManager& Led_URLManager::Get ()
653 if (sTheURLMgr_ ==
nullptr) {
654 sTheURLMgr_ =
new Led_URLManager ();
659void Led_URLManager::Set (Led_URLManager* newURLMgr)
661 if (sTheURLMgr_ != newURLMgr) {
663 sTheURLMgr_ = newURLMgr;
667void Led_URLManager::Open (
const string& url)
669#if qStroika_Foundation_Common_Platform_Windows
670#if qUseActiveXToOpenURLs
676string Led_URLManager::FileSpecToURL ([[maybe_unused]]
const filesystem::path& p)
682#if qUseActiveXToOpenURLs
683void Led_URLManager::Open_ActiveX (
const string& url)
687 int nWideChars = ::MultiByteToWideChar (CP_ACP, 0, url.c_str (),
static_cast<int> (url.length ()), wideURLBuf.data (),
688 static_cast<int> (url.length ()));
689 wideURLBuf[nWideChars] =
'\0';
691 IUnknown* pUnk =
nullptr;
702 static HINSTANCE urlmonLibrary = ::LoadLibrary (_T (
"urlmon"));
703 if (urlmonLibrary ==
nullptr) {
706 static HRESULT (WINAPI * hlinkNavigateString) (IUnknown*, LPCWSTR) =
707 (HRESULT (WINAPI*) (IUnknown*, LPCWSTR))::GetProcAddress (urlmonLibrary,
"HlinkNavigateString");
708 if (hlinkNavigateString ==
nullptr) {
711 HRESULT result = (*hlinkNavigateString) (pUnk, wideURLBuf.data ());
713 HRESULT result = ::HlinkNavigateString (pUnk, wideURLBuf.data ());
715 if (not SUCCEEDED (result)) {
726string Led::MakeSophistsAppNameVersionURL (
const string& relURL,
const string& appName,
const string& extraArgs)
728 Require (relURL.length () > 0 and relURL[0] ==
'/');
729 char fullVersionBuf[1024];
730 (void)snprintf (fullVersionBuf, Memory::NEltsOf (fullVersionBuf),
"%d", qLed_FullVersion);
731 string fullURL =
"http://www.sophists.com" + relURL +
"?AppName=" + appName +
732#if qStroika_Foundation_Common_Platform_Windows
733 string{
"&Platform=Windows"} +
734#elif qStroika_FeatureSupported_XWindows
735 string{
"&Platform=XWindows"} +
737 "&MajorMinorVersion=" + qLed_MajorMinorVersionString +
"&LedFullVersion=" + fullVersionBuf +
738 "&ShortVersionString=" + qLed_ShortVersionString + extraArgs;
747bool Led::Led_CasedCharsEqual (
char lhs,
char rhs,
bool ignoreCase)
752 if (ignoreCase and isascii (lhs) and isalpha (lhs) and isascii (rhs) and isalpha (rhs) and toupper (lhs) == toupper (rhs)) {
758CompileTimeFlagChecker_SOURCE (Stroika::Frameworks::Led, qStroika_Frameworks_Led_ProvideIMESupport, qStroika_Frameworks_Led_ProvideIMESupport);
#define AssertNotImplemented()
#define RequireNotNull(p)
#define AssertNotReached()
#define CompileTimeFlagChecker_SOURCE(NS_PREFIX, NAME, VALUE)
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
CodeCvt unifies byte <-> unicode conversions, vaguely inspired by (and wraps) std::codecvt,...
nonvirtual STRINGISH Bytes2String(span< const byte > from) const
nonvirtual BLOBISH String2Bytes(span< const CHAR_T > from) const
String is like std::u32string, except it is much easier to use, often much more space efficient,...
nonvirtual String SafeSubString(SZ from) const
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
basic_string< SDKChar > SDKString
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
void ThrowIfNull(const Private_::ConstVoidStar &p, const HRESULT &hr)
Template specialization for ThrowIfNull (), for thing being thrown HRESULT - really throw HRESULTErro...