Initial commit

This commit is contained in:
2026-04-03 00:22:39 -05:00
commit eca1e8c458
945 changed files with 218160 additions and 0 deletions

View File

@@ -0,0 +1,319 @@
/*
SST_WMDialogBox_Win32.c
Author: Patrick Baggett <ptbaggett@762studios.com>
Created: 1/5/2013
Purpose:
Model
License:
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
*/
#include "Win32Private.h"
#define BUTTON_HSPACE 16 /* Space on either side of a button */
#define BUTTON_VSPACE 16 /* Space between top of button and dialog text */
#define TEXT_HSPACE 16
#define TEXT_VSPACE 16
typedef struct DialogBoxData
{
const char* message;
int lenMessage;
int buttonId;
int exitTime;
} DialogBoxData;
/*************************************************************************/
static LONG dlgRegCount = 0;
/*************************************************************************/
static LRESULT WINAPI libsstDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
static HWND createButton(HWND owner, int id, const char* label, int x, int y, int w, int h);
/*************************************************************************/
int Win32_ShowDialogBox(SST_DisplayTarget target, SST_Window parent, const char* caption, const char* message, const char** buttons, int nrButtons)
{
SST_Window_Win32* win = (SST_Window_Win32*)parent;
SST_DisplayTarget_Win32* displayTarget = (SST_DisplayTarget_Win32*)target;
HWND hWnd;
HWND hParentWnd = NULL;
MSG msg;
HDC hDC;
HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
/* Compute size of a button. This is absolutely arcane, but the 50x14 units and the /4 /8 are documented. Somewhere... */
uint32_t units = (uint32_t)GetDialogBaseUnits();
int horiz = (int)MulDiv((units & 0xFF), 50, 4);
int vert = (int)MulDiv((units >> 16), 14, 8);
int w, h;
int returnCode;
/* Window rectangle computation */
DWORD style = WS_CAPTION | WS_SYSMENU | WS_CLIPCHILDREN;
DWORD styleEx = 0;
RECT r;
RECT textRect;
RECT centerOn;
DialogBoxData* dlgData;
/* Allocate dialog box data */
dlgData = (DialogBoxData*)HeapAlloc(GetProcessHeap(), 0, sizeof(DialogBoxData));
if(dlgData == NULL)
return -1;
dlgData->message = message;
dlgData->lenMessage = (int)strlen(message);
dlgData->buttonId = -1;
dlgData->exitTime = 0;
/*
First, figure out a good area to center the dialog box on. It's highly unintuitive to have it
it mapped in the top-left corner. We center it on the parent window (if possible) or the monitor
otherwise.
*/
/* Have a parent window? */
if(win != NULL)
{
/* Then center on it */
hParentWnd = win->hWnd;
GetWindowRect(hParentWnd, &centerOn);
}
else /* Otherwise center on default display */
{
FindMonitorInfo fmi;
/* Attempt to find the monitor associated with a display device */
findMonitor(&displayTarget->devs[0], &fmi);
/* Didn't find it? */
if(!fmi.foundIt)
return -1;
/* Center on the monitor */
centerOn.top = fmi.top;
centerOn.left = fmi.left;
centerOn.bottom = fmi.bottom;
centerOn.right = fmi.right;
}
/* Start the window as if it was at (0,0) */
r.top = 0;
r.left = 0;
r.bottom = 2*BUTTON_VSPACE+vert; /* Enough space for a button and vertical padding above and below it */
r.right = (LONG)nrButtons*(horiz + BUTTON_HSPACE)+BUTTON_HSPACE;
/* Now we need to compute the area requried to display the text. This will be summed with the area required for the buttons on the Y axis, but
the X axis will be the maximum of the two. */
/* 1) Create a fake DC to compute the size of the text's rect */
hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
SelectObject(hDC, (HGDIOBJ)hFont);
/* 2) Actually compute the rect using DrawTextEx() and DT_CALCRECT. */
memset(&textRect, 0, sizeof(textRect));
DrawTextExA(hDC, (LPSTR)dlgData->message, dlgData->lenMessage, &textRect, DT_CALCRECT | DT_TOP | DT_LEFT, NULL);
DeleteDC(hDC);
/* 3) Add a border around the entire thing */
textRect.bottom += 2*TEXT_VSPACE;
textRect.right += 2*TEXT_HSPACE;
/* For the X-axis, we want the maximum of the amount of space it takes to display buttons and text */
if(r.right < textRect.right)
r.right = textRect.right;
/* For the Y-aaxis, just append space to the top of dialog box for the text rect */
r.bottom += textRect.bottom;
/* Move the window to the center of the screen: */
/* 1) Compute width and height of window */
w = r.right - r.left;
h = r.bottom - r.top;
AdjustWindowRectEx(&r, style, FALSE, styleEx);
/* After we adjusted the size of the window so that the client area is constant, we need
to move it back to (0,0). */
if(r.top != 0)
{
LONG d = -r.top;
r.top = 0;
r.bottom += d;
}
if(r.left != 0)
{
LONG d = -r.left;
r.left = 0;
r.right += d;
}
/* 2) Adjust so it fits on the screen by doing: `adjust = (center - size) /2` */
r.left += ((centerOn.right - centerOn.left) - w) / 2;
r.right += ((centerOn.right - centerOn.left) - w) / 2;
r.top += ((centerOn.bottom - centerOn.top) - h) / 2;
r.bottom += ((centerOn.bottom - centerOn.top) - h) / 2;
/* You can't create a window without registering the class, so let's do it now. Since
this function has "Concurrent" access, use an atomic operation to decide if it is necessary. */
if(InterlockedIncrement(&dlgRegCount) == 1)
{
WNDCLASSEXA wc;
memset(&wc, 0, sizeof(wc));
wc.cbSize = sizeof(wc);
wc.lpszClassName = SST_DLGCLASS;
wc.hInstance = GetModuleHandleA(NULL);
wc.lpfnWndProc = libsstDlgProc;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.style = CS_OWNDC;
wc.hbrBackground = (HBRUSH)(uintptr_t)(COLOR_WINDOW+1);
RegisterClassExA(&wc);
} /* TODO: I suppose it is possible that someone could turn this into a race condition. Priority = lowest */
/* FINALLY, create the dialog window */
hWnd = CreateWindowExA(styleEx,
SST_DLGCLASS,
caption,
style,
r.left, r.top, /* XY position */
r.right-r.left, r.bottom-r.top, /* Size */
hParentWnd,
(NULL),
GetModuleHandleA(NULL),
NULL);
/* Made the window successfully? */
if(hWnd != NULL)
{
int i;
POINT bottomRight;
RECT clientRect;
SetWindowLongPtrA(hWnd, GWLP_USERDATA, (LONG_PTR)dlgData);
ShowWindow(hWnd, SW_SHOW);
/* Get the coordinates of the bottom right pixel */
GetClientRect(hWnd, &clientRect);
bottomRight.x = clientRect.right;
bottomRight.y = clientRect.bottom;
/* Create the dialog buttons in reverse order starting at the right end of the dialog and moving left */
for(i=0; i<nrButtons; i++)
{
HWND hButton = createButton(hWnd, nrButtons-1-i, buttons[nrButtons-1-i], bottomRight.x-((i+1)*(BUTTON_HSPACE+horiz)), bottomRight.y-(BUTTON_VSPACE+vert), horiz, vert);
/* Set the font on them too */
SendMessage(hButton, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE, 0));
}
do
{
while(PeekMessageA(&msg, hWnd, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
} while(!dlgData->exitTime);
}
returnCode = dlgData->buttonId;
HeapFree(GetProcessHeap(), 0,dlgData);
return returnCode;
}
/*************************************************************************/
static LRESULT WINAPI libsstDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
DialogBoxData* dlgData = (DialogBoxData*)GetWindowLongPtrA(hWnd, GWLP_USERDATA);
switch(msg)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
RECT r;
HDC hDC;
if(dlgData)
{
hDC = BeginPaint(hWnd, &ps);
SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT));
GetClientRect(hWnd, &r);
r.left += TEXT_HSPACE;
r.right -= TEXT_HSPACE;
r.top += TEXT_VSPACE;
r.bottom -= TEXT_VSPACE;
DrawTextExA(hDC, (LPSTR)dlgData->message, dlgData->lenMessage, &r, DT_TOP | DT_LEFT, NULL);
EndPaint(hWnd, &ps);
}
return 0;
break;
}
/* Aborting the dialog */
case WM_CLOSE:
dlgData->buttonId = -1;
dlgData->exitTime = 1;
DestroyWindow(hWnd);
return 0;
break;
case WM_COMMAND:
{
if(HIWORD(wParam) == BN_CLICKED)
{
dlgData->buttonId = (int)LOWORD(wParam);
dlgData->exitTime = 1;
DestroyWindow(hWnd);
}
return 0;
break;
}
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
/*************************************************************************/
static HWND createButton(HWND owner, int id, const char* label, int x, int y, int w, int h)
{
HWND hWnd = CreateWindowExA(0,
"BUTTON",
label,
WS_TABSTOP|WS_VISIBLE|
WS_CHILD|BS_DEFPUSHBUTTON,
x, y,
w, h,
owner,
(HMENU)(uintptr_t)id,
GetModuleHandleA(NULL),
NULL);
return hWnd;
}

View File

