//maccontrols.cpp //mac-specific input code #include #include #include #include //needed for ForceFeedback.h //----------------------------------------------------------------- typedef OSStatus HRESULT; typedef UInt32 IOByteCount; typedef unsigned int io_service_t; typedef unsigned int io_object_t; #define S_OK ((HRESULT)0x00000000L) //----------------------------------------------------------------- #include extern "C"{ #include "ImmrHIDUtilAddOn.h" } #include "controls.h" #include "config.h" #include "error.h" #include "gameframe.h" #include "gamesystem.h" #include "gametime.h" #include "gamemem.h" typedef struct{ pRecDevice device; int numElements; pRecElement *elements; } tHIDController; tHIDController *gControllers; int gInputHID=false; int gNumControllers=0; int gIShockIIFFB=false; //iS2F_DeviceRef_t giShockList[iS2F_MAX_ISHOCK2_NUM]; int gIShockIIFFBBlock=0; int gFFB=false; FFDeviceObjectReference gFFDeviceRef; FFEffectObjectReference gGroundRumbleEffectRef = NULL; FFEffectObjectReference gJoltEffectRef = NULL; FFEffectObjectReference gFrictionEffectRef = NULL; FFEffectObjectReference gAlignEffectRef = NULL; FFEffectObjectReference gEngineEffectRef = NULL; DWORD dwAxes[2] = {FFJOFS_X,FFJOFS_Y}; LONG lDirection[2] = {0,0}; extern int gAxisSteering; //returns true if the key with the key code k is pressed, false otherwise short IsPressed(unsigned short k ) { if(k<0||k>255) return false; KeyMapByteArray km; GetKeys( *((KeyMap*)&km)); return ( ( km[k>>3] >> (k & 7) ) & 1); } //Initialize controls void ControlInit() { if(HIDBuildDeviceList(NULL,NULL)) { gNumControllers=0; pRecDevice device=HIDGetFirstDevice(); do{ //if(!(device->usagePage==kHIDPage_Unknown||(device->usagePage==kHIDPage_GenericDesktop&&(device->usage==kHIDUsage_GD_Mouse||device->usage==kHIDUsage_GD_Keyboard)))) if(device->usagePage==kHIDPage_GenericDesktop&&(device->usage==kHIDUsage_GD_Joystick||device->usage==kHIDUsage_GD_GamePad))//||kHIDUsage_GD_Mouse)) gNumControllers++; device=HIDGetNextDevice(device); }while(device); gControllers=(tHIDController*)NewPtr(sizeof(tHIDController)*gNumControllers); gNumControllers=0; device=HIDGetFirstDevice(); do{ // if(!(device->usagePage==kHIDPage_Unknown||(device->usagePage==kHIDPage_GenericDesktop&&(device->usage==kHIDUsage_GD_Mouse||device->usage==kHIDUsage_GD_Keyboard)))) if(device->usagePage==kHIDPage_GenericDesktop&&(device->usage==kHIDUsage_GD_Joystick||device->usage==kHIDUsage_GD_GamePad))//||kHIDUsage_GD_Mouse)) { io_service_t hidDeviceObject=AllocateHIDObjectFromRecDevice(device); if(hidDeviceObject) if(FFIsForceFeedback(hidDeviceObject)==FF_OK) if(FFCreateDevice(hidDeviceObject,&gFFDeviceRef)==FF_OK) gFFB=true; gControllers[gNumControllers].device=device; gControllers[gNumControllers].numElements=HIDCountDeviceElements(gControllers[gNumControllers].device,kHIDElementTypeInput); gControllers[gNumControllers].elements=(pRecElement*)NewPtr(sizeof(pRecElement)*gControllers[gNumControllers].numElements); gControllers[gNumControllers].elements[0]=HIDGetFirstDeviceElement(gControllers[gNumControllers].device,kHIDElementTypeInput); for(int j=1;jiShockFFB) { iS2F_JoltCmd_t joltCmd; joltCmd.motorCmd.leftMotorMagnitude=lMag*10; joltCmd.motorCmd.rightMotorMagnitude=rMag*10; joltCmd.duration=duration*1000; iS2F_SimpleJolt(giShockList[0],&joltCmd); gIShockIIFFBBlock=gFrameCount+duration*kFPS; }*/ if(gFFB&&gAxisSteering&&gConfig->ffb) { float mag=(rMag+lMag)*0.5; float offset=rMag-lMag; HRESULT hr; FFEFFECT ffeffect; FFPERIODIC ffperiodic; FFENVELOPE ffenvelope; ffenvelope.dwSize = sizeof(ffenvelope); ffenvelope.dwAttackLevel = 0; ffenvelope.dwAttackTime = 0; /* Microseconds */ ffenvelope.dwFadeLevel = 0; ffenvelope.dwFadeTime = duration*FF_SECONDS; /* Microseconds */ ffperiodic.dwMagnitude = FF_FFNOMINALMAX*gConfig->ffbIntensity*mag; ffperiodic.lOffset = FF_FFNOMINALMAX*gConfig->ffbIntensity*offset; ffperiodic.dwPhase = 0; ffperiodic.dwPeriod = 0.1*FF_SECONDS; // 10 Hz ffeffect.dwSize = sizeof(ffeffect); /* sizeof(FFEFFECT) */ ffeffect.dwFlags = FFEFF_CARTESIAN; /* FFEFF_* */ ffeffect.dwDuration = duration*FF_SECONDS; /* Microseconds */ ffeffect.dwSamplePeriod = 1000; /* Microseconds */ ffeffect.dwGain = FF_FFNOMINALMAX; ffeffect.dwTriggerButton = FFEB_NOTRIGGER; /* or FFEB_NOTRIGGER */ ffeffect.dwTriggerRepeatInterval = 0; /* Microseconds */ ffeffect.cAxes = 1; /* Number of axes */ ffeffect.rgdwAxes = dwAxes; /* Array of axes */ ffeffect.rglDirection = lDirection; /* Array of directions */ ffeffect.lpEnvelope = &ffenvelope; /* Optional */ ffeffect.cbTypeSpecificParams = sizeof(ffperiodic); /* Size of params */ ffeffect.lpvTypeSpecificParams = &ffperiodic; /* Pointer to params */ ffeffect.dwStartDelay = 0; /* Microseconds */ if(gJoltEffectRef==NULL) { FFDeviceCreateEffect(gFFDeviceRef, kFFEffectType_Square_ID, &ffeffect, &gJoltEffectRef); FFEffectDownload(gJoltEffectRef); FFEffectStart(gJoltEffectRef, 1, 0); } else { FFEffectSetParameters(gJoltEffectRef,&ffeffect,FFEP_GAIN); FFEffectStart(gJoltEffectRef, 1, 0); } } #endif } void FFBSetGoundRumble(float velo, float rumble) { if(gFFB&&gConfig->ffb) { if(!gAxisSteering) rumble=0; velo/=60; if(velo>1)velo=1; HRESULT hr; FFEFFECT ffeffect; FFPERIODIC ffperiodic; ffperiodic.dwMagnitude = FF_FFNOMINALMAX*gConfig->ffbIntensity*rumble*velo*0.5; ffperiodic.lOffset = 0; ffperiodic.dwPhase = 0; if(velo) ffperiodic.dwPeriod = FF_SECONDS*0.8/velo; else ffperiodic.dwPeriod = 0; ffeffect.dwSize = sizeof(ffeffect); /* sizeof(FFEFFECT) */ ffeffect.dwFlags = FFEFF_CARTESIAN; /* FFEFF_* */ ffeffect.dwDuration = FF_INFINITE; /* Microseconds */ ffeffect.dwSamplePeriod = 1000; /* Microseconds */ ffeffect.dwGain = FF_FFNOMINALMAX; ffeffect.dwTriggerButton = FFEB_NOTRIGGER; /* or FFEB_NOTRIGGER */ ffeffect.dwTriggerRepeatInterval = 0; /* Microseconds */ ffeffect.cAxes = 1; /* Number of axes */ ffeffect.rgdwAxes = dwAxes; /* Array of axes */ ffeffect.rglDirection = lDirection; /* Array of directions */ ffeffect.lpEnvelope = NULL; /* Optional */ ffeffect.cbTypeSpecificParams = sizeof(ffperiodic); /* Size of params */ ffeffect.lpvTypeSpecificParams = &ffperiodic; /* Pointer to params */ ffeffect.dwStartDelay = 0; /* Microseconds */ if(gGroundRumbleEffectRef==NULL) { FFDeviceCreateEffect(gFFDeviceRef, kFFEffectType_Sine_ID, &ffeffect, &gGroundRumbleEffectRef); FFEffectDownload(gGroundRumbleEffectRef); FFEffectStart(gGroundRumbleEffectRef, 1, 0); } else { FFEffectSetParameters(gGroundRumbleEffectRef,&ffeffect,FFEP_TYPESPECIFICPARAMS+FFEP_GAIN); FFEffectStatusFlag pFlags; FFEffectGetEffectStatus(gGroundRumbleEffectRef,&pFlags); if(!pFlags&FFEGES_PLAYING) FFEffectStart(gGroundRumbleEffectRef, 1, 0); } } } void FFBSetSteerResistance(float res,float alignment) { if(gFFB&&gConfig->ffb) { if(!gAxisSteering) { res=0; alignment=0; } HRESULT hr; FFEFFECT ffeffect; FFCONDITION ffcondition; ffcondition.lOffset=0; ffcondition.lPositiveCoefficient=FF_FFNOMINALMAX*gConfig->ffbIntensity*res; ffcondition.lNegativeCoefficient=FF_FFNOMINALMAX*gConfig->ffbIntensity*res; ffcondition.dwPositiveSaturation=FF_FFNOMINALMAX*gConfig->ffbIntensity*res; ffcondition.dwNegativeSaturation=FF_FFNOMINALMAX*gConfig->ffbIntensity*res; ffcondition.lDeadBand=0; ffeffect.dwSize = sizeof(ffeffect); /* sizeof(FFEFFECT) */ ffeffect.dwFlags = FFEFF_CARTESIAN; /* FFEFF_* */ ffeffect.dwDuration = FF_INFINITE; /* Microseconds */ ffeffect.dwSamplePeriod = 1000; /* Microseconds */ ffeffect.dwGain = FF_FFNOMINALMAX; ffeffect.dwTriggerButton = FFEB_NOTRIGGER; /* or FFEB_NOTRIGGER */ ffeffect.dwTriggerRepeatInterval = 0; /* Microseconds */ ffeffect.cAxes = 1; /* Number of axes */ ffeffect.rgdwAxes = dwAxes; /* Array of axes */ ffeffect.rglDirection = lDirection; /* Array of directions */ ffeffect.lpEnvelope = NULL; /* Optional */ ffeffect.cbTypeSpecificParams = sizeof(ffcondition); /* Size of params */ ffeffect.lpvTypeSpecificParams = &ffcondition; /* Pointer to params */ ffeffect.dwStartDelay = 0; /* Microseconds */ if(gFrictionEffectRef==NULL) { FFDeviceCreateEffect(gFFDeviceRef, kFFEffectType_Friction_ID, &ffeffect, &gFrictionEffectRef); FFEffectDownload(gFrictionEffectRef); FFEffectStart(gFrictionEffectRef, 1, 0); } else { FFEffectSetParameters(gFrictionEffectRef,&ffeffect,FFEP_TYPESPECIFICPARAMS+FFEP_GAIN); FFEffectStatusFlag pFlags; FFEffectGetEffectStatus(gFrictionEffectRef,&pFlags); if(!pFlags&FFEGES_PLAYING) FFEffectStart(gFrictionEffectRef, 1, 0); } ffcondition.lOffset=0; ffcondition.lPositiveCoefficient=FF_FFNOMINALMAX*gConfig->ffbIntensity*alignment; ffcondition.lNegativeCoefficient=FF_FFNOMINALMAX*gConfig->ffbIntensity*alignment; ffcondition.dwPositiveSaturation=FF_FFNOMINALMAX*gConfig->ffbIntensity*alignment; ffcondition.dwNegativeSaturation=FF_FFNOMINALMAX*gConfig->ffbIntensity*alignment; ffcondition.lDeadBand=0; if(gAlignEffectRef==NULL) { FFDeviceCreateEffect(gFFDeviceRef, kFFEffectType_Spring_ID, &ffeffect, &gAlignEffectRef); FFEffectDownload(gAlignEffectRef); FFEffectStart(gAlignEffectRef, 1, 0); } else { FFEffectSetParameters(gAlignEffectRef,&ffeffect,FFEP_TYPESPECIFICPARAMS+FFEP_GAIN); FFEffectStatusFlag pFlags; FFEffectGetEffectStatus(gAlignEffectRef,&pFlags); if(!pFlags&FFEGES_PLAYING) FFEffectStart(gAlignEffectRef, 1, 0); } } } void FFBiShockDirect(float lMag,float rMag) { /* if(gIShockIIFFB&&gConfig->iShockFFB&&gFrameCount>gIShockIIFFBBlock) { iS2F_MotorCmd_t directCmd; directCmd.leftMotorMagnitude=lMag*10; directCmd.rightMotorMagnitude=rMag*10; iS2F_SimpleDirectControl(giShockList[0],&directCmd); }*/ } void FFBStop() { /* if(gIShockIIFFB&&gConfig->iShockFFB) { iS2F_MotorCmd_t directCmd; directCmd.leftMotorMagnitude=0; directCmd.rightMotorMagnitude=0; iS2F_SimpleDirectControl(giShockList[0],&directCmd); }*/ if(gFFB&&gConfig->ffb) { FFEffectStop(gGroundRumbleEffectRef); FFEffectStop(gJoltEffectRef); FFEffectStop(gFrictionEffectRef); FFEffectStop(gAlignEffectRef); FFEffectStop(gEngineEffectRef); gGroundRumbleEffectRef = NULL; gJoltEffectRef = NULL; gFrictionEffectRef = NULL; gAlignEffectRef = NULL; gEngineEffectRef = NULL; } } //Dispose*/ Control structures void ControlExit() { /* if(gInputHID) { HIDReleaseDeviceList(); TearDownHIDCFM(); } /* if(gIShockIIFFB) { FFBStop(); iS2F_Final(); }*/ } void CalibrateAxis(int id) { while(GetInterfaceKey(kInterfaceKeyReturn)||GetInterfaceKey(kInterfaceKeyEnter)); int controller=-1; for(int i=0;ivendorID==gConfig->axis[id].axisControllerID1&&gControllers[i].device->productID==gConfig->axis[id].axisControllerID2) controller=i; int element=gConfig->axis[id].axisElementID; if(controller=0) { gConfig->axis[id].max=gConfig->axis[id].mid+1; gConfig->axis[id].min=gConfig->axis[id].mid-1; while(!(GetInterfaceKey(kInterfaceKeyReturn)||GetInterfaceKey(kInterfaceKeyEnter))) { SInt32 value=HIDGetElementValue(gControllers[controller].device,gControllers[controller].elements[element]); if(valueaxis[id].min)gConfig->axis[id].min=value; if(value>gConfig->axis[id].max)gConfig->axis[id].max=value; } } } #define kAxisMaxZone 0.9 //read an analogue axis float GetAxisInput(int id) { if(gConfig->axis[id].axisControllerID1==-1&&gConfig->axis[id].axisControllerID2==-1) { //mouse pos tVector2 p=GetMousePos(); if(gConfig->axis[id].axisElementID==1) return p.x; if(gConfig->axis[id].axisElementID==2) return p.y; } if(gConfig->axis[id].axisControllerID1&&gInputHID){ int controller=-1; for(int i=0;ivendorID==gConfig->axis[id].axisControllerID1&&gControllers[i].device->productID==gConfig->axis[id].axisControllerID2) controller=i; int element=gConfig->axis[id].axisElementID; if(controller=0) if(element=0) { SInt32 value=HIDGetElementValue(gControllers[controller].device,gControllers[controller].elements[element]); int min=gConfig->axis[id].mid-(gConfig->axis[id].mid-gConfig->axis[id].min)*kAxisMaxZone; int max=gConfig->axis[id].mid-(gConfig->axis[id].mid-gConfig->axis[id].max)*kAxisMaxZone; float ax; if(id>=kInputThrottleAxis) { if(value<(gConfig->axis[id].max-gConfig->axis[id].min)*gConfig->axis[id].deadzone*0.5) ax=0; else ax=(value-(gConfig->axis[id].max-gConfig->axis[id].min)*gConfig->axis[id].deadzone*0.5)/((gConfig->axis[id].max-min)*(1-0.5*gConfig->axis[id].deadzone)); } else { if(fabs(gConfig->axis[id].mid-value)<(gConfig->axis[id].max-gConfig->axis[id].min)*gConfig->axis[id].deadzone) ax=0; else if(value>gConfig->axis[id].mid) ax=(value-(gConfig->axis[id].mid+(gConfig->axis[id].max-gConfig->axis[id].min)*gConfig->axis[id].deadzone))/((gConfig->axis[id].mid-max)*(1-0.5*gConfig->axis[id].deadzone)); else ax=(value-(gConfig->axis[id].mid-(gConfig->axis[id].max-gConfig->axis[id].min)*gConfig->axis[id].deadzone))/((min-gConfig->axis[id].mid)*(1-0.5*gConfig->axis[id].deadzone)); } if(ax>1)ax=1; else if(ax<-1)ax=-1; return ax; } } return 0; } //read a digital input int GetButtonInput(int id) { if(gInputChatMode) return false; //is the key pressed? int key=gSystemSuspended?false:IsPressed(gConfig->keys[id].keyID); //is the HID button pressed? if(gConfig->axis[id].axisControllerID1==-1&&gConfig->axis[id].axisControllerID2==-1) if(Button()) key=true; if(gConfig->keys[id].controllerID1&&gInputHID) { int controller=-1; for(int i=0;ivendorID==gConfig->keys[id].controllerID1&&gControllers[i].device->productID==gConfig->keys[id].controllerID2) controller=i; int element=gConfig->keys[id].elementID; if(controller=0) if(element=0) return key||HIDGetElementValue(gControllers[controller].device,gControllers[controller].elements[element]); } return key; } int gInterfaceKeys[kInterfaceNumKeys]={0x00,0x7e,0x7d,0x7b,0x7c,0x31,0x24,0x4c,0x35,0x07,0x33,0x37,0x3a,0x01,0x0f,0x0c,0xff}; int GetInterfaceKey(int id) { if(id==kInterfaceMouseDown) return gSystemSuspended?false:Button(); else return gSystemSuspended?false:IsPressed(gInterfaceKeys[id]); } static void GetElementName (pRecDevice pDevice,pRecElement pElement, char *strElementName) { if(!HIDGetElementNameFromVendorProductUsage (pDevice->vendorID, pDevice->productID, pElement->usagePage, pElement->usage,strElementName)) { // set name from vendor id/product id look up HIDGetElementNameFromVendorProductCookie (pDevice->vendorID, pDevice->productID, (long) pElement->cookie, strElementName); if (!*strElementName) { // if no name char buffer[1024]; HIDGetUsageName (pElement->usagePage, pElement->usage,buffer); if (!*buffer) // if not usage sprintf (strElementName, "Unknown"); else sprintf(strElementName,"%s %s",pDevice->product,buffer); } } } //used to user-configure input. if any key or HID button is pressed, //this changes the according ID and Identifier string. void GetInput(tKeyConfig *keyConfig) { //FlushEvents(keyDownMask,0); int key=-1; do{ for(int i=0;i<128;i++) if(IsPressed(i)) if(i!=0x35&&i!=0x33) key=i; else { if(i==0x33) { keyConfig->controllerID1=0; keyConfig->controllerID2=0; keyConfig->elementID=0; strcpy(keyConfig->controllerIdentifier,""); keyConfig->keyID=-1; strcpy(keyConfig->identifier,""); } FlushEvents(keyDownMask|mDownMask,0); FlushKeys(); return; } if(gInputHID) for(int j=0;jtype==kIOHIDElementTypeInput_Button) if(HIDGetElementValue(gControllers[j].device,gControllers[j].elements[i])) { keyConfig->controllerID1=gControllers[j].device->vendorID; keyConfig->controllerID2=gControllers[j].device->productID; keyConfig->elementID=i; char elementName[256]; GetElementName(gControllers[j].device,gControllers[j].elements[i],elementName); // sprintf(keyConfig->controllerIdentifier,"%s: %s",gControllers[j].device->product,elementName); strcpy(keyConfig->controllerIdentifier,elementName); return; } if(Button()&&!StillDown()) { keyConfig->controllerID1=-1; keyConfig->controllerID2=-1; keyConfig->elementID=1; strcpy(keyConfig->controllerIdentifier,"Mouse Button"); FlushEvents(keyDownMask|mDownMask,0); FlushKeys(); return; } }while(key==-1); int clear; do{ clear=true; for(int i=0;i<128;i++) if(IsPressed(i)) clear=false; }while(!clear); EventRecord evt; Str255 str; GetIndString(str,128,key+1); int len=str[0]; MemoryMove(str+1,str,255); str[len]='\0'; if(str[0]) strcpy(keyConfig->identifier,(char*)str); else { strcpy(keyConfig->identifier,"unknown key"); while(WaitNextEvent(keyUpMask,&evt,1,nil)) { keyConfig->identifier[0]=evt.message&charCodeMask; if(keyConfig->identifier[0]>='a'&&keyConfig->identifier[0]<='z') keyConfig->identifier[0]+='A'-'a'; keyConfig->identifier[1]='\0'; } } keyConfig->keyID=key; FlushEvents(keyDownMask|mDownMask,0); FlushKeys(); } //used to user-configure input. if any HID axis is moved, //this changes the according ID and Identifier string. void GetAxis(tAxisConfig *axis,bool allowVendor) { tVector2 basep=GetMousePos(); float **values=(float**)NewPtr(sizeof(float)*gNumControllers); for(int j=0;jtype!=kIOHIDElementTypeInput_Button) values[j][i]=HIDGetElementValue(gControllers[j].device,gControllers[j].elements[i]); } int key=-1; do{ long maxDiff=0; for(int i=0;i<128;i++) if(IsPressed(i)) key=i; if(key==0x33) { axis->axisControllerID1=0; axis->axisControllerID2=0; axis->axisElementID=0; strcpy(axis->axisIdentifier,""); } for(int j=0;jusagePage!=kHIDPage_VendorDefinedStart||allowVendor) if(gControllers[j].elements[i]->type!=kIOHIDElementTypeInput_Button) if(abs(values[j][i]-HIDGetElementValue(gControllers[j].device,gControllers[j].elements[i]))>0) { maxDiff=abs(values[j][i]-HIDGetElementValue(gControllers[j].device,gControllers[j].elements[i])); axis->axisControllerID1=gControllers[j].device->vendorID; axis->axisControllerID2=gControllers[j].device->productID; axis->axisElementID=i; axis->min=gControllers[j].elements[i]->min; axis->max=gControllers[j].elements[i]->max; axis->mid=(axis->min+axis->max)/2; char elementName[256]; GetElementName(gControllers[j].device,gControllers[j].elements[i],elementName); //sprintf(axis->axisIdentifier,"%s: %s",gControllers[j].device->product,elementName); strcpy(axis->axisIdentifier,elementName); } if(maxDiff) { for(int j=0;j0.1) { axis->axisControllerID1=-1; axis->axisControllerID2=-1; axis->axisElementID=fabs(p.x-basep.x)>fabs(p.y-basep.y)?1:2; sprintf(axis->axisIdentifier,"Mouse %c Axis",'W'+axis->axisElementID); for(int j=0;jscreenXSize)+1,(2.0*p.v/gConfig->screenYSize)-1); }