diff --git a/SpoutDX/source/SpoutCommon.h b/SpoutDX/source/SpoutCommon.h index 20dfd07..1cb3142 100644 --- a/SpoutDX/source/SpoutCommon.h +++ b/SpoutDX/source/SpoutCommon.h @@ -12,7 +12,7 @@ Thanks and credit to Malcolm Bechard, the author of this file https://github.com/mbechard - Copyright (c) 2014-2023, Lynn Jarvis. All rights reserved. + Copyright (c) 2014-2024, Lynn Jarvis. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -82,5 +82,18 @@ #pragma warning(disable:26812) // unscoped enums #endif +// +// For ARM build +// __movsd intrinsic not defined +// +#if defined _M_ARM64 +#include +inline void __movsd(unsigned long* Destination, + const unsigned long* Source, size_t Count) +{ + memcpy(Destination, Source, Count); +} +#endif + #endif diff --git a/SpoutDX/source/SpoutCopy.cpp b/SpoutDX/source/SpoutCopy.cpp index d11a911..83ce934 100644 --- a/SpoutDX/source/SpoutCopy.cpp +++ b/SpoutDX/source/SpoutCopy.cpp @@ -4,7 +4,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Copyright (c) 2016-2023, Lynn Jarvis. All rights reserved. + Copyright (c) 2016-2024, Lynn Jarvis. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -69,6 +69,8 @@ Remove rgba_to_bgr_sse3 Add experimental rgb_to_bgra_sse3 Version 2.007.012 + 07.10.23 - Conditional compile options for _M_ARM64 in CheckSSE and header + 20.10.23 - FlipBuffer / CopyPixels - default pitch width*4 */ @@ -101,10 +103,9 @@ void spoutCopy::CopyPixels(const unsigned char *source, unsigned char *dest, unsigned int width, unsigned int height, GLenum glFormat, bool bInvert) const { - unsigned int Size = width*height; // GL_LUMINANCE default - - if (glFormat == GL_RGBA || glFormat == GL_BGRA_EXT) - Size = width * height * 4; + unsigned int Size = width*height*4; // RGBA default + if (glFormat == GL_LUMINANCE) + Size = width*height; else if (glFormat == GL_RGB || glFormat == GL_BGR_EXT) Size = width*height * 3; @@ -141,11 +142,11 @@ void spoutCopy::FlipBuffer(const unsigned char *src, unsigned int height, GLenum glFormat) const { - unsigned int pitch = width; // GL_LUMINANCE default - if (glFormat == GL_RGBA || glFormat == GL_BGRA_EXT) - pitch = width * 4; // RGBA format specified + unsigned int pitch = width*4; // RGBA default + if (glFormat == GL_LUMINANCE) + pitch = width; // Luminance data else if (glFormat == GL_RGB || glFormat == GL_BGR_EXT) - pitch = width * 3; // RGB format specified + pitch = width * 3; // RGB format specified (RGB float not supported) unsigned int line_s = 0; unsigned int line_t = (height - 1)*pitch; @@ -1459,6 +1460,11 @@ void spoutCopy::bgra2bgr(const void *bgra_source, void *bgr_dest, unsigned int w // void spoutCopy::CheckSSE() { +#ifdef _M_ARM64 // All SSE will be routed to NEON + m_bSSE2 = true; + m_bSSE3 = true; + m_bSSSE3 = true; +#else // An array of four integers that contains the information returned // in EAX (0), EBX (1), ECX (2), and EDX (3) about supported features of the CPU. int CPUInfo[4] ={-1, -1, -1, -1}; @@ -1480,6 +1486,7 @@ void spoutCopy::CheckSSE() // SSSE3 = (cpuid02 & (0x1 << 9) m_bSSSE3 = ((CPUInfo[2] & (0x1 << 9)) || false); } +#endif } diff --git a/SpoutDX/source/SpoutCopy.h b/SpoutDX/source/SpoutCopy.h index 6bedb54..296adaa 100644 --- a/SpoutDX/source/SpoutCopy.h +++ b/SpoutDX/source/SpoutCopy.h @@ -6,7 +6,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Copyright (c) 2016-2023, Lynn Jarvis. All rights reserved. + Copyright (c) 2016-2024, Lynn Jarvis. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -38,8 +38,12 @@ #include // for debug printf #include // For OpenGL definitions #include // for cpuid to test for SSE2 +#ifdef _M_ARM64 +#include // for NEON +#else #include // for SSE2 #include // for SSSE3 +#endif #include // For compatibility with Clang. PR#81 #include // for _uint32 etc diff --git a/SpoutDX/source/SpoutDirectX.cpp b/SpoutDX/source/SpoutDirectX.cpp index 6e618f4..f4778d5 100644 --- a/SpoutDX/source/SpoutDirectX.cpp +++ b/SpoutDX/source/SpoutDirectX.cpp @@ -156,11 +156,12 @@ // ReleaseDX11Device - release ID3D11Device1 and ID3D11DeviceContext1 if created // Version 2.007.012 // 07.08.23 - Comment out code for debug layers +// 19.10.23 - GetNumAdapters - remove unused adapter description and output list // // ==================================================================================== /* - Copyright (c) 2014-2023. Lynn Jarvis. All rights reserved. + Copyright (c) 2014-2024. Lynn Jarvis. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -555,7 +556,7 @@ bool spoutDirectX::CreateSharedDX11Texture(ID3D11Device* pd3dDevice, } SpoutLogNotice("spoutDirectX::CreateSharedDX11Texture"); - SpoutLogNotice(" pDevice = 0x%.7X, width = %d, height = %d, format = %d", PtrToUint(pd3dDevice), width, height, format); + SpoutLogNotice(" pDevice = 0x%.7X, width = %d, height = %d, format = 0x%X (%d)", PtrToUint(pd3dDevice), width, height, format, format); // Use the format passed in // If that is zero or DX9 format, use the default format @@ -606,7 +607,6 @@ bool spoutDirectX::CreateSharedDX11Texture(ID3D11Device* pd3dDevice, desc.MipLevels = 1; desc.ArraySize = 1; - const HRESULT res = pd3dDevice->CreateTexture2D(&desc, NULL, ppSharedTexture); if (FAILED(res)) { const long long lres = static_cast((LOWORD(res))); @@ -671,7 +671,8 @@ bool spoutDirectX::CreateSharedDX11Texture(ID3D11Device* pd3dDevice, pOtherResource = nullptr; pTexture = nullptr; - SpoutLogNotice(" pTexture = [0x%8.8X] : dxShareHandle = [0x%8.8X]", PtrToUint(*ppSharedTexture), LOWORD(dxShareHandle) ); + SpoutLogNotice(" pTexture [0x%8.8X] (%dx%d format 0x%X) : dxShareHandle = [0x%8.8X]", + PtrToUint(*ppSharedTexture), width, height, texformat, LOWORD(dxShareHandle) ); return true; @@ -1077,6 +1078,9 @@ int spoutDirectX::GetNumAdapters() for (i = 0; _dxgi_factory1->EnumAdapters( i, &adapter1_ptr ) != DXGI_ERROR_NOT_FOUND; i++ ) { if (!adapter1_ptr) break; + /* + // Adapter description + // Currently not used DXGI_ADAPTER_DESC desc; adapter1_ptr->GetDesc( &desc ); // printf(" Adapter(%d) : %S\n", i, desc.Description ); @@ -1089,31 +1093,30 @@ int spoutDirectX::GetNumAdapters() // Look for outputs IDXGIOutput* p_output = nullptr; + // Is there a first output on this adapter ? - if (adapter1_ptr->EnumOutputs(0, &p_output) == DXGI_ERROR_NOT_FOUND) { - if (!p_output) break; - if (p_output == 0) { - // Warning ? - // SpoutLogWarning("spoutDirectX::GetNumAdapters - No outputs for Adapter %d : %S", i, desc.Description); - } - else { - // Here we can list all the outputs of the adapter - DXGI_OUTPUT_DESC desc_out={}; - for (UINT32 j = 0; adapter1_ptr->EnumOutputs(j, &p_output) != DXGI_ERROR_NOT_FOUND; j++) { - p_output->GetDesc(&desc_out); - // printf(" Output : %d\n", j ); - // printf(" Name %S\n", desc_out.DeviceName ); - // HMONITOR hMon = desc_out.Monitor; - // printf(" Attached to desktop : (%d) %s\n", desc_out.AttachedToDesktop, desc_out.AttachedToDesktop ? "yes" : "no" ); - // printf(" Rotation : %d\n", desc_out.Rotation ); - // printf(" Left : %d\n", desc_out.DesktopCoordinates.left ); - // printf(" Top : %d\n", desc_out.DesktopCoordinates.top ); - // printf(" Right : %d\n", desc_out.DesktopCoordinates.right ); - // printf(" Bottom : %d\n", desc_out.DesktopCoordinates.bottom ); - p_output->Release(); - } + if (adapter1_ptr->EnumOutputs(0, &p_output) != DXGI_ERROR_NOT_FOUND) { + p_output->Release(); + // Here we can list all the outputs of the adapter + DXGI_OUTPUT_DESC desc_out={}; + for (UINT32 j = 0; adapter1_ptr->EnumOutputs(j, &p_output) != DXGI_ERROR_NOT_FOUND; j++) { + p_output->GetDesc(&desc_out); + // printf(" Output : %d\n", j ); + // printf(" Name %S\n", desc_out.DeviceName ); + // HMONITOR hMon = desc_out.Monitor; + // printf(" Attached to desktop : (%d) %s\n", desc_out.AttachedToDesktop, desc_out.AttachedToDesktop ? "yes" : "no" ); + // printf(" Rotation : %d\n", desc_out.Rotation ); + // printf(" Left : %d\n", desc_out.DesktopCoordinates.left ); + // printf(" Top : %d\n", desc_out.DesktopCoordinates.top ); + // printf(" Right : %d\n", desc_out.DesktopCoordinates.right ); + // printf(" Bottom : %d\n", desc_out.DesktopCoordinates.bottom ); + p_output->Release(); } } + else { + SpoutLogWarning("spoutDirectX::GetNumAdapters - No outputs for Adapter %d : %S", i, desc.Description); + } + */ adapter1_ptr->Release(); } diff --git a/SpoutDX/source/SpoutDirectX.h b/SpoutDX/source/SpoutDirectX.h index b7131b9..ccf1b5f 100644 --- a/SpoutDX/source/SpoutDirectX.h +++ b/SpoutDX/source/SpoutDirectX.h @@ -4,7 +4,7 @@ Functions to manage DirectX 11 texture sharing - Copyright (c) 2014 - 2023, Lynn Jarvis. All rights reserved. + Copyright (c) 2014 - 2024, Lynn Jarvis. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/SpoutDX/source/SpoutFrameCount.cpp b/SpoutDX/source/SpoutFrameCount.cpp index 9cd1c4c..0f530cf 100644 --- a/SpoutDX/source/SpoutFrameCount.cpp +++ b/SpoutDX/source/SpoutFrameCount.cpp @@ -89,7 +89,7 @@ // ==================================================================================== // /* - Copyright (c) 2019-2023. Lynn Jarvis. All rights reserved. + Copyright (c) 2019-2024. Lynn Jarvis. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/SpoutDX/source/SpoutFrameCount.h b/SpoutDX/source/SpoutFrameCount.h index 6c2b7b4..d5ea289 100644 --- a/SpoutDX/source/SpoutFrameCount.h +++ b/SpoutDX/source/SpoutFrameCount.h @@ -4,7 +4,7 @@ Frame counting management - Copyright (c) 2019-2023. Lynn Jarvis. All rights reserved. + Copyright (c) 2019-2024. Lynn Jarvis. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/SpoutDX/source/SpoutSenderNames.cpp b/SpoutDX/source/SpoutSenderNames.cpp index 161c4e9..4e45bd0 100644 --- a/SpoutDX/source/SpoutSenderNames.cpp +++ b/SpoutDX/source/SpoutSenderNames.cpp @@ -97,9 +97,12 @@ Version 2.007.11 13.07.23 - setActiveSenderName - close any existing active sender map Version 2.007.012 + 07.10.23 - Conditional compile options for _M_ARM64 + Moved additonal includes from cpp to header + 28.10.23 - SetSenderInfo - use QueryFullProcessImageNameA - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Copyright (c) 2014-2023, Lynn Jarvis. All rights reserved. + Copyright (c) 2014-2024, Lynn Jarvis. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -530,7 +533,7 @@ bool spoutSenderNames::GetSenderInfo(const char* sendername, unsigned int &width if(getSharedInfo(sendername, &info)) { width = (unsigned int)info.width; height = (unsigned int)info.height; -#ifdef _M_X64 +#if defined _M_X64 || defined _M_ARM64 dxShareHandle = (HANDLE)(LongToHandle((long)info.shareHandle)); #else dxShareHandle = (HANDLE)info.shareHandle; @@ -577,21 +580,35 @@ bool spoutSenderNames::SetSenderInfo(const char* sendername, unsigned int width, #endif info.format = (uint32_t)dwFormat; - // - // Initialize unused variables - // - - // Texture usage + // Texture usage - unused info.usage = 0; - // Description : Host path - char exepath[256]={}; - GetModuleFileNameA(NULL, &exepath[0], sizeof(exepath)); - // Partner ID : Sender CPU sharing mode // Set by SetSenderID // TODO : combine here + // Description : Host path + // Description field is 256 uint8_t, initialize with zeros + char exepath[256]={0}; + + // Get the full path of the current process + // GetModuleFileNameA(NULL, &exepath[0], sizeof(exepath)); + + // GetModuleFileNameA could fail for Windows on Arm systems + // Use QueryFullProcessImageNameA instead + DWORD dwProcId = GetCurrentProcessId(); + HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcId); + if (hProc) { + DWORD bufferSize = 256; + if (!QueryFullProcessImageNameA(hProc, 0, exepath, &bufferSize)) { + SpoutLogWarning("spoutSenderNames::SetSenderInfo - QueryFullProcessImageName failed"); + } + CloseHandle(hProc); + } + else { + SpoutLogWarning("spoutSenderNames::SetSenderInfo - could not get process handle"); + } + // Description is defined as wide chars, but the path is stored as byte chars memcpy(&info.description[0], &exepath[0], 256); // wchar 128 @@ -759,7 +776,7 @@ bool spoutSenderNames::FindActiveSender(char * sendername, unsigned int& theWidt strcpy_s(sendername, maxlength, &sname[0]); // pass back sender name theWidth = (unsigned int)TextureInfo.width; theHeight = (unsigned int)TextureInfo.height; -#ifdef _M_X64 +#if defined _M_X64 || defined _M_ARM64 hSharehandle = (HANDLE)(LongToHandle((long)TextureInfo.shareHandle)); #else hSharehandle = (HANDLE)TextureInfo.shareHandle; @@ -868,7 +885,7 @@ bool spoutSenderNames::CheckSender(const char *sendername, unsigned int &theWidt // Return the texture info theWidth = (unsigned int)info.width; theHeight = (unsigned int)info.height; -#ifdef _M_X64 +#if defined _M_X64 || defined _M_ARM64 hSharehandle = (HANDLE)(LongToHandle((long)info.shareHandle)); #else hSharehandle = (HANDLE)info.shareHandle; @@ -923,7 +940,7 @@ bool spoutSenderNames::FindSender(char *sendername, unsigned int &width, unsigne if (getSharedInfo(sendername, &info)) { width = (unsigned int)info.width; // pass back sender size height = (unsigned int)info.height; -#ifdef _M_X64 +#if defined _M_X64 || defined _M_ARM64 hSharehandle = (HANDLE)(LongToHandle((long)info.shareHandle)); #else hSharehandle = (HANDLE)info.shareHandle; diff --git a/SpoutDX/source/SpoutSenderNames.h b/SpoutDX/source/SpoutSenderNames.h index 5abddda..ae50bd3 100644 --- a/SpoutDX/source/SpoutSenderNames.h +++ b/SpoutDX/source/SpoutSenderNames.h @@ -9,7 +9,7 @@ https://github.com/mbechard - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Copyright (c) 2014-2023, Lynn Jarvis. All rights reserved. + Copyright (c) 2014-2024, Lynn Jarvis. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -47,6 +47,10 @@ #include #include // for __movsd #include // for _uint32 +#include +#ifdef _M_ARM64 +#include // For ARM +#endif #include "SpoutCommon.h" #include "SpoutSharedMemory.h" diff --git a/SpoutDX/source/SpoutSharedMemory.cpp b/SpoutDX/source/SpoutSharedMemory.cpp index eb30e96..4154300 100644 --- a/SpoutDX/source/SpoutSharedMemory.cpp +++ b/SpoutDX/source/SpoutSharedMemory.cpp @@ -10,7 +10,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Copyright (c) 2014-2023, Lynn Jarvis. All rights reserved. + Copyright (c) 2014-2024, Lynn Jarvis. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -47,9 +47,7 @@ // 28.10.22 - Code documentation // 18.12.22 - Catch any exception from using Close in destructor // 07.01.23 - Change m_pName from const char* to char* for strdup -// Version 2.007.11 // 12.05.23 - Create and Open - Clear ERROR_ALREADY_EXISTS to avoid detection elsewhere. -// Version 2.007.012 // ==================================================================================== diff --git a/SpoutDX/source/SpoutSharedMemory.h b/SpoutDX/source/SpoutSharedMemory.h index dca7623..c3728f0 100644 --- a/SpoutDX/source/SpoutSharedMemory.h +++ b/SpoutDX/source/SpoutSharedMemory.h @@ -7,7 +7,7 @@ https://github.com/mbechard - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Copyright (c) 2014-2023, Lynn Jarvis. All rights reserved. + Copyright (c) 2014-2024, Lynn Jarvis. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/SpoutDX/source/SpoutUtils.cpp b/SpoutDX/source/SpoutUtils.cpp index 3e96920..723c8b4 100644 --- a/SpoutDX/source/SpoutUtils.cpp +++ b/SpoutDX/source/SpoutUtils.cpp @@ -9,7 +9,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Copyright (c) 2017-2023, Lynn Jarvis. All rights reserved. + Copyright (c) 2017-2024, Lynn Jarvis. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -142,6 +142,17 @@ 04.09.23 - MessageTaskDialog - add MB_ICONINFORMATION option. Default no icon. Add MB_ICONSTOP and MB_ICONHAND. MB_TOPMOST flag removal only if specified. 05.09.23 - Add SpoutMessageBoxIcon for custom icon + 07.09.23 - Add round to ElapsedMicroseconds + 08.09.23 - Check TDN_CREATED for taskdialog topmost + 11.09.23 - MessageTaskDialog - correct button and icon type detection + - Allow for NULL caption + 12.09.23 - Add SpoutMessageBox overload including main instruction large text + - Correct missing SPOUT_DLLEXP for SpoutMessageBox standard function + 12.10.23 - Add SpoutMessageBoxButton and CopyToClipBoard + 20.10.23 - Add OpenSpoutLogs to open Spout log folder in Windows explorer + 20-11-23 - OpenSpoutLogs() - allow for getenv if not Microsoft compile (PR #105) + 22-11-23 - Remove unused buffer length argument from _dupenv_s + 01.12.23 - Update Version to "2.007.013" */ @@ -188,7 +199,7 @@ namespace spoututils { // Spout SDK version number string // Major, minor, release - std::string SDKversion = "2.007.012"; + std::string SDKversion = "2.007.013"; // // Group: Information @@ -827,7 +838,7 @@ namespace spoututils { // Messagebox with standard arguments and optional timeout // // Replaces an existing MessageBox call. - int SpoutMessageBox(HWND hwnd, LPCSTR message, LPCSTR caption, UINT uType, DWORD dwMilliseconds) + int SPOUT_DLLEXP SpoutMessageBox(HWND hwnd, LPCSTR message, LPCSTR caption, UINT uType, DWORD dwMilliseconds) { // hwnd no longer used with taskdialog // Quiet unreferenced parameter @@ -835,9 +846,29 @@ namespace spoututils { return MessageTaskDialog(NULL, message, caption, uType, dwMilliseconds); } + // --------------------------------------------------------- + // Function: SpoutMessageBox + // + // MessageBox dialog with standard arguments + // including taskdialog main instruction large text + int SPOUT_DLLEXP SpoutMessageBox(HWND hwnd, LPCSTR message, LPCSTR caption, UINT uType, const char* instruction, DWORD dwMilliseconds) + { + hwnd = hwnd; + + // Set global main instruction + int size_needed = MultiByteToWideChar(CP_UTF8, 0, instruction, (int)strlen(instruction), NULL, 0); + wstrInstruction.resize(size_needed); + MultiByteToWideChar(CP_UTF8, 0, instruction, (int)strlen(instruction), &wstrInstruction[0], size_needed); + + return MessageTaskDialog(NULL, message, caption, uType, dwMilliseconds); + } + + + // --------------------------------------------------------- // Function: SpoutMessageBoxIcon // Custom icon for SpoutMessageBox from resources + // Use together with MB_USERICON void SPOUT_DLLEXP SpoutMessageBoxIcon(HICON hIcon) { hTaskIcon = hIcon; @@ -846,12 +877,86 @@ namespace spoututils { // --------------------------------------------------------- // Function: SpoutMessageBoxIcon // Custom icon for SpoutMessageBox from file + // Use together with MB_USERICON bool SPOUT_DLLEXP SpoutMessageBoxIcon(std::string iconfile) { hTaskIcon = reinterpret_cast(LoadImageA(nullptr, iconfile.c_str(), IMAGE_ICON, 0, 0, LR_LOADFROMFILE)); return (hTaskIcon); } + // --------------------------------------------------------- + // Function: SpoutMessageBoxButton + // Custom button for SpoutMessageBox + // Use together with MB_USERBUTTON + void SPOUT_DLLEXP SpoutMessageBoxButton(int ID, std::wstring title) + { + TDbuttonID.push_back(ID); + TDbuttonTitle.push_back(title); + } + + + // --------------------------------------------------------- + // Function: CopyToClipBoard + // Copy text to the clipboard + bool SPOUT_DLLEXP CopyToClipBoard(HWND hwnd, const char* caps) + { + HGLOBAL clipbuffer = NULL; + char* buffer = nullptr; + bool bret = false; + + if (caps[0] && strlen(caps) > 16) { + if (OpenClipboard(hwnd)) { + EmptyClipboard(); + size_t len = (strlen(caps)+1)*sizeof(char); + // Use GMEM_MOVEABLE instead of GMEM_DDESHARE to avoid crash on repeat + // GlobalUnlock then returns false but ignore + clipbuffer = GlobalAlloc(GMEM_MOVEABLE, len); // No crash but GlobalUnlock fails + if (clipbuffer) { + buffer = (char*)GlobalLock(clipbuffer); + if (buffer) { + memcpy((void*)buffer, (void*)caps, len); + SetClipboardData(CF_TEXT, clipbuffer); + bret = true; + } + GlobalUnlock(clipbuffer); + GlobalFree(clipbuffer); + clipbuffer = nullptr; + buffer = nullptr; + } + CloseClipboard(); + } + } + return bret; + } + + // --------------------------------------------------------- + // Function: OpenSpoutLogs + // Open Spout log folder in Windows explorer + bool SPOUT_DLLEXP OpenSpoutLogs() + { + char* appdatapath = nullptr; + errno_t err = 0; + std::string logfolder; +#if defined(_MSC_VER) + err = _dupenv_s(&appdatapath, NULL, "APPDATA"); +#else + appdatapath = getenv("APPDATA"); +#endif + if (err == 0 && appdatapath) { + logfolder = appdatapath; + logfolder += "\\Spout"; + if (_access(logfolder.c_str(), 0) != -1) { + // Open log folder in explorer + ShellExecuteA(NULL, "open", logfolder.c_str(), NULL, NULL, SW_SHOWNORMAL); + do {} while (!FindWindowA("CabinetWClass", NULL)); + } + } + else { + SpoutMessageBox(NULL, "Could not create AppData path", "OpenSpoutLogs", MB_OK | MB_TOPMOST | MB_ICONWARNING); + return false; + } + return true; + } // // Group: Registry utilities @@ -1148,9 +1253,8 @@ namespace spoututils { { const std::chrono::system_clock::time_point timenow = std::chrono::system_clock::now(); const std::chrono::system_clock::duration duration = timenow.time_since_epoch(); - double timestamp = 0; - timestamp = static_cast(duration.count()); // nsec/100 - duration period is 100 nanoseconds - return timestamp / 10.0; // microseconds + double timestamp = static_cast(duration.count()); // nsec/100 - duration period is 100 nanoseconds + return round(timestamp / 10.0); // microseconds } #else // Start timing period @@ -1267,11 +1371,10 @@ namespace spoututils { // Retrieve user %appdata% environment variable char *appdatapath = nullptr; - size_t len = 0; bool bSuccess = true; errno_t err = 0; #if defined(_MSC_VER) - err = _dupenv_s(&appdatapath, &len, "APPDATA"); + err = _dupenv_s(&appdatapath, NULL, "APPDATA"); #else appdatapath = getenv("APPDATA"); #endif @@ -1524,22 +1627,24 @@ namespace spoututils { // MessageBox replacement + // // https://learn.microsoft.com/en-us/windows/win32/api/commctrl/ns-commctrl-taskdialogconfig int MessageTaskDialog(HINSTANCE hInst, const char* content, const char* caption, DWORD dwButtons, DWORD dwMilliseconds) { + // User buttons + TASKDIALOG_BUTTON buttons[10]={0}; + // Use a wide string to avoid a pre-sized buffer int size_needed = MultiByteToWideChar(CP_UTF8, 0, content, (int)strlen(content), NULL, 0); std::wstring wstrTemp(size_needed, 0); MultiByteToWideChar(CP_UTF8, 0, content, (int)strlen(content), &wstrTemp[0], size_needed); - // Caption - size_needed = MultiByteToWideChar(CP_UTF8, 0, caption, (int)strlen(caption), NULL, 0); - std::wstring wstrCaption(size_needed, 0); + // Caption (default caption is the executable name) + std::wstring wstrCaption; if (caption) { + size_needed = MultiByteToWideChar(CP_UTF8, 0, caption, (int)strlen(caption), NULL, 0); + wstrCaption.resize(size_needed); MultiByteToWideChar(CP_UTF8, 0, caption, (int)strlen(caption), &wstrCaption[0], size_needed); } - else { - wstrCaption = L" "; - } // Hyperlinks can be included in the content using HTML format. // For example : @@ -1552,63 +1657,95 @@ namespace spoututils { if(bTopMost) dwl = dwl ^ MB_TOPMOST; - // https://learn.microsoft.com/en-us/windows/win32/api/commctrl/nf-commctrl-taskdialog - // TDCBF_OK_BUTTON 1 - // TDCBF_YES_BUTTON 2 - // TDCBF_NO_BUTTON 4 - // TDCBF_CANCEL_BUTTON 8 + // + // Buttons + // + DWORD dwb = dwl & 0x0F; // buttons code DWORD dwCommonButtons = MB_OK; - if ((dwl ^ MB_OK) == 0) - dwCommonButtons = MB_OK; - else if ((dwl ^ MB_OKCANCEL) == 0) - dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON; - else if ((dwl ^ MB_YESNOCANCEL) == 0) - dwCommonButtons = TDCBF_YES_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON; - else if ((dwl ^ MB_YESNO) == 0) - dwCommonButtons = TDCBF_YES_BUTTON | TDCBF_NO_BUTTON; - else - dwCommonButtons = MB_OK; + if (dwb == MB_USERBUTTON) { // 7 - SpoutSettings defined + // + // User buttons + // + if (TDbuttonID.size() > 0) { + int i = 0; + for (i=0; i<(int)TDbuttonID.size(); i++) { + buttons[i].nButtonID = TDbuttonID[i]; + buttons[i].pszButtonText = TDbuttonTitle[i].c_str(); + } + // Final button is OK + buttons[i].nButtonID = IDOK; + buttons[i].pszButtonText = L"OK"; + } + } + else { + // + // Common buttons + // + // https://learn.microsoft.com/en-us/windows/win32/api/commctrl/nf-commctrl-taskdialog + // TDCBF_OK_BUTTON 1 + // TDCBF_YES_BUTTON 2 + // TDCBF_NO_BUTTON 4 + // TDCBF_CANCEL_BUTTON 8 + // MB_OK 0x00 + // MB_OKCANCEL 0x01 + // MB_YESNOCANCEL 0x03 + // MB_YESNO 0x04 + if (dwb == MB_YESNO) { // 4 + dwCommonButtons = TDCBF_YES_BUTTON | TDCBF_NO_BUTTON; + } + else if (dwb == MB_YESNOCANCEL) { // 3 + dwCommonButtons = TDCBF_YES_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON; + } + else if (dwb == MB_OKCANCEL) { // 1 + dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON; + } + else { + dwCommonButtons = MB_OK; + } + } + // + // Icons + // // Icons available // TD_WARNING_ICON, TD_ERROR_ICON, TD_INFORMATION_ICON, TD_SHIELD_ICON // // Icons to allow for - // MB_ICONEXCLAMATION - // MB_ICONWARNING - // MB_ICONINFORMATION - // MB_ICONASTERISK - // MB_ICONQUESTION - // MB_ICONSTOP - // MB_ICONERROR - // MB_ICONHAND + // MB_ICONSTOP 0x10 + // MB_ICONERROR 0x10 + // MB_ICONHAND 0x10 + // MB_ICONQUESTION 0x20 + // MB_ICONEXCLAMATION 0x30 + // MB_ICONWARNING 0x30 + // MB_ICONINFORMATION 0x40 + // MB_ICONASTERISK 0x40 + // MB_USERICON 0x80 + HICON hMainIcon = NULL; // No user icon WCHAR* wMainIcon = nullptr; // No resource icon - const WCHAR* wMainInstruction = nullptr; // No instruction - if ((dwl ^ MB_ICONERROR) == 0) { - wMainIcon = TD_ERROR_ICON; - wMainInstruction = L"Error"; - } - else if ((dwl ^ MB_ICONSTOP) == 0 || (dwl ^ MB_ICONHAND) == 0) { - wMainIcon = TD_ERROR_ICON; - wMainInstruction = L"Stop"; - } - else if ((dwl ^ MB_ICONWARNING) == 0 || (dwl ^ MB_ICONEXCLAMATION) == 0) { - wMainIcon = TD_WARNING_ICON; - wMainInstruction = L"Warning"; - } - else if ((dwl ^ MB_YESNOCANCEL) == 0 || (dwl ^ MB_YESNO) == 0 || (dwl ^ MB_ICONQUESTION) == 0) { - wMainIcon = TD_INFORMATION_ICON; - wMainInstruction = L"Question"; - } - else if ((dwl ^ MB_ICONINFORMATION) == 0) { - wMainIcon = TD_INFORMATION_ICON; - wMainInstruction = L"Information"; - } - else if ((dwl ^ MB_USERICON) == 0) { - // Private SpoutUtils icon handle set by SpoutMessageBoxIcon - hMainIcon = hTaskIcon; - wMainIcon = nullptr; + dwl = dwl & 0xF0; // remove buttons for icons + switch(dwl) { + case MB_USERICON: // 0x80 + // Private SpoutUtils icon handle set by SpoutMessageBoxIcon + hMainIcon = hTaskIcon; + wMainIcon = nullptr; + break; + case MB_ICONINFORMATION: // 0x40 + wMainIcon = TD_INFORMATION_ICON; + break; + case MB_ICONWARNING: // 0x30 + wMainIcon = TD_WARNING_ICON; + break; + case MB_ICONQUESTION: // 0x20 + wMainIcon = TD_INFORMATION_ICON; + break; + case MB_ICONERROR: // 0x10 + wMainIcon = TD_ERROR_ICON; + break; + default: + wMainIcon = nullptr; + break; } int nButtonPressed = 0; @@ -1621,9 +1758,20 @@ namespace spoututils { config.hMainIcon = hMainIcon; if (!hMainIcon) config.pszMainIcon = wMainIcon; // Important to remove this - config.pszMainInstruction = wMainInstruction; + config.pszMainInstruction = wstrInstruction.c_str(); config.pszContent = wstrTemp.c_str(); - config.dwCommonButtons = dwCommonButtons; + + // User buttons in TASKDIALOG_BUTTON buttons + // Otherwise use common buttons + config.nDefaultButton = IDOK; + if (dwb == MB_USERBUTTON) { + config.pButtons = buttons; + config.cButtons = (UINT)TDbuttonID.size()+1; // Includes OK button + } + else { + config.dwCommonButtons = dwCommonButtons; + } + config.cxWidth = 0; // auto width - requires TDF_SIZE_TO_CONTENT config.dwFlags = TDF_SIZE_TO_CONTENT | TDF_CALLBACK_TIMER | TDF_ENABLE_HYPERLINKS; if (hMainIcon) @@ -1653,7 +1801,16 @@ namespace spoututils { SetWindowPos(hwndTop, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } + // Clear global main instruction + wstrInstruction.clear(); + + // Clear custom buttons + TDbuttonID.clear(); + TDbuttonTitle.clear(); + + // Return button pressed // IDCANCEL, IDNO, IDOK, IDRETRY, IDYES + // or custom button ID return nButtonPressed; } @@ -1661,9 +1818,11 @@ namespace spoututils { HRESULT TDcallbackProc(HWND hwnd, UINT uNotification, WPARAM wParam, LPARAM lParam, LONG_PTR dwRefData) { // Topmost - if (bTopMost && TaskHwnd == NULL) { - TaskHwnd = hwnd; - SetWindowPos(TaskHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + if (uNotification == TDN_CREATED) { + if (bTopMost && TaskHwnd == NULL) { + TaskHwnd = hwnd; + SetWindowPos(TaskHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + } } // Timeout diff --git a/SpoutDX/source/SpoutUtils.h b/SpoutDX/source/SpoutUtils.h index fd4a360..ac75afe 100644 --- a/SpoutDX/source/SpoutUtils.h +++ b/SpoutDX/source/SpoutUtils.h @@ -6,7 +6,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Copyright (c) 2017-2023, Lynn Jarvis. All rights reserved. + Copyright (c) 2017-2024, Lynn Jarvis. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -76,6 +76,11 @@ name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") + +// For custom SpoutMessageBox button +#define MB_USERBUTTON 0x00000007L + + // SpoutUtils namespace spoututils { @@ -211,14 +216,33 @@ namespace spoututils { // MessageBox dialog with standard arguments. // Replaces an existing MessageBox call. + // uType options : standard MessageBox buttons and icons + // MB_USERICON - use together with SpoutMessageBoxIcon + // MB_USERBUTTON - use together with SpoutMessageBoxButton + // Hyperlinks can be included in the content using HTML format. + // For example : Spout home page + // Only double quotes are supported and must be escaped. int SPOUT_DLLEXP SpoutMessageBox(HWND hwnd, LPCSTR message, LPCSTR caption, UINT uType, DWORD dwMilliseconds = 0); + // MessageBox dialog with standard arguments + // including taskdialog main instruction large text + int SPOUT_DLLEXP SpoutMessageBox(HWND hwnd, LPCSTR message, LPCSTR caption, UINT uType, const char* instruction, DWORD dwMilliseconds = 0); + // Custom icon for SpoutMessageBox from resources void SPOUT_DLLEXP SpoutMessageBoxIcon(HICON hIcon); // Custom icon for SpoutMessageBox from file bool SPOUT_DLLEXP SpoutMessageBoxIcon(std::string iconfile); + // Custom button for SpoutMessageBox + void SPOUT_DLLEXP SpoutMessageBoxButton(int ID, std::wstring title); + + // Copy text to the clipboard + bool SPOUT_DLLEXP CopyToClipBoard(HWND hwnd, const char* caps); + + // Open logs folder + bool SPOUT_DLLEXP OpenSpoutLogs(); + // // Registry utilities // @@ -286,6 +310,7 @@ namespace spoututils { bool SetNVIDIAmode(const char *command, int mode); bool ExecuteProcess(const char *path); + // Taskdialog for SpoutMessageBox int SPOUT_DLLEXP MessageTaskDialog(HINSTANCE hInst, const char* content, const char* caption, DWORD dwButtons, DWORD dwMilliseconds); // TaskDialogIndirect callback to handle timer, topmost and hyperlinks HRESULT TDcallbackProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, LONG_PTR lpRefData); @@ -295,6 +320,11 @@ namespace spoututils { bool bTopMost = false; // For custom icon HICON hTaskIcon = NULL; + // For custom buttons + std::vectorTDbuttonID; + std::vectorTDbuttonTitle; + // Main instruction text + std::wstring wstrInstruction; } } diff --git a/binaries/SPOUTCAM/SpoutCam32/SpoutCam32.ax b/binaries/SPOUTCAM/SpoutCam32/SpoutCam32.ax index 7e32cfd..ea0c0af 100644 Binary files a/binaries/SPOUTCAM/SpoutCam32/SpoutCam32.ax and b/binaries/SPOUTCAM/SpoutCam32/SpoutCam32.ax differ diff --git a/binaries/SPOUTCAM/SpoutCam64/SpoutCam64.ax b/binaries/SPOUTCAM/SpoutCam64/SpoutCam64.ax index 0d6f83c..f6eb364 100644 Binary files a/binaries/SPOUTCAM/SpoutCam64/SpoutCam64.ax and b/binaries/SPOUTCAM/SpoutCam64/SpoutCam64.ax differ diff --git a/binaries/SPOUTCAM/readme.txt b/binaries/SPOUTCAM/readme.txt index 59105aa..eca1c66 100644 --- a/binaries/SPOUTCAM/readme.txt +++ b/binaries/SPOUTCAM/readme.txt @@ -5,7 +5,7 @@ SpoutCam is a virtual webcam that is also a Spout receiver. SPOUTCAM SETTINGS -A separate utility program "SpoutCamSettings" allows options to be selected for SpoutCam. +A utility program "SpoutCamSettings" allows options to be selected for SpoutCam. SpoutCamSettings.exe, 64 bit build, must be copied to this folder, RESOLUTION diff --git a/source/cam.cpp b/source/cam.cpp index 44f57e5..c777061 100644 --- a/source/cam.cpp +++ b/source/cam.cpp @@ -305,7 +305,8 @@ Revise SpoutDX ReceiveImage/ReadPixelData to handle RGBA textures 06.09.23 Rebuild x86/x64 with Spout version 2.007.012 files VS2022 /MT Version 2.030 - + 01.12.23 Rebuild x86/x64 with Spout version 2.007.013 files + VS2022 /MT Version 2.031 */ @@ -337,7 +338,7 @@ CUnknown * WINAPI CVCam::CreateInstance(LPUNKNOWN lpunk, HRESULT *phr) // OpenSpoutConsole(); // Empty console // EnableSpoutLog(); // Show error logs // EnableSpoutLogFile("SpoutCamDX_2023"); - // SpoutLog("SpoutCamDX ~ Vers 2.029\n"); + // SpoutLog("SpoutCamDX ~ Vers 2.031\n"); // For clear options dialog for scaled display SetProcessDPIAware(); diff --git a/source/version.h b/source/version.h index 6705b66..2108b46 100644 --- a/source/version.h +++ b/source/version.h @@ -9,7 +9,7 @@ #define _VER_MAJORVERSION_STRING L"2" #define _VER_MINORVERSION 1 -#define _VER_MINORVERSION_STRING L"030" +#define _VER_MINORVERSION_STRING L"031" #define _VER_BUGFIXVERSION 0 #define _VER_BUGFIXVERSION_STRING L"0" @@ -27,5 +27,5 @@ #define _VER_PRODUCTNAME L"SpoutCam" #define _VER_INTERNALNAME L"SpoutCam.ax" -#define _VER_COPYRIGHT L"Copyright (c) Lynn Jarvis 2013-2023" -#define _VER_YEAR L"2023" +#define _VER_COPYRIGHT L"Copyright (c) Lynn Jarvis 2013-2024" +#define _VER_YEAR L"2024"