[Lazarus] Cursor handling on Windows

Vladimir Zhirov vvzh.lists at gmail.com
Fri Aug 14 11:54:23 CEST 2009


Hi,

Martin and I started a discussion in the bugtracker 
and decided to move it here so more people can participate.

The bug report (http://bugs.freepascal.org/view.php?id=14336)
is related to cursor handling on Windows.
To sum up, the bug is resolved, but we are still looking for 
more reliable means to set cursors in general and default cursor 
in particular.

Martin Friebe wrote:
> There are 2 issues:
> 1) For SetCursor it is already documented on msdn that 
> it does nothing, if the value is already set. 
> Who is to say that future Window versions will check 
> the position first, and ignore if it hasn't changed?
> It should work using "CallWindowProc "

CallWindowProc would require us to pass WParam an LParam values
for WM_SETCURSOR message, and documentation about them seems 
a bit vague to me. I would not rely on default WindowProc behaviour 
unless I know what exactly must be passed in WParam and LParam.
If I don't know it I would rather use SetCursorPos because at least
it does not have documented restrictions like SetCursor has.
SetCursorPos behavior can change later but for now it is predictable 
and clearly documented in contrast to WM_SETCURSOR.
That's my thoughts on "CallWindowProc vs SetCursorPos".

The problem is, as you stated, both CallWindowProc and SetCursorPos 
will cause WindowProc execution, that is more expensive than SetCursor
preceded by some calculations.

So, let me summarize your proposed changes for the future 
as I understand them and add my comments.

1) Remove cursor handling from MouseMove (at least in the cross widget 
   part of the LCL)

   I agree with this suggestion. AFAIU the first place to look 
   at is TControl.WMMouseMove.

2) Add new LCL message talking controls to update their cursor 
   (like WM_SETCURSOR on Windows) and ensure all WidgetSets use 
   this message.

   I'm not sure I understand you right here, but missing LM_SETMOUSE 
   message led me to conclusion that you suggest what I stated above. 
   If I get it right, it is hard to comment for me because I'm almost 
   completely clueless about GTK and QT APIs so I cannot say if it worth
   the effort to make such change for all widgetsets.

3) Decide (or find out) if Control.SetCursor should trigger this message 
   to update a cursor.
   
   This is a controversial question, because triggering this message 
   would involve costly message processing, but allow to handle cursors 
   from one place. So it seems to evaluate to "execution speed vs code clarity" 
   and the problem is to estimate differences.
   I have not done any measurements but both 
      "CalculateBounds[+GetClassCursor]+SetCursor" (acting independently) and 
      "GetCursorPos+SetCursorPos" (causing costly message processing)
   seems to work fast enough for me not to notice the difference. 
   So maybe it is a matter of taste. It would be interesting to know 
   what other think.
   
4) Optimize the current handling in WindowProc
   
   See (3).

5) Decide if passing on handling to parent WinControl is wanted if 
   "control.Cursor := crDefault" is done
   
   To conitue "Intended behaviour of crDefault cursor..." thread, 
   here is what I think.
    a. We usually want to set a specific cursor for single control 
       like Button, not for container like Panel
    b. It means containers usually have Cursor = crDefault, 
       so it makes no difference whether cursor handling 
       is passed to Parent or not.
    c. If we set custom cursor for container like Panel what
       do we most likely mean by it? Users coming from Delphi
       would expect Delphi behaviour that is setting it for 
       Panel only. If there is a Button on the Panel and 
       Button.Cursor = crDefault then it is system default 
       cursor for buttons.
       <IMHO>
       So it seems crDefault in Delphi means 
       "System default cursor for this kind of control".
       Screen.Cursor is an exception and crDefault here means
       "Do not override cursor settings"
       </IMHO>
    d. Sometimes it is useful to make child controls inherit
       cursor settings from parent. This helps to set e.g.
       crHourGlass for disabled panel with all child 
       controls and then restore original cursor.
       In order to keep Delphi compatibility we can introduce
       crParent cursor and pass cursor handling to parent
       control if this cursor is set for child control.
       In this case crParent could still fallback to crDefault
       if the control has no parent.





More information about the Lazarus mailing list