Joystick v1.0, Windows, 6 axes, 32 button, POV, deadzones

Share your advanced PureBasic knowledge/code with the community.
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Joystick v1.0, Windows, 6 axes, 32 button, POV, deadzones

Post by Rescator »

Code: Select all

;Joystick Include v1.0 (c) Roger Hågensen, EmSai 2011.
;zlib license, http://en.wikipedia.org/wiki/Zlib_license

;Warning!
;structures and fields marked ;Internal are just that, they are used by the joystick functions() or the OS API.
;structures or fields marked as ;Readable can be read but must not be written to directly.
;structures or fields marked as ;Writable can be written to but must not be read from directly.
;structures or fields marked ;Readable/Writable can be read and/or written to directly.

Structure JOYSTICK_INFO_CAPS ;Internal
 	wPeriodMin.l
 	wPeriodMax.l
 	wNumButtons.l
 	wNumAxes.l
 	wXmin.l
 	wXmax.l
 	wYmin.l
 	wYmax.l
 	wZmin.l
 	wZmax.l
 	wRmin.l
 	wRmax.l
 	wUmin.l
 	wUmax.l
 	wVmin.l
 	wVmax.l
 	wPOV.l
	wMid.w
	wPid.w
	name.s
EndStructure
Structure JOYSTICK_INFO_DEADZONE ;Internal/Debugging
	c.f ;Readable
	e.f ;Readable
EndStructure
Structure JOYSTICK_INFO
 	x.f ;Readable
 	y.f ;Readable
 	z.f ;Readable
 	r.f ;Readable
 	u.f ;Readable
 	v.f ;Readable
 	pov.f ;Readable
	button.f[32] ;Readable

	;The following fields may or may not be changed in the future.
	deadzonex.JOYSTICK_INFO_DEADZONE ;Internal
	deadzoney.JOYSTICK_INFO_DEADZONE ;Internal
	deadzonez.JOYSTICK_INFO_DEADZONE ;Internal
	deadzoner.JOYSTICK_INFO_DEADZONE ;Internal
	deadzoneu.JOYSTICK_INFO_DEADZONE ;Internal
	deadzonev.JOYSTICK_INFO_DEADZONE ;Internal

	;The following fields are internal only, and may change at any time.
	id.i ;Internal
	jic.JOYSTICK_INFO_CAPS ;Internal
	pji.JOYINFOEX ;Internal
EndStructure

