This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/intern/ghost/intern/GHOST_WindowWin32.cpp
Germano Cavalcante 0bf615fc24 GHOST: Optimize and simplify the creation of a GHOST_Context on Windows
Instead of cloning the window to create dummyHWNDs and dummyHDCs to avoid calling the SetPixelFormat more than once in the same window, use the original window and HDC and do not call the SetPixelFormat again.

In addition to avoiding a lot of unnecessary calls, it simplifies the code and makes it match the others OS
2018-02-10 00:32:13 -02:00

1073 lines
31 KiB
C++

/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Maarten Gribnau.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file ghost/intern/GHOST_WindowWin32.cpp
* \ingroup GHOST
*/
#define _USE_MATH_DEFINES
#include "GHOST_WindowWin32.h"
#include "GHOST_SystemWin32.h"
#include "GHOST_DropTargetWin32.h"
#include "GHOST_ContextNone.h"
#include "utfconv.h"
#include "utf_winfunc.h"
#if defined(WITH_GL_EGL)
# include "GHOST_ContextEGL.h"
#else
# include "GHOST_ContextWGL.h"
#endif
#ifdef WIN32_COMPOSITING
#include <Dwmapi.h>
#endif
#include <math.h>
#include <string.h>
#include <assert.h>
const wchar_t *GHOST_WindowWin32::s_windowClassName = L"GHOST_WindowClass";
const int GHOST_WindowWin32::s_maxTitleLength = 128;
/* force NVidia Optimus to used dedicated graphics */
extern "C" {
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
}
GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
const STR_String &title,
GHOST_TInt32 left,
GHOST_TInt32 top,
GHOST_TUns32 width,
GHOST_TUns32 height,
GHOST_TWindowState state,
GHOST_TDrawingContextType type,
bool wantStereoVisual,
bool alphaBackground,
GHOST_TUns16 wantNumOfAASamples,
GHOST_TEmbedderWindowID parentwindowhwnd,
bool is_debug)
: GHOST_Window(width, height, state,
wantStereoVisual, false, wantNumOfAASamples),
m_inLiveResize(false),
m_system(system),
m_hDC(0),
m_hasMouseCaptured(false),
m_hasGrabMouse(false),
m_nPressedButtons(0),
m_customCursor(0),
m_wantAlphaBackground(alphaBackground),
m_wintab(NULL),
m_tabletData(NULL),
m_tablet(0),
m_maxPressure(0),
m_normal_state(GHOST_kWindowStateNormal),
m_user32(NULL),
m_parentWindowHwnd(parentwindowhwnd),
m_debug_context(is_debug)
{
if (state != GHOST_kWindowStateFullScreen) {
RECT rect;
MONITORINFO monitor;
GHOST_TUns32 tw, th;
#ifndef _MSC_VER
int cxsizeframe = GetSystemMetrics(SM_CXSIZEFRAME);
int cysizeframe = GetSystemMetrics(SM_CYSIZEFRAME);
#else
// MSVC 2012+ returns bogus values from GetSystemMetrics, bug in Windows
// http://connect.microsoft.com/VisualStudio/feedback/details/753224/regression-getsystemmetrics-delivers-different-values
RECT cxrect = {0, 0, 0, 0};
AdjustWindowRectEx(&cxrect, WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_THICKFRAME | WS_DLGFRAME, FALSE, 0);
int cxsizeframe = abs(cxrect.bottom);
int cysizeframe = abs(cxrect.left);
#endif
width += cxsizeframe * 2;
height += cysizeframe * 2 + GetSystemMetrics(SM_CYCAPTION);
rect.left = left;
rect.right = left + width;
rect.top = top;
rect.bottom = top + height;
monitor.cbSize = sizeof(monitor);
monitor.dwFlags = 0;
// take taskbar into account
GetMonitorInfo(MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST), &monitor);
th = monitor.rcWork.bottom - monitor.rcWork.top;
tw = monitor.rcWork.right - monitor.rcWork.left;
if (tw < width) {
width = tw;
left = monitor.rcWork.left;
}
else if (monitor.rcWork.right < left + (int)width)
left = monitor.rcWork.right - width;
else if (left < monitor.rcWork.left)
left = monitor.rcWork.left;
if (th < height) {
height = th;
top = monitor.rcWork.top;
}
else if (monitor.rcWork.bottom < top + (int)height)
top = monitor.rcWork.bottom - height;
else if (top < monitor.rcWork.top)
top = monitor.rcWork.top;
int wintype = WS_OVERLAPPEDWINDOW;
if (m_parentWindowHwnd != 0) {
wintype = WS_CHILD;
GetWindowRect((HWND)m_parentWindowHwnd, &rect);
left = 0;
top = 0;
width = rect.right - rect.left;
height = rect.bottom - rect.top;
}
wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0);
m_hWnd = ::CreateWindowW(
s_windowClassName, // pointer to registered class name
title_16, // pointer to window name
wintype, // window style
left, // horizontal position of window
top, // vertical position of window
width, // window width
height, // window height
(HWND)m_parentWindowHwnd, // handle to parent or owner window
0, // handle to menu or child-window identifier
::GetModuleHandle(0), // handle to application instance
0); // pointer to window-creation data
free(title_16);
}
else {
wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0);
m_hWnd = ::CreateWindowW(
s_windowClassName, // pointer to registered class name
title_16, // pointer to window name
WS_POPUP | WS_MAXIMIZE, // window style
left, // horizontal position of window
top, // vertical position of window
width, // window width
height, // window height
HWND_DESKTOP, // handle to parent or owner window
0, // handle to menu or child-window identifier
::GetModuleHandle(0), // handle to application instance
0); // pointer to window-creation data
free(title_16);
}
if (m_hWnd) {
// Register this window as a droptarget. Requires m_hWnd to be valid.
// Note that OleInitialize(0) has to be called prior to this. Done in GHOST_SystemWin32.
m_dropTarget = new GHOST_DropTargetWin32(this, m_system);
if (m_dropTarget) {
::RegisterDragDrop(m_hWnd, m_dropTarget);
}
// Store a pointer to this class in the window structure
::SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR) this);
// Store the device context
m_hDC = ::GetDC(m_hWnd);
GHOST_TSuccess success = setDrawingContextType(type);
if (success) {
// Show the window
int nCmdShow;
switch (state) {
case GHOST_kWindowStateMaximized:
nCmdShow = SW_SHOWMAXIMIZED;
break;
case GHOST_kWindowStateMinimized:
nCmdShow = SW_SHOWMINIMIZED;
break;
case GHOST_kWindowStateNormal:
default:
nCmdShow = SW_SHOWNORMAL;
break;
}
::ShowWindow(m_hWnd, nCmdShow);
#ifdef WIN32_COMPOSITING
if (alphaBackground && parentwindowhwnd == 0) {
HRESULT hr = S_OK;
// Create and populate the Blur Behind structure
DWM_BLURBEHIND bb = { 0 };
// Enable Blur Behind and apply to the entire client area
bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
bb.fEnable = true;
bb.hRgnBlur = CreateRectRgn(0, 0, -1, -1);
// Apply Blur Behind
hr = DwmEnableBlurBehindWindow(m_hWnd, &bb);
DeleteObject(bb.hRgnBlur);
}
#endif
// Force an initial paint of the window
::UpdateWindow(m_hWnd);
}
else {
//invalidate the window
::DestroyWindow(m_hWnd);
m_hWnd = NULL;
}
}
if (parentwindowhwnd != 0) {
RAWINPUTDEVICE device = {0};
device.usUsagePage = 0x01; /* usUsagePage & usUsage for keyboard*/
device.usUsage = 0x06; /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */
device.dwFlags |= RIDEV_INPUTSINK; // makes WM_INPUT is visible for ghost when has parent window
device.hwndTarget = m_hWnd;
RegisterRawInputDevices(&device, 1, sizeof(device));
}
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;
/* The maximum tablet size, pressure and orientation (tilt) */
AXIS TabletX, TabletY, Pressure, Orientation[3];
// 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;
}
}
if (fpWTOpen) {
m_tablet = fpWTOpen(m_hWnd, &lc, TRUE);
if (m_tablet) {
m_tabletData = new GHOST_TabletData();
m_tabletData->Active = GHOST_kTabletModeNone;
}
}
}
}
CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (LPVOID *)&m_Bar);
}
GHOST_WindowWin32::~GHOST_WindowWin32()
{
if (m_Bar) {
m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
m_Bar->Release();
}
if (m_wintab) {
GHOST_WIN32_WTClose fpWTClose = (GHOST_WIN32_WTClose) ::GetProcAddress(m_wintab, "WTClose");
if (fpWTClose) {
if (m_tablet)
fpWTClose(m_tablet);
delete m_tabletData;
m_tabletData = NULL;
}
}
if (m_customCursor) {
DestroyCursor(m_customCursor);
m_customCursor = NULL;
}
if (m_hWnd != NULL && m_hDC != NULL && releaseNativeHandles()) {
::ReleaseDC(m_hWnd, m_hDC);
}
if (m_hWnd) {
if (m_dropTarget) {
// Disable DragDrop
RevokeDragDrop(m_hWnd);
// Release our reference of the DropTarget and it will delete itself eventually.
m_dropTarget->Release();
}
::SetWindowLongPtr(m_hWnd, GWLP_USERDATA, NULL);
::DestroyWindow(m_hWnd);
m_hWnd = 0;
}
}
bool GHOST_WindowWin32::getValid() const
{
return GHOST_Window::getValid() && m_hWnd != 0 && m_hDC != 0;
}
HWND GHOST_WindowWin32::getHWND() const
{
return m_hWnd;
}
void GHOST_WindowWin32::setTitle(const STR_String &title)
{
wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0);
::SetWindowTextW(m_hWnd, (wchar_t *)title_16);
free(title_16);
}
void GHOST_WindowWin32::getTitle(STR_String &title) const
{
char buf[s_maxTitleLength]; /*CHANGE + never used yet*/
::GetWindowText(m_hWnd, buf, s_maxTitleLength);
STR_String temp(buf);
title = buf;
}
void GHOST_WindowWin32::getWindowBounds(GHOST_Rect &bounds) const
{
RECT rect;
::GetWindowRect(m_hWnd, &rect);
bounds.m_b = rect.bottom;
bounds.m_l = rect.left;
bounds.m_r = rect.right;
bounds.m_t = rect.top;
}
void GHOST_WindowWin32::getClientBounds(GHOST_Rect &bounds) const
{
RECT rect;
POINT coord;
if (!IsIconic(m_hWnd)) {
::GetClientRect(m_hWnd, &rect);
coord.x = rect.left;
coord.y = rect.top;
::ClientToScreen(m_hWnd, &coord);
bounds.m_l = coord.x;
bounds.m_t = coord.y;
coord.x = rect.right;
coord.y = rect.bottom;
::ClientToScreen(m_hWnd, &coord);
bounds.m_r = coord.x;
bounds.m_b = coord.y;
}
else {
bounds.m_b = 0;
bounds.m_l = 0;
bounds.m_r = 0;
bounds.m_t = 0;
}
}
GHOST_TSuccess GHOST_WindowWin32::setClientWidth(GHOST_TUns32 width)
{
GHOST_TSuccess success;
GHOST_Rect cBnds, wBnds;
getClientBounds(cBnds);
if (cBnds.getWidth() != (GHOST_TInt32)width) {
getWindowBounds(wBnds);
int cx = wBnds.getWidth() + width - cBnds.getWidth();
int cy = wBnds.getHeight();
success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
GHOST_kSuccess : GHOST_kFailure;
}
else {
success = GHOST_kSuccess;
}
return success;
}
GHOST_TSuccess GHOST_WindowWin32::setClientHeight(GHOST_TUns32 height)
{
GHOST_TSuccess success;
GHOST_Rect cBnds, wBnds;
getClientBounds(cBnds);
if (cBnds.getHeight() != (GHOST_TInt32)height) {
getWindowBounds(wBnds);
int cx = wBnds.getWidth();
int cy = wBnds.getHeight() + height - cBnds.getHeight();
success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
GHOST_kSuccess : GHOST_kFailure;
}
else {
success = GHOST_kSuccess;
}
return success;
}
GHOST_TSuccess GHOST_WindowWin32::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
{
GHOST_TSuccess success;
GHOST_Rect cBnds, wBnds;
getClientBounds(cBnds);
if ((cBnds.getWidth() != (GHOST_TInt32)width) || (cBnds.getHeight() != (GHOST_TInt32)height)) {
getWindowBounds(wBnds);
int cx = wBnds.getWidth() + width - cBnds.getWidth();
int cy = wBnds.getHeight() + height - cBnds.getHeight();
success = ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER) ?
GHOST_kSuccess : GHOST_kFailure;
}
else {
success = GHOST_kSuccess;
}
return success;
}
GHOST_TWindowState GHOST_WindowWin32::getState() const
{
GHOST_TWindowState state;
// XXX 27.04.2011
// we need to find a way to combine parented windows + resizing if we simply set the
// state as GHOST_kWindowStateEmbedded we will need to check for them somewhere else.
// It's also strange that in Windows is the only platform we need to make this separation.
if (m_parentWindowHwnd != 0) {
state = GHOST_kWindowStateEmbedded;
return state;
}
if (::IsIconic(m_hWnd)) {
state = GHOST_kWindowStateMinimized;
}
else if (::IsZoomed(m_hWnd)) {
LONG_PTR result = ::GetWindowLongPtr(m_hWnd, GWL_STYLE);
if ((result & (WS_POPUP | WS_MAXIMIZE)) != (WS_POPUP | WS_MAXIMIZE))
state = GHOST_kWindowStateMaximized;
else
state = GHOST_kWindowStateFullScreen;
}
else {
state = GHOST_kWindowStateNormal;
}
return state;
}
void GHOST_WindowWin32::screenToClient(
GHOST_TInt32 inX, GHOST_TInt32 inY,
GHOST_TInt32 &outX, GHOST_TInt32 &outY) const
{
POINT point = {inX, inY};
::ScreenToClient(m_hWnd, &point);
outX = point.x;
outY = point.y;
}
void GHOST_WindowWin32::clientToScreen(
GHOST_TInt32 inX, GHOST_TInt32 inY,
GHOST_TInt32 &outX, GHOST_TInt32 &outY) const
{
POINT point = {inX, inY};
::ClientToScreen(m_hWnd, &point);
outX = point.x;
outY = point.y;
}
GHOST_TSuccess GHOST_WindowWin32::setState(GHOST_TWindowState state)
{
GHOST_TWindowState curstate = getState();
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
::GetWindowPlacement(m_hWnd, &wp);
if (state == GHOST_kWindowStateNormal)
state = m_normal_state;
switch (state) {
case GHOST_kWindowStateMinimized:
wp.showCmd = SW_SHOWMINIMIZED;
break;
case GHOST_kWindowStateMaximized:
wp.showCmd = SW_SHOWMAXIMIZED;
::SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
break;
case GHOST_kWindowStateFullScreen:
if (curstate != state && curstate != GHOST_kWindowStateMinimized)
m_normal_state = curstate;
wp.showCmd = SW_SHOWMAXIMIZED;
wp.ptMaxPosition.x = 0;
wp.ptMaxPosition.y = 0;
::SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_POPUP | WS_MAXIMIZE);
break;
case GHOST_kWindowStateEmbedded:
::SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_CHILD);
break;
case GHOST_kWindowStateNormal:
default:
wp.showCmd = SW_SHOWNORMAL;
::SetWindowLongPtr(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
break;
}
/* Clears window cache for SetWindowLongPtr */
::SetWindowPos(m_hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
return ::SetWindowPlacement(m_hWnd, &wp) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
}
GHOST_TSuccess GHOST_WindowWin32::setOrder(GHOST_TWindowOrder order)
{
HWND hWndInsertAfter, hWndToRaise;
if (order == GHOST_kWindowOrderBottom) {
hWndInsertAfter = HWND_BOTTOM;
hWndToRaise = ::GetWindow(m_hWnd, GW_HWNDNEXT); /* the window to raise */
}
else {
hWndInsertAfter = HWND_TOP;
hWndToRaise = NULL;
}
if (::SetWindowPos(m_hWnd, hWndInsertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) == FALSE) {
return GHOST_kFailure;
}
if (hWndToRaise && ::SetWindowPos(hWndToRaise, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) == FALSE) {
return GHOST_kFailure;
}
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_WindowWin32::invalidate()
{
GHOST_TSuccess success;
if (m_hWnd) {
success = ::InvalidateRect(m_hWnd, 0, FALSE) != 0 ? GHOST_kSuccess : GHOST_kFailure;
}
else {
success = GHOST_kFailure;
}
return success;
}
GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType type)
{
if (type == GHOST_kDrawingContextTypeOpenGL) {
GHOST_Context *context;
#if defined(WITH_GL_PROFILE_CORE)
/* - AMD and Intel give us exactly this version
* - NVIDIA gives at least this version <-- desired behavior
* So we ask for 4.5, 4.4 ... 3.3 in descending order to get the best version on the user's system. */
for (int minor = 5; minor >= 0; --minor) {
context = new GHOST_ContextWGL(
m_wantStereoVisual,
m_wantAlphaBackground,
m_wantNumOfAASamples,
m_hWnd,
m_hDC,
WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
4, minor,
(m_debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
if (context->initializeDrawingContext()) {
return context;
}
else {
delete context;
}
}
context = new GHOST_ContextWGL(
m_wantStereoVisual,
m_wantAlphaBackground,
m_wantNumOfAASamples,
m_hWnd,
m_hDC,
WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
3, 3,
(m_debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
if (context->initializeDrawingContext()) {
return context;
}
else {
MessageBox(
m_hWnd,
"Blender requires a graphics driver with at least OpenGL 3.3 support.\n\n"
"The program will now close.",
"Blender - Unsupported Graphics Driver!",
MB_OK | MB_ICONERROR);
delete context;
exit(0);
}
#elif defined(WITH_GL_PROFILE_COMPAT)
// ask for 2.1 context, driver gives any GL version >= 2.1 (hopefully the latest compatibility profile)
// 2.1 ignores the profile bit & is incompatible with core profile
context = new GHOST_ContextWGL(
m_wantStereoVisual,
m_wantAlphaBackground,
m_wantNumOfAASamples,
m_hWnd,
m_hDC,
0, // no profile bit
2, 1,
(m_debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY);
if (context->initializeDrawingContext()) {
return context;
}
else {
delete context;
}
#else
# error // must specify either core or compat at build time
#endif
}
return NULL;
}
void GHOST_WindowWin32::lostMouseCapture()
{
if (m_hasMouseCaptured) {
m_hasGrabMouse = false;
m_nPressedButtons = 0;
m_hasMouseCaptured = false;
}
}
void GHOST_WindowWin32::registerMouseClickEvent(int press)
{
switch (press) {
case 0: m_nPressedButtons++; break;
case 1: if (m_nPressedButtons) m_nPressedButtons--; break;
case 2: m_hasGrabMouse = true; break;
case 3: m_hasGrabMouse = false; break;
}
if (!m_nPressedButtons && !m_hasGrabMouse && m_hasMouseCaptured) {
::ReleaseCapture();
m_hasMouseCaptured = false;
}
else if ((m_nPressedButtons || m_hasGrabMouse) && !m_hasMouseCaptured) {
::SetCapture(m_hWnd);
m_hasMouseCaptured = true;
}
}
void GHOST_WindowWin32::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
{
if (!visible) {
while (::ShowCursor(FALSE) >= 0) ;
}
else {
while (::ShowCursor(TRUE) < 0) ;
}
if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
::SetCursor(m_customCursor);
}
else {
// Convert GHOST cursor to Windows OEM cursor
bool success = true;
LPCSTR id;
switch (cursor) {
case GHOST_kStandardCursorDefault: id = IDC_ARROW; break;
case GHOST_kStandardCursorRightArrow: id = IDC_ARROW; break;
case GHOST_kStandardCursorLeftArrow: id = IDC_ARROW; break;
case GHOST_kStandardCursorInfo: id = IDC_SIZEALL; break; // Four-pointed arrow pointing north, south, east, and west
case GHOST_kStandardCursorDestroy: id = IDC_NO; break; // Slashed circle
case GHOST_kStandardCursorHelp: id = IDC_HELP; break; // Arrow and question mark
case GHOST_kStandardCursorCycle: id = IDC_NO; break; // Slashed circle
case GHOST_kStandardCursorSpray: id = IDC_SIZEALL; break; // Four-pointed arrow pointing north, south, east, and west
case GHOST_kStandardCursorWait: id = IDC_WAIT; break; // Hourglass
case GHOST_kStandardCursorText: id = IDC_IBEAM; break; // I-beam
case GHOST_kStandardCursorCrosshair: id = IDC_CROSS; break; // Crosshair
case GHOST_kStandardCursorUpDown: id = IDC_SIZENS; break; // Double-pointed arrow pointing north and south
case GHOST_kStandardCursorLeftRight: id = IDC_SIZEWE; break; // Double-pointed arrow pointing west and east
case GHOST_kStandardCursorTopSide: id = IDC_UPARROW; break; // Vertical arrow
case GHOST_kStandardCursorBottomSide: id = IDC_SIZENS; break;
case GHOST_kStandardCursorLeftSide: id = IDC_SIZEWE; break;
case GHOST_kStandardCursorTopLeftCorner: id = IDC_SIZENWSE; 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_ARROW; break;
case GHOST_kStandardCursorCopy: id = IDC_ARROW; break;
default:
success = false;
}
if (success) {
::SetCursor(::LoadCursor(0, id));
}
}
}
GHOST_TSuccess GHOST_WindowWin32::setWindowCursorVisibility(bool visible)
{
if (::GetForegroundWindow() == m_hWnd) {
loadCursor(visible, getCursorShape());
}
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
{
if (mode != GHOST_kGrabDisable) {
if (mode != GHOST_kGrabNormal) {
m_system->getCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
setCursorGrabAccum(0, 0);
if (mode == GHOST_kGrabHide)
setWindowCursorVisibility(false);
}
registerMouseClickEvent(2);
}
else {
if (m_cursorGrab == GHOST_kGrabHide) {
m_system->setCursorPosition(m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
setWindowCursorVisibility(true);
}
if (m_cursorGrab != GHOST_kGrabNormal) {
/* use to generate a mouse move event, otherwise the last event
* blender gets can be outside the screen causing menus not to show
* properly unless the user moves the mouse */
GHOST_TInt32 pos[2];
m_system->getCursorPosition(pos[0], pos[1]);
m_system->setCursorPosition(pos[0], pos[1]);
}
/* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
setCursorGrabAccum(0, 0);
m_cursorGrabBounds.m_l = m_cursorGrabBounds.m_r = -1; /* disable */
registerMouseClickEvent(3);
}
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape)
{
if (m_customCursor) {
DestroyCursor(m_customCursor);
m_customCursor = NULL;
}
if (::GetForegroundWindow() == m_hWnd) {
loadCursor(getCursorVisibility(), cursorShape);
}
return GHOST_kSuccess;
}
void GHOST_WindowWin32::processWin32TabletInitEvent()
{
if (m_wintab && m_tabletData) {
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 = GHOST_kTabletModeNone;
}
}
}
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 % 3) { /* % 3 for multiple devices ("DualTrack") */
case 0:
m_tabletData->Active = GHOST_kTabletModeNone; /* puck - not yet supported */
break;
case 1:
m_tabletData->Active = GHOST_kTabletModeStylus; /* stylus */
break;
case 2:
m_tabletData->Active = GHOST_kTabletModeEraser; /* 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 = (float)((fabs((float)ort.orAltitude) / (float)m_maxAltitude) * M_PI / 2.0);
azmRad = (float)(((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 = (float)(sin(M_PI / 2.0 - azmRad) * vecLen);
}
else {
m_tabletData->Xtilt = 0.0f;
m_tabletData->Ytilt = 0.0f;
}
}
}
}
}
}
void GHOST_WindowWin32::bringTabletContextToFront()
{
if (m_wintab) {
GHOST_WIN32_WTOverlap fpWTOverlap = (GHOST_WIN32_WTOverlap) ::GetProcAddress(m_wintab, "WTOverlap");
if (fpWTOverlap) {
fpWTOverlap(m_tablet, TRUE);
}
}
}
GHOST_TUns16 GHOST_WindowWin32::getDPIHint()
{
if (!m_user32) {
m_user32 = ::LoadLibrary("user32.dll");
}
if (m_user32) {
GHOST_WIN32_GetDpiForWindow fpGetDpiForWindow = (GHOST_WIN32_GetDpiForWindow) ::GetProcAddress(m_user32, "GetDpiForWindow");
if (fpGetDpiForWindow) {
return fpGetDpiForWindow(this->m_hWnd);
}
}
return USER_DEFAULT_SCREEN_DPI;
}
/** 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;
}
#if 0 /* UNUSED */
/** Reverse the bits in a GHOST_TUns16 */
static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
{
shrt = ((shrt >> 1) & 0x5555) | ((shrt << 1) & 0xAAAA);
shrt = ((shrt >> 2) & 0x3333) | ((shrt << 2) & 0xCCCC);
shrt = ((shrt >> 4) & 0x0F0F) | ((shrt << 4) & 0xF0F0);
shrt = ((shrt >> 8) & 0x00FF) | ((shrt << 8) & 0xFF00);
return shrt;
}
#endif
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,
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];
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 < 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;
}
m_customCursor = ::CreateCursor(::GetModuleHandle(0), hotX, hotY, 32, 32, andData, xorData);
if (!m_customCursor) {
return GHOST_kFailure;
}
if (::GetForegroundWindow() == m_hWnd) {
loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
}
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_WindowWin32::setProgressBar(float progress)
{
/*SetProgressValue sets state to TBPF_NORMAL automaticly*/
if (m_Bar && S_OK == m_Bar->SetProgressValue(m_hWnd, 10000 * progress, 10000))
return GHOST_kSuccess;
return GHOST_kFailure;
}
GHOST_TSuccess GHOST_WindowWin32::endProgressBar()
{
if (m_Bar && S_OK == m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS))
return GHOST_kSuccess;
return GHOST_kFailure;
}
#ifdef WITH_INPUT_IME
void GHOST_WindowWin32::beginIME(GHOST_TInt32 x, GHOST_TInt32 y, GHOST_TInt32 w, GHOST_TInt32 h, int completed)
{
m_imeImput.BeginIME(m_hWnd, GHOST_Rect(x, y - h, x, y), (bool)completed);
}
void GHOST_WindowWin32::endIME()
{
m_imeImput.EndIME(m_hWnd);
}
#endif /* WITH_INPUT_IME */