Files
geant4/source/externals/g4tools/include/toolx/Windows/pixwin
T
2025-12-05 08:54:02 +01:00

378 lines
12 KiB
Plaintext

// Copyright (C) 2010, Guy Barrand. All rights reserved.
// See the file tools.license for terms.
#ifndef toolx_Windows_pixwin
#define toolx_Windows_pixwin
#include <windows.h>
#include <windowsx.h>
#include <wingdi.h>
#include <tools/sg/device_interactor>
#include <tools/sg/keys>
#include <string>
#if defined(_MSC_VER) && _MSC_VER < 1900
#elif defined(__MINGW32__)
#else
#define TOOLX_WINDOWS_TOUCH
#endif
namespace toolx {
namespace Windows {
#ifdef TOOLX_WINDOWS_TOUCH
inline bool is_message_touch_event() {
// from https://stackoverflow.com/questions/29857587/detect-if-wm-mousemove-is-caused-by-touch-pen.
static const LONG_PTR c_SIGNATURE_MASK = 0xFFFFFF00;
static const LONG_PTR c_MOUSEEVENTF_FROMTOUCH = 0xFF515700;
LONG_PTR extraInfo = ::GetMessageExtraInfo();
return ( ( extraInfo & c_SIGNATURE_MASK ) == c_MOUSEEVENTF_FROMTOUCH );
}
#endif
class pixwin {
static const std::string& s_class() {
static const std::string s_v("toolx::Windows::pixwin");
return s_v;
}
static void register_class(){
static bool s_done = false; //not const, then not thread safe.
if(!s_done) {
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)proc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = ::GetModuleHandle(NULL);
wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground = GetSysColorBrush(COLOR_BTNFACE);
wc.lpszMenuName = (PTSTR)s_class().c_str();
wc.lpszClassName = (PTSTR)s_class().c_str();
::RegisterClass(&wc);
s_done = true;
}
}
public:
virtual void resize(unsigned int,unsigned int){}
virtual void paint() {}
virtual void close(){}
virtual void left_button_up(unsigned int a_x,unsigned int a_y) {}
virtual void left_button_down(unsigned int a_x,unsigned int a_y) {}
virtual void mouse_move(unsigned int a_x,unsigned int a_y,bool) {}
public:
pixwin(HWND a_parent,unsigned int a_w,unsigned int a_h)
:m_parent(a_parent)
,m_hwnd(0)
,m_touch_available(false)
,m_interactor(0)
{
register_class();
m_hwnd = ::CreateWindow((PTSTR)s_class().c_str(),
NULL,
WS_CHILD | WS_VISIBLE,
0,0,
a_w,a_h,
m_parent,NULL,
GetWindowInstance(m_parent),
NULL);
if(!m_hwnd) return;
::SetWindowLongPtr(m_hwnd,GWLP_USERDATA,LONG_PTR(this));
#ifdef TOOLX_WINDOWS_TOUCH
{BYTE digitizer_status = (BYTE)::GetSystemMetrics(SM_DIGITIZER);
if((digitizer_status & (0x80 + 0x40)) == 0) {
m_touch_available = false;
} else {
m_touch_available = true;
if(!::RegisterTouchWindow(m_hwnd,0)) m_touch_available = false;
}}
#endif
}
virtual ~pixwin(){
if(m_hwnd) {
::SetWindowLongPtr(m_hwnd,GWLP_USERDATA,LONG_PTR(NULL));
::DestroyWindow(m_hwnd);
m_hwnd = 0;
}
}
protected:
pixwin(const pixwin& a_from)
:m_parent(a_from.m_parent)
,m_hwnd(0)
,m_touch_available(a_from.m_touch_available)
,m_interactor(0)
{
if(!m_parent) return;
register_class();
RECT rect;
::GetClientRect(m_parent,&rect);
m_hwnd = ::CreateWindow((PTSTR)s_class().c_str(),
NULL,
WS_CHILD | WS_VISIBLE,
0,0,
rect.right-rect.left,
rect.bottom-rect.top,
m_parent,NULL,
GetWindowInstance(m_parent),
NULL);
if(!m_hwnd) return;
::SetWindowLongPtr(m_hwnd,GWLP_USERDATA,LONG_PTR(this));
#ifdef TOOLX_WINDOWS_TOUCH
if(a_from.m_touch_available) {
if(!::RegisterTouchWindow(m_hwnd,0)) m_touch_available = false;
}
#endif
}
protected:
pixwin& operator=(const pixwin&){return *this;}
public:
void set_client_area_size(unsigned int a_w,unsigned int a_h) {
RECT rect;
::GetClientRect(m_hwnd,&rect);
::MoveWindow(m_hwnd,rect.left,rect.top,a_w,a_h,TRUE);
}
HWND hwnd() const {return m_hwnd;}
void post_WM_PAINT() const {
if(!m_hwnd) return;
::PostMessage(m_hwnd,WM_PAINT,(WPARAM)0,(LPARAM)0);
}
void send_WM_PAINT() const {
if(!m_hwnd) return;
::SendMessage(m_hwnd,WM_PAINT,(WPARAM)0,(LPARAM)0);
}
void wm_paint() {paint();}
public:
void set_device_interactor(tools::sg::device_interactor* a_interactor) {m_interactor = a_interactor;} //we do not have ownership.
protected:
bool is_touch_event() {
#ifdef TOOLX_WINDOWS_TOUCH
if(!m_touch_available) return false;
return is_message_touch_event();
#else
return false;
#endif
}
protected:
static LRESULT CALLBACK proc(HWND a_hwnd,UINT a_msg,WPARAM a_wparam,LPARAM a_lparam) {
switch (a_msg) {
case WM_SIZE:{
int width = LOWORD(a_lparam);
int height = HIWORD(a_lparam);
pixwin* _this = (pixwin*)::GetWindowLongPtr(a_hwnd,GWLP_USERDATA);
if(_this) {
_this->resize(width,height);
} else {
// CreateWindow send a WM_SIZE but GWLP_USERDATA not yet set.
}
}return 0;
case WM_PAINT:{
pixwin* _this = (pixwin*)::GetWindowLongPtr(a_hwnd,GWLP_USERDATA);
if(_this) _this->wm_paint();
}return 0;
case WM_KEYDOWN:{
pixwin* _this = (pixwin*)::GetWindowLongPtr(a_hwnd,GWLP_USERDATA);
if(_this) {
if(_this->m_interactor) {
tools::sg::key_down_event event(_this->convert_key(a_wparam));
_this->m_interactor->key_press(event);
}
}
} return 0;
case WM_KEYUP:{
pixwin* _this = (pixwin*)::GetWindowLongPtr(a_hwnd,GWLP_USERDATA);
if(_this) {
if(_this->m_interactor) {
tools::sg::key_up_event event(_this->convert_key(a_wparam));
_this->m_interactor->key_release(event);
}
}
} return 0;
case WM_LBUTTONDOWN:{
pixwin* _this = (pixwin*)::GetWindowLongPtr(a_hwnd,GWLP_USERDATA);
if(_this) {
if(_this->m_interactor) {
bool shift_modifier = ::GetKeyState(VK_SHIFT) & 0x8000;
bool control_modifier = ::GetKeyState(VK_CONTROL) & 0x8000;
tools::sg::mouse_down_event event(LOWORD(a_lparam),HIWORD(a_lparam),shift_modifier,control_modifier);
_this->m_interactor->mouse_press(event);
} else {
RECT rect;
::GetClientRect(a_hwnd,&rect);
unsigned int h = rect.bottom-rect.top;
if(!_this->is_touch_event()) _this->left_button_down(LOWORD(a_lparam),h-HIWORD(a_lparam));
}
}
}return 0;
case WM_LBUTTONUP:{
pixwin* _this = (pixwin*)::GetWindowLongPtr(a_hwnd,GWLP_USERDATA);
if(_this) {
if(_this->m_interactor) {
bool shift_modifier = ::GetKeyState(VK_SHIFT) & 0x8000;
bool control_modifier = ::GetKeyState(VK_CONTROL) & 0x8000;
tools::sg::mouse_up_event event(LOWORD(a_lparam),HIWORD(a_lparam),shift_modifier,control_modifier);
_this->m_interactor->mouse_release(event);
} else {
RECT rect;
::GetClientRect(a_hwnd,&rect);
unsigned int h = rect.bottom-rect.top;
if(!_this->is_touch_event()) _this->left_button_up(LOWORD(a_lparam),h-HIWORD(a_lparam));
}
}
} return 0;
case WM_MOUSEMOVE:{
pixwin* _this = (pixwin*)::GetWindowLongPtr(a_hwnd,GWLP_USERDATA);
if(_this) {
WPARAM state = a_wparam;
bool ldown = ((state & MK_LBUTTON)==MK_LBUTTON)?true:false;
if(_this->m_interactor) {
if(ldown) {
bool shift_modifier = ::GetKeyState(VK_SHIFT) & 0x8000;
bool control_modifier = ::GetKeyState(VK_CONTROL) & 0x8000;
tools::sg::mouse_move_event event(LOWORD(a_lparam),HIWORD(a_lparam),shift_modifier,control_modifier,0,0,false);
_this->m_interactor->mouse_move(event);
}
} else {
RECT rect;
::GetClientRect(a_hwnd,&rect);
unsigned int h = rect.bottom-rect.top;
if(!_this->is_touch_event()) _this->mouse_move(LOWORD(a_lparam),h-HIWORD(a_lparam),ldown);
}
}
}return 0;
case WM_MOUSEWHEEL:{
pixwin* _this = (pixwin*)::GetWindowLongPtr(a_hwnd,GWLP_USERDATA);
if(_this) {
if(_this->m_interactor) {
bool shift_modifier = ::GetKeyState(VK_SHIFT) & 0x8000;
bool control_modifier = ::GetKeyState(VK_CONTROL) & 0x8000;
POINT p;
p.x = LOWORD(a_lparam);
p.y = HIWORD(a_lparam);
if(!::ScreenToClient(a_hwnd,&p)) {}
tools::sg::wheel_rotate_event event(GET_WHEEL_DELTA_WPARAM(a_wparam),int(p.x),int(p.y),shift_modifier,control_modifier);
_this->m_interactor->wheel_rotate(event);
}
}
} return 0;
#ifdef TOOLX_WINDOWS_TOUCH
case WM_TOUCH:{
pixwin* _this = (pixwin*)::GetWindowLongPtr(a_hwnd,GWLP_USERDATA);
if(_this && _this->m_touch_available) {
RECT rect;
::GetClientRect(a_hwnd,&rect);
unsigned int h = rect.bottom-rect.top;
unsigned int num_inputs = (int)a_wparam;
TOUCHINPUT* ti = new TOUCHINPUT[num_inputs];
POINT p;
if(::GetTouchInputInfo((HTOUCHINPUT)a_lparam,num_inputs,ti,sizeof(TOUCHINPUT))) {
for(unsigned int i=0;i<num_inputs; ++i) {
p.x = TOUCH_COORD_TO_PIXEL(ti[i].x);
p.y = TOUCH_COORD_TO_PIXEL(ti[i].y);
if(!::ScreenToClient(a_hwnd,&p)) {}
if(ti[i].dwFlags & TOUCHEVENTF_DOWN) {
_this->left_button_down(p.x,h-p.y);
} else if (ti[i].dwFlags & TOUCHEVENTF_UP) {
_this->left_button_up(p.x,h-p.y);
} else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) {
bool ldown = true; //we assume that a TOUCHEVENT_DOWN had been done.
_this->mouse_move(p.x,h-p.y,ldown);
}
}
}
::CloseTouchInputHandle((HTOUCHINPUT)a_lparam);
delete [] ti;
}
}return 0;
#endif //TOOLX_WINDOWS_TOUCH
case WM_DESTROY:wm__destroy(a_hwnd);return 0;
}
return (DefWindowProc(a_hwnd,a_msg,a_wparam,a_lparam));
}
static bool SetWindowPixelFormat(HDC a_HDC){
PIXELFORMATDESCRIPTOR pfd;
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags =
PFD_DRAW_TO_WINDOW |
PFD_SUPPORT_OPENGL |
PFD_DOUBLEBUFFER |
PFD_STEREO_DONTCARE;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cRedBits = 8;
pfd.cRedShift = 16;
pfd.cGreenBits = 8;
pfd.cGreenShift = 8;
pfd.cBlueBits = 8;
pfd.cBlueShift = 0;
pfd.cAlphaBits = 0;
pfd.cAlphaShift = 0;
pfd.cAccumBits = 64;
pfd.cAccumRedBits = 16;
pfd.cAccumGreenBits = 16;
pfd.cAccumBlueBits = 16;
pfd.cAccumAlphaBits = 0;
pfd.cDepthBits = 32;
pfd.cStencilBits = 8;
pfd.cAuxBuffers = 0;
pfd.iLayerType = PFD_MAIN_PLANE;
pfd.bReserved = 0;
pfd.dwLayerMask = 0;
pfd.dwVisibleMask = 0;
pfd.dwDamageMask = 0;
int pixelIndex = ::ChoosePixelFormat(a_HDC,&pfd);
if (pixelIndex==0) {
// Let's choose a default index.
pixelIndex = 1;
if (::DescribePixelFormat(a_HDC,
pixelIndex,
sizeof(PIXELFORMATDESCRIPTOR),
&pfd)==0) {
return false;
}
}
if (::SetPixelFormat(a_HDC,pixelIndex,&pfd)==FALSE) return false;
return true;
}
static void wm__destroy(HWND a_hwnd) {
pixwin* _this = (pixwin*)::GetWindowLongPtr(a_hwnd,GWLP_USERDATA);
if(_this) { //How to be sure that we have a pixwin* ???
_this->close();
if(_this->m_hwnd!=a_hwnd) {
}
_this->m_hwnd = 0;
}
::SetWindowLongPtr(a_hwnd,GWLP_USERDATA,LONG_PTR(NULL));
}
protected:
tools::key_code convert_key(WPARAM a_key) {
if(a_key==VK_SHIFT) return tools::sg::key_shift();
return (tools::key_code)a_key;
}
protected:
HWND m_parent;
HWND m_hwnd;
bool m_touch_available;
tools::sg::device_interactor* m_interactor;
};
}}
#endif