/* -LICENSE-START- ** Copyright (c) 2012 Blackmagic Design ** ** Permission is hereby granted, free of charge, to any person or organization ** obtaining a copy of the software and accompanying documentation (the ** "Software") to use, reproduce, display, distribute, sub-license, execute, ** and transmit the Software, and to prepare derivative works of the Software, ** and to permit third-parties to whom the Software is furnished to do so, in ** accordance with: ** ** (1) if the Software is obtained from Blackmagic Design, the End User License ** Agreement for the Software Development Kit ("EULA") available at ** https://www.blackmagicdesign.com/EULA/DeckLinkSDK; or ** ** (2) if the Software is obtained from any third party, such licensing terms ** as notified by that third party, ** ** and all subject to the following: ** ** (3) the copyright notices in the Software and this entire statement, ** including the above license grant, this restriction and the following ** disclaimer, must be included in all copies of the Software, in whole or in ** part, and all derivative works of the Software, unless such copies or ** derivative works are solely in the form of machine-executable object code ** generated by a source language processor. ** ** (4) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT ** SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ** FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** DEALINGS IN THE SOFTWARE. ** ** A copy of the Software is available free of charge at ** https://www.blackmagicdesign.com/desktopvideo_sdk under the EULA. ** ** -LICENSE-END- */ // // LoopThroughWithOpenGLCompositing.cpp // LoopThroughWithOpenGLCompositing // #include "stdafx.h" #include "resource.h" #include "OpenGLComposite.h" #include #include #include #ifndef WGL_CONTEXT_MAJOR_VERSION_ARB #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 #endif #ifndef WGL_CONTEXT_MINOR_VERSION_ARB #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 #endif #ifndef WGL_CONTEXT_PROFILE_MASK_ARB #define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 #endif #ifndef WGL_CONTEXT_CORE_PROFILE_BIT_ARB #define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 #endif #define MAX_LOADSTRING 100 // Declaration for Window procedure LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); typedef HGLRC (WINAPI* PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hShareContext, const int* attribList); namespace { const int kStatusPanelWidth = 680; const int kStatusPanelHeight = 92; const int kStatusPadding = 8; const int kStatusLabelWidth = 58; const int kStatusButtonWidth = 86; const int kStatusRowHeight = 24; const int kStatusGap = 6; const UINT kCreateStatusStripMessage = WM_APP + 1; enum StatusControlId { kControlUrlEditId = 2001, kDocsUrlEditId = 2002, kOscAddressEditId = 2003, kOpenControlButtonId = 2004, kOpenDocsButtonId = 2005 }; struct StatusStripControls { HWND panel = NULL; HWND controlLabel = NULL; HWND controlUrl = NULL; HWND openControl = NULL; HWND docsLabel = NULL; HWND docsUrl = NULL; HWND openDocs = NULL; HWND oscLabel = NULL; HWND oscAddress = NULL; }; bool StatusStripCreated(const StatusStripControls& controls) { return controls.panel != NULL; } HWND CreateStatusChild(HWND parent, const char* className, const char* text, DWORD style, DWORD exStyle, int controlId) { return CreateWindowExA( exStyle, className, text, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | style, 0, 0, 0, 0, parent, reinterpret_cast(static_cast(controlId)), reinterpret_cast(GetWindowLongPtr(parent, GWLP_HINSTANCE)), NULL); } void CreateStatusStrip(HWND hWnd, StatusStripControls& controls) { controls.panel = CreateStatusChild(hWnd, "STATIC", "", SS_LEFT, WS_EX_CLIENTEDGE, 0); controls.controlLabel = CreateStatusChild(hWnd, "STATIC", "Control", SS_LEFT, 0, 0); controls.controlUrl = CreateStatusChild(hWnd, "EDIT", "", ES_AUTOHSCROLL | ES_READONLY | WS_TABSTOP, WS_EX_CLIENTEDGE, kControlUrlEditId); controls.openControl = CreateStatusChild(hWnd, "BUTTON", "Open", BS_PUSHBUTTON | WS_TABSTOP, 0, kOpenControlButtonId); controls.docsLabel = CreateStatusChild(hWnd, "STATIC", "Docs", SS_LEFT, 0, 0); controls.docsUrl = CreateStatusChild(hWnd, "EDIT", "", ES_AUTOHSCROLL | ES_READONLY | WS_TABSTOP, WS_EX_CLIENTEDGE, kDocsUrlEditId); controls.openDocs = CreateStatusChild(hWnd, "BUTTON", "Open", BS_PUSHBUTTON | WS_TABSTOP, 0, kOpenDocsButtonId); controls.oscLabel = CreateStatusChild(hWnd, "STATIC", "OSC", SS_LEFT, 0, 0); controls.oscAddress = CreateStatusChild(hWnd, "EDIT", "", ES_AUTOHSCROLL | ES_READONLY | WS_TABSTOP, WS_EX_CLIENTEDGE, kOscAddressEditId); HFONT guiFont = reinterpret_cast(GetStockObject(DEFAULT_GUI_FONT)); HWND children[] = { controls.controlLabel, controls.controlUrl, controls.openControl, controls.docsLabel, controls.docsUrl, controls.openDocs, controls.oscLabel, controls.oscAddress }; for (HWND child : children) { if (child) SendMessage(child, WM_SETFONT, reinterpret_cast(guiFont), TRUE); } SetWindowTextA(controls.controlUrl, "Starting control server..."); SetWindowTextA(controls.docsUrl, "Starting API docs..."); SetWindowTextA(controls.oscAddress, "Starting OSC listener..."); } void RaiseStatusControls(const StatusStripControls& controls) { if (!StatusStripCreated(controls)) return; SetWindowPos(controls.panel, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); HWND interactiveControls[] = { controls.controlLabel, controls.controlUrl, controls.openControl, controls.docsLabel, controls.docsUrl, controls.openDocs, controls.oscLabel, controls.oscAddress }; for (HWND control : interactiveControls) { if (control) SetWindowPos(control, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } } void LayoutStatusStrip(HWND hWnd, const StatusStripControls& controls) { RECT clientRect = {}; if (!GetClientRect(hWnd, &clientRect) || !controls.panel) return; const int clientWidth = static_cast(clientRect.right - clientRect.left); const int clientHeight = static_cast(clientRect.bottom - clientRect.top); const int panelWidth = std::max(280, std::min(kStatusPanelWidth, clientWidth - (kStatusPadding * 2))); const int panelHeight = kStatusPanelHeight; const int panelLeft = kStatusPadding; const int panelTop = std::max(kStatusPadding, clientHeight - panelHeight - kStatusPadding); MoveWindow(controls.panel, panelLeft, panelTop, panelWidth, panelHeight, TRUE); const int rowX = panelLeft + kStatusPadding; const int editX = rowX + kStatusLabelWidth + kStatusGap; const int buttonX = panelLeft + panelWidth - kStatusPadding - kStatusButtonWidth; const int editWidth = std::max(80, buttonX - editX - kStatusGap); const int oscWidth = std::max(80, panelLeft + panelWidth - editX - kStatusPadding); const int row1 = panelTop + kStatusPadding; const int row2 = row1 + kStatusRowHeight + kStatusGap; const int row3 = row2 + kStatusRowHeight + kStatusGap; MoveWindow(controls.controlLabel, rowX, row1 + 3, kStatusLabelWidth, kStatusRowHeight, TRUE); MoveWindow(controls.controlUrl, editX, row1, editWidth, kStatusRowHeight, TRUE); MoveWindow(controls.openControl, buttonX, row1, kStatusButtonWidth, kStatusRowHeight, TRUE); MoveWindow(controls.docsLabel, rowX, row2 + 3, kStatusLabelWidth, kStatusRowHeight, TRUE); MoveWindow(controls.docsUrl, editX, row2, editWidth, kStatusRowHeight, TRUE); MoveWindow(controls.openDocs, buttonX, row2, kStatusButtonWidth, kStatusRowHeight, TRUE); MoveWindow(controls.oscLabel, rowX, row3 + 3, kStatusLabelWidth, kStatusRowHeight, TRUE); MoveWindow(controls.oscAddress, editX, row3, oscWidth, kStatusRowHeight, TRUE); RaiseStatusControls(controls); } void UpdateStatusStrip(const StatusStripControls& controls, const OpenGLComposite& composite) { if (!StatusStripCreated(controls)) return; SetWindowTextA(controls.controlUrl, composite.GetControlUrl().c_str()); SetWindowTextA(controls.docsUrl, composite.GetDocsUrl().c_str()); SetWindowTextA(controls.oscAddress, composite.GetOscAddress().c_str()); } void OpenUrl(const char* url) { ShellExecuteA(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL); } } void ShowUnhandledExceptionMessage(const char* prefix) { try { throw; } catch (const std::exception& exception) { std::string message = std::string(prefix) + "\n\n" + exception.what(); MessageBoxA(NULL, message.c_str(), "Unhandled exception", MB_OK | MB_ICONERROR); } catch (...) { MessageBoxA(NULL, prefix, "Unhandled exception", MB_OK | MB_ICONERROR); } } // Select the pixel format for a given device context void SetDCPixelFormat(HDC hDC) { int nPixelFormat; static PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), // Size of this structure 1, // Version of this structure PFD_DRAW_TO_WINDOW | // Draw to Window (not to bitmap) PFD_SUPPORT_OPENGL | // Support OpenGL calls in window PFD_DOUBLEBUFFER, // Double buffered mode PFD_TYPE_RGBA, // RGBA Color mode 32, // Want 32 bit color 0,0,0,0,0,0, // Not used to select mode 0,0, // Not used to select mode 0,0,0,0,0, // Not used to select mode 16, // Size of depth buffer 0, // Not used 0, // Not used 0, // Not used 0, // Not used 0,0,0 }; // Not used // Choose a pixel format that best matches that described in pfd nPixelFormat = ChoosePixelFormat(hDC, &pfd); // Set the pixel format for the device context SetPixelFormat(hDC, nPixelFormat, &pfd); } HGLRC CreateModernOpenGLContext(HDC hDC) { PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = reinterpret_cast(wglGetProcAddress("wglCreateContextAttribsARB")); if (!wglCreateContextAttribsARB) return NULL; const int versionCandidates[][2] = { { 4, 5 }, { 4, 3 }, { 3, 3 } }; for (const auto& version : versionCandidates) { const int attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, version[0], WGL_CONTEXT_MINOR_VERSION_ARB, version[1], WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, 0 }; HGLRC modernContext = wglCreateContextAttribsARB(hDC, 0, attribs); if (modernContext != NULL) return modernContext; } return NULL; } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg; // Windows message structure WNDCLASS wc; // Windows class structure HWND hWnd; // Storeage for window handle TCHAR szTitle[MAX_LOADSTRING]; // The title bar text TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name // Initialize global strings LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_OPENGLOUTPUT, szWindowClass, MAX_LOADSTRING); // Register Window style wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); // No need for background brush for OpenGL window wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = szWindowClass; // Register the window class if (RegisterClass(&wc) == 0) return FALSE; // Create the main application window hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, CW_USEDEFAULT, 0, 250, 250, NULL, NULL, hInstance, NULL); // If window was not created, quit if (hWnd == NULL) return FALSE; // Display the window ShowWindow(hWnd,SW_SHOW); UpdateWindow(hWnd); // Process application messages until the application closes while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; } // Window procedure, handles all messages for this program LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { static HGLRC hRC = NULL; // Permenant Rendering context static HDC hDC = NULL; // Private GDI Device context static OpenGLComposite* pOpenGLComposite = NULL; static bool sInteractiveResize = false; static StatusStripControls sStatusStrip; switch (message) { // Window creation, setup for OpenGL context case WM_CREATE: { try { // Store the device context hDC = GetDC(hWnd); // Select the pixel format SetDCPixelFormat(hDC); // Create the rendering context and make it current hRC = wglCreateContext(hDC); wglMakeCurrent(hDC, hRC); HGLRC modernRC = CreateModernOpenGLContext(hDC); if (modernRC == NULL) { MessageBox(NULL, _T("This application requires an OpenGL 3.3+ core profile context."), _T("OpenGL initialization Error."), MB_OK); PostMessage(hWnd, WM_CLOSE, 0, 0); break; } wglMakeCurrent(NULL, NULL); wglDeleteContext(hRC); hRC = modernRC; wglMakeCurrent(hDC, hRC); // Initialize COM HRESULT result; result = CoInitialize(NULL); if (FAILED(result)) { MessageBox(NULL, _T("Initialization of COM failed."), _T("Application initialization Error."),MB_OK); PostMessage(hWnd, WM_CLOSE, 0, 0); break; } // Setup OpenGL and DeckLink capture and playout object pOpenGLComposite = new OpenGLComposite(hWnd, hDC, hRC); if (pOpenGLComposite->InitDeckLink()) { wglMakeCurrent( NULL, NULL ); if (pOpenGLComposite->Start()) { PostMessage(hWnd, kCreateStatusStripMessage, 0, 0); break; // success } MessageBoxA(NULL, "The OpenGL/DeckLink runtime initialized, but playout failed to start. See the previous DeckLink start message for the failing call.", "Startup failed", MB_OK | MB_ICONERROR); } else { MessageBoxA(NULL, "The OpenGL/DeckLink runtime failed to initialize. See the previous initialization message for the failing call.", "Startup failed", MB_OK | MB_ICONERROR); } // Failed to initialize - cleanup delete pOpenGLComposite; pOpenGLComposite = NULL; PostMessage(hWnd, WM_CLOSE, 0, 0); break; } catch (...) { ShowUnhandledExceptionMessage("Startup failed while creating the OpenGL/DeckLink runtime."); PostMessage(hWnd, WM_CLOSE, 0, 0); break; } } case kCreateStatusStripMessage: if (pOpenGLComposite) { if (!StatusStripCreated(sStatusStrip)) CreateStatusStrip(hWnd, sStatusStrip); UpdateStatusStrip(sStatusStrip, *pOpenGLComposite); LayoutStatusStrip(hWnd, sStatusStrip); RECT clientRect = {}; if (GetClientRect(hWnd, &clientRect)) { pOpenGLComposite->resizeGL( static_cast(clientRect.right - clientRect.left), static_cast(clientRect.bottom - clientRect.top)); } InvalidateRect(hWnd, NULL, FALSE); } break; case WM_DESTROY: try { if (pOpenGLComposite) { pOpenGLComposite->Stop(); delete pOpenGLComposite; } } catch (...) { ShowUnhandledExceptionMessage("Shutdown failed while tearing down the OpenGL/DeckLink runtime."); } // Deselect the current rendering context and delete it wglMakeCurrent(hDC, NULL); wglDeleteContext(hRC); // Tell the application to terminate after the window is gone PostQuitMessage(0); break; case WM_ENTERSIZEMOVE: sInteractiveResize = true; break; case WM_EXITSIZEMOVE: sInteractiveResize = false; if (pOpenGLComposite) { RECT clientRect = {}; if (GetClientRect(hWnd, &clientRect)) { pOpenGLComposite->resizeGL( static_cast(clientRect.right - clientRect.left), static_cast(clientRect.bottom - clientRect.top)); } } InvalidateRect(hWnd, NULL, FALSE); break; case WM_SIZE: try { if (StatusStripCreated(sStatusStrip)) LayoutStatusStrip(hWnd, sStatusStrip); if (pOpenGLComposite) pOpenGLComposite->resizeGL(LOWORD(lParam), HIWORD(lParam)); } catch (...) { ShowUnhandledExceptionMessage("Resize failed inside the OpenGL runtime."); } break; case WM_ERASEBKGND: return 1; case WM_PAINT: try { PAINTSTRUCT paint = {}; BeginPaint(hWnd, &paint); EndPaint(hWnd, &paint); if (!sInteractiveResize && pOpenGLComposite) { wglMakeCurrent(hDC, hRC); pOpenGLComposite->paintGL(); wglMakeCurrent( NULL, NULL ); RaiseStatusControls(sStatusStrip); } } catch (...) { wglMakeCurrent( NULL, NULL ); ShowUnhandledExceptionMessage("Paint failed inside the OpenGL runtime."); } break; case WM_KEYDOWN: try { if (pOpenGLComposite && (wParam == 'R' || wParam == 'r')) { pOpenGLComposite->ReloadShader(); InvalidateRect(hWnd, NULL, FALSE); } } catch (...) { ShowUnhandledExceptionMessage("Shader reload failed inside the OpenGL runtime."); } break; case WM_COMMAND: switch (LOWORD(wParam)) { case kOpenControlButtonId: if (pOpenGLComposite) { std::string url = pOpenGLComposite->GetControlUrl(); OpenUrl(url.c_str()); } break; case kOpenDocsButtonId: if (pOpenGLComposite) { std::string url = pOpenGLComposite->GetDocsUrl(); OpenUrl(url.c_str()); } break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; default: return (DefWindowProc(hWnd, message, wParam, lParam)); } return (0L); }