Overview
Examples
Screenshots
Comparisons
Applications
Download
Documentation
Tutorials
Bazaar
Status & Roadmap
FAQ
Authors & License
Forums
Funding Ultimate++
Search on this site
Search in forums












SourceForge.net Logo
Home » Developing U++ » U++ Developers corner » Using Pen with U++
Re: Using Pen with U++ [message #56459 is a reply to message #56458] Sat, 13 March 2021 18:02 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
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? Smile

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
Re: Using Pen with U++ [message #56460 is a reply to message #56459] Sat, 13 March 2021 19:18 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
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)...
Re: Using Pen with U++ [message #56461 is a reply to message #56460] Sat, 13 March 2021 20:54 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
Hi,

News: Wacom works on Linux Mint out of the box on real hardware. Right from login screen!! Smile

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
Re: Using Pen with U++ [message #56462 is a reply to message #56459] Sat, 13 March 2021 21:09 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
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
Re: Using Pen with U++ [message #56466 is a reply to message #56461] Sun, 14 March 2021 00:05 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
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!! Smile

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...
Re: Using Pen with U++ [message #56467 is a reply to message #56466] Sun, 14 March 2021 12:14 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
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
Re: Using Pen with U++ [message #56484 is a reply to message #56466] Wed, 17 March 2021 09:59 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
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

[Updated on: Wed, 17 March 2021 17:39]

Report message to a moderator

Re: Using Pen with U++ [message #56493 is a reply to message #56484] Fri, 19 March 2021 09:09 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
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
Re: Using Pen with U++ [message #56498 is a reply to message #56493] Fri, 19 March 2021 12:57 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
Thanks, applied.

Mirek
Re: Using Pen with U++ [message #56501 is a reply to message #56498] Fri, 19 March 2021 16:02 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
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
Re: Using Pen with U++ [message #56510 is a reply to message #56501] Sat, 20 March 2021 11:25 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
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
Re: Using Pen with U++ [message #56523 is a reply to message #56510] Sun, 21 March 2021 12:29 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
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
Re: Using Pen with U++ [message #56527 is a reply to message #56523] Mon, 22 March 2021 00:39 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
It is getting complicated to carefuly apply all changes to WindowProc; could you please send the whole file?
Re: Using Pen with U++ [message #56531 is a reply to message #56527] Mon, 22 March 2021 08:46 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
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...)

[Updated on: Mon, 22 March 2021 13:11]

Report message to a moderator

Re: Using Pen with U++ [message #56533 is a reply to message #56531] Mon, 22 March 2021 10:35 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
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
Re: Using Pen with U++ [message #56535 is a reply to message #56533] Mon, 22 March 2021 16:17 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
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

Re: Using Pen with U++ [message #56543 is a reply to message #56535] Tue, 23 March 2021 13:36 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
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

[Updated on: Tue, 23 March 2021 17:29]

Report message to a moderator

Re: Using Pen with U++ [message #56545 is a reply to message #56543] Wed, 24 March 2021 11:35 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
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.
Re: Using Pen with U++ [message #56546 is a reply to message #56545] Wed, 24 March 2021 11:57 Go to previous messageGo to next message
Tom1
Messages: 1212
Registered: March 2007
Senior Contributor
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... Smile

Best regards,

Tom
Re: Using Pen with U++ [message #56547 is a reply to message #56546] Wed, 24 March 2021 13:31 Go to previous messageGo to previous message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
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... Smile

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.
Previous Topic: WString vs Grapheme Cluster idea (with possible flaw)
Next Topic: .gitignore
Goto Forum:
  


Current Time: Sun Apr 28 13:58:36 CEST 2024

Total time taken to generate the page: 0.03888 seconds