reFX
August 20, 2023, 2:44am
1
I want to create a child window that I can use as a render target for bgfx. The app I’m writing is targeting Windows, macOS, and potentially Linux.
The idea is to have the entire UI done with JUCE, except for one viewport (a rectangular portion) in which I want to use pixel-shaders and textures.
I’m trying to use bgfx so that I can write three machine-specific shaders (D3D for Windows, Metal for macOS, and openGL for Linux).
Ideally, I would use JUCE to create a platform-specific Window and somehow attach it to my main app Window, so it acts as a child, moves/resizes with the app, but otherwise stays untouched by JUCE.
Then, using a VBlankAttachment, I would trigger updates for that particular Window.
Is this possible right now?
2 Likes
matt
August 21, 2023, 2:57am
2
I don’t know about the other platforms but I’ll post an example of an HWNDComponent for Windows.
Matt
reFX
August 21, 2023, 4:18am
3
Thanks. Much appreciated.
matt
August 22, 2023, 11:38pm
4
#pragma once
#include <JuceHeader.h>
class HwndComponentExample : public juce::HWNDComponent
{
public:
HwndComponentExample();
~HwndComponentExample() override;
protected:
void createWindow(void* ancestorHwnd, juce::Rectangle<int> bounds);
void parentHierarchyChanged() override;
juce::HeapBlock<char> windowClassName;
void* childWindowHandle = nullptr;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(HwndComponentExample)
};
1 Like
matt
August 22, 2023, 11:39pm
5
#include <windows.h>
#include "HwndComponentExample.h"
#if JUCE_DEBUG
static void getLastError()
{
LPVOID lpMsgBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, nullptr);
DBG((LPTSTR)lpMsgBuf);
LocalFree(lpMsgBuf);
}
#endif
HwndComponentExample::HwndComponentExample() {}
HwndComponentExample::~HwndComponentExample() {}
static LRESULT CALLBACK windowProc(HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
if (WM_CREATE == message)
{
LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
HwndComponentExample* that = (HwndComponentExample*)pcs->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(that));
return 1;
}
HwndComponentExample* that = reinterpret_cast<HwndComponentExample*>(static_cast<LONG_PTR>(GetWindowLongPtrW(hwnd, GWLP_USERDATA)));
if (that)
{
switch (message)
{
case WM_ENTERSIZEMOVE:
break;
case WM_SIZE:
{
return 0;
}
case WM_EXITSIZEMOVE:
break;
case WM_PAINT:
{
PAINTSTRUCT paintStruct;
HDC dc = BeginPaint(hwnd, &paintStruct);
RECT clientRect;
GetClientRect(hwnd, &clientRect);
auto region = CreateRectRgnIndirect(&clientRect);
auto brush = CreateSolidBrush(RGB(0, 200, 0));
FillRgn(dc, region, brush);
DeleteObject(region);
DeleteObject(brush);
EndPaint(hwnd, &paintStruct);
ValidateRect(hwnd, nullptr);
return 0;
}
}
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
void HwndComponentExample::createWindow(void* ancestorHwnd, juce::Rectangle<int> bounds)
{
if (nullptr == childWindowHandle)
{
juce::String tempClassName = "HwndComponentExample_" + juce::String::toHexString(juce::Time::getHighResolutionTicks());
auto numClassNameBytes = tempClassName.getNumBytesAsUTF8() + 1;
windowClassName.allocate(numClassNameBytes, true);
tempClassName.copyToUTF8(windowClassName.getData(), numClassNameBytes);
WNDCLASSEX wcex = {};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = windowProc;
wcex.cbWndExtra = 0;
wcex.hInstance = 0;
wcex.lpszClassName = windowClassName.get();
RegisterClassEx(&wcex);
childWindowHandle = CreateWindowEx(WS_EX_NOREDIRECTIONBITMAP,
windowClassName.get(),
"",
WS_VISIBLE | WS_CHILD | WS_DISABLED,
0, 0, bounds.getWidth(), bounds.getHeight(),
(HWND)ancestorHwnd,
nullptr,
(HINSTANCE)juce::Process::getCurrentModuleInstanceHandle(),
this);
#if JUCE_DEBUG
if (!childWindowHandle)
{
getLastError();
}
jassert(childWindowHandle);
#endif
}
}
void HwndComponentExample::parentHierarchyChanged()
{
if (auto topLevelComponent = getTopLevelComponent())
{
if (auto topLevelPeer = topLevelComponent->getPeer())
{
if (nullptr == childWindowHandle)
{
//
// Create the native window if the top level component has been added to the desktop
//
createWindow(topLevelPeer->getNativeHandle(), getLocalBounds());
if (childWindowHandle)
{
setHWND(childWindowHandle);
}
}
}
}
}
That should create a child window, assign that child window to an HWNDComponent, and paint it green.
Matt
1 Like
reFX
August 23, 2023, 5:02pm
6
Thank you so much. This is a huge help.
1 Like