|
// SDL + windows pressure sensitivity demo |
|
// Written in July 2013 |
|
// |
|
// This file is public domain. No copyright is claimed and you may use it for any purpose you like. |
|
// |
|
// No warranty for any purpose is expressed or implied by the author. You use this file at your own risk. |
|
// |
|
// At the time of this writing, you can get the SimpleDirect Media library at http://www.libsdl.org |
|
// The stylus code in this file was researched in the RealTimeStylus demo from the windows SDK. |
|
// You can pick up the Windows SDK from http://www.microsoft.com if you need more windows touch-interface demos. |
|
|
|
#include <iostream> |
|
|
|
#include <SDL2/SDL.h> |
|
#include <SDL2/SDL_opengl.h> |
|
#include <SDL2/SDL_syswm.h> |
|
|
|
#include <ole2.h> |
|
#include <rtscom.h> |
|
#include <rtscom_i.c> |
|
|
|
typedef unsigned long uint32; |
|
|
|
// Some constants |
|
const uint32 c_WinWidth = 1024; |
|
const uint32 c_WinHeight = 768; |
|
const char *c_WinCaption = "Pressure test SDL window"; |
|
|
|
// SDL main surface |
|
// SDL_Surface *g_pScreen = NULL; |
|
SDL_Window *g_pWindow = NULL; |
|
SDL_Renderer *g_pRenderer = NULL; |
|
bool g_bValid = true; |
|
|
|
// Current settings |
|
bool g_bInverted = false; |
|
float g_Pressure = 0.0f; |
|
int g_MouseX; |
|
int g_MouseY; |
|
|
|
// GL resources |
|
GLuint g_iTexture; |
|
|
|
// Stylus handler |
|
bool g_bTriedToCreateRTSHandler = false; |
|
class CRTSEventHandler *g_pRTSHandler = NULL; |
|
IRealTimeStylus *g_pRTSStylus = NULL; |
|
IInkRenderer *g_pRTSRenderer = NULL; |
|
|
|
// Tablet context to pressure sensitivity |
|
const uint32 c_nMaxContexts = 4; |
|
uint32 g_nContexts = 0; |
|
HDC hDC; |
|
|
|
struct |
|
{ |
|
TABLET_CONTEXT_ID iTabletContext; |
|
uint32 iPressure; |
|
float PressureRcp; |
|
std::string names[32]; |
|
PROPERTY_METRICS props[32]; |
|
|
|
} g_lContexts[c_nMaxContexts]; |
|
|
|
std::string guidToName(GUID guid) { |
|
if (guid == GUID_PACKETPROPERTY_GUID_X) |
|
return "GUID_PACKETPROPERTY_GUID_X"; |
|
else if (guid == GUID_PACKETPROPERTY_GUID_Y) |
|
return "GUID_PACKETPROPERTY_GUID_Y"; |
|
else if (guid == GUID_PACKETPROPERTY_GUID_Z) |
|
return "GUID_PACKETPROPERTY_GUID_Z"; |
|
else if (guid == GUID_PACKETPROPERTY_GUID_PACKET_STATUS) |
|
return "GUID_PACKETPROPERTY_GUID_PACKET_STATUS"; |
|
else if (guid == GUID_PACKETPROPERTY_GUID_TIMER_TICK) |
|
return "GUID_PACKETPROPERTY_GUID_TIMER_TICK"; |
|
else if (guid == GUID_PACKETPROPERTY_GUID_SERIAL_NUMBER) |
|
return "GUID_PACKETPROPERTY_GUID_SERIAL_NUMBER"; |
|
else if (guid == GUID_PACKETPROPERTY_GUID_NORMAL_PRESSURE) |
|
return "GUID_PACKETPROPERTY_GUID_NORMAL_PRESSURE"; |
|
else if (guid == GUID_PACKETPROPERTY_GUID_TANGENT_PRESSURE) |
|
return "GUID_PACKETPROPERTY_GUID_TANGENT_PRESSURE"; |
|
else if (guid == GUID_PACKETPROPERTY_GUID_BUTTON_PRESSURE) |
|
return "GUID_PACKETPROPERTY_GUID_BUTTON_PRESSURE"; |
|
else if (guid == GUID_PACKETPROPERTY_GUID_X_TILT_ORIENTATION) |
|
return "GUID_PACKETPROPERTY_GUID_X_TILT_ORIENTATION"; |
|
else if (guid == GUID_PACKETPROPERTY_GUID_Y_TILT_ORIENTATION) |
|
return "GUID_PACKETPROPERTY_GUID_Y_TILT_ORIENTATION"; |
|
else if (guid == GUID_PACKETPROPERTY_GUID_AZIMUTH_ORIENTATION) |
|
return "GUID_PACKETPROPERTY_GUID_AZIMUTH_ORIENTATION"; |
|
else if (guid == GUID_PACKETPROPERTY_GUID_ALTITUDE_ORIENTATION) |
|
return "GUID_PACKETPROPERTY_GUID_ALTITUDE_ORIENTATION"; |
|
else if (guid == GUID_PACKETPROPERTY_GUID_TWIST_ORIENTATION) |
|
return "GUID_PACKETPROPERTY_GUID_TWIST_ORIENTATION"; |
|
else if (guid == GUID_PACKETPROPERTY_GUID_PITCH_ROTATION) |
|
return "GUID_PACKETPROPERTY_GUID_PITCH_ROTATION"; |
|
else if (guid == GUID_PACKETPROPERTY_GUID_ROLL_ROTATION) |
|
return "GUID_PACKETPROPERTY_GUID_ROLL_ROTATION"; |
|
else if (guid == GUID_PACKETPROPERTY_GUID_YAW_ROTATION) |
|
return "GUID_PACKETPROPERTY_GUID_YAW_ROTATION"; |
|
else if (guid == GUID_PACKETPROPERTY_GUID_WIDTH) |
|
return "GUID_PACKETPROPERTY_GUID_WIDTH"; |
|
else if (guid == GUID_PACKETPROPERTY_GUID_HEIGHT) |
|
return "GUID_PACKETPROPERTY_GUID_HEIGHT"; |
|
else if (guid == GUID_PACKETPROPERTY_GUID_FINGERCONTACTCONFIDENCE) |
|
return "GUID_PACKETPROPERTY_GUID_FINGERCONTACTCONFIDENCE"; |
|
else if (guid == GUID_PACKETPROPERTY_GUID_DEVICE_CONTACT_ID) |
|
return "GUID_PACKETPROPERTY_GUID_DEVICE_CONTACT_ID"; |
|
else |
|
return "unknown"; |
|
} |
|
|
|
std::string unitToName(PROPERTY_UNITS unit) { |
|
const char *unitName; |
|
|
|
switch (unit) { |
|
case PROPERTY_UNITS_DEFAULT: |
|
unitName = "Default"; |
|
break; |
|
case PROPERTY_UNITS_INCHES: |
|
unitName = "Inches"; |
|
break; |
|
case PROPERTY_UNITS_CENTIMETERS: |
|
unitName = "Centimeters"; |
|
break; |
|
case PROPERTY_UNITS_DEGREES: |
|
unitName = "Degrees"; |
|
break; |
|
case PROPERTY_UNITS_RADIANS: |
|
unitName = "Radians"; |
|
break; |
|
case PROPERTY_UNITS_SECONDS: |
|
unitName = "Seconds"; |
|
break; |
|
case PROPERTY_UNITS_POUNDS: |
|
unitName = "Pounds"; |
|
break; |
|
case PROPERTY_UNITS_GRAMS: |
|
unitName = "Grams"; |
|
break; |
|
case PROPERTY_UNITS_SILINEAR: |
|
unitName = "SI Linear"; |
|
break; |
|
case PROPERTY_UNITS_SIROTATION: |
|
unitName = "SI Rotation"; |
|
break; |
|
case PROPERTY_UNITS_ENGLINEAR: |
|
unitName = "English Linear"; |
|
break; |
|
case PROPERTY_UNITS_ENGROTATION: |
|
unitName = "English Rotation"; |
|
break; |
|
case PROPERTY_UNITS_SLUGS: |
|
unitName = "Slugs"; |
|
break; |
|
case PROPERTY_UNITS_KELVIN: |
|
unitName = "Kelvin"; |
|
break; |
|
case PROPERTY_UNITS_FAHRENHEIT: |
|
unitName = "Fahrenheit"; |
|
break; |
|
case PROPERTY_UNITS_AMPERE: |
|
unitName = "Ampere"; |
|
break; |
|
case PROPERTY_UNITS_CANDELA: |
|
unitName = "Candela"; |
|
break; |
|
default: |
|
unitName = "Unknown Unit"; |
|
break; |
|
} |
|
|
|
return unitName; |
|
} |
|
|
|
//------------------------------------------------------------------------------------------------- |
|
// RealTimeStylus syncevent plugin |
|
class CRTSEventHandler : public IStylusSyncPlugin { |
|
public: |
|
CRTSEventHandler() : m_nRef(1), m_pPunkFTMarshaller(NULL) {} |
|
virtual ~CRTSEventHandler() { |
|
if (m_pPunkFTMarshaller != NULL) |
|
m_pPunkFTMarshaller->Release(); |
|
} |
|
|
|
// IStylusSyncPlugin inherited methods |
|
|
|
// Methods whose data we use |
|
STDMETHOD(Packets) |
|
(IRealTimeStylus *pStylus, const StylusInfo *pStylusInfo, ULONG nPackets, ULONG nPacketBuf, LONG *pPackets, ULONG *nOutPackets, LONG **ppOutPackets) { |
|
uint32 iCtx = c_nMaxContexts; |
|
for (uint32 i = 0; i < g_nContexts; i++) { |
|
if (g_lContexts[i].iTabletContext == pStylusInfo->tcid) { |
|
iCtx = i; |
|
} |
|
} |
|
|
|
if (iCtx >= c_nMaxContexts) { |
|
std::cout << "Unknown Device" << std::endl; |
|
} else { |
|
std::cout << "Device Index: " << iCtx + 1 << std::endl; |
|
} |
|
|
|
// Get properties |
|
ULONG nProps = nPacketBuf / nPackets; |
|
|
|
for (int i = 0; i < nPackets; i++) { |
|
std::cout << "Packet " << i + 1 << " / " << nPackets << std::endl; |
|
for (int j = 0; j < nProps; j++) { |
|
if (iCtx < c_nMaxContexts) { |
|
auto prop = g_lContexts[iCtx].props[j]; |
|
std::cout << g_lContexts[iCtx].names[j] << "(" << j << ")" |
|
<< " : " << (prop.fResolution != 0 ? pPackets[i * nProps + j] / prop.fResolution : pPackets[i * nProps + j]) |
|
<< " " << unitToName(prop.Units) << " [" << prop.nLogicalMin << ", " << prop.nLogicalMax << "]" |
|
<< std::endl; |
|
} else { |
|
std::cout << "(" << j << ")" |
|
<< " : " << pPackets[i * nProps + j] |
|
<< std::endl; |
|
} |
|
} |
|
|
|
long x = pPackets[i * nProps + 0], y = pPackets[i * nProps + 1]; |
|
std::cout << "(Ink space) X: " << x << " Y: " << y << std::endl; |
|
g_pRTSRenderer->InkSpaceToPixel(reinterpret_cast<LONG_PTR>(hDC), &x, &y); |
|
std::cout << "(Pixel) X: " << x << " Y: " << y << std::endl; |
|
|
|
g_MouseX = x; |
|
g_MouseY = c_WinHeight - y; |
|
} |
|
|
|
// Pointer to the last packet in the batch - we don't really care about intermediates for this example |
|
// LONG *pLastPacket = pPackets + (nPacketBuf - nProps); |
|
|
|
// g_Pressure = pLastPacket[g_lContexts[iCtx].iPressure] * g_lContexts[iCtx].PressureRcp; |
|
// g_bInverted = (pStylusInfo->bIsInvertedCursor != 0); |
|
// std::cout << "pressure: " << g_Pressure << std::endl; |
|
|
|
return S_OK; |
|
} |
|
STDMETHOD(DataInterest) |
|
(RealTimeStylusDataInterest *pEventInterest) { |
|
*pEventInterest = (RealTimeStylusDataInterest)(RTSDI_Packets); |
|
return S_OK; |
|
} |
|
|
|
// Methods you can add if you need the alerts - don't forget to change DataInterest! |
|
STDMETHOD(StylusDown) |
|
(IRealTimeStylus *, const StylusInfo *, ULONG, LONG *_pPackets, LONG **) { return S_OK; } |
|
STDMETHOD(StylusUp) |
|
(IRealTimeStylus *, const StylusInfo *, ULONG, LONG *_pPackets, LONG **) { return S_OK; } |
|
STDMETHOD(RealTimeStylusEnabled) |
|
(IRealTimeStylus *, ULONG, const TABLET_CONTEXT_ID *) { return S_OK; } |
|
STDMETHOD(RealTimeStylusDisabled) |
|
(IRealTimeStylus *, ULONG, const TABLET_CONTEXT_ID *) { return S_OK; } |
|
STDMETHOD(StylusInRange) |
|
(IRealTimeStylus *, TABLET_CONTEXT_ID, STYLUS_ID) { return S_OK; } |
|
STDMETHOD(StylusOutOfRange) |
|
(IRealTimeStylus *, TABLET_CONTEXT_ID, STYLUS_ID) { return S_OK; } |
|
STDMETHOD(InAirPackets) |
|
(IRealTimeStylus *, const StylusInfo *, ULONG, ULONG, LONG *, ULONG *, LONG **) { return S_OK; } |
|
STDMETHOD(StylusButtonUp) |
|
(IRealTimeStylus *, STYLUS_ID, const GUID *, POINT *) { return S_OK; } |
|
STDMETHOD(StylusButtonDown) |
|
(IRealTimeStylus *, STYLUS_ID, const GUID *, POINT *) { return S_OK; } |
|
STDMETHOD(SystemEvent) |
|
(IRealTimeStylus *, TABLET_CONTEXT_ID, STYLUS_ID, SYSTEM_EVENT, SYSTEM_EVENT_DATA) { return S_OK; } |
|
STDMETHOD(TabletAdded) |
|
(IRealTimeStylus *, IInkTablet *) { return S_OK; } |
|
STDMETHOD(TabletRemoved) |
|
(IRealTimeStylus *, LONG) { return S_OK; } |
|
STDMETHOD(CustomStylusDataAdded) |
|
(IRealTimeStylus *, const GUID *, ULONG, const BYTE *) { return S_OK; } |
|
STDMETHOD(Error) |
|
(IRealTimeStylus *, IStylusPlugin *, RealTimeStylusDataInterest, HRESULT, LONG_PTR *) { return S_OK; } |
|
STDMETHOD(UpdateMapping) |
|
(IRealTimeStylus *) { return S_OK; } |
|
|
|
// IUnknown methods |
|
STDMETHOD_(ULONG, AddRef) |
|
() { |
|
return InterlockedIncrement(&m_nRef); |
|
} |
|
STDMETHOD_(ULONG, Release) |
|
() { |
|
ULONG nNewRef = InterlockedDecrement(&m_nRef); |
|
if (nNewRef == 0) |
|
delete this; |
|
|
|
return nNewRef; |
|
} |
|
STDMETHOD(QueryInterface) |
|
(REFIID riid, LPVOID *ppvObj) { |
|
if ((riid == IID_IStylusSyncPlugin) || (riid == IID_IUnknown)) { |
|
*ppvObj = this; |
|
AddRef(); |
|
return S_OK; |
|
} else if ((riid == IID_IMarshal) && (m_pPunkFTMarshaller != NULL)) { |
|
return m_pPunkFTMarshaller->QueryInterface(riid, ppvObj); |
|
} |
|
|
|
*ppvObj = NULL; |
|
return E_NOINTERFACE; |
|
} |
|
|
|
LONG m_nRef; // COM object reference count |
|
IUnknown *m_pPunkFTMarshaller; // free-threaded marshaller |
|
}; |
|
|
|
//------------------------------------------------------------------------------------------------- |
|
void ReleaseRTS() { |
|
if (g_pRTSStylus) { |
|
g_pRTSStylus->Release(); |
|
g_pRTSStylus = NULL; |
|
} |
|
|
|
if (g_pRTSHandler) { |
|
g_pRTSHandler->Release(); |
|
g_pRTSHandler = NULL; |
|
} |
|
} |
|
|
|
//------------------------------------------------------------------------------------------------- |
|
// Create stylus |
|
bool CreateRTS(HWND hWnd) { |
|
// Release, just in case |
|
ReleaseRTS(); |
|
|
|
// Create stylus |
|
HRESULT hr = CoCreateInstance(CLSID_RealTimeStylus, NULL, CLSCTX_ALL, IID_PPV_ARGS(&g_pRTSStylus)); |
|
if (FAILED(hr)) |
|
return false; |
|
|
|
// Attach RTS object to a window |
|
hr = g_pRTSStylus->put_HWND((HANDLE_PTR)hWnd); |
|
if (FAILED(hr)) { |
|
ReleaseRTS(); |
|
return false; |
|
} |
|
|
|
// Create eventhandler |
|
g_pRTSHandler = new CRTSEventHandler(); |
|
|
|
// Create free-threaded marshaller for this object and aggregate it. |
|
hr = CoCreateFreeThreadedMarshaler(g_pRTSHandler, &g_pRTSHandler->m_pPunkFTMarshaller); |
|
if (FAILED(hr)) { |
|
ReleaseRTS(); |
|
return false; |
|
} |
|
|
|
// Add handler object to the list of synchronous plugins in the RTS object. |
|
hr = g_pRTSStylus->AddStylusSyncPlugin(0, g_pRTSHandler); |
|
if (FAILED(hr)) { |
|
ReleaseRTS(); |
|
return false; |
|
} |
|
|
|
// Set data we want - we're not actually using all of this, but we're gonna get X and Y anyway so might as well set it |
|
GUID lWantedProps[] = { |
|
GUID_PACKETPROPERTY_GUID_X, |
|
GUID_PACKETPROPERTY_GUID_Y, |
|
GUID_PACKETPROPERTY_GUID_Z, |
|
GUID_PACKETPROPERTY_GUID_PACKET_STATUS, |
|
GUID_PACKETPROPERTY_GUID_TIMER_TICK, |
|
GUID_PACKETPROPERTY_GUID_SERIAL_NUMBER, |
|
GUID_PACKETPROPERTY_GUID_NORMAL_PRESSURE, |
|
GUID_PACKETPROPERTY_GUID_TANGENT_PRESSURE, |
|
GUID_PACKETPROPERTY_GUID_BUTTON_PRESSURE, |
|
GUID_PACKETPROPERTY_GUID_X_TILT_ORIENTATION, |
|
GUID_PACKETPROPERTY_GUID_Y_TILT_ORIENTATION, |
|
GUID_PACKETPROPERTY_GUID_AZIMUTH_ORIENTATION, |
|
GUID_PACKETPROPERTY_GUID_ALTITUDE_ORIENTATION, |
|
GUID_PACKETPROPERTY_GUID_TWIST_ORIENTATION, |
|
GUID_PACKETPROPERTY_GUID_PITCH_ROTATION, |
|
GUID_PACKETPROPERTY_GUID_ROLL_ROTATION, |
|
GUID_PACKETPROPERTY_GUID_YAW_ROTATION, |
|
GUID_PACKETPROPERTY_GUID_WIDTH, |
|
GUID_PACKETPROPERTY_GUID_HEIGHT, |
|
GUID_PACKETPROPERTY_GUID_FINGERCONTACTCONFIDENCE, |
|
GUID_PACKETPROPERTY_GUID_DEVICE_CONTACT_ID}; |
|
g_pRTSStylus->SetDesiredPacketDescription(std::size(lWantedProps), lWantedProps); |
|
g_pRTSStylus->put_Enabled(true); |
|
|
|
// Detect what tablet context IDs will give us pressure data |
|
{ |
|
g_nContexts = 0; |
|
ULONG nTabletContexts = 0; |
|
TABLET_CONTEXT_ID *piTabletContexts; |
|
HRESULT res = g_pRTSStylus->GetAllTabletContextIds(&nTabletContexts, &piTabletContexts); |
|
for (ULONG i = 0; i < nTabletContexts; i++) { |
|
IInkTablet *pInkTablet; |
|
if (SUCCEEDED(g_pRTSStylus->GetTabletFromTabletContextId(piTabletContexts[i], &pInkTablet))) { |
|
float ScaleX, ScaleY; |
|
ULONG nPacketProps; |
|
PACKET_PROPERTY *pPacketProps; |
|
g_pRTSStylus->GetPacketDescriptionData(piTabletContexts[i], &ScaleX, &ScaleY, &nPacketProps, &pPacketProps); |
|
|
|
for (ULONG j = 0; j < nPacketProps; j++) { |
|
std::cout << "Device " << i + 1 << " Prop " << j + 1 << " : " << guidToName(pPacketProps[j].guid) << std::endl; |
|
g_lContexts[i].names[j] = guidToName(pPacketProps[j].guid); |
|
g_lContexts[i].props[j] = pPacketProps[j].PropertyMetrics; |
|
|
|
if (pPacketProps[j].guid != GUID_PACKETPROPERTY_GUID_NORMAL_PRESSURE) |
|
continue; |
|
|
|
g_lContexts[i].iTabletContext = piTabletContexts[i]; |
|
g_lContexts[i].iPressure = j; |
|
g_lContexts[i].PressureRcp = 1.0f / pPacketProps[j].PropertyMetrics.nLogicalMax; |
|
// break; |
|
} |
|
CoTaskMemFree(pPacketProps); |
|
} |
|
g_nContexts++; |
|
} |
|
|
|
// If we can't get pressure information, no use in having the tablet context |
|
if (g_nContexts == 0) { |
|
ReleaseRTS(); |
|
return false; |
|
} |
|
} |
|
|
|
hr = CoCreateInstance(__uuidof(InkRenderer), NULL, CLSCTX_ALL, IID_PPV_ARGS(&g_pRTSRenderer)); |
|
if (FAILED(hr)) { |
|
ReleaseRTS(); |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//------------------------------------------------------------------------------------------------- |
|
// Initialize SDL openGL window |
|
bool CreateSDLWindow() { |
|
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); |
|
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); |
|
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); |
|
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); |
|
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0); |
|
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0); |
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); |
|
|
|
g_pWindow = SDL_CreateWindow( |
|
c_WinCaption, |
|
SDL_WINDOWPOS_UNDEFINED, |
|
SDL_WINDOWPOS_UNDEFINED, |
|
c_WinWidth, |
|
c_WinHeight, |
|
SDL_WINDOW_OPENGL); |
|
if (!g_pWindow) |
|
return false; |
|
SDL_GL_CreateContext(g_pWindow); |
|
// g_pScreen = SDL_SetVideoMode(c_WinWidth, c_WinHeight, 32, SDL_OPENGL); |
|
// if (!g_pScreen) |
|
// return false; |
|
|
|
// SDL_WM_SetCaption(c_WinCaption, 0); |
|
return true; |
|
} |
|
|
|
//------------------------------------------------------------------------------------------------- |
|
// Setup GL state |
|
void InitializeGL() { |
|
std::cout << "init gl" << std::endl; |
|
glViewport(0, 0, c_WinWidth, c_WinHeight); |
|
glMatrixMode(GL_PROJECTION); |
|
glOrtho(0, c_WinWidth, 0, c_WinHeight, -1.0f, 1.0f); |
|
glMatrixMode(GL_MODELVIEW); |
|
|
|
// Generate texture data |
|
GLubyte *pTexData = new GLubyte[64 * 64]; |
|
GLubyte *pDst = pTexData; |
|
for (int i = 0; i < 64; i++) { |
|
int y = i - 31; |
|
for (int j = 0; j < 64; j++) { |
|
int x = j - 31; |
|
int intensity = (1024 - (x * x + y * y)); |
|
if (intensity < 0) |
|
intensity = 0; |
|
else if (intensity > 255) |
|
intensity = 255; |
|
|
|
*pDst = intensity; |
|
pDst++; |
|
} |
|
} |
|
|
|
// Create texture |
|
glGenTextures(1, &g_iTexture); |
|
glEnable(GL_TEXTURE_2D); |
|
glBindTexture(GL_TEXTURE_2D, g_iTexture); |
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 64, 64, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pTexData); |
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); |
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); |
|
|
|
// Release data |
|
delete[] pTexData; |
|
} |
|
|
|
//------------------------------------------------------------------------------------------------- |
|
// Release GL resources |
|
void ReleaseGL() { |
|
glDeleteTextures(1, &g_iTexture); |
|
} |
|
|
|
//------------------------------------------------------------------------------------------------- |
|
// Render each frame |
|
void Render() { |
|
glClear(GL_COLOR_BUFFER_BIT); |
|
|
|
glLoadIdentity(); |
|
glTranslatef((GLfloat)g_MouseX, (GLfloat)g_MouseY, 0); |
|
|
|
float scale = 1.0f + g_Pressure * 9.0f; |
|
glScalef(scale, scale, scale); |
|
|
|
if (g_bInverted) |
|
glColor3ub(255, 0, 0); |
|
else |
|
glColor3ub(0, 255, 0); |
|
|
|
glBegin(GL_QUADS); |
|
glTexCoord2f(0.0f, 0.0f); |
|
glVertex2f(-32.0f, -32.0f); |
|
glTexCoord2f(1.0f, 0.0f); |
|
glVertex2f(32.0f, -32.0f); |
|
glTexCoord2f(1.0f, 1.0f); |
|
glVertex2f(32.0f, 32.0f); |
|
glTexCoord2f(0.0f, 1.0f); |
|
glVertex2f(-32.0f, 32.0f); |
|
glEnd(); |
|
|
|
// SDL_GL_SwapBuffers(); |
|
SDL_GL_SwapWindow(g_pWindow); |
|
} |
|
|
|
//------------------------------------------------------------------------------------------------- |
|
// Process events |
|
void PumpSDLEvents() { |
|
SDL_PumpEvents(); |
|
|
|
SDL_Event Ev; |
|
while (SDL_PollEvent(&Ev)) { |
|
switch (Ev.type) { |
|
case SDL_QUIT: |
|
g_bValid = false; |
|
break; |
|
|
|
case SDL_SYSWMEVENT: |
|
|
|
// We would normally do this in WM_CREATE but SDL intercepts that message so use WM_SETFOCUS instead |
|
SDL_SysWMmsg *pMsg = Ev.syswm.msg; |
|
if (pMsg && pMsg->msg.win.msg == WM_SETFOCUS) { |
|
if (!g_bTriedToCreateRTSHandler) { |
|
LPCREATESTRUCT pStruct = (LPCREATESTRUCT)pMsg->msg.win.lParam; |
|
auto res = CreateRTS(pMsg->msg.win.hwnd); |
|
hDC = GetDC(pMsg->msg.win.hwnd); |
|
} |
|
g_bTriedToCreateRTSHandler = true; |
|
} |
|
break; |
|
} |
|
} |
|
|
|
// SDL_GetMouseState(&g_MouseX, &g_MouseY); |
|
// Invert Y coordinate |
|
// g_MouseY = c_WinHeight - g_MouseY; |
|
} |
|
|
|
//------------------------------------------------------------------------------------------------- |
|
// Application entry point |
|
int main(int argc, char **argv) { |
|
if (SDL_Init(SDL_INIT_VIDEO)) |
|
return 1; |
|
|
|
// Need to enable SysWM messages so we can get the window handle |
|
SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); |
|
|
|
if (!CreateSDLWindow()) { |
|
SDL_Quit(); |
|
return 1; |
|
} |
|
|
|
InitializeGL(); |
|
|
|
// Global event loop |
|
while (g_bValid) { |
|
PumpSDLEvents(); |
|
Render(); |
|
} |
|
|
|
ReleaseRTS(); |
|
ReleaseGL(); |
|
|
|
// SDL_FreeSurface(g_pScreen); |
|
SDL_DestroyWindow(g_pWindow); |
|
SDL_Quit(); |
|
return 0; |
|
} |