/* ---- * ---- file : lglw_windows.c * ---- author : bsp * ---- legal : Distributed under terms of the MIT LICENSE (MIT). * ---- * ---- Permission is hereby granted, free of charge, to any person obtaining a copy * ---- of this software and associated documentation files (the "Software"), to deal * ---- in the Software without restriction, including without limitation the rights * ---- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * ---- copies of the Software, and to permit persons to whom the Software is * ---- furnished to do so, subject to the following conditions: * ---- * ---- The above copyright notice and this permission notice shall be included in * ---- all copies or substantial portions of the Software. * ---- * ---- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * ---- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * ---- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * ---- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * ---- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * ---- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * ---- THE SOFTWARE. * ---- * ---- info : This is part of the "lglw" package. * ---- * ---- created: 04Aug2018 * ---- changed: 05Aug2018, 06Aug2018, 07Aug2018, 08Aug2018, 09Aug2018, 18Aug2018, 07Mar2019 * ---- 10Mar2019, 19May2019 * ---- * ---- * ---- */ #include "lglw.h" #include #include #include #include #include #define Dprintf if(0);else printf // #define Dprintf if(1);else printf // ---------------------------------------------------------------------------- macros and defines #define LGLW(a) lglw_int_t *lglw = ((lglw_int_t*)(a)) #define LGLW_HIDDEN_BASE_CLASS_NAME "hidden_LGLW" #define LGLW_WIN_BASE_CLASS_NAME "LGLW_" #define LGLW_DEFAULT_HIDDEN_W (800) #define LGLW_DEFAULT_HIDDEN_H (600) #define LGLW_MOUSE_TOUCH_LMB_TIMEOUT (250u) #define LGLW_MOUSE_TOUCH_RMB_TIMEOUT (500u) #define LGLW_MOUSE_TOUCH_RMB_STATE_IDLE (0u) #define LGLW_MOUSE_TOUCH_RMB_STATE_LMB (1u) #define LGLW_MOUSE_TOUCH_RMB_STATE_WAIT (2u) #define LGLW_MOUSE_TOUCH_RMB_STATE_RMB (3u) #define LGLW_MOUSE_TOUCH_RMB_MOVE_THRESHOLD (7u) #define sABS(x) (((x)>0)?(x):-(x)) // ---------------------------------------------------------------------------- structs and typedefs typedef struct lglw_int_s { void *user_data; // arbitrary user data HWND parent_hwnd; // created by host struct { char class_name[128]; HWND hwnd; HDC hdc; lglw_vec2i_t size; } hidden; struct { char class_name[128]; HWND hwnd; HDC hdc; HHOOK keyboard_ll_hhook; lglw_vec2i_t size; int32_t swap_interval; } win; struct { HDC hdc; HGLRC hglrc; } prev; HGLRC hglrc; struct { uint32_t kmod_state; // See LGLW_KMOD_xxx lglw_keyboard_fxn_t cbk; } keyboard; struct { lglw_vec2i_t p; // last seen mouse position uint32_t button_state; lglw_mouse_fxn_t cbk; struct { uint32_t mode; lglw_vec2i_t p; // grab-start mouse position lglw_bool_t b_queue_warp; lglw_vec2i_t last_p; } grab; struct { lglw_bool_t b_enable; lglw_bool_t b_update_queued; lglw_bool_t b_syn_rmb; uint32_t syn_rmb_hold_state; // see LGLW_MOUSE_TOUCH_RMB_STATE_xxx uint32_t hold_start_ms; lglw_vec2i_t hold_start_p; } touch; } mouse; struct { uint32_t state; lglw_focus_fxn_t cbk; } focus; struct { lglw_bool_t b_running; lglw_timer_fxn_t cbk; } timer; struct { lglw_dropfiles_fxn_t cbk; } dropfiles; struct { lglw_redraw_fxn_t cbk; } redraw; } lglw_int_t; // ---------------------------------------------------------------------------- module fxn fwd decls static lglw_bool_t loc_create_hidden_window (lglw_int_t *lglw, int32_t _w, int32_t _h); static void loc_destroy_hidden_window(lglw_int_t *lglw); static LRESULT CALLBACK loc_WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); static void loc_key_hook(lglw_int_t *lglw); static void loc_key_unhook(lglw_int_t *lglw); static LRESULT CALLBACK loc_LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam); static lglw_bool_t loc_handle_key (lglw_int_t *lglw, lglw_bool_t _bPressed, uint32_t _vkey); // static lglw_bool_t loc_touchkeyboard_get_rect (RECT *rect); // static lglw_bool_t loc_touchkeyboard_is_visible (void); extern lglw_bool_t lglw_int_touchkeyboard_toggle (void); static void loc_handle_mouseleave (lglw_int_t *lglw); static void loc_handle_mouseenter (lglw_int_t *lglw); static void loc_handle_mousebutton (lglw_int_t *lglw, lglw_bool_t _bPressed, uint32_t _button); static void loc_handle_mousemotion (lglw_int_t *lglw); static void loc_handle_queued_mouse_warp (lglw_int_t *lglw); static void loc_touchinput_update (lglw_int_t *lglw); static void loc_enable_dropfiles (lglw_int_t *lglw, lglw_bool_t _bEnable); // ---------------------------------------------------------------------------- module vars static lglw_int_t *khook_lglw = NULL; // currently key-hooked lglw instance (one at a time) // ---------------------------------------------------------------------------- lglw_init lglw_t lglw_init(int32_t _w, int32_t _h) { lglw_int_t *lglw = malloc(sizeof(lglw_int_t)); if(NULL != lglw) { memset(lglw, 0, sizeof(lglw_int_t)); if(_w <= 16) _w = LGLW_DEFAULT_HIDDEN_W; if(_h <= 16) _h = LGLW_DEFAULT_HIDDEN_H; if(!loc_create_hidden_window(lglw, _w, _h)) { free(lglw); lglw = NULL; } } return lglw; } // ---------------------------------------------------------------------------- lglw_exit void lglw_exit(lglw_t _lglw) { LGLW(_lglw); if(NULL != lglw) { loc_destroy_hidden_window(lglw); free(lglw); } } // ---------------------------------------------------------------------------- lglw_userdata_set void lglw_userdata_set(lglw_t _lglw, void *_userData) { LGLW(_lglw); if(NULL != lglw) { lglw->user_data = _userData; } } // ---------------------------------------------------------------------------- lglw_userdata_get void *lglw_userdata_get(lglw_t _lglw) { LGLW(_lglw); if(NULL != lglw) { return lglw->user_data; } return NULL; } // ---------------------------------------------------------------------------- loc_create_hidden_window static lglw_bool_t loc_create_hidden_window(lglw_int_t *lglw, int32_t _w, int32_t _h) { sprintf(lglw->hidden.class_name, LGLW_HIDDEN_BASE_CLASS_NAME "%p", lglw); WNDCLASS wc; ZeroMemory(&wc, sizeof(wc)); wc.style = CS_OWNDC; wc.lpfnWndProc = (WNDPROC) &loc_WndProc; wc.hInstance = GetModuleHandle(NULL); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.lpszClassName = lglw->hidden.class_name; if(!RegisterClass(&wc)) { // something went terribly wrong printf("[---] lglw: failed to register hidden window class\n"); return LGLW_FALSE; } DWORD dwExStyle = 0; DWORD dwStyle = 0; lglw->hidden.hwnd = CreateWindowEx(dwExStyle, lglw->hidden.class_name, "LGLW_hidden", dwStyle, 0/*xpos*/, 0/*ypos*/, _w, _h, NULL/*parentHWND*/, NULL/*window menu*/, GetModuleHandle(NULL), NULL ); lglw->hidden.size.x = _w; lglw->hidden.size.y = _h; PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, //Flags PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette. 32, // Colordepth of the framebuffer. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, // Number of bits for the depthbuffer 8, // Number of bits for the stencilbuffer 0, // Number of Aux buffers in the framebuffer. PFD_MAIN_PLANE, 0, 0, 0, 0 }; lglw->hidden.hdc = GetDC(lglw->hidden.hwnd); Dprintf("xxx lglw: hidden hdc=%p\n", lglw->hidden.hdc); int pfmt = ChoosePixelFormat(lglw->hidden.hdc, &pfd); Dprintf("xxx lglw: hidden pfmt=%d\n", pfmt); SetPixelFormat(lglw->hidden.hdc, pfmt, &pfd); lglw->hglrc = wglCreateContext(lglw->hidden.hdc); Dprintf("xxx lglw: hidden hglrc=%p\n", lglw->hglrc); return LGLW_TRUE; } // ---------------------------------------------------------------------------- loc_destroy_hidden_window static void loc_destroy_hidden_window(lglw_int_t *lglw) { if(NULL != lglw->hidden.hwnd) { wglDeleteContext(lglw->hglrc); lglw->hglrc = NULL; DestroyWindow(lglw->hidden.hwnd); lglw->hidden.hwnd = NULL; UnregisterClass(lglw->hidden.class_name, GetModuleHandle(NULL)); } } // ---------------------------------------------------------------------------- lglw_window_open lglw_bool_t lglw_window_open (lglw_t _lglw, void *_parentHWNDOrNull, int32_t _x, int32_t _y, int32_t _w, int32_t _h) { lglw_bool_t r = LGLW_FALSE; LGLW(_lglw); if(NULL != lglw) { lglw->parent_hwnd = (HWND)_parentHWNDOrNull; sprintf(lglw->win.class_name, LGLW_WIN_BASE_CLASS_NAME "%p", lglw); WNDCLASS wc; ZeroMemory(&wc, sizeof(wc)); wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wc.lpfnWndProc = (WNDPROC) &loc_WndProc; wc.hInstance = GetModuleHandle(NULL); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.lpszClassName = lglw->win.class_name; if(!RegisterClass(&wc)) { // something went terribly wrong printf("[---] lglw: failed to register window class\n"); return LGLW_FALSE; } if(_w <= 16) _w = lglw->hidden.size.x; if(_h <= 16) _h = lglw->hidden.size.y; DWORD dwExStyle = 0; DWORD dwStyle = ((NULL != _parentHWNDOrNull) ? WS_CHILD : 0) | WS_VISIBLE; lglw->win.hwnd = CreateWindowEx(dwExStyle, lglw->win.class_name, "LGLW", dwStyle, (NULL == _parentHWNDOrNull) ? _x : 0, (NULL == _parentHWNDOrNull) ? _y : 0, _w, _h, _parentHWNDOrNull, NULL/*window menu*/, GetModuleHandle(NULL), NULL ); lglw->win.size.x = _w; lglw->win.size.y = _h; // window_to_wrapper = this; PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, //Flags PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette. 32, // Colordepth of the framebuffer. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, // Number of bits for the depthbuffer 8, // Number of bits for the stencilbuffer 0, // Number of Aux buffers in the framebuffer. PFD_MAIN_PLANE, 0, 0, 0, 0 }; lglw->win.hdc = GetDC(lglw->win.hwnd); int pfmt = ChoosePixelFormat(lglw->win.hdc, &pfd); Dprintf("xxx lglw: win pfmt=%d\n", pfmt); SetPixelFormat(lglw->win.hdc, pfmt, &pfd); (void)SetWindowLongPtr(lglw->win.hwnd, GWLP_USERDATA, (LONG_PTR)lglw); loc_enable_dropfiles(lglw, (NULL != lglw->dropfiles.cbk)); } return r; } // ---------------------------------------------------------------------------- lglw_window_resize lglw_bool_t lglw_window_resize (lglw_t _lglw, int32_t _w, int32_t _h) { lglw_bool_t r = LGLW_FALSE; LGLW(_lglw); if(NULL != lglw) { if(NULL != lglw->win.hwnd) { r = LGLW_TRUE; SetWindowPos(lglw->win.hwnd, HWND_TOP, 0, 0, // x/y _w, _h, SWP_NOMOVE | SWP_SHOWWINDOW ); int deltaW = _w - lglw->win.size.x; int deltaH = _h - lglw->win.size.y; lglw->win.size.x = _w; lglw->win.size.y = _h; // Resize parent window (if any) { HWND hwnd; hwnd = GetAncestor(lglw->win.hwnd, GA_ROOT); Dprintf("xxx lglw_window_resize: ancestor=%p\n", hwnd); if(NULL != hwnd) { RECT rect; if(GetWindowRect(hwnd, &rect)) { rect.right += deltaW; rect.bottom += deltaH; SetWindowPos(hwnd, HWND_TOP, 0, 0, // x/y rect.right - rect.left, rect.bottom - rect.top, SWP_NOMOVE | SWP_FRAMECHANGED | SWP_SHOWWINDOW ); } } } } } return r; } // ---------------------------------------------------------------------------- lglw_window_close void lglw_window_close (lglw_t _lglw) { LGLW(_lglw); if(NULL != lglw) { if(NULL != lglw->win.hwnd) { lglw_timer_stop(_lglw); loc_key_unhook(lglw); DestroyWindow(lglw->win.hwnd); lglw->win.hwnd = NULL; lglw->win.hdc = NULL; UnregisterClass(lglw->win.class_name, GetModuleHandle(NULL)); // window_to_wrapper = NULL; } } } // ---------------------------------------------------------------------------- lglw_window_show void lglw_window_show(lglw_t _lglw) { LGLW(_lglw); if(NULL != lglw) { if(NULL != lglw->win.hwnd) { ShowWindow(lglw->win.hwnd, SW_SHOWNORMAL); } } } // ---------------------------------------------------------------------------- lglw_window_hide void lglw_window_hide(lglw_t _lglw) { LGLW(_lglw); if(NULL != lglw) { if(NULL != lglw->win.hwnd) { ShowWindow(lglw->win.hwnd, SW_HIDE); } } } // ---------------------------------------------------------------------------- lglw_window_is_visible lglw_bool_t lglw_window_is_visible(lglw_t _lglw) { lglw_bool_t r = LGLW_FALSE; LGLW(_lglw); if(NULL != lglw) { r = (lglw_bool_t)IsWindowVisible(lglw->win.hwnd); } return r; } // ---------------------------------------------------------------------------- lglw_window_size_get void lglw_window_size_get(lglw_t _lglw, int32_t *_retX, int32_t *_retY) { LGLW(_lglw); if(NULL != lglw) { if(NULL != lglw->win.hwnd) { if(NULL != _retX) *_retX = lglw->win.size.x; if(NULL != _retY) *_retY = lglw->win.size.y; } } } // ---------------------------------------------------------------------------- lglw_redraw void lglw_redraw(lglw_t _lglw) { LGLW(_lglw); if(NULL != lglw) { if(NULL != lglw->win.hwnd) { RedrawWindow(lglw->win.hwnd, NULL/*lprcUpdate*/, NULL/*hrgnUpdate*/, RDW_INTERNALPAINT | RDW_UPDATENOW); } } } // ---------------------------------------------------------------------------- lglw_redraw_callback_set void lglw_redraw_callback_set(lglw_t _lglw, lglw_redraw_fxn_t _cbk) { LGLW(_lglw); if(NULL != lglw) { lglw->redraw.cbk = _cbk; } } // ---------------------------------------------------------------------------- lglw_glcontext_push void lglw_glcontext_push(lglw_t _lglw) { LGLW(_lglw); if(NULL != lglw) { lglw->prev.hdc = wglGetCurrentDC(); lglw->prev.hglrc = wglGetCurrentContext(); // Dprintf("xxx lglw_glcontext_push: prev.hdc=%p prev.hglrc=%p\n", lglw->prev.hdc, lglw->prev.hglrc); // Dprintf("xxx lglw_glcontext_push: win.hdc=%p lglw->hglrc=%p\n", lglw->win.hdc, lglw->hglrc); if(!wglMakeCurrent((NULL == lglw->win.hdc) ? lglw->hidden.hdc : lglw->win.hdc, lglw->hglrc)) { printf("[---] lglw_glcontext_push: wglMakeCurrent() failed. win.hdc=%p hidden.hdc=%p hglrc=%p GetLastError()=%d\n", lglw->win.hdc, lglw->hidden.hdc, lglw->hglrc, GetLastError()); } } } // ---------------------------------------------------------------------------- lglw_glcontext_pop void lglw_glcontext_pop(lglw_t _lglw) { LGLW(_lglw); if(NULL != lglw) { // Dprintf("xxx lglw_glcontext_pop: prev.hdc=%p prev.hglrc=%p\n", lglw->prev.hdc, lglw->prev.hglrc); if(!wglMakeCurrent(lglw->prev.hdc, lglw->prev.hglrc)) { printf("[---] lglw_glcontext_pop: wglMakeCurrent() failed. prev.hdc=%p hglrc=%p GetLastError()=%d\n", lglw->prev.hdc, lglw->prev.hglrc, GetLastError()); } } } // ---------------------------------------------------------------------------- lglw_swap_buffers void lglw_swap_buffers(lglw_t _lglw) { LGLW(_lglw); if(NULL != lglw) { wglSwapLayerBuffers(lglw->win.hdc, WGL_SWAP_MAIN_PLANE); } } // ---------------------------------------------------------------------------- lglw_swap_interval_set typedef void (APIENTRY *PFNWGLEXTSWAPINTERVALPROC) (int); void lglw_swap_interval_set(lglw_t _lglw, int32_t _ival) { LGLW(_lglw); if(NULL != lglw) { PFNWGLEXTSWAPINTERVALPROC wglSwapIntervalEXT; wglSwapIntervalEXT = (PFNWGLEXTSWAPINTERVALPROC) wglGetProcAddress("wglSwapIntervalEXT"); if(NULL != wglSwapIntervalEXT) { wglSwapIntervalEXT(_ival); lglw->win.swap_interval = _ival; } } } // ---------------------------------------------------------------------------- lglw_swap_interval_get int32_t lglw_swap_interval_get(lglw_t _lglw) { LGLW(_lglw); int32_t r = 0; if(NULL != lglw) { r = lglw->win.swap_interval; } return r; } // ---------------------------------------------------------------------------- loc_key_hook static void loc_key_hook(lglw_int_t *lglw) { loc_key_unhook(lglw); // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644990(v=vs.85).aspx lglw->win.keyboard_ll_hhook = SetWindowsHookEx(WH_KEYBOARD_LL, &loc_LowLevelKeyboardProc, GetModuleHandle(NULL), 0/*dwThreadId*/); khook_lglw = lglw; } // ---------------------------------------------------------------------------- loc_key_unhook static void loc_key_unhook(lglw_int_t *lglw) { if(NULL != lglw->win.keyboard_ll_hhook) { UnhookWindowsHookEx(lglw->win.keyboard_ll_hhook); lglw->win.keyboard_ll_hhook = NULL; if(khook_lglw == lglw) khook_lglw = NULL; } } // ---------------------------------------------------------------------------- loc_handle_mouseleave static void loc_handle_mouseleave(lglw_int_t *lglw) { loc_key_unhook(lglw); lglw->focus.state &= ~LGLW_FOCUS_MOUSE; if(NULL != lglw->focus.cbk) { lglw->focus.cbk(lglw, lglw->focus.state, LGLW_FOCUS_MOUSE); } Dprintf("xxx lglw:loc_handle_mouseleave: LEAVE\n"); } // ---------------------------------------------------------------------------- loc_handle_mouseenter static void loc_handle_mouseenter(lglw_int_t *lglw) { TRACKMOUSEEVENT tme; ZeroMemory(&tme, sizeof(tme)); tme.cbSize = sizeof(TRACKMOUSEEVENT); tme.dwFlags = TME_LEAVE; tme.hwndTrack = lglw->win.hwnd; tme.dwHoverTime = HOVER_DEFAULT; (void)TrackMouseEvent(&tme); loc_key_hook(lglw); lglw->focus.state |= LGLW_FOCUS_MOUSE; if(NULL != lglw->focus.cbk) { lglw->focus.cbk(lglw, lglw->focus.state, LGLW_FOCUS_MOUSE); } Dprintf("xxx lglw:loc_handle_mouseenter: LEAVE\n"); } // ---------------------------------------------------------------------------- loc_handle_mousebutton static void loc_handle_mousebutton(lglw_int_t *lglw, lglw_bool_t _bPressed, uint32_t _button) { if(_bPressed) lglw->mouse.button_state |= _button; else lglw->mouse.button_state &= ~_button; if(NULL != lglw->mouse.cbk) { lglw->mouse.cbk(lglw, lglw->mouse.p.x, lglw->mouse.p.y, lglw->mouse.button_state, _button); } } // ---------------------------------------------------------------------------- loc_handle_mousemotion static void loc_handle_mousemotion(lglw_int_t *lglw) { if(NULL != lglw->mouse.cbk) { lglw->mouse.cbk(lglw, lglw->mouse.p.x, lglw->mouse.p.y, lglw->mouse.button_state, 0u/*changedbuttonstate*/); } } // ---------------------------------------------------------------------------- lglw_mouse_callback_set void lglw_mouse_callback_set(lglw_t _lglw, lglw_mouse_fxn_t _cbk) { LGLW(_lglw); if(NULL != lglw) { lglw->mouse.cbk = _cbk; } } // ---------------------------------------------------------------------------- lglw_mouse_callback_set void lglw_focus_callback_set(lglw_t _lglw, lglw_focus_fxn_t _cbk) { LGLW(_lglw); if(NULL != lglw) { lglw->focus.cbk = _cbk; } } // ---------------------------------------------------------------------------- loc_handle_key static lglw_bool_t loc_handle_key(lglw_int_t *lglw, lglw_bool_t _bPressed, uint32_t _vkey) { lglw_bool_t r = LGLW_FALSE; if(NULL != lglw->keyboard.cbk) { r = lglw->keyboard.cbk(lglw, _vkey, lglw->keyboard.kmod_state, _bPressed); } return r; } // ---------------------------------------------------------------------------- lglw_keyboard_callback_set void lglw_keyboard_callback_set(lglw_t _lglw, lglw_keyboard_fxn_t _cbk) { LGLW(_lglw); if(NULL != lglw) { lglw->keyboard.cbk = _cbk; } } // ---------------------------------------------------------------------------- lglw_keyboard_get_modifiers uint32_t lglw_keyboard_get_modifiers(lglw_t _lglw) { uint32_t r = 0u; LGLW(_lglw); if(NULL != lglw) { r = lglw->keyboard.kmod_state; } return r; } // ---------------------------------------------------------------------------- lglw_keyboard_clear_modifiers void lglw_keyboard_clear_modifiers(lglw_t _lglw) { LGLW(_lglw); if(NULL != lglw) { lglw->keyboard.kmod_state = 0; } } // ---------------------------------------------------------------------------- loc_touchkeyboard_get_rect #if 0 static lglw_bool_t loc_touchkeyboard_get_rect(RECT *rect) { lglw_bool_t r = LGLW_FALSE; //******************************************************************* // // RETURNS KEYBOARD RECTANGLE OR EMPTY ONE IF KEYBOARD IS NOT VISIBLE // //******************************************************************* // by "Sevast", IFrameworkInputPane *inputPane = NULL; HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); rect->left = rect->right = rect->top = rect->bottom = 0; // screen location if (SUCCEEDED(hr)) { hr = CoCreateInstance(CLSID_FrameworkInputPane, NULL, CLSCTX_INPROC_SERVER, IID_IFrameworkInputPane, (LPVOID*)&inputPane); if (SUCCEEDED(hr)) { hr=inputPane->Location(rect); if(SUCCEEDED(hr)) { r = LGLW_TRUE; } inputPane->Release(); } } CoUninitialize(); return r; } #endif // ---------------------------------------------------------------------------- loc_touchkeyboard_is_visible #if 0 static lglw_bool_t loc_touchkeyboard_is_visible(void) { lglw_bool_t r = LGLW_FALSE; HWND hwnd = FindWindow("IPTIP_Main_Window", NULL); printf("xxx lglw: IPTIP hwnd=%p\n", hwnd); if(NULL != hwnd) { #if 0 // (note) does not work, style flags are always the same and rect is always (0; 0; 0; 0) LONG style = GetWindowLong(hwnd, GWL_STYLE); RECT rect; r = (0u != (style & WS_VISIBLE)) && (0u == (style & WS_DISABLED)); GetWindowRect(hwnd, &rect); printf("xxx lglw: touchkeyboard_is_visible: style=0x%08x rect=(%d; %d; %d; %d) r=%d\n", style, rect.top, rect.left, rect.bottom, rect.right, r); #endif // (note) does not work, either static const char *WindowClass1709 = "Windows.UI.Core.CoreWindow"; static const char *WindowCaption1709 = "Microsoft Text Input Application"; static const char *WindowParentClass1709 = "ApplicationFrameWindow"; HWND hwnd1709 = FindWindowEx(NULL, NULL, WindowClass1709, WindowCaption1709); printf("xxx lglw: touchkeyboard_is_visible: hwnd1709=%p\n", hwnd1709); // if there is a top-level window - the keyboard is closed if(NULL != hwnd1709) { r = LGLW_FALSE; } else { HWND parent = NULL; for(;;) { HWND childHWND; parent = FindWindowEx(NULL, parent, WindowParentClass1709, NULL); if(NULL == parent) break; // no more windows, keyboard state is unknown // if it's a child of a WindowParentClass1709 window - the keyboard is open childHWND = FindWindowEx(parent, NULL, WindowClass1709, WindowCaption1709); if (NULL != childHWND) { r = LGLW_TRUE; break; } } } } return r; } #endif // ---------------------------------------------------------------------------- lglw_touchkeyboard_show void lglw_touchkeyboard_show(lglw_t _lglw, lglw_bool_t _bEnable) { LGLW(_lglw); if(NULL != lglw) { #if 0 RECT keyRect; if(loc_touchkeyboard_get_rect(&keyRect)) { lglw_bool_t bToggle = _bEnable ^ ((keyRect.right - keyRect.left) > 0); if(bToggle) { loc_touchkeyboard_toggle(); } } #endif #if 0 lglw_bool_t bToggle = _bEnable ^ loc_touchkeyboard_is_visible(); if(bToggle) { lglw_int_touchkeyboard_toggle(); } #endif // (note) there seems to be no (sane) way to determine whether the virtual keyboard is visible. // the solutions suggested on stack overflow don't work (anymore). // => just toggle the kbd lglw_int_touchkeyboard_toggle(); } } // ---------------------------------------------------------------------------- lglw_mouse_get_buttons uint32_t lglw_mouse_get_buttons(lglw_t _lglw) { uint32_t r = 0u; LGLW(_lglw); if(NULL != lglw) { r = lglw->mouse.button_state; } return r; } // ---------------------------------------------------------------------------- lglw_mouse_grab void lglw_mouse_grab(lglw_t _lglw, uint32_t _grabMode) { LGLW(_lglw); if(NULL != lglw) { if(NULL != lglw->win.hwnd) { if(!lglw->mouse.touch.b_enable) { if(lglw->mouse.grab.mode != _grabMode) { lglw_mouse_ungrab(_lglw); } switch(_grabMode) { default: case LGLW_MOUSE_GRAB_NONE: break; case LGLW_MOUSE_GRAB_CAPTURE: (void)SetCapture(lglw->win.hwnd); lglw->mouse.grab.mode = _grabMode; break; case LGLW_MOUSE_GRAB_WARP: (void)SetCapture(lglw->win.hwnd); lglw_mouse_cursor_show(_lglw, LGLW_FALSE); lglw->mouse.grab.p = lglw->mouse.p; lglw->mouse.grab.last_p = lglw->mouse.p; lglw->mouse.grab.mode = _grabMode; break; } } } } } // ---------------------------------------------------------------------------- lglw_mouse_ungrab void lglw_mouse_ungrab(lglw_t _lglw) { LGLW(_lglw); if(NULL != lglw) { if(NULL != lglw->win.hwnd) { if(!lglw->mouse.touch.b_enable) { switch(lglw->mouse.grab.mode) { default: case LGLW_MOUSE_GRAB_NONE: break; case LGLW_MOUSE_GRAB_CAPTURE: (void)ReleaseCapture(); lglw->mouse.grab.mode = LGLW_MOUSE_GRAB_NONE; break; case LGLW_MOUSE_GRAB_WARP: (void)ReleaseCapture(); lglw->mouse.grab.mode = LGLW_MOUSE_GRAB_NONE; lglw->mouse.grab.b_queue_warp = LGLW_TRUE; lglw_mouse_cursor_show(_lglw, LGLW_TRUE); break; } } } } } // ---------------------------------------------------------------------------- lglw_mouse_warp void lglw_mouse_warp(lglw_t _lglw, int32_t _x, int32_t _y) { LGLW(_lglw); if(NULL != lglw) { if(NULL != lglw->win.hwnd) { POINT p; p.x = _x; p.y = _y; if(ClientToScreen(lglw->win.hwnd, &p)) { SetCursorPos(p.x, p.y); if(LGLW_MOUSE_GRAB_WARP != lglw->mouse.grab.mode) { lglw->mouse.p.x = p.x; lglw->mouse.p.y = p.y; } } } } } // ---------------------------------------------------------------------------- loc_handle_queued_mouse_warp static void loc_handle_queued_mouse_warp(lglw_int_t *lglw) { if(lglw->mouse.grab.b_queue_warp) { lglw->mouse.grab.b_queue_warp = LGLW_FALSE; lglw_mouse_warp(lglw, lglw->mouse.grab.p.x, lglw->mouse.grab.p.y); lglw->mouse.grab.last_p = lglw->mouse.grab.p; } } // ---------------------------------------------------------------------------- lglw_mouse_cursor_show void lglw_mouse_cursor_show (lglw_t _lglw, lglw_bool_t _bShow) { LGLW(_lglw); if(NULL != lglw) { if(NULL != lglw->win.hwnd) { (void)ShowCursor(_bShow); } } } // ---------------------------------------------------------------------------- lglw_timer_start void lglw_timer_start(lglw_t _lglw, uint32_t _millisec) { LGLW(_lglw); if(NULL != lglw) { if(NULL != lglw->win.hwnd) { (void)SetTimer(lglw->win.hwnd, 1/*nIDEvent*/, _millisec, NULL/*lpTimerFunc*/); lglw->timer.b_running = LGLW_TRUE; } } } // ---------------------------------------------------------------------------- lglw_timer_stop void lglw_timer_stop(lglw_t _lglw) { LGLW(_lglw); if(NULL != lglw) { if(NULL != lglw->win.hwnd) { if(lglw->timer.b_running) { (void)KillTimer(lglw->win.hwnd, 1/*nIDEvent*/); lglw->timer.b_running = LGLW_FALSE; } } } } // ---------------------------------------------------------------------------- lglw_timer_callback_set void lglw_timer_callback_set(lglw_t _lglw, lglw_timer_fxn_t _cbk) { LGLW(_lglw); if(NULL != lglw) { lglw->timer.cbk = _cbk; } } // ---------------------------------------------------------------------------- lglw_time_get_millisec uint32_t lglw_time_get_millisec(lglw_t _lglw) { uint32_t r = 0u; LGLW(_lglw); if(NULL != lglw) { r = GetTickCount(); } return r; } // ---------------------------------------------------------------------------- loc_enable_dropfiles static void loc_enable_dropfiles(lglw_int_t *lglw, lglw_bool_t _bEnable) { if(NULL != lglw->win.hwnd) { DragAcceptFiles(lglw->win.hwnd, _bEnable ? TRUE : FALSE); } } // ---------------------------------------------------------------------------- lglw_dropfiles_callback_set void lglw_dropfiles_callback_set(lglw_t _lglw, lglw_dropfiles_fxn_t _cbk) { LGLW(_lglw); if(NULL != _lglw) { lglw->dropfiles.cbk = _cbk; loc_enable_dropfiles(lglw, (NULL != _cbk)); } } // ---------------------------------------------------------------------------- loc_touchinput_update typedef void (*EnableMouseInPointer_fxn_t)(BOOL fEnable); static void loc_touchinput_update(lglw_int_t *lglw) { HMODULE hDLL = LoadLibrary("User32.dll"); if(NULL != hDLL) { EnableMouseInPointer_fxn_t enableMouseInPointer = (EnableMouseInPointer_fxn_t) GetProcAddress(hDLL, "EnableMouseInPointer"); if(NULL != enableMouseInPointer) { Dprintf("[dbg] lglw: EnableMouseInPointer() detected (win8+)\n"); (void)enableMouseInPointer(lglw->mouse.touch.b_enable); } FreeLibrary(hDLL); } } // ---------------------------------------------------------------------------- lglw_touchinput_set void lglw_touchinput_set(lglw_t _lglw, lglw_bool_t _bEnable) { LGLW(_lglw); if(NULL != _lglw) { lglw->mouse.touch.b_enable = _bEnable; lglw->mouse.touch.b_update_queued = LGLW_TRUE; } } // ---------------------------------------------------------------------------- lglw_touchinput_get lglw_bool_t lglw_touchinput_get(lglw_t _lglw) { lglw_bool_t r = LGLW_FALSE; LGLW(_lglw); if(NULL != _lglw) { r = lglw->mouse.touch.b_enable; } return r; } // ---------------------------------------------------------------------------- lglw_clipboard_text_set void lglw_clipboard_text_set(lglw_t _lglw, const uint32_t _numChars, const char *_text) { LGLW(_lglw); (void)_numChars; if(NULL != _text) { if(NULL != _lglw) { if(NULL != lglw->win.hwnd) { uint32_t numChars = (0u == _numChars) ? ((uint32_t)strlen(_text)+1u) : _numChars; if(numChars > 0u) { if(OpenClipboard(lglw->win.hwnd)) { HANDLE hGlobal = GlobalAlloc(GMEM_MOVEABLE, numChars * sizeof(TCHAR)); if(NULL != hGlobal) { char *d = (char*) GlobalLock(hGlobal); if(NULL != d) { uint32_t i; for(i = 0u; i < numChars; i++) { d[i] = _text[i]; } d[numChars - 1] = 0; Dprintf("xxx lglw_clipboard_text_set: SetClipboardData(\"%s\")\n", d); GlobalUnlock(hGlobal); (void)SetClipboardData(CF_TEXT, hGlobal); } } CloseClipboard(); } } } } } } // ---------------------------------------------------------------------------- lglw_clipboard_text_get void lglw_clipboard_text_get(lglw_t _lglw, uint32_t _maxChars, uint32_t *_retNumChars, char *_retText) { LGLW(_lglw); if(NULL != _retNumChars) *_retNumChars = 0u; if(NULL != _retText) *_retText = 0; if(_maxChars > 0u) { if(NULL != _lglw) { if(NULL != lglw->win.hwnd) { if(OpenClipboard(lglw->win.hwnd)) { const char *data = (const char*)GetClipboardData(CF_TEXT); Dprintf("xxx lglw_clipboard_text_get: data=\"%s\"\n", data); if(NULL != data) { uint32_t i = 0u; for(; i < _maxChars; i++) { _retText[i] = data[i]; if(0 == _retText[i]) break; } _retText[_maxChars - 1u] = 0; // ASCIIZ if(NULL != _retNumChars) { *_retNumChars = i; } } CloseClipboard(); } } } } } // ---------------------------------------------------------------------------- loc_LowLevelKeyboardProc static LRESULT CALLBACK loc_LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { Dprintf("xxx lglw:loc_LowLevelKeyboardProc: wParam=0x%08x lParam=0x%08x\n", (uint32_t)wParam, (uint32_t)lParam); if(HC_ACTION == nCode) { if(NULL != khook_lglw) { KBDLLHOOKSTRUCT *hs = (KBDLLHOOKSTRUCT*)lParam; lglw_bool_t bHandled = LGLW_FALSE; switch(wParam) { default: break; case WM_KEYDOWN: // Dprintf("xxx lglw:loc_LowLevelKeyboardProc: WM_KEYDOWN vkCode=0x%08x scanCode=0x%08x bExt=%d\n", hs->vkCode, hs->scanCode, (0 != (hs->flags & LLKHF_EXTENDED))); switch(hs->vkCode) { default: if(0u == (hs->flags & LLKHF_EXTENDED)) { uint16_t ucBuf[16]; BYTE keyState[256]; // Dprintf("xxx lglw call toUnicode\n"); GetKeyboardState(keyState); // don't let e.g. ctrl- produce symbol chars keyState[VK_CONTROL] = 0; keyState[VK_LCONTROL] = 0; keyState[VK_RCONTROL] = 0; if(ToUnicode(hs->vkCode, hs->scanCode, keyState, (LPWSTR)ucBuf, 8/*cchBuff*/, 0/*wFlags*/) >= 1) { // Dprintf("xxx lglw toUnicode: ucBuf[0]=0x%04x\n", ucBuf[0]); bHandled = loc_handle_key(khook_lglw, LGLW_TRUE/*bPressed*/, ucBuf[0]); } else { Dprintf("xxx lglw:loc_LowLevelKeyboardProc: ToUnicode failed\n"); } } else { bHandled = loc_handle_key(khook_lglw, LGLW_TRUE/*bPressed*/, hs->vkCode | LGLW_VKEY_EXT); bHandled = LGLW_FALSE; } break; case VK_F1: case VK_F2: case VK_F3: case VK_F4: case VK_F5: case VK_F6: case VK_F7: case VK_F8: case VK_F9: case VK_F10: case VK_F11: case VK_F12: case VK_BACK: case VK_TAB: case VK_RETURN: case VK_ESCAPE: bHandled = loc_handle_key(khook_lglw, LGLW_TRUE/*bPressed*/, hs->vkCode | LGLW_VKEY_EXT); break; case VK_LSHIFT: // Dprintf("xxx lglw:loc_LowLevelKeyboardProc: VK_LSHIFT\n"); khook_lglw->keyboard.kmod_state |= LGLW_KMOD_LSHIFT; bHandled = loc_handle_key(khook_lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_LSHIFT); bHandled = LGLW_FALSE; break; case VK_RSHIFT: // Dprintf("xxx lglw:loc_LowLevelKeyboardProc: VK_RSHIFT\n"); khook_lglw->keyboard.kmod_state |= LGLW_KMOD_RSHIFT; bHandled = loc_handle_key(khook_lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_RSHIFT); bHandled = LGLW_FALSE; break; case VK_LCONTROL: // Dprintf("xxx lglw:loc_LowLevelKeyboardProc: VK_LCONTROL\n"); khook_lglw->keyboard.kmod_state |= LGLW_KMOD_LCTRL; bHandled = loc_handle_key(khook_lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_LCTRL); bHandled = LGLW_FALSE; break; case VK_RCONTROL: // Dprintf("xxx lglw:loc_LowLevelKeyboardProc: VK_RCONTROL\n"); khook_lglw->keyboard.kmod_state |= LGLW_KMOD_RCTRL; bHandled = loc_handle_key(khook_lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_RCTRL); bHandled = LGLW_FALSE; break; case VK_LMENU: // Dprintf("xxx lglw:loc_LowLevelKeyboardProc: VK_LALT\n"); khook_lglw->keyboard.kmod_state |= LGLW_KMOD_LALT; bHandled = loc_handle_key(khook_lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_LALT); bHandled = LGLW_FALSE; break; case VK_RMENU: // Dprintf("xxx lglw:loc_LowLevelKeyboardProc: VK_RALT\n"); khook_lglw->keyboard.kmod_state |= LGLW_KMOD_RALT; bHandled = loc_handle_key(khook_lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_RALT); bHandled = LGLW_FALSE; break; // //case VK_RWIN: // //case VK_F1: // case VK_LMENU: // // not received // // Dprintf("xxx lglw:loc_LowLevelKeyboardProc: VK_LMENU\n"); // khook_lglw->keyboard.kmod_state |= LGLW_KMOD_LSUPER; // bHandled = loc_handle_key(khook_lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_LSUPER); // bHandled = LGLW_FALSE; // break; case 0x5D/*VK_CONTEXTKEY*/: // Dprintf("xxx lglw:loc_LowLevelKeyboardProc: VK_RMENU\n"); khook_lglw->keyboard.kmod_state |= LGLW_KMOD_RSUPER; bHandled = loc_handle_key(khook_lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_RSUPER); bHandled = LGLW_FALSE; break; } break; case WM_KEYUP: // Dprintf("xxx lglw:loc_LowLevelKeyboardProc: WM_KEYUP vkCode=0x%08x scanCode=0x%08x bExt=%d\n", hs->vkCode, hs->scanCode, (0 != (hs->flags & LLKHF_EXTENDED))); switch(hs->vkCode) { default: if(0u == (hs->flags & LLKHF_EXTENDED)) { uint16_t ucBuf[16]; BYTE keyState[256]; GetKeyboardState(keyState); if(ToUnicode(hs->vkCode, hs->scanCode, keyState, (LPWSTR)ucBuf, 8/*cchBuff*/, 0/*wFlags*/) >= 1) { bHandled = loc_handle_key(khook_lglw, LGLW_FALSE/*bPressed*/, ucBuf[0]); } } else { bHandled = loc_handle_key(khook_lglw, LGLW_FALSE/*bPressed*/, hs->vkCode | LGLW_VKEY_EXT); bHandled = LGLW_FALSE; } break; case VK_F1: case VK_F2: case VK_F3: case VK_F4: case VK_F5: case VK_F6: case VK_F7: case VK_F8: case VK_F9: case VK_F10: case VK_F11: case VK_F12: case VK_BACK: case VK_TAB: case VK_RETURN: case VK_ESCAPE: bHandled = loc_handle_key(khook_lglw, LGLW_FALSE/*bPressed*/, hs->vkCode | LGLW_VKEY_EXT); break; case VK_LSHIFT: khook_lglw->keyboard.kmod_state &= ~LGLW_KMOD_LSHIFT; bHandled = loc_handle_key(khook_lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_LSHIFT); bHandled = LGLW_FALSE; break; case VK_RSHIFT: khook_lglw->keyboard.kmod_state &= ~LGLW_KMOD_RSHIFT; bHandled = loc_handle_key(khook_lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_RSHIFT); bHandled = LGLW_FALSE; break; case VK_LCONTROL: khook_lglw->keyboard.kmod_state &= ~LGLW_KMOD_LCTRL; bHandled = loc_handle_key(khook_lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_LCTRL); bHandled = LGLW_FALSE; break; case VK_RCONTROL: khook_lglw->keyboard.kmod_state &= ~LGLW_KMOD_RCTRL; bHandled = loc_handle_key(khook_lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_RCTRL); bHandled = LGLW_FALSE; break; case VK_LMENU: khook_lglw->keyboard.kmod_state &= ~LGLW_KMOD_LALT; bHandled = loc_handle_key(khook_lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_LALT); bHandled = LGLW_FALSE; break; case VK_RMENU: khook_lglw->keyboard.kmod_state &= ~LGLW_KMOD_RALT; bHandled = loc_handle_key(khook_lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_RALT); bHandled = LGLW_FALSE; break; // case VK_LMENU: // not revc'd // khook_lglw->keyboard.kmod_state &= ~LGLW_KMOD_LSUPER; // bHandled = loc_handle_key(khook_lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_LSUPER); // bHandled = LGLW_FALSE; // break; case 0x5D/*VK_CONTEXTKEY*/: khook_lglw->keyboard.kmod_state &= ~LGLW_KMOD_RSUPER; bHandled = loc_handle_key(khook_lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_RSUPER); bHandled = LGLW_FALSE; break; } break; } if(!bHandled) { Dprintf("xxx lglw: CallNextHookEx\n"); return CallNextHookEx(NULL, nCode, wParam, lParam); } else { Dprintf("xxx lglw: CallNextHookEx: was handled\n"); return 1; } } // if khook_lglw } return CallNextHookEx(NULL, nCode, wParam, lParam); } // ---------------------------------------------------------------------------- loc_WndProc LRESULT CALLBACK loc_WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { LGLW(GetWindowLongPtr(hWnd, GWLP_USERDATA)); if(NULL != lglw) { // Dprintf("xxx lglw:loc_WndProc: message=%08x wParam=0x%08x lParam=0x%08x\n", message, (uint32_t)wParam, (uint32_t)lParam); switch(message) { case WM_CREATE: Dprintf("xxx lglw: WM_CREATE\n"); break; case WM_DESTROY: Dprintf("xxx lglw: WM_DESTROY\n"); break; case WM_ACTIVATEAPP: // 0x1c wParam=0/1 Dprintf("xxx lglw: WM_ACTIVATEAPP wParam=%d\n", (int32_t)wParam); break; case WM_MOUSELEAVE: // (note) only received after TrackMouseEvent() has been called Dprintf("xxx lglw: WM_MOUSELEAVE\n"); loc_handle_mouseleave(lglw); break; case WM_MOUSEMOVE: // Dprintf("xxx lglw: WM_MOUSEMOVE:\n"); if(lglw->mouse.touch.b_update_queued) { loc_touchinput_update(lglw); lglw->mouse.touch.b_update_queued = LGLW_FALSE; } if(!lglw->mouse.touch.b_enable) { int32_t x = GET_X_LPARAM(lParam); // lo int32_t y = GET_Y_LPARAM(lParam); // hi if(LGLW_MOUSE_GRAB_WARP == lglw->mouse.grab.mode) { lglw->mouse.grab.b_queue_warp = LGLW_TRUE; lglw->mouse.p.x += (x - lglw->mouse.grab.last_p.x); lglw->mouse.p.y += (y - lglw->mouse.grab.last_p.y); lglw->mouse.grab.last_p.x = x; lglw->mouse.grab.last_p.y = y; } else { lglw->mouse.p.x = x; lglw->mouse.p.y = y; } if(0u == (lglw->focus.state & LGLW_FOCUS_MOUSE)) { loc_handle_mouseenter(lglw); } loc_handle_mousemotion(lglw); } break; case WM_LBUTTONDOWN: if(!lglw->mouse.touch.b_enable) { // Dprintf("xxx lglw: WM_LBUTTONDOWN\n"); loc_handle_mousebutton(lglw, LGLW_TRUE/*bPressed*/, LGLW_MOUSE_LBUTTON); } break; case WM_LBUTTONUP: if(!lglw->mouse.touch.b_enable) { // Dprintf("xxx lglw: WM_LBUTTONUP\n"); loc_handle_mousebutton(lglw, LGLW_FALSE/*bPressed*/, LGLW_MOUSE_LBUTTON); } break; case WM_RBUTTONDOWN: if(!lglw->mouse.touch.b_enable) { // Dprintf("xxx lglw: WM_RBUTTONDOWN\n"); loc_handle_mousebutton(lglw, LGLW_TRUE/*bPressed*/, LGLW_MOUSE_RBUTTON); } break; case WM_RBUTTONUP: if(!lglw->mouse.touch.b_enable) { // Dprintf("xxx lglw: WM_RBUTTONUP\n"); loc_handle_mousebutton(lglw, LGLW_FALSE/*bPressed*/, LGLW_MOUSE_RBUTTON); } break; case WM_MBUTTONDOWN: if(!lglw->mouse.touch.b_enable) { // Dprintf("xxx lglw: WM_MBUTTONDOWN\n"); loc_handle_mousebutton(lglw, LGLW_TRUE/*bPressed*/, LGLW_MOUSE_MBUTTON); } break; case WM_MBUTTONUP: if(!lglw->mouse.touch.b_enable) { // Dprintf("xxx lglw: WM_MBUTTONUP\n"); loc_handle_mousebutton(lglw, LGLW_FALSE/*bPressed*/, LGLW_MOUSE_MBUTTON); } break; case WM_MOUSEWHEEL: // Dprintf("xxx lglw: WM_MOUSEWHEEL\n"); { uint32_t bt = (((int16_t)(((uint32_t)wParam)>>16)) > 0) ? LGLW_MOUSE_WHEELUP : LGLW_MOUSE_WHEELDOWN; loc_handle_mousebutton(lglw, LGLW_TRUE/*bPressed*/, bt); loc_handle_mousebutton(lglw, LGLW_FALSE/*bPressed*/, bt); } break; case WM_CAPTURECHANGED: // e.g. after alt-tab Dprintf("xxx lglw: WM_CAPTURECHANGED\n"); lglw->mouse.grab.mode = LGLW_MOUSE_GRAB_NONE; break; #if 1 // (note) VST windows usually don't receive key/char messages (they are consumed by the DAW) case WM_GETDLGCODE: // never received Dprintf("xxx lglw: WM_GETDLGCODE\n"); return DLGC_WANTALLKEYS; case WM_KEYDOWN: // never received Dprintf("xxx lglw: WM_KEYDOWN nVirtKey=0x%08x lKeydata=0x%08x\n", (uint32_t)wParam, (uint32_t)lParam); break; case WM_KEYUP: // never received Dprintf("xxx lglw: WM_KEYUP nVirtKey=0x%08x lKeydata=0x%08x\n", (uint32_t)wParam, (uint32_t)lParam); break; case WM_CHAR: // never received Dprintf("xxx lglw: WM_CHAR charCode=0x%08x bPressed=%u\n", (uint32_t)wParam, (((uint32_t)lParam)>>31)&1u); break; #endif case WM_PAINT: // https://docs.microsoft.com/en-us/windows/desktop/api/Winuser/nf-winuser-redrawwindow // Dprintf("xxx lglw: WM_PAINT\n"); loc_handle_queued_mouse_warp(lglw); if(NULL != lglw->redraw.cbk) { lglw->redraw.cbk(lglw); } break; case WM_TIMER: // Dprintf("xxx lglw: WM_TIMER cbk=%p\n", lglw->timer.cbk); if(lglw->mouse.touch.b_enable) { // Dprintf("xxx lglw: WM_TIMER hold_state=%u\n", lglw->mouse.touch.syn_rmb_hold_state); if(LGLW_MOUSE_TOUCH_RMB_STATE_IDLE != lglw->mouse.touch.syn_rmb_hold_state) { uint32_t ms = (uint32_t)GetTickCount(); uint32_t delta; if(ms < lglw->mouse.touch.hold_start_ms) { // Overflow after 49.7 days delta = ~lglw->mouse.touch.hold_start_ms + 1u + ms; } else { delta = (ms - lglw->mouse.touch.hold_start_ms); } if(LGLW_MOUSE_TOUCH_RMB_STATE_LMB == lglw->mouse.touch.syn_rmb_hold_state) { if(delta >= LGLW_MOUSE_TOUCH_LMB_TIMEOUT) { loc_handle_mousebutton(lglw, LGLW_FALSE/*bPressed*/, LGLW_MOUSE_LBUTTON); lglw->mouse.touch.syn_rmb_hold_state = LGLW_MOUSE_TOUCH_RMB_STATE_WAIT; } } if(LGLW_MOUSE_TOUCH_RMB_STATE_WAIT == lglw->mouse.touch.syn_rmb_hold_state) { if(delta >= LGLW_MOUSE_TOUCH_RMB_TIMEOUT) { lglw->mouse.touch.syn_rmb_hold_state = LGLW_MOUSE_TOUCH_RMB_STATE_RMB; loc_handle_mousebutton(lglw, LGLW_TRUE/*bPressed*/, LGLW_MOUSE_RBUTTON); } } } } else { loc_handle_queued_mouse_warp(lglw); } if(NULL != lglw->timer.cbk) { lglw->timer.cbk(lglw); } break; case WM_DROPFILES: if(NULL != lglw->dropfiles.cbk) { HDROP hDrop = (HDROP) wParam; POINT p; if(TRUE == DragQueryPoint(hDrop, &p)) { Dprintf("xxx lglw: WM_DROPFILES: p=(%d; %d)\n", p.x, p.y); UINT numFiles = DragQueryFile(hDrop, 0xffffffff, NULL, 0); Dprintf("xxx lglw: WM_DROPFILES: dropped %u file(s)\n", numFiles); if(numFiles > 0u) { UINT i; char **pathnames = malloc((sizeof(char*) * numFiles) + (MAX_PATH * sizeof(char) * numFiles)); char *pathname = (char*)&pathnames[numFiles]; for(i=0; idropfiles.cbk(lglw, p.x, p.y, numFiles, pathnames); free(pathnames); } } } break; #ifdef BUILD_64 // Touch messages: #ifndef GET_X_LPARAM #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp)) #define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam)) #define IS_POINTER_FLAG_SET_WPARAM(wParam, flag) (((DWORD)HIWORD(wParam) & (flag)) == (flag)) #define IS_POINTER_NEW_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_NEW) #define IS_POINTER_INRANGE_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_INRANGE) #define IS_POINTER_INCONTACT_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_INCONTACT) #define IS_POINTER_FIRSTBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIRSTBUTTON) #define IS_POINTER_SECONDBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_SECONDBUTTON) #define IS_POINTER_THIRDBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_THIRDBUTTON) #define IS_POINTER_FOURTHBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FOURTHBUTTON) #define IS_POINTER_FIFTHBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIFTHBUTTON) #define IS_POINTER_PRIMARY_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_PRIMARY) #define HAS_POINTER_CONFIDENCE_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_CONFIDENCE) #define IS_POINTER_CANCELED_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_CANCELED) #endif case WM_POINTERDOWN: { POINT p; lglw->mouse.touch.b_enable = LGLW_TRUE; lglw->mouse.touch.b_update_queued = LGLW_FALSE; if(0u == (lglw->focus.state & LGLW_FOCUS_MOUSE)) { loc_handle_mouseenter(lglw); } // Dprintf("xxx lglw: WM_POINTERDOWN\n"); // Dprintf("xxx lglw: WM_POINTERDOWN: IS_POINTER_FIRSTBUTTON_WPARAM(wParam)=%d\n", IS_POINTER_FIRSTBUTTON_WPARAM(wParam)); // Dprintf("xxx lglw: WM_POINTERDOWN: IS_POINTER_SECONDBUTTON_WPARAM(wParam)=%d\n", IS_POINTER_SECONDBUTTON_WPARAM(wParam)); // Dprintf("xxx lglw: WM_POINTERDOWN: IS_POINTER_THIRDBUTTON_WPARAM(wParam)=%d\n", IS_POINTER_THIRDBUTTON_WPARAM(wParam)); p.x = GET_X_LPARAM(lParam); p.y = GET_Y_LPARAM(lParam); (void)ScreenToClient(lglw->win.hwnd, &p); lglw->mouse.p.x = p.x; lglw->mouse.p.y = p.y; loc_handle_mousemotion(lglw); if(IS_POINTER_FIRSTBUTTON_WPARAM(wParam)) { lglw->mouse.touch.b_syn_rmb = (0u != (lglw->keyboard.kmod_state & LGLW_KMOD_CTRL)); loc_handle_mousebutton(lglw, LGLW_TRUE/*bPressed*/, lglw->mouse.touch.b_syn_rmb ? LGLW_MOUSE_RBUTTON : LGLW_MOUSE_LBUTTON ); if(lglw->mouse.touch.b_syn_rmb) { lglw->mouse.touch.syn_rmb_hold_state = LGLW_MOUSE_TOUCH_RMB_STATE_IDLE; } else { lglw->mouse.touch.syn_rmb_hold_state = LGLW_MOUSE_TOUCH_RMB_STATE_LMB; lglw->mouse.touch.hold_start_ms = GetTickCount(); lglw->mouse.touch.hold_start_p = lglw->mouse.p; } } else if(IS_POINTER_SECONDBUTTON_WPARAM(wParam)) { loc_handle_mousebutton(lglw, LGLW_TRUE/*bPressed*/, LGLW_MOUSE_RBUTTON); } else if(IS_POINTER_THIRDBUTTON_WPARAM(wParam)) { loc_handle_mousebutton(lglw, LGLW_TRUE/*bPressed*/, LGLW_MOUSE_MBUTTON); } } break; case WM_POINTERUP: { POINT p; // Dprintf("xxx lglw: WM_POINTERUP\n"); lglw->mouse.touch.b_enable = LGLW_TRUE; lglw->mouse.touch.b_update_queued = LGLW_FALSE; p.x = GET_X_LPARAM(lParam); p.y = GET_Y_LPARAM(lParam); (void)ScreenToClient(lglw->win.hwnd, &p); lglw->mouse.p.x = p.x; lglw->mouse.p.y = p.y; loc_handle_mousemotion(lglw); // Dprintf("xxx lglw: WM_POINTERUP: IS_POINTER_FIRSTBUTTON_WPARAM(wParam)=%d\n", IS_POINTER_FIRSTBUTTON_WPARAM(wParam)); // Dprintf("xxx lglw: WM_POINTERUP: IS_POINTER_SECONDBUTTON_WPARAM(wParam)=%d\n", IS_POINTER_SECONDBUTTON_WPARAM(wParam)); // Dprintf("xxx lglw: WM_POINTERUP: IS_POINTER_THIRDBUTTON_WPARAM(wParam)=%d\n", IS_POINTER_THIRDBUTTON_WPARAM(wParam)); if( (0u != (lglw->mouse.button_state & LGLW_MOUSE_LBUTTON)) && !IS_POINTER_FIRSTBUTTON_WPARAM(wParam) ) { if(LGLW_MOUSE_TOUCH_RMB_STATE_RMB == lglw->mouse.touch.syn_rmb_hold_state) { loc_handle_mousebutton(lglw, LGLW_FALSE/*bPressed*/, LGLW_MOUSE_RBUTTON); } else { loc_handle_mousebutton(lglw, LGLW_FALSE/*bPressed*/, lglw->mouse.touch.b_syn_rmb ? LGLW_MOUSE_RBUTTON : LGLW_MOUSE_LBUTTON ); } lglw->mouse.touch.b_syn_rmb = LGLW_FALSE; lglw->mouse.touch.syn_rmb_hold_state = LGLW_MOUSE_TOUCH_RMB_STATE_IDLE; } else if( (0u != (lglw->mouse.button_state & LGLW_MOUSE_RBUTTON)) && !IS_POINTER_SECONDBUTTON_WPARAM(wParam)) { loc_handle_mousebutton(lglw, LGLW_FALSE/*bPressed*/, LGLW_MOUSE_RBUTTON); } else if( (0u != (lglw->mouse.button_state & LGLW_MOUSE_MBUTTON)) && !IS_POINTER_THIRDBUTTON_WPARAM(wParam)) { loc_handle_mousebutton(lglw, LGLW_FALSE/*bPressed*/, LGLW_MOUSE_MBUTTON); } } break; case WM_POINTERENTER: { POINT p; Dprintf("xxx lglw: WM_POINTERENTER\n"); lglw->mouse.touch.b_enable = LGLW_TRUE; lglw->mouse.touch.b_update_queued = LGLW_FALSE; if(0u == (lglw->focus.state & LGLW_FOCUS_MOUSE)) { loc_handle_mouseenter(lglw); } p.x = GET_X_LPARAM(lParam); p.y = GET_Y_LPARAM(lParam); (void)ScreenToClient(lglw->win.hwnd, &p); lglw->mouse.p.x = p.x; lglw->mouse.p.y = p.y; loc_handle_mousemotion(lglw); loc_handle_mouseenter(lglw); } break; case WM_POINTERLEAVE: { POINT p; Dprintf("xxx lglw: WM_POINTERLEAVE\n"); lglw->mouse.touch.b_enable = LGLW_TRUE; lglw->mouse.touch.b_update_queued = LGLW_FALSE; p.x = GET_X_LPARAM(lParam); p.y = GET_Y_LPARAM(lParam); (void)ScreenToClient(lglw->win.hwnd, &p); loc_handle_mouseleave(lglw); } break; case WM_POINTERUPDATE: { POINT p; lglw->mouse.touch.b_enable = LGLW_TRUE; lglw->mouse.touch.b_update_queued = LGLW_FALSE; p.x = GET_X_LPARAM(lParam); p.y = GET_Y_LPARAM(lParam); (void)ScreenToClient(lglw->win.hwnd, &p); lglw->mouse.p.x = p.x; lglw->mouse.p.y = p.y; loc_handle_mousemotion(lglw); if(LGLW_MOUSE_TOUCH_RMB_STATE_LMB == lglw->mouse.touch.syn_rmb_hold_state) { if( (sABS(p.x - lglw->mouse.touch.hold_start_p.x) >= LGLW_MOUSE_TOUCH_RMB_MOVE_THRESHOLD) || (sABS(p.y - lglw->mouse.touch.hold_start_p.y) >= LGLW_MOUSE_TOUCH_RMB_MOVE_THRESHOLD) ) { lglw->mouse.touch.syn_rmb_hold_state = LGLW_MOUSE_TOUCH_RMB_STATE_IDLE; } } } break; #endif // BUILD_64 } // switch message } // if lglw return DefWindowProc(hWnd, message, wParam, lParam); }