Skip to content

Instantly share code, notes, and snippets.

@Kiterai
Last active August 31, 2023 15:16
Show Gist options
  • Save Kiterai/d86b3ce91eaa7256b5510ab378182684 to your computer and use it in GitHub Desktop.
Save Kiterai/d86b3ce91eaa7256b5510ab378182684 to your computer and use it in GitHub Desktop.
windows RealTimeStylus input

what is this

the demo of stylus input by windows api (RealTimeStylus)

based on this: http://web.archive.org/web/20221004105734/https://backworlds.com/under-pressure/

what I did:

  • updated sdl1 to sdl2
  • rewrote to get and show all available input data

how to build

  1. install vcpkg
  2. vcpkg install sdl2 opengl
  3. mkdir build && cd build
  4. cmake .. -DCMAKE_TOOLCHAIN_FILE=(vcpkg toolchain file) && cmake --build .

known issue

  • slows down because of cout bottleneck
cmake_minimum_required(VERSION 3.7)
project(stylus_test)
add_executable(test main.cpp)
find_package(SDL2 CONFIG REQUIRED)
target_link_libraries(test
PRIVATE
$<TARGET_NAME_IF_EXISTS:SDL2::SDL2main>
$<IF:$<TARGET_EXISTS:SDL2::SDL2>,SDL2::SDL2,SDL2::SDL2-static>
)
find_package(OpenGL REQUIRED)
target_link_libraries(test PRIVATE OpenGL::GL)
// 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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment