DAY 016: _MOUSEINPUT
#11
(11-22-2022, 12:38 AM)james2464 Wrote: I think my confusion centers around the "AND NOT".  I have never used this and I need to give this a try in the future.   Thanks!

You're almost there with your own statement: "I believe it means literally that:    if mb is true..."

^This part is correct.  The next part should, however, be:

"and if NOT oldmb is true..;"

In other words, "if mb is true and oldmb is false".   (Not true is false, after all.)
Reply
#12
I use mouse routines extensively. Good routines use variables to indicate a button is down or released, if a double click is initiated or if a drag event is occurring. Drag is simple enough with a left mouse button down variable and checking if the mouse has moved. I believe we have a _MOUSEMOVEMENT keyword, but I'm old school. I just go with an oldmy and oldmy variable in the loop. If the left mouse button is down and my <> oldmy or mx <> oldmx then the drag routine gets called. Fun stuff when down properly and a bit easier than it used to be with CALL INTERRUPT routines, although they still function in QB64. Note however that CALL ABSOLUTE mouse routines are not supported.

Pete
Reply
#13
@Pete If you're going that complex, use MBS: https://staging.qb64phoenix.com/showthread.php?tid=138
Reply
#14
Nice, but my mouse routines have more BS than yours! Ah, wait a minute; that didn't come out right...

Oh, and in other news, Apparently Joe Brandon gave your code a pardon. (Joe Biden pardons Saudi Leader.)

Pete Big Grin
If eggs are brain food, Biden takes his scrambled.
Reply
#15
We were also discussing this topic over at our Discord channel, and it seems there may be a few use cases where you'd actually place more code inside the While: Wend loop than I mentioned -- and that's basically whenever your main loop is running at an incredibly slow rate.

For example:

Code: (Select All)
oldmb = -1 'count as if the mouse is down to begin with
'this is so the mouse event has to have an UP then DOWN event to count as a click

Do
    While _MouseInput: Wend
    mb = _MouseButton(1)
    If mb And Not oldmb Then
        count = count + 1
        Print "You just clicked the button! For a total of"; count
    End If
    oldmb = mb
    _Limit 1
Loop Until _MouseButton(2) Or _KeyHit > 0


Try the above and see how many times you have to click the mouse before a click registers.  (For me, it's about half a dozen times, give or take.)  Your processing time is much less than your down time with the limit, and a vast majority of mouse buttons are lost when you clear the buffer.  This isn't exactly an idea approach to handling things...





So, we can try something like the following:

Code: (Select All)
oldmb = -1
Do
    While _MouseInput And Not click
        mb = _MouseButton(1)
        If mb And Not oldmb Then click = -1
        oldmb = mb
    Wend
    If click Then
        count = count + 1
        Print "You just clicked the button! For a total of"; count
        click = 0
    End If
    _Limit 1
Loop Until _KeyHit = 27


Now here, we can click all we want, and every click event is read.  The problem with this approach is that our program is running one loop per second, the user is entering multiple clicks per second -- we're going to generate lag in there, no matter what!!

If I can count one penny per second, and you suddenly hand me a dozen pennies, it's going to take me 12 seconds before I can do anything else besides count those pennies.   < -- That's basically what we're seeing here.



So, we can take a middle ground approach:

Code: (Select All)
oldmb = -1
Do
    While _MouseInput
        mb = _MouseButton(1)
        If mb And Not oldmb Then click = -1
        oldmb = mb
    Wend
    If click Then
        count = count + 1
        Print "You just clicked the button! For a total of"; count
        click = 0
    End If
    _Limit 1
Loop Until _KeyHit = 27


Like this, we check for a mouseclick inside our mouseinput loop, and we basically set a flag if one occurs.  It'll limit us to ONE click event per loop, no matter how many times the user mashes in the button, so we'll end up missing some clicks -- but we won't lag up the rest of our program either.  We're running at one loop per second, and we're throttling clicks down to a maximum of one click per second.




For most people, and most programs (which you seldom ever see with less than a limit 30, 60, or even higher), this type of concern isn't one they'd ever have to worry over.  Their program polls the mouse faster than a human can click a button up and down, so there's no chance for that backlog to appear in the buffer to the point where they start to miss events.  

Once a program gets bogged down to the point where mouseclicks CAN be missed, however, there's really no "golden" solution for how to deal with things.  Your three choices are basically:

1) Skip events so the program flows as smoothly as possible.
2) Deal with every event, even if it causes the program to have to pause execution until it's caught up and cleared the buffer.
3) Try to find a middle ground and watch to see if an event occured during the previous execution of the main program, and if so, deal with it -- even if you lose some other events in the process.

I mentioned that you never want anything inside that While - Wend _MOUSEINPUT Loop...  That's not entirely true in extreme use cases, like the ones I showcased above.  

One thing to remember:  For every rule someone gives you in programming, there's always some exception to that rule -- no matter how rare, extreme, or unique it might be.  Wink
Reply
#16
Ever notice that some apps trigger an event when the left mouse is pressed, but a few trigger it only after the left mouse button once pressed is released? The following demonstrates how this can be accomplished...

Code: (Select All)
REM click_event = 1 to initiate on button press.
REM click_event = 2 to initiate on button release.

