Geant4 Cross Reference |
1 // Copyright (C) 2010, Guy Barrand. All rights reserved. 2 // See the file tools.license for terms. 3 4 #ifndef toolx_Windows_glarea 5 #define toolx_Windows_glarea 6 7 #include <windows.h> 8 #include <windowsx.h> 9 #include <wingdi.h> 10 11 #include <tools/sg/device_interactor> 12 #include <tools/sg/keys> 13 //#include <tools/log_file> 14 15 #include <string> 16 17 #if defined(_MSC_VER) && _MSC_VER < 1900 18 #elif defined(__MINGW32__) 19 #else 20 #define TOOLX_WINDOWS_TOUCH 21 #endif 22 23 namespace toolx { 24 namespace Windows { 25 26 #ifdef TOOLX_WINDOWS_TOUCH 27 inline bool is_message_touch_event() { 28 // from https://stackoverflow.com/questions/29857587/detect-if-wm-mousemove-is-caused-by-touch-pen. 29 static const LONG_PTR c_SIGNATURE_MASK = 0xFFFFFF00; 30 static const LONG_PTR c_MOUSEEVENTF_FROMTOUCH = 0xFF515700; 31 LONG_PTR extraInfo = ::GetMessageExtraInfo(); 32 return ( ( extraInfo & c_SIGNATURE_MASK ) == c_MOUSEEVENTF_FROMTOUCH ); 33 } 34 #endif 35 36 class glarea { 37 static const std::string& s_class() { 38 static const std::string s_v("toolx::Windows::glarea"); 39 return s_v; 40 } 41 static void register_class(){ 42 static bool s_done = false; //not const, then not thread safe. 43 if(!s_done) { 44 WNDCLASS wc; 45 wc.style = CS_HREDRAW | CS_VREDRAW; 46 wc.lpfnWndProc = (WNDPROC)proc; 47 wc.cbClsExtra = 0; 48 wc.cbWndExtra = 0; 49 wc.hInstance = ::GetModuleHandle(NULL); 50 wc.hIcon = LoadIcon(NULL,IDI_APPLICATION); 51 wc.hCursor = LoadCursor(NULL,IDC_ARROW); 52 wc.hbrBackground = GetSysColorBrush(COLOR_BTNFACE); 53 wc.lpszMenuName = s_class().c_str(); 54 wc.lpszClassName = s_class().c_str(); 55 ::RegisterClass(&wc); 56 s_done = true; 57 } 58 } 59 public: 60 virtual void resize(unsigned int,unsigned int){} 61 virtual void paint(unsigned int,unsigned int) {} 62 virtual void close(){} 63 64 virtual void left_button_up(unsigned int,unsigned int) {} 65 virtual void left_button_down(unsigned int,unsigned int) {} 66 virtual void mouse_move(unsigned int,unsigned int,bool) {} 67 public: 68 glarea(HWND a_parent) 69 :m_parent(a_parent) 70 ,m_hwnd(0) 71 ,m_context(0) 72 ,m_HDC(0) 73 ,m_touch_available(false) 74 ,m_interactor(0) 75 { 76 register_class(); 77 // The WS_BORDER is needed. Else probleme of size at startup. 78 RECT rect; 79 ::GetClientRect(m_parent,&rect); 80 //printf("debug : glarea : ca : %d %d\n",rect.right-rect.left,rect.bottom-rect.top); 81 //toolx::log_file::get().writef("debug : glarea : ca : %d %d\n",rect.right-rect.left,rect.bottom-rect.top); 82 m_hwnd = ::CreateWindow(s_class().c_str(), 83 NULL, 84 WS_CHILD | WS_VISIBLE, 85 0,0, 86 rect.right-rect.left, 87 rect.bottom-rect.top, 88 m_parent,NULL, 89 GetWindowInstance(m_parent), 90 NULL); 91 if(!m_hwnd) return; 92 ::SetWindowLongPtr(m_hwnd,GWLP_USERDATA,LONG_PTR(this)); 93 94 // initialize OpenGL rendering : 95 m_HDC = ::GetDC(m_hwnd); 96 if(m_HDC && SetWindowPixelFormat(m_HDC) ) { 97 m_context = ::wglCreateContext(m_HDC); 98 } 99 100 #ifdef TOOLX_WINDOWS_TOUCH 101 {BYTE digitizer_status = (BYTE)::GetSystemMetrics(SM_DIGITIZER); 102 if((digitizer_status & (0x80 + 0x40)) == 0) { 103 m_touch_available = false; 104 } else { 105 //BYTE nInputs = (BYTE)::GetSystemMetrics(SM_MAXIMUMTOUCHES); 106 m_touch_available = true; 107 if(!::RegisterTouchWindow(m_hwnd,0)) m_touch_available = false; 108 }} 109 #endif 110 111 } 112 virtual ~glarea(){ 113 if(::wglGetCurrentContext()!=NULL) ::wglMakeCurrent(NULL,NULL); 114 if(m_context) { 115 ::wglDeleteContext(m_context); 116 m_context = 0; 117 } 118 if(m_HDC && m_hwnd) ::ReleaseDC(m_hwnd,m_HDC); 119 if(m_hwnd) { 120 ::SetWindowLongPtr(m_hwnd,GWLP_USERDATA,LONG_PTR(NULL)); 121 ::DestroyWindow(m_hwnd); 122 m_hwnd = 0; 123 } 124 } 125 protected: 126 glarea(const glarea& a_from) 127 :m_parent(a_from.m_parent) 128 ,m_hwnd(0) 129 ,m_context(0) 130 ,m_HDC(0) 131 ,m_touch_available(a_from.m_touch_available) 132 ,m_interactor(0) 133 { 134 if(!m_parent) return; 135 register_class(); 136 RECT rect; 137 ::GetClientRect(m_parent,&rect); 138 m_hwnd = ::CreateWindow(s_class().c_str(), 139 NULL, 140 WS_CHILD | WS_VISIBLE, 141 0,0, 142 rect.right-rect.left, 143 rect.bottom-rect.top, 144 m_parent,NULL, 145 GetWindowInstance(m_parent), 146 NULL); 147 if(!m_hwnd) return; 148 ::SetWindowLongPtr(m_hwnd,GWLP_USERDATA,LONG_PTR(this)); 149 150 // initialize OpenGL rendering : 151 m_HDC = ::GetDC(m_hwnd); 152 if(m_HDC && SetWindowPixelFormat(m_HDC) ) { 153 m_context = ::wglCreateContext(m_HDC); 154 } 155 156 #ifdef TOOLX_WINDOWS_TOUCH 157 if(a_from.m_touch_available) { 158 if(!::RegisterTouchWindow(m_hwnd,0)) m_touch_available = false; 159 } 160 #endif 161 } 162 protected: 163 glarea& operator=(const glarea&){return *this;} 164 public: 165 void set_client_area_size(unsigned int a_w,unsigned int a_h) { 166 RECT rect; 167 ::GetClientRect(m_hwnd,&rect); 168 ::MoveWindow(m_hwnd,rect.left,rect.top,a_w,a_h,TRUE); 169 } 170 HWND hwnd() const {return m_hwnd;} 171 void post_WM_PAINT() const { 172 ::PostMessage(m_hwnd,WM_PAINT,(WPARAM)0,(LPARAM)0); 173 } 174 void send_WM_PAINT() const { 175 ::SendMessage(m_hwnd,WM_PAINT,(WPARAM)0,(LPARAM)0); 176 } 177 void wm_paint() { 178 PAINTSTRUCT ps; 179 //HDC hDC = 180 BeginPaint(m_hwnd,&ps); 181 if(m_HDC && m_context) { 182 ::wglMakeCurrent(m_HDC,m_context); 183 184 RECT rect; 185 ::GetClientRect(m_hwnd,&rect); 186 unsigned int w = rect.right-rect.left; 187 unsigned int h = rect.bottom-rect.top; 188 189 //printf("debug : glarea : paint : %d %d\n",w,h); 190 //toolx::log_file::get().writef("debug : glarea : paint : %d %d\n",w,h); 191 192 paint(w,h); 193 194 ::SwapBuffers(m_HDC); 195 ::wglMakeCurrent(m_HDC,0); 196 } 197 EndPaint(m_hwnd,&ps); 198 } 199 public: 200 void set_device_interactor(tools::sg::device_interactor* a_interactor) {m_interactor = a_interactor;} //we do not have ownership. 201 protected: 202 bool is_touch_event() { 203 #ifdef TOOLX_WINDOWS_TOUCH 204 if(!m_touch_available) return false; 205 return is_message_touch_event(); 206 #else 207 return false; 208 #endif 209 } 210 protected: 211 static LRESULT CALLBACK proc(HWND a_hwnd,UINT a_msg,WPARAM a_wparam,LPARAM a_lparam) { 212 switch (a_msg) { 213 case WM_SIZE:{ 214 int width = LOWORD(a_lparam); 215 int height = HIWORD(a_lparam); 216 glarea* _this = (glarea*)::GetWindowLongPtr(a_hwnd,GWLP_USERDATA); 217 if(_this) { 218 _this->resize(width,height); 219 } else { 220 // CreateWindow send a WM_SIZE but GWLP_USERDATA not yet set. 221 } 222 }return 0; 223 case WM_PAINT:{ 224 glarea* _this = (glarea*)::GetWindowLongPtr(a_hwnd,GWLP_USERDATA); 225 if(_this) _this->wm_paint(); 226 }return 0; 227 228 case WM_KEYDOWN:{ 229 glarea* _this = (glarea*)::GetWindowLongPtr(a_hwnd,GWLP_USERDATA); 230 if(_this) { 231 if(_this->m_interactor) { 232 tools::sg::key_down_event event(_this->convert(a_wparam)); 233 _this->m_interactor->key_press(event); 234 } 235 } 236 } return 0; 237 case WM_KEYUP:{ 238 glarea* _this = (glarea*)::GetWindowLongPtr(a_hwnd,GWLP_USERDATA); 239 if(_this) { 240 if(_this->m_interactor) { 241 tools::sg::key_up_event event(_this->convert(a_wparam)); 242 _this->m_interactor->key_release(event); 243 } 244 } 245 } return 0; 246 247 case WM_LBUTTONDOWN:{ 248 glarea* _this = (glarea*)::GetWindowLongPtr(a_hwnd,GWLP_USERDATA); 249 if(_this) { 250 if(_this->m_interactor) { 251 tools::sg::mouse_down_event event(LOWORD(a_lparam),HIWORD(a_lparam)); 252 _this->m_interactor->mouse_press(event); 253 } else { 254 RECT rect; 255 ::GetClientRect(a_hwnd,&rect); 256 unsigned int h = rect.bottom-rect.top; 257 if(!_this->is_touch_event()) _this->left_button_down(LOWORD(a_lparam),h-HIWORD(a_lparam)); 258 } 259 } 260 }return 0; 261 case WM_LBUTTONUP:{ 262 glarea* _this = (glarea*)::GetWindowLongPtr(a_hwnd,GWLP_USERDATA); 263 if(_this) { 264 if(_this->m_interactor) { 265 tools::sg::mouse_up_event event(LOWORD(a_lparam),HIWORD(a_lparam)); 266 _this->m_interactor->mouse_release(event); 267 } else { 268 RECT rect; 269 ::GetClientRect(a_hwnd,&rect); 270 unsigned int h = rect.bottom-rect.top; 271 if(!_this->is_touch_event()) _this->left_button_up(LOWORD(a_lparam),h-HIWORD(a_lparam)); 272 } 273 } 274 } return 0; 275 case WM_MOUSEMOVE:{ 276 glarea* _this = (glarea*)::GetWindowLongPtr(a_hwnd,GWLP_USERDATA); 277 if(_this) { 278 if(_this->m_interactor) { 279 tools::sg::mouse_move_event event(LOWORD(a_lparam),HIWORD(a_lparam),0,0,false); 280 _this->m_interactor->mouse_move(event); 281 } else { 282 WPARAM state = a_wparam; 283 bool ldown = ((state & MK_LBUTTON)==MK_LBUTTON)?true:false; 284 RECT rect; 285 ::GetClientRect(a_hwnd,&rect); 286 unsigned int h = rect.bottom-rect.top; 287 if(!_this->is_touch_event()) _this->mouse_move(LOWORD(a_lparam),h-HIWORD(a_lparam),ldown); 288 } 289 } 290 291 }return 0; 292 293 case WM_MOUSEWHEEL:{ 294 glarea* _this = (glarea*)::GetWindowLongPtr(a_hwnd,GWLP_USERDATA); 295 if(_this) { 296 if(_this->m_interactor) { 297 tools::sg::wheel_rotate_event event(GET_WHEEL_DELTA_WPARAM(a_wparam)); 298 _this->m_interactor->wheel_rotate(event); 299 } 300 } 301 } return 0; 302 303 #ifdef TOOLX_WINDOWS_TOUCH 304 case WM_TOUCH:{ 305 glarea* _this = (glarea*)::GetWindowLongPtr(a_hwnd,GWLP_USERDATA); 306 if(_this && _this->m_touch_available) { 307 RECT rect; 308 //::GetWindowRect(hwnd,&rect); ??? 309 ::GetClientRect(a_hwnd,&rect); 310 unsigned int h = rect.bottom-rect.top; 311 312 unsigned int num_inputs = (int)a_wparam; 313 //::printf("debug : WM_TOUCH : 001 : %d\n",num_inputs); 314 TOUCHINPUT* ti = new TOUCHINPUT[num_inputs]; 315 POINT p; 316 if(::GetTouchInputInfo((HTOUCHINPUT)a_lparam,num_inputs,ti,sizeof(TOUCHINPUT))) { 317 for(unsigned int i=0;i<num_inputs; ++i) { 318 p.x = TOUCH_COORD_TO_PIXEL(ti[i].x); 319 p.y = TOUCH_COORD_TO_PIXEL(ti[i].y); 320 if(!::ScreenToClient(a_hwnd,&p)) {} 321 if(ti[i].dwFlags & TOUCHEVENTF_DOWN) { 322 //::printf("debug : TOUCHEVENTF_DOWN %lu %lu\n",p.x,p.y); 323 _this->left_button_down(p.x,h-p.y); 324 } else if (ti[i].dwFlags & TOUCHEVENTF_UP) { 325 //::printf("debug : TOUCHEVENTF_UP %lu %lu\n",p.x,p.y); 326 _this->left_button_up(p.x,h-p.y); 327 } else if (ti[i].dwFlags & TOUCHEVENTF_MOVE) { 328 bool ldown = true; //we assume that a TOUCHEVENT_DOWN had been done. 329 //::printf("debug : TOUCHEVENTF_MOVE %lu %lu\n",p.x,p.y); 330 _this->mouse_move(p.x,h-p.y,ldown); 331 } 332 } 333 } 334 ::CloseTouchInputHandle((HTOUCHINPUT)a_lparam); 335 delete [] ti; 336 } 337 }return 0; 338 #endif //TOOLX_WINDOWS_TOUCH 339 case WM_DESTROY:wm__destroy(a_hwnd);return 0; 340 } 341 return (DefWindowProc(a_hwnd,a_msg,a_wparam,a_lparam)); 342 } 343 static bool SetWindowPixelFormat(HDC a_HDC){ 344 PIXELFORMATDESCRIPTOR pfd; 345 pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); 346 pfd.nVersion = 1; 347 pfd.dwFlags = 348 PFD_DRAW_TO_WINDOW | 349 PFD_SUPPORT_OPENGL | 350 PFD_DOUBLEBUFFER | 351 PFD_STEREO_DONTCARE; 352 pfd.iPixelType = PFD_TYPE_RGBA; 353 pfd.cColorBits = 32; 354 pfd.cRedBits = 8; 355 pfd.cRedShift = 16; 356 pfd.cGreenBits = 8; 357 pfd.cGreenShift = 8; 358 pfd.cBlueBits = 8; 359 pfd.cBlueShift = 0; 360 pfd.cAlphaBits = 0; 361 pfd.cAlphaShift = 0; 362 pfd.cAccumBits = 64; 363 pfd.cAccumRedBits = 16; 364 pfd.cAccumGreenBits = 16; 365 pfd.cAccumBlueBits = 16; 366 pfd.cAccumAlphaBits = 0; 367 pfd.cDepthBits = 32; 368 pfd.cStencilBits = 8; 369 pfd.cAuxBuffers = 0; 370 pfd.iLayerType = PFD_MAIN_PLANE; 371 pfd.bReserved = 0; 372 pfd.dwLayerMask = 0; 373 pfd.dwVisibleMask = 0; 374 pfd.dwDamageMask = 0; 375 376 int pixelIndex = ::ChoosePixelFormat(a_HDC,&pfd); 377 if (pixelIndex==0) { 378 // Let's choose a default index. 379 pixelIndex = 1; 380 if (::DescribePixelFormat(a_HDC, 381 pixelIndex, 382 sizeof(PIXELFORMATDESCRIPTOR), 383 &pfd)==0) { 384 return false; 385 } 386 } 387 388 if (::SetPixelFormat(a_HDC,pixelIndex,&pfd)==FALSE) return false; 389 390 return true; 391 } 392 static void wm__destroy(HWND a_hwnd) { 393 glarea* _this = (glarea*)::GetWindowLongPtr(a_hwnd,GWLP_USERDATA); 394 if(_this) { //How to be sure that we have a glarea* ??? 395 _this->close(); 396 if(_this->m_hwnd!=a_hwnd) { 397 //::printf("WinTk::Component::wm_destroy : HWND mismatch !\n"); 398 } 399 _this->m_hwnd = 0; 400 } 401 ::SetWindowLongPtr(a_hwnd,GWLP_USERDATA,LONG_PTR(NULL)); 402 } 403 protected: 404 tools::key_code convert(WPARAM a_key) { 405 if(a_key==VK_SHIFT) return tools::sg::key_shift(); 406 return (tools::key_code)a_key; 407 } 408 protected: 409 HWND m_parent; 410 HWND m_hwnd; 411 HGLRC m_context; 412 HDC m_HDC; 413 bool m_touch_available; 414 tools::sg::device_interactor* m_interactor; 415 }; 416 417 }} 418 419 #endif