@Wuerfel_21 said:
A better question: why?
What's the point of yoinking the cursor position?
In general you don't need to, but maybe you want to center the mouse cursor at startup so it's immediately visible, or if you had two COGs sharing the display and each had their own cursor position state you could hit some key to flip the window and the mouse position could be retained from prior session/window etc. There are a few use cases, but admittedly not that many.
Region mice are other things possible in my driver - each region can have its own mouse sprite not just a single global one. Maybe you want to multiplex a single physical device in different window regions with different co-ordinates (but not simulataneously - that'd be crazy).
This is all very niche/adcanced.
I should perhaps clarify that the mouse limits and pointer write are intended as "bonus features" to help with simple applications that might not have a poll loop running at all times. If you do have a poll loop, do not set the mouse limits and use the X/Y as running accumulators, somewhat like:
VAR
long prev_x,prev_y
PUB main() | cur_x,cur_y,delta_x,delta_y
repeat
video.wait_vblank()
cur_x,cur_y,_ := usb.mouse_xyz()
delta_x := cur_x - (prev_x\cur_x) ' <- this is post-set, not divide
delta_y := cur_y - (prev_y\cur_y)
' Do whatever with the deltas now
Setting start position should work, but currently doesn't because the mouse data gets reset upon root port connection. There's really no reason for that, so I'll maybe change that.
I came up with one possible method for USB mouse position change. Mostly seems to work, but something resets the co-ordinates to 0,0 after I change them if I try to initialise them before the mouse is detected. If I wait for the mouse to be found first before setting the new position it seems okay, plus it doesn't reset to 0,0 if I hot swap the mouse which is interesting. Still looking.
@Wuerfel_21 said:
I should perhaps clarify that the mouse limits and pointer write are intended as "bonus features" to help with simple applications that might not have a poll loop running at all times. If you do have a poll loop, do not set the mouse limits and use the X/Y as running accumulators, somewhat like:
VAR
long prev_x,prev_y
PUB main() | cur_x,cur_y,delta_x,delta_y
repeat
video.wait_vblank()
cur_x,cur_y,_ := usb.mouse_xyz()
delta_x := cur_x - (prev_x\cur_x) ' <- this is post-set, not divide
delta_y := cur_y - (prev_y\cur_y)
' Do whatever with the deltas now
Setting start position should work, but currently doesn't because the mouse data gets reset upon root port connection. There's really no reason for that, so I'll maybe change that.
Yeah if you burn a COG you can do this, but it can be heavy on the CPU if you want a smooth mouse response on screen.
I have already managed to replace the mouse in Prop2Play and there is another function to implement: a double click (that my RPi contraption reports as an independent event)
I will implement the double click detection directly in the player. Until I do this, I used the right button instead
@Wuerfel_21 said:
Setting start position should work, but currently doesn't because the mouse data gets reset upon root port connection. There's really no reason for that, so I'll maybe change that.
Ok that must have been what I was running into. If that is changed this may work and I'll look into what you mean by the cache region. My waitus(2) could probably be replaced with a small delay in actual clocks with inline PASM. The critical section was reduced to a very small amount of code by polling the mouse_update state much later in the sequence just before the write back. It's probably something around 50 clocks now.
rdlong htmp, ptrb[4] ' read update flag
cmp htmp, #0 wz
if_nz ret ' don't write to HUB if being updated
' write back accumulators
setq #3-1
wrlong hr1,ptrb
@Wuerfel_21 said:
I guess your method here is low-enough overhead, so I think I'll add it. (the flag should be a byte though )
Have to rest a bit first though.
No problem, thanks for doing that. I'm really not trying to make things too difficult, I promise. I know about the byte vs long thing and was thinking the same but just got worried it was out of range of PTR indexing without resorting to further changes of data structure order etc. Can we access ptrb[16] or does it top out at ptrb[15]? Can't recall offhand, needed to double check that. Also we may be able to set Z flag in the new rdlong too, to remove an instruction following it.
PS. Moving that mouse state outside of cached area did fix the initial state issue I had. Thanks.
@Wuerfel_21 said:
Also, no, you do need a slightly longer wait, because at any time the USB cog might get it's 1000 Hz frame IRQ.
It can, but the new position will just override it. Any previous increments to the update will be ignored. You just need to ensure that the driver doesn't clobber the newly updated values in HUB RAM before they are applied.
EDIT: Sorry I see what you mean now, there is an ISR inside the USB code that could extend the time in the critical section.
@pik33 said:
7-port hub strange behavior: 4 ports works of 7 available. I'd be less surprised if only 3 were working: that would mean the second hub in the chain is not supported.
A second hub in not supported (yet), but also the current implementation is limited to 4 ports. Should be a fairly easy fix to add the remaining 3 ports.
Can you post the debug output with only the hub connected to be sure that it is not chained ?
That's what I mean though. If the interrupt hits between reading the update flag and writing the accumulators, it will spend some(tm)(????) amount of time in start-of-frame lala land and then overwrite the accumulators. One could block the IRQ for this sequence but I think(???) that's asking for trouble.
@Wuerfel_21 said:
That's what I mean though. If the interrupt hits between reading the update flag and writing the accumulators, it will spend some(tm)(????) amount of time in start-of-frame lala land and then overwrite the accumulators. One could block the IRQ for this sequence but I think(???) that's asking for trouble.
Gotcha. Yeah increase the delay slightly in the SPIN2 code to compensate. Not a big deal, this mouse_set_xyz thing is an uncommon action anyway and doesn't slow down the driver side in any significant way.
@pik33 said:
7-port hub strange behavior: 4 ports works of 7 available. I'd be less surprised if only 3 were working: that would mean the second hub in the chain is not supported.
A second hub in not supported (yet), but also the current implementation is limited to 4 ports. Should be a fairly easy fix to add the remaining 3 ports.
Can you post the debug output with only the hub connected to be sure that it is not chained ?
By the way, I just found a good application for controlling the mouse position in software when I ran a test of this feature.
Consider a case where you have some on screen menuing system and you have a USB mouse and/or gamepad present. It's easy to achieve parallel control of the mouse cursor position on screen by causing the mouse co-ordinates to increment/decrement slightly whenever the gamepad up/down/left right buttons are held. This lets both the mouse and a gamepad control the current mouse position on screen and is actually quite useful in real life when you try it out. Example code snippet/patch for hidpad_to_vga.spin2 with this feature is added below. No bounds checking is being done in this test, but that would obviously be included in a real application.
PRI moveMouse(incx,incy) | x,y,z
x,y,z:=usb.mouse_xyz() ' read mouse position
usb.mouse_set_xyz(x+incx,y+incy,z) ' shift it and update position
PRI print_hidpad(num) | id, caps, i
id := usb.hidpad_id(num)
caps := usb.hidpad_getcaps(num)
if caps.[16]
i := usb.hidpad_axis(num, 0)
if abs(i) > 400 ' threshold
moveMouse(i>0 ? 2 : -2, 0)
if caps.[17]
i := usb.hidpad_axis(num, 1)
if abs(i) > 400 ' threshold
moveMouse(0, i>0 ? 2 : -2)
text.setTextColours(id ? $F : $8,$1)
text.printStr(@"HIDpad ")
text.dec(num)
...
Okay, upstream version now has a mouse_move method. I've left off the wheel (Z) value because I don't think you'd want to update it together with the position (actually, reading it together with the position is slightly weird, isn't it? Should I make it a separate function?)
@Wuerfel_21 said:
Okay, upstream version now has a mouse_move method. I've left off the wheel (Z) value because I don't think you'd want to update it together with the position (actually, reading it together with the position is slightly weird, isn't it? Should I make it a separate function?)
I think the wheel is independent. I also realized this when I was trying to update the position. You needed to read Z even if you just want to change X and Y onscreen. Separate function for accessing Z should be okay IMO.
Z needs a separate function. May be useful in a GUI with several windows that can scroll vertically (Prop2play!), while switching between them. Now I simply compute deltas for z restricting it to +-1
There could be a couple of other things that may be helpful in the driver layer for a mouse controller to use with slower application polling but I don't want to push my luck too much right now...maybe it could be added later one day if considered very useful to clients and there is room etc.
optional COG ATN notification on mouse changes to trigger some actions in other COGs
latching current mouse co-ordinates, sysclk ticks, on mouse button up/down events
You can probably still achieve this with continuous rapid polling from another COG but sometimes could miss certain things if you are not fast enough to keep up during fast movement and clicking.
There's currently some bytes trying to hold whether there's a pending click, but I think it doesn't work and I might remove it again.
If you can't get your application to actually react at 60 Hz... it probably has no business having mouse input. The main issue here is that none of the high level environments support multiple threads / interrupts within a single cog.
The good old retrocomputing way is to check all UI things in vblanks> @Wuerfel_21 said:
You're supposed to calculate the delta. Storing your app state into the real-time updating driver buffer is mildly awful.
And you can still directly update the variable from the Spin code. There is a small risk that the variable will not update when the driver updates it at the same time than the Spin method. The workaround is very simple, the mouse update rate is no more than 1000 Hz. Set the variable, read the variable, if the same, ok, if not, set again, as the procedure will be much faster than 1 ms
@Wuerfel_21 said:
There's currently some bytes trying to hold whether there's a pending click, but I think it doesn't work and I might remove it again.
If you can't get your application to actually react at 60 Hz... it probably has no business having mouse input. The main issue here is that none of the high level environments support multiple threads / interrupts within a single cog.
Yeah an ISR triggered off some event such as mouse change would be really helpful, it could then go update its local states and populate an application event queue to be processed at will by the slower application. No need for continuous 60Hz operation then by the application and it can go process things at leisure. But as you say we don't have such things (yet). Until then it might be a case where we would have a USB driver COG, an input poller COG just to keep up with the driver changes and fill an event queue, and an application COG. Sucks a bit that way but whatever. I'm thinking GUI not 60Hz gaming in this case where some graphics refresh/redraw activities by the application will be slow.
If the redraw takes that long, you might just want to set an hourglass cursor and ignore clicks while it's doing that (since the user doesn't really see what they're clicking on).
If you really wanted a mouse event queue still, it could be made similar to the key queue. Though I wouldn't implement it now. I still want to make full HID mouse a thing, wherein a lot of the current code (as little as it is) might be canned.
What does that mean? A mouse already works. A full report mode?
where some graphics refresh/redraw activities by the application will be slow.
The PSRAM driver with command list is fast enough to composite several windows in the real time (= no redraw needed) - the window manager is still on my todo P2 list.
I've got one of these USB HID encoder devices (Griffin Powermate) here and it would be interesting to see if it could be made to work with the P2 now we have some working USB code.
Apparently according to someone's Java USB driver I found for it on GitHub it just uses interrupt transfers on endpoint $81 to poll a single axis delta value and the button state in a similar looking format to the mouse boot protocol so I'm now sorta wondering if it could be used like a mouse or gamepad with some mods in the driver code I could add if required. Any thoughts or hints on this? https://github.com/EsotericSoftware/powermate/blob/master/src/powermate/PowerMate.java
It can also set its LED brightness with an interrupt write transfer of a single byte on endpoint 2, although that doesn't map into typical mouse functionality but looks similar to writing LED states for a keyboard perhaps.
Update: Also when I plugged it into the USB hub on the P2 and ran hidpad_to_vga.spin2 to see what happens, it does show up as a HID gamepad entry with ID 077D0410 but doesn't report an axis or button changes (it does print RX=0 while all others are N/A). It also seems to show up detected on screen only after I actually spin the encoder wheel, not immediately after I plug it in which is strange. Any useful debug code to help figure out what this thing actually reports itself as?
Here's what I got when I enabled debug and had (just) this device fitted to the USB hub...if it makes sense to anyone.
Comments
7 port hubs are not neccessarily chained 4-ports. I read somewhere that the hub status report format also just happens to be limited to 7 ports.
This is all very niche/adcanced.
I should perhaps clarify that the mouse limits and pointer write are intended as "bonus features" to help with simple applications that might not have a poll loop running at all times. If you do have a poll loop, do not set the mouse limits and use the X/Y as running accumulators, somewhat like:
Setting start position should work, but currently doesn't because the mouse data gets reset upon root port connection. There's really no reason for that, so I'll maybe change that.
I came up with one possible method for USB mouse position change. Mostly seems to work, but something resets the co-ordinates to 0,0 after I change them if I try to initialise them before the mouse is detected. If I wait for the mouse to be found first before setting the new position it seems okay, plus it doesn't reset to 0,0 if I hot swap the mouse which is interesting. Still looking.
Yeah if you burn a COG you can do this, but it can be heavy on the CPU if you want a smooth mouse response on screen.
I have already managed to replace the mouse in Prop2Play and there is another function to implement: a double click (that my RPi contraption reports as an independent event)
I will implement the double click detection directly in the player. Until I do this, I used the right button instead
No? You just do this once in your vblank loop/interrupt.
Move the mouse data out of the cache region.
Ok that must have been what I was running into. If that is changed this may work and I'll look into what you mean by the cache region. My waitus(2) could probably be replaced with a small delay in actual clocks with inline PASM. The critical section was reduced to a very small amount of code by polling the mouse_update state much later in the sequence just before the write back. It's probably something around 50 clocks now.
I guess your method here is low-enough overhead, so I think I'll add it. (the flag should be a byte though )
Have to rest a bit first though.
No problem, thanks for doing that. I'm really not trying to make things too difficult, I promise. I know about the byte vs long thing and was thinking the same but just got worried it was out of range of PTR indexing without resorting to further changes of data structure order etc. Can we access ptrb[16] or does it top out at ptrb[15]? Can't recall offhand, needed to double check that. Also we may be able to set Z flag in the new rdlong too, to remove an instruction following it.
PS. Moving that mouse state outside of cached area did fix the initial state issue I had. Thanks.
Also, no, you do need a slightly longer wait, because at any time the USB cog might get it's 1000 Hz frame IRQ.
Non-modify PTR expression goes from -32 to 31: https://p2docs.github.io/hubmem.html#pointer-expressions
It can, but the new position will just override it. Any previous increments to the update will be ignored. You just need to ensure that the driver doesn't clobber the newly updated values in HUB RAM before they are applied.
EDIT: Sorry I see what you mean now, there is an ISR inside the USB code that could extend the time in the critical section.
A second hub in not supported (yet), but also the current implementation is limited to 4 ports. Should be a fairly easy fix to add the remaining 3 ports.
Can you post the debug output with only the hub connected to be sure that it is not chained ?
That's what I mean though. If the interrupt hits between reading the update flag and writing the accumulators, it will spend some(tm)(????) amount of time in start-of-frame lala land and then overwrite the accumulators. One could block the IRQ for this sequence but I think(???) that's asking for trouble.
Gotcha. Yeah increase the delay slightly in the SPIN2 code to compensate. Not a big deal, this mouse_set_xyz thing is an uncommon action anyway and doesn't slow down the driver side in any significant way.
By the way, I just found a good application for controlling the mouse position in software when I ran a test of this feature.
Consider a case where you have some on screen menuing system and you have a USB mouse and/or gamepad present. It's easy to achieve parallel control of the mouse cursor position on screen by causing the mouse co-ordinates to increment/decrement slightly whenever the gamepad up/down/left right buttons are held. This lets both the mouse and a gamepad control the current mouse position on screen and is actually quite useful in real life when you try it out. Example code snippet/patch for hidpad_to_vga.spin2 with this feature is added below. No bounds checking is being done in this test, but that would obviously be included in a real application.
I guess that's one way to implement that
Okay, upstream version now has a mouse_move method. I've left off the wheel (Z) value because I don't think you'd want to update it together with the position (actually, reading it together with the position is slightly weird, isn't it? Should I make it a separate function?)
I think the wheel is independent. I also realized this when I was trying to update the position. You needed to read Z even if you just want to change X and Y onscreen. Separate function for accessing Z should be okay IMO.
Z needs a separate function. May be useful in a GUI with several windows that can scroll vertically (Prop2play!), while switching between them. Now I simply compute deltas for z restricting it to +-1
You're supposed to calculate the delta. Storing your app state into the real-time updating driver buffer is mildly awful.
There could be a couple of other things that may be helpful in the driver layer for a mouse controller to use with slower application polling but I don't want to push my luck too much right now...maybe it could be added later one day if considered very useful to clients and there is room etc.
You can probably still achieve this with continuous rapid polling from another COG but sometimes could miss certain things if you are not fast enough to keep up during fast movement and clicking.
There's currently some bytes trying to hold whether there's a pending click, but I think it doesn't work and I might remove it again.
If you can't get your application to actually react at 60 Hz... it probably has no business having mouse input. The main issue here is that none of the high level environments support multiple threads / interrupts within a single cog.
The good old retrocomputing way is to check all UI things in vblanks> @Wuerfel_21 said:
And you can still directly update the variable from the Spin code. There is a small risk that the variable will not update when the driver updates it at the same time than the Spin method. The workaround is very simple, the mouse update rate is no more than 1000 Hz. Set the variable, read the variable, if the same, ok, if not, set again, as the procedure will be much faster than 1 ms
Yeah an ISR triggered off some event such as mouse change would be really helpful, it could then go update its local states and populate an application event queue to be processed at will by the slower application. No need for continuous 60Hz operation then by the application and it can go process things at leisure. But as you say we don't have such things (yet). Until then it might be a case where we would have a USB driver COG, an input poller COG just to keep up with the driver changes and fill an event queue, and an application COG. Sucks a bit that way but whatever. I'm thinking GUI not 60Hz gaming in this case where some graphics refresh/redraw activities by the application will be slow.
If the redraw takes that long, you might just want to set an hourglass cursor and ignore clicks while it's doing that (since the user doesn't really see what they're clicking on).
If you really wanted a mouse event queue still, it could be made similar to the key queue. Though I wouldn't implement it now. I still want to make full HID mouse a thing, wherein a lot of the current code (as little as it is) might be canned.
What does that mean? A mouse already works. A full report mode?
The PSRAM driver with command list is fast enough to composite several windows in the real time (= no redraw needed) - the window manager is still on my todo P2 list.
I've got one of these USB HID encoder devices (Griffin Powermate) here and it would be interesting to see if it could be made to work with the P2 now we have some working USB code.
Apparently according to someone's Java USB driver I found for it on GitHub it just uses interrupt transfers on endpoint $81 to poll a single axis delta value and the button state in a similar looking format to the mouse boot protocol so I'm now sorta wondering if it could be used like a mouse or gamepad with some mods in the driver code I could add if required. Any thoughts or hints on this?
https://github.com/EsotericSoftware/powermate/blob/master/src/powermate/PowerMate.java
It can also set its LED brightness with an interrupt write transfer of a single byte on endpoint 2, although that doesn't map into typical mouse functionality but looks similar to writing LED states for a keyboard perhaps.
Update: Also when I plugged it into the USB hub on the P2 and ran hidpad_to_vga.spin2 to see what happens, it does show up as a HID gamepad entry with ID 077D0410 but doesn't report an axis or button changes (it does print RX=0 while all others are N/A). It also seems to show up detected on screen only after I actually spin the encoder wheel, not immediately after I plug it in which is strange. Any useful debug code to help figure out what this thing actually reports itself as?
Here's what I got when I enabled debug and had (just) this device fitted to the USB hub...if it makes sense to anyone.