Main place for discussions about U++ cross platform development framework.
Hi, I just got a Wacom Intuos S (pen tablet). I'm planning on supporting using these devices with my software. Using the pen in place of the mouse works mostly as expected. However, I would like to read the pen pressure in MouseMove(), (and possibly tilt/rotation some day too): POINTER_INFO pi; POINTER_PEN_INFO ppi; UINT32 pressure=0; if(GetPointerInfo(GET_POINTERID_WPARAM(wParam), &pi)) { if(pi.pointerType == PT_PEN) { UINT32 ppid = pi.pointerId; if(GetPointerPenInfo(ppid, &ppi)) pressure=ppi.pressure; } } ,but I can't, since I cannot access wParam. Would it be much trouble to add support inside CtrlCore for getting pen pressure for current MouseMove()? Unfortunately GetPointerInfo() and GetPointerPenInfo() are only available in Windows 8 and later, so some conditioning is required. Best regards, Tom
Tom1 wrote on Wed, 03 March 2021 16:13 Hi, I just got a Wacom Intuos S (pen tablet). I'm planning on supporting using these devices with my software. Using the pen in place of the mouse works mostly as expected. However, I would like to read the pen pressure in MouseMove(), (and possibly tilt/rotation some day too): POINTER_INFO pi; POINTER_PEN_INFO ppi; UINT32 pressure=0; if(GetPointerInfo(GET_POINTERID_WPARAM(wParam), &pi)) { if(pi.pointerType == PT_PEN) { UINT32 ppid = pi.pointerId; if(GetPointerPenInfo(ppid, &ppi)) pressure=ppi.pressure; } } ,but I can't, since I cannot access wParam. Would it be much trouble to add support inside CtrlCore for getting pen pressure for current MouseMove()? Unfortunately GetPointerInfo() and GetPointerPenInfo() are only available in Windows 8 and later, so some conditioning is required. Best regards, Tom Sure and you can code around Windows 8 stuff (we do it here and there already, you just need to load the function pointer in the code). Going to search for cheap tablet Mirek
Hi Mirek, Thanks, sounds great! I was too frugal to get one with tilt and/or rotation as I don't really need them at this time. So I can only test the pressure part here. I wonder if Gdk support is straight forward... Best regards, Tom
Should be implemented in windows. This works on my computer: #include <CtrlLib/CtrlLib.h> using namespace Upp; struct MyApp : TopWindow { Point pos; Vector<Vector<Tuple<double, Pointf>>> drawing; virtual void LeftDown(Point p, dword) { if(IsPointerPen()) drawing.Add().Add(MakeTuple(GetPenPressure(), p)); Refresh(); } virtual void MouseMove(Point p, dword keyflags) { pos = p; if(IsPointerPen() && drawing.GetCount()) drawing.Top().Add(MakeTuple(GetPenPressure(), p)); Refresh(); } virtual void Paint(Draw& w0) { DrawPainter w(w0, GetSize()); w.Clear(SColorPaper()); w.LineCap(LINECAP_ROUND); for(const auto& stroke : drawing) if(stroke.GetCount()) for(int i = 0; i < stroke.GetCount() - 1; i++) { w.Move(stroke[i].b); w.Line(stroke[i + 1].b); w.Stroke(DPI(20) * stroke[i].a, Black()); } int fcy = GetStdFontCy(); int y = 10; auto Text = [&] (const String& text) { w.DrawText(10, y, text); y += fcy; }; Text(AsString(pos)); Text(String() << "Pen: " << IsPointerPen()); Text(String() << "Pressure: " << GetPenPressure()); Text(String() << "Rotation: " << GetPenRotation()); Text(String() << "Tilt: " << GetPenTilt()); Text(String() << "Barrel: " << IsPenBarrelPressed()); Text(String() << "Inverted: " << IsPenInverted()); Text(String() << "Eraser: " << IsPenInverted()); Refresh(); } }; GUI_APP_MAIN { MyApp().Run(); } Cheap tablet I have bought does not have tilt/rotation support so I cannot test it, but the code is quite straightforward so there is a good chance it will work - it is good that you have that HW in yours These changes should not break Win7 compatibility, once again would be nice if you tested that.
Hi Mirek, Thanks for getting this going! Unfortunately, my hardware returns false for IsPointerPen(). Based on RDUMPs I tried, it never even visits this code in Ctrl::WindowProc() : case WM_POINTERDOWN: case WM_POINTERUPDATE: case WM_POINTERUP: ... I will try to dig in further to figure out what's going on... As for Windows 7, I have none left... I hope someone around here still does. Best regards, Tom
Tom1 wrote on Tue, 09 March 2021 13:10 Hi Mirek, Thanks for getting this going! Unfortunately, my hardware returns false for IsPointerPen(). Based on RDUMPs I tried, it never even visits this code in Ctrl::WindowProc() : case WM_POINTERDOWN: case WM_POINTERUPDATE: case WM_POINTERUP: ... I will try to dig in further to figure out what's going on... As for Windows 7, I have none left... I hope someone around here still does. Best regards, Tom Do you have proper drivers installed? I have found it is not quite easy to get the pen working... Mirek
Hi, Yes, drivers are the current WACOM drivers. The pen does show pressure on Wacom Tablet Properties -control app. Now I found that I had to enable 'Use Windows Ink' checkbox on Wacom Tablet Properties / Mapping page. Otherwise there will be no WM_POINTER... messages at all. However, this is unfortunate, since this causes drawing to start with a couple of centimeters long straight line before I can draw any curves. Anyway, this gets the IsPointerPen() true! I also found that: if(ppi.penMask & PEN_MASK_ROTATION) pen_pressure = ppi.rotation * M_2PI / 360; should be if(ppi.penMask & PEN_MASK_ROTATION) pen_rotation = ppi.rotation * M_2PI / 360; Then the thickness works based on pressure. Best regards, Tom
Tom1 wrote on Tue, 09 March 2021 13:41 Hi, Yes, drivers are the current WACOM drivers. The pen does show pressure on Wacom Tablet Properties -control app. Now I found that I had to enable 'Use Windows Ink' checkbox on Wacom Tablet Properties / Mapping page. Otherwise there will be no WM_POINTER... messages at all. However, this is unfortunate, since this causes drawing to start with a couple of centimeters long straight line before I can draw any curves. Anyway, this gets the IsPointerPen() true! I also found that: if(ppi.penMask & PEN_MASK_ROTATION) pen_pressure = ppi.rotation * M_2PI / 360; should be if(ppi.penMask & PEN_MASK_ROTATION) pen_rotation = ppi.rotation * M_2PI / 360; Then the thickness works based on pressure. Best regards, Tom Thanks, thats a typo, fixed.
Mirek, did you get a Wacom or something else? When I start drawing with the pen (and using 'Use Windows Ink' mode), Windows first draws a circle around the pointer and then only after I have moved about two centimeters, it starts drawing... with the first two centimeters of straight line and only thereafter it accurately follows the pen. I do not know, but this feels like a Windows Ink behavior. Anyway, it makes freehand drawing impossible. I need to figure out how to get rid of this pre-processing of pen movements. Best regards, Tom
Hi, I can now see that even when using 'Use Windows Ink' -mode, Microsoft's own 'Snip and Sketch' tool can draw tiny features without any 2 cm straight lines starters. There must be something that can be used to turn off that pre-processing programmatically. Best regards, Tom
Tom1 wrote on Tue, 09 March 2021 13:59 Mirek, did you get a Wacom or something else? When I start drawing with the pen (and using 'Use Windows Ink' mode), Windows first draws a circle around the pointer and then only after I have moved about two centimeters, it starts drawing... with the first two centimeters of straight line and only thereafter it accurately follows the pen. I do not know, but this feels like a Windows Ink behavior. Anyway, it makes freehand drawing impossible. I need to figure out how to get rid of this pre-processing of pen movements. Best regards, Tom XP-PEN. Knowing that you have Wacom, wanted to diversify...
Are you testing with my code? Because I can only speak about that one and with xp-pen, it works fine with my example... Mirek
Diversifying input devices sounds a good idea. Especially now that they indeed work differently. And yes, I'm using your testing code, since diversifying there does not sound too smart... - Anyway, now I found that I cannot get any WM_MOUSEMOVE from the first 2 cm on screen when using Wacom with Use Windows Ink enabled. However, all the moves right from start end up in WM_POINTERUPDATE. Now this means that we need to add the following code to WM_POINTERUPDATE for pen and disable it in WM_MOUSEMOVE for pen: DoMouse(MOUSEMOVE, Point(lParam)); DoCursorShape(); However, there's a slight problem here. The lParam coordinates are for the screen not window, so it needs some translation. I don't know yet how to do it... Best regards, Tom
Tom1 wrote on Tue, 09 March 2021 15:07 Diversifying input devices sounds a good idea. Especially now that they indeed work differently. And yes, I'm using your testing code, since diversifying there does not sound too smart... - Anyway, now I found that I cannot get any WM_MOUSEMOVE from the first 2 cm on screen when using Wacom with Use Windows Ink enabled. However, all the moves right from start end up in WM_POINTERUPDATE. Now this means that we need to add the following code to WM_POINTERUPDATE for pen and disable it in WM_MOUSEMOVE for pen: Disable part seems quite hard to do... Mirek
How about this approach using EnableMouseInPointer() as follows? LRESULT Ctrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { GuiLock __; eventid++; // LLOG("Ctrl::WindowProc(" << message << ") in " << ::Name(this) << ", focus " << (void *)::GetFocus()); Ptr<Ctrl> _this = this; HWND hwnd = GetHWND(); ONCELOCK { EnableMouseInPointer(true); // #1 - enable mouse in WM_POINTER* messages }; switch(message) { case WM_POINTERDOWN: case WM_POINTERUPDATE: case WM_POINTERUP: { POINT p = Point(lParam); ScreenToClient(hwnd, &p); pen = false; pen_pressure = pen_rotation = Null; pen_tilt = Null; pen_eraser = false; pen_barrel = false; pen_inverted = false; static BOOL (WINAPI *GetPointerType)(UINT32 pointerId, POINTER_INPUT_TYPE *pointerType); static BOOL (WINAPI *GetPointerInfo)(UINT32 pointerId, POINTER_INFO *pointerInfo); static BOOL (WINAPI *GetPointerPenInfo)(UINT32 pointerId, POINTER_PEN_INFO *penInfo); static BOOL (WINAPI *GetPointerTouchInfo)(UINT32 pointerId, POINTER_TOUCH_INFO *touchInfo); ONCELOCK { DllFn(GetPointerType, "User32.dll", "GetPointerType"); DllFn(GetPointerInfo, "User32.dll", "GetPointerInfo"); DllFn(GetPointerPenInfo, "User32.dll", "GetPointerPenInfo"); DllFn(GetPointerTouchInfo, "User32.dll", "GetPointerTouchInfo"); }; POINTER_INPUT_TYPE pointerType; UINT32 pointerId = GET_POINTERID_WPARAM(wParam); if(GetPointerType && GetPointerPenInfo && GetPointerType(pointerId, &pointerType)) { switch(pointerType){ case PT_PEN:{ POINTER_PEN_INFO ppi; if(GetPointerPenInfo(pointerId, &ppi)) { pen = true; if(ppi.penFlags & PEN_FLAG_BARREL) pen_barrel = true; if(ppi.penFlags & PEN_FLAG_INVERTED) pen_inverted = true; if(ppi.penFlags & PEN_FLAG_ERASER) pen_eraser = true; if(ppi.penMask & PEN_MASK_PRESSURE) pen_pressure = ppi.pressure / 1024.0; if(ppi.penMask & PEN_MASK_ROTATION) pen_rotation = ppi.rotation * M_2PI / 360; if(ppi.penMask & PEN_MASK_TILT_X) pen_tilt.x = ppi.tiltX * M_2PI / 360; if(ppi.penMask & PEN_MASK_TILT_Y) pen_tilt.y = ppi.tiltY * M_2PI / 360; } break; } case PT_TOUCH:{ POINTER_TOUCH_INFO pti; if(GetPointerTouchInfo(pointerId, &pti)) { // Add something touch specific here some day maybe... } break; } default:{ POINTER_INFO pi; if(GetPointerInfo(pointerId, &pi)) { } break; } } if(_this) switch(message){ case WM_POINTERDOWN: ClickActivateWnd(); DoMouse(LEFTDOWN, Point(p), 0); PostInput(); break; case WM_POINTERUP: DoMouse(LEFTUP, Point(p), 0); PostInput(); break; case WM_POINTERUPDATE: DoMouse(MOUSEMOVE, Point(p)); DoCursorShape(); break; } } } break; case WM_POINTERLEAVE: pen = false; break; Further more, this requires disabling WM_LEFTUP, WM_LEFTDOWN and WM_MOUSEMOVE as they now come in as WM_POINTER* messages. IMPORTANT WARNING: I have not replicated their function completely in WM_POINTER above as I do not quite understand all the finer details there. Also, I'm worried about breaking everything, so I'll just throw this in 'as is' for you to consider... Oh and yes, this fixes the 2 cm straight line start issue... Best regards, Tom EDIT: EnableMouseInPointer() is available on Windows8 and above only, so it needs to be loaded in a similar way as the rest of the Pointer -functions.
Tom1 wrote on Tue, 09 March 2021 16:56 How about this approach using EnableMouseInPointer() as follows? LRESULT Ctrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { GuiLock __; eventid++; // LLOG("Ctrl::WindowProc(" << message << ") in " << ::Name(this) << ", focus " << (void *)::GetFocus()); Ptr<Ctrl> _this = this; HWND hwnd = GetHWND(); ONCELOCK { EnableMouseInPointer(true); // #1 - enable mouse in WM_POINTER* messages }; switch(message) { case WM_POINTERDOWN: case WM_POINTERUPDATE: case WM_POINTERUP: { POINT p = Point(lParam); ScreenToClient(hwnd, &p); pen = false; pen_pressure = pen_rotation = Null; pen_tilt = Null; pen_eraser = false; pen_barrel = false; pen_inverted = false; static BOOL (WINAPI *GetPointerType)(UINT32 pointerId, POINTER_INPUT_TYPE *pointerType); static BOOL (WINAPI *GetPointerInfo)(UINT32 pointerId, POINTER_INFO *pointerInfo); static BOOL (WINAPI *GetPointerPenInfo)(UINT32 pointerId, POINTER_PEN_INFO *penInfo); static BOOL (WINAPI *GetPointerTouchInfo)(UINT32 pointerId, POINTER_TOUCH_INFO *touchInfo); ONCELOCK { DllFn(GetPointerType, "User32.dll", "GetPointerType"); DllFn(GetPointerInfo, "User32.dll", "GetPointerInfo"); DllFn(GetPointerPenInfo, "User32.dll", "GetPointerPenInfo"); DllFn(GetPointerTouchInfo, "User32.dll", "GetPointerTouchInfo"); }; POINTER_INPUT_TYPE pointerType; UINT32 pointerId = GET_POINTERID_WPARAM(wParam); if(GetPointerType && GetPointerPenInfo && GetPointerType(pointerId, &pointerType)) { switch(pointerType){ case PT_PEN:{ POINTER_PEN_INFO ppi; if(GetPointerPenInfo(pointerId, &ppi)) { pen = true; if(ppi.penFlags & PEN_FLAG_BARREL) pen_barrel = true; if(ppi.penFlags & PEN_FLAG_INVERTED) pen_inverted = true; if(ppi.penFlags & PEN_FLAG_ERASER) pen_eraser = true; if(ppi.penMask & PEN_MASK_PRESSURE) pen_pressure = ppi.pressure / 1024.0; if(ppi.penMask & PEN_MASK_ROTATION) pen_rotation = ppi.rotation * M_2PI / 360; if(ppi.penMask & PEN_MASK_TILT_X) pen_tilt.x = ppi.tiltX * M_2PI / 360; if(ppi.penMask & PEN_MASK_TILT_Y) pen_tilt.y = ppi.tiltY * M_2PI / 360; } break; } case PT_TOUCH:{ POINTER_TOUCH_INFO pti; if(GetPointerTouchInfo(pointerId, &pti)) { // Add something touch specific here some day maybe... } break; } default:{ POINTER_INFO pi; if(GetPointerInfo(pointerId, &pi)) { } break; } } if(_this) switch(message){ case WM_POINTERDOWN: ClickActivateWnd(); DoMouse(LEFTDOWN, Point(p), 0); PostInput(); break; case WM_POINTERUP: DoMouse(LEFTUP, Point(p), 0); PostInput(); break; case WM_POINTERUPDATE: DoMouse(MOUSEMOVE, Point(p)); DoCursorShape(); break; } } } break; case WM_POINTERLEAVE: pen = false; break; Further more, this requires disabling WM_LEFTUP, WM_LEFTDOWN and WM_MOUSEMOVE as they now come in as WM_POINTER* messages. IMPORTANT WARNING: I have not replicated their function completely in WM_POINTER above as I do not quite understand all the finer details there. Also, I'm worried about breaking everything, so I'll just throw this in 'as is' for you to consider... Oh and yes, this fixes the 2 cm straight line start issue... Best regards, Tom EDIT: EnableMouseInPointer() is available on Windows8 and above only, so it needs to be loaded in a similar way as the rest of the Pointer -functions. Looks good. Will try tommorow.
OK, good! But please try this one instead, as it takes into account the Win8 thing and also adds conditional blocking of WM_MOUSEMOVE, WM_LBUTTONUP, WM_LBUTTONDOWN: LRESULT Ctrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { GuiLock __; eventid++; // LLOG("Ctrl::WindowProc(" << message << ") in " << ::Name(this) << ", focus " << (void *)::GetFocus()); Ptr<Ctrl> _this = this; HWND hwnd = GetHWND(); static bool disableOldWMs=false; // When true, blocks out original WM_MOUSEMOVE, WM_LBUTTONUP, WM_LBUTTONDOWN switch(message) { case WM_POINTERDOWN: case WM_POINTERUPDATE: case WM_POINTERUP: { POINT p = Point(lParam); ScreenToClient(hwnd, &p); pen = false; pen_pressure = pen_rotation = Null; pen_tilt = Null; pen_eraser = false; pen_barrel = false; pen_inverted = false; static BOOL (WINAPI *EnableMouseInPointer)(WINBOOL fEnable); static BOOL (WINAPI *GetPointerType)(UINT32 pointerId, POINTER_INPUT_TYPE *pointerType); static BOOL (WINAPI *GetPointerInfo)(UINT32 pointerId, POINTER_INFO *pointerInfo); static BOOL (WINAPI *GetPointerPenInfo)(UINT32 pointerId, POINTER_PEN_INFO *penInfo); static BOOL (WINAPI *GetPointerTouchInfo)(UINT32 pointerId, POINTER_TOUCH_INFO *touchInfo); ONCELOCK { DllFn(EnableMouseInPointer, "User32.dll", "EnableMouseInPointer"); if(EnableMouseInPointer && EnableMouseInPointer(true)) disableOldWMs=true; // Switching over to WM_POINTER* functions for mouse DllFn(GetPointerType, "User32.dll", "GetPointerType"); DllFn(GetPointerInfo, "User32.dll", "GetPointerInfo"); DllFn(GetPointerPenInfo, "User32.dll", "GetPointerPenInfo"); DllFn(GetPointerTouchInfo, "User32.dll", "GetPointerTouchInfo"); }; POINTER_INPUT_TYPE pointerType; UINT32 pointerId = GET_POINTERID_WPARAM(wParam); if(GetPointerType && GetPointerPenInfo && GetPointerType(pointerId, &pointerType)) { switch(pointerType){ case PT_PEN:{ POINTER_PEN_INFO ppi; if(GetPointerPenInfo(pointerId, &ppi)) { pen = true; if(ppi.penFlags & PEN_FLAG_BARREL) pen_barrel = true; if(ppi.penFlags & PEN_FLAG_INVERTED) pen_inverted = true; if(ppi.penFlags & PEN_FLAG_ERASER) pen_eraser = true; if(ppi.penMask & PEN_MASK_PRESSURE) pen_pressure = ppi.pressure / 1024.0; if(ppi.penMask & PEN_MASK_ROTATION) pen_rotation = ppi.rotation * M_2PI / 360; if(ppi.penMask & PEN_MASK_TILT_X) pen_tilt.x = ppi.tiltX * M_2PI / 360; if(ppi.penMask & PEN_MASK_TILT_Y) pen_tilt.y = ppi.tiltY * M_2PI / 360; } break; } case PT_TOUCH:{ POINTER_TOUCH_INFO pti; if(GetPointerTouchInfo(pointerId, &pti)) { // Add something touch specific here some day maybe... } break; } default:{ POINTER_INFO pi; if(GetPointerInfo(pointerId, &pi)) { } break; } } if(_this) switch(message){ case WM_POINTERDOWN: ClickActivateWnd(); DoMouse(LEFTDOWN, Point(p), 0); PostInput(); break; case WM_POINTERUP: DoMouse(LEFTUP, Point(p), 0); PostInput(); break; case WM_POINTERUPDATE: DoMouse(MOUSEMOVE, Point(p)); DoCursorShape(); break; } } } break; case WM_POINTERLEAVE: pen = false; break; ... case WM_LBUTTONDOWN: if(disableOldWMs) break; ... case WM_LBUTTONUP: if(disableOldWMs) break; ... case WM_MOUSEMOVE: if(disableOldWMs) break; Without native POINTER sources, this one should use the original code all the way, I hope... Best regards, Tom
So I have some bad news, even if somewhat funny news: With your code, it starts drawing those nasty initial lines with my XP-PEN...
OK, if I add this to the beginning of WindowProc(): switch(message) { case WM_POINTERDOWN: RLOG("WM_POINTERDOWN"); break; case WM_POINTERUPDATE: RLOG("WM_POINTERUPDATE"); break; case WM_POINTERUP: RLOG("WM_POINTERUP"); break; case WM_MOUSEMOVE: RLOG("WM_MOUSEMOVE"); break; case WM_LBUTTONUP: RLOG("WM_LBUTTONUP"); break; case WM_LBUTTONDOWN: RLOG("WM_LBUTTONDOWN"); break; } I will get this sequence for drawing one line: ... WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_POINTERDOWN WM_POINTERUPDATE WM_POINTERUPDATE ... WM_POINTERUPDATE WM_POINTERUPDATE WM_POINTERUPDATE WM_POINTERUPDATE WM_POINTERUPDATE WM_POINTERUPDATE WM_POINTERUPDATE WM_MOUSEMOVE WM_LBUTTONDOWN WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE ... WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_POINTERUP WM_MOUSEMOVE WM_LBUTTONUP WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE ... How about you? Best regards, Tom
Added some log to the testing code too. virtual void LeftDown(Point p, dword) { if(IsPointerPen()) { DLOG("Start line"); drawing.Add().Add(MakeTuple(GetPenPressure(), p)); } Refresh(); } virtual void MouseMove(Point p, dword keyflags) { pos = p; if(IsPointerPen() && drawing.GetCount()) { DLOG("Drawing line, pressure: " << GetPenPressure()); drawing.Top().Add(MakeTuple(GetPenPressure(), p)); } Refresh(); } WM_MOUSEMOVE WM_POINTERUPDATE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_POINTERUPDATE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_POINTERDOWN WM_MOUSEMOVE WM_POINTERUPDATE WM_POINTERUPDATE WM_POINTERUPDATE WM_POINTERUPDATE WM_POINTERUPDATE WM_POINTERUPDATE WM_LBUTTONDOWN Start line WM_MOUSEMOVE Drawing line, pressure: 0.109375 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.12890625 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.1318359375 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.1337890625 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.1435546875 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.14453125 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.150390625 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.1689453125 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.1767578125 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.205078125 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.220703125 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.2421875 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.24609375 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.2578125 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.2607421875 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.2607421875 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.2607421875 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.2578125 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.2578125 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.2548828125 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.25390625 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.2548828125 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.2548828125 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.267578125 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.2666015625 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.2666015625 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.2685546875 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.267578125 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.267578125 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.267578125 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.26953125 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.2705078125 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.2705078125 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.271484375 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.1015625 WM_POINTERUPDATE WM_POINTERUP WM_LBUTTONUP WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0 WM_POINTERUPDATE WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0 WM_POINTERUPDATE WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0 WM_POINTERUPDATE WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0 WM_POINTERUPDATE WM_MOUSEMOVE WM_MOUSEMOVE WM_MOUSEMOVE WM_MOUSEMOVE WM_MOUSEMOVE WM_MOUSEMOVE WM_MOUSEMOVE Looking at it, maybe the problem is actually in testing code, at least partially? (drawing line continues after mouseup). Also note that I am not getting any other WM_POINTER message than UPDATE.... P.S.: Why RLOG? Do you want to forget it in the code?
Actually you do get those WM_POINTERDOWN and WM_POINTERUP. From your log: WM_POINTERUPDATE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_POINTERDOWN <<< HERE WM_MOUSEMOVE WM_POINTERUPDATE WM_POINTERUPDATE WM_POINTERUPDATE WM_POINTERUPDATE WM_POINTERUPDATE WM_POINTERUPDATE WM_LBUTTONDOWN Start line WM_MOUSEMOVE Drawing line, pressure: 0.109375 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.12890625 ... WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.271484375 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0.1015625 WM_POINTERUPDATE WM_POINTERUP <<< HERE WM_LBUTTONUP WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0 WM_POINTERUPDATE WM_MOUSEMOVE Drawing line, pressure: 0 WM_POINTERUPDATE WM_MOUSEMOVE What I find strange is that in the end, where you (I guess) take over with mouse, you do not get WM_POINTERUPDATEs. This suggests that the EnableMouseInPointer() is not working for you. Also, all your drawing takes place after WM_MOUSEMOVE and not WM_POINTERUPDATE, for some reason... It is no wonder the drawing continues at zero width after mouse up, since there is no code for LeftUp to stop it. Well, my RLOGs... I just use DEBUG mode only after getting in deep trouble... Best regards, Tom
OK, here's a fix for the testing code: #include <CtrlLib/CtrlLib.h> using namespace Upp; struct MyApp : TopWindow { Point pos; Vector<Vector<Tuple<double, Pointf>>> drawing; bool pendown; MyApp(){ pendown=false; } virtual void LeftUp(Point p, dword) { if(IsPointerPen()) { DLOG("End line"); pendown=false; } Refresh(); } virtual void LeftDown(Point p, dword) { if(IsPointerPen()) { DLOG("Start line"); pendown=true; drawing.Add().Add(MakeTuple(GetPenPressure(), p)); } Refresh(); } virtual void MouseMove(Point p, dword keyflags) { pos = p; if(IsPointerPen() && drawing.GetCount() && pendown) { DLOG("Drawing line, pressure: " << GetPenPressure()); drawing.Top().Add(MakeTuple(GetPenPressure(), p)); } Refresh(); } virtual void Paint(Draw& w0) { DrawPainter w(w0, GetSize()); w.Clear(SColorPaper()); w.LineCap(LINECAP_ROUND); for(const auto& stroke : drawing) if(stroke.GetCount()) for(int i = 0; i < stroke.GetCount() - 1; i++) { w.Move(stroke[i].b); w.Line(stroke[i + 1].b); w.Stroke(DPI(20) * stroke[i].a, Black()); } int fcy = GetStdFontCy(); int y = 10; auto Text = [&] (const String& text) { w.DrawText(10, y, text); y += fcy; }; Text(AsString(pos)); Text(String() << "Pen: " << IsPointerPen()); Text(String() << "Pressure: " << GetPenPressure()); Text(String() << "Rotation: " << GetPenRotation()); Text(String() << "Tilt: " << GetPenTilt()); Text(String() << "Barrel: " << IsPenBarrelPressed()); Text(String() << "Inverted: " << IsPenInverted()); Text(String() << "Eraser: " << IsPenEraserPressed()); // FIXED to IsPenEraserPressed() //Refresh(); // REMOVED } }; GUI_APP_MAIN { MyApp().Run(); } And results for single line draw: WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERDOWN Start line WM_POINTERUPDATE Drawing line, pressure: 0.107421875 WM_POINTERUPDATE Drawing line, pressure: 0.154296875 WM_POINTERUPDATE Drawing line, pressure: 0.205078125 WM_POINTERUPDATE Drawing line, pressure: 0.2255859375 WM_POINTERUPDATE Drawing line, pressure: 0.2255859375 WM_POINTERUPDATE Drawing line, pressure: 0.2255859375 WM_POINTERUPDATE Drawing line, pressure: 0.228515625 WM_POINTERUPDATE Drawing line, pressure: 0.2451171875 WM_POINTERUPDATE Drawing line, pressure: 0.2451171875 WM_MOUSEMOVE WM_LBUTTONDOWN WM_MOUSEMOVE WM_POINTERUPDATE Drawing line, pressure: 0.244140625 WM_MOUSEMOVE WM_POINTERUPDATE Drawing line, pressure: 0.263671875 WM_MOUSEMOVE WM_POINTERUPDATE Drawing line, pressure: 0.2802734375 WM_MOUSEMOVE WM_POINTERUPDATE Drawing line, pressure: 0.29296875 WM_MOUSEMOVE WM_POINTERUPDATE Drawing line, pressure: 0.3056640625 WM_MOUSEMOVE WM_POINTERUPDATE Drawing line, pressure: 0.3193359375 ... Drawing line, pressure: 0.4169921875 WM_MOUSEMOVE WM_POINTERUPDATE Drawing line, pressure: 0.4052734375 WM_MOUSEMOVE WM_POINTERUPDATE Drawing line, pressure: 0.37890625 WM_MOUSEMOVE WM_POINTERUPDATE Drawing line, pressure: 0.365234375 WM_MOUSEMOVE WM_POINTERUPDATE Drawing line, pressure: 0.3359375 WM_MOUSEMOVE WM_POINTERUPDATE Drawing line, pressure: 0.0625 WM_MOUSEMOVE WM_POINTERUP End line WM_LBUTTONUP WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE WM_POINTERUPDATE WM_MOUSEMOVE Best regards, Tom
Hi, This change fixes some tails when drawing fast. (This actually brings in your original 'ignoreclick' code and more.) static bool disableOldWMs=false; // When true, blocks out original WM_MOUSEMOVE, WM_LBUTTONUP, WM_LBUTTONDOWN switch(message) { case WM_POINTERDOWN: case WM_POINTERUPDATE: case WM_POINTERUP: { POINT p = Point((LONG)lParam); ScreenToClient(hwnd, &p); pen = false; pen_pressure = pen_rotation = Null; pen_tilt = Null; pen_eraser = false; pen_barrel = false; pen_inverted = false; static BOOL (WINAPI *EnableMouseInPointer)(BOOL fEnable); static BOOL (WINAPI *GetPointerType)(UINT32 pointerId, POINTER_INPUT_TYPE *pointerType); static BOOL (WINAPI *GetPointerInfo)(UINT32 pointerId, POINTER_INFO *pointerInfo); static BOOL (WINAPI *GetPointerPenInfo)(UINT32 pointerId, POINTER_PEN_INFO *penInfo); static BOOL (WINAPI *GetPointerTouchInfo)(UINT32 pointerId, POINTER_TOUCH_INFO *touchInfo); ONCELOCK { DllFn(EnableMouseInPointer, "User32.dll", "EnableMouseInPointer"); if(EnableMouseInPointer && EnableMouseInPointer(true)) disableOldWMs=true; // Switching over to WM_POINTER* functions for mouse DllFn(GetPointerType, "User32.dll", "GetPointerType"); DllFn(GetPointerInfo, "User32.dll", "GetPointerInfo"); DllFn(GetPointerPenInfo, "User32.dll", "GetPointerPenInfo"); DllFn(GetPointerTouchInfo, "User32.dll", "GetPointerTouchInfo"); }; POINTER_INPUT_TYPE pointerType; UINT32 pointerId = GET_POINTERID_WPARAM(wParam); if(GetPointerType && GetPointerType(pointerId, &pointerType)) { switch(pointerType){ case PT_PEN:{ POINTER_PEN_INFO ppi; if(GetPointerPenInfo && GetPointerPenInfo(pointerId, &ppi)) { pen = true; if(ppi.penFlags & PEN_FLAG_BARREL) pen_barrel = true; if(ppi.penFlags & PEN_FLAG_INVERTED) pen_inverted = true; if(ppi.penFlags & PEN_FLAG_ERASER) pen_eraser = true; if(ppi.penMask & PEN_MASK_PRESSURE) pen_pressure = ppi.pressure / 1024.0; if(ppi.penMask & PEN_MASK_ROTATION) pen_rotation = ppi.rotation * M_2PI / 360; if(ppi.penMask & PEN_MASK_TILT_X) pen_tilt.x = ppi.tiltX * M_2PI / 360; if(ppi.penMask & PEN_MASK_TILT_Y) pen_tilt.y = ppi.tiltY * M_2PI / 360; } break; } case PT_TOUCH:{ POINTER_TOUCH_INFO pti; if(GetPointerTouchInfo && GetPointerTouchInfo(pointerId, &pti)) { // Add something touch specific here some day maybe... } break; } default:{ POINTER_INFO pi; if(GetPointerInfo && GetPointerInfo(pointerId, &pi)) { } break; } } switch(message){ case WM_POINTERDOWN: ClickActivateWnd(); if(ignoreclick) return 0L; DoMouse(LEFTDOWN, Point(p), 0); if(_this) PostInput(); break; case WM_POINTERUP: if(ignoreclick) EndIgnore(); else DoMouse(LEFTUP, Point(p), 0); if(_this) PostInput(); break; case WM_POINTERUPDATE: if(ignoreclick) { EndIgnore(); return 0L; } if(_this) DoMouse(MOUSEMOVE, Point(p)); DoCursorShape(); break; } return 0L; } } break; case WM_POINTERLEAVE: pen = false; break; The 'if(disableOldWMs) break;' are still needed in WM_MOUSEMOVE, WM_LBUTTONUP, WM_LBUTTONDOWN. Does this work for you? Best regards, Tom
It seems to work, however I feel pretty uneasy that it is handling just LBUTTON messages and leaves the rest to "old" WMs. What about right button and double-clicks? Documentation is pretty sparse there.... If I understand you right, buttons are not a problem with Wacom, just mousemove messages. Maybe we could be more conservative, leave old messages, issue WM_MOUSEMOVE on POINTERUPDATE and solve the problem by ignoring duplicit messages (store screen hwnd and position, only issue mousemove if it changes...)? Mirek
Or, maybe, just forget about MouseMove and add a new virtual method? E.g. virtual Pen(Point pos, double pressure, double rotation, Pointf tilt, dword flags); which would simply be updated on WM_POINTERUPDATE....
BTW, what do you get with DDUMPHEX(GetMessageExtraInfo()); It looks like that 0xff515704 indicates pen for "old" WM messages, which could be useful in some context... https://stackoverflow.com/questions/29857587/detect-if-wm-mo usemove-is-caused-by-touch-pen Mirek
Hi, WM_LBUTTONDOWN comes in late with Wacom, so it needs to be caught with WM_POINTERDOWN. WM_MOUSEMOVEs are also missing in the beginning of draw, so WM_POINTERUPDATE needs to replace WM_MOUSEMOVE there. The only way (I can see) to properly handle each pointing device when more than one exist, is taking the full control of moves and left button to WM_POINTER* messages. I think that we should aim for the Pen to work in place of a mouse as logically and cleanly as possible without any changes to the final application. I'm thinking of people using pen with U++ applications without specific pen support. Their experience should not suffer. Only when pressure/tilt/rotation are beneficial for the application, then additional calls should be placed to acquire their values. If I understand it correctly, the RBUTTON and DOUBLECLICKS are not part of WM_POINTER* message family. Anyway, at least RBUTTON works through the old messages. I have not tried doubleclicking yet... I'll do it after I get back to my tablet later in the afternoon. Best regards, Tom
mirek wrote on Fri, 12 March 2021 11:18 BTW, what do you get with DDUMPHEX(GetMessageExtraInfo()); It looks like that 0xff515704 indicates pen for "old" WM messages, which could be useful in some context... https://stackoverflow.com/questions/29857587/detect-if-wm-mo usemove-is-caused-by-touch-pen Mirek I'll need to check this when I get to my tablet... You're moving fast! Just cannot keep up writing my replies... Best regards, Tom
Tom1 wrote on Fri, 12 March 2021 11:28 mirek wrote on Fri, 12 March 2021 11:18 BTW, what do you get with DDUMPHEX(GetMessageExtraInfo()); It looks like that 0xff515704 indicates pen for "old" WM messages, which could be useful in some context... https://stackoverflow.com/questions/29857587/detect-if-wm-mo usemove-is-caused-by-touch-pen Mirek I'll need to check this when I get to my tablet... You're moving fast! Just cannot keep up writing my replies... Best regards, Tom It's GetMessageExtraInfo() = 0x0 all the way for WM_POINTER*, WM_MOUSEMOVE, WM_LBUTTONUP, WM_LBUTTONDOWN... Best regards, Tom
Hi, It seems, when the pen is moving fast, Windows filters out part of the intermediate pen positions. Therefore, processing GetPointerPenInfoHistory() improves drawing quality especially at higher pen speeds: static bool disableOldWMs=false; // When true, blocks out original WM_MOUSEMOVE, WM_LBUTTONUP, WM_LBUTTONDOWN switch(message) { case WM_POINTERDOWN: case WM_POINTERUPDATE: case WM_POINTERUP: { POINT p = Point((LONG)lParam); ScreenToClient(hwnd, &p); pen = false; pen_pressure = pen_rotation = Null; pen_tilt = Null; pen_eraser = false; pen_barrel = false; pen_inverted = false; static BOOL (WINAPI *EnableMouseInPointer)(BOOL fEnable); static BOOL (WINAPI *GetPointerType)(UINT32 pointerId, POINTER_INPUT_TYPE *pointerType); static BOOL (WINAPI *GetPointerInfo)(UINT32 pointerId, POINTER_INFO *pointerInfo); static BOOL (WINAPI *GetPointerPenInfo)(UINT32 pointerId, POINTER_PEN_INFO *penInfo); static BOOL (WINAPI *GetPointerTouchInfo)(UINT32 pointerId, POINTER_TOUCH_INFO *touchInfo); static BOOL (WINAPI *GetPointerPenInfoHistory)(UINT32 pointerId, UINT32 *entriesCount, POINTER_PEN_INFO *penInfo); ONCELOCK { DllFn(EnableMouseInPointer, "User32.dll", "EnableMouseInPointer"); if(EnableMouseInPointer && EnableMouseInPointer(true)) disableOldWMs=true; // Switching over to WM_POINTER* functions for mouse DllFn(GetPointerType, "User32.dll", "GetPointerType"); DllFn(GetPointerInfo, "User32.dll", "GetPointerInfo"); DllFn(GetPointerPenInfo, "User32.dll", "GetPointerPenInfo"); DllFn(GetPointerTouchInfo, "User32.dll", "GetPointerTouchInfo"); DllFn(GetPointerPenInfoHistory, "User32.dll", "GetPointerPenInfoHistory"); }; POINTER_INPUT_TYPE pointerType; UINT32 pointerId = GET_POINTERID_WPARAM(wParam); if(GetPointerType && GetPointerType(pointerId, &pointerType)) { switch(pointerType){ case PT_PEN:{ UINT32 hc=256; POINTER_PEN_INFO ppit[hc]; if(message==WM_POINTERUPDATE && GetPointerPenInfoHistory && GetPointerPenInfoHistory(pointerId, &hc, ppit)) { for(int i=hc-1;i>=0;i--){ pen = true; if(ppit[i].penFlags & PEN_FLAG_BARREL) pen_barrel = true; if(ppit[i].penFlags & PEN_FLAG_INVERTED) pen_inverted = true; if(ppit[i].penFlags & PEN_FLAG_ERASER) pen_eraser = true; if(ppit[i].penMask & PEN_MASK_PRESSURE) pen_pressure = ppit[i].pressure / 1024.0; if(ppit[i].penMask & PEN_MASK_ROTATION) pen_rotation = ppit[i].rotation * M_2PI / 360; if(ppit[i].penMask & PEN_MASK_TILT_X) pen_tilt.x = ppit[i].tiltX * M_2PI / 360; if(ppit[i].penMask & PEN_MASK_TILT_Y) pen_tilt.y = ppit[i].tiltY * M_2PI / 360; POINT hp = ppit[i].pointerInfo.ptPixelLocation; ScreenToClient(hwnd, &hp); if(ignoreclick) { EndIgnore(); return 0L; } if(_this) DoMouse(MOUSEMOVE, Point(hp)); DoCursorShape(); } return 0L; } POINTER_PEN_INFO ppi; if(GetPointerPenInfo && GetPointerPenInfo(pointerId, &ppi)) { if(ppi.pointerInfo.historyCount){ } pen = true; if(ppi.penFlags & PEN_FLAG_BARREL) pen_barrel = true; if(ppi.penFlags & PEN_FLAG_INVERTED) pen_inverted = true; if(ppi.penFlags & PEN_FLAG_ERASER) pen_eraser = true; if(ppi.penMask & PEN_MASK_PRESSURE) pen_pressure = ppi.pressure / 1024.0; if(ppi.penMask & PEN_MASK_ROTATION) pen_rotation = ppi.rotation * M_2PI / 360; if(ppi.penMask & PEN_MASK_TILT_X) pen_tilt.x = ppi.tiltX * M_2PI / 360; if(ppi.penMask & PEN_MASK_TILT_Y) pen_tilt.y = ppi.tiltY * M_2PI / 360; } break; } case PT_TOUCH:{ POINTER_TOUCH_INFO pti; if(GetPointerTouchInfo && GetPointerTouchInfo(pointerId, &pti)) { // Add something touch specific here some day maybe... } break; } /* default:{ POINTER_INFO pi; if(GetPointerInfo && GetPointerInfo(pointerId, &pi)) { } break; } */ } switch(message){ case WM_POINTERDOWN: ClickActivateWnd(); if(ignoreclick) return 0L; DoMouse(LEFTDOWN, Point(p), 0); if(_this) PostInput(); break; case WM_POINTERUP: if(ignoreclick) EndIgnore(); else DoMouse(LEFTUP, Point(p), 0); if(_this) PostInput(); break; case WM_POINTERUPDATE: if(ignoreclick) { EndIgnore(); return 0L; } if(_this) DoMouse(MOUSEMOVE, Point(p)); DoCursorShape(); break; } return 0L; } } break; case WM_POINTERLEAVE: pen = false; break; ... Best regards, Tom
OK, now I checked LeftDouble(), RightDouble(), RightUp()... and NONE of them works with the above code. Need to think more... Best regards, Tom EDIT: I'm starting to believe that if WM_POINTER* is used for mouse, it needs processing WM_POINTERUP and WM_POINTERDOWN in a way that checks IS_POINTER_FIRSTBUTTON_WPARAM(wParam), IS_POINTER_SECONDBUTTON_WPARAM(wParam) and so on to detect WM_LBUTTONUP/DOWN and WM_RBUTTONUP/DOWN and others correctly. Seems a bit complex to me. And still DOUBLECLICKs are a mystery.
Well, that is about what I expected. The Pen virtual method seems more and more favorable solution. Can you please test DDUMPHEX(GetMessageExtraInfo()); ? It would be really helpful in new API, which would use normal mouse, but put K_PEN into flags of Mouse messages... (because we WILL need to distinguish these). Mirek
Hi, GetMessageExtraInfo() returned always zero... (Reported this earlier.) Best regards, Tom
Mirek, Can you test this: switch(message) { case WM_POINTERDOWN: case WM_POINTERUPDATE: case WM_POINTERUP: { POINT p = Point((LONG)lParam); ScreenToClient(hwnd, &p); pen = false; pen_pressure = pen_rotation = Null; pen_tilt = Null; pen_eraser = false; pen_barrel = false; pen_inverted = false; static BOOL (WINAPI *EnableMouseInPointer)(BOOL fEnable); static BOOL (WINAPI *GetPointerType)(UINT32 pointerId, POINTER_INPUT_TYPE *pointerType); static BOOL (WINAPI *GetPointerInfo)(UINT32 pointerId, POINTER_INFO *pointerInfo); static BOOL (WINAPI *GetPointerPenInfo)(UINT32 pointerId, POINTER_PEN_INFO *penInfo); static BOOL (WINAPI *GetPointerTouchInfo)(UINT32 pointerId, POINTER_TOUCH_INFO *touchInfo); static BOOL (WINAPI *GetPointerPenInfoHistory)(UINT32 pointerId, UINT32 *entriesCount, POINTER_PEN_INFO *penInfo); ONCELOCK { DllFn(GetPointerType, "User32.dll", "GetPointerType"); DllFn(GetPointerInfo, "User32.dll", "GetPointerInfo"); DllFn(GetPointerPenInfo, "User32.dll", "GetPointerPenInfo"); DllFn(GetPointerTouchInfo, "User32.dll", "GetPointerTouchInfo"); DllFn(GetPointerPenInfoHistory, "User32.dll", "GetPointerPenInfoHistory"); }; POINTER_INPUT_TYPE pointerType; UINT32 pointerId = GET_POINTERID_WPARAM(wParam); if(GetPointerType && GetPointerType(pointerId, &pointerType)) { switch(pointerType){ case PT_PEN:{ UINT32 hc=256; POINTER_PEN_INFO ppit[hc]; if(message==WM_POINTERUPDATE && GetPointerPenInfoHistory && GetPointerPenInfoHistory(pointerId, &hc, ppit)) { for(int i=hc-1;i>=0;i--){ pen = true; if(ppit[i].penFlags & PEN_FLAG_BARREL) pen_barrel = true; if(ppit[i].penFlags & PEN_FLAG_INVERTED) pen_inverted = true; if(ppit[i].penFlags & PEN_FLAG_ERASER) pen_eraser = true; if(ppit[i].penMask & PEN_MASK_PRESSURE) pen_pressure = ppit[i].pressure / 1024.0; if(ppit[i].penMask & PEN_MASK_ROTATION) pen_rotation = ppit[i].rotation * M_2PI / 360; if(ppit[i].penMask & PEN_MASK_TILT_X) pen_tilt.x = ppit[i].tiltX * M_2PI / 360; if(ppit[i].penMask & PEN_MASK_TILT_Y) pen_tilt.y = ppit[i].tiltY * M_2PI / 360; POINT hp = ppit[i].pointerInfo.ptPixelLocation; ScreenToClient(hwnd, &hp); if(ignoreclick) { EndIgnore(); return 0L; } if(_this) DoMouse(MOUSEMOVE, Point(hp)); DoCursorShape(); } return 0L; } POINTER_PEN_INFO ppi; if(GetPointerPenInfo && GetPointerPenInfo(pointerId, &ppi)) { pen = true; if(ppi.penFlags & PEN_FLAG_BARREL) pen_barrel = true; if(ppi.penFlags & PEN_FLAG_INVERTED) pen_inverted = true; if(ppi.penFlags & PEN_FLAG_ERASER) pen_eraser = true; if(ppi.penMask & PEN_MASK_PRESSURE) pen_pressure = ppi.pressure / 1024.0; if(ppi.penMask & PEN_MASK_ROTATION) pen_rotation = ppi.rotation * M_2PI / 360; if(ppi.penMask & PEN_MASK_TILT_X) pen_tilt.x = ppi.tiltX * M_2PI / 360; if(ppi.penMask & PEN_MASK_TILT_Y) pen_tilt.y = ppi.tiltY * M_2PI / 360; } break; } case PT_TOUCH:{ POINTER_TOUCH_INFO pti; if(GetPointerTouchInfo && GetPointerTouchInfo(pointerId, &pti)) { // Add something touch specific here some day maybe... } break; } } switch(message){ case WM_POINTERDOWN: ClickActivateWnd(); if(ignoreclick) return 0L; DoMouse(LEFTDOWN, Point(p), 0); if(_this) PostInput(); break; case WM_POINTERUP: if(ignoreclick) EndIgnore(); else DoMouse(LEFTUP, Point(p), 0); if(_this) PostInput(); break; case WM_POINTERUPDATE: if(ignoreclick) { EndIgnore(); return 0L; } if(_this) DoMouse(MOUSEMOVE, Point(p)); DoCursorShape(); break; } return 0L; } } break; case WM_POINTERLEAVE: pen = false; break; No blocking of original WM_MOUSEMOVE, etc. required at all. For some reason, with this code I do not get those WM_MOUSEMOVE messages for Pen, but only for mouse. Similarly, WM_POINTER... messages only appear for Pen. Right clicks and double clicks work for mouse as before. So this is closest to a working solution so far. Best regards, Tom
Hi, OK, now I found the reason why the original code produced both WM_MOUSEMOVEs and WM_POINTERUPDATEs for the pen moving: When case WM_POINTERUPDATE: exits with break; , it causes some emulation to result in WM_MOUSEMOVE. However, when WM_POINTERUPDATE exits with return 0L; , there will not be any WM_MOUSEMOVE and the two get processed separately and correctly. This must be the reason why the latest code works. Best regards, Tom
Looks good, however, should you return immediately after processing the history?
mirek wrote on Sat, 13 March 2021 15:19 Looks good, however, should you return immediately after processing the history? I think so, yes. History[0] contains the latest POINTERUPDATE, so everything gets processed this way. Best regards, Tom
OK, done some cleanup/deduplication, I think this might be final. Can you test? And now important question: Does Wacom work with Linux? Mirek
... I'm just thinking about the situation, where an application written with U++ has not been specifically designed to support pen: I think that the barrel button should be mapped to the right mouse button in U++ -- as is the default Windows pen behavior, I think. This (and maybe some other things) should give a reasonable pen experience without any special work on the application itself. The default pen behavior can be seen in: https://docs.microsoft.com/en-us/previous-versions/windows/d esktop/inputmsg/wm-pointerdown Look specifically at IS_POINTER_FIRSTBUTTON_WPARAM(wParam) and IS_POINTER_SECONDBUTTON_WPARAM(wParam)... Best regards, Tom
mirek wrote on Sat, 13 March 2021 16:07 OK, done some cleanup/deduplication, I think this might be final. Can you test? And now important question: Does Wacom work with Linux? Mirek Yes, it works just great! Nice cleanup too. You can remove this too: static BOOL (WINAPI *EnableMouseInPointer)(BOOL fEnable); Tracking pen_history is a mystery to me, though... Any reason for that? Currently my linux is on the virtual machine on top of windows... It does not see pen operations directly. I need to check if I can reconnect the wacom directly to the VM... Best regards, Tom EDIT: A quick search says it should work after installing some X wacom drivers...
Tom1 wrote on Sat, 13 March 2021 15:20 mirek wrote on Sat, 13 March 2021 16:07 OK, done some cleanup/deduplication, I think this might be final. Can you test? And now important question: Does Wacom work with Linux? Mirek Yes, it works just great! Nice cleanup too. You can remove this too: static BOOL (WINAPI *EnableMouseInPointer)(BOOL fEnable); Tracking pen_history is a mystery to me, though... Any reason for that? Well, the reason why it is in history is that your app is not fast enough to process events. So maybe the app should have a chance to know that, right ? (perhaps decide to ignore events that are from the history...) Mirek
Implemented in Linux/gtk too now. Just bare minimum (pressure, maybe tilt and rotation - could not test), without history yet (which is actually quite similar in linux)...
Hi, News: Wacom works on Linux Mint out of the box on real hardware. Right from login screen!! On LM, the barrel button generally seems to work like the right mouse button. State of U++ support for Wacom: + Pressure works ? Tilt with non-tilt-supporting hardware reports around 0.007 on both axis - barrel button is not detected at all Processing the history on linux seems even more important than with Windows. The drawing output is very coarse. Best regards, Tom
mirek wrote on Sat, 13 March 2021 19:02 Tom1 wrote on Sat, 13 March 2021 15:20 Tracking pen_history is a mystery to me, though... Any reason for that? Well, the reason why it is in history is that your app is not fast enough to process events. So maybe the app should have a chance to know that, right ? (perhaps decide to ignore events that are from the history...) Mirek OK, good point. That may prove useful in the long run... Best regards, Tom
Tom1 wrote on Sat, 13 March 2021 20:54 Hi, News: Wacom works on Linux Mint out of the box on real hardware. Right from login screen!! On LM, the barrel button generally seems to work like the right mouse button. State of U++ support for Wacom: + Pressure works ? Tilt with non-tilt-supporting hardware reports around 0.007 on both axis - barrel button is not detected at all Processing the history on linux seems even more important than with Windows. The drawing output is very coarse. Best regards, Tom I can handle the history, maybe disable unsupported tilt, no so sure about barrel...
mirek wrote on Sun, 14 March 2021 01:05 I can handle the history, maybe disable unsupported tilt, no so sure about barrel... About the barrel on Linux: Now with wacom it goes directly to right mouse button (with pen flag active though). I guess for uniform behavior the two options are now: 1. To catch in Linux RightDown() and RightUp() with IsPointerPen() and set the barrel flag accordingly. 2. Or to catch barrel flag in Windows WM_POINTERDOWN / WM_POINTERUP and call RightDown() / RightUp() accordingly on state changes with correctly set up IsPointerPen() result. I do not know, which one would be more logical and better in line with general pen experience expectations. Or should we do both? Best regards, Tom
Hi Mirek, EDIT: Removed earlier content... OK, as it turns out, so far I tested with the common testcase drawing stuff on the surface. However, after starting to adapt the pen to the actual application, I found that e.g. Button clicks and double clicks do not work with pen. I have now tried the whole day to figure out what's the problem, but it seems Windows really wants to make this difficult for me. The point is that in order to have a usable pen experience on the desktop, Windows wants to process all the WM_POINTER* messages using DefWindowProc*() and generate WM_LBUTTONDOWN,WM_LBUTTONUP,WM_MOUSEMOVE,... etc. messages. This is also a prerequisite for WM_LBUTTONDBLCLK to be generated at all. So, now I'm starting to think that it might be easier to arrange something special to extract the full pen trace while drawing with pen and have the ordinary mouse emulation handled by Windows with DefWindowProc*(). I have nothing that works yet towards this target, but I will still try to figure out something... Best regards, Tom
Hi Mirek, Here's an intermediate finding. While working on pen support I accidentally found this code to fix in CtrlMouse.cpp: if(e == LEFTUP) leftmousepos = Null; if(e == RIGHTUP) rightmousepos = Null; if(e == MIDDLEUP) rightmousepos = Null; if(findarg(e, LEFTUP, RIGHTUP, MIDDLEUP) >= 0) Should be: if(e == LEFTUP) leftmousepos = Null; if(e == RIGHTUP) rightmousepos = Null; if(e == MIDDLEUP) middlemousepos = Null; if(findarg(e, LEFTUP, RIGHTUP, MIDDLEUP) >= 0) As for the pen, I'm still trying to work around Windows induced issues... Best regards, Tom
Hi Mirek, OK, now I'm nearly there with Windows. What's missing is that I cannot get RectTracker to work with Pen. I would really appreciate, if you could take a look at that. There's a new testcase with on-screen instructions below to test the central functions of pen and mouse... In order to keep everything else working with mouse and a pen emulating a mouse, I have introduced a new flag and function for enabling accurate pen support for Ctrls requiring fine drawing. The reason I had to do this is that I could not retain usable mouse emulation when processing WM_POINTER* messages for fine drawing. (As an example, the buttons would not work with WM_POINTER* generated LEFTDOWN/UPs.) Anyway, here it is. Add to "Ctrl{" in CtrlCore.h: bool pen_supported; void EnablePenSupport(bool flag=true) { pen_supported=flag; } Add to "Ctrl::Ctrl() {" in Ctrl.cpp: pen_supported = false; Changes in "WindowProc()" in Win32Proc.cpp: ... LRESULT Ctrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { GuiLock __; eventid++; // LLOG("Ctrl::WindowProc(" << message << ") in " << ::Name(this) << ", focus " << (void *)::GetFocus()); Ptr<Ctrl> _this = this; HWND hwnd = GetHWND(); auto DoMouseMove = [&](POINT p) { if(ignoreclick) EndIgnore(); else { if(_this) DoMouse(MOUSEMOVE, Point(p)); DoCursorShape(); } }; static bool left_down=false; static bool right_down=false; static Point pendownpos=Null; switch(message) { case WM_POINTERDOWN: case WM_POINTERUP: case WM_POINTERUPDATE:{ if(!pen_supported) break; // Go with mouse emulation (default processing) instead POINT p = Point((LONG)lParam); ScreenToClient(hwnd, &p); pen = false; pen_pressure = pen_rotation = Null; pen_tilt = Null; pen_eraser = false; pen_barrel = false; pen_inverted = false; pen_history = false; static BOOL (WINAPI *GetPointerType)(UINT32 pointerId, POINTER_INPUT_TYPE *pointerType); static BOOL (WINAPI *GetPointerInfo)(UINT32 pointerId, POINTER_INFO *pointerInfo); static BOOL (WINAPI *GetPointerPenInfo)(UINT32 pointerId, POINTER_PEN_INFO *penInfo); static BOOL (WINAPI *GetPointerPenInfoHistory)(UINT32 pointerId, UINT32 *entriesCount, POINTER_PEN_INFO *penInfo); ONCELOCK { DllFn(GetPointerType, "User32.dll", "GetPointerType"); DllFn(GetPointerInfo, "User32.dll", "GetPointerInfo"); DllFn(GetPointerPenInfo, "User32.dll", "GetPointerPenInfo"); DllFn(GetPointerPenInfoHistory, "User32.dll", "GetPointerPenInfoHistory"); }; if(!(GetPointerType && GetPointerInfo && GetPointerPenInfo && GetPointerPenInfoHistory)) break; POINTER_INPUT_TYPE pointerType; auto ProcessPenInfo = [&] (POINTER_PEN_INFO& ppi) { pen = true; if(ppi.penFlags & PEN_FLAG_BARREL) pen_barrel = true; if(ppi.penFlags & PEN_FLAG_INVERTED) pen_inverted = true; if(ppi.penFlags & PEN_FLAG_ERASER) pen_eraser = true; if(ppi.penMask & PEN_MASK_PRESSURE) pen_pressure = ppi.pressure / 1024.0; if(ppi.penMask & PEN_MASK_ROTATION) pen_rotation = ppi.rotation * M_2PI / 360; if(ppi.penMask & PEN_MASK_TILT_X) pen_tilt.x = ppi.tiltX * M_2PI / 360; if(ppi.penMask & PEN_MASK_TILT_Y) pen_tilt.y = ppi.tiltY * M_2PI / 360; }; UINT32 pointerId = GET_POINTERID_WPARAM(wParam); if(GetPointerType(pointerId, &pointerType) && pointerType == PT_PEN) { UINT32 hc = 256; Buffer<POINTER_PEN_INFO> ppit(hc); if(message == WM_POINTERUPDATE && (right_down || left_down) && GetPointerPenInfoHistory(pointerId, &hc, ppit)) { for(int i = hc - 1; i >= 0; i--) { ProcessPenInfo(ppit[i]); POINT hp = ppit[i].pointerInfo.ptPixelLocation; ScreenToClient(hwnd, &hp); pen_history = (bool)i; DoMouseMove(hp); } pen_history = false; } else{ POINTER_PEN_INFO ppi; if(GetPointerPenInfo(pointerId, &ppi)){ ProcessPenInfo(ppi); switch(message) { case WM_POINTERDOWN: ClickActivateWnd(); if(ignoreclick) return 0L; right_down=pen_barrel; left_down=!right_down; if(right_down) DoMouse(RIGHTDOWN, pendownpos=Point(p)); else DoMouse(LEFTDOWN, pendownpos=Point(p), 0); if(_this) PostInput(); break; } } } } } break; case WM_POINTERLEAVE: pen = false; right_down=false; left_down=false; break; ... case WM_LBUTTONDOWN: ClickActivateWnd(); if(ignoreclick) return 0L; if(pendownpos==Point((dword)lParam)) { pendownpos=Null; return 0L; } else{ DoMouse(LEFTDOWN, Point((dword)lParam)); if(_this) PostInput(); } return 0L; case WM_LBUTTONUP: if(ignoreclick) EndIgnore(); else{ if(right_down) DoMouse(RIGHTUP, Point((dword)lParam)); else DoMouse(LEFTUP, Point((dword)lParam)); } if(_this) PostInput(); right_down=false; left_down=false; pendownpos=Null; return 0L; case WM_LBUTTONDBLCLK: ClickActivateWnd(); if(ignoreclick) return 0L; DoMouse(LEFTDOUBLE, Point((dword)lParam)); if(_this) PostInput(); return 0L; case WM_RBUTTONDOWN: if(pendownpos==Point((dword)lParam)) { pendownpos=Null; return 0L; } ClickActivateWnd(); if(ignoreclick) return 0L; DoMouse(RIGHTDOWN, Point((dword)lParam)); if(_this) PostInput(); return 0L; case WM_RBUTTONUP: if(ignoreclick) EndIgnore(); else DoMouse(RIGHTUP, Point((dword)lParam)); if(_this) PostInput(); right_down=false; left_down=false; pendownpos=Null; return 0L; ... case WM_MOUSEMOVE: if(left_down || right_down) return 0L; LLOG("WM_MOUSEMOVE: ignoreclick = " << ignoreclick); DoMouseMove(Point((dword)lParam)); return 0L; ... And finally the extended testcase: #include <CtrlLib/CtrlLib.h> using namespace Upp; #define LLOG(x) DLOG(x) struct MyApp : TopWindow { Point pos; Point lDouble; Point rDouble; Point lHold; Point rHold; bool lDoublePen; bool rDoublePen; bool lHoldPen; bool rHoldPen; Vector<Vector<Tuple<double, Pointf>>> drawing; Vector<Vector<Tuple<double, Pointf>>> rdrawing; bool rdown; bool ldown; Point p1,p2; // Tracker test variables Rect trect; MyApp(){ rdown=false; ldown=false; lDouble=Null; rDouble=Null; lHold=Null; rHold=Null; lDoublePen=false; rDoublePen=false; lHoldPen=false; rHoldPen=false; trect=Null; p1=p2=Null; EnablePenSupport(); } virtual void RightHold(Point p, dword){ LLOG((IsPointerPen()?"PenRightHold":"MouseRightHold")); rHold=p; rHoldPen=IsPointerPen(); Refresh(); } virtual void RightDouble(Point p, dword){ LLOG((IsPointerPen()?"PenRightDouble":"MouseRightDouble")); rDouble=p; rDoublePen=IsPointerPen(); Refresh(); } virtual void RightDown(Point p, dword){ LLOG((IsPointerPen()?"PenRightDown":"MouseRightDown")); rdown=true; rdrawing.Add().Add(MakeTuple(IsPointerPen()?GetPenPressure():0.1, p)); Refresh(); } virtual void RightUp(Point p, dword){ LLOG((IsPointerPen()?"PenRightUp":"MouseRightUp")); rdown=false; Refresh(); } virtual void LeftHold(Point p, dword){ LLOG((IsPointerPen()?"PenLeftHold":"MouseLeftHold")); lHold=p; lHoldPen=IsPointerPen(); Refresh(); } virtual void LeftDouble(Point p, dword){ LLOG((IsPointerPen()?"PenLeftDouble":"MouseLeftDouble")); lDouble=p; lDoublePen=IsPointerPen(); Refresh(); } virtual void LeftDown(Point p, dword keyflags){ if(keyflags&K_SHIFT){ RectTracker tracker(*this); tracker.sync= [=](Rect r) { }; tracker.MinSize(Size(-100000,-100000)); trect=tracker.Track(Rect(p,p)); } else if(keyflags&K_CTRL){ RectTracker tracker(*this); p1=p; p2=tracker.TrackLine(p.x,p.y); } else{ LLOG((IsPointerPen()?"PenLeftDown":"MouseLeftDown")); ldown=true; drawing.Add().Add(MakeTuple(IsPointerPen()?GetPenPressure():0.1, p)); } Refresh(); } virtual void LeftUp(Point p, dword){ LLOG((IsPointerPen()?"PenLeftUp":"MouseLeftUp")); ldown=false; Refresh(); } Vector<String> report; virtual void MouseMove(Point p, dword keyflags) { pos = p; LLOG((IsPointerPen()?"PenMove":"MouseMove")); if((ldown && drawing.GetCount()) || (rdown && rdrawing.GetCount())) { if(rdown){ if(!IsPenHistoryEvent()) rdrawing.Top().Add(MakeTuple(IsPointerPen()?GetPenPressure():0.1, p)); } else if(ldown) drawing.Top().Add(MakeTuple(IsPointerPen()?GetPenPressure():0.1, p)); } report.Clear(); report.Add() << AsString(pos); report.Add() << "Pen: " << IsPointerPen(); report.Add() << "Pressure: " << GetPenPressure(); report.Add() << "Rotation: " << GetPenRotation(); report.Add() << "Tilt: " << GetPenTilt(); report.Add() << "Barrel: " << IsPenBarrelPressed(); report.Add() << "Inverted: " << IsPenInverted(); report.Add() << "Eraser: " << IsPenEraserPressed(); Refresh(); } virtual void Paint(Draw& w0){ DrawPainter w(w0, GetSize()); w.Clear(SColorPaper()); w.LineCap(LINECAP_ROUND); for(const auto& stroke : drawing) if(stroke.GetCount()) for(int i = 0; i < stroke.GetCount() - 1; i++) { w.Move(stroke[i].b); w.Line(stroke[i + 1].b); w.Stroke(DPI(20) * stroke[i].a, LtBlue()); } for(const auto& stroke : rdrawing) if(stroke.GetCount()) for(int i = 0; i < stroke.GetCount() - 1; i++) { w.Move(stroke[i].b); w.Line(stroke[i + 1].b); w.Stroke(DPI(20) * stroke[i].a, Black()); } if(!IsNull(trect)) w.RectPath(trect).Stroke(2.0,Red()); if(!IsNull(p2)) w.Move(p1).Line(p2).Stroke(2.0,Green()); int fcy = GetStdFontCy(); int y = 10; auto Text = [&] (const String& text) { w.DrawText(10, y, text); y += fcy; }; Text("1. Test Clicks, Holds, Drags and DoubleClicks using Mouse with both Left and Right buttons."); Text("2. Test Clicks, Drags and DoubleClicks using Pen without and with Barrel button."); Text("3. Test RectTracker with Ctrl+LeftDrag and Shift+LeftDrag using Mouse and then Pen."); for(int i=0;i<report.GetCount();i++) Text(report[i]); if(!IsNull(lHold)) w.DrawText(lHold.x, lHold.y, "LeftHold", StdFont(), lHoldPen?LtBlue():Black()); if(!IsNull(rHold)) w.DrawText(rHold.x, rHold.y, "RightHold", StdFont(), rHoldPen?LtBlue():Black()); if(!IsNull(lDouble)) w.DrawText(lDouble.x, lDouble.y, "LeftDouble", StdFont(), lDoublePen?LtBlue():Black()); if(!IsNull(rDouble)) w.DrawText(rDouble.x, rDouble.y, "RightDouble", StdFont(), rDoublePen?LtBlue():Black()); } }; GUI_APP_MAIN { PromptOK("Please tap OK with Pen to verify button operation!"); MyApp().Run(); } Best regards, Tom
Hi Mirek, Here are some further findings for the code I posted above. If intermediate MouseMove's are ignored, the following U++ callbacks result for each mouse operation: CLICK: LeftDown LeftUp DOUBLECLICK: LeftDown LeftUp LeftDouble LeftUp TRIPLECLICK: LeftDown LeftUp LeftDouble LeftUp LeftTriple LeftUp DRAG: LeftDown LeftDrag LeftUp HOLD: LeftDown LeftHold LeftUp HOLD+DRAG: LeftDown LeftHold LeftDrag LeftUp For Pen we currently get: CLICK: LeftDown LeftUp DOUBLECLICK: LeftDown LeftUp LeftDown ** Extra LeftDouble LeftUp TRIPLECLICK: LeftDown LeftUp LeftDown ** Extra LeftDouble LeftUp LeftTriple LeftUp DRAG: LeftDown LeftDrag LeftUp HOLD: LeftDown !! Missing: LeftHold LeftRightUp ** !!! (Hold missing and RightUp emitted instead of LeftUp call!) HOLD+DRAG: LeftDown !! Missing: LeftHold LeftDrag LeftUp The weird Hold behavior is likely caused by Windows Ink internals. Anyway, in my opinion we should try to get exact the same callbacks from pen as we get from the mouse. Best regards, Tom
Hi, Updated the testing code for completeness: #include <CtrlLib/CtrlLib.h> using namespace Upp; #define LLOG(x) DLOG(x) struct MyApp : TopWindow { Point pos; Point lTriple; Point rTriple; Point lDouble; Point rDouble; Point lHold; Point rHold; bool lTriplePen; bool rTriplePen; bool lDoublePen; bool rDoublePen; bool lHoldPen; bool rHoldPen; Vector<Vector<Tuple<double, Pointf>>> drawing; Vector<Vector<Tuple<double, Pointf>>> rdrawing; bool rdown; bool ldown; Point p1,p2; // Tracker test variables Rect trect; MyApp(){ rdown=false; ldown=false; lTriple=Null; rTriple=Null; lDouble=Null; rDouble=Null; lHold=Null; rHold=Null; lTriplePen=false; rTriplePen=false; lDoublePen=false; rDoublePen=false; lHoldPen=false; rHoldPen=false; trect=Null; p1=p2=Null; EnablePenSupport(); } virtual void RightDown(Point p, dword){ LLOG((IsPointerPen()?"PenRightDown":"MouseRightDown")); rdown=true; rdrawing.Add().Add(MakeTuple(IsPointerPen()?GetPenPressure():0.1, p)); Refresh(); } virtual void RightHold(Point p, dword){ LLOG((IsPointerPen()?"PenRightHold":"MouseRightHold")); rHold=p; rHoldPen=IsPointerPen(); Refresh(); } virtual void RightDrag(Point p, dword){ LLOG((IsPointerPen()?"PenRightDrag":"MouseRightDrag")); } virtual void RightDouble(Point p, dword){ LLOG((IsPointerPen()?"PenRightDouble":"MouseRightDouble")); rDouble=p; rDoublePen=IsPointerPen(); Refresh(); } virtual void RightTriple(Point p, dword){ LLOG((IsPointerPen()?"PenRightTriple":"MouseRightTriple")); rTriple=p; rTriplePen=IsPointerPen(); Refresh(); } virtual void RightUp(Point p, dword){ LLOG((IsPointerPen()?"PenRightUp":"MouseRightUp")); rdown=false; Refresh(); } virtual void LeftDown(Point p, dword keyflags){ if(keyflags&K_SHIFT){ RectTracker tracker(*this); tracker.sync= [=](Rect r) { }; tracker.MinSize(Size(-100000,-100000)); trect=tracker.Track(Rect(p,p)); } else if(keyflags&K_CTRL){ RectTracker tracker(*this); p1=p; p2=tracker.TrackLine(p.x,p.y); } else{ LLOG((IsPointerPen()?"PenLeftDown":"MouseLeftDown")); ldown=true; drawing.Add().Add(MakeTuple(IsPointerPen()?GetPenPressure():0.1, p)); } Refresh(); } virtual void LeftHold(Point p, dword){ LLOG((IsPointerPen()?"PenLeftHold":"MouseLeftHold")); lHold=p; lHoldPen=IsPointerPen(); Refresh(); } virtual void LeftDrag(Point p, dword){ LLOG((IsPointerPen()?"PenLeftDrag":"MouseLeftDrag")); } virtual void LeftDouble(Point p, dword){ LLOG((IsPointerPen()?"PenLeftDouble":"MouseLeftDouble")); lDouble=p; lDoublePen=IsPointerPen(); Refresh(); } virtual void LeftTriple(Point p, dword){ LLOG((IsPointerPen()?"PenLeftTriple":"MouseLeftTriple")); lTriple=p; lTriplePen=IsPointerPen(); Refresh(); } virtual void LeftUp(Point p, dword){ LLOG((IsPointerPen()?"PenLeftUp":"MouseLeftUp")); ldown=false; Refresh(); } Vector<String> report; virtual void MouseMove(Point p, dword keyflags) { pos = p; LLOG((IsPointerPen()?"PenMove":"MouseMove")); if((ldown && drawing.GetCount()) || (rdown && rdrawing.GetCount())) { if(rdown){ if(!IsPenHistoryEvent()) rdrawing.Top().Add(MakeTuple(IsPointerPen()?GetPenPressure():0.1, p)); } else if(ldown) drawing.Top().Add(MakeTuple(IsPointerPen()?GetPenPressure():0.1, p)); } report.Clear(); report.Add() << AsString(pos); report.Add() << "Pen: " << IsPointerPen(); report.Add() << "Pressure: " << GetPenPressure(); report.Add() << "Rotation: " << GetPenRotation(); report.Add() << "Tilt: " << GetPenTilt(); report.Add() << "Barrel: " << IsPenBarrelPressed(); report.Add() << "Inverted: " << IsPenInverted(); report.Add() << "Eraser: " << IsPenEraserPressed(); Refresh(); } virtual void Paint(Draw& w0){ DrawPainter w(w0, GetSize()); w.Clear(SColorPaper()); w.LineCap(LINECAP_ROUND); for(const auto& stroke : drawing) if(stroke.GetCount()) for(int i = 0; i < stroke.GetCount() - 1; i++) { w.Move(stroke[i].b); w.Line(stroke[i + 1].b); w.Stroke(DPI(20) * stroke[i].a, LtBlue()); } for(const auto& stroke : rdrawing) if(stroke.GetCount()) for(int i = 0; i < stroke.GetCount() - 1; i++) { w.Move(stroke[i].b); w.Line(stroke[i + 1].b); w.Stroke(DPI(20) * stroke[i].a, Black()); } if(!IsNull(trect)) w.RectPath(trect).Stroke(2.0,Red()); if(!IsNull(p2)) w.Move(p1).Line(p2).Stroke(2.0,Green()); int fcy = GetStdFontCy(); int y = 10; auto Text = [&] (const String& text) { w.DrawText(10, y, text); y += fcy; }; Text("1. Test Clicks, Holds, Drags, DoubleClicks and TripleClicks using Mouse with both Left and Right buttons."); Text("2. Test Clicks, Holds, Drags, DoubleClicks and TripleClicks using Pen without and with Barrel button."); Text("3. Test RectTracker with Ctrl+LeftDrag and Shift+LeftDrag using Mouse and then Pen."); for(int i=0;i<report.GetCount();i++) Text(report[i]); if(!IsNull(lHold)) w.DrawText(lHold.x, lHold.y, "LeftHold", StdFont(), lHoldPen?LtBlue():Black()); if(!IsNull(rHold)) w.DrawText(rHold.x, rHold.y, "RightHold", StdFont(), rHoldPen?LtBlue():Black()); if(!IsNull(lDouble)) w.DrawText(lDouble.x, lDouble.y, "LeftDouble", StdFont(), lDoublePen?LtBlue():Black()); if(!IsNull(rDouble)) w.DrawText(rDouble.x, rDouble.y, "RightDouble", StdFont(), rDoublePen?LtBlue():Black()); if(!IsNull(lTriple)) w.DrawText(lTriple.x, lTriple.y, "LeftTriple", StdFont(), lTriplePen?LtBlue():Black()); if(!IsNull(rTriple)) w.DrawText(rTriple.x, rTriple.y, "RightTriple", StdFont(), rTriplePen?LtBlue():Black()); } }; GUI_APP_MAIN { PromptOK("Please tap OK with Pen to verify button operation!"); MyApp().Run(); } Best regards, Tom
It is getting complicated to carefuly apply all changes to WindowProc; could you please send the whole file?
Hi, Sure, please find attached Win32Proc.cpp. I wish I could have achieved a better solution. I.e. one with: - correct callback sequences (same as mouse) - buttons (and what not) working without flag steered bypass - working RectTracker Anyway, Windows beats me on this one. Best regards, Tom EDIT: Updated the Win32Proc.cpp. (Managed to improve callback sequences a little bit. The rest is as is...)
Hi, Here's an improved testcase code for easier callback monitoring: #include <CtrlLib/CtrlLib.h> using namespace Upp; #define LLOG(x) DLOG(x) bool moved=false; class PointerEvent{ public: Point p; int row; String label; PointerEvent(Point p_, String label_, int row_){ if(moved){ moved=false; LLOG("..."); } p=p_; label=label_; row=row_; LLOG(label << ": " << p); } }; struct MyApp : TopWindow { Array<PointerEvent> elist; Vector<Vector<Tuple<double, Pointf>>> drawing; Vector<Vector<Tuple<double, Pointf>>> rdrawing; bool rdown; bool ldown; Point p1,p2; // Tracker test variables Rect trect; MyApp(){ rdown=false; ldown=false; moved=false; trect=Null; p1=p2=Null; EnablePenSupport(); } virtual void RightDown(Point p, dword){ elist.Insert(0,PointerEvent(p,IsPointerPen()?"PenRightDown":"MouseRightDown",0)); rdown=true; rdrawing.Add().Add(MakeTuple(IsPointerPen()?GetPenPressure():0.1, p)); Refresh(); } virtual void RightHold(Point p, dword){ elist.Insert(0,PointerEvent(p,IsPointerPen()?"PenRightHold":"MouseRightHold",1)); Refresh(); } virtual void RightDrag(Point p, dword){ elist.Insert(0,PointerEvent(p,IsPointerPen()?"PenRightDrag":"MouseRightDrag",-1)); Refresh(); } virtual void RightDouble(Point p, dword){ elist.Insert(0,PointerEvent(p,IsPointerPen()?"PenRightDouble":"MouseRightDouble",-2)); Refresh(); } virtual void RightTriple(Point p, dword){ elist.Insert(0,PointerEvent(p,IsPointerPen()?"PenRightTriple":"MouseRightTriple",-3)); Refresh(); } virtual void RightUp(Point p, dword){ elist.Insert(0,PointerEvent(p,IsPointerPen()?"PenRightUp":"MouseRightUp",-1)); rdown=false; Refresh(); } virtual void LeftDown(Point p, dword keyflags){ if(keyflags&K_SHIFT){ RectTracker tracker(*this); tracker.sync= [=](Rect r) { }; tracker.MinSize(Size(-100000,-100000)); trect=tracker.Track(Rect(p,p)); } else if(keyflags&K_CTRL){ RectTracker tracker(*this); p1=p; p2=tracker.TrackLine(p.x,p.y); } else{ elist.Insert(0,PointerEvent(p,IsPointerPen()?"PenLeftDown":"MouseLeftDown",0)); ldown=true; drawing.Add().Add(MakeTuple(IsPointerPen()?GetPenPressure():0.1, p)); } Refresh(); } virtual void LeftHold(Point p, dword){ elist.Insert(0,PointerEvent(p,IsPointerPen()?"PenLeftHold":"MouseLeftHold",1)); Refresh(); } virtual void LeftDrag(Point p, dword){ elist.Insert(0,PointerEvent(p,IsPointerPen()?"PenLeftDrag":"MouseLeftDrag",-1)); Refresh(); } virtual void LeftDouble(Point p, dword){ elist.Insert(0,PointerEvent(p,IsPointerPen()?"PenLeftDouble":"MouseLeftDouble",-2)); Refresh(); } virtual void LeftTriple(Point p, dword){ elist.Insert(0,PointerEvent(p,IsPointerPen()?"PenLeftTriple":"MouseLeftTriple",-3)); Refresh(); } virtual void LeftUp(Point p, dword){ elist.Insert(0,PointerEvent(p,IsPointerPen()?"PenLeftUp":"MouseLeftUp",-1)); ldown=false; Refresh(); } Vector<String> report; virtual void MouseMove(Point p, dword keyflags) { moved = true; if((ldown && drawing.GetCount()) || (rdown && rdrawing.GetCount())) { if(rdown){ if(!IsPenHistoryEvent()) rdrawing.Top().Add(MakeTuple(IsPointerPen()?GetPenPressure():0.1, p)); } else if(ldown) drawing.Top().Add(MakeTuple(IsPointerPen()?GetPenPressure():0.1, p)); } report.Clear(); report.Add() << AsString(p); report.Add() << "Pen: " << IsPointerPen(); report.Add() << "Pressure: " << GetPenPressure(); report.Add() << "Rotation: " << GetPenRotation(); report.Add() << "Tilt: " << GetPenTilt(); report.Add() << "Barrel: " << IsPenBarrelPressed(); report.Add() << "Inverted: " << IsPenInverted(); report.Add() << "Eraser: " << IsPenEraserPressed(); Refresh(); } virtual void Paint(Draw& w0){ DrawPainter w(w0, GetSize()); w.Clear(SColorPaper()); w.LineCap(LINECAP_ROUND); for(const auto& stroke : drawing) if(stroke.GetCount()) for(int i = 0; i < stroke.GetCount() - 1; i++) { w.Move(stroke[i].b); w.Line(stroke[i + 1].b); w.Stroke(DPI(20) * stroke[i].a, LtBlue()); } for(const auto& stroke : rdrawing) if(stroke.GetCount()) for(int i = 0; i < stroke.GetCount() - 1; i++) { w.Move(stroke[i].b); w.Line(stroke[i + 1].b); w.Stroke(DPI(20) * stroke[i].a, Black()); } if(!IsNull(trect)) w.RectPath(trect).Stroke(2.0,Red()); if(!IsNull(p2)) w.Move(p1).Line(p2).Stroke(2.0,Green()); int fcy = GetStdFontCy(); int y = 10; auto Text = [&] (const String& text) { w.DrawText(10, y, text); y += fcy; }; Text("1. Test Clicks, Holds, Drags, DoubleClicks and TripleClicks using Mouse with both Left and Right buttons."); Text("2. Test Clicks, Holds, Drags, DoubleClicks and TripleClicks using Pen without and with Barrel button."); Text("3. Test RectTracker with Ctrl+LeftDrag and Shift+LeftDrag using Mouse and then Pen."); for(int i=0;i<report.GetCount();i++) Text(report[i]); elist.Trim(min(15,elist.GetCount())); for(int i=0;i<elist.GetCount();i++) w.DrawText(elist[i].p.x, elist[i].p.y + elist[i].row*GetStdFontCy(), elist[i].label); } }; GUI_APP_MAIN { PromptOK("Please tap OK with Pen to verify button operation!"); MyApp().Run(); } Best regards, Tom
Hi Mirek, It seems GetMousePos() is not working unless WM_MOUSEMOVE can call DoMouseMove(). It does not help that DoMouseMove() has already been called for WM_POINTERUPDATE at the same coordinates just previously. Many things are based on GetMousePos() so it has to work. However, it is based on ::GetCursorPos() (on WIN32 API). How to fix this? Best regards, Tom
Hi, Here's the current code that now fixes RectTracker and GetMousePos() with Pen: Add to "Ctrl{" in CtrlCore.h: bool pen_supported; void EnablePenSupport(bool flag=true) { pen_supported=flag; } Add to "Ctrl::Ctrl() {" in Ctrl.cpp: pen_supported = false; Changes around Point GetMousePos() in Win32Wnd.cpp: static Point screen_mouse_pos=Null; void SetMousePos(const Point &p) { screen_mouse_pos=p; } Point GetMousePos() { return screen_mouse_pos; // Point p; // return ::GetCursorPos(p) ? p : Null; // ::GetCursorPos(p); // return p; } The rest of the changes are in the attached Win32Proc.cpp. The last issue (I know of) requiring switchable 'EnablePenSupport()' is the malfunction of buttons. Best regards, Tom
Well, I do not quite like this, it becomes too clumsy. I am starting to think that we rather need a new virtual method for pen (as result of POINTERUPDATE) that would return true if it would want to avoid further processing of event.
Hi, Actually, I went that way too, but had to return back to this. The reason is that we need to be able to use all standard Ctrls with Pen equally to Mouse. Otherwise, it becomes tedious to build pen interfaces for each and every standard Ctrl. Or, do you have something very smart in mind already? (Many times you have, so I just have to ask... Best regards, Tom
Tom1 wrote on Wed, 24 March 2021 11:57 Hi, Actually, I went that way too, but had to return back to this. The reason is that we need to be able to use all standard Ctrls with Pen equally to Mouse. Otherwise, it becomes tedious to build pen interfaces for each and every standard Ctrl. Or, do you have something very smart in mind already? (Many times you have, so I just have to ask... Best regards, Tom You missed 'bool' return parameter... All standard Ctrls would return false, meaning it should be convert to good old WM messages by windows.
OK, I see. I'll give it a try... (will return soon.) Best regards, Tom
Tom1 wrote on Wed, 24 March 2021 15:21 OK, I see. I'll give it a try... (will return soon.) Best regards, Tom Well, I thought I will do that tonight or tomorrow, but if you want to try, there are some tips and tricks how I planned to do it: - add another kind of event PEN 0xf0 after TRIPLE constant - will need to add handling in DoMouse around CtrlCore/CtrlMouse.cpp:149 - we will need to somehow return that bool; DoMouse returns Image, here I planned to use a trick to return Null or Non-null image to signal true-falase (use any image available) - as for Pen signature, I was thinking about creating some struct type with all the information (pressure, tilt etc) that would be passed as parameter. Perhaps leave Point as separate parameter. So something like virtual bool Pen(Point p, const PenInfo& f); Mirek
Mirek, I was only thinking about declaring: virtual bool PenDown(Point p, dword keyflags){ return false; } virtual bool PenUp(Point p, dword keyflags){ return false; } virtual bool PenMove(Point p, dword keyflags){ return false; } and calling them directly like this switch(message) { case WM_POINTERDOWN: ClickActivateWnd(); if(PenDown(Point(p), GetMouseFlags())) return 0L; break; case WM_POINTERUP: if(PenUp(Point(p), GetMouseFlags())) return 0L; break; case WM_POINTERUPDATE: if(PenMove(Point(p), GetMouseFlags())) return 0L; break; } break; ...and using the Pen -functions you made earlier. However, my simple approach above would inevitably have its shortcomings as properly tracking the pen/mouse position (e.g. GetMousePos()) cannot be taken care of this way. RectTracker would likely also be broken in this case. Despite working several tens of hours on this, my understanding on Ctrl and mouse processing is not sufficient yet to do it the right way. (Despite the several tens of hours I have spent on this during the few last days. Shame on me.) So, absolutely, if you have time, please do it the right way. (I have a strong feeling that it will eventually save time for both of us.) In any case, I'll be here ready to test as soon as you post an update. Best regards, Tom EDIT: BTW: How did you think about handling barrel-button and other flags? After playing around with the pen, I have noticed that it is very difficult to click barrel and other buttons on the pen while keeping it steady. So, IMO, barrel should be just a flag to read when pen goes down/up or moves. Just like Shift/Ctrl/Alt, which are used and needed as modifiers similarly for pen and mouse. Also, I feel PenMove + PenDown + PenUp make it easier to write code for the pen, instead of a single PenUpdate function. Further on, readily supporting PenHold, PenDouble (and possibly PenTriple) would be useful. Decoding them in application would require passing timestamps too, which is not very convenient. EDIT2: Actually, I would rather have PEN defined as 3 or 4, i.e. in parallel to LEFT, RIGHT and MIDDLE. This way we can have PEN|DOWN, PEN|UP, PEN|DOUBLE, ... and just PEN for move.
Tom1 wrote on Wed, 24 March 2021 16:51 Mirek, I was only thinking about declaring: virtual bool PenDown(Point p, dword keyflags){ return false; } virtual bool PenUp(Point p, dword keyflags){ return false; } virtual bool PenMove(Point p, dword keyflags){ return false; } and calling them directly like this switch(message) { case WM_POINTERDOWN: ClickActivateWnd(); if(PenDown(Point(p), GetMouseFlags())) return 0L; break; case WM_POINTERUP: if(PenUp(Point(p), GetMouseFlags())) return 0L; break; case WM_POINTERUPDATE: if(PenMove(Point(p), GetMouseFlags())) return 0L; break; } break; ...and using the Pen -functions you made earlier. This would not work - only top level windows are receiving messages from Win32. DoMouse then distributes them to target widgets (with a ton of logic and coordinate translations involved). Quote: EDIT: BTW: How did you think about handling barrel-button and other flags? After playing around with the pen, I have noticed that it is very difficult to click barrel and other buttons on the pen while keeping it steady. So, IMO, barrel should be just a flag to read when pen goes down/up or moves. Just like Shift/Ctrl/Alt, which are used and needed as modifiers similarly for pen and mouse. As flag in PenInfo. That is the reason I vote for structure passed so that it can be extended. Quote: Also, I feel PenMove + PenDown + PenUp make it easier to write code for the pen, instead of a single PenUpdate function. IDK. I feel like pen actually would be better handled with single method. I guess I have to try. Mirek
OK. PenInfo structure sounds good. Please include 'dword keyflags' in call parameters or PenInfo. Also, if you end up with a single method like 'PenUpdate()' or whatever, please also include reason = MOVE/DOWN/UP/HOLD/DOUBLE/... in call parameters or PenInfo. Thanks and best regards, Tom
First attempt commited. #include <CtrlLib/CtrlLib.h> using namespace Upp; struct MyApp : TopWindow { Point pos; Vector<Vector<Tuple<double, Pointf>>> drawing; PenInfo pen; virtual bool Pen(Point p, const PenInfo& pn, dword keyflags) { if(pn.pressure) { if((!!pn.pressure == !!pen.pressure) && drawing.GetCount()) drawing.Top().Add(MakeTuple(pn.pressure, p)); else drawing.Add().Add(MakeTuple(pn.pressure, p)); } pen = pn; Refresh(); return true; } virtual void Paint(Draw& w0) { DrawPainter w(w0, GetSize()); w.Co(); w.Clear(SColorPaper()); w.LineCap(LINECAP_ROUND); for(const auto& stroke : drawing) if(stroke.GetCount()) for(int i = 0; i < stroke.GetCount() - 1; i++) { w.Move(stroke[i].b); w.Line(stroke[i + 1].b); w.Stroke(DPI(20) * stroke[i].a, Black()); } int fcy = GetStdFontCy(); int y = 10; auto Text = [&] (const String& text) { w.DrawText(10, y, text); y += fcy; }; Text(AsString(pos)); Text(String() << "Pressure: " << pen.pressure); Text(String() << "Rotation: " << pen.rotation); Text(String() << "Tilt: " << pen.tilt); Text(String() << "Barrel: " << pen.barrel); Text(String() << "Inverted: " << pen.inverted); Text(String() << "Eraser: " << pen.eraser); } }; GUI_APP_MAIN { MyApp().Run(); }
Hi Mirek, And thanks for the first round on the new Pen interface. The standard controls seem to work correctly (as expected) and the new interface is nice and clean. However, the RectTracker needs attention. Please add the following code to the test case and see what happens with Ctrl+Pen and Shift+Pen: virtual bool Pen(Point p, const PenInfo& pn, dword keyflags) { if(pn.action==PEN_DOWN){ if(keyflags&K_SHIFT){ RectTracker tracker(*this); tracker.sync= [=](Rect r) { }; tracker.MinSize(Size(-100000,-100000)); tracker.Track(Rect(p,p)); } else if(keyflags&K_CTRL){ RectTracker tracker(*this); tracker.TrackLine(p.x,p.y); } } if(pn.pressure) { if((!!pn.pressure == !!pen.pressure) && drawing.GetCount()) drawing.Top().Add(MakeTuple(pn.pressure, p)); else drawing.Add().Add(MakeTuple(pn.pressure, p)); } pen = pn; Refresh(); return true; } Thanks and best regards, Tom
Well, I will try to make it work, however the indended road was something like virtual bool Pen(Point p, const PenInfo& pn, dword keyflags) { if(keyflags&K_SHIFT) return false; if(pn.pressure) { if((!!pn.pressure == !!pen.pressure) && drawing.GetCount()) drawing.Top().Add(MakeTuple(pn.pressure, p)); else drawing.Add().Add(MakeTuple(pn.pressure, p)); } pen = pn; Refresh(); return true; } void LeftDown(Point p, dword keyflags) override { if(keyflags & K_SHIFT) { RectTracker tracker(*this); tracker.MinSize(Size(-100000,-100000)); tracker.Track(Rect(p,p)); } }
Mirek, Is pn.action intentionally merely a state flag showing continuously PEN_UP or PEN_DOWN? Or should we reset it along with other PenInfo items in the beginning of processing WM_POINTER* messages? I would like to have it reset, in order to more easily detect UP/DOWN state changes without a state variable and treat zero action as 'PEN_MOVE'. Then we could do this to decode all kinds of pen events: #define PENMOVE 0 #define PENDOWN 1 #define PENUP 2 #define PENHOLD 3 #define PENDRAG 4 #define PENDOUBLE 5 #define PENTRIPLE 6 bool IsNear(Point p, Point o){ Point delta(p-o); if(abs(delta.x)<5 && abs(delta.y)<5) return true; return false; } int PenDecoder(Point p, const PenInfo& pn, dword keyflags){ static int action=0; static int downt=0; static Point downp=Null; static bool doubleClick=true; static bool down=false; static bool solved=false; int now=msecs(); int deltat=now-downt; bool isNear=IsNear(p,downp); switch(pn.action){ case PEN_DOWN: down=true; solved=false; downp=p; downt=now; if(isNear){ if(now-downt < 500){ if(doubleClick){ return PENTRIPLE; } else{ doubleClick=true; return PENDOUBLE; } } } doubleClick=false; return PENDOWN; case PEN_UP: down=false; return PENUP; default: if(down && !solved){ if(isNear){ if(now-downt > 1000){ solved=true; return PENHOLD; } } else{ solved=true; return PENDRAG; } } return PENMOVE; } } Best regards, Tom
mirek wrote on Thu, 25 March 2021 23:07 Well, I will try to make it work, however the indended road was something like virtual bool Pen(Point p, const PenInfo& pn, dword keyflags) { if(keyflags&K_SHIFT) return false; if(pn.pressure) { if((!!pn.pressure == !!pen.pressure) && drawing.GetCount()) drawing.Top().Add(MakeTuple(pn.pressure, p)); else drawing.Add().Add(MakeTuple(pn.pressure, p)); } pen = pn; Refresh(); return true; } void LeftDown(Point p, dword keyflags) override { if(keyflags & K_SHIFT) { RectTracker tracker(*this); tracker.MinSize(Size(-100000,-100000)); tracker.Track(Rect(p,p)); } } Previous message content cleared. Issue solved: I will return with a working RectTracker solution in the next message. Best regards, Tom
OK, here are all the RectTracker changes required: Add in RectTracker:: in CtrlCore.h: virtual bool Pen(Point p, const PenInfo& pn, dword keyflags); Add in LocalLoop.cpp: bool RectTracker::Pen(Point p, const PenInfo& pn, dword keyflags){ switch(pn.action){ default: MouseMove(p, keyflags); break; case PEN_UP: LeftUp(p, keyflags); break; } return true; } And finally changes in Win32Proc.cpp: ... static bool pendown=false; bool GetShift() { return !!(GetKeyStateSafe(VK_SHIFT) & 0x8000); } bool GetCtrl() { return !!(GetKeyStateSafe(VK_CONTROL) & 0x8000); } bool GetAlt() { return !!(GetKeyStateSafe(VK_MENU) & 0x8000); } bool GetCapsLock() { return !!(GetKeyStateSafe(VK_CAPITAL) & 1); } bool GetMouseLeft() { return pendown || !!(GetKeyStateSafe(VK_LBUTTON) & 0x8000); } bool GetMouseRight() { return !!(GetKeyStateSafe(VK_RBUTTON) & 0x8000); } bool GetMouseMiddle() { return !!(GetKeyStateSafe(VK_MBUTTON) & 0x8000); } bool PassWindowsKey(int wParam); LRESULT Ctrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { GuiLock __; eventid++; // LLOG("Ctrl::WindowProc(" << message << ") in " << ::Name(this) << ", focus " << (void *)::GetFocus()); Ptr<Ctrl> _this = this; HWND hwnd = GetHWND(); switch(message) { case WM_POINTERDOWN: case WM_POINTERUPDATE: case WM_POINTERUP: { pen.action = 0; pen.pressure = pen.rotation = Null; pen.tilt = Null; pen.eraser = pen.barrel = pen.inverted = pen.history = false; static BOOL (WINAPI *GetPointerType)(UINT32 pointerId, POINTER_INPUT_TYPE *pointerType); static BOOL (WINAPI *GetPointerInfo)(UINT32 pointerId, POINTER_INFO *pointerInfo); static BOOL (WINAPI *GetPointerPenInfo)(UINT32 pointerId, POINTER_PEN_INFO *penInfo); static BOOL (WINAPI *GetPointerPenInfoHistory)(UINT32 pointerId, UINT32 *entriesCount, POINTER_PEN_INFO *penInfo); ONCELOCK { DllFn(GetPointerType, "User32.dll", "GetPointerType"); DllFn(GetPointerInfo, "User32.dll", "GetPointerInfo"); DllFn(GetPointerPenInfo, "User32.dll", "GetPointerPenInfo"); DllFn(GetPointerPenInfoHistory, "User32.dll", "GetPointerPenInfoHistory"); }; if(!(GetPointerType && GetPointerInfo && GetPointerPenInfo && GetPointerPenInfoHistory)) break; POINTER_INPUT_TYPE pointerType; auto ProcessPenInfo = [&] (POINTER_PEN_INFO& ppi) { if(ppi.penFlags & PEN_FLAG_BARREL) pen.barrel = true; if(ppi.penFlags & PEN_FLAG_INVERTED) pen.inverted = true; if(ppi.penFlags & PEN_FLAG_ERASER) pen.eraser = true; if(ppi.penMask & PEN_MASK_PRESSURE) pen.pressure = ppi.pressure / 1024.0; if(ppi.penMask & PEN_MASK_ROTATION) pen.rotation = ppi.rotation * M_2PI / 360; if(ppi.penMask & PEN_MASK_TILT_X) pen.tilt.x = ppi.tiltX * M_2PI / 360; if(ppi.penMask & PEN_MASK_TILT_Y) pen.tilt.y = ppi.tiltY * M_2PI / 360; }; UINT32 pointerId = GET_POINTERID_WPARAM(wParam); if(GetPointerType(pointerId, &pointerType) && pointerType == PT_PEN) { UINT32 hc = 256; Buffer<POINTER_PEN_INFO> ppit(hc); if(message == WM_POINTERUPDATE && GetPointerPenInfoHistory(pointerId, &hc, ppit)) { bool processed = false; for(int i = hc - 1; i >= 0; i--) { ProcessPenInfo(ppit[i]); POINT hp = ppit[i].pointerInfo.ptPixelLocation; ::SetCursorPos(hp.x,hp.y); ::ScreenToClient(hwnd, &hp); pen.history = (bool)i; processed = !IsNull(DoMouse(PEN, hp, 0)); } if(processed) return 0L; else break; } POINTER_PEN_INFO ppi; if(GetPointerPenInfo(pointerId, &ppi)) ProcessPenInfo(ppi); POINT p = ppi.pointerInfo.ptPixelLocation; ::SetCursorPos(p.x,p.y); ::ScreenToClient(hwnd, &p); switch(message) { case WM_POINTERDOWN: pendown=true; pen.action = PEN_DOWN; ClickActivateWnd(); break; case WM_POINTERUP: pendown=false; pen.action = PEN_UP; break; } if(!IsNull(DoMouse(PEN, p, 0))) return 0L; break; } } break; ... The main problem was that ::GetCursorPos() was not working as ::SetCursorPos() was not called from WM_POINTER*. Now it all works and mouse follows pen. Best regards, Tom EDIT: Not quite... calling SetCursorPos() causes incoming WM_MOUSEMOVE messages at some frequency, but the coordinates are obviously old ones causing erratic behavior if both MouseMove and Pen are processed... However, if SetCursorPos() is not called, the mouse position will be different to pen and it will in turn cause flickering RectTracker with intermediate rectangles to mouseposition! Well, I'm done for today. (or yesterday, or whatever.) Anyway, if you have any ideas how to go around this, please fill in...
Tom1 wrote on Thu, 25 March 2021 22:28 Mirek, Is pn.action intentionally merely a state flag showing continuously PEN_UP or PEN_DOWN? Or should we reset it along with other PenInfo items in the beginning of processing WM_POINTER* messages? I would like to have it reset, in order to more easily detect UP/DOWN state changes without a state variable and treat zero action as 'PEN_MOVE'. Then we could do this to decode all kinds of pen events: #define PENMOVE 0 #define PENDOWN 1 #define PENUP 2 #define PENHOLD 3 #define PENDRAG 4 #define PENDOUBLE 5 #define PENTRIPLE 6 bool IsNear(Point p, Point o){ Point delta(p-o); if(abs(delta.x)<5 && abs(delta.y)<5) return true; return false; } int PenDecoder(Point p, const PenInfo& pn, dword keyflags){ static int action=0; static int downt=0; static Point downp=Null; static bool doubleClick=true; static bool down=false; static bool solved=false; int now=msecs(); int deltat=now-downt; bool isNear=IsNear(p,downp); switch(pn.action){ case PEN_DOWN: down=true; solved=false; downp=p; downt=now; if(isNear){ if(now-downt < 500){ if(doubleClick){ return PENTRIPLE; } else{ doubleClick=true; return PENDOUBLE; } } } doubleClick=false; return PENDOWN; case PEN_UP: down=false; return PENUP; default: if(down && !solved){ if(isNear){ if(now-downt > 1000){ solved=true; return PENHOLD; } } else{ solved=true; return PENDRAG; } } return PENMOVE; } } Best regards, Tom Would it solve any real problem? I mean, I believe that the application can do all these on its own, if it needs to.
Hi, Well, I put the 'PenDecoder' here just for reference for anyone wishing to map pen to mouse like callbacks in application. It does not really need to be part of CtrlCore. However, my point here is that, I would feel more at home, if: - only WM_POINTERUP would cause pn.action=PEN_UP - only WM_POINTERDOWN would cause pn.action=PEN_DOWN - and WM_POINTERUPDATE would always keep pn.action=0; (or PEN_MOVE). I.e. pn.action would be event type instead of state flag of up/down status. Or do you have some particular reason to always have pn.action set to either PEN_UP or PEN_DOWN depepending on the last change? Best regards, Tom
Tom1 wrote on Fri, 26 March 2021 10:15 Or do you have some particular reason to always have pn.action set to either PEN_UP or PEN_DOWN depepending on the last change? Best regards, Tom Ah, sorry, I misunderstood the intent. Nope, that was just a bug, forgot to assign 0 in WindowProc...
Good! Maybe you can add a: #define PEN_MOVE 0 or something alike in CtrlCore.h? Now about that RectTracker: I'm having hard time with the ::GetCursorPos/::SetCursorPos... ::GetCursorPos knows where mouse is, but in order for it to know where Pen is, we would need to call ::SetCursorPos in WM_POINTER*. But calling it results in a slightly delayed WM_MOUSEMOVE message coming in causing all kinds of annoying stuff happening. If we do not call ::SetCursorPos in WM_POINTER, the Pen and Mouse will be in two different positions causing other issues in many places because ::GetCursorPos only knows about the mouse, not pen. Any ideas how to proceed? Best regards, Tom
Linux, Clang: ./umk uppsrc ide CLANG -bus /home/buildbot/worker/l-upp/build/uppsrc/CtrlCore/GtkEvent.cpp:429:6: error: no viable overloaded '=' pen = CurrentEvent.pen; ~~~ ^ ~~~~~~~~~~~~~~~~
Alpine Linux (in addition to previous error): ./umk uppsrc ide CLANG -bus /home/buildbot/worker/al-upp/build/uppsrc/CtrlCore/GtkEvent.cpp:430:2: error: use of undeclared identifier 'pen_barrel' pen_barrel = CurrentEvent.pen_barrel; ^ /home/buildbot/worker/al-upp/build/uppsrc/CtrlCore/GtkEvent.cpp:431:2: error: use of undeclared identifier 'pen_inverted' pen_inverted = CurrentEvent.pen_inverted; ^ /home/buildbot/worker/al-upp/build/uppsrc/CtrlCore/GtkEvent.cpp:432:2: error: use of undeclared identifier 'pen_eraser' pen_eraser = CurrentEvent.pen_eraser; ^ /home/buildbot/worker/al-upp/build/uppsrc/CtrlCore/GtkEvent.cpp:433:2: error: use of undeclared identifier 'pen_pressure' pen_pressure = CurrentEvent.pen_pressure; ^ /home/buildbot/worker/al-upp/build/uppsrc/CtrlCore/GtkEvent.cpp:434:2: error: use of undeclared identifier 'pen_rotation' pen_rotation = CurrentEvent.pen_rotation; ^ /home/buildbot/worker/al-upp/build/uppsrc/CtrlCore/GtkEvent.cpp:435:2: error: use of undeclared identifier 'pen_tilt' pen_tilt = CurrentEvent.pen_tilt; ^
Tom1 wrote on Fri, 26 March 2021 13:39 Good! Maybe you can add a: #define PEN_MOVE 0 or something alike in CtrlCore.h? Is this just MOVE though? I can add PEN_OTHER, but do not see much point. It is UP, DOWN or nothing... Now about that RectTracker: I'm having hard time with the ::GetCursorPos/::SetCursorPos... Quote: If we do not call ::SetCursorPos in WM_POINTER, the Pen and Mouse will be in two different positions causing other issues in many places because ::GetCursorPos only knows about the mouse, not pen. Any ideas how to proceed? Well, the whole point of Pen virtual method is that we should not meddle with normal mouse.... Is it so bad to just leave it to LeftDown? Mirek
Mirek, 'case PEN_OTHER:' Looks better than 'case 0:' ... Quote: Is it so bad to just leave it to LeftDown? I'm not quite sure what do you mean by this.. (?) (If it is about double clicks, drags or barrel mappings to right mouse button, I can handle them in the application side.) -- Anyway, now the good news: I finally got through with RectTracker and Get/SetCursorPos. The set of changes required follow. Add to CtrlCore.h: bool GetPenDown(); // << ADD THIS ... AND ... class RectTracker : public LocalLoop { public: virtual void LeftUp(Point, dword); virtual void RightUp(Point, dword); virtual void MouseMove(Point p, dword); virtual bool Pen(Point p, const PenInfo& pn, dword keyflags); // << ADD THIS Add "&& !GetPenDown()" at line 595 in CtrlMouse.cpp: ... if(findarg(e, LEFTUP, RIGHTUP, MIDDLEUP) >= 0) KillRepeat(); Image result = DispatchMouseEvent(e, p, zd); if(!GetMouseRight() && !GetMouseMiddle() && !GetMouseLeft() && !GetPenDown()) ReleaseCtrlCapture(); return result; ... Add to LocalLoop.cpp: bool RectTracker::Pen(Point p, const PenInfo& pn, dword keyflags){ switch(pn.action){ case 0: MouseMove(p,keyflags); break; case PEN_UP: EndLoop(); break; } return true; } Add "&& !GetPenDown()" at line 781 in Win32Wnd.cpp: ... bool Ctrl::ProcessEvent(bool *quit) { ASSERT_(IsMainThread(), "ProcessEvent can only run in the main thread"); if(!GetMouseLeft() && !GetMouseRight() && !GetMouseMiddle() && !GetPenDown()) // << HERE ReleaseCtrlCapture(); ... And the rest is in the attached Win32Proc.cpp. Best regards, Tom
Tom1 wrote on Fri, 26 March 2021 16:44 Mirek, 'case PEN_OTHER:' Looks better than 'case 0:' ... 'default:' is fine too Quote: Quote: Is it so bad to just leave it to LeftDown? I'm not quite sure what do you mean by this.. (?) (If it is about double clicks, drags or barrel mappings to right mouse button, I can handle them in the application side.) bool Pen(Point p, const PenInfo& pn, dword keyflags) override { if(keyflags & K_SHIFT) return false; if(pn.pressure) { if((!!pn.pressure == !!pen.pressure) && drawing.GetCount()) drawing.Top().Add(MakeTuple(pn.pressure, p)); else drawing.Add().Add(MakeTuple(pn.pressure, p)); } pen = pn; Refresh(); return true; } void LeftDown(Point p, dword keyflags) override { if(keyflags & K_SHIFT) { RectTracker tracker(*this); tracker.MinSize(Size(-100000,-100000)); tracker.Track(Rect(p,p)); } } I mean, the main reason for separated Pen method interface is that we do not want to add support to any client code. So to start adding just that right away is perhaps not the best idea? Mirek
Yes, default: is great (if you also handle both "case PEN_DOWN:" and "case PEN_UP:") Anyway, this is not important now. I can write case 0: I agree 100 %: We do not want to add any pen support to client code. However, e.g. RectTracker does not work as is with my Wacom tablet. The specific pen support (I just posted) is simply required for it to work. (I'm now under impression that RectTracker works with your tablet perfectly without these changes... ?? Perhaps XP-PEN drivers can avoids Windows Ink and work more mouse-like through the stack.) Best regards, Tom
Tom1 wrote on Fri, 26 March 2021 18:52 However, e.g. RectTracker does not work as is with my Wacom tablet. The specific pen support (I just posted) is simply required for it to work. (I'm now under impression that RectTracker works with your tablet perfectly without these changes... ?? Perhaps XP-PEN drivers can avoids Windows Ink and work more mouse-like through the stack.) Sorry, I might get lost in the code that we exchange here a lot... Do you mean that my variant where Pen returns false and RectTracker is called in normal LeftDown does not work with Wacom? (My impression so far was that you are only testing RectTracker in Pen virtual method). If so, does it mean RectTracker does not work even in pre-Pen U++? (With Wacom in "mouse mode") Mirek
mirek wrote on Sat, 27 March 2021 10:11 Tom1 wrote on Fri, 26 March 2021 18:52 However, e.g. RectTracker does not work as is with my Wacom tablet. The specific pen support (I just posted) is simply required for it to work. (I'm now under impression that RectTracker works with your tablet perfectly without these changes... ?? Perhaps XP-PEN drivers can avoids Windows Ink and work more mouse-like through the stack.) Sorry, I might get lost in the code that we exchange here a lot... Do you mean that my variant where Pen returns false and RectTracker is called in normal LeftDown does not work with Wacom? (My impression so far was that you are only testing RectTracker in Pen virtual method). If so, does it mean RectTracker does not work even in pre-Pen U++? (With Wacom in "mouse mode") Mirek Hi, With original pre-Pen U++, RectTracker worked with Wacom at correct coordinates. (It only suffered from the Windows Ink induced 2 cm starting delay, but the rectangle followed pen thereafter.) With current trunk code and testing with: bool Pen(Point p, const PenInfo& pn, dword keyflags) override { if(keyflags & K_SHIFT) return false; if(pn.pressure) { if((!!pn.pressure == !!pen.pressure) && drawing.GetCount()) drawing.Top().Add(MakeTuple(pn.pressure, p)); else drawing.Add().Add(MakeTuple(pn.pressure, p)); } pen = pn; Refresh(); return true; } void LeftDown(Point p, dword keyflags) override { if(keyflags & K_SHIFT) { RectTracker tracker(*this); tracker.MinSize(Size(-100000,-100000)); tracker.Track(Rect(p,p)); } } the tracking rect still of course has the 2 cm starting delay. However, now the rect dragged corner lags behind with a 2 cm constant dx,dy offset. Please see below a photo of a live 'while dragging' situation. So, actually, RectTracker is now worse than before introducing pen support: Further on, what I really need with RectTracker, is that there is no 2 cm reaction delay. The tracking must start immediately from PEN_DOWN event. My RectTracker inherited sketching code requires immediate reaction for even a single pixel pen drags. This is why I need specific Pen() support in RectTracker. Best regards, Tom
Tom1 wrote on Sat, 27 March 2021 12:20 mirek wrote on Sat, 27 March 2021 10:11 Tom1 wrote on Fri, 26 March 2021 18:52 However, e.g. RectTracker does not work as is with my Wacom tablet. The specific pen support (I just posted) is simply required for it to work. (I'm now under impression that RectTracker works with your tablet perfectly without these changes... ?? Perhaps XP-PEN drivers can avoids Windows Ink and work more mouse-like through the stack.) Sorry, I might get lost in the code that we exchange here a lot... Do you mean that my variant where Pen returns false and RectTracker is called in normal LeftDown does not work with Wacom? (My impression so far was that you are only testing RectTracker in Pen virtual method). If so, does it mean RectTracker does not work even in pre-Pen U++? (With Wacom in "mouse mode") Mirek Hi, With original pre-Pen U++, RectTracker worked with Wacom at correct coordinates. (It only suffered from the Windows Ink induced 2 cm starting delay, but the rectangle followed pen thereafter.) With current trunk code and testing with: bool Pen(Point p, const PenInfo& pn, dword keyflags) override { if(keyflags & K_SHIFT) return false; if(pn.pressure) { if((!!pn.pressure == !!pen.pressure) && drawing.GetCount()) drawing.Top().Add(MakeTuple(pn.pressure, p)); else drawing.Add().Add(MakeTuple(pn.pressure, p)); } pen = pn; Refresh(); return true; } void LeftDown(Point p, dword keyflags) override { if(keyflags & K_SHIFT) { RectTracker tracker(*this); tracker.MinSize(Size(-100000,-100000)); tracker.Track(Rect(p,p)); } } the tracking rect still of course has the 2 cm starting delay. However, now the rect dragged corner lags behind with a 2 cm constant dx,dy offset. Please see below a photo of a live 'while dragging' situation. So, actually, RectTracker is now worse than before introducing pen support: Further on, what I really need with RectTracker, is that there is no 2 cm reaction delay. The tracking must start immediately from PEN_DOWN event. My RectTracker inherited sketching code requires immediate reaction for even a single pixel pen drags. This is why I need specific Pen() support in RectTracker. Best regards, Tom OK, this works just fine with xp-pen... Widgets in general do work? (Because if it is this way, I would just expect the offset to be everywhere...) What happens if you remove return 0L in pen processing? Mirek
Yes. Widgets mostly work. Some flickering with e.g. Menu, but I think nothing is severely broken. I can do a more thorough check later. Breaking out from WM_POINTER* instead of return 0L; does not have any effect on this test with RectTracker. Offset remains. The core problem in my opinion is ::GetCursorPos which does not work with pen. This comes evident with drags. Best regards, Tom
Tom1 wrote on Sat, 27 March 2021 20:45 Yes. Widgets mostly work. Some flickering with e.g. Menu, but I think nothing is severely broken. I can do a more thorough check later. Breaking out from WM_POINTER* instead of return 0L; does not have any effect on this test with RectTracker. Offset remains. The core problem in my opinion is ::GetCursorPos which does not work with pen. This comes evident with drags. OK. In the new commit I treat GetCursorPos as "not reliable", overriding it with position got from lParam. Can you try?
Quote: OK. In the new commit I treat GetCursorPos as "not reliable", overriding it with position got from lParam. Hi Mirek, That was a correct move. Now RectTracker works correctly after 2 cm Windows Ink threshold is crossed. This is via "Pen() return false; to LeftDown()". In Pen(), RectTracker does not work at all yet. I also tested with UWord to see how different widgets work. I could not immediately find anything that does not. Only dragging a top menu item to open a drop down menu causes flickering of the selected item on the drop down menu. If the drop down menu is opened by a pen click, hovering on top of the sub menu works correctly without any flickering. Best regards, Tom
[quote title=Tom1 wrote on Sun, 28 March 2021 18:11]Quote: Only dragging a top menu item to open a drop down menu causes flickering of the selected item on the drop down menu. If the drop down menu is opened by a pen click, hovering on top of the sub menu works correctly without any flickering. I do not see anything like that, which probably means I am not testing correctly. Also, can you test the same thing with "pre-pen" U++? Mirek
Mirek, I did a video clip portraying the menu highlight flickering issue. Sent a PM on that. There is no such anomaly on pre-pen U++. Best regards, Tom
Tom1 wrote on Mon, 29 March 2021 12:33 Mirek, I did a video clip portraying the menu highlight flickering issue. Sent a PM on that. There is no such anomaly on pre-pen U++. Best regards, Tom Well, all works fine with xp-pen (again...). If you deactivate Windows Ink for tablet, what does it do? Does it mean it now does those weird click animations (extending circle) even for mouse? Mirek
The click animations are never present with mouse. When using Windows Ink -mode for Wacom, I get those click animations for pen. When I disable Windows Ink -mode for Wacom, the thing starts to work exactly like mouse: + no click animations + immediate response with drags (i.e. no annoying 2 cm delay) - NO Pen() event. Everything comes in as MouseMove, LeftDown, LeftUp... (This likely means there is no WM_POINTER* messages and everything is already translated to WM_MOUSEMOVE, WM_LBUTTONDOWN,... etc.) - NO pressure detection either due to missing WM_POINTER* -messages. So, using Windows Ink -mode is a must. Best regards, Tom
Well, that means that in the movie, you have used pen to open the menu, right? Does this disbehave with mouse too? (Without animation?) What happens if you comment out CtrlCore/Win32Proc.cpp:74? Mirek
mirek wrote on Mon, 29 March 2021 16:35 Well, that means that in the movie, you have used pen to open the menu, right? Does this disbehave with mouse too? (Without animation?) What happens if you comment out CtrlCore/Win32Proc.cpp:74? Mirek 1. Yes, I used pen throughout the video. 2. With mouse it works perfectly OK. There is no click animation with mouse. 3. Commenting out: // POINT p; // if(::GetCursorPos(&p)) //CurrentMousePos = p; Does not change anything. Best regards, Tom
Tom1 wrote on Mon, 29 March 2021 15:50 mirek wrote on Mon, 29 March 2021 16:35 Well, that means that in the movie, you have used pen to open the menu, right? Does this disbehave with mouse too? (Without animation?) What happens if you comment out CtrlCore/Win32Proc.cpp:74? Mirek 1. Yes, I used pen throughout the video. 2. With mouse it works perfectly OK. There is no click animation with mouse. 3. Commenting out: // POINT p; // if(::GetCursorPos(&p)) //CurrentMousePos = p; Does not change anything. Best regards, Tom Pity, I was almost sure it must be related... OK. The place that says whether the item is highlighted or not is CtrlLib/MenuItem.cpp:267 Putting some DDUMPs for parts of that complex condition should reveal the source of oscilation. Mirek
Logging the changes shows that it's HasFocus() and especially HasMouse() that oscillates: [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 0 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 32 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 0 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 31 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 0 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 31 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 0 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 31 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 0 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 32 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 0 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 31 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 0 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 31 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 0 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 31 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true Best regards, Tom
Tom1 wrote on Mon, 29 March 2021 16:20 Logging the changes shows that it's HasFocus() and especially HasMouse() that oscillates: [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 0 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 32 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 0 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 31 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 0 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 31 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 0 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 31 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 0 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 32 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 0 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 31 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 0 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 31 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 0 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true [+ 0 ms] HasMouse() = true [+ 31 ms] HasFocus() = false [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasMouse() = true [+ 0 ms] HasMouse() = false [+ 0 ms] HasFocus() = true Best regards, Tom Frankly, not sure what to do next...
Do you have Windows Ink enabled with your XP-PEN? If not, please do. You might then see the click animations and experience the drag lag. Maybe the menu issue shows itself then...(?) BR, Tom
Thinking about it, as hovering with pen has no trouble, it must be an issue with 'pendown' not comparable to left mouse button being down. It might be partly identified as left mouse button being pressed and partly not, giving mixed messages about the state. This is something that partially prevents RectTracker from being used with/from Pen() interface. (Which is still a main concern for me...) Best regards, Tom
Tom1 wrote on Mon, 29 March 2021 17:04 Do you have Windows Ink enabled with your XP-PEN? If not, please do. You might then see the click animations and experience the drag lag. Maybe the menu issue shows itself then...(?) BR, Tom I have it enabled, I see the animation, but no problems.