@@ -0,0 +1,268 @@
/*
SST_WMEnum_Win32.c
Author: Patrick Baggett <ptbaggett@762studios.com>
Created: 1/7/2012
Purpose:
Enumerates graphics adapters and screens.
License:
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
*/
#include <SST/SST_WM.h>
#include "Win32Private.h"
#include "../APIPrivate.h"
#include <string.h> /* strlen() etc. */
#include <stdlib.h>
/*************************************************************************/
static SST_GraphicsEnumerator Win32_CreateGraphicsEnumerator(void)
{
DISPLAY_DEVICEA* devsFound = NULL;
char* adapterNames;
char* adapterGUIDs;
size_t* screenCount;
size_t adapterCount;
size_t devCount = 0;
size_t ASPairCount = 0;
size_t i;
SST_GraphicsEnumerator_Win32* enumerator;
ASMapEntry* ASMap;
HANDLE hProcessHeap;
/* Get a list of Win32 display devices */
devsFound = get_win32devs(&devCount);
if(devsFound == NULL)
return NULL;
hProcessHeap = GetProcessHeap();
/* Get a list of the graphics adapters' names */
adapterNames = get_adapters(devsFound, devCount, &adapterCount, &adapterGUIDs);
if(adapterNames == NULL)
{
HeapFree(hProcessHeap, 0, devsFound);
return NULL;
}
/* Make a map of adapter->screen count */
screenCount = HeapAlloc(hProcessHeap, 0, adapterCount*sizeof(size_t));
if(screenCount == NULL)
{
HeapFree(hProcessHeap, 0, adapterGUIDs);
HeapFree(hProcessHeap, 0, adapterNames);
HeapFree(hProcessHeap, 0, devsFound);
return NULL;
}
for(i=0; i<adapterCount; i++)
screenCount[i] = 0;
/* Attempt to build the adapter-screen map */
ASMap = build_asmap(devsFound, adapterGUIDs, devCount, adapterCount, screenCount, &ASPairCount);
if(ASMap == NULL)
{
HeapFree(hProcessHeap, 0, adapterGUIDs);
HeapFree(hProcessHeap, 0, adapterNames);
HeapFree(hProcessHeap, 0, devsFound);
HeapFree(hProcessHeap, 0, screenCount);
return NULL;
}
/* Done with GUIDs */
HeapFree(hProcessHeap, 0, adapterGUIDs);
/* Get the list of video modes for each adapter-screen pair */
for(i=0; i<ASPairCount; i++)
{
size_t vmodeCount;
SST_VideoMode* vmodes;
vmodes = get_vmodes(ASMap[i].dev, &vmodeCount, &ASMap[i].defaultVmode);
if(vmodes == NULL)
{
size_t j;
for(j=0; j<i; j++)
HeapFree(hProcessHeap, 0, ASMap[i].vmodes);
HeapFree(hProcessHeap, 0, adapterNames);
HeapFree(hProcessHeap, 0, devsFound);
HeapFree(hProcessHeap, 0, screenCount);
HeapFree(hProcessHeap, 0, ASMap);
return NULL;
}
/* Save the screens for this */
ASMap[i].vmodeCount = vmodeCount;
ASMap[i].vmodes = vmodes;
}
/* Prepare a structure to return... */
enumerator = HeapAlloc(hProcessHeap, 0, sizeof(SST_GraphicsEnumerator_Win32));
if(enumerator == NULL)
{
/* Doh! So close :\ */
size_t j;
for(j=0; j<i; j++)
HeapFree(hProcessHeap, 0, ASMap[i].vmodes);
HeapFree(hProcessHeap, 0, adapterNames);
HeapFree(hProcessHeap, 0, devsFound);
HeapFree(hProcessHeap, 0, screenCount);
HeapFree(hProcessHeap, 0, ASMap);
return NULL;
}
/* Save fields */
enumerator->adapterNames = adapterNames;
enumerator->adapterCount = adapterCount;
enumerator->devCount = devCount;
enumerator->devsFound = devsFound;
enumerator->screenCount = screenCount;
enumerator->ASMap = ASMap;
enumerator->ASPairCount = ASPairCount;
return enumerator;
}
/*************************************************************************/
static size_t Win32_GetEnumAdapterCount(SST_GraphicsEnumerator enumerator)
{
SST_GraphicsEnumerator_Win32* enumWin32 = (SST_GraphicsEnumerator_Win32*)enumerator;
return enumWin32->adapterCount;
}
/*************************************************************************/
static void Win32_GetEnumAdapterName(SST_GraphicsEnumerator enumerator, size_t adapterId, char* nameReturn, size_t* bufferSize)
{
SST_GraphicsEnumerator_Win32* enumWin32 = (SST_GraphicsEnumerator_Win32*)enumerator;
size_t len;
const char* name = &enumWin32->adapterNames[adapterId * ADAPTER_NAME_STRLEN];
len = strlen(name);
/* Query name length */
if(nameReturn == NULL)
{
*bufferSize = len+1;
}
else
{
size_t copyAmount;
/* Nothing to do? */
if(*bufferSize == 0)
return;
/* Use min(len, (*bufferSize)-1) */
copyAmount = len;
if(copyAmount > (*bufferSize)-1)
copyAmount = (*bufferSize)-1;
memcpy(nameReturn, name, copyAmount);
nameReturn[copyAmount] = '\0';
}
}
/*************************************************************************/
static size_t Win32_GetEnumScreenCount(SST_GraphicsEnumerator enumerator, size_t adapterId)
{
SST_GraphicsEnumerator_Win32* enumWin32 = (SST_GraphicsEnumerator_Win32*)enumerator;
return enumWin32->screenCount[adapterId];
}
/*************************************************************************/
static void Win32_GetEnumVideoModes(SST_GraphicsEnumerator enumerator, size_t adapterId, size_t screenId, SST_VideoMode* modesReturn, size_t* modeCountReturn)
{
SST_GraphicsEnumerator_Win32* enumWin32 = (SST_GraphicsEnumerator_Win32*)enumerator;
size_t i;
ASMapEntry* entry = NULL;
/* Determine which adapter-screen pair to use */
for(i=0; i<enumWin32->ASPairCount; i++)
{
entry = &enumWin32->ASMap[i];
if(entry->adapter == adapterId && entry->screen == screenId)
{
entry = &enumWin32->ASMap[i];
break;
}
}
/* When 'modesReturn' is null, user is getting mode count */
if(modesReturn == NULL)
*modeCountReturn = entry->vmodeCount;
else /* else, copy '*modeCountReturn' number of modes */
{
size_t nrCopy = *modeCountReturn;
if(nrCopy > entry->vmodeCount)
nrCopy = entry->vmodeCount;
memcpy(modesReturn, entry->vmodes, sizeof(SST_VideoMode) * nrCopy);
}
}
/*************************************************************************/
static void Win32_GetEnumCurrentVideoMode(SST_GraphicsEnumerator enumerator, size_t adapterId, size_t screenId, SST_VideoMode* mode)
{
SST_GraphicsEnumerator_Win32* enumWin32 = (SST_GraphicsEnumerator_Win32*)enumerator;
size_t i;
for(i=0; i<enumWin32->ASPairCount; i++)
{
if(enumWin32->ASMap[i].adapter == adapterId && enumWin32->ASMap[i].screen == screenId)
{
*mode = enumWin32->ASMap[i].defaultVmode;
return;
}
}
}
/*************************************************************************/
static void Win32_DestroyGraphicsEnumerator(SST_GraphicsEnumerator enumerator)
{
SST_GraphicsEnumerator_Win32* enumWin32 = (SST_GraphicsEnumerator_Win32*)enumerator;
size_t i;
HANDLE hProcessHeap;
hProcessHeap = GetProcessHeap();
HeapFree(hProcessHeap, 0, enumWin32->adapterNames);
HeapFree(hProcessHeap, 0, enumWin32->devsFound);
HeapFree(hProcessHeap, 0, enumWin32->screenCount);
/* Free modes for each adapter-screen pair */
for(i=0; i<enumWin32->ASPairCount; i++)
HeapFree(hProcessHeap, 0, enumWin32->ASMap[i].vmodes);
HeapFree(hProcessHeap, 0, enumWin32->ASMap);
HeapFree(hProcessHeap, 0, enumWin32);
}
struct SST_WM_EnumFuncs Win32_EnumFuncs = {
Win32_CreateGraphicsEnumerator,
Win32_GetEnumAdapterCount,
Win32_GetEnumAdapterName,
Win32_GetEnumScreenCount,
Win32_GetEnumVideoModes,
Win32_GetEnumCurrentVideoMode,
Win32_DestroyGraphicsEnumerator
};

View File