VIEW PRINT 1 TO 23
click_event = 1
LOCATE 25, 1: PRINT "Event triggered on left button press. Press 2 to change."
LOCATE 1, 1
DO
    _LIMIT 30
    b$ = INKEY$
    IF LEN(b$) AND lb = 0 THEN
        IF b$ = "1" THEN click_event = 1: y = CSRLIN: x = POS(0): LOCATE 25, 1: PRINT "Event triggered on left button press. Press 2 to change.  ";: LOCATE y, x
        IF b$ = "2" THEN click_event = 2: y = CSRLIN: x = POS(0): LOCATE 25, 1: PRINT "Event triggered on left button release. Press 1 to change.";: LOCATE y, x
    END IF
    WHILE _MOUSEINPUT: WEND
    IF _MOUSEBUTTON(1) THEN
        IF lb = 0 THEN lb = -1
    ELSE
        IF lb THEN
            IF click_event = -2 THEN CALL event
            lb = 0: click_event = ABS(click_event) ' Button release.
        END IF
    END IF

    IF lb AND click_event = 1 THEN CALL event: click_event = -1 '  Event on button down.
    IF lb AND click_event = 2 THEN click_event = -2 ' Event when button down is released.
LOOP

SUB event
    SOUND 1000, .2
    PRINT "Pete!"
END SUB

Pete
Reply
#17
The other problem would be like a program I wrote which read the right mouse button click to quit. The program ended with "SYSTEM", that is, without "Press any key to continue" prompt, and then suddenly a context menu was opened in File Explorer or another app that happened to have a window just under that of my program. It was annoying but I had trouble killing the mouse events upon user program finish.
Reply
#18
Which leads into yet another discussion, how to save yourself from an unintended exit. So to demonstrate how this is done, try the following...

If you press the left mouse button down, over the exit icon, but then move the cursor off it before you release the button, you avoid the system action with this mini-mouse routine!

Code: (Select All)
DIM mse AS mm ' Let's do a TYPE instead of passing all variables or doing a DIM SHARE.
WIDTH 25, 25
TYPE mm
    my AS INTEGER
    mx AS INTEGER
    oldmy AS INTEGER
    oldmx AS INTEGER
    lb AS INTEGER
END TYPE
COLOR 9, 1: CLS

COLOR 0, 7
icon_x = _WIDTH - 1 ' Row.
icon_y = 2 ' Column.
LOCATE icon_y, icon_x: PRINT "X"; ' Place an exit symbol on the screen.
'
DO
    _LIMIT 30

    mini_mouse mse ' Poll the mini mouse routine.

    ' Highlight the exit icon on hover, but only once so it isn't going on and off like hell. The oldmx variables insure this once and done action.
    IF mse.my = icon_y AND mse.mx = icon_x AND mse_old.my <> mse.my AND mse_old.mx <> mse.mx THEN
        hotspot = ((mse.my - 1) * _WIDTH + mse.mx - 1) + 1 ' Matrix the screen to combine the row and column values as a single variable.
        trigger = hotspot ' Just a shorthand way so we don't keep using ((mse.my - 1) * _WIDTH + mse.mx - 1) + 1
        PALETTE 7, 4: PALETTE 0, 63
    ELSE ' When no longer hovering on exit icon reset the variables and lose the highlighting.
        hotspot = 0: trigger = 0
        PALETTE 7, 7: PALETTE 0, 0
    END IF
    IF mse.lb THEN ' Left button down.
        IF trigger = hotspot THEN activate = hotspot ' Setting activate means we mouse clicked down on it but haven't released yet.
    ELSE
        IF activate THEN ' When button is released we check to see if the exit icon was clicked and if the mouse pointer is still on the icon.
            IF activate = hotspot THEN
                SYSTEM ' Quit.
            ELSE
                activate = 0: trigger = 0: hotspot = 0 ' The mouse cursor got moved so the exit was avoided.
            END IF
        END IF
    END IF
    REM LOCATE 7, 1: PRINT mse.my; mse.mx; mse_old.my; mse_old.mx; trigger; hotspot; activate; "    ";
LOOP

SUB mini_mouse (mse AS mm)
    mse_old.my = mse.my: mse_old.mx = mse.mx
    WHILE _MOUSEINPUT: WEND
    mse.mx = _MOUSEX
    mse.my = _MOUSEY
    mse.lb = _MOUSEINPUT(1)
    IF mse.lb AND _MOUSEBUTTON(1) = 0 THEN
        mse.lb = 0
    ELSE
        IF mse.lb = 0 AND _MOUSEBUTTON(1) THEN
            mse.lb = -1
        END IF
    END IF
END SUB

Pete
Reply
#19
@Pete Or you just code a confirmation box into your program.   "Are you certain you want to exit now?  Unsaved data may be lost."

Or, if they've already saved: "Thank you for using a Steve-smart program.  Please try all our other fine products at https://smcneill.online Mention "Steve is Awesome" and get 50% off all product prices!"
Reply
#20
Let's not get ahead of ourselves there Sparky. We want to save that for the new keyword or until we get to _EXIT. That's what I've used for all my previous files programs, although I auto-update most stuff.

LOL @ Channeling Mike Lindell. I do that too sometimes. Hey, I just had a thought... I guess that makes you The Farmer in Lindell.

Oh no... H.R. memo again!!!!

Pete Big Grin
Reply




Users browsing this thread: 6 Guest(s)