#include <string.h>
#include "GHOST_WindowWin32.h"
#include <GL/gl.h>
+#include <math.h>
+
+// MSVC6 still doesn't define M_PI
+#ifndef M_PI
+#define M_PI 3.1415926536
+#endif
LPCSTR GHOST_WindowWin32::s_windowClassName = "GHOST_WindowClass";
const int GHOST_WindowWin32::s_maxTitleLength = 128;
HGLRC GHOST_WindowWin32::s_firsthGLRc = NULL;
+static int WeightPixelFormat(PIXELFORMATDESCRIPTOR& pfd);
+static int EnumPixelFormats(HDC hdc);
+
/*
* Color and depth bit values are not to be trusted.
* For instance, on TNT2:
1, /* version */
PFD_SUPPORT_OPENGL |
PFD_DRAW_TO_WINDOW |
+ PFD_SWAP_COPY | /* support swap copy */
PFD_DOUBLEBUFFER, /* support double-buffering */
PFD_TYPE_RGBA, /* color type */
32, /* prefered color depth */
m_hGlRc(0),
m_hasMouseCaptured(false),
m_nPressedButtons(0),
- m_customCursor(0)
+ m_customCursor(0),
+ m_tabletData(NULL),
+ m_tablet(0),
+ m_wintab(NULL),
+ m_maxPressure(0)
{
if (state != GHOST_kWindowStateFullScreen) {
/* Convert client size into window size */
// Force an initial paint of the window
::UpdateWindow(m_hWnd);
}
+
+ m_wintab = ::LoadLibrary("Wintab32.dll");
+ if (m_wintab) {
+ GHOST_WIN32_WTInfo fpWTInfo = ( GHOST_WIN32_WTInfo ) ::GetProcAddress( m_wintab, "WTInfoA" );
+ GHOST_WIN32_WTOpen fpWTOpen = ( GHOST_WIN32_WTOpen ) ::GetProcAddress( m_wintab, "WTOpenA" );
+
+ // let's see if we can initialize tablet here
+ /* check if WinTab available. */
+ if (fpWTInfo && fpWTInfo(0, 0, NULL)) {
+ // Now init the tablet
+ LOGCONTEXT lc;
+ AXIS TabletX, TabletY, Pressure, Orientation[3]; /* The maximum tablet size, pressure and orientation (tilt) */
+
+ // Open a Wintab context
+
+ // Get default context information
+ fpWTInfo( WTI_DEFCONTEXT, 0, &lc );
+
+ // Open the context
+ lc.lcPktData = PACKETDATA;
+ lc.lcPktMode = PACKETMODE;
+ lc.lcOptions |= CXO_MESSAGES | CXO_SYSTEM;
+
+ /* Set the entire tablet as active */
+ fpWTInfo(WTI_DEVICES,DVC_X,&TabletX);
+ fpWTInfo(WTI_DEVICES,DVC_Y,&TabletY);
+
+ /* get the max pressure, to divide into a float */
+ BOOL pressureSupport = fpWTInfo (WTI_DEVICES, DVC_NPRESSURE, &Pressure);
+ if (pressureSupport)
+ m_maxPressure = Pressure.axMax;
+ else
+ m_maxPressure = 0;
+
+ /* get the max tilt axes, to divide into floats */
+ BOOL tiltSupport = fpWTInfo (WTI_DEVICES, DVC_ORIENTATION, &Orientation);
+ if (tiltSupport) {
+ /* does the tablet support azimuth ([0]) and altitude ([1]) */
+ if (Orientation[0].axResolution && Orientation[1].axResolution) {
+ /* all this assumes the minimum is 0 */
+ m_maxAzimuth = Orientation[0].axMax;
+ m_maxAltitude = Orientation[1].axMax;
+ }
+ else { /* no so dont do tilt stuff */
+ m_maxAzimuth = m_maxAltitude = 0;
+ }
+ }
+
+ lc.lcInOrgX = 0;
+ lc.lcInOrgY = 0;
+ lc.lcInExtX = TabletX.axMax;
+ lc.lcInExtY = TabletY.axMax;
+
+ if (fpWTOpen) {
+ m_tablet = fpWTOpen( m_hWnd, &lc, TRUE );
+ if (m_tablet) {
+ m_tabletData = new GHOST_TabletData();
+ m_tabletData->Active = 0;
+ }
+ }
+ }
+ }
}
GHOST_WindowWin32::~GHOST_WindowWin32()
{
+ if (m_wintab) {
+ GHOST_WIN32_WTClose fpWTClose = ( GHOST_WIN32_WTClose ) ::GetProcAddress( m_wintab, "WTClose" );
+ if (fpWTClose) {
+ if (m_tablet)
+ fpWTClose(m_tablet);
+ if (m_tabletData)
+ delete m_tabletData;
+ m_tabletData = NULL;
+ }
+ }
if (m_customCursor) {
DestroyCursor(m_customCursor);
m_customCursor = NULL;
switch (type) {
case GHOST_kDrawingContextTypeOpenGL:
{
- if(m_stereoVisual)
+ if(m_stereoVisual)
sPreferredFormat.dwFlags |= PFD_STEREO;
// Attempt to match device context pixel format to the preferred format
- int iPixelFormat = ::ChoosePixelFormat(m_hDC, &sPreferredFormat);
+ int iPixelFormat = EnumPixelFormats(m_hDC);
if (iPixelFormat == 0) {
success = GHOST_kFailure;
break;
case GHOST_kStandardCursorTopRightCorner: id = IDC_SIZENESW; break;
case GHOST_kStandardCursorBottomRightCorner: id = IDC_SIZENWSE; break;
case GHOST_kStandardCursorBottomLeftCorner: id = IDC_SIZENESW; break;
- case GHOST_kStandardCursorPencil: id = IDC_CROSS; break;
+ case GHOST_kStandardCursorPencil: id = IDC_ARROW; break;
default:
success = false;
}
return GHOST_kSuccess;
}
+void GHOST_WindowWin32::processWin32TabletInitEvent()
+{
+ if (m_wintab) {
+ GHOST_WIN32_WTInfo fpWTInfo = ( GHOST_WIN32_WTInfo ) ::GetProcAddress( m_wintab, "WTInfoA" );
+
+ // let's see if we can initialize tablet here
+ /* check if WinTab available. */
+ if (fpWTInfo) {
+ AXIS Pressure, Orientation[3]; /* The maximum tablet size */
+
+ BOOL pressureSupport = fpWTInfo (WTI_DEVICES, DVC_NPRESSURE, &Pressure);
+ if (pressureSupport)
+ m_maxPressure = Pressure.axMax;
+ else
+ m_maxPressure = 0;
+
+ BOOL tiltSupport = fpWTInfo (WTI_DEVICES, DVC_ORIENTATION, &Orientation);
+ if (tiltSupport) {
+ /* does the tablet support azimuth ([0]) and altitude ([1]) */
+ if (Orientation[0].axResolution && Orientation[1].axResolution) {
+ m_maxAzimuth = Orientation[0].axMax;
+ m_maxAltitude = Orientation[1].axMax;
+ }
+ else { /* no so dont do tilt stuff */
+ m_maxAzimuth = m_maxAltitude = 0;
+ }
+ }
+
+ m_tabletData->Active = 0;
+ }
+ }
+}
+
+void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam)
+{
+ PACKET pkt;
+ if (m_wintab) {
+ GHOST_WIN32_WTPacket fpWTPacket = ( GHOST_WIN32_WTPacket ) ::GetProcAddress( m_wintab, "WTPacket" );
+ if (fpWTPacket) {
+ if (fpWTPacket((HCTX)lParam, wParam, &pkt)) {
+ if (m_tabletData) {
+ switch (pkt.pkCursor) {
+ case 0: /* first device */
+ case 3: /* second device */
+ m_tabletData->Active = 0; /* puck - not yet supported */
+ break;
+ case 1:
+ case 4:
+ m_tabletData->Active = 1; /* stylus */
+ break;
+ case 2:
+ case 5:
+ m_tabletData->Active = 2; /* eraser */
+ break;
+ }
+ if (m_maxPressure > 0) {
+ m_tabletData->Pressure = (float)pkt.pkNormalPressure / (float)m_maxPressure;
+ } else {
+ m_tabletData->Pressure = 1.0f;
+ }
+
+ if ((m_maxAzimuth > 0) && (m_maxAltitude > 0)) {
+ ORIENTATION ort = pkt.pkOrientation;
+ float vecLen;
+ float altRad, azmRad; /* in radians */
+
+ /*
+ from the wintab spec:
+ orAzimuth Specifies the clockwise rotation of the
+ cursor about the z axis through a full circular range.
+
+ orAltitude Specifies the angle with the x-y plane
+ through a signed, semicircular range. Positive values
+ specify an angle upward toward the positive z axis;
+ negative values specify an angle downward toward the negative z axis.
+
+ wintab.h defines .orAltitude as a UINT but documents .orAltitude
+ as positive for upward angles and negative for downward angles.
+ WACOM uses negative altitude values to show that the pen is inverted;
+ therefore we cast .orAltitude as an (int) and then use the absolute value.
+ */
+
+ /* convert raw fixed point data to radians */
+ altRad = (fabs((float)ort.orAltitude)/(float)m_maxAltitude) * M_PI/2.0;
+ azmRad = ((float)ort.orAzimuth/(float)m_maxAzimuth) * M_PI*2.0;
+
+ /* find length of the stylus' projected vector on the XY plane */
+ vecLen = cos(altRad);
+
+ /* from there calculate X and Y components based on azimuth */
+ m_tabletData->Xtilt = sin(azmRad) * vecLen;
+ m_tabletData->Ytilt = sin(M_PI/2.0 - azmRad) * vecLen;
+
+ } else {
+ m_tabletData->Xtilt = 0.0f;
+ m_tabletData->Ytilt = 0.0f;
+ }
+ }
+ }
+ }
+ }
+}
+
+/** Reverse the bits in a GHOST_TUns8 */
+static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
+{
+ ch= ((ch>>1)&0x55) | ((ch<<1)&0xAA);
+ ch= ((ch>>2)&0x33) | ((ch<<2)&0xCC);
+ ch= ((ch>>4)&0x0F) | ((ch<<4)&0xF0);
+ return ch;
+}
/** Reverse the bits in a GHOST_TUns16 */
static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
return shrt;
}
+GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2],
+ GHOST_TUns8 mask[16][2], int hotX, int hotY)
+{
+ return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*)mask,
+ 16, 16, hotX, hotY, 0, 1);
+}
-GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], GHOST_TUns8 mask[16][2], int hotX, int hotY)
+GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 *bitmap,
+ GHOST_TUns8 *mask, int sizeX, int sizeY, int hotX, int hotY,
+ int fg_color, int bg_color)
{
GHOST_TUns32 andData[32];
GHOST_TUns32 xorData[32];
- int y;
-
+ GHOST_TUns32 fullBitRow, fullMaskRow;
+ int x, y, cols;
+
+ cols=sizeX/8; /* Num of whole bytes per row (width of bm/mask) */
+ if (sizeX%8) cols++;
+
if (m_customCursor) {
DestroyCursor(m_customCursor);
m_customCursor = NULL;
memset(&andData, 0xFF, sizeof(andData));
memset(&xorData, 0, sizeof(xorData));
- for (y=0; y<16; y++) {
- GHOST_TUns32 fullBitRow = uns16ReverseBits((bitmap[y][0]<<8) | (bitmap[y][1]<<0));
- GHOST_TUns32 fullMaskRow = uns16ReverseBits((mask[y][0]<<8) | (mask[y][1]<<0));
-
+ for (y=0; y<sizeY; y++) {
+ fullBitRow=0;
+ fullMaskRow=0;
+ for (x=cols-1; x>=0; x--){
+ fullBitRow<<=8;
+ fullMaskRow<<=8;
+ fullBitRow |= uns8ReverseBits(bitmap[cols*y + x]);
+ fullMaskRow |= uns8ReverseBits( mask[cols*y + x]);
+ }
xorData[y]= fullBitRow & fullMaskRow;
andData[y]= ~fullMaskRow;
}
return GHOST_kSuccess;
}
+
+/* Ron Fosner's code for weighting pixel formats and forcing software.
+ See http://www.opengl.org/resources/faq/technical/weight.cpp */
+
+static int WeightPixelFormat(PIXELFORMATDESCRIPTOR& pfd) {
+ int weight = 0;
+
+ /* assume desktop color depth is 32 bits per pixel */
+
+ /* cull unusable pixel formats */
+ /* if no formats can be found, can we determine why it was rejected? */
+ if( !(pfd.dwFlags & PFD_SUPPORT_OPENGL) ||
+ !(pfd.dwFlags & PFD_DRAW_TO_WINDOW) ||
+ !(pfd.dwFlags & PFD_DOUBLEBUFFER) || /* Blender _needs_ this */
+ ( pfd.cDepthBits <= 8 ) ||
+ !(pfd.iPixelType == PFD_TYPE_RGBA) )
+ return 0;
+
+ weight = 1; /* it's usable */
+
+ /* the bigger the depth buffer the better */
+ /* give no weight to a 16-bit depth buffer, because those are crap */
+ weight += pfd.cDepthBits - 16;
+
+ weight += pfd.cColorBits - 8;
+
+ /* want swap copy capability -- it matters a lot */
+ if(pfd.dwFlags & PFD_SWAP_COPY) weight += 16;
+
+ /* but if it's a generic (not accelerated) view, it's really bad */
+ if(pfd.dwFlags & PFD_GENERIC_FORMAT) weight /= 10;
+
+ return weight;
+}
+
+/* A modification of Ron Fosner's replacement for ChoosePixelFormat */
+/* returns 0 on error, else returns the pixel format number to be used */
+static int EnumPixelFormats(HDC hdc) {
+ int iPixelFormat;
+ int i, n, w, weight = 0;
+ PIXELFORMATDESCRIPTOR pfd, pfd_fallback;
+
+ /* we need a device context to do anything */
+ if(!hdc) return 0;
+
+ iPixelFormat = 1; /* careful! PFD numbers are 1 based, not zero based */
+
+ /* obtain detailed information about
+ the device context's first pixel format */
+ n = 1+::DescribePixelFormat(hdc, iPixelFormat,
+ sizeof(PIXELFORMATDESCRIPTOR), &pfd);
+
+ /* choose a pixel format using the useless Windows function in case
+ we come up empty handed */
+ iPixelFormat = ::ChoosePixelFormat( hdc, &sPreferredFormat );
+
+ if(!iPixelFormat) return 0; /* couldn't find one to use */
+
+ for(i=1; i<=n; i++) { /* not the idiom, but it's right */
+ ::DescribePixelFormat( hdc, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd );
+ w = WeightPixelFormat(pfd);
+ if(w > weight) {
+ weight = w;
+ iPixelFormat = i;
+ }
+ }
+
+ return iPixelFormat;
+}
+
+