/* Starshatter OpenSource Distribution Copyright (c) 1997-2004, Destroyer Studios LLC. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name "Destroyer Studios" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SUBSYSTEM: nGenEx.lib FILE: Joystick.cpp AUTHOR: John DiCamillo OVERVIEW ======== Joystick Input class */ #include "MemDebug.h" #include "Joystick.h" #include "MachineInfo.h" #include "Game.h" #define DIRECTINPUT_VERSION 0x0800 #include #define JOY_POVUPRIGHT 4500 #define JOY_POVDNRIGHT 13500 #define JOY_POVDNLEFT 22500 #define JOY_POVUPLEFT 31500 // +--------------------------------------------------------------------+ // DIRECT INPUT SUPPORT const int MAX_DEVICES = 8; static LPDIRECTINPUT7 pdi = 0; static LPDIRECTINPUTDEVICE7 pdev = 0; static DIDEVICEINSTANCE devices[MAX_DEVICES]; static int ndev = 0; static int idev = -1; static int strikes = 3; static Joystick* joystick = 0; void DirectInputError(const char* msg, HRESULT err); const char* DIErrStr(HRESULT hr); void ReleaseDirectInput(); // +--------------------------------------------------------------------+ Joystick::Joystick() : x(0), y(0), z(0), p(0), r(0), w(0), t(0) { if (!joystick) joystick = this; select = 1; rudder = 0; throttle = 1; sensitivity = 25; dead_zone = 100; for (int i = 0; i < MotionController::MaxActions; i++) action[i] = false; for (int i = 0; i < KEY_MAP_SIZE; i++) map[i] = 0; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { hat[i][j] = false; } } map_axis[0] = KEY_JOY_AXIS_X; map_axis[1] = KEY_JOY_AXIS_Y; map_axis[2] = KEY_JOY_AXIS_RZ; map_axis[3] = KEY_JOY_AXIS_S0; inv_axis[0] = false; inv_axis[1] = false; inv_axis[2] = false; inv_axis[3] = false; if (MachineInfo::GetDirectXVersion() < MachineInfo::DX_7) { Print("Joystick: DI7 not found, using multimedia library\n"); pdi = 0; pdev = 0; } else if (!pdi) { HRESULT hr = DirectInput8Create(Game::GetHINST(), DIRECTINPUT_VERSION, IID_IDirectInput7, (void**)&pdi, NULL); if FAILED(hr) { DirectInputError("Failed to initialize DI7", hr); pdi = 0; pdev = 0; } else { Print("Joystick: initialized DI7 pdi = %08x\n", (DWORD) pdi); } } } Joystick::~Joystick() { ReleaseDirectInput(); joystick = 0; } void ReleaseDirectInput() { if (pdev) { pdev->Unacquire(); pdev->Release(); pdev = 0; } if (pdi) { pdi->Release(); pdi = 0; } } Joystick* Joystick::GetInstance() { return joystick; } // +--------------------------------------------------------------------+ void Joystick::MapKeys(KeyMapEntry* mapping, int nkeys) { ZeroMemory(map, sizeof(map)); for (int i = 0; i < nkeys; i++) { KeyMapEntry k = mapping[i]; if (k.act >= KEY_MAP_FIRST && k.act < KEY_MAP_LAST) { if (k.act == KEY_JOY_SENSE) sensitivity = k.key; else if (k.act == KEY_JOY_DEAD_ZONE) dead_zone = k.key; else if (k.act == KEY_JOY_SWAP) swapped = k.key; else if (k.act == KEY_JOY_RUDDER) rudder = k.key; else if (k.act == KEY_JOY_THROTTLE) throttle = k.key; else if (k.act == KEY_JOY_SELECT) select = k.key; else if (k.act == KEY_AXIS_YAW) map_axis[0] = k.key; else if (k.act == KEY_AXIS_PITCH) map_axis[1] = k.key; else if (k.act == KEY_AXIS_ROLL) map_axis[2] = k.key; else if (k.act == KEY_AXIS_THROTTLE) map_axis[3] = k.key; else if (k.act == KEY_AXIS_YAW_INVERT) inv_axis[0] = k.key ? true : false; else if (k.act == KEY_AXIS_PITCH_INVERT) inv_axis[1] = k.key ? true : false; else if (k.act == KEY_AXIS_ROLL_INVERT) inv_axis[2] = k.key ? true : false; else if (k.act == KEY_AXIS_THROTTLE_INVERT) inv_axis[3] = k.key ? true : false; else if (k.key >= KEY_JOY_1 && k.key <= KEY_JOY_32) map[k.act] = k.key; else if (k.alt >= KEY_JOY_1 && k.alt <= KEY_JOY_32) map[k.act] = k.alt; else if (k.joy >= KEY_JOY_1 && k.joy <= KEY_JOY_32) map[k.act] = k.joy; else if (k.key >= KEY_POV_0_UP && k.key <= KEY_POV_3_RIGHT) map[k.act] = k.key; else if (k.alt >= KEY_POV_0_UP && k.alt <= KEY_POV_3_RIGHT) map[k.act] = k.alt; else if (k.joy >= KEY_POV_0_UP && k.joy <= KEY_POV_3_RIGHT) map[k.act] = k.joy; } } } // +--------------------------------------------------------------------+ static inline double sqr(double a) { return a*a; } BOOL FAR PASCAL EnumJoystick(LPCDIDEVICEINSTANCE pdinst, LPVOID pvSelect) { CopyMemory(&devices[ndev++], pdinst, pdinst->dwSize); ::Print("EnumJoystick %d: '%s'\n", ndev, pdinst->tszInstanceName); ::Print(" guid: {%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x} \n", (DWORD) pdinst->guidInstance.Data1, (WORD) pdinst->guidInstance.Data2, (WORD) pdinst->guidInstance.Data3, (BYTE) pdinst->guidInstance.Data4[0], (BYTE) pdinst->guidInstance.Data4[1], (BYTE) pdinst->guidInstance.Data4[2], (BYTE) pdinst->guidInstance.Data4[3], (BYTE) pdinst->guidInstance.Data4[4], (BYTE) pdinst->guidInstance.Data4[5]); if (ndev < MAX_DEVICES) return DIENUM_CONTINUE; return DIENUM_STOP; } bool CreateDevice(int select) { if (!pdi || ndev < select) return false; LPCDIDEVICEINSTANCE pdinst = &devices[select-1]; ::Print("Joystick CreateDevice(%d)\n", select); ::Print(" name: %s\n\n", pdinst->tszInstanceName); // release current device before trying to get another: if (pdev) { pdev->Unacquire(); pdev->Release(); pdev = 0; } HRESULT hr = DI_OK; // Create the DirectInput joystick device: hr = pdi->CreateDeviceEx(pdinst->guidInstance, IID_IDirectInputDevice7, (void**)&pdev, NULL); if (hr != DI_OK || pdev == 0) { DirectInputError("Create Device Ex failed", hr); return false; } // Set the data format: hr = pdev->SetDataFormat(&c_dfDIJoystick); if (hr != DI_OK) { DirectInputError("Set Data Format failed", hr); pdev->Release(); pdev = 0; return false; } // Set the coop level: hr = pdev->SetCooperativeLevel(Game::GetHWND(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND); if (hr != DI_OK) { DirectInputError("Set Cooperative Level failed", hr); pdev->Release(); return false; } // Set data ranges DIPROPRANGE diprg; diprg.lMin = -32768; diprg.lMax = +32768; diprg.diph.dwSize = sizeof(diprg); diprg.diph.dwHeaderSize = sizeof(diprg.diph); diprg.diph.dwObj = DIJOFS_X; diprg.diph.dwHow = DIPH_BYOFFSET; pdev->SetProperty(DIPROP_RANGE, &diprg.diph); diprg.diph.dwObj = DIJOFS_Y; pdev->SetProperty(DIPROP_RANGE, &diprg.diph); diprg.diph.dwObj = DIJOFS_Z; pdev->SetProperty(DIPROP_RANGE, &diprg.diph); diprg.diph.dwObj = DIJOFS_RX; pdev->SetProperty(DIPROP_RANGE, &diprg.diph); diprg.diph.dwObj = DIJOFS_RY; pdev->SetProperty(DIPROP_RANGE, &diprg.diph); diprg.diph.dwObj = DIJOFS_RZ; pdev->SetProperty(DIPROP_RANGE, &diprg.diph); diprg.diph.dwObj = DIJOFS_SLIDER(0); pdev->SetProperty(DIPROP_RANGE, &diprg.diph); diprg.diph.dwObj = DIJOFS_SLIDER(1); pdev->SetProperty(DIPROP_RANGE, &diprg.diph); ::Print("Created joystick %d (pdev = %08x)\n", select, (DWORD) pdev); idev = select; return true; } void Joystick::EnumerateDevices() { if (!pdi) { Print("Joystick: no DI7, unable to enumerate devices\n"); ndev = 0; } else if (ndev < 1) { Print("Joystick: preparing to enumerate devices\n"); ndev = 0; HRESULT hr = pdi->EnumDevices(DI8DEVTYPE_JOYSTICK, EnumJoystick, (LPVOID) 0, DIEDFL_ATTACHEDONLY); if (FAILED(hr)) { DirectInputError("Failed to enumerate devices", hr); ReleaseDirectInput(); } else if (ndev < 1) { Print("Joystick: no devices found\n"); ReleaseDirectInput(); } } } int Joystick::NumDevices() { return ndev; } const char* Joystick::GetDeviceName(int i) { if (i >= 0 && i < ndev) return devices[i].tszInstanceName; return 0; } // +--------------------------------------------------------------------+ static DIJOYSTATE joystate; static JOYINFOEX joyinfo; int Joystick::ReadRawAxis(int a) { if (!joystick) return 0; int result = 0; if (pdev) { switch (a) { case KEY_JOY_AXIS_X: result = joystate.lX; break; case KEY_JOY_AXIS_Y: result = joystate.lY; break; case KEY_JOY_AXIS_Z: result = joystate.lZ; break; case KEY_JOY_AXIS_RX: result = joystate.lRx; break; case KEY_JOY_AXIS_RY: result = joystate.lRy; break; case KEY_JOY_AXIS_RZ: result = joystate.lRz; break; case KEY_JOY_AXIS_S0: result = joystate.rglSlider[0]; break; case KEY_JOY_AXIS_S1: result = joystate.rglSlider[1]; break; } } else { switch (a) { case KEY_JOY_AXIS_X: if (joyinfo.dwFlags & JOY_RETURNX) result = joyinfo.dwXpos; break; case KEY_JOY_AXIS_Y: if (joyinfo.dwFlags & JOY_RETURNY) result = joyinfo.dwYpos; break; case KEY_JOY_AXIS_Z: if (joyinfo.dwFlags & JOY_RETURNZ) result = joyinfo.dwZpos; break; case KEY_JOY_AXIS_RZ: if (joyinfo.dwFlags & JOY_RETURNR) result = joyinfo.dwRpos; break; } } return result; } double Joystick::ReadAxisDI(int a) { if (a < 0 || a > 3) return 0; double result = 0; switch (map_axis[a]) { case KEY_JOY_AXIS_X: result = joystate.lX; break; case KEY_JOY_AXIS_Y: result = joystate.lY; break; case KEY_JOY_AXIS_Z: result = joystate.lZ; break; case KEY_JOY_AXIS_RX: result = joystate.lRx; break; case KEY_JOY_AXIS_RY: result = joystate.lRy; break; case KEY_JOY_AXIS_RZ: result = joystate.lRz; break; case KEY_JOY_AXIS_S0: result = joystate.rglSlider[0]; break; case KEY_JOY_AXIS_S1: result = joystate.rglSlider[1]; break; } if (a < 3) { // ignore small movements: if (result > dead_zone) result -= dead_zone; else if (result < -dead_zone) result += dead_zone; else result = 0; double scale = 1.0 / (32768.0-dead_zone); if (result >= 0) result = sqr(result * scale); else result = sqr(result * scale) * -1.0; if (inv_axis[a]) result = -result; } else { result = (result+32768.0) / 65536.0; if (inv_axis[a]) result = 1 - result; } return result; } double Joystick::ReadAxisMM(int a) { if (a < 0 || a > 3) return 0; double result = 0; switch (map_axis[a]) { case KEY_JOY_AXIS_X: if (joyinfo.dwFlags & JOY_RETURNX) result = joyinfo.dwXpos - 32768; break; case KEY_JOY_AXIS_Y: if (joyinfo.dwFlags & JOY_RETURNY) result = joyinfo.dwYpos - 32768; break; case KEY_JOY_AXIS_Z: if (joyinfo.dwFlags & JOY_RETURNZ) result = joyinfo.dwZpos - 32768; break; case KEY_JOY_AXIS_RZ: if (joyinfo.dwFlags & JOY_RETURNR) result = joyinfo.dwRpos - 32768; break; } if (a < 3) { // ignore small movements: if (result > dead_zone) result -= dead_zone; else if (result < -dead_zone) result += dead_zone; else result = 0; double scale = 1.0 / (32768.0-dead_zone); if (result >= 0) result = sqr(result * scale); else result = sqr(result * scale) * -1.0; if (inv_axis[a]) result = -result; } else { result = (result+32768.0) / 65536.0; if (inv_axis[a]) result = 1 - result; } return result; } // +--------------------------------------------------------------------+ void Joystick::Acquire() { t = x = y = z = p = r = w = 0; for (int i = 0; i < MotionController::MaxActions; i++) action[i] = false; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) hat[i][j] = false; if (select == 0) return; //============================================================ // // FIRST TRY DIRECT INPUT bool acquired = false; if (pdi) { if (idev != select) { if (ndev < 1) EnumerateDevices(); if (CreateDevice(select) && pdev) pdev->Acquire(); } if (pdev) { HRESULT hr = 0; hr = pdev->Poll(); hr = pdev->GetDeviceState(sizeof(joystate), &joystate); if (hr == DIERR_INPUTLOST) { pdev->Acquire(); hr = pdev->Poll(); hr = pdev->GetDeviceState(sizeof(joystate), &joystate); if (FAILED(hr)) { strikes--; ::Print("Joystick could not re-acquire joystick (%08x)\n", hr); // give up before you hurt yourself: if (strikes <= 0) { ReleaseDirectInput(); ndev = 0; select = 0; } return; } } for (int i = 0; i < 32; i++) action[i] = (joystate.rgbButtons[i] & 0x80) != 0; double joy_x = ReadAxisDI(0); double joy_y = ReadAxisDI(1); double joy_r = rudder ? ReadAxisDI(2) : 0; double joy_t = throttle ? ReadAxisDI(3) : 0; int joy_p = joystate.rgdwPOV[0]; ProcessAxes(joy_x, joy_y, joy_r, joy_t); for (int i = 0; i < 4; i++) ProcessHat(i, joystate.rgdwPOV[i]); acquired = true; } } //============================================================ // // THEN TRY WINDOWS MULTIMEDIA LIBRARY if (!acquired) { memset(&joyinfo, 0, sizeof(JOYINFOEX)); joyinfo.dwSize = sizeof(JOYINFOEX); joyinfo.dwFlags = JOY_RETURNALL; HRESULT hr = 0; if (select == 1) hr = joyGetPosEx(JOYSTICKID1, &joyinfo); else if (select == 2) hr = joyGetPosEx(JOYSTICKID2, &joyinfo); if (hr != 0) { Print("\nJoystick::Acquire() joyGetPosEx %d failed (err=%08x)\n\n", select, hr); select = 0; } action[0] = (joyinfo.dwButtons & JOY_BUTTON1) ? true : false; action[1] = (joyinfo.dwButtons & JOY_BUTTON2) ? true : false; action[2] = (joyinfo.dwButtons & JOY_BUTTON3) ? true : false; action[3] = (joyinfo.dwButtons & JOY_BUTTON4) ? true : false; double joy_x = ReadAxisMM(0); double joy_y = ReadAxisMM(1); double joy_r = rudder ? ReadAxisMM(2) : 0; double joy_t = throttle ? ReadAxisMM(3) : 0; ProcessAxes(joy_x, joy_y, joy_r, joy_t); ProcessHat(0, joyinfo.dwPOV); } // lateral translations: if (KeyDownMap(KEY_PLUS_Y)) y = 1; else if (KeyDownMap(KEY_MINUS_Y)) y = -1; if (KeyDownMap(KEY_PLUS_Z)) z = 1; else if (KeyDownMap(KEY_MINUS_Z)) z = -1; if (KeyDownMap(KEY_MINUS_X)) x = -1; else if (KeyDownMap(KEY_PLUS_X)) x = 1; // button-based turns: const double steps=10; static double p1=0, r1=0, w1=0; // if roll and yaw are swapped -------------------------- if (swapped) { // yaw: if (KeyDownMap(KEY_ROLL_LEFT)) { if (w1 3) return; if (LOWORD(joy_pov) == 0xFFFF) return; switch (joy_pov) { case JOY_POVFORWARD: hat[i][0] = true; break; case JOY_POVBACKWARD: hat[i][1] = true; break; case JOY_POVLEFT: hat[i][2] = true; break; case JOY_POVRIGHT: hat[i][3] = true; break; case JOY_POVUPRIGHT: hat[i][0] = true; hat[i][3] = true; break; case JOY_POVDNRIGHT: hat[i][1] = true; hat[i][3] = true; break; case JOY_POVDNLEFT: hat[i][1] = true; hat[i][2] = true; break; case JOY_POVUPLEFT: hat[i][0] = true; hat[i][2] = true; break; default: break; } } // +--------------------------------------------------------------------+ bool Joystick::KeyDown(int key) { if (!joystick) return false; if (key >= KEY_JOY_1 && key <= KEY_JOY_32) return joystick->action[key - KEY_JOY_1]; else if (key >= KEY_POV_0_UP && key <= KEY_POV_0_RIGHT) return joystick->hat[0][key - KEY_POV_0_UP]; else if (key >= KEY_POV_1_UP && key <= KEY_POV_1_RIGHT) return joystick->hat[1][key - KEY_POV_1_UP]; else if (key >= KEY_POV_2_UP && key <= KEY_POV_2_RIGHT) return joystick->hat[2][key - KEY_POV_2_UP]; else if (key >= KEY_POV_3_UP && key <= KEY_POV_3_RIGHT) return joystick->hat[3][key - KEY_POV_3_UP]; return false; } // +--------------------------------------------------------------------+ bool Joystick::KeyDownMap(int key) { if (!joystick) return false; if (key >= KEY_MAP_FIRST && key <= KEY_MAP_LAST && joystick->map[key]) return KeyDown(joystick->map[key]); return false; } // +--------------------------------------------------------------------+ int Joystick::GetAxisMap(int n) { if (!joystick || n < 0 || n > 3) return -1; return joystick->map_axis[n]; } int Joystick::GetAxisInv(int n) { if (!joystick || n < 0 || n > 3) return -1; return joystick->inv_axis[n]; } // +--------------------------------------------------------------------+ void DirectInputError(const char* msg, HRESULT err) { static int report = 50; if (report > 0) report--; else return; Print(" DirectInput7: %s. [%s]\n", msg, DIErrStr(err)); } static char errstrbuf[128]; const char* DIErrStr(HRESULT hr) { auto casted = static_cast(hr); switch (casted) { default: sprintf_s(errstrbuf, "Unrecognized error value = %08x.", casted); return errstrbuf; case DI_OK: return "No error."; case DI_BUFFEROVERFLOW: return "The device buffer overflowed and some input was lost. This value is equal to the S_FALSE standard COM return value."; case DI_DOWNLOADSKIPPED: return "The parameters of the effect were successfully updated, but the effect could not be downloaded because the associated device was not acquired in exclusive mode."; case DI_EFFECTRESTARTED: return "The effect was stopped, the parameters were updated, and the effect was restarted."; case DI_POLLEDDEVICE: return "The device is a polled device. As a result, device buffering does not collect any data and event notifications is not signaled until the IDirectInputDevice7::Poll method is called."; case DI_TRUNCATED: return "The parameters of the effect were successfully updated, but some of them were beyond the capabilities of the device and were truncated to the nearest supported value."; case DI_TRUNCATEDANDRESTARTED: return "Equal to DI_EFFECTRESTARTED | DI_TRUNCATED"; case DIERR_ACQUIRED: return "The operation cannot be performed while the device is acquired."; case DIERR_ALREADYINITIALIZED: return "This object is already initialized"; case DIERR_BADDRIVERVER: return "The object could not be created due to an incompatible driver version or mismatched or incomplete driver components."; case DIERR_BETADIRECTINPUTVERSION: return "The application was written for an unsupported prerelease version of DirectInput."; case DIERR_DEVICEFULL: return "The device is full."; case DIERR_DEVICENOTREG: return "The device or device instance is not registered with DirectInput. This value is equal to the REGDB_E_CLASSNOTREG standard COM return value."; case DIERR_EFFECTPLAYING: return "The parameters were updated in memory but were not downloaded to the device because the device does not support updating an effect while it is still playing."; case DIERR_HASEFFECTS: return "The device cannot be reinitialized because there are still effects attached to it."; case DIERR_GENERIC: return "An undetermined error occurred inside the DirectInput subsystem. This value is equal to the E_FAIL standard COM return value."; case DIERR_HANDLEEXISTS: return "The device already has an event notification associated with it. This value is equal to the E_ACCESSDENIED standard COM return value."; case DIERR_INCOMPLETEEFFECT: return "The effect could not be downloaded because essential information is missing. For example, no axes have been associated with the effect, or no type-specific information has been supplied."; case DIERR_INPUTLOST: return "Access to the input device has been lost. It must be reacquired."; case DIERR_INVALIDPARAM: return "An invalid parameter was passed to the returning function, or the object was not in a state that permitted the function to be called. This value is equal to the E_INVALIDARG standard COM return value."; case DIERR_MOREDATA: return "Not all the requested information fit into the buffer."; case DIERR_NOAGGREGATION: return "This object does not support aggregation."; case DIERR_NOINTERFACE: return "The specified interface is not supported by the object. This value is equal to the E_NOINTERFACE standard COM return value."; case DIERR_NOTACQUIRED: return "The operation cannot be performed unless the device is acquired."; case DIERR_NOTBUFFERED: return "The device is not buffered. Set the DIPROP_BUFFERSIZE property to enable buffering."; case DIERR_NOTDOWNLOADED: return "The effect is not downloaded."; case DIERR_NOTEXCLUSIVEACQUIRED: return "The operation cannot be performed unless the device is acquired in DISCL_EXCLUSIVE mode."; case DIERR_NOTFOUND: return "The requested object does not exist."; case DIERR_NOTINITIALIZED: return "This object has not been initialized."; case DIERR_OLDDIRECTINPUTVERSION: return "The application requires a newer version of DirectInput."; case DIERR_OUTOFMEMORY: return "The DirectInput subsystem could not allocate sufficient memory to complete the call. This value is equal to the E_OUTOFMEMORY standard COM return value."; case DIERR_REPORTFULL: return "More information was requested to be sent than can be sent to the device."; case DIERR_UNPLUGGED: return "The operation could not be completed because the device is not plugged in."; case DIERR_UNSUPPORTED: return "The function called is not supported at this time. This value is equal to the E_NOTIMPL standard COM return value."; } }