Also replace integer with bool in Ghost API when only used as boolean, and uint8* with char* in Ghost API when variable is a string. Reviewed By: brecht Differential Revision: https://developer.blender.org/D11617 Signed-off-by: Nicholas Rishel <rishel.nick@gmail.com>
1123 lines
35 KiB
C++
1123 lines
35 KiB
C++
/*
|
|
* 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.
|
|
*/
|
|
|
|
/** \file
|
|
* \ingroup GHOST
|
|
*/
|
|
|
|
#include "GHOST_WindowWin32.h"
|
|
#include "GHOST_ContextD3D.h"
|
|
#include "GHOST_ContextNone.h"
|
|
#include "GHOST_DropTargetWin32.h"
|
|
#include "GHOST_SystemWin32.h"
|
|
#include "GHOST_WindowManager.h"
|
|
#include "utf_winfunc.h"
|
|
#include "utfconv.h"
|
|
|
|
#include "GHOST_ContextWGL.h"
|
|
|
|
#ifdef WIN32_COMPOSITING
|
|
# include <Dwmapi.h>
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <shellscalingapi.h>
|
|
#include <string.h>
|
|
#include <windowsx.h>
|
|
|
|
#ifndef GET_POINTERID_WPARAM
|
|
# define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam))
|
|
#endif // GET_POINTERID_WPARAM
|
|
|
|
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 char *title,
|
|
int32_t left,
|
|
int32_t top,
|
|
uint32_t width,
|
|
uint32_t height,
|
|
GHOST_TWindowState state,
|
|
GHOST_TDrawingContextType type,
|
|
bool wantStereoVisual,
|
|
bool alphaBackground,
|
|
GHOST_WindowWin32 *parentwindow,
|
|
bool is_debug,
|
|
bool dialog)
|
|
: GHOST_Window(width, height, state, wantStereoVisual, false),
|
|
m_mousePresent(false),
|
|
m_inLiveResize(false),
|
|
m_system(system),
|
|
m_hDC(0),
|
|
m_isDialog(dialog),
|
|
m_hasMouseCaptured(false),
|
|
m_hasGrabMouse(false),
|
|
m_nPressedButtons(0),
|
|
m_customCursor(0),
|
|
m_wantAlphaBackground(alphaBackground),
|
|
m_wintab(NULL),
|
|
m_lastPointerTabletData(GHOST_TABLET_DATA_NONE),
|
|
m_normal_state(GHOST_kWindowStateNormal),
|
|
m_user32(::LoadLibrary("user32.dll")),
|
|
m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : HWND_DESKTOP),
|
|
m_debug_context(is_debug)
|
|
{
|
|
DWORD style = parentwindow ?
|
|
WS_POPUPWINDOW | WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX :
|
|
WS_OVERLAPPEDWINDOW;
|
|
|
|
if (state == GHOST_kWindowStateFullScreen) {
|
|
style |= WS_MAXIMIZE;
|
|
}
|
|
|
|
/* Forces owned windows onto taskbar and allows minimization. */
|
|
DWORD extended_style = parentwindow ? WS_EX_APPWINDOW : 0;
|
|
|
|
if (dialog) {
|
|
/* When we are ready to make windows of this type:
|
|
* style = WS_POPUPWINDOW | WS_CAPTION
|
|
* extended_style = WS_EX_DLGMODALFRAME | WS_EX_TOPMOST
|
|
*/
|
|
}
|
|
|
|
RECT win_rect = {left, top, (long)(left + width), (long)(top + height)};
|
|
adjustWindowRectForClosestMonitor(&win_rect, style, extended_style);
|
|
|
|
wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0);
|
|
m_hWnd = ::CreateWindowExW(extended_style, // window extended style
|
|
s_windowClassName, // pointer to registered class name
|
|
title_16, // pointer to window name
|
|
style, // window style
|
|
win_rect.left, // horizontal position of window
|
|
win_rect.top, // vertical position of window
|
|
win_rect.right - win_rect.left, // window width
|
|
win_rect.bottom - win_rect.top, // window height
|
|
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);
|
|
|
|
if (m_hWnd == NULL) {
|
|
return;
|
|
}
|
|
|
|
/* Store the device context. */
|
|
m_hDC = ::GetDC(m_hWnd);
|
|
|
|
if (!setDrawingContextType(type)) {
|
|
::DestroyWindow(m_hWnd);
|
|
m_hWnd = NULL;
|
|
return;
|
|
}
|
|
|
|
RegisterTouchWindow(m_hWnd, 0);
|
|
|
|
/* Register as drop-target. #OleInitialize(0) required first, done in GHOST_SystemWin32. */
|
|
m_dropTarget = new GHOST_DropTargetWin32(this, m_system);
|
|
::RegisterDragDrop(m_hWnd, m_dropTarget);
|
|
|
|
/* Store a pointer to this class in the window structure. */
|
|
::SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR)this);
|
|
|
|
if (!m_system->m_windowFocus) {
|
|
/* If we don't want focus then lower to bottom. */
|
|
::SetWindowPos(m_hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
|
}
|
|
|
|
/* Show the window. */
|
|
int nCmdShow;
|
|
switch (state) {
|
|
case GHOST_kWindowStateMaximized:
|
|
nCmdShow = SW_SHOWMAXIMIZED;
|
|
break;
|
|
case GHOST_kWindowStateMinimized:
|
|
nCmdShow = (m_system->m_windowFocus) ? SW_SHOWMINIMIZED : SW_SHOWMINNOACTIVE;
|
|
break;
|
|
case GHOST_kWindowStateNormal:
|
|
default:
|
|
nCmdShow = (m_system->m_windowFocus) ? SW_SHOWNORMAL : SW_SHOWNOACTIVATE;
|
|
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);
|
|
|
|
/* Initialize Wintab. */
|
|
if (system->getTabletAPI() != GHOST_kTabletWinPointer) {
|
|
loadWintab(GHOST_kWindowStateMinimized != state);
|
|
}
|
|
|
|
/* Allow the showing of a progress bar on the taskbar. */
|
|
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();
|
|
m_Bar = NULL;
|
|
}
|
|
|
|
closeWintab();
|
|
|
|
if (m_user32) {
|
|
FreeLibrary(m_user32);
|
|
m_user32 = NULL;
|
|
}
|
|
|
|
if (m_customCursor) {
|
|
DestroyCursor(m_customCursor);
|
|
m_customCursor = NULL;
|
|
}
|
|
|
|
if (m_hWnd != NULL && m_hDC != NULL && releaseNativeHandles()) {
|
|
::ReleaseDC(m_hWnd, m_hDC);
|
|
m_hDC = NULL;
|
|
}
|
|
|
|
if (m_hWnd) {
|
|
/* If this window is referenced by others as parent, clear that relation or windows will free
|
|
* the handle while we still reference it. */
|
|
for (GHOST_IWindow *iter_win : m_system->getWindowManager()->getWindows()) {
|
|
GHOST_WindowWin32 *iter_winwin = (GHOST_WindowWin32 *)iter_win;
|
|
if (iter_winwin->m_parentWindowHwnd == m_hWnd) {
|
|
::SetWindowLongPtr(iter_winwin->m_hWnd, GWLP_HWNDPARENT, NULL);
|
|
iter_winwin->m_parentWindowHwnd = 0;
|
|
}
|
|
}
|
|
|
|
if (m_dropTarget) {
|
|
// Disable DragDrop
|
|
RevokeDragDrop(m_hWnd);
|
|
// Release our reference of the DropTarget and it will delete itself eventually.
|
|
m_dropTarget->Release();
|
|
m_dropTarget = NULL;
|
|
}
|
|
::SetWindowLongPtr(m_hWnd, GWLP_USERDATA, NULL);
|
|
::DestroyWindow(m_hWnd);
|
|
m_hWnd = 0;
|
|
}
|
|
}
|
|
|
|
void GHOST_WindowWin32::adjustWindowRectForClosestMonitor(LPRECT win_rect,
|
|
DWORD dwStyle,
|
|
DWORD dwExStyle)
|
|
{
|
|
/* Get Details of the closest monitor. */
|
|
HMONITOR hmonitor = MonitorFromRect(win_rect, MONITOR_DEFAULTTONEAREST);
|
|
MONITORINFOEX monitor;
|
|
monitor.cbSize = sizeof(MONITORINFOEX);
|
|
monitor.dwFlags = 0;
|
|
GetMonitorInfo(hmonitor, &monitor);
|
|
|
|
/* Constrain requested size and position to fit within this monitor. */
|
|
LONG width = min(monitor.rcWork.right - monitor.rcWork.left, win_rect->right - win_rect->left);
|
|
LONG height = min(monitor.rcWork.bottom - monitor.rcWork.top, win_rect->bottom - win_rect->top);
|
|
win_rect->left = min(max(monitor.rcWork.left, win_rect->left), monitor.rcWork.right - width);
|
|
win_rect->right = win_rect->left + width;
|
|
win_rect->top = min(max(monitor.rcWork.top, win_rect->top), monitor.rcWork.bottom - height);
|
|
win_rect->bottom = win_rect->top + height;
|
|
|
|
/* With Windows 10 and newer we can adjust for chrome that differs with DPI and scale. */
|
|
GHOST_WIN32_AdjustWindowRectExForDpi fpAdjustWindowRectExForDpi = nullptr;
|
|
if (m_user32) {
|
|
fpAdjustWindowRectExForDpi = (GHOST_WIN32_AdjustWindowRectExForDpi)::GetProcAddress(
|
|
m_user32, "AdjustWindowRectExForDpi");
|
|
}
|
|
|
|
/* Adjust to allow for caption, borders, shadows, scaling, etc. Resulting values can be
|
|
* correctly outside of monitor bounds. Note: You cannot specify WS_OVERLAPPED when calling. */
|
|
if (fpAdjustWindowRectExForDpi) {
|
|
UINT dpiX, dpiY;
|
|
GetDpiForMonitor(hmonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
|
|
fpAdjustWindowRectExForDpi(win_rect, dwStyle & ~WS_OVERLAPPED, FALSE, dwExStyle, dpiX);
|
|
}
|
|
else {
|
|
AdjustWindowRectEx(win_rect, dwStyle & ~WS_OVERLAPPED, FALSE, dwExStyle);
|
|
}
|
|
|
|
/* But never allow a top position that can hide part of the title bar. */
|
|
win_rect->top = max(monitor.rcWork.top, win_rect->top);
|
|
}
|
|
|
|
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 char *title)
|
|
{
|
|
wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0);
|
|
::SetWindowTextW(m_hWnd, (wchar_t *)title_16);
|
|
free(title_16);
|
|
}
|
|
|
|
std::string GHOST_WindowWin32::getTitle() const
|
|
{
|
|
std::wstring wtitle(::GetWindowTextLengthW(m_hWnd) + 1, L'\0');
|
|
::GetWindowTextW(m_hWnd, &wtitle[0], wtitle.capacity());
|
|
|
|
std::string title(count_utf_8_from_16(wtitle.c_str()) + 1, '\0');
|
|
conv_utf_16_to_8(wtitle.c_str(), &title[0], title.capacity());
|
|
|
|
return title;
|
|
}
|
|
|
|
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(uint32_t width)
|
|
{
|
|
GHOST_TSuccess success;
|
|
GHOST_Rect cBnds, wBnds;
|
|
getClientBounds(cBnds);
|
|
if (cBnds.getWidth() != (int32_t)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(uint32_t height)
|
|
{
|
|
GHOST_TSuccess success;
|
|
GHOST_Rect cBnds, wBnds;
|
|
getClientBounds(cBnds);
|
|
if (cBnds.getHeight() != (int32_t)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(uint32_t width, uint32_t height)
|
|
{
|
|
GHOST_TSuccess success;
|
|
GHOST_Rect cBnds, wBnds;
|
|
getClientBounds(cBnds);
|
|
if ((cBnds.getWidth() != (int32_t)width) || (cBnds.getHeight() != (int32_t)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
|
|
{
|
|
if (::IsIconic(m_hWnd)) {
|
|
return GHOST_kWindowStateMinimized;
|
|
}
|
|
else if (::IsZoomed(m_hWnd)) {
|
|
LONG_PTR result = ::GetWindowLongPtr(m_hWnd, GWL_STYLE);
|
|
return (result & WS_CAPTION) ? GHOST_kWindowStateMaximized : GHOST_kWindowStateFullScreen;
|
|
}
|
|
return GHOST_kWindowStateNormal;
|
|
}
|
|
|
|
void GHOST_WindowWin32::screenToClient(int32_t inX,
|
|
int32_t inY,
|
|
int32_t &outX,
|
|
int32_t &outY) const
|
|
{
|
|
POINT point = {inX, inY};
|
|
::ScreenToClient(m_hWnd, &point);
|
|
outX = point.x;
|
|
outY = point.y;
|
|
}
|
|
|
|
void GHOST_WindowWin32::clientToScreen(int32_t inX,
|
|
int32_t inY,
|
|
int32_t &outX,
|
|
int32_t &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();
|
|
LONG_PTR style = GetWindowLongPtr(m_hWnd, GWL_STYLE) | WS_CAPTION;
|
|
WINDOWPLACEMENT wp;
|
|
wp.length = sizeof(WINDOWPLACEMENT);
|
|
::GetWindowPlacement(m_hWnd, &wp);
|
|
|
|
switch (state) {
|
|
case GHOST_kWindowStateMinimized:
|
|
wp.showCmd = SW_MINIMIZE;
|
|
break;
|
|
case GHOST_kWindowStateMaximized:
|
|
wp.showCmd = SW_SHOWMAXIMIZED;
|
|
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;
|
|
style &= ~(WS_CAPTION | WS_MAXIMIZE);
|
|
break;
|
|
case GHOST_kWindowStateNormal:
|
|
default:
|
|
if (curstate == GHOST_kWindowStateFullScreen &&
|
|
m_normal_state == GHOST_kWindowStateMaximized) {
|
|
wp.showCmd = SW_SHOWMAXIMIZED;
|
|
m_normal_state = GHOST_kWindowStateNormal;
|
|
}
|
|
else {
|
|
wp.showCmd = SW_SHOWNORMAL;
|
|
}
|
|
break;
|
|
}
|
|
::SetWindowLongPtr(m_hWnd, GWL_STYLE, style);
|
|
/* SetWindowLongPtr Docs: frame changes not visible until SetWindowPos with SWP_FRAMECHANGED. */
|
|
::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 {
|
|
if (getState() == GHOST_kWindowStateMinimized) {
|
|
setState(GHOST_kWindowStateNormal);
|
|
}
|
|
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_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_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,
|
|
"A graphics card and driver with support for OpenGL 3.3 or higher is required.\n"
|
|
"Installing the latest driver for your graphics card may resolve the issue.\n\n"
|
|
"The program will now close.",
|
|
"Blender - Unsupported Graphics Card or 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_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
|
|
}
|
|
else if (type == GHOST_kDrawingContextTypeD3D) {
|
|
GHOST_Context *context;
|
|
|
|
context = new GHOST_ContextD3D(false, m_hWnd);
|
|
if (context->initializeDrawingContext()) {
|
|
return context;
|
|
}
|
|
else {
|
|
delete context;
|
|
}
|
|
|
|
return context;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void GHOST_WindowWin32::lostMouseCapture()
|
|
{
|
|
if (m_hasMouseCaptured) {
|
|
m_hasGrabMouse = false;
|
|
m_nPressedButtons = 0;
|
|
m_hasMouseCaptured = false;
|
|
}
|
|
}
|
|
|
|
bool GHOST_WindowWin32::isDialog() const
|
|
{
|
|
return m_isDialog;
|
|
}
|
|
|
|
void GHOST_WindowWin32::updateMouseCapture(GHOST_MouseCaptureEventWin32 event)
|
|
{
|
|
switch (event) {
|
|
case MousePressed:
|
|
m_nPressedButtons++;
|
|
break;
|
|
case MouseReleased:
|
|
if (m_nPressedButtons)
|
|
m_nPressedButtons--;
|
|
break;
|
|
case OperatorGrab:
|
|
m_hasGrabMouse = true;
|
|
break;
|
|
case OperatorUngrab:
|
|
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;
|
|
}
|
|
}
|
|
|
|
HCURSOR GHOST_WindowWin32::getStandardCursor(GHOST_TStandardCursor shape) const
|
|
{
|
|
// Convert GHOST cursor to Windows OEM cursor
|
|
HANDLE cursor = NULL;
|
|
HMODULE module = ::GetModuleHandle(0);
|
|
uint32_t flags = LR_SHARED | LR_DEFAULTSIZE;
|
|
int cx = 0, cy = 0;
|
|
|
|
switch (shape) {
|
|
case GHOST_kStandardCursorCustom:
|
|
if (m_customCursor) {
|
|
return m_customCursor;
|
|
}
|
|
else {
|
|
return NULL;
|
|
}
|
|
case GHOST_kStandardCursorRightArrow:
|
|
cursor = ::LoadImage(module, "arrowright_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break;
|
|
case GHOST_kStandardCursorLeftArrow:
|
|
cursor = ::LoadImage(module, "arrowleft_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break;
|
|
case GHOST_kStandardCursorUpArrow:
|
|
cursor = ::LoadImage(module, "arrowup_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break;
|
|
case GHOST_kStandardCursorDownArrow:
|
|
cursor = ::LoadImage(module, "arrowdown_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break;
|
|
case GHOST_kStandardCursorVerticalSplit:
|
|
cursor = ::LoadImage(module, "splitv_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break;
|
|
case GHOST_kStandardCursorHorizontalSplit:
|
|
cursor = ::LoadImage(module, "splith_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break;
|
|
case GHOST_kStandardCursorKnife:
|
|
cursor = ::LoadImage(module, "knife_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break;
|
|
case GHOST_kStandardCursorEyedropper:
|
|
cursor = ::LoadImage(module, "eyedropper_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break;
|
|
case GHOST_kStandardCursorZoomIn:
|
|
cursor = ::LoadImage(module, "zoomin_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break;
|
|
case GHOST_kStandardCursorZoomOut:
|
|
cursor = ::LoadImage(module, "zoomout_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break;
|
|
case GHOST_kStandardCursorMove:
|
|
cursor = ::LoadImage(module, "handopen_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break;
|
|
case GHOST_kStandardCursorNSEWScroll:
|
|
cursor = ::LoadImage(module, "scrollnsew_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break;
|
|
case GHOST_kStandardCursorNSScroll:
|
|
cursor = ::LoadImage(module, "scrollns_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break;
|
|
case GHOST_kStandardCursorEWScroll:
|
|
cursor = ::LoadImage(module, "scrollew_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break;
|
|
case GHOST_kStandardCursorHelp:
|
|
cursor = ::LoadImage(NULL, IDC_HELP, IMAGE_CURSOR, cx, cy, flags);
|
|
break; // Arrow and question mark
|
|
case GHOST_kStandardCursorWait:
|
|
cursor = ::LoadImage(NULL, IDC_WAIT, IMAGE_CURSOR, cx, cy, flags);
|
|
break; // Hourglass
|
|
case GHOST_kStandardCursorText:
|
|
cursor = ::LoadImage(NULL, IDC_IBEAM, IMAGE_CURSOR, cx, cy, flags);
|
|
break; // I-beam
|
|
case GHOST_kStandardCursorCrosshair:
|
|
cursor = ::LoadImage(module, "cross_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break; // Standard Cross
|
|
case GHOST_kStandardCursorCrosshairA:
|
|
cursor = ::LoadImage(module, "crossA_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break; // Crosshair A
|
|
case GHOST_kStandardCursorCrosshairB:
|
|
cursor = ::LoadImage(module, "crossB_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break; // Diagonal Crosshair B
|
|
case GHOST_kStandardCursorCrosshairC:
|
|
cursor = ::LoadImage(module, "crossC_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break; // Minimal Crosshair C
|
|
case GHOST_kStandardCursorBottomSide:
|
|
case GHOST_kStandardCursorUpDown:
|
|
cursor = ::LoadImage(module, "movens_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break; // Double-pointed arrow pointing north and south
|
|
case GHOST_kStandardCursorLeftSide:
|
|
case GHOST_kStandardCursorLeftRight:
|
|
cursor = ::LoadImage(module, "moveew_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break; // Double-pointed arrow pointing west and east
|
|
case GHOST_kStandardCursorTopSide:
|
|
cursor = ::LoadImage(NULL, IDC_UPARROW, IMAGE_CURSOR, cx, cy, flags);
|
|
break; // Vertical arrow
|
|
case GHOST_kStandardCursorTopLeftCorner:
|
|
cursor = ::LoadImage(NULL, IDC_SIZENWSE, IMAGE_CURSOR, cx, cy, flags);
|
|
break;
|
|
case GHOST_kStandardCursorTopRightCorner:
|
|
cursor = ::LoadImage(NULL, IDC_SIZENESW, IMAGE_CURSOR, cx, cy, flags);
|
|
break;
|
|
case GHOST_kStandardCursorBottomRightCorner:
|
|
cursor = ::LoadImage(NULL, IDC_SIZENWSE, IMAGE_CURSOR, cx, cy, flags);
|
|
break;
|
|
case GHOST_kStandardCursorBottomLeftCorner:
|
|
cursor = ::LoadImage(NULL, IDC_SIZENESW, IMAGE_CURSOR, cx, cy, flags);
|
|
break;
|
|
case GHOST_kStandardCursorPencil:
|
|
cursor = ::LoadImage(module, "pencil_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break;
|
|
case GHOST_kStandardCursorEraser:
|
|
cursor = ::LoadImage(module, "eraser_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break;
|
|
case GHOST_kStandardCursorDestroy:
|
|
case GHOST_kStandardCursorStop:
|
|
cursor = ::LoadImage(module, "forbidden_cursor", IMAGE_CURSOR, cx, cy, flags);
|
|
break; // Slashed circle
|
|
case GHOST_kStandardCursorDefault:
|
|
cursor = NULL;
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
if (cursor == NULL) {
|
|
cursor = ::LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, cx, cy, flags);
|
|
}
|
|
|
|
return (HCURSOR)cursor;
|
|
}
|
|
|
|
void GHOST_WindowWin32::loadCursor(bool visible, GHOST_TStandardCursor shape) const
|
|
{
|
|
if (!visible) {
|
|
while (::ShowCursor(FALSE) >= 0)
|
|
;
|
|
}
|
|
else {
|
|
while (::ShowCursor(TRUE) < 0)
|
|
;
|
|
}
|
|
|
|
HCURSOR cursor = getStandardCursor(shape);
|
|
if (cursor == NULL) {
|
|
cursor = getStandardCursor(GHOST_kStandardCursorDefault);
|
|
}
|
|
::SetCursor(cursor);
|
|
}
|
|
|
|
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);
|
|
}
|
|
updateMouseCapture(OperatorGrab);
|
|
}
|
|
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 */
|
|
int32_t 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 */
|
|
updateMouseCapture(OperatorUngrab);
|
|
}
|
|
|
|
return GHOST_kSuccess;
|
|
}
|
|
|
|
GHOST_TSuccess GHOST_WindowWin32::setWindowCursorShape(GHOST_TStandardCursor cursorShape)
|
|
{
|
|
if (::GetForegroundWindow() == m_hWnd) {
|
|
loadCursor(getCursorVisibility(), cursorShape);
|
|
}
|
|
|
|
return GHOST_kSuccess;
|
|
}
|
|
|
|
GHOST_TSuccess GHOST_WindowWin32::hasCursorShape(GHOST_TStandardCursor cursorShape)
|
|
{
|
|
return (getStandardCursor(cursorShape)) ? GHOST_kSuccess : GHOST_kFailure;
|
|
}
|
|
|
|
GHOST_TSuccess GHOST_WindowWin32::getPointerInfo(
|
|
std::vector<GHOST_PointerInfoWin32> &outPointerInfo, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
int32_t pointerId = GET_POINTERID_WPARAM(wParam);
|
|
int32_t isPrimary = IS_POINTER_PRIMARY_WPARAM(wParam);
|
|
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem();
|
|
uint32_t outCount = 0;
|
|
|
|
if (!(GetPointerPenInfoHistory(pointerId, &outCount, NULL))) {
|
|
return GHOST_kFailure;
|
|
}
|
|
|
|
std::vector<POINTER_PEN_INFO> pointerPenInfo(outCount);
|
|
outPointerInfo.resize(outCount);
|
|
|
|
if (!(GetPointerPenInfoHistory(pointerId, &outCount, pointerPenInfo.data()))) {
|
|
return GHOST_kFailure;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < outCount; i++) {
|
|
POINTER_INFO pointerApiInfo = pointerPenInfo[i].pointerInfo;
|
|
// Obtain the basic information from the event
|
|
outPointerInfo[i].pointerId = pointerId;
|
|
outPointerInfo[i].isPrimary = isPrimary;
|
|
|
|
switch (pointerApiInfo.ButtonChangeType) {
|
|
case POINTER_CHANGE_FIRSTBUTTON_DOWN:
|
|
case POINTER_CHANGE_FIRSTBUTTON_UP:
|
|
outPointerInfo[i].buttonMask = GHOST_kButtonMaskLeft;
|
|
break;
|
|
case POINTER_CHANGE_SECONDBUTTON_DOWN:
|
|
case POINTER_CHANGE_SECONDBUTTON_UP:
|
|
outPointerInfo[i].buttonMask = GHOST_kButtonMaskRight;
|
|
break;
|
|
case POINTER_CHANGE_THIRDBUTTON_DOWN:
|
|
case POINTER_CHANGE_THIRDBUTTON_UP:
|
|
outPointerInfo[i].buttonMask = GHOST_kButtonMaskMiddle;
|
|
break;
|
|
case POINTER_CHANGE_FOURTHBUTTON_DOWN:
|
|
case POINTER_CHANGE_FOURTHBUTTON_UP:
|
|
outPointerInfo[i].buttonMask = GHOST_kButtonMaskButton4;
|
|
break;
|
|
case POINTER_CHANGE_FIFTHBUTTON_DOWN:
|
|
case POINTER_CHANGE_FIFTHBUTTON_UP:
|
|
outPointerInfo[i].buttonMask = GHOST_kButtonMaskButton5;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
outPointerInfo[i].pixelLocation = pointerApiInfo.ptPixelLocation;
|
|
outPointerInfo[i].tabletData.Active = GHOST_kTabletModeStylus;
|
|
outPointerInfo[i].tabletData.Pressure = 1.0f;
|
|
outPointerInfo[i].tabletData.Xtilt = 0.0f;
|
|
outPointerInfo[i].tabletData.Ytilt = 0.0f;
|
|
outPointerInfo[i].time = system->performanceCounterToMillis(pointerApiInfo.PerformanceCount);
|
|
|
|
if (pointerPenInfo[i].penMask & PEN_MASK_PRESSURE) {
|
|
outPointerInfo[i].tabletData.Pressure = pointerPenInfo[i].pressure / 1024.0f;
|
|
}
|
|
|
|
if (pointerPenInfo[i].penFlags & PEN_FLAG_ERASER) {
|
|
outPointerInfo[i].tabletData.Active = GHOST_kTabletModeEraser;
|
|
}
|
|
|
|
if (pointerPenInfo[i].penMask & PEN_MASK_TILT_X) {
|
|
outPointerInfo[i].tabletData.Xtilt = fmin(fabs(pointerPenInfo[i].tiltX / 90.0f), 1.0f);
|
|
}
|
|
|
|
if (pointerPenInfo[i].penMask & PEN_MASK_TILT_Y) {
|
|
outPointerInfo[i].tabletData.Ytilt = fmin(fabs(pointerPenInfo[i].tiltY / 90.0f), 1.0f);
|
|
}
|
|
}
|
|
|
|
if (!outPointerInfo.empty()) {
|
|
m_lastPointerTabletData = outPointerInfo.back().tabletData;
|
|
}
|
|
|
|
return GHOST_kSuccess;
|
|
}
|
|
|
|
void GHOST_WindowWin32::resetPointerPenInfo()
|
|
{
|
|
m_lastPointerTabletData = GHOST_TABLET_DATA_NONE;
|
|
}
|
|
|
|
GHOST_Wintab *GHOST_WindowWin32::getWintab() const
|
|
{
|
|
return m_wintab;
|
|
}
|
|
|
|
void GHOST_WindowWin32::loadWintab(bool enable)
|
|
{
|
|
if (!m_wintab) {
|
|
if (m_wintab = GHOST_Wintab::loadWintab(m_hWnd)) {
|
|
if (enable) {
|
|
m_wintab->enable();
|
|
|
|
/* Focus Wintab if cursor is inside this window. This ensures Wintab is enabled when the
|
|
* tablet is used to change the Tablet API. */
|
|
int32_t x, y;
|
|
if (m_system->getCursorPosition(x, y)) {
|
|
GHOST_Rect rect;
|
|
getClientBounds(rect);
|
|
|
|
if (rect.isInside(x, y)) {
|
|
m_wintab->gainFocus();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GHOST_WindowWin32::closeWintab()
|
|
{
|
|
delete m_wintab;
|
|
m_wintab = NULL;
|
|
}
|
|
|
|
bool GHOST_WindowWin32::usingTabletAPI(GHOST_TTabletAPI api) const
|
|
{
|
|
if (m_system->getTabletAPI() == api) {
|
|
return true;
|
|
}
|
|
else if (m_system->getTabletAPI() == GHOST_kTabletAutomatic) {
|
|
if (m_wintab && m_wintab->devicesPresent()) {
|
|
return api == GHOST_kTabletWintab;
|
|
}
|
|
else {
|
|
return api == GHOST_kTabletWinPointer;
|
|
}
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
GHOST_TabletData GHOST_WindowWin32::getTabletData()
|
|
{
|
|
if (usingTabletAPI(GHOST_kTabletWintab)) {
|
|
return m_wintab ? m_wintab->getLastTabletData() : GHOST_TABLET_DATA_NONE;
|
|
}
|
|
else {
|
|
return m_lastPointerTabletData;
|
|
}
|
|
}
|
|
|
|
uint16_t GHOST_WindowWin32::getDPIHint()
|
|
{
|
|
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 uint8_t */
|
|
static uint8_t uns8ReverseBits(uint8_t 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 uint16_t */
|
|
static uint16_t uns16ReverseBits(uint16_t 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(
|
|
uint8_t *bitmap, uint8_t *mask, int sizeX, int sizeY, int hotX, int hotY, bool canInvertColor)
|
|
{
|
|
uint32_t andData[32];
|
|
uint32_t xorData[32];
|
|
uint32_t fullBitRow, fullMaskRow;
|
|
int x, y, cols;
|
|
|
|
cols = sizeX / 8; /* Number of whole bytes per row (width of bitmap/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 automatically. */
|
|
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(int32_t x, int32_t y, int32_t w, int32_t h, bool completed)
|
|
{
|
|
m_imeInput.BeginIME(m_hWnd, GHOST_Rect(x, y - h, x, y), completed);
|
|
}
|
|
|
|
void GHOST_WindowWin32::endIME()
|
|
{
|
|
m_imeInput.EndIME(m_hWnd);
|
|
}
|
|
#endif /* WITH_INPUT_IME */
|