Author Topic: reading multiple mice input with _DEVICES and _DEVICE$, or RawInput ?  (Read 3546 times)

0 Members and 1 Guest are viewing this topic.

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
Hi all,

I'm looking to get separate input from 2 or more USB mice plugged into one computer.

I think the ability to use more than one mouse could be very useful and open up a lot of possibilities for games and other collaborative programs, etc.

My initial motivation / inspiration is to make simple Pong type games for 2 or more players - not on a network but on one computer, where each player controls their paddle with their own mouse. For example, for 4 players, you would plug in 4 optical USB mice into a USB hub and it would just work (no special hardware should be needed). Like "Quadrapong" and the old "Video Olympics" for Atari.

I found some older threads about this
but they seem to be more oriented towards C++ or .NET.
(Note: I'm trying to do this for Windows, but I did see some talk in there about it being possible on Mac and Linux, which might mean this would be possible on all platforms...)

User Pikalek on stackexchange replied and suggested using _DEVICES and _DEVICE$.
I gave that a try, but my code is only detecting one mouse, even though 2 are plugged in.
(I'm still pretty new to QB64, so I could be missing something...)

They also suggested looking into _MOUSEPIPEOPEN
but they said they tried it and were not able to separate clicks from two mice on the same PC.

From what I'm reading it seems Microsoft's RawInput API might work
but their docs are very low level with no examples of how to use the various functions / macros / notifications / structures. I am not that advanced a programmer to make sense of it (part of why I am using BASIC!).

I found this c# example for reading multiple keyboards:
but it's for C# and is pretty daunting - I would have to install VS, jump back into C# after 10+ years, etc. Yikes.

A similar topic is here
but it's kind of getting away from mice, they're talking about Nintendo switches and custom DLLs.

Is anyone interested in helping figure out how to read >1 mouse separately from QB64?

I think a simple reusable set of functions or a library that anyone can use, without having to get too deeply technical, would be a worthwhile effort.

(See attached or below for my test code / proof of concept.)


Code: QB64: [Select]
  1. ' #############################################################################
  2. ' MULTIMOUSE
  3. ' ----------
  4. ' A proof of concept / experiment to try to get the computer to read
  5. ' 2 or mice plugged into the computer, as separate devices,
  6. ' to control 2 or more cursors on the screen (for multiplayer games, etc.)
  7. '
  8. ' #############################################################################
  9.  
  10. ' =============================================================================
  11. ' GLOBAL DECLARATIONS a$=string, i%=integer, L&=long, s!=single, d#=double
  12. CONST FALSE = 0
  13. CONST TRUE = NOT FALSE
  14.  
  15. DIM ProgramPath$
  16. DIM ProgramName$
  17.  
  18. ' =============================================================================
  19. ' INITIALIZE
  20. ProgramName$ = MID$(COMMAND$(0), _INSTRREV(COMMAND$(0), "\") + 1)
  21. ProgramPath$ = LEFT$(COMMAND$(0), _INSTRREV(COMMAND$(0), "\"))
  22.  
  23. ' =============================================================================
  24. ' TRY THE MOUSE
  25. main ProgramName$
  26.  
  27. ' =============================================================================
  28. ' FINISH
  29. SYSTEM ' return control to the operating system
  30. PRINT ProgramName$ + " finished."
  31.  
  32. ' /////////////////////////////////////////////////////////////////////////////
  33.  
  34. SUB main (ProgName$)
  35.     DIM in$: in$ = ""
  36.     DO
  37.         CLS
  38.         PRINT ProgName$
  39.         PRINT
  40.         PRINT "How can we get separate input from 2 or more USB mice "
  41.         PRINT "plugged into one computer?"
  42.         PRINT
  43.         PRINT "1. Test using _MOUSEX, _MOUSEY, etc."
  44.         PRINT
  45.         PRINT "2. Test using _DEVICE commands"
  46.         PRINT
  47.         PRINT "3. Enumerate devices with _DEVICES to try and detect >1 mouse"
  48.         PRINT
  49.         PRINT "What to do ('q' to exit)"
  50.  
  51.         INPUT in$: in$ = LCASE$(LEFT$(in$, 1))
  52.         IF in$ = "1" THEN
  53.             MouseInputTest in$
  54.         ELSEIF in$ = "2" THEN
  55.             MouseInputTest in$
  56.         ELSEIF in$ = "3" THEN
  57.             EnumerateDevices
  58.         END IF
  59.     LOOP UNTIL in$ = "q"
  60. END SUB ' main
  61.  
  62. ' /////////////////////////////////////////////////////////////////////////////
  63. ' Gets mouse input using _MOUSEX, _MOUSEY, _MOUSEBUTTON commands.
  64.  
  65. SUB MouseInputTest (in$)
  66.     ' UDT TO HOLD THE INFO FOR EACH MOUSE
  67.     TYPE InfoType
  68.         c AS STRING ' cursor character
  69.         x AS INTEGER ' screen x position
  70.         y AS INTEGER ' screen y position
  71.         wheel AS INTEGER ' mouse wheel value
  72.         LeftDown AS INTEGER ' tracks left mouse button state, TRUE=down
  73.         MiddleDown AS INTEGER ' tracks middle mouse button state, TRUE=down
  74.         RightDown AS INTEGER ' tracks right mouse button state, TRUE=down
  75.         LeftCount AS INTEGER ' counts left clicks
  76.         MiddleCount AS INTEGER ' counts middle clicks
  77.         RightCount AS INTEGER ' counts right clicks
  78.     END TYPE ' InfoType
  79.  
  80.     ' MIN/MAX VALUES
  81.     CONST cMinX = 2
  82.     CONST cMaxX = 79
  83.     CONST cMinY = 16
  84.     CONST cMaxY = 24
  85.     CONST cMinWheel = 0
  86.     CONST cMaxWheel = 255
  87.  
  88.     ' MAIN VARIABLES
  89.     DIM iCount AS INTEGER ' # OF MICE ATTACHED
  90.     DIM arrMouseID(8) AS STRING ' device IDs for mice connected to system (guessing this would be a string, dunno)
  91.     DIM arrInfo(8) AS InfoType ' STORES INFO FOR EACH MOUSE
  92.     DIM left%, middle%, right% ' temp mouse variables
  93.     DIM iLoop AS INTEGER
  94.     DIM iIndex AS INTEGER
  95.  
  96.     ' TEMP VARIABLES FOR DISPLAYING FORMATTED VALUES TO SCREEN
  97.     DIM iLen AS INTEGER
  98.     DIM sCount AS STRING
  99.     DIM sX AS STRING
  100.     DIM sY AS STRING
  101.     DIM sWheel AS STRING
  102.     DIM sLeftDown AS STRING
  103.     DIM sMiddleDown AS STRING
  104.     DIM sRightDown AS STRING
  105.     DIM sLeftCount AS STRING
  106.     DIM sMiddleCount AS STRING
  107.     DIM sRightCount AS STRING
  108.  
  109.     ' MOUSE CURSORS (JUST SOME LETTERS)
  110.     CData:
  111.     DATA A,b,C,D,E,f,G,H
  112.  
  113.     ' DEFAULT/INTIAL X COORDINATE OF EACH CURSOR ON SCREEN
  114.     XData:
  115.     DATA 5,15,25,35,45,55,65,75
  116.  
  117.     ' DEFAULT/INTIAL Y COORDINATE OF EACH CURSOR ON SCREEN
  118.     YData:
  119.     DATA 17,17,19,19,21,21,23,23
  120.  
  121.     ' DEFAULT/INITIAL VALUE OF EACH SCROLL WHEEL
  122.     WData:
  123.     DATA 224,192,160,128,96,64,32,0
  124.  
  125.     ' COUNT # OF MICE CONNECTED + GET DEVICE IDs
  126.     iCount = GetMouseCount% ' THIS FUNCTION WOULD ENUMERATE MICE, SHOULD RETURN 1+
  127.     IF (iCount > 8) THEN iCount = 8: ' FOR NOW ONLY SUPPORT UPTO 8 MICE
  128.     GetMouseIDs arrMouseID() ' GET MOUSE IDs
  129.  
  130.     ' INITIALIZE CURSORS, MOUSE STATE, ETC.
  131.     RESTORE CData
  132.     iIndex = LBOUND(arrInfo) - 1
  133.     FOR iLoop = 1 TO iCount
  134.         iIndex = iIndex + 1
  135.         READ arrInfo(iIndex).c
  136.         ' INITIALIZED BELOW: arrInfo(iIndex).x = 0
  137.         ' INITIALIZED BELOW: arrInfo(iIndex).y = 0
  138.         ' INITIALIZED BELOW: arrInfo(iIndex).wheel = 127
  139.         arrInfo(iIndex).LeftDown = FALSE
  140.         arrInfo(iIndex).MiddleDown = FALSE
  141.         arrInfo(iIndex).RightDown = FALSE
  142.         arrInfo(iIndex).LeftCount = 0
  143.         arrInfo(iIndex).MiddleCount = 0
  144.         arrInfo(iIndex).RightCount = 0
  145.     NEXT iLoop
  146.  
  147.     ' INITIALIZE X COORDINATES
  148.     RESTORE XData
  149.     iIndex = LBOUND(arrInfo) - 1
  150.     FOR iLoop = 1 TO iCount
  151.         iIndex = iIndex + 1
  152.         READ arrInfo(iIndex).x
  153.     NEXT iLoop
  154.  
  155.     ' INITIALIZE Y COORDINATES
  156.     RESTORE YData
  157.     iIndex = LBOUND(arrInfo) - 1
  158.     FOR iLoop = 1 TO iCount
  159.         iIndex = iIndex + 1
  160.         READ arrInfo(iIndex).y
  161.     NEXT iLoop
  162.  
  163.     ' INITIALIZE SCROLL WHEEL
  164.     RESTORE WData
  165.     iIndex = LBOUND(arrInfo) - 1
  166.     FOR iLoop = 1 TO iCount
  167.         iIndex = iIndex + 1
  168.         READ arrInfo(iIndex).wheel
  169.     NEXT iLoop
  170.  
  171.     ' DRAW PLAYING FIELD
  172.     CLS ' clear screen
  173.     LOCATE 1, 1: PRINT "1. PLUG 1-8 MICE INTO THE COMPUTER"
  174.     LOCATE 2, 1: PRINT "2. USE MICE TO POSITION LETTERS ON SCREEN"
  175.     LOCATE 3, 1: PRINT "3. PRESS <ESC> TO QUIT"
  176.     LOCATE 4, 1: PRINT "--------------------------------------------------------------------------------";
  177.     LOCATE 5, 1: PRINT "#  X  Y  Wheel LeftDown MiddleDown RightDown LeftCount MiddleCount RightCount   "
  178.     LOCATE 6, 1: PRINT "--------------------------------------------------------------------------------";
  179.  
  180.     ' NOTE: LEAVE THE NEXT 8 LINES FREE (ROWS 8-15)
  181.     '       TO DISPLAY TEST VALUES FOR UPTO 8 MICE
  182.  
  183.     ' DRAW BORDER AROUND PLAYING FIELD
  184.     DrawTextLine cMinX - 1, cMinY - 1, cMinX - 1, cMaxY + 1, "#"
  185.     DrawTextLine cMinX - 1, cMinY - 1, cMaxX + 1, cMinY - 1, "#"
  186.     DrawTextLine cMaxX + 1, cMaxY + 1, cMaxX + 1, cMinY - 1, "#"
  187.     DrawTextLine cMaxX + 1, cMaxY + 1, cMinX - 1, cMaxY + 1, "#"
  188.  
  189.     ' GET INPUT AND MOVE PLAYERS
  190.     DO
  191.         iIndex = LBOUND(arrInfo) - 1
  192.         FOR iLoop = 1 TO iCount
  193.             iIndex = iIndex + 1
  194.  
  195.             ' ERASE CURSORS AT CURRENT POSITION
  196.             LOCATE arrInfo(iIndex).y, arrInfo(iIndex).x: PRINT " ";
  197.  
  198.             ' GET NEXT MOUSE INPUT
  199.             IF in$ = "1" THEN
  200.                 ReadMouse1 arrMouseID(iIndex), arrInfo(iIndex).x, arrInfo(iIndex).y, left%, middle%, right%, arrInfo(iIndex).wheel, cMinWheel, cMaxWheel
  201.             ELSEIF in$ = "2" THEN
  202.                 ReadMouse2 arrMouseID(iIndex), arrInfo(iIndex).x, arrInfo(iIndex).y, left%, middle%, right%, arrInfo(iIndex).wheel, cMinWheel, cMaxWheel
  203.             END IF
  204.  
  205.             ' HANDLE LEFT MOUSE BUTTON
  206.             IF left% THEN
  207.                 IF arrInfo(iIndex).LeftDown = FALSE THEN
  208.                     ' BUTTON DOWN EVENT
  209.                     arrInfo(iIndex).LeftDown = TRUE
  210.                     arrInfo(iIndex).LeftCount = arrInfo(iIndex).LeftCount + 1
  211.                 END IF
  212.             ELSE
  213.                 IF arrInfo(iIndex).LeftDown = TRUE THEN
  214.                     ' BUTTON UP EVENT
  215.                     arrInfo(iIndex).LeftDown = FALSE
  216.                 END IF
  217.             END IF
  218.  
  219.             ' HANDLE MIDDLE MOUSE BUTTON (SCROLL WHEEL BUTTON)
  220.             IF middle% THEN
  221.                 IF arrInfo(iIndex).MiddleDown = FALSE THEN
  222.                     ' BUTTON DOWN EVENT
  223.                     arrInfo(iIndex).MiddleDown = TRUE
  224.                     arrInfo(iIndex).MiddleCount = arrInfo(iIndex).MiddleCount + 1
  225.                 END IF
  226.             ELSE
  227.                 IF arrInfo(iIndex).MiddleDown = TRUE THEN
  228.                     ' BUTTON UP EVENT
  229.                     arrInfo(iIndex).MiddleDown = FALSE
  230.                 END IF
  231.             END IF
  232.  
  233.             ' HANDLE RIGHT MOUSE BUTTON
  234.             IF right% THEN
  235.                 IF arrInfo(iIndex).RightDown = FALSE THEN
  236.                     ' BUTTON DOWN EVENT
  237.                     arrInfo(iIndex).RightDown = TRUE
  238.                     arrInfo(iIndex).RightCount = arrInfo(iIndex).RightCount + 1
  239.                 END IF
  240.             ELSE
  241.                 IF arrInfo(iIndex).RightDown = TRUE THEN
  242.                     ' BUTTON UP EVENT
  243.                     arrInfo(iIndex).RightDown = FALSE
  244.                 END IF
  245.             END IF
  246.  
  247.             ' CHECK BOUNDARIES
  248.             IF arrInfo(iIndex).x < cMinX THEN arrInfo(iIndex).x = cMinX
  249.             IF arrInfo(iIndex).x > cMaxX THEN arrInfo(iIndex).x = cMaxX
  250.             IF arrInfo(iIndex).y < cMinY THEN arrInfo(iIndex).y = cMinY
  251.             IF arrInfo(iIndex).y > cMaxY THEN arrInfo(iIndex).y = cMaxY
  252.  
  253.             ' PLOT CURSOR
  254.             LOCATE arrInfo(iIndex).y, arrInfo(iIndex).x: PRINT arrInfo(iIndex).c;
  255.  
  256.             ' DISPLAY VARIABLES
  257.             iLen = 3: sCount = LEFT$(LTRIM$(RTRIM$(STR$(iLoop))) + STRING$(iLen, " "), iLen)
  258.             iLen = 3: sX = LEFT$(LTRIM$(RTRIM$(STR$(arrInfo(iIndex).x))) + STRING$(iLen, " "), iLen)
  259.             iLen = 3: sY = LEFT$(LTRIM$(RTRIM$(STR$(arrInfo(iIndex).y))) + STRING$(iLen, " "), iLen)
  260.             iLen = 6: sWheel = LEFT$(LTRIM$(RTRIM$(STR$(arrInfo(iIndex).wheel))) + STRING$(iLen, " "), iLen)
  261.             iLen = 9: sLeftDown = LEFT$(LTRIM$(RTRIM$(STR$(arrInfo(iIndex).LeftDown))) + STRING$(iLen, " "), iLen)
  262.             iLen = 11: sMiddleDown = LEFT$(LTRIM$(RTRIM$(STR$(arrInfo(iIndex).MiddleDown))) + STRING$(iLen, " "), iLen)
  263.             iLen = 10: sRightDown = LEFT$(LTRIM$(RTRIM$(STR$(arrInfo(iIndex).RightDown))) + STRING$(iLen, " "), iLen)
  264.             iLen = 10: sLeftCount = LEFT$(LTRIM$(RTRIM$(STR$(arrInfo(iIndex).LeftCount))) + STRING$(iLen, " "), iLen)
  265.             iLen = 12: sMiddleCount = LEFT$(LTRIM$(RTRIM$(STR$(arrInfo(iIndex).MiddleCount))) + STRING$(iLen, " "), iLen)
  266.             iLen = 11: sRightCount = LEFT$(LTRIM$(RTRIM$(STR$(arrInfo(iIndex).RightCount))) + STRING$(iLen, " "), iLen)
  267.  
  268.             'LOCATE 5,       1: PRINT "#  X  Y  Wheel LeftDown MiddleDown RightDown LeftCount MiddleCount RightCount   "
  269.             LOCATE 6 + iLoop, 1: PRINT sCount + sX + sY + sWheel + sLeftDown + sMiddleDown + sRightDown + sLeftCount + sMiddleCount + sRightCount
  270.  
  271.         NEXT iLoop
  272.  
  273.         _LIMIT 100 ' keep loop at 100 frames per second
  274.     LOOP UNTIL _KEYDOWN(27) ' escape key exit
  275.     _KEYCLEAR: '_DELAY 1
  276. END SUB ' MouseInputTest
  277.  
  278. ' /////////////////////////////////////////////////////////////////////////////
  279. ' Returns a count of # of mouse devices connected to the system
  280.  
  281. ' *** Currently hardcoded to 1 until we figure out how to do this. ***
  282.  
  283. FUNCTION GetMouseCount% ()
  284.     GetMouseCount% = 1
  285. END FUNCTION ' GetMouseCount%
  286.  
  287. ' /////////////////////////////////////////////////////////////////////////////
  288. ' Gets ID of each mouse device connected to the system (for now upto 8)
  289. ' and returns the IDs in an array of strings
  290. ' (assuming the ID is a string and not numeric?).
  291. ' If no mouse found, the ID will just be a blank string.
  292.  
  293. ' *** Currently hardcoded to "1" until we figure out how to do this. ***
  294.  
  295. SUB GetMouseIDs (arrMouseID( 8) AS STRING)
  296.     DIM iLoop AS INTEGER
  297.  
  298.     ' CLEAR OUT IDs
  299.     FOR iLoop = 1 TO 8
  300.         arrMouseID(iLoop) = ""
  301.     NEXT iLoop
  302.  
  303.     ' GET IDs
  304.     arrMouseID(1) = "1" ' for now just fudge it!
  305.  
  306. END SUB ' GetMouseCount%
  307.  
  308. ' /////////////////////////////////////////////////////////////////////////////
  309. ' Read mouse method #1, using _MOUSEX, _MOUSEY, etc.
  310.  
  311. ' Gets input from mouse identified by deviceid$
  312. ' (or does that needs to be an ordinal position?)
  313.  
  314. ' For version 1 we only return the input from the one mouse
  315. ' regardless of deviceid$.
  316.  
  317. ' NOTE: click events (mouse up/mouse down) are handled by the calling sub,
  318. '       this routine just sends back
  319. '       TRUE if the given button is currently down or FALSE if it is up.
  320.  
  321. ' Parameters (values returned):
  322. ' x% = x position of mouse pointer
  323. ' y% = y position of mouse pointer
  324. ' left% = current state of left mouse button (up or down)
  325. ' middle% = current state of middle mouse button / scroll wheel button (up or down)
  326. ' right% = current state of right mouse button (up or down)
  327. ' wheel% = value of mouse scroll wheel (passed in and incremented/decremented by 1 if wheel move detected)
  328.  
  329. ' Parameters (input only):
  330. ' wheelmin% = minimum value to allow wheel% to be decremented to
  331. ' wheelmax% = maximum value to allow wheel% to be incremened to
  332.  
  333. SUB ReadMouse1 (deviceid$, x%, y%, left%, middle%, right%, wheel%, wheelmin%, wheelmax%)
  334.     DIM scrollAmount%
  335.  
  336.     ' read scroll wheel
  337.     WHILE _MOUSEINPUT ' get latest mouse information
  338.         scrollAmount% = _MOUSEWHEEL ' (Returns -1 when scrolling up and 1 when scrolling down with 0 indicating no movement since last read.)
  339.         IF (scrollAmount% = -1) AND (wheel% > wheelmin%) THEN
  340.             wheel% = wheel% + scrollAmount%
  341.         ELSEIF (scrollAmount% = 1) AND (wheel% < wheelmax%) THEN
  342.             wheel% = wheel% + scrollAmount%
  343.         END IF
  344.     WEND
  345.  
  346.     ' read x position
  347.     x% = _MOUSEX
  348.  
  349.     ' read y position
  350.     y% = _MOUSEY
  351.  
  352.     ' read mouse buttons
  353.     left% = _MOUSEBUTTON(1)
  354.     middle% = _MOUSEBUTTON(3)
  355.     right% = _MOUSEBUTTON(2)
  356.  
  357. END SUB ' ReadMouse1
  358.  
  359. ' /////////////////////////////////////////////////////////////////////////////
  360. ' Read mouse method #2, using _DEVICE commands.
  361.  
  362. ' Gets input from mouse identified by deviceid$
  363. ' (or does that needs to be an ordinal position?)
  364.  
  365. ' For version 1 we only return the input from the one mouse
  366. ' regardless of deviceid$.
  367.  
  368. ' NOTE: click events (mouse up/mouse down) are handled by the calling sub,
  369. '       this routine just sends back
  370. '       TRUE if the given button is currently down or FALSE if it is up.
  371.  
  372. ' Parameters (values returned):
  373. ' x% = x position of mouse pointer
  374. ' y% = y position of mouse pointer
  375. ' left% = current state of left mouse button (up or down)
  376. ' middle% = current state of middle mouse button / scroll wheel button (up or down)
  377. ' right% = current state of right mouse button (up or down)
  378. ' wheel% = value of mouse scroll wheel (passed in and incremented/decremented by 1 if wheel move detected)
  379.  
  380. ' Parameters (input only):
  381. ' wheelmin% = minimum value to allow wheel% to be decremented to
  382. ' wheelmax% = maximum value to allow wheel% to be incremened to
  383.  
  384. SUB ReadMouse2 (deviceid$, x%, y%, left%, middle%, right%, wheel%, wheelmin%, wheelmax%)
  385.     DIM scrollAmount%
  386.     DIM ScreenWidth% ' screen width
  387.     DIM ScreenHeight% ' screen height
  388.  
  389.     ' read scroll wheel
  390.     WHILE _DEVICEINPUT(2) ' clear and update the mouse buffer
  391.         scrollAmount% = _WHEEL(3)
  392.         IF (scrollAmount% = -1) AND (wheel% > wheelmin%) THEN
  393.             wheel% = wheel% + scrollAmount%
  394.         ELSEIF (scrollAmount% = 1) AND (wheel% < wheelmax%) THEN
  395.             wheel% = wheel% + scrollAmount%
  396.         END IF
  397.     WEND ' clear and update the mouse buffer
  398.  
  399.     ' read x position
  400.     ScreenWidth% = _WIDTH \ 2
  401.     x% = _AXIS(1) * ScreenWidth% + ScreenWidth%
  402.  
  403.     ' read y position
  404.     ScreenHeight% = _HEIGHT \ 2
  405.     y% = _AXIS(2) * ScreenHeight% + ScreenHeight%
  406.  
  407.     ' read mouse buttons
  408.     left% = _BUTTON(1)
  409.     middle% = _BUTTON(2)
  410.     right% = _BUTTON(3)
  411.  
  412. END SUB ' ReadMouse2
  413.  
  414. ' /////////////////////////////////////////////////////////////////////////////
  415. ' ORIGINAL VERSION OF FUNCTION FOR READING MOUSE WITH _DEVICE:
  416.  
  417. ' Gets mouse input using _DEVICE commands (part 2 of 2, subroutine)
  418.  
  419. ' SOURCE : https://www.qb64.org/forum/index.php?topic=1087.0
  420. ' Subject: Mouse demo using _DEVICE commands
  421. ' From   : SMcNeill
  422. ' Date   : February 21, 2019, 06:15:28 AM »
  423.  
  424. SUB UpdateMouseInfo (MouseX AS INTEGER, MouseY AS INTEGER, MOUSEWHEEL AS INTEGER, LeftMouse AS INTEGER, RightMouse AS INTEGER, MiddleMouse AS INTEGER, ClickThreshold AS SINGLE)
  425.     DIM SW AS INTEGER, SH AS INTEGER
  426.     DIM LM AS INTEGER, MM AS INTEGER, RM AS INTEGER
  427.  
  428.     STATIC leftdown AS SINGLE, middledown AS SINGLE, rightdown AS SINGLE
  429.  
  430.     WHILE _DEVICEINPUT(2): MOUSEWHEEL = MOUSEWHEEL + _WHEEL(3): WEND 'clear and update the mouse buffer
  431.  
  432.     SW = _WIDTH \ 2: SH = _HEIGHT \ 2
  433.     MouseX = _AXIS(1) * SW + SW: MouseY = _AXIS(2) * SH + SH
  434.  
  435.  
  436.  
  437.     LM = _BUTTON(1): MM = _BUTTON(2): RM = _BUTTON(3)
  438.  
  439.     IF leftdown THEN 'if it was down
  440.         IF LM = 0 THEN 'and is now up
  441.             IF TIMER - leftdown < ClickThreshold THEN
  442.                 LeftMouse = 2 'clicked
  443.             ELSE 'if it's still down
  444.                 LeftMouse = 0 'the mouse was just released
  445.             END IF
  446.             leftdown = 0 'timer is cleared either way
  447.         ELSE
  448.             LeftMouse = 1 'the left mouse is down , timer should have already been set
  449.         END IF
  450.     ELSE
  451.         IF LM THEN
  452.             leftdown = TIMER 'set the timer to see if we have click or hold events
  453.             LeftMouse = 1 'the left mouse is down
  454.         ELSE
  455.             LeftMouse = 0
  456.         END IF
  457.     END IF
  458.  
  459.     IF middledown THEN 'if it was down
  460.         IF MM = 0 THEN 'and is now up
  461.             IF TIMER - middledown < ClickThreshold THEN
  462.                 MiddleMouse = 2 'clicked
  463.             ELSE 'if it's still down
  464.                 MiddleMouse = 0 'the mouse was just released
  465.             END IF
  466.             middledown = 0 'timer is cleared either way
  467.         ELSE
  468.             MiddleMouse = 1 'the middle mouse is down , timer should have already been set
  469.         END IF
  470.     ELSE
  471.         IF MM THEN
  472.             middledown = TIMER 'set the timer to see if we have click or hold events
  473.             MiddleMouse = 1 'the middle mouse is down
  474.         ELSE
  475.             MiddleMouse = 0
  476.         END IF
  477.     END IF
  478.  
  479.     IF rightdown THEN 'if it was down
  480.         IF RM = 0 THEN 'and is now up
  481.             IF TIMER - rightdown < ClickThreshold THEN
  482.                 RightMouse = 2 'clicked
  483.             ELSE 'if it's still down
  484.                 RightMouse = 0 'the mouse was just released
  485.             END IF
  486.             rightdown = 0 'timer is cleared either way
  487.         ELSE
  488.             RightMouse = 1 'the right mouse is down , timer should have already been set
  489.         END IF
  490.     ELSE
  491.         IF RM THEN
  492.             rightdown = TIMER 'set the timer to see if we have click or hold events
  493.             RightMouse = 1 'the right mouse is down
  494.         ELSE
  495.             RightMouse = 0
  496.         END IF
  497.     END IF
  498.  
  499.  
  500. END SUB ' UpdateMouseInfo
  501.  
  502. ' /////////////////////////////////////////////////////////////////////////////
  503. ' Example: Checking for the system's input devices.
  504.  
  505. ' _DEVICES FUNCTION (QB64 REFERENCE)
  506. ' http://www.[abandoned, outdated and now likely malicious qb64 dot net website - don’t go there]/wiki/index_title_DEVICES/
  507. '
  508. ' The _DEVICES function returns the number of INPUT devices on your computer
  509. ' including keyboard, mouse and game devices.
  510. '
  511. ' Syntax:
  512. '
  513. ' device_count% = _DEVICES
  514. '
  515. ' Returns the number of devices that can be listed separately with the _DEVICE$
  516. ' function by the device number.
  517. ' Devices include keyboard, mouse, joysticks, game pads and multiple stick game
  518. ' controllers.
  519. ' Note: This function MUST be read before trying to use the _DEVICE$,
  520. ' _DEVICEINPUT or _LAST control functions!
  521.  
  522. ' Note: The STRIG/STICK commands won't read from the keyboard
  523. '       or mouse device the above example lists.
  524.  
  525. SUB EnumerateDevices
  526.     DIM devices%
  527.     DIM iLoop%
  528.     DIM sCount$
  529.     DIM iLen AS INTEGER
  530.  
  531.     devices% = _DEVICES ' MUST be read in order for other 2 device functions to work!
  532.  
  533.     CLS
  534.     PRINT "Total devices found: "; STR$(devices%)
  535.     FOR iLoop% = 1 TO devices%
  536.         iLen = 4
  537.         sCount$ = LEFT$(LTRIM$(RTRIM$(STR$(iLoop%))) + STRING$(iLen, " "), iLen)
  538.         PRINT sCount$ + _DEVICE$(iLoop%) + " (" + LTRIM$(RTRIM$(STR$(_LASTBUTTON(iLoop%)))) + " buttons)"
  539.     NEXT iLoop%
  540.     PRINT
  541.     PRINT "PRESS <ESC> TO CONTINUE"
  542.     DO: LOOP UNTIL _KEYDOWN(27) ' leave loop when ESC key pressed
  543.     _KEYCLEAR: '_DELAY 1
  544.  
  545. END SUB ' EnumerateDevices
  546.  
  547. ' /////////////////////////////////////////////////////////////////////////////
  548. ' based on code from:
  549. ' Qbasic Programs - Download free bas source code
  550. ' http://www.thedubber.altervista.org/qbsrc.htm
  551.  
  552. SUB DrawTextLine (y%, x%, y2%, x2%, c$)
  553.     'bError% = FALSE
  554.     'LOCATE 2, 2: PRINT "(" + STR$(x%) + "," + STR$(y%) + ") to (" + STR$(x2%) + "," + STR$(y2%) + ") of " + CHR$(34) + c$ + CHR$(34);
  555.  
  556.     i% = 0: steep% = 0: e% = 0
  557.     IF (x2% - x%) > 0 THEN sx% = 1: ELSE sx% = -1
  558.     dx% = ABS(x2% - x%)
  559.     IF (y2% - y%) > 0 THEN sy% = 1: ELSE sy% = -1
  560.     dy% = ABS(y2% - y%)
  561.     IF (dy% > dx%) THEN
  562.         steep% = 1
  563.         SWAP x%, y%
  564.         SWAP dx%, dy%
  565.         SWAP sx%, sy%
  566.     END IF
  567.     e% = 2 * dy% - dx%
  568.     FOR i% = 0 TO dx% - 1
  569.         IF steep% = 1 THEN
  570.             'PSET (y%, x%), c%:
  571.             LOCATE y%, x%
  572.             PRINT c$;
  573.         ELSE
  574.             'PSET (x%, y%), c%
  575.             LOCATE x%, y%
  576.             PRINT c$;
  577.         END IF
  578.  
  579.         WHILE e% >= 0
  580.             y% = y% + sy%: e% = e% - 2 * dx%
  581.         WEND
  582.         x% = x% + sx%: e% = e% + 2 * dy%
  583.     NEXT
  584.     'PSET (x2%, y2%), c%
  585.     LOCATE x2%, y2%
  586.     PRINT c$;
  587.  
  588. END SUB ' DrawTextLine
  589.  
« Last Edit: December 09, 2020, 03:24:00 pm by madscijr »

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • GitHub
Re: reading multiple mice input with _DEVICES and _DEVICE$, or RawInput ?
« Reply #1 on: December 09, 2020, 03:30:24 pm »
Regarding your reply on my HID post:

I think that using HID could solve your problem very nicely if we knew the correct packet info for the mice. Gaming mice might respond differently (and they should as they typically have more buttons / features) and a wired might be different from a wireless. The main reason I went for discussing Nintendo Switch controllers is because it's the only peripheral I have with enough documentation available so that I can make a test. It required a DLL because that's just how that library is. A regular mouse will be hard to find information on concerning packet size and interpretation of the packet returns. You are most likely better off sticking to _DEVICES and letting the magic take place there.
« Last Edit: December 09, 2020, 03:58:00 pm by SpriggsySpriggs »
Shuwatch!

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
Re: reading multiple mice input with _DEVICES and _DEVICE$, or RawInput ?
« Reply #2 on: December 09, 2020, 03:57:07 pm »
You are most likely better off sticking to _DEVICES and letting the magic take place there.

Thanks for your reply. I would prefer to use _DEVICES or whatever is simple both for the programmer and user.
The idea would be something "plug and play" where the user just has to plug in a USB mouse.
If they had to go looking for manufacturer documentation for packet information, that would kind of defeat the purpose.

Although maybe a simple "autodetect your mouse" setup utility wouldn't make it too hard on the user. Would there be any way to get QB64 to autodetect the settings for each mouse?

Any idea how to get _DEVICES to recognize >1 mice as separate devices?
Currently I have 2 Microsoft optical USB wheel mice plugged in, and the code
Code: QB64: [Select]
  1. DIM devices%
  2. DIM iLoop%
  3. devices% = _DEVICES
  4. PRINT "# input devices found ="; STR$(devices%)
  5. FOR iLoop% = 1 TO devices%
  6.     PRINT _DEVICE$(iLoop%)
  7.     PRINT "Buttons:"; _LASTBUTTON(iLoop%)
  8. NEXT iLoop%
  9. PRINT "PRESS <ESC>" : DO : LOOP UNTIL _KEYDOWN(27) : _KEYCLEAR
  10.  
Outputs:
Code: [Select]
# input devices found = 2;
[KEYBOARD][BUTTON]; Buttons: 512;
[MOUSE][BUTTON][AXIS][WHEEL]; Buttons: 3

Any idea about RawInput? Would that require manufacturer info, etc.?

Thanks again...

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • GitHub
Re: reading multiple mice input with _DEVICES and _DEVICE$, or RawInput ?
« Reply #3 on: December 09, 2020, 04:00:18 pm »
I've not looked at RawInput before but I will gladly do so for you tonight. I won't be able to test two mice since I only have one but I'll try making code and then letting you test it with your two mice if it seems to work. This article looks like it has what we need: https://asawicki.info/news_1533_handling_multiple_mice_with_raw_input
« Last Edit: December 09, 2020, 04:03:34 pm by SpriggsySpriggs »
Shuwatch!

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
Re: reading multiple mice input with _DEVICES and _DEVICE$, or RawInput ?
« Reply #4 on: December 09, 2020, 04:07:32 pm »
I havn't seen that article - thanks for sharing the link.
Any help with this would be most appreciated!
Thanks again...

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • Steve’s QB64 Archive Forum
Re: reading multiple mice input with _DEVICES and _DEVICE$, or RawInput ?
« Reply #5 on: December 09, 2020, 04:09:26 pm »
I was hoping to run two mice and two keyboards in Windows, in the past, so I could basically turn one PC into two, but it never would work.  Windows itself tends to just wrap the two keyboards up into one input event (like with the on-screen virtual keyboard and your physical keyboard -- they both are active and affect the same program at the same time, with it being impossible to assign one to one app and the other to a different one), so I never could do what I wanted to do.

It's a shame -- today's PCs are 100 times more powerful than the ones we used to have back in the 80s, so there's no reason why one PC couldn't have 2 monitors, 2 keyboards, and 2 mice, and allow 2 different people to run 2 different programs at the same time.  No reason except the obvious one -- companies make money by selling 2 PCs to a a family, rather than selling one with a few cheap accessories on the side.

I'm hoping you'll be able to sort out what you're wanting to do here, but I'm not going to hold my breath until it occurs.  ;D
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
Re: reading multiple mice input with _DEVICES and _DEVICE$, or RawInput ?
« Reply #6 on: December 09, 2020, 06:24:19 pm »
I'm hoping you'll be able to sort out what you're wanting to do here, but I'm not going to hold my breath until it occurs.  ;D

Heh, thanks. I think what I'm looking to do is a little simpler because it's just the one program actively _looking_ for input from those other devices. What you're talking about sounds more like hacking the OS.

It's a shame -- today's PCs are 100 times more powerful than the ones we used to have back in the 80s, so there's no reason why one PC couldn't have 2 monitors, 2 keyboards, and 2 mice, and allow 2 different people to run 2 different programs at the same time.

Not just the 80s - we have had time sharing systems that let people do that since the late '50s! ! !
The difference (I think?) is, your user would just need a keyboard/mouse, whereas with those time sharing systems, the users had a "dumb terminal" with its own display, communicating with the computer via some sort of network connection.
Following that model, perhaps one could build a simple hardware "client" with a Raspberry Pi or Arduino, that a keyboard/mouse plugs into, and sens the input to the target PC over a network/wifi connection? We would still need to have a listener on the target system to receive the input and "distribute" it out to the target application(s).

No reason except the obvious one -- companies make money by selling 2 PCs to a a family, rather than selling one with a few cheap accessories on the side.
Always the problem of commerce getting in the way of progress! LOL
But with open source and lots of smart people interested in solving problems, it might be possible to find a way around this?

I was hoping to run two mice and two keyboards in Windows, in the past, so I could basically turn one PC into two, but it never would work.  Windows itself tends to just wrap the two keyboards up into one input event (like with the on-screen virtual keyboard and your physical keyboard -- they both are active and affect the same program at the same time, with it being impossible to assign one to one app and the other to a different one), so I never could do what I wanted to do.

That's an interesting idea, and still might be possible. Initially I thought that maybe this would require some real low level operating system hacking - one could probably do it in an open source OS like Linux, where there is a community of knowledgable hobbyist developers who would find that a worthwhile project to jump into and lend a hand.

However on further thought, maybe it could be done from an app, without having to modify the OS. Assuming we have figured out how to read separate input from multiple keyboards/mice using RawInput or whatever (hopefully the Raspberry Pi mini dumb terminal wouldn't be necessary!), the problem is how to route that input to the programs. It sounds like you are talking about controlling pretty much any application, not just a program written with custom input routines to look for input from a second keyboard/mouse/etc.

In that case, we would have a central "input collector and redistributor" program that would read the input from the various keyboards/mice, that is configured to route that input from a given keyboard/mouse pair to a given application (or a given display).

Perhaps if we had the process ID (or the window handle) of the target program to route the input to, we could temporarily freeze system screen updates and events (kind of like in Office VBA where you can do Application.EnableEvents = False and Application.ScreenUpdating = False, but for Windows in general), then switch focus to the process ID of the target application, and do a SendKeys type of operation to send the keyboard input to that app. And basically cycle through all the keyboards and target apps, before re-enabling screen updates and events? Our app would also need to draw a pseudo-cursor to track wherever the given mouse is guided.

For the process ID, a while back I wrote a primative process explorer (in HTA/vbscript) to get task info and kill tasks, etc. so I know it's pretty easy to get the process IDs. Below is a script ripped out of that, that gets all the process IDs (and other info) in Windows and dumps them to the console. If you can figure out how to switch focus to an app based on process ID and do a sendkeys that could be a start...

I don't know how we would handle the mouse, maybe there's a mouse equivalent to "sendkeys"?

PS Thinking further, I'm thinking about how the cursor might change from a regular pointer to an "I" beam, etc., depending on where in an application you mouse over.
This behavior can be programmed in the application's logic (I've seen this stuff in VB and I think even JavaScript for Web pages) but it might also be tied to the OS, where it changes the cursor's appearance when the user mouses over a certain standard GUI element (like displaying the "i" beam when mousing over a textbox, for instance).
So this leads me to think that instead of a pseudo-cursor, what we need is more of an "interrupt" that, for the nanosecond or millisecond or whatever that our program devotes to a given mouse and its associated application (or display), it would relocate the _system_ mouse to that part of the screen, and "forward" any click events to look like it came from the "system" mouse, thus triggering an "official" mouse-click event on the target display/application.
So basically we are doing a time-sliced mouse/keyboard interrupt, injecting the _individual_ mouse/keyboard input into the system buffer, and hopefully fast enough to where there isn't any noticable lag or flicker.
(Perhaps our program can detect the type of system mouse pointer after it's repositioned (is it an arrow, an "i" beam, an hourglass, etc.?) to use for a "pseudo-pointer" which would be drawn in the same location, to prevent screen flicker?)

Note: In my research for the multiple mice, I came across a couple of products/projects out there that do multiple mouse with multiple mouse pointers - check out

Well, if anything that was a fun thought experiment.
Hopefully something in there can help, or at least provide a good laugh :-D

Code: Text: [Select]
  1. 'RUN WITH:
  2. 'Cscript.exe DumpWindowsProcesses.vbs
  3.  
  4. DumpWindowsProcesses
  5.  
  6. sub DumpWindowsProcesses()
  7.         dim RoutineName :: RoutineName = "DumpWindowsProcesses"
  8.         dim oWMI
  9.         dim oProcesses
  10.         dim oProcess
  11.         dim sQuery
  12.         dim iCount
  13.        
  14.         On Error Resume Next
  15.        
  16.         sQuery = "select * from win32_process"
  17.        
  18.         Set oWMI = GetObject("winmgmts://./root/cimv2")
  19.         Set oProcesses = oWMI.execQuery(sQuery)
  20.        
  21.         if err.number = 0 then
  22.                 iCount = 0
  23.                 For Each oProcess In oProcesses
  24.                         iCount = iCount + 1
  25.                        
  26.                         wscript.echo "-------------------------------------------------------------------------------"
  27.                         wscript.echo "Process: " & cstr(iCount)
  28.                        
  29.                         wscript.echo "ProcessId: " & cstr(oProcess.ProcessId)
  30.                         if err.number <> 0 then err.clear
  31.                        
  32.                         wscript.echo "Caption: " & oProcess.Caption
  33.                         if err.number <> 0 then err.clear
  34.                         wscript.echo "CommandLine: " & oProcess.CommandLine
  35.                         if err.number <> 0 then err.clear
  36.                         wscript.echo "CreationClassName: " & oProcess.CreationClassName
  37.                         if err.number <> 0 then err.clear
  38.                         wscript.echo "CreationDate: " & cstr(oProcess.CreationDate)
  39.                         if err.number <> 0 then err.clear
  40.                         wscript.echo "CSCreationClassName: " & oProcess.CSCreationClassName
  41.                         if err.number <> 0 then err.clear
  42.                         wscript.echo "CSName: " & oProcess.CSName
  43.                         if err.number <> 0 then err.clear
  44.                         wscript.echo "Description: " & oProcess.Description
  45.                         if err.number <> 0 then err.clear
  46.                         wscript.echo "ExecutablePath: " & oProcess.ExecutablePath
  47.                         if err.number <> 0 then err.clear
  48.                         wscript.echo "ExecutionState: " & cstr(oProcess.ExecutionState)
  49.                         if err.number <> 0 then err.clear
  50.                         wscript.echo "Handle: " & oProcess.Handle
  51.                         if err.number <> 0 then err.clear
  52.                         wscript.echo "HandleCount: " & cstr(oProcess.HandleCount)
  53.                         if err.number <> 0 then err.clear
  54.                         wscript.echo "InstallDate: " & cstr(oProcess.InstallDate)
  55.                         if err.number <> 0 then err.clear
  56.                         wscript.echo "KernelModeTime: " & cstr(oProcess.KernelModeTime)
  57.                         if err.number <> 0 then err.clear
  58.                         wscript.echo "MaximumWorkingSetSize: " & cstr(oProcess.MaximumWorkingSetSize)
  59.                         if err.number <> 0 then err.clear
  60.                         wscript.echo "MinimumWorkingSetSize: " & cstr(oProcess.MinimumWorkingSetSize)
  61.                         if err.number <> 0 then err.clear
  62.                         wscript.echo "Name: " & oProcess.Name
  63.                         if err.number <> 0 then err.clear
  64.                         wscript.echo "OSCreationClassName: " & oProcess.OSCreationClassName
  65.                         if err.number <> 0 then err.clear
  66.                         wscript.echo "OSName: " & oProcess.OSName
  67.                         if err.number <> 0 then err.clear
  68.                         wscript.echo "OtherOperationCount: " & cstr(oProcess.OtherOperationCount)
  69.                         if err.number <> 0 then err.clear
  70.                         wscript.echo "OtherTransferCount: " & cstr(oProcess.OtherTransferCount)
  71.                         if err.number <> 0 then err.clear
  72.                         wscript.echo "PageFaults: " & cstr(oProcess.PageFaults)
  73.                         if err.number <> 0 then err.clear
  74.                         wscript.echo "PageFileUsage: " & cstr(oProcess.PageFileUsage)
  75.                         if err.number <> 0 then err.clear
  76.                         wscript.echo "ParentProcessId: " & cstr(oProcess.ParentProcessId)
  77.                         if err.number <> 0 then err.clear
  78.                         wscript.echo "PeakPageFileUsage: " & cstr(oProcess.PeakPageFileUsage)
  79.                         if err.number <> 0 then err.clear
  80.                         wscript.echo "PeakVirtualSize: " & cstr(oProcess.PeakVirtualSize)
  81.                         if err.number <> 0 then err.clear
  82.                         wscript.echo "PeakWorkingSetSize: " & cstr(oProcess.PeakWorkingSetSize)
  83.                         if err.number <> 0 then err.clear
  84.                         wscript.echo "Priority: " & cstr(oProcess.Priority)
  85.                         if err.number <> 0 then err.clear
  86.                         wscript.echo "PrivatePageCount: " & cstr(oProcess.PrivatePageCount)
  87.                         if err.number <> 0 then err.clear
  88.                         wscript.echo "QuotaNonPagedPoolUsage: " & cstr(oProcess.QuotaNonPagedPoolUsage)
  89.                         if err.number <> 0 then err.clear
  90.                         wscript.echo "QuotaPagedPoolUsage: " & cstr(oProcess.QuotaPagedPoolUsage)
  91.                         if err.number <> 0 then err.clear
  92.                         wscript.echo "QuotaPeakNonPagedPoolUsage: " & cstr(oProcess.QuotaPeakNonPagedPoolUsage)
  93.                         if err.number <> 0 then err.clear
  94.                         wscript.echo "QuotaPeakPagedPoolUsage: " & cstr(oProcess.QuotaPeakPagedPoolUsage)
  95.                         if err.number <> 0 then err.clear
  96.                         wscript.echo "ReadOperationCount: " & cstr(oProcess.ReadOperationCount)
  97.                         if err.number <> 0 then err.clear
  98.                         wscript.echo "ReadTransferCount: " & cstr(oProcess.ReadTransferCount)
  99.                         if err.number <> 0 then err.clear
  100.                         wscript.echo "SessionId: " & cstr(oProcess.SessionId)
  101.                         if err.number <> 0 then err.clear
  102.                         wscript.echo "Status: " & oProcess.Status
  103.                         if err.number <> 0 then err.clear
  104.                         wscript.echo "TerminationDate: " & cstr(oProcess.TerminationDate)
  105.                         if err.number <> 0 then err.clear
  106.                         wscript.echo "ThreadCount: " & cstr(oProcess.ThreadCount)
  107.                         if err.number <> 0 then err.clear
  108.                         wscript.echo "UserModeTime: " & cstr(oProcess.UserModeTime)
  109.                         if err.number <> 0 then err.clear
  110.                         wscript.echo "VirtualSize: " & cstr(oProcess.VirtualSize)
  111.                         if err.number <> 0 then err.clear
  112.                         wscript.echo "WindowsVersion: " & oProcess.WindowsVersion
  113.                         if err.number <> 0 then err.clear
  114.                         wscript.echo "WorkingSetSize: " & cstr(oProcess.WorkingSetSize)
  115.                         if err.number <> 0 then err.clear
  116.                         wscript.echo "WriteOperationCount: " & cstr(oProcess.WriteOperationCount)
  117.                         if err.number <> 0 then err.clear
  118.                         wscript.echo "WriteTransferCount: " & cstr(oProcess.WriteTransferCount)
  119.                         if err.number <> 0 then err.clear
  120.                        
  121.                         wscript.echo ""
  122.                        
  123.                 Next ' oProcess
  124.                
  125.                 wscript.echo "-------------------------------------------------------------------------------"
  126.                 wscript.echo "Found " & cstr(iCount) & " processes."
  127.         else
  128.                 wscript.echo "Error #" & cstr(err.number) & ": " & err.message
  129.                 Err.Clear
  130.         end if
  131.        
  132.         on error goto 0
  133.        
  134.         wscript.echo RoutineName & " finished at " & cstr(now) & "."
  135.        
  136.         Set oWMI = Nothing
  137.         Set oProcesses = Nothing
  138.         Set oProcess = Nothing
  139. end sub ' DumpWindowsProcesses
  140.  

« Last Edit: December 10, 2020, 12:03:05 pm by madscijr »

Offline TempodiBasic

  • Forum Resident
  • Posts: 1792
Re: reading multiple mice input with _DEVICES and _DEVICE$, or RawInput ?
« Reply #7 on: December 11, 2020, 07:07:27 pm »
Hi madscijr
use multiple mice in the same PC as different devices...

it is an original idea.... but the OS let you see the different mice?
I use a notebook with a pad for the primary mouse, but often I like to use an USB mouse to use the mousewheel.
In this setting we have 2 different input devices (pad and USB mouse) but the OS showes me only one cursor on the screen. If I move USB mouse that cursor moves, if I move my finger on the pad OS moves the same cursor.
So it seems that you got from OS the redirection of the input of these 2 different devices into the mouse entity of the OS.
To change the aspect of the mouse I remember an example posted on the [abandoned, outdated and now likely malicious qb64 dot net website - don’t go there] using FREEGLUT command.
If you want to change the aspect of mouse in QB64, you can do this task manually using images  and shouting down the OS image of cursor of mouse.
You can use the same QB64 and the QBasic keyword like INP http://qb64.org/wiki/INP OUT http://qb64.org/wiki/OUT  PEEKhttp://qb64.org/wiki/PEEK POKE http://qb64.org/wiki/POKE  and _MEMhttp://qb64.org/wiki/MEMGET_(function) to access to the different port of connection with mice.  But you must have their connection protocol to distinguish the different USB mice connected to the PC.
So it seems that you need to build a kind of driver for have more cursors on the screen.
Good Luck!
Programming isn't difficult, only it's  consuming time and coffee

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
Re: reading multiple mice input with _DEVICES and _DEVICE$, or RawInput ?
« Reply #8 on: December 16, 2020, 03:28:50 pm »
I've not looked at RawInput before but I will gladly do so for you tonight. I won't be able to test two mice since I only have one but I'll try making code and then letting you test it with your two mice if it seems to work. This article looks like it has what we need: https://asawicki.info/news_1533_handling_multiple_mice_with_raw_input

Did you ever get any of that working?
It looks like all those examples are C or C++.
Would one need to take that code and use it to create a "wrapper" Windows library in C/C++ that can be used from QB64?
I don't really know C/C+ and that looks like a bunch of gibberish, but I might be foolish enough to try...
No idea how to make a C program or DLL in Windows 10 , but it might be worth a shot...
(Unless someone else wants to step in and be a hero! Lol)
Otherwise, any info or advice on how to get started in that would be appreciated:
  • How do you write a Windows library that can be used from QB64?
  • What compiler/IDE is best (free, easy, reliable), etc. for this purpose?
  • How do you reference and call it from within a QB64 program?
  • Etc.
« Last Edit: December 16, 2020, 03:34:46 pm by madscijr »

Offline TempodiBasic

  • Forum Resident
  • Posts: 1792
Re: reading multiple mice input with _DEVICES and _DEVICE$, or RawInput ?
« Reply #9 on: December 17, 2020, 05:00:21 pm »
Hi
it seems that it is possible, EitherMouse does already  this https://www.eithermouse.com
moreover Teamplayer seems to let use more mice and more keyboards on the same pc https://teamplayer.it.uptodown.com/windows  and Pluralinput https://pluralinput.com/index-old.html also if this last seems in development.
If you are interested to use multiple input devices (mice, keyboards and a twofinger on touchpad of notebook) see here this resource on github  https://github.com/nesherhh/MultiTouchVista and video demo https://www.youtube.com/watch?v=_maaH1gecxw
moreover a BasicLike script for windows seems to work see here https://www.autoitscript.com/forum/topic/39571-two-mice-two-cursors-in-autoit/
and read here
Quote
Keyboard pairing means linking a keyboard to a mouse – together they represent a (virtual) user. Pairing is simple: move a mouse and hit a key on the keyboard that you want to pair. You will see both the mouse and keyboard light up orange in the row of your user number. Working with paired keyboards may feel unintuitive at first; here’s a use case to explain why. User A and B sit behind the same computer, there are two mice attached and one single keyboard. Both users are interacting with the desktop with their own mouse. User A pairs his mouse to the keyboard. User A clicks on notepad and starts typing. User B takes over and types on the same keyboard. Now user B clicks on some other window and expects to start typing in that window – but since the keyboard is paired with user A, and user A did not click on any other window user B will keep typing in notepad! Practically speaking; user B does not have a keyboard – he/she just follows what the focus window is of user A. This may feel unintuitive because generally you expect to be able to type in a window once you click on it. For this scenario we added an option to synchronize all keyboards (even if it’s just one) to all users – that means mice can operate independently but the keyboard is shared for all users. You can uncheck the Multi Keyboard option in the main UI to disable per-user keyboard pairing – when this option is switched off the keyboard(s) will work just like in normal windows – follow the window with focus to send key data to. Of course, if you have two keyboards and two mice and user A and B are both working on the desktop we suggest to keep the Multi Keyboard option checked and pair each keyboards to an individual mouse so that both A and B can work independently with each a mouse and keyboard.
  taken form https://www.mousemux.com

and read this https://social.msdn.microsoft.com/Forums/en-US/0e257f53-c3ff-43f8-b7f2-835e1db207d0/how-to-have-multi-cursors-in-windows?forum=csharpgeneral it seems that there is a Windows SDK to manage multimouse!

But the question is Do you want to rely on windows version your results? Or can you  (by using QB64 )  get a more direct way to manage the input from USB ports?

Programming isn't difficult, only it's  consuming time and coffee

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
Re: reading multiple mice input with _DEVICES and _DEVICE$, or RawInput ?
« Reply #10 on: December 17, 2020, 06:05:26 pm »
But the question is Do you want to rely on windows version your results?
Or can you  (by using QB64 )  get a more direct way to manage the input from USB ports?

Thank you for all the information!

Well, if it can be done in pure QB64, that would be great. I am a novice in QB4,
but I think someone with more experience might be able to accomplish it?

I would just be happy to get it working without having to install any special drivers or services,
or relying on a 3rd party product/binary (such as mousemux, pluralinput, eithermouse, teamplayer, etc.)

I would like to avoid running or installing any binary that I didn't compile myself
(and would expect other users to feel the same way!)

So the best solution would be to get it done in plain QB64 source code.

However, if the raw input functionality is beyond the capability of QB64, and writing C/C++ code is necessary, here is a thought/question:
Since QB64 first generates C or C++ code and then compiles that, I am guessing it has a built-in C/C++ compiler out of the box?
Would it be possible to include the raw input routines as "inline" C/C++ source code, and have QB64 compile it along with the BASIC code?
(Basically I'd perfer to get around requiring the user to install some separate binary, or compile c/c++ code themself.
For me the ideal scenario would be for it to work as a "type in" program.)

Also, there is the cross-platform issue:
I have read that Mac and Linux can also support multiple mice,
and would *prefer* any programs I write for multi mice to work in other operating systems
without too much work other than substituting a some different source code.
However I would be happy if I can just get it working in QB64 program under Windows.

The link you provided https://jstookey.com/multiple-mice-raw-input/
has examples in C, # and C++, which is what I would need to understand and try it out.
However that page is from 2003, so we will have to see if any of that will work in Windows 10, 17 years later! :-D

Thanks again for all the information.

« Last Edit: December 17, 2020, 06:13:53 pm by madscijr »

Offline TempodiBasic

  • Forum Resident
  • Posts: 1792
Re: reading multiple mice input with _DEVICES and _DEVICE$, or RawInput ?
« Reply #11 on: December 18, 2020, 09:13:29 pm »
I suggest you to search into this forum: including external library, using C code
see here http://www.qb64.org/wiki/C_Libraries
Programming isn't difficult, only it's  consuming time and coffee

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
Re: reading multiple mice input with _DEVICES and _DEVICE$, or RawInput ?
« Reply #12 on: December 22, 2020, 06:46:07 pm »
I suggest you to search into this forum: including external library, using C code
see here http://www.qb64.org/wiki/C_Libraries

Thanks, I'll give that a look...

Offline Cobalt

  • QB64 Developer
  • Forum Resident
  • Posts: 878
  • At 60 I become highly radioactive!
Re: reading multiple mice input with _DEVICES and _DEVICE$, or RawInput ?
« Reply #13 on: December 23, 2020, 12:21:43 am »
I was hoping to run two mice and two keyboards in Windows, in the past, so I could basically turn one PC into two, but it never would work.  Windows itself tends to just wrap the two keyboards up into one input event (like with the on-screen virtual keyboard and your physical keyboard -- they both are active and affect the same program at the same time, with it being impossible to assign one to one app and the other to a different one), so I never could do what I wanted to do.

It's a shame -- today's PCs are 100 times more powerful than the ones we used to have back in the 80s, so there's no reason why one PC couldn't have 2 monitors, 2 keyboards, and 2 mice, and allow 2 different people to run 2 different programs at the same time.  No reason except the obvious one -- companies make money by selling 2 PCs to a a family, rather than selling one with a few cheap accessories on the side.

I'm hoping you'll be able to sort out what you're wanting to do here, but I'm not going to hold my breath until it occurs.  ;D

Pulled something similar on my kids, with a wireless mouse. kept making their pointer go to weird places on the screen or randomly click. but and their mouse would pick up from the last pos the pointer was in. so even though there was 2 mice it behaved as just one at the response level.
Granted after becoming radioactive I only have a half-life!

Offline madscijr

  • Seasoned Forum Regular
  • Posts: 295
Re: reading multiple mice input with _DEVICES and _DEVICE$, or RawInput ?
« Reply #14 on: December 23, 2020, 10:29:48 am »
Pulled something similar on my kids, with a wireless mouse. kept making their pointer go to weird places on the screen or randomly click. but and their mouse would pick up from the last pos the pointer was in. so even though there was 2 mice it behaved as just one at the response level.

Funny! That's a classic gag. Yes, I get the same behavior when I plug an extra mouse info my USB hub.

Incidentally, I read somewhere you can do the same with multiple keyboards, so if you write a multiplayer game that takes keybord input, and want to spare everyone from crowding around the same keyboard, you would plug in a couple extra keyboards for each player, and as long as they use separate keys for their input, it avoids the “ghosting” that you sometimes see from too many keys being pressed simultaneously on one keyboard. I haven’t tried it yet (not by a PC right now), but your mouse example reminded me...

Anyway, once I get less busy I will attempt the multi mouse thing using the C and C# examples at the link TempodiBasic posted.
https://jstookey.com/multiple-mice-raw-input/
First step would be to get a C compiler or Visual Studio installed, and get it working.
Next step would be to figure out a way to use it from within QB64.
We shall see!

Happy Holidays everyone!
« Last Edit: December 23, 2020, 10:34:42 am by madscijr »