Procedure.i Joystick_Alloc(joystick.i)
	Protected *jsi.JOYSTICK_INFO,pji.JOYINFOEX,pjc.JOYCAPS,error,text$,hkey.i,regtype.l,regsize.l
	pji\dwSize=SizeOf(JOYINFOEX)
	pji\dwFlags=#JOY_RETURNALL|#JOY_RETURNPOVCTS
	If joyGetPosEx_(joystick,pji)=#JOYERR_NOERROR
		error=joyGetDevCaps_(joystick,pjc,SizeOf(JOYCAPS))
		If error=#JOYERR_NOERROR
			*jsi=AllocateMemory(SizeOf(JOYSTICK_INFO))
			If *jsi
				*jsi\id=joystick
				*jsi\jic\name="Joystick/Gamepad "+Str(joystick+1)
				text$="System\CurrentControlSet\Control\MediaProperties\PrivateProperties\Joystick\OEM\VID_"+RSet(Hex(pjc\wMid,#PB_Word),4,"0")+"&PID_"+RSet(Hex(pjc\wPid,#PB_Word),4,"0")
				If RegOpenKeyEx_(#HKEY_CURRENT_USER,text$,#Null,#KEY_QUERY_VALUE,@hkey)=#ERROR_SUCCESS
					If RegQueryValueEx_(hkey,"OEMName",#Null,#Null,#Null,@regsize)=#ERROR_SUCCESS
						text$=Space(regsize+1)
						If RegQueryValueEx_(hkey,"OEMName",#Null,@regtype,@text$,@regsize)=#ERROR_SUCCESS
							If regtype=#REG_SZ
								*jsi\jic\name=Left(text$,255)
							EndIf
						EndIf
					EndIf
					RegCloseKey_(hkey)
				EndIf
				*jsi\jic\wMid=pjc\wMid
				*jsi\jic\wPid=pjc\wPid
				*jsi\jic\wPeriodMin=pjc\wPeriodMin
				*jsi\jic\wPeriodMax=pjc\wPeriodMax
				*jsi\jic\wNumButtons=pjc\wNumButtons
				*jsi\jic\wNumAxes=pjc\wNumAxes
				*jsi\jic\wXmin=pjc\wXmin
				*jsi\jic\wXmax=pjc\wXmax
				*jsi\jic\wYmin=pjc\wYmin
				*jsi\jic\wYmax=pjc\wYmax
				If (pjc\wCaps&#JOYCAPS_HASPOV)
					*jsi\jic\wPOV=#True
				EndIf
				If (pjc\wCaps&#JOYCAPS_HASZ)
					*jsi\jic\wZmin=pjc\wZmin
					*jsi\jic\wZmax=pjc\wZmax
				EndIf
				If (pjc\wCaps&#JOYCAPS_HASR)
					*jsi\jic\wRmin=pjc\wRmin
					*jsi\jic\wRmax=pjc\wRmax
				EndIf
				If (pjc\wCaps&#JOYCAPS_HASU)
					*jsi\jic\wUmin=pjc\wUmin
					*jsi\jic\wUmax=pjc\wUmax
				EndIf
				If (pjc\wCaps&#JOYCAPS_HASV)
					*jsi\jic\wVmin=pjc\wVmin
					*jsi\jic\wVmax=pjc\wVmax
				EndIf
				*jsi\pji\dwSize=SizeOf(JOYINFOEX)
				*jsi\pji\dwFlags=#JOY_RETURNALL|#JOY_RETURNPOVCTS
			EndIf
		EndIf
	EndIf
	ProcedureReturn *jsi
EndProcedure

Procedure.i Joystick_Free(*jsi.JOYSTICK_INFO)
	If *jsi
		FreeMemory(*jsi)
	EndIf
	ProcedureReturn #Null
EndProcedure

Procedure.i Joystick_Update(*jsi.JOYSTICK_INFO)
	Protected result.i,value.l,min.l,max.l,n.i,i.i
	If *jsi
		If JoyGetPosEx_(*jsi\id,*jsi\pji)=#JOYERR_NOERROR

			min=*jsi\jic\wXmin
			max=*jsi\jic\wXmax
			value=*jsi\pji\dwXpos
			If value<=min
				value=0
			ElseIf value>=max
				value=65535
			EndIf
			;Note! Centered is actually 32767 while it ideally should be 32767.5, which is why this adjustment is needed.
			If value<32768
				value-32767
			Else
				value-32768
			EndIf
			*jsi\x=value/32767.0
			If *jsi\deadzonex\c Or *jsi\deadzonex\e
				If Abs(*jsi\x)<*jsi\deadzonex\c
					*jsi\x=0.0
				ElseIf *jsi\x>(1.0-*jsi\deadzonex\e)
					*jsi\x=1.0
				ElseIf *jsi\x<(-1.0+*jsi\deadzonex\e)
					*jsi\x=-1.0
				EndIf
			EndIf

			min=*jsi\jic\wYmin
			max=*jsi\jic\wYmax
			value=*jsi\pji\dwYpos
			If value<=min
				value=0
			ElseIf value>=max
				value=65535
			EndIf
			If value<32768
				value-32767
			Else
				value-32768
			EndIf
			*jsi\y=value/32767.0
			If *jsi\deadzoney\c Or *jsi\deadzoney\e
				If Abs(*jsi\y)<*jsi\deadzoney\c
					*jsi\y=0.0
				ElseIf *jsi\y>(1.0-*jsi\deadzoney\e)
					*jsi\y=1.0
				ElseIf *jsi\y<(-1.0+*jsi\deadzoney\e)
					*jsi\y=-1.0
				EndIf
			EndIf

			min=*jsi\jic\wZmin
			max=*jsi\jic\wZmax
			If min|max
				value=*jsi\pji\dwZpos
				If value<=min
					value=0
				ElseIf value>=max
					value=65535
				EndIf
				If value<32768
					value-32767
				Else
					value-32768
				EndIf
				*jsi\z=value/32767.0
				If *jsi\deadzonez\c Or *jsi\deadzonez\e
					If Abs(*jsi\z)<*jsi\deadzonez\c
						*jsi\z=0.0
					ElseIf *jsi\z>(1.0-*jsi\deadzonez\e)
						*jsi\z=1.0
					ElseIf *jsi\z<(-1.0+*jsi\deadzonez\e)
						*jsi\z=-1.0
					EndIf
				EndIf
			EndIf

			min=*jsi\jic\wRmin
			max=*jsi\jic\wRmax
			If min|max
				value=*jsi\pji\dwRpos
				If value<=min
					value=0
				ElseIf value>=max
					value=65535
				EndIf
				If value<32768
					value-32767
				Else
					value-32768
				EndIf
				*jsi\r=value/32767.0
				If *jsi\deadzoner\c Or *jsi\deadzoner\e
					If Abs(*jsi\r)<*jsi\deadzoner\c
						*jsi\r=0.0
					ElseIf *jsi\r>(1.0-*jsi\deadzoner\e)
						*jsi\r=1.0
					ElseIf *jsi\r<(-1.0+*jsi\deadzoner\e)
						*jsi\r=-1.0
					EndIf
				EndIf
			EndIf

			min=*jsi\jic\wUmin
			max=*jsi\jic\wUmax
			If min|max
				value=*jsi\pji\dwUpos
				If value<=min
					value=0
				ElseIf value>=max
					value=65535
				EndIf
				If value<32768
					value-32767
				Else
					value-32768
				EndIf
				*jsi\u=value/32767.0
				If *jsi\deadzoneu\c Or *jsi\deadzoneu\e
					If Abs(*jsi\u)<*jsi\deadzoneu\c
						*jsi\u=0.0
					ElseIf *jsi\u>(1.0-*jsi\deadzoneu\e)
						*jsi\u=1.0
					ElseIf *jsi\u<(-1.0+*jsi\deadzoneu\e)
						*jsi\u=-1.0
					EndIf
				EndIf
			EndIf

			min=*jsi\jic\wVmin
			max=*jsi\jic\wVmax
			If min|max
				value=*jsi\pji\dwVpos
				If value<=min
					value=0
				ElseIf value>=max
					value=65535
				EndIf
				If value<32768
					value-32767
				Else
					value-32768
				EndIf
				*jsi\v=value/32767.0
				If *jsi\deadzonev\c Or *jsi\deadzonev\e
					If Abs(*jsi\v)<*jsi\deadzonev\c
						*jsi\v=0.0
					ElseIf *jsi\v>(1.0-*jsi\deadzonev\e)
						*jsi\v=1.0
					ElseIf *jsi\v<(-1.0+*jsi\deadzonev\e)
						*jsi\v=-1.0
					EndIf
				EndIf
			EndIf

			If *jsi\jic\wPOV
				value=*jsi\pji\dwPOV
				If value=0
					value=36000
				ElseIf value<1 Or value>35999
					value=0
				EndIf
				*jsi\pov=value/36000.0
				;no deadzone as this is a digital input rather than analog
			EndIf

			n=*jsi\jic\wNumButtons-1
			For i=0 To n
				If *jsi\pji\dwButtons&(1<<i)
					*jsi\button[i]=1.0
				Else
					*jsi\button[i]=0.0
				EndIf
				;no deadzone as this is a digital input rather than analog
			Next

			result=#True
		Else
			*jsi\pov=0.0
			*jsi\x=0.0
			*jsi\y=0.0
			*jsi\z=0.0
			*jsi\r=0.0
			*jsi\u=0.0
			*jsi\v=0.0
			n=*jsi\jic\wNumButtons-1
			For i=0 To n
				*jsi\button[i]=0.0
			Next
		EndIf
	EndIf
	ProcedureReturn result
EndProcedure

Procedure.i Joystick_Count()
	Protected result.i,pji.JOYINFOEX,i.i
	pji\dwSize=SizeOf(JOYINFOEX)
	While joyGetPosEx_(i,pji)=#JOYERR_NOERROR
		i+1
		result+1
	Wend
	ProcedureReturn result
EndProcedure

Procedure.s Joystick_OEMName(*jsi.JOYSTICK_INFO)
	Protected result.s
	If *jsi
		result=*jsi\jic\name
	EndIf
	ProcedureReturn result
EndProcedure

Procedure.i Joystick_OEMID(*jsi.JOYSTICK_INFO)
	Protected result.i
	If *jsi
		result=(*jsi\jic\wMid<<16)|*jsi\jic\wPid
	EndIf
	ProcedureReturn result
EndProcedure

Procedure.i Joystick_ButtonCount(*jsi.JOYSTICK_INFO)
	Protected result.i
	If *jsi
		result=*jsi\jic\wNumButtons
	EndIf
	ProcedureReturn result
EndProcedure

Procedure.i Joystick_AxesCount(*jsi.JOYSTICK_INFO)
	Protected result.i
	If *jsi
		result=*jsi\jic\wNumAxes
	EndIf
	ProcedureReturn result
EndProcedure

Procedure.i Joystick_HasPOV(*jsi.JOYSTICK_INFO)
	Protected result.i
	If *jsi
		result=*jsi\jic\wPOV
	EndIf
	ProcedureReturn result
EndProcedure

Procedure.i Joystick_HasZ(*jsi.JOYSTICK_INFO)
	Protected result.i
	If *jsi
		If *jsi\jic\wZmin|*jsi\jic\wZmax
			result=#True
		EndIf
	EndIf
	ProcedureReturn result
EndProcedure

Procedure.i Joystick_HasR(*jsi.JOYSTICK_INFO)
	Protected result.i
	If *jsi
		If *jsi\jic\wRmin|*jsi\jic\wRmax
			result=#True
		EndIf
	EndIf
	ProcedureReturn result
EndProcedure

Procedure.i Joystick_HasU(*jsi.JOYSTICK_INFO)
	Protected result.i
	If *jsi
		If *jsi\jic\wUmin|*jsi\jic\wUmax
			result=#True
		EndIf
	EndIf
	ProcedureReturn result
EndProcedure

Procedure.i Joystick_HasV(*jsi.JOYSTICK_INFO)
	Protected result.i
	If *jsi
		If *jsi\jic\wVmin|*jsi\jic\wVmax
			result=#True
		EndIf
	EndIf
	ProcedureReturn result
EndProcedure

#JOYSTICK_DEADZONE_ALL=0
#JOYSTICK_DEADZONE_X=1
#JOYSTICK_DEADZONE_Y=2
#JOYSTICK_DEADZONE_Z=3
#JOYSTICK_DEADZONE_R=4
#JOYSTICK_DEADZONE_U=5
#JOYSTICK_DEADZONE_V=6

Procedure.i Joystick_Deadzone(*jsi.JOYSTICK_INFO,deadzone_c.f,deadzone_e.f,axis.i)
	Protected result.i
	If *jsi
		deadzone_c=Abs(deadzone_c)
		If deadzone_c>0.5
			deadzone_c=0.5
		EndIf
		deadzone_e=Abs(deadzone_e)
		If deadzone_e>0.5
			deadzone_e=0.5
		EndIf
		If axis=#JOYSTICK_DEADZONE_ALL
			*jsi\deadzonex\c=deadzone_c
			*jsi\deadzonex\e=deadzone_e
			*jsi\deadzoney\c=deadzone_c
			*jsi\deadzoney\e=deadzone_e
			*jsi\deadzonez\c=deadzone_c
			*jsi\deadzonez\e=deadzone_e
			*jsi\deadzoner\c=deadzone_c
			*jsi\deadzoner\e=deadzone_e
			*jsi\deadzoneu\c=deadzone_c
			*jsi\deadzoneu\e=deadzone_e
			*jsi\deadzonev\c=deadzone_c
			*jsi\deadzonev\e=deadzone_e
		ElseIf axis=#JOYSTICK_DEADZONE_X
			*jsi\deadzonex\c=deadzone_c
			*jsi\deadzonex\e=deadzone_e
		ElseIf axis=#JOYSTICK_DEADZONE_Y
			*jsi\deadzoney\c=deadzone_c
			*jsi\deadzoney\e=deadzone_e
		ElseIf axis=#JOYSTICK_DEADZONE_Z
			*jsi\deadzonez\c=deadzone_c
			*jsi\deadzonez\e=deadzone_e
		ElseIf axis=#JOYSTICK_DEADZONE_R
			*jsi\deadzoner\c=deadzone_c
			*jsi\deadzoner\e=deadzone_e
		ElseIf axis=#JOYSTICK_DEADZONE_U
			*jsi\deadzoneu\c=deadzone_c
			*jsi\deadzoneu\e=deadzone_e
		ElseIf axis=#JOYSTICK_DEADZONE_V
			*jsi\deadzonev\c=deadzone_c
			*jsi\deadzonev\e=deadzone_e
		EndIf
		result=#True
	EndIf
	ProcedureReturn result
EndProcedure

Procedure.i Joystick_GetDeadzone(*jsi.JOYSTICK_INFO,axis.i)
	Protected result.i
	If *jsi
		If axis=#JOYSTICK_DEADZONE_X
			result=*jsi\deadzonex
		ElseIf axis=#JOYSTICK_DEADZONE_Y
			result=*jsi\deadzoney
		ElseIf axis=#JOYSTICK_DEADZONE_Z
			result=*jsi\deadzonez
		ElseIf axis=#JOYSTICK_DEADZONE_R
			result=*jsi\deadzoner
		ElseIf axis=#JOYSTICK_DEADZONE_U
			result=*jsi\deadzoneu
		ElseIf axis=#JOYSTICK_DEADZONE_V
			result=*jsi\deadzonev
		EndIf
	EndIf
	ProcedureReturn result
EndProcedure
Last edited by Rescator on Wed Sep 28, 2011 3:37 am, edited 2 times in total.
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Example

Post by Rescator »

Code: Select all

;Just a quick and simple demo.
EnableExplicit

Define text$,n.i,i.i,c.i
Define joysticok.i,joysticks.i,joystick.i,buttons.i,*deadzone.JOYSTICK_INFO_DEADZONE
Dim sticks.i(0)
Define *jsi.JOYSTICK_INFO

;You are not required to use Joystick_Count(),
;you could also do Joystick_Alloc() in a loop until it fails,
;however doing that uses more resources than just using Joystick_Count() in the first place.
If OpenConsole()
	EnableGraphicalConsole(#True)
	joysticks=Joystick_Count()
	If joysticks
		joysticks-1
		Dim sticks(joysticks)
		For joystick=0 To joysticks
			sticks(joystick)=Joystick_Alloc(joystick) ;remember to free these using Joystick_Free() at the end of the program.
		Next
		;If the number of joysticks in the system is changed you will most likely need to repeat the stuff above as joystick id's may have changed.
		;A disconnect and reconnect of a single joystick should not cause any issues though, even if it happens during gameplay.
		;Please be aware that the joystick/gamepad itself might temporarily have it's data skewed,
		;this may vary from hardware to hardware/device to device, and the OEM drivers used (if any), and may vary per USB port even,
		;the only way to fix this is to rotate all joystick/gamepad axes for a second or two and all is back to normal.
		Repeat
			For joystick=0 To joysticks
				*jsi=sticks(joystick)
				Joystick_Deadzone(*jsi.JOYSTICK_INFO,0.135,0.05,#JOYSTICK_DEADZONE_ALL)
				;0.025 = 2.5% deadzone, set deadzone to 0.0 to disable deadzone, #JOYSTICK_DEADZONE_ALL applies the deadzone to all axis inputs.
				;You must specify both center and edge zones, normally both would be set with the same value, so consider that an advanced feature.
				;Deadzone is applied at both ends of each axis half, so in total: max left 2.5% + center left 2.5% + center right 2.5% + max right 2.5%,
				;in other words 10% of the axis, leaving 90% or more correctly 40% left and 40% right of the axis with full accuracy.
				;If you wish to let the user specify individual input deadzones then use the matching #JOYSTICK_DEADZONE_ instead for individual setting.
				;The goal of the deadzone adjustment is to allow easier centering as few analog inputs are perfect, some may have the center off slightly
				;others may have a jittery center, a jittery center is less annoying than a steering control pulling in one direction,
				;but jitter may cause a 1pixel jitter in the rending on the screen as well if you are unlucky.
				;Other benefits of a deadzone is that the center of a stick is less sensitive to bumping into it or shaking of the controller.
				;And a deadzone will also make it easier to max out a direction,
				;particularly the "corner" may be difficult to get maxed out as a corner is usually where two axes need to reach 100% at the same time.
				;Imagine a deadzone as trimming the edge off a frisbee and cutting a hole in the center. (there are frisbees like that actually).
				;So that's a lot of positive about the deadzones, what is the negative then?
				;Well...other than some CPU cycles being used that could have been avoided if the OS (or optional driver software) let you calibrate with deadzones...
				;there is no negatives really, well, a huge one if you as a programmer forget to let the user/player adjust the deadzone percentage obviously.
				;My advise is to let the user set a deadzone ranging from 0% (0.0, off) to 50.00% (0.5),
				;you might wonder, why not higher than 50% ? Well that would have to be one crappy stick if it's precision is worse than a on/off switch *laughs*,
				;even cheaper joysticks/gamepads rarely need a deadzone more than a few %, and a high quality one may not need any deadzones at all.
				;There may be some use in higher value deadzones, for example setting the deadzones to 0.5 (50%).
				;Remember that the deadzone as implemented here is (with 50% deadzone): max left 50% + center left 50% + center right 50% + max right 50%.
				;That will make it act like it's a digital stick actually (left center right), which in some games might be a benefit as well,
				;and since the deadzone adjustment automatically does this, you avoid having to code your own analog to digital stick conversion, cool right?
				;Why are the buttons returning floats rather than 0 or 1? Because using 0.0 to 1.0 makes this joystick lib uniform in it's math/use,
				;and should analog buttons be available in the future there will be a minimal amount of code changes needed.
				;Also since the buttons are floats you could even let the user manually adjust the "preassure" of the button,
				;if the user then sets the button to be 50% (0.5) instead of 100% (1.0) you can simply do buttonvalue*modifier,
				;and you suddenly have a fake analog button, benefits of this is for example allowing the user to define a break button that breaks at 50% power,
				;now that's very cool indeed and can be fiddly to do with digital button input. Modifiers like that are very easy to do with floating point as you see.
				;What would a nice default deadzone be? I'm not sure. MicroSoft's XNA framework seem to have a center deadzone of 25% (center left + center right).
				;I guess it's possible for crappy or really old sticks to have a center that is that much off though *shrug*,
				;there might also be a benefit in such a high deadzone to avoid the user accidentally touching the other axis when moving a stick.
				;For example my old Saitek P2600 gamepad shows the following (when idle) when deadzone is set to 0.0 (0%):
				;X: Centered
				;Y: Centered or -0.78%
				;Z: -0.79%
				;R: -0.79% or 1.57%
				;The Y axis jitter at the center, the non-centered Z axis is also some concern, and again the R axis jitters but also never truly centers.
				;A center deadzone of 1% (0.01) should make the center solid with some margin even, but the R axis messes that up,
				;so maybe a 2% (0.02) Center deadzone should be used, but there are other things to concider too.
				;The stick may feel a bit flakey or touchy, so 2.5% might give a better "feel", there is also the issue of accidentally
				;moving the stick down or up a little when moving it left and right for example, so maybe 5%?
				;The stick does go fully to 100% luckily, but I might have to press very hard to do that, especially when pushing the stick to a corner.
				;So the goal of a deadzone is not just to compensate for jitter or alignment issues, but also bottom/top sensitivity.
				;Too low a deadzone and the user may get quickly tired from having to be very carefull in moving the sticks,
				;too high and the user looses a lot of the sticks range. The 12.5% suddenly does not seem so high after all does it?
				;Some joysticks or gamepads may have built in deadzones from the factory, while others may have them in the drivers or in profiles,
				;so in some cases the deadzone may have to be set to 0.0 since the input is allready adjusted, while in other cases it's not.
				;Because of this "mess" the user should always have the option to adjust the deadzone to their liking and turn it on/off.

				buttons=Joystick_ButtonCount(*jsi)
				PrintN("Joystick "+Str(joystick+1)+" of "+Str(joysticks+1)+", OEMName="+#DQUOTE$+Joystick_OEMName(*jsi)+#DQUOTE$+", OEMID="+RSet(Hex(Joystick_OEMID(*jsi)),8,"0"))
				Print("Features: Axes="+Str(Joystick_AxesCount(*jsi))+","+" Buttons="+Str(buttons))
				text$=", POV="
				If Joystick_HasPOV(*jsi)
					text$+"Yes"
				Else
					text$+"No"
				EndIf
				text$+", Z="
				If Joystick_HasZ(*jsi)
					text$+"Yes"
				Else
					text$+"No"
				EndIf
				text$+", R="
				If Joystick_HasR(*jsi)
					text$+"Yes"
				Else
					text$+"No"
				EndIf
				text$+", U="
				If Joystick_HasU(*jsi)
					text$+"Yes"
				Else
					text$+"No"
				EndIf
				text$+", V="
				If Joystick_HasV(*jsi)
					text$+"Yes"
				Else
					text$+"No"
				EndIf
				PrintN(text$)
				PrintN("")

				*deadzone=Joystick_GetDeadzone(*jsi,#JOYSTICK_DEADZONE_X)
				If *deadzone
					PrintN("Deadzone X: "+StrF(*deadzone\c*100.0,2)+"%, "+StrF(*deadzone\e*100.0,2)+"% (Total "+StrF((*deadzone\c+*deadzone\e)*200.0,2)+"%)")
				EndIf
				*deadzone=Joystick_GetDeadzone(*jsi,#JOYSTICK_DEADZONE_Y)
				If *deadzone
					PrintN("Deadzone Y: "+StrF(*deadzone\c*100.0,2)+"%, "+StrF(*deadzone\e*100.0,2)+"% (Total "+StrF((*deadzone\c+*deadzone\e)*200.0,2)+"%)")
				EndIf
				*deadzone=Joystick_GetDeadzone(*jsi,#JOYSTICK_DEADZONE_Z)
				If *deadzone
					PrintN("Deadzone Z: "+StrF(*deadzone\c*100.0,2)+"%, "+StrF(*deadzone\e*100.0,2)+"% (Total "+StrF((*deadzone\c+*deadzone\e)*200.0,2)+"%)")
				EndIf
				*deadzone=Joystick_GetDeadzone(*jsi,#JOYSTICK_DEADZONE_R)
				If *deadzone
					PrintN("Deadzone R: "+StrF(*deadzone\c*100.0,2)+"%, "+StrF(*deadzone\e*100.0,2)+"% (Total "+StrF((*deadzone\c+*deadzone\e)*200.0,2)+"%)")
				EndIf
				*deadzone=Joystick_GetDeadzone(*jsi,#JOYSTICK_DEADZONE_U)
				If *deadzone
					PrintN("Deadzone U: "+StrF(*deadzone\c*100.0,2)+"%, "+StrF(*deadzone\e*100.0,2)+"% (Total "+StrF((*deadzone\c+*deadzone\e)*200.0,2)+"%)")
				EndIf
				*deadzone=Joystick_GetDeadzone(*jsi,#JOYSTICK_DEADZONE_V)
				If *deadzone
					PrintN("Deadzone V: "+StrF(*deadzone\c*100.0,2)+"%, "+StrF(*deadzone\e*100.0,2)+"% (Total "+StrF((*deadzone\c+*deadzone\e)*200.0,2)+"%)")
				EndIf

				;For maximum performance reasons you must read POV, Axes and buttons directly from the *jsi structure.
				;x.f
				;y.f
				;z.f
				;r.f
				;u.f
				;v.f
				;pov.f
				;button.f[32] ;button 0-31
				;You can read as much as you want from these, each time Joystick_Update(*jsi) is called those fields will be updated again.
				;Below you will see examples of how these fields are read/used.

				joysticok=Joystick_Update(*jsi)
				PrintN("")
				PrintN("Buttons:")
				text$=""
				n=buttons-1
				c=0
				For i=0 To n
					c+1
					text$+Str(i+1)+"="+StrF(*jsi\button[i],2)+" "
					If c=8
						PrintN(text$)
						text$=""
						c=0
					EndIf
				Next
				PrintN(text$)
				PrintN("")

				text$="POV: " ;range 0.0 to 1.0 = 0-360 degrees, as 0 is the same as 360 the value 0.0 is ised to indicate center instead.
				If *jsi\pov=0.0
					text$+"Centered"
				Else
					text$+StrF(*jsi\pov*360.0,2)+"°"
				EndIf
				PrintN(text$)

				text$="X: "
				If *jsi\x=0.0
					text$+"Centered"
				Else
					text$+StrF(*jsi\x*100.0,2)+"%"
				EndIf
				PrintN(text$)

				text$="Y: "
				If *jsi\y=0.0
					text$+"Centered"
				Else
					text$+StrF(*jsi\y*100.0,2)+"%"
				EndIf
				PrintN(text$)

				text$="Z: "
				If *jsi\z=0.0
					text$+"Centered"
				Else
					text$+StrF(*jsi\z*100.0,2)+"%"
				EndIf
				PrintN(text$)

				text$="R: "
				If *jsi\r=0.0
					text$+"Centered"
				Else
					text$+StrF(*jsi\r*100.0,2)+"%"
				EndIf
				PrintN(text$)

				text$="U: "
				If *jsi\u=0.0
					text$+"Centered"
				Else
					text$+StrF(*jsi\u*100.0,2)+"%"
				EndIf
				PrintN(text$)

				text$="V: "
				If *jsi\v=0.0
					text$+"Centered"
				Else
					text$+StrF(*jsi\v*100.0,2)+"%"
				EndIf
				PrintN(text$)

				PrintN("----------------------------------------------------------------")
				PrintN("")

				;If the joystick/gamepad is unplugged while playing it's nice to pause the game and inform the user,
				;this joystick library will return all values as centered on each update until the joystick/gamepad is reconnected again,
				;I'm sure the player/user will appreciate not running into walls while they panic to re-connect the cable.
				If Not joysticok
					MessageRequester("Joystick Demo PAUSED","Joystick/gamepad lost!"+#LF$+"Re-connect and make sure to rotate all inputs before continuing!",#PB_MessageRequester_Ok)
					;Due to re-connect hickups from the actual device, it may be smart to tell the user/player that they need to move
					;all the axes (sticks) to ensure they all rest properly at center again,
					;the "junk" data is possibly left over from device init/auto re-calibration or similar.
					;Not all devices may need to be "warmed" up after a re-connect, it's hardware or driver dependent.
					;If there was a deadzone set ten make sure to display/show the user a quick test,
					;maybe just re-display the deadzone configuration window/screen? (no need for anything fancy, but if you got a graphics artist then go nuts)
					;Once the user has done that, then unpause the game/program again.
				EndIf
			Next
			Delay(100)
			If Inkey()=Chr(27)
				Break
			EndIf
			ClearConsole()
		Forever
		For joystick=0 To joysticks
			Joystick_Free(sticks(joystick))
		Next
	EndIF
	CloseConsole()
EndIf
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Mouse Float

Post by Rescator »

Here's a mini-tip on how to get Mouse Floating point values.
I'm posting this here as it matches the way the Joystick returns floats above.

The Mouse_Float() routine is very simple, just feed in screen width and height and the mouse x and mouse y values.
The floating point mouse coordinates are put in the MOUSE_FLOAT structure.
In this implementation max left and max down is -1.0 and max up and max right is 1.0.
The 4 pixel center "square" is 0.0.

How do you use these mouse float values?
Well, one way is to use them as indicators of the left/right, up/down turning or aiming speed.
Basically this mini-tip let you translate the mouse coordinates into almost the same values as a analog joystick would return in the code above.
Also remember that trackballs behave just like a mouse, so this will work for trackballs as well.

Code: Select all

EnableExplicit
;Mouse Float v1.0 (c) Roger Hågensen, EmSai 2011.
;zlib license, http://en.wikipedia.org/wiki/Zlib_license

Structure MOUSE_FLOAT
	x.f
	y.f
EndStructure

Procedure Mouse_Float(*mf.MOUSE_FLOAT,screenwidth.l,screenheight.l,mousex.l,mousey.l)
	screenwidth=(screenwidth>>1)-1
	screenheight=(screenheight>>1)-1
	If mousex<=screenwidth
		mousex-screenwidth
	Else
		mousex-(screenwidth+1)
	EndIf
	If mousey<=screenheight
		mousey-screenheight
	Else
		mousey-(screenheight+1)
	EndIf
	*mf\x=mousex/screenwidth
	*mf\y=-(mousey/screenheight)
EndProcedure

Define width.l,height.l,x.l,y.l,mf.MOUSE_FLOAT
If ExamineDesktops()
	width=GetSystemMetrics_(#SM_CXSCREEN)
	height=GetSystemMetrics_(#SM_CYSCREEN)
	
	If OpenConsole()
		EnableGraphicalConsole(#True)
		Repeat
			x=DesktopMouseX()
			y=DesktopMouseY()
			PrintN("Mouse: "+Str(x)+"x"+Str(y))
			Mouse_Float(mf,width,height,x,y)
			PrintN("Mouse: "+StrF(mf\x)+", "+StrF(mf\y))
			Delay(100)
			If Inkey()=Chr(27)
				Break
			EndIf
			ClearConsole()
		Forever
		CloseConsole()
	EndIf
EndIf
User avatar
luis
Addict
Addict
Posts: 3895
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: Joystick v1.0, Windows, 8 axes, 32 button, POV, deadzone

Post by luis »

Looks very nice thank you :)
"Have you tried turning it off and on again ?"
A little PureBasic review
Post Reply