Do we need a mouse trap?

Adrian Hey ahey@iee.org
Wed, 03 Mar 1999 10:26:34 +0000 (GMT)


Hello,

I seem to be having a few problems getting mouse handling to work
using ObjectIO 1.0.1 for PowerPC/MacOS. I suspect there might be
a bug in ObjectIO (or else I just don't understand how to use it
properly).

The problem seems to be that I get multiple MouseMove and
MouseDrag up calls, even if the mouse state hasn't changed.
Is this supposed to happen?

Anyway, for my purposes I only want to know if the mouse state has
changed, so I implemented the code sequences at the bottom of this
message, which appear to fix the problem for MouseMove, but not
MouseDrag. The only explanation I can think of is that MouseDrags
don't get scrutinised by the mouse filter.

What makes me suspicious about the bug hypothesis is that this is
such a basic utility I find it hard to believe nobody has noticed this
before, unless I'm the only person using ObjectIO 1.0.1 on a Mac
(which I doubt). How can anybody get their programs to work if this
really is a bug in ObjectIO?  

Worse still, the multitude of MouseDrags pretty soon causes my
test program to crash with a Heap Full error, which indicates a
severe space leak somewhere. I've noticed that the Life Game example
also crashes if you drag for too long. This could be another bug in
ObjectIO, or perhaps my program is building lots of unreduced thunks?
If the latter explanation is correct I could probably fix the leak
with appropriate strictness annotations. I haven't tried yet because
I'm confused on a few points regarding the effect of strictness
annotations in Clean and I don't want my programs to be any stricter
than necessary (but that's a subject for another message).

Has anybody else encountered this problem?
Does anybody know what I'm doing wrong or have a fix?

Thanks
--
Adrian Hey 

//---------------------------------------------------------
// This code updates a window mouse filter to prevent the
// mouse function up call being called more than once with
// the same mouse state. It needs the Modifiers to be an
// instance of (==) class.
// N.B. THIS CODE DOESN'T WORK (BUT SHOULD?)
//---------------------------------------------------------
// Put this in StdIOCommon.icl
instance == Modifiers
where
 (==) :: !Modifiers !Modifiers -> Bool
 (==) {shiftDown=a1,optionDown=a2,commandDown=a3,controlDown=a4,altDown=a5}
      {shiftDown=b1,optionDown=b2,commandDown=b3,controlDown=b4,altDown=b5}
      = (a1==b1)&&(a2==b2)&&(a3==b3)&&(a4==b4)&&(a5==b5)

// Put this in StdIOCommon.dcl
instance == Modifiers
     
// Put all the rest in my test module
import StdDebug

// This is a dummy user mouse function, for test purposes only.
// Beeps when invoked and shows the MouseState
user_mousefunc :: !MouseState -> (wls,PSt l p) -> (wls,PSt l p)
user_mousefunc ms = trace (showmouse ms) (noLS (appPIO beep))
 where showmouse :: !MouseState -> String
       showmouse (MouseMove _ _  ) = "Move "
       showmouse (MouseDown _ _ n) = "Down[" +++ (toString n) +++ "] "
       showmouse (MouseDrag _ _  ) = "Drag "
       showmouse (MouseUp   _ _  ) = "Up "

// Put this in the initial list of WindowAttributes
WindowMouse (\ms -> True) Able (mouse_trap user_mousefunc windowId)

// This function changes the mouse filter window attribute, based on the
// current mouse state, and then applies user_mousefunc
mouse_trap ::
 (MouseState -> (wls,PSt ls ps) -> (wls,PSt ls ps))  //User mouse function
 Id                                                  //Window Identifier
 MouseState     (wls,PSt ls ps) -> (wls,PSt ls ps)   //Mouse function result
mouse_trap user_mousefunc wid mousestate (winlocalstate,pst)
 # pst = appPIO (setWindowMouseStateFilter wid (mouse_filter mousestate)) pst
 =  user_mousefunc mousestate (winlocalstate,pst)

// This is the filter I want to use, this should stop multiple moves and
// drags with the same parameters
// Works ok for moves but not drags?
mouse_filter :: !MouseState     //Old MouseState
                !MouseState     //New MouseState
                -> Bool
mouse_filter (MouseMove p0 m0) (MouseMove p1 m1) = not((p0==p1)&&(m0==m1))
mouse_filter (MouseDrag p0 m0) (MouseDrag p1 m1) = not((p0==p1)&&(m0==m1))
mouse_filter _                 _                 = True

// This version of mouse_filter _definitely_ should stop all consecutive drags,
// but it doesn't :-(
//mouse_filter (MouseMove p0 m0) (MouseMove p1 m1) = not((p0==p1)&&(m0==m1))
//mouse_filter (MouseDrag _  _ ) (MouseDrag _  _ ) = False
//mouse_filter _                 _                 = True

// Don't filter any, results in multiple moves and drags with same parameters
//mouse_filter _ _ = True
//---------------------------------------------------------