@@ -0,0 +1,90 @@
/*
SST_WMEvent_Win32.c
Author: Patrick Baggett <ptbaggett@762studios.com>
Created: 6/5/2012
Purpose:
Window event functions
License:
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
*/
#include <SST/SST_WMEvent.h>
#include "Win32Private.h"
#include "../APIPrivate.h"
static void copyAndRemoveUserEvent(SST_DisplayTarget_Win32* displayTarget, SST_WMEvent* eventReturn);
/*************************************************************************/
static int Win32_GetEvent(SST_DisplayTarget target, SST_WMEvent* eventReturn)
{
MSG msg;
SST_DisplayTarget_Win32* displayTarget = (SST_DisplayTarget_Win32*)target;
SST_Window_Win32* win;
int found = 0;
/* First, get Win32 messages and dispatch to winproc. We
do this immediately, otherwise the OS thinks we've hung. */
while(PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
/* Check for user events */
if(RemoveFromEQ(&displayTarget->userEventQueue, eventReturn))
return 1;
/* Now check each window's event queue */
win = displayTarget->firstWindow;
while(win)
{
if(RemoveFromEQ(&win->eventQueue, eventReturn))
{
found = 1;
break;
}
win = win->next;
}
return found;
}
/*************************************************************************/
/* Platform-specific code dealing with user event queue */
static void Win32_lockUserEventQueue(SST_DisplayTarget target)
{
SST_DisplayTarget_Win32* displayTarget = (SST_DisplayTarget_Win32*)target;
EnterCriticalSection(&displayTarget->userEventLock);
}
static void Win32_unlockUserEventQueue(SST_DisplayTarget target)
{
SST_DisplayTarget_Win32* displayTarget = (SST_DisplayTarget_Win32*)target;
LeaveCriticalSection(&displayTarget->userEventLock);
}
static EventQueue* Win32_getUserEventQueue(SST_DisplayTarget target)
{
SST_DisplayTarget_Win32* displayTarget = (SST_DisplayTarget_Win32*)target;
return &displayTarget->userEventQueue;
}
const struct SST_WM_EventFuncs Win32_EventFuncs = {
Win32_GetEvent,
Win32_getUserEventQueue,
Win32_lockUserEventQueue,
Win32_unlockUserEventQueue
};

View File

@@ -0,0 +1,26 @@
/*
SST_WMNonPortable_Win32.c
Author: Patrick Baggett <ptbaggett@762studios.com>
Created: 6/1/2012
Purpose:
Non-portable API calls in libsst-wm for the Win32 platform
License:
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
*/
#include "Win32Private.h"
HWND SST_WM_GetHWNDWin32(SST_Window window)
{
return ((SST_Window_Win32*)window)->hWnd;
}

View File

@@ -0,0 +1,848 @@
/*
SST_WMOpenGL_Win32.c
Author: Patrick Baggett <ptbaggett@762studios.com>
Created: 6/28/2012
Purpose:
OpenGL context creation
License:
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
*/
#include <SST/SST_WMOpenGL.h>
#include "Win32Private.h"
#include "../APIPrivate.h"
#include <GL/gl.h>
/*
NOTE: Herein lines madness. Abandon faith all ye who would enter.
Windows has been notoriously bad at keeping up with OpenGL progress. In particular, to get a modern context,
one must:
1) Create a fake window
2) Select a fake pixel format
4) Create a fake GL context
5) Bind that context
6) Resolve the wglGetExtensionStringARB() symbol
7) Call wglGetExtensionStringARB() and check for various WGL extensions
8) Resolve required extensions
9) Select a real pixel format
10) Create a real context
This doesn't even consider the complexity of handling issues such as pushing/popping the current OpenGL context
so that calling SST_WM_CreateOpenGLContext() doesn't result in losing the currently active context, or providing
fallbacks in case the person wants GL <= 2.x and doesn't have the GL >= 3.x WGL extensions. So much madness.
*/
/* Create a temporary 1x1 window that is invisible */
static HWND createTmpWindow(SST_DisplayTarget_Win32* copyFrom);
/* Create OpenGL context that would probably work with Windows 95 */
static SST_OpenGLContext legacyCreateOpenGLContext(SST_DisplayTarget_Win32* win, const SST_OpenGLContextAttributes* attribs, SST_OpenGLContextAttributes* selectedAttribsReturn, HMODULE opengl32, WGLFunctions* wgl);
/* Use wglChoosePixelFormatARB() to find a useful pixel format */
static int modernChoosePixelFormat(HDC hDC, WGLFunctions* wgl, const SST_OpenGLContextAttributes* attribs);
/* Get selected pixel format return value information */
static void modernGetPixelInfo(HDC hDC, int format, WGLFunctions* wgl, SST_OpenGLContextAttributes* selectedAttribsReturn);
static int* addAttr(int* ptr, int x, int y)
{
ptr[0] = x;
ptr[1] = y;
return ptr + 2;
}
/*************************************************************************/
static SST_OpenGLContext Win32_CreateOpenGLContext(SST_DisplayTarget target, SST_WMOpenGLType apiType, const SST_OpenGLContextAttributes* attribs, SST_OpenGLContextAttributes* selectedAttribsReturn)
{
HMODULE opengl32;
HWND hTmpWnd, hTmpWnd2;
HDC hTmpDC, hTmpDC2, oldDC = NULL;
HGLRC hTmpCtx, oldRC = NULL;
PIXELFORMATDESCRIPTOR pfd;
WGLFunctions wgl;
SST_DisplayTarget_Win32* displayTarget = (SST_DisplayTarget_Win32*)target;
SST_OpenGLContext_Win32* glctx;
int format;
int OK;
/* Win32 doesn't have OpenGL ES AFAIK. */
if(apiType == SSTGL_OPENGL_ES)
return NULL;
/* Load opengl32.dll */
opengl32 = LoadLibraryA("opengl32.dll");
if(opengl32 == NULL)
return NULL;
memset(&wgl, 0, sizeof(wgl));
resolveWGLSymbols(opengl32, &wgl);
/*
OK, this next code attempts to create an invisible window which can then be used
to bind a fake GL context to. This allows us to grab WGL extensions for advanced
OpenGL context creation (i.e. forward compatible contexts). If this isn't supported,
then old style context creation will be used as a fallback.
*/
hTmpWnd = createTmpWindow(displayTarget);
if(hTmpWnd == NULL)
{
FreeLibrary(opengl32);
return NULL;
}
/* Grab the window's DC */
hTmpDC = GetDC(hTmpWnd);
/* Initialize pixel format descriptor to find 24-bit color / double-buffered / render to window */
memset(&pfd, 0, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_DRAW_TO_WINDOW;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cRedBits = 8;
pfd.cGreenBits = 8;
pfd.cBlueBits = 8;
pfd.cDepthBits = 24;
pfd.iLayerType = PFD_MAIN_PLANE;
/* Attempt to find a matching pixel format */
OK = 0;
hTmpCtx = NULL;
format = ChoosePixelFormat(hTmpDC, &pfd);
if(format > 0)
{
/* Attempt to set the pixel format */
OK = (int)SetPixelFormat(hTmpDC, format, &pfd);
if(OK)
{
/* Create the context */
hTmpCtx = wgl.CreateContext(hTmpDC);
OK = (hTmpCtx != NULL);
/* Bind it */
if(OK)
{
/* Save old context */
oldDC = wgl.GetCurrentDC();
oldRC = wgl.GetCurrentContext();
OK = (int)wgl.MakeCurrent(hTmpDC, hTmpCtx);
}
}
}
/* Failed in any of these steps? */
if(!OK)
{
if(hTmpCtx != NULL)
{
wgl.DeleteContext(hTmpCtx);
/* Restore old context (since wglMakeCurrent() failed with new context) */
wgl.MakeCurrent(oldDC, oldRC);
}
ReleaseDC(hTmpWnd, hTmpDC);
DestroyWindow(hTmpWnd);
FreeLibrary(opengl32);
return NULL;
}
/* Resolve context-specific strings */
resolveWGLExtSymbols(hTmpDC, &wgl);
/* Only "legacy" context functions allowed? */
if(wgl.ChoosePixelFormatARB == NULL || wgl.CreateContextAttribsARB == NULL)
{
/* Remove temporary context / window, they aren't needed for legacy contexts */
wgl.MakeCurrent(oldDC, oldRC);
wgl.DeleteContext(hTmpCtx);
ReleaseDC(hTmpWnd, hTmpDC);
DestroyWindow(hTmpWnd);
/* Just make a legacy context */
return legacyCreateOpenGLContext(displayTarget, attribs, selectedAttribsReturn, opengl32, &wgl);
}
else /* "Modern" context creation */
{
#define MAX_CTX_ATTRS 16
int contextAttrs[MAX_CTX_ATTRS];
int* ptr;
HGLRC hGLRC;
size_t i;
int ctxFlags = 0;
/* Create another set of temporary handles. Since we already called
SetPixelFormat() on hTmpDC, we can't set it to the new pixel format
that we picked with modernChoosePixelFormat(). Thus, if we do wglCreateContext() with
hTmpDC, we'll get an invalid context. Instead, create a new window/DC and use the new
pixel format. */
hTmpWnd2 = createTmpWindow(displayTarget);
hTmpDC2 = GetDC(hTmpWnd2);
/* Choose a pixel format */
format = modernChoosePixelFormat(hTmpDC2, &wgl, attribs);
if(format == 0)
{
wgl.MakeCurrent(oldDC, oldRC);
wgl.DeleteContext(hTmpCtx);
ReleaseDC(hTmpWnd, hTmpDC);
DestroyWindow(hTmpWnd);
}
SetPixelFormat(hTmpDC2, format, NULL);
i = 0;
if(attribs->debugEnabled)
ctxFlags |= WGL_CONTEXT_DEBUG_BIT_ARB;
ptr = contextAttrs;
ptr = addAttr(ptr, WGL_CONTEXT_MAJOR_VERSION_ARB, attribs->contextVersionMajor);
ptr = addAttr(ptr, WGL_CONTEXT_MINOR_VERSION_ARB, attribs->contextVersionMinor);
ptr = addAttr(ptr, WGL_CONTEXT_LAYER_PLANE_ARB, 0);
/* If WGL_ARB_create_context_profile is supported, add a profile mask */
if(wgl.supportsProfiles)
ptr = addAttr(ptr, WGL_CONTEXT_PROFILE_MASK_ARB, (attribs->legacyEnabled ? WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB : WGL_CONTEXT_CORE_PROFILE_BIT_ARB));
/* Legacy-free? */
if(!attribs->legacyEnabled)
ctxFlags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
/* Debug mode? */
if(attribs->debugEnabled)
ctxFlags |= WGL_CONTEXT_DEBUG_BIT_ARB;
ptr = addAttr(ptr, WGL_CONTEXT_FLAGS_ARB, ctxFlags);
*ptr = 0;
/* Create the OpenGL context */
hGLRC = wgl.CreateContextAttribsARB(hTmpDC2, NULL, contextAttrs);
glctx = (SST_OpenGLContext_Win32*)HeapAlloc(GetProcessHeap(), 0, sizeof(SST_OpenGLContext_Win32));
if(hGLRC == NULL || glctx == NULL)
{
if(hGLRC != NULL)
wgl.DeleteContext(hGLRC);
if(glctx != NULL)
HeapFree(GetProcessHeap(), 0, glctx);
wgl.MakeCurrent(oldDC, oldRC);
wgl.DeleteContext(hTmpCtx);
ReleaseDC(hTmpWnd, hTmpDC);
DestroyWindow(hTmpWnd);
ReleaseDC(hTmpWnd2, hTmpDC2);
DestroyWindow(hTmpWnd2);
return NULL;
}
glctx->displayTarget = displayTarget;
glctx->context = hGLRC;
glctx->hDCActive = NULL;
glctx->hSlaveWnd = NULL;
glctx->opengl32 = opengl32;
glctx->pixelFormat = format;
glctx->wgl = wgl;
glctx->isLegacy = FALSE;
glctx->ctxVersion[0] = (short)attribs->contextVersionMajor;
glctx->ctxVersion[1] = (short)attribs->contextVersionMinor;
glctx->legacyEnabled = attribs->legacyEnabled? TRUE : FALSE;
glctx->debugEnabled = attribs->debugEnabled? TRUE : FALSE;
if(selectedAttribsReturn)
{
modernGetPixelInfo(hTmpDC2, format, &wgl, selectedAttribsReturn);
selectedAttribsReturn->contextVersionMajor = attribs->contextVersionMajor;
selectedAttribsReturn->contextVersionMinor = attribs->contextVersionMinor;
selectedAttribsReturn->legacyEnabled = (attribs->legacyEnabled ? 1 : 0);
selectedAttribsReturn->debugEnabled = (ctxFlags & WGL_CONTEXT_DEBUG_BIT_ARB ? 1 : 0);
}
/* Delete temporary window */
wgl.MakeCurrent(oldDC, oldRC);
wgl.DeleteContext(hTmpCtx);
ReleaseDC(hTmpWnd, hTmpDC);
DestroyWindow(hTmpWnd);
ReleaseDC(hTmpWnd2, hTmpDC2);
DestroyWindow(hTmpWnd2);
}
return glctx;
}
/*************************************************************************/
static SST_OpenGLContext Win32_CreateSlaveOpenGLContext(SST_OpenGLContext masterGLContext)
{
SST_OpenGLContext_Win32* master = (SST_OpenGLContext_Win32*)masterGLContext;
SST_OpenGLContext_Win32* slave;
HWND hInvisWnd;
FindMonitorInfo fmi;
HDC hDC = NULL;
HGLRC hGLRC = NULL;
BOOL ok;
const WGLFunctions* wgl = &master->wgl;
/* Since the slave context merely needs to be on *a* screen controlled by the display target, just use the first one */
findMonitor(&master->displayTarget->devs[0], &fmi);
if(!fmi.foundIt)
return NULL;
/* Make 1x1 hidden window that doesn't appear in the titlebar and has no borders */
hInvisWnd = CreateWindowExA(WS_EX_TOOLWINDOW, SST_WINCLASS, "(temp)", WS_POPUP, fmi.left, fmi.top, 1, 1, NULL, NULL, GetModuleHandleA(NULL), NULL);
if(hInvisWnd == NULL)
return NULL;
ShowWindow(hInvisWnd, SW_HIDE);
/* Allocate context structure */
slave = HeapAlloc(GetProcessHeap(), 0, sizeof(SST_OpenGLContext_Win32));
if(slave == NULL)
{
DestroyWindow(hInvisWnd);
return NULL;
}
ok = FALSE;
hDC = GetDC(hInvisWnd);
/* If master context was done using legacy functions... */
if(master->isLegacy)
{
PIXELFORMATDESCRIPTOR pfd;
/* Get the PFD for this pixel foramt */
DescribePixelFormat(hDC, master->pixelFormat, sizeof(pfd), &pfd);
/* Set it */
if(SetPixelFormat(hDC, master->pixelFormat, &pfd))
{
hGLRC = wgl->CreateContext(hDC);
if(hGLRC != NULL)
{
if(wgl->ShareLists(master->context, hGLRC))
ok = TRUE;
}
}
}
else /* Modern OpenGL functions */
{
if(SetPixelFormat(hDC, master->pixelFormat, NULL))
{
HGLRC oldRC;
HDC oldDC;
oldDC = wgl->GetCurrentDC();
oldRC = wgl->GetCurrentContext();
/* Bind master context so we can call wgl extension functions. Note that we're using
the invisible window's HDC. This should work because we just set the pixel format to be
identical to the master context, meaning they should be compatible */
if(wgl->MakeCurrent(hDC, master->context))
{
int attrs[16];
int* ptr = attrs;
int ctxFlags;
if(master->debugEnabled)
ctxFlags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
ptr = addAttr(ptr, WGL_CONTEXT_MAJOR_VERSION_ARB, master->ctxVersion[0]);
ptr = addAttr(ptr, WGL_CONTEXT_MINOR_VERSION_ARB, master->ctxVersion[1]);
ptr = addAttr(ptr, WGL_CONTEXT_LAYER_PLANE_ARB, 0);
/* If WGL_ARB_create_context_profile is supported, add a profile mask */
if(wgl->supportsProfiles)
ptr = addAttr(ptr, WGL_CONTEXT_PROFILE_MASK_ARB, (master->legacyEnabled ? WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB : WGL_CONTEXT_CORE_PROFILE_BIT_ARB));
/* Legacy-free? */
if(!master->legacyEnabled)
ctxFlags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
/* Debug mode? */
if(master->debugEnabled)
ctxFlags |= WGL_CONTEXT_DEBUG_BIT_ARB;
ptr = addAttr(ptr, WGL_CONTEXT_FLAGS_ARB, ctxFlags);
*ptr = 0;
/* Create the new context, sharing with the master context */
hGLRC = wgl->CreateContextAttribsARB(hDC, master->context, attrs);
if(hGLRC != NULL)
ok = TRUE;
}
/* Restore old context */
wgl->MakeCurrent(oldDC, oldRC);
}
}
/* Done with the DC */
if(hDC)
ReleaseDC(hInvisWnd, hDC);
/* Did we succeed in making the slave context? */
if(ok)
{
/* Set up slave context info */
slave->context = hGLRC;
slave->displayTarget = master->displayTarget;
slave->hDCActive = NULL;
slave->hSlaveWnd = hInvisWnd;
slave->isLegacy = master->isLegacy;
slave->opengl32 = LoadLibraryA("opengl32.dll"); /* Need to increase ref count for opengl32.dll */
slave->pixelFormat = master->pixelFormat;
slave->wgl = master->wgl; /* TODO: should be safe, right? context dependent, but same device/GL impl... */
slave->ctxVersion[0] = master->ctxVersion[0];
slave->ctxVersion[1] = master->ctxVersion[1];
slave->legacyEnabled = master->legacyEnabled;
slave->debugEnabled = master->debugEnabled;
}
else /* Failure, clean up */
{
if(hGLRC)
master->wgl.DeleteContext(hGLRC);
DestroyWindow(hInvisWnd);
HeapFree(GetProcessHeap(), 0, slave);
slave = NULL;
}
return slave;
}
/*************************************************************************/
static void Win32_SwapOpenGLBuffers(SST_OpenGLContext ctx)
{
SST_OpenGLContext_Win32* glctx = (SST_OpenGLContext_Win32*)ctx;
SwapBuffers(glctx->hDCActive);
}
/*************************************************************************/
static int Win32_BindOpenGLContext(SST_OpenGLContext ctx, SST_Window window)
{
SST_OpenGLContext_Win32* glctx = (SST_OpenGLContext_Win32*)ctx;
SST_Window_Win32* win = (SST_Window_Win32*)window;
/* Unbinding a context from a thread */
if(ctx == NULL)
{
HMODULE opengl32;
/* We actually need some WGL symbols. Oops. That's OK, we can just grab the symbol */
pf_wglMakeCurrent sst_wglMakeCurrent;
pf_wglGetCurrentDC sst_wglGetCurrentDC;
/* We use LoadLibrary() instead of GetModuleHandle() so that we don't unmap the DLL
in the middle of using it. */
opengl32 = LoadLibraryA("opengl32.dll");
if(opengl32)
{
HDC hDC;
HWND hWnd;
sst_wglMakeCurrent = (pf_wglMakeCurrent)GetProcAddress(opengl32, "wglMakeCurrent");
sst_wglGetCurrentDC = (pf_wglGetCurrentDC)GetProcAddress(opengl32, "wglGetCurrentDC");
/* Release the DC */
hDC = sst_wglGetCurrentDC();
hWnd = WindowFromDC(hDC);
if(hDC != NULL && hWnd != NULL)
ReleaseDC(hWnd, hDC);
/* OK, unbind it */
sst_wglMakeCurrent(NULL, NULL);
FreeLibrary(opengl32);
}
/* OK */
return 1;
}
else /* Have a valid context */
{
HDC hDC, oldDC;
HGLRC oldRC;
if( (glctx->hSlaveWnd != NULL && win != NULL) || /* Slave context provided a SST_Window */
(glctx->hSlaveWnd == NULL && win == NULL) /* Master context with no SST_Window */
)
{
/* Both are illegal according the the functions */
return 0;
}
/* Get old context info */
oldDC = glctx->wgl.GetCurrentDC();
oldRC = glctx->wgl.GetCurrentContext();
/* First, are we re-binding the same context? If so, then just return "success" */
if( oldDC != NULL && oldRC != NULL &&
oldDC == glctx->hDCActive && oldRC == glctx->context)
return 1;
/* Binding master context to a window */
if(win != NULL)
{
hDC = GetDC(win->hWnd);
/* Haven't set the pixel format for this window yet? */
if(!win->setPixelFormat)
{
PIXELFORMATDESCRIPTOR pfd;
DescribePixelFormat(hDC, glctx->pixelFormat, sizeof(pfd), &pfd);
if(SetPixelFormat(hDC, glctx->pixelFormat, &pfd))
{
/* OK, mark that we've set this window's pixel format once, and we won't do it again. */
win->setPixelFormat = TRUE;
}
else
{
/* Failed to set this window's pixel format, so we can't do much */
return 0;
}
}
}
else /* Bind slave context */
{
hDC = GetDC(glctx->hSlaveWnd);
}
/* Make current */
if(!glctx->wgl.MakeCurrent(hDC, glctx->context))
{
/* Failed -- restore old context settings */
glctx->wgl.MakeCurrent(oldDC, oldRC);
return 0;
}
/* Since we were successful, release the old DC */
if(oldDC)
{
HWND hWnd = WindowFromDC(oldDC);
if(hWnd)
ReleaseDC(hWnd, oldDC);
}
/* Save window / DC */
glctx->hDCActive = hDC;
}
return 1;
}
/*************************************************************************/
static void Win32_DestroyOpenGLContext(SST_OpenGLContext ctx)
{
SST_OpenGLContext_Win32* glctx = (SST_OpenGLContext_Win32*)ctx;
if(glctx->hSlaveWnd)
DestroyWindow(glctx->hSlaveWnd);
glctx->wgl.DeleteContext(glctx->context);
FreeLibrary(glctx->opengl32);
HeapFree(GetProcessHeap(), 0, glctx);
}
/*************************************************************************/
static HWND createTmpWindow(SST_DisplayTarget_Win32* copyFrom)
{
HWND hTmpWnd;
FindMonitorInfo fmi;
/* Find the first monitor */
findMonitor(&copyFrom->devs[0], &fmi);
if(!fmi.foundIt)
return NULL;
/* Make 1x1 hidden window that doesn't appear in the titlebar and has no borders */
hTmpWnd = CreateWindowExA(WS_EX_TOOLWINDOW, SST_WINCLASS, "(temp)", WS_POPUP, fmi.left, fmi.top, 1, 1, NULL, NULL, GetModuleHandleA(NULL), NULL);
if(hTmpWnd == NULL)
return NULL;
ShowWindow(hTmpWnd, SW_HIDE);
return hTmpWnd;
}
/*************************************************************************/
static SST_OpenGLContext legacyCreateOpenGLContext(SST_DisplayTarget_Win32* displayTarget, const SST_OpenGLContextAttributes* attribs, SST_OpenGLContextAttributes* selectedAttribsReturn, HMODULE opengl32, WGLFunctions* wgl)
{
PIXELFORMATDESCRIPTOR pfd;
HWND hWnd;
HDC hDC, oldDC = NULL;
HGLRC hGLRC, oldRC = NULL;
int format;
int major, minor;
const char* version;
const GLubyte* (APIENTRY *sst_glGetString)(GLenum);
SST_OpenGLContext_Win32* ctx;
BYTE rgb[3] = { 1, 1, 1 };
/* No multisample support, but 2x or greater MSAA requested -> fail */
if(attribs->multisampleFactor > 1)
{
FreeLibrary(opengl32);
return NULL;
}
/* Allocate a GL context */
ctx = (SST_OpenGLContext_Win32*)HeapAlloc(GetProcessHeap(), 0, sizeof(SST_OpenGLContext_Win32));
if(ctx == NULL)
{
FreeLibrary(opengl32);
return NULL;
}
/* Get window DC */
hWnd = createTmpWindow(displayTarget);
hDC = GetDC(hWnd);
/* We can't do MSAA at all, though */
if(attribs->multisampleFactor > 1)
{
FreeLibrary(opengl32);
return NULL;
}
/* Figure out how to set up color bits. These are minimums. MSDN states that they
are "Not used", but be safe anyways. */
if(attribs->colorBits == 24)
{
rgb[0] = rgb[1] = rgb[2] = 8;
}
else if(attribs->colorBits == 16 || attribs->colorBits == 15)
{
/* 555 or 565 color. Set all to 5 since "minimum" of 5 allows for 565 too */
rgb[0] = rgb[1] = rgb[2] = 5;
}
/* Initialize the PFD */
memset(&pfd, 0, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_DRAW_TO_WINDOW | (attribs->stereoEnabled? PFD_STEREO : 0);
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = attribs->colorBits;
pfd.cAlphaBits = attribs->alphaBits;
pfd.cRedBits = rgb[0];
pfd.cGreenBits = rgb[1];
pfd.cBlueBits = rgb[2];
pfd.cDepthBits = attribs->depthBits;
pfd.cStencilBits = attribs->stencilBits;
pfd.iLayerType = PFD_MAIN_PLANE;
/* Get a compatiable pixel format */
format = ChoosePixelFormat(hDC, &pfd);
if(format == 0)
{
ReleaseDC(hWnd, hDC);
DestroyWindow(hWnd);
FreeLibrary(opengl32);
return NULL;
}
/* Get info about the pixel format we're using */
if(selectedAttribsReturn != NULL)
{
PIXELFORMATDESCRIPTOR spfd;
DescribePixelFormat(hDC, format, sizeof(spfd), &spfd);
selectedAttribsReturn->alphaBits = pfd.cAlphaBits;
selectedAttribsReturn->colorBits = pfd.cColorBits;
selectedAttribsReturn->contextVersionMajor = 0;
selectedAttribsReturn->contextVersionMinor = 0;
selectedAttribsReturn->depthBits = pfd.cDepthBits;
selectedAttribsReturn->multisampleFactor = 1;
selectedAttribsReturn->stencilBits = pfd.cStencilBits;
selectedAttribsReturn->stereoEnabled = (pfd.dwFlags & PFD_STEREO);
}
/* Set the pixel format for the device */
if(!SetPixelFormat(hDC, format, &pfd))
{
ReleaseDC(hWnd, hDC);
DestroyWindow(hWnd);
FreeLibrary(opengl32);
return NULL;
}
/* Create the GL context */
hGLRC = wgl->CreateContext(hDC);
if(hGLRC == NULL)
{
ReleaseDC(hWnd, hDC);
DestroyWindow(hWnd);
FreeLibrary(opengl32);
return NULL;
}
/* Activate the context to see if correct version was set */
oldDC = wgl->GetCurrentDC();
oldRC = wgl->GetCurrentContext();
if(!wgl->MakeCurrent(hDC, hGLRC))
{
wgl->DeleteContext(hGLRC);
ReleaseDC(hWnd, hDC);
DestroyWindow(hWnd);
FreeLibrary(opengl32);
return NULL;
}
/* Resolve glGetString() and call it to check version number matching */
sst_glGetString = (const GLubyte* (APIENTRY*)(GLenum name))GetProcAddress(opengl32, "glGetString");
version = (const char*)sst_glGetString(GL_VERSION);
major = (int)(version[0] - '0'); /* "X.Y" -> [0]:major, [2]:minor */
minor = (int)(version[2] - '0');
/* Got version info. Detach our context, restore old one */
wgl->MakeCurrent(oldDC, oldRC);
/* Save context version information */
if(selectedAttribsReturn != NULL)
{
selectedAttribsReturn->contextVersionMajor = (uint8_t)major;
selectedAttribsReturn->contextVersionMinor = (uint8_t)minor;
}
/* If the major version is too low or the major version is OK, but the minor version is lacking, then fail */
if(major < attribs->contextVersionMajor ||
(major == attribs->contextVersionMajor && minor < attribs->contextVersionMinor))
{
wgl->DeleteContext(hGLRC);
ReleaseDC(hWnd, hDC);
DestroyWindow(hWnd);
FreeLibrary(opengl32);
return NULL;
}
ReleaseDC(hWnd, hDC);
DestroyWindow(hWnd);
ctx->displayTarget = displayTarget;
ctx->hSlaveWnd = NULL;
ctx->hDCActive = NULL;
ctx->opengl32 = opengl32;
ctx->wgl = *wgl;
ctx->pixelFormat = format;
ctx->context = hGLRC;
ctx->isLegacy = TRUE;
ctx->ctxVersion[0] = (short)major;
ctx->ctxVersion[1] = (short)minor;
ctx->legacyEnabled = FALSE; /* These refer to profiles in GL >= 3.x contexts. Here they are meaningless, so set to false */
ctx->debugEnabled = FALSE;
return (SST_OpenGLContext)ctx;
}
/*************************************************************************/
static int modernChoosePixelFormat(HDC hDC, WGLFunctions* wgl, const SST_OpenGLContextAttributes* attribs)
{
int attrs[MAX_GL_ATTRS];
int format;
int* ptr = attrs;
UINT nrFormats;
BOOL ok;
/* Set up basic attributes */
ptr = addAttr(ptr, WGL_DRAW_TO_WINDOW_ARB, GL_TRUE);
ptr = addAttr(ptr, WGL_SUPPORT_OPENGL_ARB, GL_TRUE);
ptr = addAttr(ptr, WGL_DOUBLE_BUFFER_ARB, GL_TRUE);
ptr = addAttr(ptr, WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB);
ptr = addAttr(ptr, WGL_COLOR_BITS_ARB, attribs->colorBits);
ptr = addAttr(ptr, WGL_ALPHA_BITS_ARB, attribs->alphaBits);
ptr = addAttr(ptr, WGL_DEPTH_BITS_ARB, attribs->depthBits);
ptr = addAttr(ptr, WGL_STENCIL_BITS_ARB, attribs->stencilBits);
ptr = addAttr(ptr, WGL_STEREO_ARB, attribs->stereoEnabled ? GL_TRUE : GL_FALSE); /* Must be GL_TRUE || GL_FALSE, not just "non-zero" */
/* Multisampling requested? */
if(attribs->multisampleFactor > 1)
{
/* Is it supported? */
if(wgl->supportsMultisample)
{
ptr = addAttr(ptr, WGL_SAMPLE_BUFFERS_ARB, 1);
ptr = addAttr(ptr, WGL_SAMPLES_ARB, attribs->multisampleFactor);
}
else
return 0;
}
*ptr = 0;
nrFormats = 0;
/* Attempt to get pixel formats that match ours */
ok = wgl->ChoosePixelFormatARB(hDC, attrs, NULL, 1, &format, &nrFormats);
/* This can return success with 0 formats, so check if it was actually "successful" in finding > 0 formats */
if(!ok || nrFormats == 0)
return 0;
return format;
}
/*************************************************************************/
static void modernGetPixelInfo(HDC hDC, int format, WGLFunctions* wgl, SST_OpenGLContextAttributes* selectedAttribsReturn)
{
int attrs[8];
int values[8] = { 0 };
int attrCount;
attrs[0] = WGL_COLOR_BITS_ARB;
attrs[1] = WGL_ALPHA_BITS_ARB;
attrs[2] = WGL_DEPTH_BITS_ARB;
attrs[3] = WGL_STENCIL_BITS_ARB;
attrs[4] = WGL_STEREO_ARB;
attrs[5] = WGL_SAMPLE_BUFFERS_ARB;
/* Check multisample */
attrCount = (wgl->supportsMultisample ? 6 : 5);
wgl->GetPixelFormatAttribivARB(hDC, format, 0, attrCount, attrs, values);
selectedAttribsReturn->colorBits = (uint8_t)values[0];
selectedAttribsReturn->alphaBits = (uint8_t)values[1];
selectedAttribsReturn->depthBits = (uint8_t)values[2];
selectedAttribsReturn->stencilBits = (uint8_t)values[3];
selectedAttribsReturn->stereoEnabled = (uint8_t)values[4];
if(wgl->supportsMultisample)
selectedAttribsReturn->multisampleFactor = (uint8_t)values[5];
else
selectedAttribsReturn->multisampleFactor = 1;
}
const struct SST_WM_OpenGLFuncs Win32_OpenGLFuncs = {
Win32_CreateOpenGLContext,
Win32_CreateSlaveOpenGLContext,
Win32_SwapOpenGLBuffers,
Win32_BindOpenGLContext,
Win32_DestroyOpenGLContext
};

View File

@@ -0,0 +1,97 @@
/*
SST_WMRender_Win32.c
Author: Patrick Baggett <ptbaggett@762studios.com>
Created: 9/19/2012
Purpose:
Software rendering support (Win32)
License:
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
*/
#include "Win32Private.h"
#include "../APIPrivate.h"
#include <SST/SST_WMWindow.h>
/*************************************************************************/
static int Win32_EnableSoftwareRendering(SST_Window window)
{
SST_Window_Win32* win = (SST_Window_Win32*)window;
HDC hDC;
HDC hDCTmp;
HBITMAP hBMP;
RECT r;
hDCTmp = GetDC(win->hWnd);
hDC = CreateCompatibleDC(hDCTmp);
if(GetDeviceCaps(hDCTmp, BITSPIXEL) != 32)
{
DeleteDC(hDC);
return 0;
}
GetClientRect(win->hWnd, &r);
hBMP = CreateCompatibleBitmap(hDCTmp, r.right, r.bottom);
SelectObject(hDC, hBMP);
ReleaseDC(win->hWnd, hDCTmp);
win->softwareDC = hDC;
win->softwareImage = hBMP;
win->softwareBackbuffer = HeapAlloc(GetProcessHeap(), 0, (SIZE_T)r.right * (SIZE_T)r.bottom * 4);
win->softwarePitch = r.right * 4;
return 1;
}
/*************************************************************************/
static void Win32_DisableSoftwareRendering(SST_Window window)
{
SST_Window_Win32* win = (SST_Window_Win32*)window;
if(win->softwareBackbuffer)
HeapFree(GetProcessHeap(), 0, win->softwareBackbuffer);
if(win->softwareDC)
DeleteDC(win->softwareDC);
if(win->softwareImage)
DeleteObject(win->softwareImage);
}
/*************************************************************************/
static void* Win32_LockBackbuffer(SST_Window window, size_t* pitchReturn)
{
SST_Window_Win32* win = (SST_Window_Win32*)window;
*pitchReturn = win->softwarePitch;
return win->softwareBackbuffer;
}
/*************************************************************************/
static void Win32_UnlockBackbuffer(SST_Window window)
{
SST_Window_Win32* win = (SST_Window_Win32*)window;
InvalidateRect(win->hWnd, NULL, FALSE);
UpdateWindow(win->hWnd);
}
struct SST_WM_RenderFuncs Win32_RenderFuncs = {
Win32_EnableSoftwareRendering,
Win32_DisableSoftwareRendering,
Win32_LockBackbuffer,
Win32_UnlockBackbuffer
};

View File

@@ -0,0 +1,72 @@
/*
SST_WMVideoMode_Win32.c
Author: Patrick Baggett <ptbaggett@762studios.com>
Created: 6/26/2012
Purpose:
Video mode setting functions
License:
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
*/
#include "Win32Private.h"
#include "../APIPrivate.h"
/*************************************************************************/
static int Win32_SetVideoModeOnScreen(SST_DisplayTarget target, size_t screenIndex, const SST_VideoMode* vmode)
{
DEVMODEA devMode;
SST_DisplayTarget_Win32* displayTarget = (SST_DisplayTarget_Win32*)target;
const DISPLAY_DEVICEA* dev = &displayTarget->devs[screenIndex];
memset(&devMode, 0, sizeof(devMode));
devMode.dmSize = sizeof(devMode);
devMode.dmBitsPerPel = vmode->bpp;
devMode.dmPelsWidth = vmode->width;
devMode.dmPelsHeight = vmode->height;
devMode.dmDisplayFrequency = vmode->refreshRate;
devMode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
return (ChangeDisplaySettingsExA(dev->DeviceName, &devMode, NULL, CDS_FULLSCREEN, NULL) == DISP_CHANGE_SUCCESSFUL);
}
/*************************************************************************/
static int Win32_GetVideoModeOnScreen(SST_DisplayTarget target, size_t screenIndex, SST_VideoMode* vmodeReturn)
{
SST_DisplayTarget_Win32* displayTarget = (SST_DisplayTarget_Win32*)target;
const DISPLAY_DEVICEA* dev = &displayTarget->devs[screenIndex];
DEVMODEA devMode;
int ok = 0;
devMode.dmSize = sizeof(devMode);
if(EnumDisplaySettingsA(dev->DeviceName, ENUM_CURRENT_SETTINGS, &devMode))
{
vmodeReturn->bpp = devMode.dmBitsPerPel;
vmodeReturn->width = devMode.dmPelsWidth;
vmodeReturn->height = devMode.dmPelsHeight;
vmodeReturn->refreshRate = devMode.dmDisplayFrequency;
if(vmodeReturn->refreshRate == 1)
vmodeReturn->refreshRate = SST_DEFAULT_REFRESHRATE;
ok = 1;
}
return ok;
}
const struct SST_WM_VideoModeFuncs Win32_VideoModeFuncs = {
Win32_SetVideoModeOnScreen,
Win32_GetVideoModeOnScreen
};

View File

@@ -0,0 +1,667 @@
/*
SST_WMWindow_Win32.c
Author: Patrick Baggett <ptbaggett@762studios.com>
Created: 6/1/2012
Purpose:
Window creation (Win32)
License:
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
*/
#include <SST/SST_WMWindow.h>
#include "Win32Private.h"
#include "../EventQueue.h"
#include "../APIPrivate.h"
static LONG regRefCount = 0;
/*************************************************************************/
static SST_DisplayTarget Win32_CreateDisplayTarget(size_t adapterIndex, size_t screenIndexOrMultihead)
{
DISPLAY_DEVICEA* devs;
size_t devCount;
SST_DisplayTarget_Win32* displayTarget;
/* Get Win32 devices */
devs = get_win32devs(&devCount);
if(devs == NULL)
return NULL;
/* Filter the list */
devCount = filter_win32devs(devs, devCount, adapterIndex);
if(devCount == 0)
{
HeapFree(GetProcessHeap(), 0, devs);
return NULL;
}
/* Not doing multihead? */
if(screenIndexOrMultihead != SST_MULTIHEAD)
{
/* Does the screen index exceed the number of attached screens? */
if(screenIndexOrMultihead >= devCount)
{
/* Failure */
HeapFree(GetProcessHeap(), 0, devs);
return NULL;
}
}
/* Allocate a display target structure */
displayTarget = (SST_DisplayTarget_Win32*)HeapAlloc(GetProcessHeap(), 0, sizeof(SST_DisplayTarget_Win32));
if(displayTarget == NULL)
{
/* Failure */
HeapFree(GetProcessHeap(), 0, devs);
return NULL;
}
/* Initialize user event queue */
if(!InitEQ(&displayTarget->userEventQueue))
{
/* Failure */
HeapFree(GetProcessHeap(), 0, displayTarget);
HeapFree(GetProcessHeap(), 0, devs);
return NULL;
}
InitializeCriticalSection(&displayTarget->userEventLock);
displayTarget->devs = devs;
displayTarget->screenCount = devCount;
displayTarget->screenIndex = screenIndexOrMultihead;
displayTarget->firstWindow = NULL;
displayTarget->relativeMouse = FALSE;
/* Register class if the refcount == 1 */
if(InterlockedIncrement(&regRefCount) == 1)
{
WNDCLASSEXA wc;
memset(&wc, 0, sizeof(wc));
wc.cbSize = sizeof(wc);
wc.lpszClassName = SST_WINCLASS;
wc.hInstance = GetModuleHandleA(NULL);
wc.lpfnWndProc = libsstWndProc;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.style = CS_OWNDC;
wc.hbrBackground = (HBRUSH)(uintptr_t)(COLOR_WINDOW+1);
RegisterClassExA(&wc);
}
return (SST_DisplayTarget)displayTarget;
}
/*************************************************************************/
static size_t Win32_GetDisplayTargetScreenCount(SST_DisplayTarget target)
{
SST_DisplayTarget_Win32* displayTarget = (SST_DisplayTarget_Win32*)target;
/*
Multihead -> real screen count
Otherwise -> 1
*/
if(displayTarget->screenIndex == SST_MULTIHEAD)
return displayTarget->screenCount;
return 1;
}
/*************************************************************************/
static SST_Window Win32_CreateWindowOnScreen(SST_DisplayTarget target, size_t screenIndex, uint32_t x, uint32_t y, uint32_t width, uint32_t height, const char* title)
{
RECT r;
DWORD style, styleEx;
HWND hWnd;
SST_Window_Win32* win;
SST_DisplayTarget_Win32* displayTarget = (SST_DisplayTarget_Win32*)target;
FindMonitorInfo fmi;
/* Attempt to find the monitor associated with a display device */
findMonitor(&displayTarget->devs[screenIndex], &fmi);
/* Didn't find it? */
if(!fmi.foundIt)
return NULL;
/* Allocate SST window structure */
win = (SST_Window_Win32*)HeapAlloc(GetProcessHeap(), 0, sizeof(SST_Window_Win32));
if(win == NULL)
return NULL;
win->owner = displayTarget;
win->next = displayTarget->firstWindow;
win->owner = displayTarget;
win->isFullscreen = FALSE;
win->setPixelFormat = FALSE;
win->softwareBackbuffer = NULL;
win->softwareDC = NULL;
win->softwareImage = NULL;
win->softwarePitch = 0;
/* These styles may be useful later.
DWORD style = (Fullscreen ? WS_POPUP : WS_CAPTION | WS_SYSMENU);
DWORD styleEx = (Multihead2ndWindow? WS_EX_TOOLWINDOW : WS_EX_APPWINDOW);
*/
style = WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
styleEx = WS_EX_APPWINDOW;
/* Start the window relative to those coordinates */
r.top = 0;
r.left = 0;
r.bottom = (LONG)height;
r.right = (LONG)width;
AdjustWindowRectEx(&r, style, FALSE, styleEx);
if(r.top != 0)
{
LONG d = -r.top;
r.top = 0;
r.bottom += d;
}
if(r.left != 0)
{
LONG d = -r.left;
r.left = 0;
r.right += d;
}
r.top += fmi.top + (LONG)y;
r.left += fmi.left + (LONG)x;
r.bottom += r.top;
r.right += r.left;
hWnd = CreateWindowExA(
styleEx,
SST_WINCLASS,
title,
style,
r.left, r.top, /* XY position */
r.right-r.left, r.bottom-r.top, /* Position */
NULL, /* parent window */
NULL, GetModuleHandleA(NULL), win);
/* Failed to create window */
if(hWnd == NULL)
{
HeapFree(GetProcessHeap(), 0, win);
return NULL;
}
/* Save window info */
win->hWnd = hWnd;
ShowWindow(hWnd, SW_SHOW);
/* Link window as new root */
displayTarget->firstWindow = win;
return (SST_Window)win;
}
/*************************************************************************/
static SST_DisplayTarget Win32_GetWindowDisplayTarget(SST_Window window)
{
SST_Window_Win32* win = (SST_Window_Win32*)window;
return win->owner;
}
/*************************************************************************/
static void Win32_SetWindowText(SST_Window window, const char* titleBar)
{
SST_Window_Win32* win = (SST_Window_Win32*)window;
SetWindowTextA(win->hWnd, titleBar);
}
/*************************************************************************/
static void Win32_GetWindowRect(SST_Window window, SST_Rect* rectReturn)
{
SST_Window_Win32* win = (SST_Window_Win32*)window;
MONITORINFO info;
RECT rectClient;
RECT rectWin;
/* Get info about the monitor the window is on */
info.cbSize = sizeof(info);
if(!GetMonitorInfoA(MonitorFromWindow(win->hWnd, MONITOR_DEFAULTTONEAREST), &info))
return;
/* This returns (0,0) - (w,h). I don't know why they bother using a RECT instead
of a POINT structure, since the top/left is always (0,0). */
GetClientRect(win->hWnd, &rectClient);
GetWindowRect(win->hWnd, &rectWin);
rectReturn->x = (uint32_t)(rectWin.left - info.rcMonitor.left);
rectReturn->y = (uint32_t)(rectWin.top - info.rcMonitor.top);
rectReturn->width = (uint32_t)rectClient.right;
rectReturn->height = (uint32_t)rectClient.bottom;
}
/*************************************************************************/
static void Win32_MoveWindowOnScreen(SST_Window window, size_t screenIndex, uint32_t x, uint32_t y)
{
FindMonitorInfo fmi;
SST_Window_Win32* win = (SST_Window_Win32*)window;
SST_DisplayTarget_Win32* displayTarget = win->owner;
int px, py;
if(screenIndex != SST_SAME_SCREEN)
{
/* Atttempt to find the monitor associated with a display device */
findMonitor(&displayTarget->devs[screenIndex], &fmi);
/* Didn't find it? (monitor unplugged?) */
if(!fmi.foundIt)
return;
/* New position = monitor base + offset */
px = (int)x + (int)fmi.top;
py = (int)y + (int)fmi.left;
}
else /* Move relative to the same screen */
{
MONITORINFO info;
/* Get info about the monitor the window is on */
info.cbSize = sizeof(info);
if(!GetMonitorInfoA(MonitorFromWindow(win->hWnd, MONITOR_DEFAULTTONEAREST), &info))
return;
px = (int)x + (int)info.rcMonitor.left;
py = (int)y + (int)info.rcMonitor.top;
}
SetWindowPos(win->hWnd, NULL,
px, py,
0, 0,
SWP_NOOWNERZORDER | SWP_NOSIZE);
}
/*************************************************************************/
static void Win32_ResizeWindow(SST_Window window, uint32_t width, uint32_t height)
{
SST_Window_Win32* win = (SST_Window_Win32*)window;
RECT r;
BOOL hasMenu;
HWND hParent;
/* Get the client & window rect */
GetClientRect(win->hWnd, &r);
r.right = r.left + (LONG)width;
r.bottom = r.top + (LONG)height;
hParent = (HWND)GetWindowLongPtrA(win->hWnd, GWLP_HWNDPARENT);
if(hParent == NULL)
hasMenu = (GetMenu(win->hWnd) != NULL);
else
hasMenu = FALSE;
AdjustWindowRectEx(&r, GetWindowLongA(win->hWnd, GWL_STYLE), hasMenu, GetWindowLongA(win->hWnd, GWL_EXSTYLE));
SetWindowPos(win->hWnd, NULL, 0, 0, r.right - r.left, r.bottom - r.top, SWP_NOOWNERZORDER | SWP_NOMOVE);
}
/*************************************************************************/
static void Win32_SetWindowState(SST_Window window, SST_WMWindowState state, uint32_t param)
{
SST_Window_Win32* win = (SST_Window_Win32*)window;
switch(state)
{
case SSTWS_SHOWN:
{
int cmd;
if(param == 0)
cmd = SW_HIDE;
else
cmd = SW_SHOW;
ShowWindow(win->hWnd, cmd);
break;
}
/* Turn on/off resizeability */
case SSTWS_RESIZEABLE:
{
DWORD style;
RECT rect;
POINT tl, br;
/* Get info about the window */
style = GetWindowLongA(win->hWnd, GWL_STYLE);
GetClientRect(win->hWnd, &rect);
tl.x = rect.left;
tl.y = rect.top;
br.x = rect.right;
br.y = rect.bottom;
ClientToScreen(win->hWnd, &tl);
ClientToScreen(win->hWnd, &br);
rect.left = tl.x;
rect.top = tl.y;
rect.right = br.x;
rect.bottom = br.y;
if(param == 0)
style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
else
style |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
AdjustWindowRectEx(&rect, style, (GetMenu(win->hWnd) ? TRUE : FALSE), GetWindowLongA(win->hWnd, GWL_EXSTYLE));
SetWindowLongA(win->hWnd, GWL_STYLE, (LONG)style);
/* MSDN: "Certain window data is cached, so changes you make using SetWindowLong() will not take effect until you
call the SetWindowPos() function." So, let's update it... */
SetWindowPos(win->hWnd, NULL,
rect.left, rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
SWP_NOZORDER | SWP_FRAMECHANGED);
break;
}
case SSTWS_FULLSCREEN:
{
DWORD style;
/* Enabling fullscreen? */
if(param && !win->isFullscreen)
{
MONITORINFO info;
RECT r;
POINT pt;
/* Get current window style */
style = GetWindowLongA(win->hWnd, GWL_STYLE);
/* Save info about the window so when we exit fullscreen, we're good */
win->wp.length = sizeof(win->wp);
GetWindowPlacement(win->hWnd, &win->wp);
GetWindowRect(win->hWnd, &r);
pt.x = r.top;
pt.y = r.left;
/* Get info about the monitor the window is on */
info.cbSize = sizeof(MONITORINFO);
GetMonitorInfo(MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST), &info);
/* Remove title bar, caption, etc. */
SetWindowLong(win->hWnd, GWL_STYLE, style & (~WS_OVERLAPPEDWINDOW));
/* Move & resize to cover entire screen */
SetWindowPos(win->hWnd, HWND_TOP,
(int)info.rcMonitor.left, (int)info.rcMonitor.top, /* Position window in the top-left corner */
(int)(info.rcMonitor.right - info.rcMonitor.left), /* Width = width of monitor */
(int)(info.rcMonitor.bottom - info.rcMonitor.top), /* Height = height of monitor */
SWP_NOOWNERZORDER | SWP_FRAMECHANGED); /* Don't change*/
win->isFullscreen = TRUE;
}
else if(win->isFullscreen) /* Disabling fullscreen */
{
style = GetWindowLongA(win->hWnd, GWL_STYLE);
SetWindowLong(win->hWnd, GWL_STYLE, style | WS_OVERLAPPEDWINDOW);
/* Restore old window placement */
SetWindowPlacement(win->hWnd, &win->wp);
SetWindowPos(win->hWnd, NULL, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
win->isFullscreen = FALSE;
}
break;
}
case SSTWS_MINIMIZED:
{
int cmd;
if(param == 0)
{
SST_WMEvent* event;
cmd = SW_RESTORE;
/* Win32 doesn't send a restore message if pragmatically restored, so do it ourselves */
event = AllocSlotInEQ(&win->eventQueue);
if(event)
{
event->window = win;
event->type = SSTWMEVENT_RESTORED;
memset(&event->details, 0, sizeof(event->details));
}
}
else
cmd = SW_MINIMIZE;
ShowWindow(win->hWnd, cmd);
break;
}
default: break;
}
}
/*************************************************************************/
static void Win32_SetDisplayTargetState(SST_DisplayTarget target, SST_WMDisplayTargetState state, uint32_t param)
{
SST_DisplayTarget_Win32* displayTarget = (SST_DisplayTarget_Win32*)target;
switch(state)
{
case SSTDTS_RELMOUSE:
{
RAWINPUTDEVICE rawMouse = { 0x01, 0x02, 0, NULL }; /* Mouse: UsagePage = 1, Usage = 2 */
/* Disabling? */
if(displayTarget->relativeMouse && param == 0)
{
rawMouse.dwFlags |= RIDEV_REMOVE;
/* Attempt to unregister raw mouse. If successful, set 'relativeMouse' to FALSE */
if(RegisterRawInputDevices(&rawMouse, 1, sizeof(RAWINPUTDEVICE)))
{
displayTarget->relativeMouse = FALSE;
ClipCursor(NULL);
ShowCursor(TRUE);
}
}
else if(!displayTarget->relativeMouse && param != 0) /* Enabling? */
{
if(RegisterRawInputDevices(&rawMouse, 1, sizeof(RAWINPUTDEVICE)))
{
HWND active;
SST_Window_Win32* win;
displayTarget->relativeMouse = TRUE;
ShowCursor(FALSE);
/* Decide if this window belows to any in this display target, if so, lock cursor into it */
active = GetActiveWindow();
win = displayTarget->firstWindow;
while(win)
{
/* It does belong to this display target, so lock it into place */
if(win->hWnd == active)
{
LONG cx, cy;
RECT rect;
GetWindowRect(win->hWnd, &rect);
cx = (rect.left + rect.right) / 2;
cy = (rect.top + rect.bottom) / 2;
/* Ensure cursor cannot leave the center of the window */
rect.left = cx-1;
rect.right = cx+1;
rect.top = cy-1;
rect.bottom = cy+1;
ClipCursor(&rect);
break;
}
else
win = win->next;
}
}
}
break;
}
}
}
/*************************************************************************/
static void Win32_DestroyWindow(SST_Window window)
{
SST_DisplayTarget_Win32* displayTarget;
SST_Window_Win32* win = (SST_Window_Win32*)window;
SST_Window_Win32* nextWin;
displayTarget = win->owner;
nextWin = displayTarget->firstWindow;
/* Special case: root window */
if(nextWin == win)
{
/* Set new root to be this->next */
displayTarget->firstWindow = win->next;
}
else
{
int found = 0;
/* Check list */
while(nextWin)
{
/* Did we find the window? */
if(nextWin->next == win)
{
/* Remove this window from the linked list */
nextWin->next = win->next;
found = 1;
break;
}
else
nextWin = nextWin->next;
}
/* Don't destroy another display target's window */
if(!found)
return;
}
/* Actually destroy the Win32 window */
DestroyWindow(win->hWnd);
if(win->softwareBackbuffer)
HeapFree(GetProcessHeap(), 0, win->softwareBackbuffer);
if(win->softwareDC)
DeleteDC(win->softwareDC);
if(win->softwareImage)
DeleteObject(win->softwareImage);
/* TODO empty message queue? */
/* Free the window */
HeapFree(GetProcessHeap(), 0, win);
}
/*************************************************************************/
static void Win32_DestroyDisplayTarget(SST_DisplayTarget target)
{
SST_DisplayTarget_Win32* displayTarget = (SST_DisplayTarget_Win32*)target;
SST_Window_Win32* window = displayTarget->firstWindow;
/* Destroy all windows */
while(window)
{
/* Save the next window */
SST_Window_Win32* next = window->next;
/* Actually destroy the Win32 window handle */
DestroyWindow(window->hWnd);
HeapFree(GetProcessHeap(), 0, window);
/*
TODO: should we empty the message queue of all messages owned by this window? Do
they automatically get removed? Aiiyeeee!
*/
/* Move to next window */
window = next;
}
/* Delete user event queue lock */
DeleteCriticalSection(&displayTarget->userEventLock);
/* Free structures */
DestroyEQ(&displayTarget->userEventQueue);
HeapFree(GetProcessHeap(), 0, displayTarget->devs);
HeapFree(GetProcessHeap(), 0, displayTarget);
/* Unregister */
if(InterlockedDecrement(&regRefCount) == 0)
UnregisterClassA(SST_WINCLASS, GetModuleHandleA(NULL));
}
extern int Win32_ShowDialogBox(SST_DisplayTarget target, SST_Window parent, const char* caption, const char* message, const char** buttons, int nrButtons);
const struct SST_WM_WindowFuncs Win32_WindowFuncs = {
Win32_CreateDisplayTarget,
Win32_GetDisplayTargetScreenCount,
Win32_CreateWindowOnScreen,
Win32_GetWindowDisplayTarget,
Win32_SetWindowText,
Win32_GetWindowRect,
Win32_MoveWindowOnScreen,
Win32_ResizeWindow,
Win32_ShowDialogBox,
Win32_SetWindowState,
Win32_SetDisplayTargetState,
Win32_DestroyWindow,
Win32_DestroyDisplayTarget
};

View File

@@ -0,0 +1,62 @@
/*
Win32Driver.c
Author: Patrick Baggett <ptbaggett@762studios.com>
Created: 12/18/2014
Purpose:
Windows (Win32 API) driver for libsst-wm
License:
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
*/
#include "../APIPrivate.h"
#include "Win32Private.h"
extern const struct SST_WM_WindowFuncs Win32_WindowFuncs;
extern const struct SST_WM_EnumFuncs Win32_EnumFuncs;
extern const struct SST_WM_EventFuncs Win32_EventFuncs;
extern const struct SST_WM_OpenGLFuncs Win32_OpenGLFuncs;
extern const struct SST_WM_RenderFuncs Win32_RenderFuncs;
extern const struct SST_WM_VideoModeFuncs Win32_VideoModeFuncs;
/******************************************************************************/
int Win32_init()
{
if(getenv("LIBSST_NO_WIN32"))
return 0;
return 1;
}
/******************************************************************************/
void Win32_shutdown()
{
/* Nothing to do (now) */
return;
}
/******************************************************************************/
const struct SST_WM_Driver Win32Driver = {
"Win32 Driver",
Win32_init,
Win32_shutdown,
&Win32_WindowFuncs,
&Win32_EnumFuncs,
&Win32_EventFuncs,
&Win32_OpenGLFuncs,
&Win32_RenderFuncs,
&Win32_VideoModeFuncs
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,267 @@
/*
Win32Private.h
Author: Patrick Baggett <ptbaggett@762studios.com>
Created: 1/7/2012
Purpose:
Private defintions and functions for Win32 implementation of libsst-wm
License:
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
*/
#pragma once
#ifndef _WIN32PRIVATE_H
#define _WIN32PRIVATE_H
#include <windows.h>
#include <windowsx.h> /* More Win32 macros */
#include <GL/gl.h>
#include <SST/SST_WMTypes.h>
#include "../EventQueue.h"
#define ADAPTER_NAME_STRLEN 128
#define STRLEN_GUID ((size_t)64) /* Storage enough to hold a Win32 GUID if it was written as a string of hex characters with dashes (need only like 36-38) */
#define MIN_BPP 24 /* Minimum BPP for a mode to be considered */
#define SST_WINCLASS "libsstwm"
#define SST_DLGCLASS "libsstwmdlg"
#define MAX_GL_ATTRS 32
/*************************************************************************/
typedef HGLRC (WINAPI * pf_wglCreateContext)(HDC hdc);
typedef BOOL (WINAPI * pf_wglMakeCurrent)(HDC hdc, HGLRC hglrc);
typedef PROC (WINAPI * pf_wglGetProcAddress)(LPCSTR lpszProc);
typedef BOOL (WINAPI * pf_wglDeleteContext)(HGLRC hglrc);
typedef BOOL (WINAPI * pf_wglShareLists)(HGLRC hglrc1, HGLRC hglrc2);
typedef HGLRC (WINAPI * pf_wglGetCurrentContext)(void);
typedef HDC (WINAPI * pf_wglGetCurrentDC)(void);
typedef const char* (WINAPI * pf_wglGetExtensionsStringARB)(HDC hdc);
typedef BOOL (WINAPI * pf_wglChoosePixelFormatARB)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats);
typedef HGLRC (WINAPI * pf_wglCreateContextAttribsARB)(HDC hDC, HGLRC hshareContext, const int *attribList);
typedef BOOL (WINAPI * pf_wglGetPixelFormatAttribivARB)(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, int *piValues);
/*************************************************************************/
/*
Note about memory management -- these functions all use the Win32 API (HeapAlloc()/HeapFree()) instead of the CRT functions (malloc()/free()).
This is so that it has less of dependency on the CRT.
*/
/*************************************************************************/
/* Structure to maps an adapter-display pair to a DISPLAY_DEVICE */
typedef struct ASMapEntry
{
SST_VideoMode defaultVmode;
const DISPLAY_DEVICEA* dev;
SST_VideoMode* vmodes;
size_t adapter;
size_t screen;
size_t vmodeCount;
} ASMapEntry;
/*************************************************************************/
typedef struct FindMonitorInfo
{
const DISPLAY_DEVICEA* dev; /* Name of the device we are trying to find a matching HMONITOR for */
BOOL foundIt; /* Did we find it? */
LONG top, left; /* Monitor corner (top,left) */
LONG bottom, right;
} FindMonitorInfo;
/*************************************************************************/
typedef struct SST_GraphicsEnumerator_Win32
{
char* adapterNames;
DISPLAY_DEVICEA* devsFound;
size_t* screenCount;
ASMapEntry* ASMap;
size_t adapterCount;
size_t devCount;
size_t ASPairCount;
} SST_GraphicsEnumerator_Win32;
/*************************************************************************/
typedef struct SST_DisplayTarget_Win32
{
CRITICAL_SECTION userEventLock; /* Lock protection user events */
EventQueue userEventQueue;
DISPLAY_DEVICEA* devs; /* Array of devices representing this display target */
struct SST_Window_Win32* firstWindow; /* First window in a list of windows */
size_t screenCount;
size_t screenIndex;
BOOL relativeMouse;
} SST_DisplayTarget_Win32;
/*************************************************************************/
typedef struct SST_Window_Win32
{
WINDOWPLACEMENT wp;
EventQueue eventQueue;
struct SST_Window_Win32* next;
SST_DisplayTarget_Win32* owner;
HWND hWnd;
BOOL isFullscreen;
BOOL setPixelFormat; /* If TRUE, then SetPixelFormat() was called on this window */
/* Software rendering support */
HBITMAP softwareImage;
HDC softwareDC;
void* softwareBackbuffer;
size_t softwarePitch;
} SST_Window_Win32;
/*************************************************************************/
typedef struct WGLFunctions
{
pf_wglCreateContext CreateContext;
pf_wglMakeCurrent MakeCurrent;
pf_wglGetProcAddress GetProcAddress;
pf_wglDeleteContext DeleteContext;
pf_wglShareLists ShareLists;
pf_wglGetCurrentContext GetCurrentContext;
pf_wglGetCurrentDC GetCurrentDC;
/* WGL Extensions */
pf_wglGetExtensionsStringARB GetExtensionsStringARB;
pf_wglChoosePixelFormatARB ChoosePixelFormatARB;
pf_wglCreateContextAttribsARB CreateContextAttribsARB;
pf_wglGetPixelFormatAttribivARB GetPixelFormatAttribivARB;
BOOL supportsProfiles;
BOOL supportsMultisample;
} WGLFunctions;
/*************************************************************************/
typedef struct SST_OpenGLContext_Win32
{
WGLFunctions wgl;
SST_DisplayTarget_Win32* displayTarget;
HMODULE opengl32;
HGLRC context;
HDC hDCActive;
HWND hSlaveWnd; /* Slave contexts use a dummy window. Regular (master) GL contexts have this as NULL */
int pixelFormat;
short ctxVersion[2]; /* context version major/minor */
BOOL isLegacy; /* Did we use legacy context creation functions? */
BOOL legacyEnabled; /* Did we use legacy OpenGL (< 3.0) context support? */
BOOL debugEnabled; /* Did we use debug OpenGL context support? */
} SST_OpenGLContext_Win32;
/*************************************************************************/
DISPLAY_DEVICEA* get_win32devs(size_t* devCountReturn);
char* get_adapters(const DISPLAY_DEVICEA* devsFound, size_t devCount, size_t* adapterCountReturn, char** adapterGUIDReturn);
/*
Filter a list of Win32 devices in-place; modify list so that only relevant Win32 devices are returned.
For example, if there are 3 devices: { Gpu0Screen0, Gpu1Screen0, Gpu1Screen1}, and adapterIndex == 1,
then this returns { Gpu1Screen0, Gpu1Screen1 } in place. The number of devices in the filtered list are
returned (in the example, 2).
*/
size_t filter_win32devs(DISPLAY_DEVICEA* devsFound, size_t devCount, size_t adapterIndex);
ASMapEntry* build_asmap(const DISPLAY_DEVICEA* devsFound, const char* adapterGUIDs, size_t devCount, size_t adapterCount, size_t* screenCount, size_t* mapSizeReturn);
SST_VideoMode* get_vmodes(const DISPLAY_DEVICEA* dev, size_t* modeCountReturn, SST_VideoMode* defaultMode);
LRESULT WINAPI libsstWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
void resolveWGLSymbols(HMODULE opengl32, WGLFunctions* wgl);
void resolveWGLExtSymbols(HDC hDC, WGLFunctions* wgl);
void findMonitor(const DISPLAY_DEVICEA* dev, FindMonitorInfo* fmi);
/* WGL_ARB_multisample */
#define WGL_SAMPLE_BUFFERS_ARB 0x2041
#define WGL_SAMPLES_ARB 0x2042
/* WGL_ARB_pixel_format */
#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
#define WGL_DRAW_TO_WINDOW_ARB 0x2001
#define WGL_DRAW_TO_BITMAP_ARB 0x2002
#define WGL_ACCELERATION_ARB 0x2003
#define WGL_NEED_PALETTE_ARB 0x2004
#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005
#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006
#define WGL_SWAP_METHOD_ARB 0x2007
#define WGL_NUMBER_OVERLAYS_ARB 0x2008
#define WGL_NUMBER_UNDERLAYS_ARB 0x2009
#define WGL_TRANSPARENT_ARB 0x200A
#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037
#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038
#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039
#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A
#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B
#define WGL_SHARE_DEPTH_ARB 0x200C
#define WGL_SHARE_STENCIL_ARB 0x200D
#define WGL_SHARE_ACCUM_ARB 0x200E
#define WGL_SUPPORT_GDI_ARB 0x200F
#define WGL_SUPPORT_OPENGL_ARB 0x2010
#define WGL_DOUBLE_BUFFER_ARB 0x2011
#define WGL_STEREO_ARB 0x2012
#define WGL_PIXEL_TYPE_ARB 0x2013
#define WGL_COLOR_BITS_ARB 0x2014
#define WGL_RED_BITS_ARB 0x2015
#define WGL_RED_SHIFT_ARB 0x2016
#define WGL_GREEN_BITS_ARB 0x2017
#define WGL_GREEN_SHIFT_ARB 0x2018
#define WGL_BLUE_BITS_ARB 0x2019
#define WGL_BLUE_SHIFT_ARB 0x201A
#define WGL_ALPHA_BITS_ARB 0x201B
#define WGL_ALPHA_SHIFT_ARB 0x201C
#define WGL_ACCUM_BITS_ARB 0x201D
#define WGL_ACCUM_RED_BITS_ARB 0x201E
#define WGL_ACCUM_GREEN_BITS_ARB 0x201F
#define WGL_ACCUM_BLUE_BITS_ARB 0x2020
#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021
#define WGL_DEPTH_BITS_ARB 0x2022
#define WGL_STENCIL_BITS_ARB 0x2023
#define WGL_AUX_BUFFERS_ARB 0x2024
#define WGL_NO_ACCELERATION_ARB 0x2025
#define WGL_GENERIC_ACCELERATION_ARB 0x2026
#define WGL_FULL_ACCELERATION_ARB 0x2027
#define WGL_SWAP_EXCHANGE_ARB 0x2028
#define WGL_SWAP_COPY_ARB 0x2029
#define WGL_SWAP_UNDEFINED_ARB 0x202A
#define WGL_TYPE_RGBA_ARB 0x202B
#define WGL_TYPE_COLORINDEX_ARB 0x202C
/* WGL_ARB_create_context */
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093
#define WGL_CONTEXT_FLAGS_ARB 0x2094
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
#define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
#define ERROR_INVALID_VERSION_ARB 0x2095
#define ERROR_INVALID_PROFILE_ARB 0x2096
#endif