@ Jamz - Not at the moment, no
The reason being, I cannot HIDE the original input.
I can do a similar thing for sensitivity amplifications, because I send additional output in addition to the input, but reversing the input is not something I cannot currently do.
HOWEVER. This is likely to change once Nefarius' HidGuardian is finished - this should allow us to hide the physical mouse from applications, so we can then send inverted virtual output without the original input cancelling it out.
I have no idea when Nefarius will release a working version tho
It also may be possible with SetWindowsHookEx Dll Calls (ie low-level mouse hooks)
@Tharg
I have extended the plugin to support up to 10 bands.
I will be adding it to the main UCR distro as-is, I think this is too useful to leave out, now that I have written it...
Universal Control Remapper (UCR) - v0.1.22 28th Oct 2018
Re: Universal Control Remapper (UCR) - v0.1.11 19th Feb 2017
Thanks again. With a bit of tweaking this is doing exactly what I wanted.
I realised after trying your new plugin that I'd (tried to) ask for slightly the wrong thing - actually the buttons don't need to be held, and the HUD element doesn't disappear on releasing the key. It goes when you press a the same key again, or open another HUD element, or press the back button. Poor comments in my AHK script led me astray . I was in fact doing this in a round about way by pressing a different button depending on the direction a band was entered. I didn't use the back button, but when I entered the middle band from the "b" band I'd press "b" again, or if from the "c" band I'd press "c" again.
At first I couldn't quite get it working with your plugin because if my initial misunderstandings, but once I set the middle band to send the "back" key it all works perfectly, so far as I can tell.
Do you have a donation link? I'd like to make a small donation if so.
I realised after trying your new plugin that I'd (tried to) ask for slightly the wrong thing - actually the buttons don't need to be held, and the HUD element doesn't disappear on releasing the key. It goes when you press a the same key again, or open another HUD element, or press the back button. Poor comments in my AHK script led me astray . I was in fact doing this in a round about way by pressing a different button depending on the direction a band was entered. I didn't use the back button, but when I entered the middle band from the "b" band I'd press "b" again, or if from the "c" band I'd press "c" again.
At first I couldn't quite get it working with your plugin because if my initial misunderstandings, but once I set the middle band to send the "back" key it all works perfectly, so far as I can tell.
Do you have a donation link? I'd like to make a small donation if so.
Last edited by Tharg on 05 Mar 2017, 11:16, edited 1 time in total.
Re: Universal Control Remapper (UCR) - v0.1.11 19th Feb 2017
OK, I will look into whether I can add an option to only tap the button. Is this a show-stopper for you using the plugin at the moment?
I am currently redirecting donations to Nefarius, to help fund his ViGEm project: https://www.paypal.com/cgi-bin/webscr?c ... Y8RYKZZ28E
Pls mention me or UCR in the donation text, thankyou!
I am currently redirecting donations to Nefarius, to help fund his ViGEm project: https://www.paypal.com/cgi-bin/webscr?c ... Y8RYKZZ28E
Pls mention me or UCR in the donation text, thankyou!
Re: Universal Control Remapper (UCR) - v0.1.11 19th Feb 2017
It was pretty easy to implement. New version here
I would delete the copy in your user folder and save this one to the core folder - it will be included in the next release.
I added a "Tap Mode" checkbox, with an Editbox to select how long to hold the button for (Defaults to 50ms).
The releasing of buttons is asynchronous, so if you enter a really high value, you could end up with more than one button pressed at once.
I did this, because many games do not recognize keys held for less than ~50ms, so if you moved the axis too quickly, it might not hit one of the buttons, and some use-cases may absolutely need the buttons for bands that you passed through to be pressed for at least 50ms.
I would delete the copy in your user folder and save this one to the core folder - it will be included in the next release.
I added a "Tap Mode" checkbox, with an Editbox to select how long to hold the button for (Defaults to 50ms).
The releasing of buttons is asynchronous, so if you enter a really high value, you could end up with more than one button pressed at once.
I did this, because many games do not recognize keys held for less than ~50ms, so if you moved the axis too quickly, it might not hit one of the buttons, and some use-cases may absolutely need the buttons for bands that you passed through to be pressed for at least 50ms.
Re: Universal Control Remapper (UCR) - v0.1.11 19th Feb 2017
Final update for today.
Renamed "Band" to "Range"
Fixed bug where there were gaps in the range unless you used floating point values (eg 0-9.9999, 10-19.999).
Now it rounds the input, so you can use integers for the ranges.
Improved behavior when toggling Tap Mode - it now presses/releases buttons as appropriate.
https://raw.githubusercontent.com/evilC ... uttons.ahk
Renamed "Band" to "Range"
Fixed bug where there were gaps in the range unless you used floating point values (eg 0-9.9999, 10-19.999).
Now it rounds the input, so you can use integers for the ranges.
Improved behavior when toggling Tap Mode - it now presses/releases buttons as appropriate.
https://raw.githubusercontent.com/evilC ... uttons.ahk
Re: Universal Control Remapper (UCR) - v0.1.11 19th Feb 2017
Hi, jamz.Jamz wrote:Hi, i'm hopeing UCR can solve my issue but can't seem to find a way, maybe I'm missing it?
I'm looking to invert the axis of a second mouse, is this possible?
My setup: A virtual table top/TV embedded into a table. So I want one mouse on one side of the table as normal, and a second mouse on the opposite side with it's x and y axis reversed (as they are viewing windows upside down). I really don't need a separate cursor, just the axis changed for a second mouse. (Both mice are logitech 5 button mice)
I just made a script for multiple cursors, it is very simple to add inverted axis to that, as long as you don't move both mice simultaneously, the cursor acts pretty normal. This is is a modified version if you like to try. You invert the axis by setting a negative sensitivity. (Left edit is for x, right for y). You also need to edit the the confinment rectangle under the coordinates tab. More instructions: here.
Code: Select all
; TODO,
; remove debug hotkey - and ea() func
; reconsider choise of default cursor when A_Cursor is Unknown.
; Manage click up when more than mouse is moving and click is down....
class Cursor extends _UCR.Classes.Plugin
{
type:="Cursor"
description:="Mulitple cursor controller"
static AllCursors:=[] ; Tracks all instances of this plugin
static MouseIdCounter:=[] ; For tracking multiple active cursor from the same mouse id.
static doNothingFunc:=ObjBindMethod(Cursor, "doNothing") ; For disabling mouse buttons.
init()
{
this.LoadCursor() ; Loads cursor handles.
; Gui
; Some control(s) are placed at rowX, colY, defined here:
row0:=" y5 ", row0_:=" y2 "
row1:=" y35 ", row1_:=" y32 "
row2:=" y65 ", row2_:=" y62 "
row3:=" y95 ", row3_:=" y92 "
row4:=" y125 ", row4_:=" y123 "
row5:=" y155 ", row5_:=" y153 "
row6:=" y185 ", row6_:=" y183 "
row7:=" y215 ", row7_:=" y213 "
; Columns for gui placement
col1:=" x25 ", col2:=" x145 ", col3:=" x260 ", col4:=" x375 ", col5:=" x490 "
Gui, Add, Tab2, w650 h65, % "Main|Cycle|Coordinates|Help"
Gui, Tab, 1
; Mouse selection
Gui, Add, Button, % " w100 h25 hwndselectMouseButton", % "Select mouse"
this.selectMouseButton:=selectMouseButton
gFunc := this.SelectMouseInit.Bind(this)
GuiControl, +g, % this.selectMouseButton, % gFunc
this.AddControl("Edit", "MouseIdEdit", this.MouseIdSet.Bind(this),"x+10 yp+3 w75 center")
this.AddControl("InputButton", "toggle", 0, this.toggle.Bind(this),"x+10 h25")
; Toggle light
this.AddControl("ButtonPreview", "bp1", 0,0, "x+10")
; Sensitivity
Gui,Add,Text,x+10 yp+3,Sensitivity:
this.AddControl("Edit", "sensEditX", this.sensChanged.Bind(this,1),"x+10 yp-3 w35 center",1)
this.AddControl("Edit", "sensEditY", this.sensChanged.Bind(this,2),"x+5 w35 center",1)
; Hide/show option
this.AddControl("Checkbox", "hideWhenInactiveCB", this.hideWhenInactiveCBChanged.Bind(this),"x+10 yp+3 checked", "Hide when inactive")
Gui, Tab, 2
Gui,Add,Text,,Cycle cursors:
this.AddControl("InputButton", "cycleCursor", 0, this.cycleCursor.Bind(this),"x+10 h25")
this.AddControl("Checkbox", "cycleCursorCB", 0,"x+10 yp+3 checked", "Cycle by id")
; Coordinates tab
Gui, Tab, 3
Gui,Add,Text, x+5,Confine to: (x1,y1)
this.AddControl("Edit", "confineX1", this.confineEditChanged.Bind(this,1),"x+5 yp-3 w35 number center",0)
this.AddControl("Edit", "confineY1", this.confineEditChanged.Bind(this,2),"x+5 w35 number center",0)
Gui,Add,Text, x+5 yp+3,(x2,y2)
this.AddControl("Edit", "confineX2", this.confineEditChanged.Bind(this,3),"x+5 yp-3 w35 number center",A_ScreenWidth)
this.AddControl("Edit", "confineY2", this.confineEditChanged.Bind(this,4),"x+5 w35 number center",A_ScreenHeight)
Gui,Add,Text, x+5 yp+3,Start at: (x,y)
this.AddControl("Edit", "startX1", this.startEditChanged.Bind(this,1),"x+5 yp-3 w35 number center",A_ScreenWidth//2)
this.AddControl("Edit", "startY1", this.startEditChanged.Bind(this,2),"x+5 w35 number center",A_ScreenHeight//2)
Gui, Add, Button, % "x+5 hwndresetButton", % "Reset"
this.resetButton:=resetButton
gFunc := this.resetCoords.Bind(this)
GuiControl, +g, % this.resetButton, % gFunc
; Help tab
Gui, Tab, 4
Gui, Font, s8 w400, Courier New
Gui, add, Link, % row1 col1, % "Author: Helgef (2017-03-03). "
. "Instructions are available at: <a href=""https://autohotkey.com/boards/viewtopic.php?f=19&t=24538"">AutoHotkey.com forum.</a>`n"
; Misc.
this.createCursor() ; Make the cursor's gui.
Cursor.AllCursors.Push(this) ; The plugin is self aware.
this.hasBeenToggled:=0
this.hideWhenInactive && !this.toggleState ? this.hideCursor() : this.showCursor()
; DEBUG - REMOVE
; ff:=ObjBindMethod(Cursor,"ea")
; try
; Hotkey, Esc, % ff
}
; Clean up for UCR.
onClose(){
GuiControl, -g, % this.selectMouseButton
GuiControl, -g, % this.resetButton
for k, cur in Cursor.AllCursors ; Find position in AllCursors list.
if (cur=this)
break
Cursor.AllCursors.RemoveAt(k) ; Remove from AllCursors list
Cursor.MouseIdCounter.HasKey(this.SelectedMouse) ? --Cursor.MouseIdCounter[this.SelectedMouse] : "" ; Decerment mouse id counter
if !Cursor.AllCursors.length()
Cursor.md.Delete(),Cursor.md:="",Cursor.toggleHotkeys("Off") ; Remove the mouse delta if no more cursor plugins exists.
this.destroyCursor() ; Destroy the gui cursor.
base.onClose() ;?
}
; Gui functions
; Coords tab
confineEditChanged(num,val){ ; Confinement rectangle.
if (num=1)
this.minX:=val
else if (num=2)
this.minY:=val
else if (num=3)
this.maxX:=val
else if (num=4)
this.maxY:=val
return
}
startEditChanged(num,val){ ; start position
if (num=1)
this.oX:=val
else if (num=2)
this.oY:=val
return
}
resetCoords(){ ; Reset coords button
this.GuiControls.confineX1.Set(0)
this.GuiControls.confineY1.Set(0)
this.GuiControls.confineX2.Set(A_ScreenWidth)
this.GuiControls.confineY2.Set(A_ScreenHeight)
this.GuiControls.startX1.Set(A_ScreenWidth//2)
this.GuiControls.startY1.Set(A_ScreenHeight//2)
}
sensChanged(num,val){
if val is number
{
if (num=1)
this.sensX:=val ; Set sensitivity x-axis
else if (num=2)
this.sensY:=val ; Set sensitivity y-axis
}
return
}
hideWhenInactiveCBChanged(state){
state && !this.toggleState ? this.hideCursor() : this.showCursor() ; Show/hide cursor to match setting.
return this.hideWhenInactive:=state ; Indicates wether cursor is hidden/shown when inactive
}
createCursor(){
if this.hWin
return ;?
this.x:=this.oX
this.y:=this.oY
Gui, new, +hwndhWin -caption +toolwindow +alwaysontop +E0x20
this.hWin:=hWin
Gui, % hWin ": Margin",0,0
Gui, % hWin ": Color", abcdef
Gui, % hWin ": Add", Picture, % "x0 y0 hwndhPic", % "hIcon:*" this.hIcons["Arrow"]
this.hPic:=hPic
this.setCursor()
this.hs:=Cursor.GetCursorHotspot()
GuiControl,, % this.hPic, % "hIcon:*" this.hIcons[this.IDC] ; Consider to remove this.
;Gui, % hWin ": Show", % "Hide NA x" this.x " y" this.y
this.hidden:=1
WinSet, TransColor, abcdef 255, % "ahk_id " hWin
return
}
; Handle mouse selection
SelectMouseInit(){
; Starts the mouse selection procedure.
; How to:
; - Press the button
; - Move the mouse within 5 seconds.
; - Done!
p:=this.SelectedMouse ; Previous mouse
selectMouseMD:= new Cursor.MouseDelta(ObjBindMethod(this,"SelectMouse"))
selectMouseMD.Start()
timeout:=A_TickCount ; For timeout
while (this.SelectedMouse=p && A_TickCount-timeout<5000) ; 5 second timeout.
Sleep,-1
if (this.SelectedMouse!=p){
Cursor.MouseIdCounter.HasKey(p) ? --Cursor.MouseIdCounter[p] : "" ; A new mouse chosen
Cursor.MouseIdCounter.HasKey(this.SelectedMouse) ? ++Cursor.MouseIdCounter[this.SelectedMouse] : (Cursor.MouseIdCounter[this.SelectedMouse]:=1)
}
selectMouseMD.Delete() ; Delete the old mouse delta
return
}
MouseIdSet(id){
return this.SelectedMouse:=id
}
SelectMouse(MouseID,dx,dy) {
; Callback function for temporary mouse delta instance.
if (this.selectedMouse=MouseID)
return
this.GuiControls.MouseIdEdit.Set(MouseID)
return
}
; destroy/hide/show/moveCursor() - these functions apply to the "gui cursor" not the real windows cursor.
destroyCursor(){
if !this.hWin
return
Gui, % this.hWin ": Destroy"
return this.hWin:=""
}
hideCursor(){
if !this.hWin
return
if this.hidden
return
Gui, % this.hWin ": Show", % "NA hide"
this.hidden:=1
return
}
showCursor(move:=0){
; Consider toggle aot to avoid getting under other aot win.
if !this.hWin
return
if (this.hidden && move)
return this.showCursor()
else if (!this.hidden && !move)
return
this.hidden:=0
if (this.IDC!=this.pIDC) ; Only update if changed
GuiControl,, % this.hPic, % "hIcon:*" this.hIcons[this.IDC]
this.pIDC:=this.IDC
if !move
Gui, % this.hWin ": Show", % "NA x" this.x-this.hs[1] " y" this.y-this.hs[2] ; Adjustment for hotspot.
else
WinMove, % "ahk_id " this.hWin,, this.x-this.hs[1], this.y-this.hs[2]
return
}
moveCursor(){
return this.showCursor(true)
}
ea(){ ; remove
ExitApp
}
; For suppressing native mouse button functionality
doNothing(){
return
}
; Main togle
toggle(state) {
if !state
return
if !this.hasBeenToggled { ; Add to MouseIdCounter on first time toggle on.
Cursor.MouseIdCounter.HasKey(this.SelectedMouse) ? ++Cursor.MouseIdCounter[this.SelectedMouse] : (this.SelectedMouse ? (Cursor.MouseIdCounter[this.SelectedMouse]:=1) : "")
this.hasBeenToggled:=true
}
this.toggleState:=!this.toggleState
if (!Cursor.md && this.toggleState) {
Cursor.md:=new Cursor.MouseDelta(ObjBindMethod(Cursor,"MoveCursors")) ; Create a new mousedelta for the Cursor class, if non exists.
}
Cursor.globalToggle(this.toggleState)
if !this.toggleState {
if (this.hideWhenInactive) ; Hide/show according to settings
this.hideCursor()
else
this.showCursor()
} else {
this.showCursor()
}
this.GuiControls.bp1.SetState(this.toggleState) ; Green light when on (in UCR gui)
return
}
globalToggle(state){
; Sets the correct state of the MouseDelta: Cursor.md, depending on all toggleStates of all cursors in Cursor.AllCursors
mdState:=Cursor.md.State
if (state=mdState) ; State is matched, do nothing
return
else if (state && !mdState) { ; State is on, and md is off, turn on md.
Cursor.toggleHotkeys("On")
Cursor.md.Start()
return
}else if (!state && mdState) { ; State is off, but md is on, check if any other cursor is on, then do nothing, if no other is on, restore mouse functionality and turn off md.
for k, cur in Cursor.AllCursors
if cur.toggleState
return
Cursor.toggleHotkeys("Off")
Cursor.md.Stop()
}
return
}
toggleHotkeys(state){
; Normal mouse button functionality is suppressed while a cursor is active.
hkfn:=Cursor.doNothingFunc
Hotkey, *LButton, % hkfn, % state
Hotkey, *RButton, % hkfn, % state
Hotkey, *MButton, % hkfn, % state
Hotkey, *WheelUp, % hkfn, % state
Hotkey, *WheelDown, % hkfn, % state
Hotkey, *XButton1, % hkfn, % state
Hotkey, *XButton2, % hkfn, % state
if (state="On") ; Disables all mouse movement when "On"
BlockInput, MouseMove
else
BlockInput, MouseMoveOff
return
}
; Cycle cursors.
cycleCursor(state){
if !state ; Manage hotkey up event - do nothing.
return
if this.GuiControls.cycleCursorCB.Get() ; Only cycle same id
this.cycleSame()
else
this.cycleAll()
}
cycleSame(){
for k, cur in Cursor.AllCursors
if (cur.selectedMouse!=this.selectedMouse) { ; Wrong id, continue
continue
} else if (cur.toggleState) { ; Turn off the first one that is on
cur.toggle(1), oneOff:=1
} else if (oneOff) { ; Now we can turn on the next
cur.toggle(1), oneOn:=1
break
}
if !oneOn ; No cursor was turned on, turn on the first with correct id.
for k, cur in Cursor.AllCursors
if (cur.selectedMouse=this.selectedMouse) {
cur.toggle(1)
break
}
return
}
cycleAll(){
; Find the next cursor, turn it on
for k, cur in Cursor.AllCursors
if (cur.toggleState) { ; Turn off the first one that is on
cur.toggle(1), oneOff:=1
} else if (oneOff) {
cur.toggle(1), oneOn:=1 ; Turn on the next one
break
}
if !oneOn
Cursor.AllCursors[1].toggle(1) ; No cursor turned on, turn on the first one.
return
}
MoveCursors(MouseID, dx, dy, usButtonFlags, usButtonData){
; Move all cursors for the MouseId that generated the movement, dx,dy.
; Confine to (x,y) ∈ [cur.MinW,cur.MaxW]x[cur.MinH,cur.MaxH]
; Note: hide/show/move Cursor functions refer to the cursor GUI representation, not the actual (windows) cursor.
Cursor.md.SetState(0)
SetWinDelay,-1
SetMouseDelay,-1
CoordMode, Mouse, Screen
idCtr:=Cursor.MouseIdCounter[MouseID]
for k, cur in Cursor.AllCursors ; Consider all cursors. each "cur" is one instances of the plugin.
if (cur.toggleState && cur.SelectedMouse=MouseID) {
cur.x+=dx?dx*cur.sensX:0 ; Update position
cur.y+=dy?dy*cur.sensY:0
cur.x:= cur.x>cur.MaxX?cur.MaxX:(cur.x<cur.MinX?cur.MinX:cur.x) ; Confine
cur.y:= cur.y>cur.MaxY?cur.MaxY:(cur.y<cur.MinY?cur.MinY:cur.y)
MouseMove, cur.x, cur.y, 0
cs:=A_Cursor
cur.hs:=Cursor.GetCursorHotspot(cs) ; Adjustment for gui, w.r.t cursor hotspot
cur.setCursor(cs)
--idCtr
if !(idCtr) {
cur.hideCursor() ; Hide the gui, the real mouse cursor is showing
} else {
cur.moveCursor() ; Move the gui, the real mouse is not here
}
if usButtonFlags
Cursor.MouseButtons(usButtonFlags,usButtonData) ; Handle mouse buttons
} else if (cur.toggleState && cur.hidden) { ; Show the gui, the real cursor has moved elsewhere.
cur.showCursor()
}
Cursor.md.SetState(1)
return
}
setCursor(CursorStyle:=""){
; Decides which cursor image to show on the cursor gui.
if !CursorStyle
CursorStyle:=A_Cursor
if (this.IDC=CursorStyle)
return
if this.hIcons.HasKey(CursorStyle)
this.IDC:=CursorStyle
else
this.IDC:="Arrow" ; reconsider this choise. This is when A_Cursor is "Unknown" <--- NOTE
return
}
MouseButtons(usButtonFlags,usButtonData){
/*
RAWMOUSE structure
URL:https://msdn.microsoft.com/en-us/library/windows/desktop/ms645578(v=vs.85).aspx
usButtonFlags:
The transition state of the mouse buttons. This member can be one or more of the following values:
RI_MOUSE_LEFT_BUTTON_DOWN:=0x0001
RI_MOUSE_LEFT_BUTTON_UP:=0x0002
RI_MOUSE_RIGHT_BUTTON_DOWN:=0x0004
RI_MOUSE_RIGHT_BUTTON_UP:=0x0008
RI_MOUSE_MIDDLE_BUTTON_DOWN:=0x0010
RI_MOUSE_MIDDLE_BUTTON_UP:=0x0020
RI_MOUSE_BUTTON_4_DOWN:=0x0040
RI_MOUSE_BUTTON_4_UP:=0x0080
RI_MOUSE_BUTTON_5_DOWN:=0x100
RI_MOUSE_BUTTON_5_UP:=0x0200
RI_MOUSE_WHEEL:=0x0400
usButtonData:
If usButtonFlags is RI_MOUSE_WHEEL, this member is a signed value that specifies the wheel delta.
*/
static ClickParams:= { 1:"Down", 2:"Up", 4:"Down Right", 8:"Up Right"
, 16:"Down Middle", 32:"Up Middle", 64:"Down X1", 128:"Up X1"
, 256:"Down X2", 512:"Up X2", 1024:"Wheel" } ; 1024:Wheel isn't used.
static WheelDelta:=120
(RI_MOUSE_WHEEL:= usButtonFlags & 1024) ? usButtonFlags^=1024 : ""
while usButtonFlags {
if ((paramNum:=2**(A_Index-1)) & usButtonFlags) {
SendInput, % "{Click " . ClickParams[paramNum] . "}"
usButtonFlags^=paramNum
}
}
if RI_MOUSE_WHEEL ; Mouse wheel
Loop, % Round(abs(usButtonData)/WheelDelta)
SendInput % "{Click " . (usButtonData>0 ? "WheelUp" : "WheelDown") . "}"
return
}
LoadCursor(){
; Url https://msdn.microsoft.com/en-us/library/windows/desktop/ms648391%28v=vs.85%29.aspx
static cursorIds:={ IDC_ARROW :32512
, IDC_IBEAM :32513
, IDC_WAIT :32514
, IDC_CROSS :32515
, IDC_UPARROW :32516
, IDC_SIZE :32640
, IDC_ICON :32641
, IDC_SIZENWSE :32642
, IDC_SIZENESW :32643
, IDC_SIZEWE :32644
, IDC_SIZENS :32645
, IDC_SIZEALL :32646
, IDC_NO :32648
, IDC_HAND :32649
, IDC_APPSTARTING :32650
, IDC_HELP :32651 }
this.hIcons:={}
for IDC, id in cursorIds
this.hIcons[SubStr(IDC,5)]:=DllCall("LoadCursor", "Uint", 0, "Uint", id)
return
}
GetCursorHotspot(CursorStyle:=""){
; Url :
; - https://msdn.microsoft.com/en-us/library/windows/desktop/ms648389%28v=vs.85%29.aspx (GetCursorInfo)
; - https://msdn.microsoft.com/en-us/library/windows/desktop/ms648381(v=vs.85).aspx (Cursorinfo struct)
; struct cursorinfo {
; DWORD cbSize;
; DWORD flags;
; HCURSOR hCursor;
; POINT ptScreenPos;
; }
; Url:
; - https://msdn.microsoft.com/en-us/library/windows/desktop/ms648070(v=vs.85).aspx (GetIconInfo)
; - https://msdn.microsoft.com/en-us/library/windows/desktop/ms648052(v=vs.85).aspx (Iconinfo struct)
;
; struct _ICONINFO {
; BOOL fIcon; fIcon := NumGet(&PICONINFO, 0,"Int")
; DWORD xHotspot;
; DWORD yHotspot;
; HBITMAP hbmMask; hBMMask := NumGet(&PICONINFO,12,"UPtr")
; HBITMAP hbmColor; BMColor := NumGet(&PICONINFO,12+A_PtrSize,"UPtr")
; }
;
; Get Cursor handle
static hotspots:={}
if !CursorStyle
CursorStyle:=A_Cursor
if hotspots.Haskey(CursorStyle)
return hotspots[CursorStyle]
VarSetCapacity(pci, (cbSize:=16+A_PtrSize), 0)
NumPut(cbSize,&pci,"Uint")
DllCall("GetCursorInfo", "UPtr", &pci)
hCursor := NumGet(pci, 8, "UPtr")
; Get icon info - cursor hotspot
VarSetCapacity(PICONINFO, 12+2*A_PtrSize, 0)
DllCall("GetIconInfo", "Ptr", hCursor, "Ptr", &PICONINFO)
xHotspot := NumGet(&PICONINFO, 4,"UInt")
yHotspot := NumGet(&PICONINFO, 8,"UInt")
hs:=[xHotspot?xHotspot:0, yHotspot?yHotspot:0]
hotspots[CursorStyle]:=hs
return hs
}
; Credits, Class MouseDelta: https://autohotkey.com/boards/viewtopic.php?f=19&t=10159
; This version is slightly modified.
Class MouseDelta {
State := 0
__New(callback){
this.MouseMovedFn := this.MouseMoved.Bind(this)
this.Callback := callback
}
Start(){
static DevSize := 8 + A_PtrSize, RIDEV_INPUTSINK := 0x00000100
; Register mouse for WM_INPUT messages.
VarSetCapacity(RAWINPUTDEVICE, DevSize)
NumPut(1, RAWINPUTDEVICE, 0, "UShort")
NumPut(2, RAWINPUTDEVICE, 2, "UShort")
NumPut(RIDEV_INPUTSINK, RAWINPUTDEVICE, 4, "Uint")
; WM_INPUT needs a hwnd to route to, so get the hwnd of the AHK Gui.
; It doesn't matter if the GUI is showing, it still exists
Gui +hwndhwnd
NumPut(hwnd, RAWINPUTDEVICE, 8, "Uint")
this.RAWINPUTDEVICE := RAWINPUTDEVICE
DllCall("RegisterRawInputDevices", "Ptr", &RAWINPUTDEVICE, "UInt", 1, "UInt", DevSize )
OnMessage(0x00FF, this.MouseMovedFn)
this.State := 1
return this ; allow chaining
}
Stop(){
static RIDEV_REMOVE := 0x00000001
static DevSize := 8 + A_PtrSize
OnMessage(0x00FF, this.MouseMovedFn, 0)
RAWINPUTDEVICE := this.RAWINPUTDEVICE
NumPut(RIDEV_REMOVE, RAWINPUTDEVICE, 4, "Uint")
DllCall("RegisterRawInputDevices", "Ptr", &RAWINPUTDEVICE, "UInt", 1, "UInt", DevSize )
this.State := 0
return this ; allow chaining
}
SetState(state){
if (state && !this.State)
this.Start()
else if (!state && this.State)
this.Stop()
return this ; allow chaining
}
Delete(){
this.Stop()
this.MouseMovedFn := ""
}
; Called when the mouse moved.
; Messages tend to contain small (+/- 1) movements, and happen frequently (~20ms)
MouseMoved(wParam, lParam){
Critical
; RawInput statics
static DeviceSize := 2 * A_PtrSize, iSize := 0, sz := 0, pcbSize:=8+2*A_PtrSize, offsets := {usButtonFlags:12+A_PtrSize*2, usButtonData:14+A_PtrSize*2, x: (20+A_PtrSize*2), y: (24+A_PtrSize*2)}, uRawInput
; Get hDevice from RAWINPUTHEADER to identify which mouse this data came from
VarSetCapacity(header, pcbSize, 0)
If (!DllCall("GetRawInputData", "UPtr", lParam, "uint", 0x10000005, "UPtr", &header, "Uint*", pcbSize, "Uint", pcbSize) or ErrorLevel)
Return 0
if !(ThisMouse := NumGet(header, 8, "UPtr"))
return
; Find size of rawinput data - only needs to be run the first time.
if (!iSize){
r := DllCall("GetRawInputData", "UInt", lParam, "UInt", 0x10000003, "Ptr", 0, "UInt*", iSize, "UInt", 8 + (A_PtrSize * 2))
VarSetCapacity(uRawInput, iSize)
}
sz := iSize ; param gets overwritten with # of bytes output, so preserve iSize
; Get RawInput data
r := DllCall("GetRawInputData", "UInt", lParam, "UInt", 0x10000003, "Ptr", &uRawInput, "UInt*", sz, "UInt", 8 + (A_PtrSize * 2))
; Ensure we always report a number for an axis. Needed? - No it is overwritten by the numget.
dx := NumGet(&uRawInput, offsets.x, "Int")
dy := NumGet(&uRawInput, offsets.y, "Int")
usButtonFlags:=NumGet(&uRawInput, offsets.usButtonFlags, "UShort") ; Transition state of the mouse buttons.
usButtonData:=NumGet(&uRawInput, offsets.usButtonData, "Short") ; Contains mouse wheel delta, if usButtonFlags=RI_MOUSE_WHEEL=0x0400
this.Callback.(ThisMouse, dx, dy, usButtonFlags, usButtonData)
}
}
}
Cheers!
Re: Universal Control Remapper (UCR) - v0.1.11 19th Feb 2017
To be honest it seems to be working perfectly right now. I thought perhaps you were only sending single taps and I hadn't explained it properly. I guess the game is forgiving on that.
...
I just managed to "prove" that you are sending held keys though, but I can get around any problems it causes I think. I have the middle band bound to vjoy button 1, which is "back" in game. I also have a joystick button bound to the same vjoy button 1 too, for use as "back" in game menus. When I'm in the middle band this joystick button doesn't work (as vjoy button 1 is held by the band I guess). I also have a button on a physical gamepad as "back" (no vjoy), for some more complex menus, and this one also doesn't work when in the middle band (Elite offers 2 binds for each function).
...
So - I've edited your script, and added 2 new bands (only really needed one I guess) so the middle band is empty, but there's a 2 small bands either side of it bound to the back button (http://i.imgur.com/1f1VkgM.png). It all seems great, but even if I get some issues I'm pretty sure I'll be able to get around them by getting a little creative.
Please don't do any more work for me! It's working pretty perfectly now.
Donation made : http://i.imgur.com/evS6eqD.png
...
[Seen reply re "tap update"]
Man you make these and reply quicker than I test, tweak, and type a reply! ( I'm in VR so it's a bit laborious to get in and out of game etc. /humblebrag)
Tried the new script, set it up slightly differently, works even better. Thanks!
Just to explain a little better (perhaps) what I did in my original script : I stored the axis value from the last tick, and compared this to the current tick, to see if a band had been entered / left and from which direction. Actually it's probably better to explain it by saying that I monitored the borders between bands to see if they'd been crossed, and in which direction. This enabled me to press a different key depending on whether I was moving down or up the axis when I crossed the threshold. I think that this is something that this plugin can't do in quite the same way(?).
My 'config' in the script might make what I mean clearer :
So when the value passes 20 going down, I send a "b", when it passes it going up, it sends an "a".
The only part I think the plugin can't do that this can is the middle 'band" I think. When I enter this from > 60 it taps "d" to close that HUD. When I enter it from < 40 it taps "a" to close that one. I don't think the plugin can do this in the same way. But I can mimic this in game by using "back" instead of the same key again to close the hud elements when I return the axis to the middle.
....
[Seen later reply with next update]
Aaand you've done more work and replied again.
That's it from me, everything is perfect, thanks again!
...
I just managed to "prove" that you are sending held keys though, but I can get around any problems it causes I think. I have the middle band bound to vjoy button 1, which is "back" in game. I also have a joystick button bound to the same vjoy button 1 too, for use as "back" in game menus. When I'm in the middle band this joystick button doesn't work (as vjoy button 1 is held by the band I guess). I also have a button on a physical gamepad as "back" (no vjoy), for some more complex menus, and this one also doesn't work when in the middle band (Elite offers 2 binds for each function).
...
So - I've edited your script, and added 2 new bands (only really needed one I guess) so the middle band is empty, but there's a 2 small bands either side of it bound to the back button (http://i.imgur.com/1f1VkgM.png). It all seems great, but even if I get some issues I'm pretty sure I'll be able to get around them by getting a little creative.
Please don't do any more work for me! It's working pretty perfectly now.
Donation made : http://i.imgur.com/evS6eqD.png
...
[Seen reply re "tap update"]
Man you make these and reply quicker than I test, tweak, and type a reply! ( I'm in VR so it's a bit laborious to get in and out of game etc. /humblebrag)
Tried the new script, set it up slightly differently, works even better. Thanks!
Just to explain a little better (perhaps) what I did in my original script : I stored the axis value from the last tick, and compared this to the current tick, to see if a band had been entered / left and from which direction. Actually it's probably better to explain it by saying that I monitored the borders between bands to see if they'd been crossed, and in which direction. This enabled me to press a different key depending on whether I was moving down or up the axis when I crossed the threshold. I think that this is something that this plugin can't do in quite the same way(?).
My 'config' in the script might make what I mean clearer :
Code: Select all
PsudoButtonAxisConfig[ "V" ][ "Pass" ][ "20" ] := { "Down": "b", "Up":"a" }
PsudoButtonAxisConfig[ "V" ][ "Pass" ][ "40" ] := { "Down": "a", "Up":"a" }
PsudoButtonAxisConfig[ "V" ][ "Pass" ][ "60" ] := { "Down": "d", "Up":"d" }
PsudoButtonAxisConfig[ "V" ][ "Pass" ][ "80" ] := { "Down": "d", "Up":"c" }
The only part I think the plugin can't do that this can is the middle 'band" I think. When I enter this from > 60 it taps "d" to close that HUD. When I enter it from < 40 it taps "a" to close that one. I don't think the plugin can do this in the same way. But I can mimic this in game by using "back" instead of the same key again to close the hud elements when I return the axis to the middle.
....
[Seen later reply with next update]
Aaand you've done more work and replied again.
That's it from me, everything is perfect, thanks again!
Re: Universal Control Remapper (UCR) - v0.1.11 19th Feb 2017
Don't sweat it, I couldn't sleep last night so I would have been up all night coding one way or another anyway
Re: Universal Control Remapper (UCR) - v0.1.11 19th Feb 2017
Thanks! I was able to easily change it to invert the axis in the MoveCursors function.Helgef wrote:Hi, jamz.Jamz wrote:Hi, i'm hopeing UCR can solve my issue but can't seem to find a way, maybe I'm missing it?
I'm looking to invert the axis of a second mouse, is this possible?
My setup: A virtual table top/TV embedded into a table. So I want one mouse on one side of the table as normal, and a second mouse on the opposite side with it's x and y axis reversed (as they are viewing windows upside down). I really don't need a separate cursor, just the axis changed for a second mouse. (Both mice are logitech 5 button mice)
I just made a script for multiple cursors, it is very simple to add inverted axis to that, as long as you don't move both mice simultaneously, the cursor acts pretty normal. This is is a modified version if you like to try. You invert the axis by setting a negative sensitivity. (Left edit is for x, right for y). You also need to edit the the confinment rectangle under the coordinates tab. More instructions: here.@evilC. Cool stuff (the band plugin and c#). And, the mouse hook can't tell which mouse generated the movement, as far as I know.Code: Select all
; TODO, ; remove debug hotkey - and ea() func ; reconsider choise of default cursor when A_Cursor is Unknown. ; Manage click up when more than mouse is moving and click is down.... class Cursor extends _UCR.Classes.Plugin { type:="Cursor" description:="Mulitple cursor controller" static AllCursors:=[] ; Tracks all instances of this plugin static MouseIdCounter:=[] ; For tracking multiple active cursor from the same mouse id. static doNothingFunc:=ObjBindMethod(Cursor, "doNothing") ; For disabling mouse buttons. init() { this.LoadCursor() ; Loads cursor handles. ; Gui ; Some control(s) are placed at rowX, colY, defined here: row0:=" y5 ", row0_:=" y2 " row1:=" y35 ", row1_:=" y32 " row2:=" y65 ", row2_:=" y62 " row3:=" y95 ", row3_:=" y92 " row4:=" y125 ", row4_:=" y123 " row5:=" y155 ", row5_:=" y153 " row6:=" y185 ", row6_:=" y183 " row7:=" y215 ", row7_:=" y213 " ; Columns for gui placement col1:=" x25 ", col2:=" x145 ", col3:=" x260 ", col4:=" x375 ", col5:=" x490 " Gui, Add, Tab2, w650 h65, % "Main|Cycle|Coordinates|Help" Gui, Tab, 1 ; Mouse selection Gui, Add, Button, % " w100 h25 hwndselectMouseButton", % "Select mouse" this.selectMouseButton:=selectMouseButton gFunc := this.SelectMouseInit.Bind(this) GuiControl, +g, % this.selectMouseButton, % gFunc this.AddControl("Edit", "MouseIdEdit", this.MouseIdSet.Bind(this),"x+10 yp+3 w75 center") this.AddControl("InputButton", "toggle", 0, this.toggle.Bind(this),"x+10 h25") ; Toggle light this.AddControl("ButtonPreview", "bp1", 0,0, "x+10") ; Sensitivity Gui,Add,Text,x+10 yp+3,Sensitivity: this.AddControl("Edit", "sensEditX", this.sensChanged.Bind(this,1),"x+10 yp-3 w35 center",1) this.AddControl("Edit", "sensEditY", this.sensChanged.Bind(this,2),"x+5 w35 center",1) ; Hide/show option this.AddControl("Checkbox", "hideWhenInactiveCB", this.hideWhenInactiveCBChanged.Bind(this),"x+10 yp+3 checked", "Hide when inactive") Gui, Tab, 2 Gui,Add,Text,,Cycle cursors: this.AddControl("InputButton", "cycleCursor", 0, this.cycleCursor.Bind(this),"x+10 h25") this.AddControl("Checkbox", "cycleCursorCB", 0,"x+10 yp+3 checked", "Cycle by id") ; Coordinates tab Gui, Tab, 3 Gui,Add,Text, x+5,Confine to: (x1,y1) this.AddControl("Edit", "confineX1", this.confineEditChanged.Bind(this,1),"x+5 yp-3 w35 number center",0) this.AddControl("Edit", "confineY1", this.confineEditChanged.Bind(this,2),"x+5 w35 number center",0) Gui,Add,Text, x+5 yp+3,(x2,y2) this.AddControl("Edit", "confineX2", this.confineEditChanged.Bind(this,3),"x+5 yp-3 w35 number center",A_ScreenWidth) this.AddControl("Edit", "confineY2", this.confineEditChanged.Bind(this,4),"x+5 w35 number center",A_ScreenHeight) Gui,Add,Text, x+5 yp+3,Start at: (x,y) this.AddControl("Edit", "startX1", this.startEditChanged.Bind(this,1),"x+5 yp-3 w35 number center",A_ScreenWidth//2) this.AddControl("Edit", "startY1", this.startEditChanged.Bind(this,2),"x+5 w35 number center",A_ScreenHeight//2) Gui, Add, Button, % "x+5 hwndresetButton", % "Reset" this.resetButton:=resetButton gFunc := this.resetCoords.Bind(this) GuiControl, +g, % this.resetButton, % gFunc ; Help tab Gui, Tab, 4 Gui, Font, s8 w400, Courier New Gui, add, Link, % row1 col1, % "Author: Helgef (2017-03-03). " . "Instructions are available at: <a href=""https://autohotkey.com/boards/viewtopic.php?f=19&t=24538"">AutoHotkey.com forum.</a>`n" ; Misc. this.createCursor() ; Make the cursor's gui. Cursor.AllCursors.Push(this) ; The plugin is self aware. this.hasBeenToggled:=0 this.hideWhenInactive && !this.toggleState ? this.hideCursor() : this.showCursor() ; DEBUG - REMOVE ; ff:=ObjBindMethod(Cursor,"ea") ; try ; Hotkey, Esc, % ff } ; Clean up for UCR. onClose(){ GuiControl, -g, % this.selectMouseButton GuiControl, -g, % this.resetButton for k, cur in Cursor.AllCursors ; Find position in AllCursors list. if (cur=this) break Cursor.AllCursors.RemoveAt(k) ; Remove from AllCursors list Cursor.MouseIdCounter.HasKey(this.SelectedMouse) ? --Cursor.MouseIdCounter[this.SelectedMouse] : "" ; Decerment mouse id counter if !Cursor.AllCursors.length() Cursor.md.Delete(),Cursor.md:="",Cursor.toggleHotkeys("Off") ; Remove the mouse delta if no more cursor plugins exists. this.destroyCursor() ; Destroy the gui cursor. base.onClose() ;? } ; Gui functions ; Coords tab confineEditChanged(num,val){ ; Confinement rectangle. if (num=1) this.minX:=val else if (num=2) this.minY:=val else if (num=3) this.maxX:=val else if (num=4) this.maxY:=val return } startEditChanged(num,val){ ; start position if (num=1) this.oX:=val else if (num=2) this.oY:=val return } resetCoords(){ ; Reset coords button this.GuiControls.confineX1.Set(0) this.GuiControls.confineY1.Set(0) this.GuiControls.confineX2.Set(A_ScreenWidth) this.GuiControls.confineY2.Set(A_ScreenHeight) this.GuiControls.startX1.Set(A_ScreenWidth//2) this.GuiControls.startY1.Set(A_ScreenHeight//2) } sensChanged(num,val){ if val is number { if (num=1) this.sensX:=val ; Set sensitivity x-axis else if (num=2) this.sensY:=val ; Set sensitivity y-axis } return } hideWhenInactiveCBChanged(state){ state && !this.toggleState ? this.hideCursor() : this.showCursor() ; Show/hide cursor to match setting. return this.hideWhenInactive:=state ; Indicates wether cursor is hidden/shown when inactive } createCursor(){ if this.hWin return ;? this.x:=this.oX this.y:=this.oY Gui, new, +hwndhWin -caption +toolwindow +alwaysontop +E0x20 this.hWin:=hWin Gui, % hWin ": Margin",0,0 Gui, % hWin ": Color", abcdef Gui, % hWin ": Add", Picture, % "x0 y0 hwndhPic", % "hIcon:*" this.hIcons["Arrow"] this.hPic:=hPic this.setCursor() this.hs:=Cursor.GetCursorHotspot() GuiControl,, % this.hPic, % "hIcon:*" this.hIcons[this.IDC] ; Consider to remove this. ;Gui, % hWin ": Show", % "Hide NA x" this.x " y" this.y this.hidden:=1 WinSet, TransColor, abcdef 255, % "ahk_id " hWin return } ; Handle mouse selection SelectMouseInit(){ ; Starts the mouse selection procedure. ; How to: ; - Press the button ; - Move the mouse within 5 seconds. ; - Done! p:=this.SelectedMouse ; Previous mouse selectMouseMD:= new Cursor.MouseDelta(ObjBindMethod(this,"SelectMouse")) selectMouseMD.Start() timeout:=A_TickCount ; For timeout while (this.SelectedMouse=p && A_TickCount-timeout<5000) ; 5 second timeout. Sleep,-1 if (this.SelectedMouse!=p){ Cursor.MouseIdCounter.HasKey(p) ? --Cursor.MouseIdCounter[p] : "" ; A new mouse chosen Cursor.MouseIdCounter.HasKey(this.SelectedMouse) ? ++Cursor.MouseIdCounter[this.SelectedMouse] : (Cursor.MouseIdCounter[this.SelectedMouse]:=1) } selectMouseMD.Delete() ; Delete the old mouse delta return } MouseIdSet(id){ return this.SelectedMouse:=id } SelectMouse(MouseID,dx,dy) { ; Callback function for temporary mouse delta instance. if (this.selectedMouse=MouseID) return this.GuiControls.MouseIdEdit.Set(MouseID) return } ; destroy/hide/show/moveCursor() - these functions apply to the "gui cursor" not the real windows cursor. destroyCursor(){ if !this.hWin return Gui, % this.hWin ": Destroy" return this.hWin:="" } hideCursor(){ if !this.hWin return if this.hidden return Gui, % this.hWin ": Show", % "NA hide" this.hidden:=1 return } showCursor(move:=0){ ; Consider toggle aot to avoid getting under other aot win. if !this.hWin return if (this.hidden && move) return this.showCursor() else if (!this.hidden && !move) return this.hidden:=0 if (this.IDC!=this.pIDC) ; Only update if changed GuiControl,, % this.hPic, % "hIcon:*" this.hIcons[this.IDC] this.pIDC:=this.IDC if !move Gui, % this.hWin ": Show", % "NA x" this.x-this.hs[1] " y" this.y-this.hs[2] ; Adjustment for hotspot. else WinMove, % "ahk_id " this.hWin,, this.x-this.hs[1], this.y-this.hs[2] return } moveCursor(){ return this.showCursor(true) } ea(){ ; remove ExitApp } ; For suppressing native mouse button functionality doNothing(){ return } ; Main togle toggle(state) { if !state return if !this.hasBeenToggled { ; Add to MouseIdCounter on first time toggle on. Cursor.MouseIdCounter.HasKey(this.SelectedMouse) ? ++Cursor.MouseIdCounter[this.SelectedMouse] : (this.SelectedMouse ? (Cursor.MouseIdCounter[this.SelectedMouse]:=1) : "") this.hasBeenToggled:=true } this.toggleState:=!this.toggleState if (!Cursor.md && this.toggleState) { Cursor.md:=new Cursor.MouseDelta(ObjBindMethod(Cursor,"MoveCursors")) ; Create a new mousedelta for the Cursor class, if non exists. } Cursor.globalToggle(this.toggleState) if !this.toggleState { if (this.hideWhenInactive) ; Hide/show according to settings this.hideCursor() else this.showCursor() } else { this.showCursor() } this.GuiControls.bp1.SetState(this.toggleState) ; Green light when on (in UCR gui) return } globalToggle(state){ ; Sets the correct state of the MouseDelta: Cursor.md, depending on all toggleStates of all cursors in Cursor.AllCursors mdState:=Cursor.md.State if (state=mdState) ; State is matched, do nothing return else if (state && !mdState) { ; State is on, and md is off, turn on md. Cursor.toggleHotkeys("On") Cursor.md.Start() return }else if (!state && mdState) { ; State is off, but md is on, check if any other cursor is on, then do nothing, if no other is on, restore mouse functionality and turn off md. for k, cur in Cursor.AllCursors if cur.toggleState return Cursor.toggleHotkeys("Off") Cursor.md.Stop() } return } toggleHotkeys(state){ ; Normal mouse button functionality is suppressed while a cursor is active. hkfn:=Cursor.doNothingFunc Hotkey, *LButton, % hkfn, % state Hotkey, *RButton, % hkfn, % state Hotkey, *MButton, % hkfn, % state Hotkey, *WheelUp, % hkfn, % state Hotkey, *WheelDown, % hkfn, % state Hotkey, *XButton1, % hkfn, % state Hotkey, *XButton2, % hkfn, % state if (state="On") ; Disables all mouse movement when "On" BlockInput, MouseMove else BlockInput, MouseMoveOff return } ; Cycle cursors. cycleCursor(state){ if !state ; Manage hotkey up event - do nothing. return if this.GuiControls.cycleCursorCB.Get() ; Only cycle same id this.cycleSame() else this.cycleAll() } cycleSame(){ for k, cur in Cursor.AllCursors if (cur.selectedMouse!=this.selectedMouse) { ; Wrong id, continue continue } else if (cur.toggleState) { ; Turn off the first one that is on cur.toggle(1), oneOff:=1 } else if (oneOff) { ; Now we can turn on the next cur.toggle(1), oneOn:=1 break } if !oneOn ; No cursor was turned on, turn on the first with correct id. for k, cur in Cursor.AllCursors if (cur.selectedMouse=this.selectedMouse) { cur.toggle(1) break } return } cycleAll(){ ; Find the next cursor, turn it on for k, cur in Cursor.AllCursors if (cur.toggleState) { ; Turn off the first one that is on cur.toggle(1), oneOff:=1 } else if (oneOff) { cur.toggle(1), oneOn:=1 ; Turn on the next one break } if !oneOn Cursor.AllCursors[1].toggle(1) ; No cursor turned on, turn on the first one. return } MoveCursors(MouseID, dx, dy, usButtonFlags, usButtonData){ ; Move all cursors for the MouseId that generated the movement, dx,dy. ; Confine to (x,y) ∈ [cur.MinW,cur.MaxW]x[cur.MinH,cur.MaxH] ; Note: hide/show/move Cursor functions refer to the cursor GUI representation, not the actual (windows) cursor. Cursor.md.SetState(0) SetWinDelay,-1 SetMouseDelay,-1 CoordMode, Mouse, Screen idCtr:=Cursor.MouseIdCounter[MouseID] for k, cur in Cursor.AllCursors ; Consider all cursors. each "cur" is one instances of the plugin. if (cur.toggleState && cur.SelectedMouse=MouseID) { cur.x+=dx?dx*cur.sensX:0 ; Update position cur.y+=dy?dy*cur.sensY:0 cur.x:= cur.x>cur.MaxX?cur.MaxX:(cur.x<cur.MinX?cur.MinX:cur.x) ; Confine cur.y:= cur.y>cur.MaxY?cur.MaxY:(cur.y<cur.MinY?cur.MinY:cur.y) MouseMove, cur.x, cur.y, 0 cs:=A_Cursor cur.hs:=Cursor.GetCursorHotspot(cs) ; Adjustment for gui, w.r.t cursor hotspot cur.setCursor(cs) --idCtr if !(idCtr) { cur.hideCursor() ; Hide the gui, the real mouse cursor is showing } else { cur.moveCursor() ; Move the gui, the real mouse is not here } if usButtonFlags Cursor.MouseButtons(usButtonFlags,usButtonData) ; Handle mouse buttons } else if (cur.toggleState && cur.hidden) { ; Show the gui, the real cursor has moved elsewhere. cur.showCursor() } Cursor.md.SetState(1) return } setCursor(CursorStyle:=""){ ; Decides which cursor image to show on the cursor gui. if !CursorStyle CursorStyle:=A_Cursor if (this.IDC=CursorStyle) return if this.hIcons.HasKey(CursorStyle) this.IDC:=CursorStyle else this.IDC:="Arrow" ; reconsider this choise. This is when A_Cursor is "Unknown" <--- NOTE return } MouseButtons(usButtonFlags,usButtonData){ /* RAWMOUSE structure URL:https://msdn.microsoft.com/en-us/library/windows/desktop/ms645578(v=vs.85).aspx usButtonFlags: The transition state of the mouse buttons. This member can be one or more of the following values: RI_MOUSE_LEFT_BUTTON_DOWN:=0x0001 RI_MOUSE_LEFT_BUTTON_UP:=0x0002 RI_MOUSE_RIGHT_BUTTON_DOWN:=0x0004 RI_MOUSE_RIGHT_BUTTON_UP:=0x0008 RI_MOUSE_MIDDLE_BUTTON_DOWN:=0x0010 RI_MOUSE_MIDDLE_BUTTON_UP:=0x0020 RI_MOUSE_BUTTON_4_DOWN:=0x0040 RI_MOUSE_BUTTON_4_UP:=0x0080 RI_MOUSE_BUTTON_5_DOWN:=0x100 RI_MOUSE_BUTTON_5_UP:=0x0200 RI_MOUSE_WHEEL:=0x0400 usButtonData: If usButtonFlags is RI_MOUSE_WHEEL, this member is a signed value that specifies the wheel delta. */ static ClickParams:= { 1:"Down", 2:"Up", 4:"Down Right", 8:"Up Right" , 16:"Down Middle", 32:"Up Middle", 64:"Down X1", 128:"Up X1" , 256:"Down X2", 512:"Up X2", 1024:"Wheel" } ; 1024:Wheel isn't used. static WheelDelta:=120 (RI_MOUSE_WHEEL:= usButtonFlags & 1024) ? usButtonFlags^=1024 : "" while usButtonFlags { if ((paramNum:=2**(A_Index-1)) & usButtonFlags) { SendInput, % "{Click " . ClickParams[paramNum] . "}" usButtonFlags^=paramNum } } if RI_MOUSE_WHEEL ; Mouse wheel Loop, % Round(abs(usButtonData)/WheelDelta) SendInput % "{Click " . (usButtonData>0 ? "WheelUp" : "WheelDown") . "}" return } LoadCursor(){ ; Url https://msdn.microsoft.com/en-us/library/windows/desktop/ms648391%28v=vs.85%29.aspx static cursorIds:={ IDC_ARROW :32512 , IDC_IBEAM :32513 , IDC_WAIT :32514 , IDC_CROSS :32515 , IDC_UPARROW :32516 , IDC_SIZE :32640 , IDC_ICON :32641 , IDC_SIZENWSE :32642 , IDC_SIZENESW :32643 , IDC_SIZEWE :32644 , IDC_SIZENS :32645 , IDC_SIZEALL :32646 , IDC_NO :32648 , IDC_HAND :32649 , IDC_APPSTARTING :32650 , IDC_HELP :32651 } this.hIcons:={} for IDC, id in cursorIds this.hIcons[SubStr(IDC,5)]:=DllCall("LoadCursor", "Uint", 0, "Uint", id) return } GetCursorHotspot(CursorStyle:=""){ ; Url : ; - https://msdn.microsoft.com/en-us/library/windows/desktop/ms648389%28v=vs.85%29.aspx (GetCursorInfo) ; - https://msdn.microsoft.com/en-us/library/windows/desktop/ms648381(v=vs.85).aspx (Cursorinfo struct) ; struct cursorinfo { ; DWORD cbSize; ; DWORD flags; ; HCURSOR hCursor; ; POINT ptScreenPos; ; } ; Url: ; - https://msdn.microsoft.com/en-us/library/windows/desktop/ms648070(v=vs.85).aspx (GetIconInfo) ; - https://msdn.microsoft.com/en-us/library/windows/desktop/ms648052(v=vs.85).aspx (Iconinfo struct) ; ; struct _ICONINFO { ; BOOL fIcon; fIcon := NumGet(&PICONINFO, 0,"Int") ; DWORD xHotspot; ; DWORD yHotspot; ; HBITMAP hbmMask; hBMMask := NumGet(&PICONINFO,12,"UPtr") ; HBITMAP hbmColor; BMColor := NumGet(&PICONINFO,12+A_PtrSize,"UPtr") ; } ; ; Get Cursor handle static hotspots:={} if !CursorStyle CursorStyle:=A_Cursor if hotspots.Haskey(CursorStyle) return hotspots[CursorStyle] VarSetCapacity(pci, (cbSize:=16+A_PtrSize), 0) NumPut(cbSize,&pci,"Uint") DllCall("GetCursorInfo", "UPtr", &pci) hCursor := NumGet(pci, 8, "UPtr") ; Get icon info - cursor hotspot VarSetCapacity(PICONINFO, 12+2*A_PtrSize, 0) DllCall("GetIconInfo", "Ptr", hCursor, "Ptr", &PICONINFO) xHotspot := NumGet(&PICONINFO, 4,"UInt") yHotspot := NumGet(&PICONINFO, 8,"UInt") hs:=[xHotspot?xHotspot:0, yHotspot?yHotspot:0] hotspots[CursorStyle]:=hs return hs } ; Credits, Class MouseDelta: https://autohotkey.com/boards/viewtopic.php?f=19&t=10159 ; This version is slightly modified. Class MouseDelta { State := 0 __New(callback){ this.MouseMovedFn := this.MouseMoved.Bind(this) this.Callback := callback } Start(){ static DevSize := 8 + A_PtrSize, RIDEV_INPUTSINK := 0x00000100 ; Register mouse for WM_INPUT messages. VarSetCapacity(RAWINPUTDEVICE, DevSize) NumPut(1, RAWINPUTDEVICE, 0, "UShort") NumPut(2, RAWINPUTDEVICE, 2, "UShort") NumPut(RIDEV_INPUTSINK, RAWINPUTDEVICE, 4, "Uint") ; WM_INPUT needs a hwnd to route to, so get the hwnd of the AHK Gui. ; It doesn't matter if the GUI is showing, it still exists Gui +hwndhwnd NumPut(hwnd, RAWINPUTDEVICE, 8, "Uint") this.RAWINPUTDEVICE := RAWINPUTDEVICE DllCall("RegisterRawInputDevices", "Ptr", &RAWINPUTDEVICE, "UInt", 1, "UInt", DevSize ) OnMessage(0x00FF, this.MouseMovedFn) this.State := 1 return this ; allow chaining } Stop(){ static RIDEV_REMOVE := 0x00000001 static DevSize := 8 + A_PtrSize OnMessage(0x00FF, this.MouseMovedFn, 0) RAWINPUTDEVICE := this.RAWINPUTDEVICE NumPut(RIDEV_REMOVE, RAWINPUTDEVICE, 4, "Uint") DllCall("RegisterRawInputDevices", "Ptr", &RAWINPUTDEVICE, "UInt", 1, "UInt", DevSize ) this.State := 0 return this ; allow chaining } SetState(state){ if (state && !this.State) this.Start() else if (!state && this.State) this.Stop() return this ; allow chaining } Delete(){ this.Stop() this.MouseMovedFn := "" } ; Called when the mouse moved. ; Messages tend to contain small (+/- 1) movements, and happen frequently (~20ms) MouseMoved(wParam, lParam){ Critical ; RawInput statics static DeviceSize := 2 * A_PtrSize, iSize := 0, sz := 0, pcbSize:=8+2*A_PtrSize, offsets := {usButtonFlags:12+A_PtrSize*2, usButtonData:14+A_PtrSize*2, x: (20+A_PtrSize*2), y: (24+A_PtrSize*2)}, uRawInput ; Get hDevice from RAWINPUTHEADER to identify which mouse this data came from VarSetCapacity(header, pcbSize, 0) If (!DllCall("GetRawInputData", "UPtr", lParam, "uint", 0x10000005, "UPtr", &header, "Uint*", pcbSize, "Uint", pcbSize) or ErrorLevel) Return 0 if !(ThisMouse := NumGet(header, 8, "UPtr")) return ; Find size of rawinput data - only needs to be run the first time. if (!iSize){ r := DllCall("GetRawInputData", "UInt", lParam, "UInt", 0x10000003, "Ptr", 0, "UInt*", iSize, "UInt", 8 + (A_PtrSize * 2)) VarSetCapacity(uRawInput, iSize) } sz := iSize ; param gets overwritten with # of bytes output, so preserve iSize ; Get RawInput data r := DllCall("GetRawInputData", "UInt", lParam, "UInt", 0x10000003, "Ptr", &uRawInput, "UInt*", sz, "UInt", 8 + (A_PtrSize * 2)) ; Ensure we always report a number for an axis. Needed? - No it is overwritten by the numget. dx := NumGet(&uRawInput, offsets.x, "Int") dy := NumGet(&uRawInput, offsets.y, "Int") usButtonFlags:=NumGet(&uRawInput, offsets.usButtonFlags, "UShort") ; Transition state of the mouse buttons. usButtonData:=NumGet(&uRawInput, offsets.usButtonData, "Short") ; Contains mouse wheel delta, if usButtonFlags=RI_MOUSE_WHEEL=0x0400 this.Callback.(ThisMouse, dx, dy, usButtonFlags, usButtonData) } } }
Cheers!
Is there a way so it doesn't require a bind key to toggle the mice? Right now, it seems I have to bind a key that turns this control on/off?
Re: Universal Control Remapper (UCR) - v0.1.11 19th Feb 2017
Ok, good, but you shouln't have to change the code for that, there is a sensitivity setting, set the first edit to -1 to invert the x-axis, similarily, the second edit is for the y-axis. But if it works for youJamz wrote: Thanks! I was able to easily change it to invert the axis in the MoveCursors function.
That is how it is now, yes. You should make two instances of the plugin, one for each mouse, and only set up one to be inverted. Toggle both on, and go about your business as usual. It seems like the mouse ids changes on reboot, so one has to set it up every time one reboots anyways.Jamz wrote:Is there a way so it doesn't require a bind key to toggle the mice? Right now, it seems I have to bind a key that turns this control on/off?
I think it will be very easy to write a script dedicated for this purpose only, using MouseDelta, as I mentioned to gwarble in the other thread, I saw you asked there too (EitherMouse).
Cheers.
Re: Universal Control Remapper (UCR) - v0.1.11 19th Feb 2017
Ahh! I didn't get that the first time lol, that would have saved time using the sensitivity setting...Helgef wrote:Ok, good, but you shouln't have to change the code for that, there is a sensitivity setting, set the first edit to -1 to invert the x-axis, similarily, the second edit is for the y-axis. But if it works for youJamz wrote: Thanks! I was able to easily change it to invert the axis in the MoveCursors function.That is how it is now, yes. You should make two instances of the plugin, one for each mouse, and only set up one to be inverted. Toggle both on, and go about your business as usual. It seems like the mouse ids changes on reboot, so one has to set it up every time one reboots anyways.Jamz wrote:Is there a way so it doesn't require a bind key to toggle the mice? Right now, it seems I have to bind a key that turns this control on/off?
I think it will be very easy to write a script dedicated for this purpose only, using MouseDelta, as I mentioned to gwarble in the other thread, I saw you asked there too (EitherMouse).
Cheers.
And ya, I was looking at EitherMouse as well. I also looked at creating a simple script myself and got it to work fine for all mice, but I was getting lost in the code on how to detect the mouse (I was fine with hardcoding it). The eithermouse code was pretty hard to follow but I may take another look at your code, it was easier to follow.
I did run into an issue using it last night as well, when using it with MapTool (a java program), the cursor didn't change to match java's cursor change (so cursors overlapped). That and 2 second mouse seem to give a lot of false double clicks when using it. I'm fine with a single cursor as well so may still need a simpler custom solution.
But until then, UCR "works" for now!
Re: Universal Control Remapper (UCR) - v0.1.11 19th Feb 2017
I'm not surprised by the issues you describe.
I have some ideas for this I want to try. Maybe later tonight, if time be.
I have some ideas for this I want to try. Maybe later tonight, if time be.
Re: Universal Control Remapper (UCR) - v0.1.11 19th Feb 2017
OK, so now I see what's going on. Being new to AHK, hard to tell what is custom libs and native.
So, I basically took the Sniper example from the MouseDelta example (thanks!) and just changed the scale to negative and added if (MouseID == SecondMouseID) and bam!
And FYI: the double-clicking issue is on my end. Apparently my mouse is dying and behaving badly resulting in double left clicks...
So, I basically took the Sniper example from the MouseDelta example (thanks!) and just changed the scale to negative and added if (MouseID == SecondMouseID) and bam!
And FYI: the double-clicking issue is on my end. Apparently my mouse is dying and behaving badly resulting in double left clicks...
Re: Universal Control Remapper (UCR) - v0.1.11 19th Feb 2017
Hmm, strange.
I would have suggested the sniper script tweak but I did not think it would work. Because it does not hide the original input, I would have thought that just inverting the input would result in the real and fake movement cancelling each other out. I suppose you could invert and double, but I thought that would be jittery and odd.
I would have suggested the sniper script tweak but I did not think it would work. Because it does not hide the original input, I would have thought that just inverting the input would result in the real and fake movement cancelling each other out. I suppose you could invert and double, but I thought that would be jittery and odd.
Re: Universal Control Remapper (UCR) - v0.1.11 19th Feb 2017
Pretty much what I had in mind, but I would hide the input using BlockInput. I guess it is good enough without the BlockInput, but, there is some jitter on one of mice. I guess it depends some on the mouse's sensitivity/acceleration/dpi settings.
Jamz, if you try to confine the mouse to only one axis, you might notice the jitter. Eg, set
Anyways, I glad you solved your problem
Cheers!
Jamz, if you try to confine the mouse to only one axis, you might notice the jitter. Eg, set
Code: Select all
if (MouseID==SomeId){
x *= -1
Cheers!
Re: Universal Control Remapper (UCR) - v0.1.12 7th Mar 2017
New version released
Code: Select all
0.1.12 - 7th Mar 2017
+ Added AxisRangeToButtons plugin
This allows you to map an axis to up to 10 buttons
Each button has a "Low" and "High" value. If the axis is in this range...
Then the button is pressed. Else it is released.
If you need more buttons, more can be added by editing the source code.
+ You can now duplicate profiles using the "Copy" button in the toolbox.
Code contributed by Snoothy - thankyou!
+ You can now rename plugins to the same name but with different case
+ IOControl menus are now initialized when first opened.
This will mainly benefit users with large numbers of profiles.
UCR should load quicker, and you should not get a "Too many menus" error.
Code contributed by Snoothy - thankyou!
+ MaxThreads setting cranked up to 255
This seems to alleviate issues with fast, simultaneous input.
+ The InputDelta now self-updates Seen Mice
Re: Universal Control Remapper (UCR) - v0.1.12 7th Mar 2017
Just a quick heads-up that the axis previews in "Remapper (Axis Merger)" plugin don't show, and layout is a bit messed up (UCR v0.1.12).
From looking in Control Panel - Game Controllers while testing, it seems I can't use the same axis input for different axis's outputs anymore? I have a setup with three pedals (G27), where I merged the Accelerator and Clutch pedal to one vJoy axis, and "reused" the Accelerator merged with the Brake pedal to another vJoy axis. Works great in UCR 0.0.15. (vJoy 2.1.6.20 and also 2.1.8.33 judging from the preview in UCR).
Also, I'm unsure if "Uninstall SCVPBus.." works. I haven't tried to use the vxbox yet. But when I tried to uninstall it (after installing ofc), there is no change in the IOClasses menu (install SCVPBus is still grayed out, I can run uninstall SCVPBus again though..).
Great work btw, it's an awesome utility you are making!
From looking in Control Panel - Game Controllers while testing, it seems I can't use the same axis input for different axis's outputs anymore? I have a setup with three pedals (G27), where I merged the Accelerator and Clutch pedal to one vJoy axis, and "reused" the Accelerator merged with the Brake pedal to another vJoy axis. Works great in UCR 0.0.15. (vJoy 2.1.6.20 and also 2.1.8.33 judging from the preview in UCR).
Also, I'm unsure if "Uninstall SCVPBus.." works. I haven't tried to use the vxbox yet. But when I tried to uninstall it (after installing ofc), there is no change in the IOClasses menu (install SCVPBus is still grayed out, I can run uninstall SCVPBus again though..).
Great work btw, it's an awesome utility you are making!
Re: Universal Control Remapper (UCR) - v0.1.12 7th Mar 2017
Thanks for the bug reports raron, will look into them.
Any idea when the axis re-use broke? 0.0.15 is a fair way back, did it work on any 0.1.x versions?
All previous versions are available here, so if you can be bothered, working out which version it broke in may well help me work out what is wrong.
Any idea when the axis re-use broke? 0.0.15 is a fair way back, did it work on any 0.1.x versions?
All previous versions are available here, so if you can be bothered, working out which version it broke in may well help me work out what is wrong.
Re: Universal Control Remapper (UCR) - v0.1.12 7th Mar 2017
I hadn't updated in a while, so I didn't know when re-use broke. I just randomly decided to try to update it now
But I briefly tested UCR Alpha 0.0.16 through Alpha 0.1.12 just now. I used the latest vJoy I installed (vJoy 2.1.8.33), and I basically only tested the "Remapper (Axis Merger)" plugin with my G27 pedals. When preview didn't work I used Game Controller properties in Control Panel. Also, I only started the "UCR.exe" from its folder when testing. Using Windows 7 Ultimate, 64-bit.
In short, the latest that works (with axis re-use) is UCR Alpha 0.0.17.
But I briefly tested UCR Alpha 0.0.16 through Alpha 0.1.12 just now. I used the latest vJoy I installed (vJoy 2.1.8.33), and I basically only tested the "Remapper (Axis Merger)" plugin with my G27 pedals. When preview didn't work I used Game Controller properties in Control Panel. Also, I only started the "UCR.exe" from its folder when testing. Using Windows 7 Ultimate, 64-bit.
In short, the latest that works (with axis re-use) is UCR Alpha 0.0.17.
- Alpha 0.0.16: Works. Nice controller selection, enumumerates vJoys.
- Alpha 0.0.17: Works. Also nice controller input selection. Output axis selection menu don't enumerate nr. of vJoys.
- Alpha 0.0.18: I can't select vJoy as output, instead it says "Titan axis" when trying to. So couldn't really test it.
- Alpha 0.1.0 to 0.1.3: I get 5-6 errors (invalid GUI name, other stuff), but selecting continue anyway starts UCR. G27 (stick #4 atm) axis #4 was unselectable. Looses input axis invert setting.
- Alpha 0.1.4 to 0.1.8: Same as above (not sure about invert setting), but G27 axis #4 was selectable again.
- Alpha 0.1.9 - 0.1.10: Started without errors, otherwise same as 0.1.8
- Alpha 0.1.11: As above, except axis preview don't show
- Alpha 0.1.12: As above, except added plugin names are missing (they are numbered though, unless renamed).
Re: Universal Control Remapper (UCR) - v0.1.12 7th Mar 2017
Awesome, thanks for taking the time to do that.
0.0.x -> 0.1.x was quite a major rewrite in terms of the code that handles this feature, so it is understandable that it broke then.
I will try and work out what has changed and fix it.
I need to do some work in this area anyway - it has never been possible, for example, to have the same keyboard key bound to multiple inputs, but it is technically feasible.
So I should do some work to harmonize the feature set across keyboard and joystick, and this would fall under that.
Regarding showing available vJoy sticks, yeah, that also is something I need to revisit. I had not planned on doing this yet though, because I am in the process of implementing a replacement for AHK's joystick support (Which will bring us full 8 axis, 128 button support) so I will quite possibly have to re-write the code that supports this feature in order to implement that.
RE: Controller names - I had to remove the old implementation because it was rendering UCR unusable for some people - the new joystick detection engine can get names, so once I implement that, this feature will return.
To verify true state, you can also open an admin command prompt to the Resources folder and run the install / uninstall bat files
0.0.x -> 0.1.x was quite a major rewrite in terms of the code that handles this feature, so it is understandable that it broke then.
I will try and work out what has changed and fix it.
I need to do some work in this area anyway - it has never been possible, for example, to have the same keyboard key bound to multiple inputs, but it is technically feasible.
So I should do some work to harmonize the feature set across keyboard and joystick, and this would fall under that.
Regarding showing available vJoy sticks, yeah, that also is something I need to revisit. I had not planned on doing this yet though, because I am in the process of implementing a replacement for AHK's joystick support (Which will bring us full 8 axis, 128 button support) so I will quite possibly have to re-write the code that supports this feature in order to implement that.
RE: Controller names - I had to remove the old implementation because it was rendering UCR unusable for some people - the new joystick detection engine can get names, so once I implement that, this feature will return.
If you do "Show vJoy Log", it will show some logging which may help determine if it thinks SCPVBus is installed.raron wrote:Also, I'm unsure if "Uninstall SCVPBus.." works. I haven't tried to use the vxbox yet. But when I tried to uninstall it (after installing ofc), there is no change in the IOClasses menu (install SCVPBus is still grayed out, I can run uninstall SCVPBus again though..).
To verify true state, you can also open an admin command prompt to the Resources folder and run the install / uninstall bat files
Return to “Gaming Scripts (v1)”
Who is online
Users browsing this forum: No registered users and 40 guests