diff --git a/idd/LGIdd/util/CSRWLock.h b/idd/LGCommon/CSRWLock.h
similarity index 100%
rename from idd/LGIdd/util/CSRWLock.h
rename to idd/LGCommon/CSRWLock.h
diff --git a/idd/LGIdd.sln b/idd/LGIdd.sln
index 7af20bf9..30bd53ca 100644
--- a/idd/LGIdd.sln
+++ b/idd/LGIdd.sln
@@ -6,9 +6,12 @@ MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LGIdd", "LGIdd\LGIdd.vcxproj", "{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}"
ProjectSection(ProjectDependencies) = postProject
{0045D7AD-3F26-4B87-81CB-78D18839596D} = {0045D7AD-3F26-4B87-81CB-78D18839596D}
+ {23EC8370-5F3B-4D18-8C31-E89A154F3666} = {23EC8370-5F3B-4D18-8C31-E89A154F3666}
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65} = {FFCC376C-4D98-4B50-B431-E1BBC9C67E65}
EndProjectSection
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LGInput", "LGInput\LGInput.vcxproj", "{2477B25B-CB62-4AD9-A260-CE5F00D77EEB}"
+EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LGMP", "..\repos\LGMP\LGMP.vcxproj", "{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LGIddHelper", "LGIddHelper\LGIddHelper.vcxproj", "{0045D7AD-3F26-4B87-81CB-78D18839596D}"
@@ -17,6 +20,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LGCommon", "LGCommon", "{AC
ProjectSection(SolutionItems) = preProject
LGCommon\CDebug.cpp = LGCommon\CDebug.cpp
LGCommon\CDebug.h = LGCommon\CDebug.h
+ LGCommon\CSRWLock.h = LGCommon\CSRWLock.h
LGCommon\DefaultDisplayModes.h = LGCommon\DefaultDisplayModes.h
LGCommon\PipeMsg.h = LGCommon\PipeMsg.h
EndProjectSection
@@ -26,49 +30,55 @@ EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
- Debug|x86 = Debug|x86
+ Debug-LGInput|x64 = Debug-LGInput|x64
Release|x64 = Release|x64
- Release|x86 = Release|x86
+ Release-LGInput|x64 = Release-LGInput|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug|x64.ActiveCfg = Debug|x64
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug|x64.Build.0 = Debug|x64
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug|x64.Deploy.0 = Debug|x64
- {1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug|x86.ActiveCfg = Debug|Win32
- {1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug|x86.Build.0 = Debug|Win32
- {1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug|x86.Deploy.0 = Debug|Win32
+ {1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug-LGInput|x64.ActiveCfg = Debug-LGInput|x64
+ {1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug-LGInput|x64.Build.0 = Debug-LGInput|x64
+ {1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug-LGInput|x64.Deploy.0 = Debug-LGInput|x64
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release|x64.ActiveCfg = Release|x64
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release|x64.Build.0 = Release|x64
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release|x64.Deploy.0 = Release|x64
- {1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release|x86.ActiveCfg = Release|Win32
- {1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release|x86.Build.0 = Release|Win32
- {1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release|x86.Deploy.0 = Release|Win32
+ {1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release-LGInput|x64.ActiveCfg = Release-LGInput|x64
+ {1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release-LGInput|x64.Build.0 = Release-LGInput|x64
+ {1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release-LGInput|x64.Deploy.0 = Release-LGInput|x64
+ {2477B25B-CB62-4AD9-A260-CE5F00D77EEB}.Debug|x64.ActiveCfg = Debug|x64
+ {2477B25B-CB62-4AD9-A260-CE5F00D77EEB}.Debug|x64.Build.0 = Debug|x64
+ {2477B25B-CB62-4AD9-A260-CE5F00D77EEB}.Debug-LGInput|x64.ActiveCfg = Debug|x64
+ {2477B25B-CB62-4AD9-A260-CE5F00D77EEB}.Debug-LGInput|x64.Build.0 = Debug|x64
+ {2477B25B-CB62-4AD9-A260-CE5F00D77EEB}.Release|x64.ActiveCfg = Release|x64
+ {2477B25B-CB62-4AD9-A260-CE5F00D77EEB}.Release|x64.Build.0 = Release|x64
+ {2477B25B-CB62-4AD9-A260-CE5F00D77EEB}.Release-LGInput|x64.ActiveCfg = Release|x64
+ {2477B25B-CB62-4AD9-A260-CE5F00D77EEB}.Release-LGInput|x64.Build.0 = Release|x64
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug|x64.ActiveCfg = Debug|x64
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug|x64.Build.0 = Debug|x64
- {FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug|x86.ActiveCfg = Debug|Win32
- {FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug|x86.Build.0 = Debug|Win32
- {FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug|x86.Deploy.0 = Debug|Win32
+ {FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug-LGInput|x64.ActiveCfg = Debug|x64
+ {FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug-LGInput|x64.Build.0 = Debug|x64
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|x64.ActiveCfg = Release|x64
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|x64.Build.0 = Release|x64
- {FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|x86.ActiveCfg = Release|Win32
- {FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|x86.Build.0 = Release|Win32
- {FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|x86.Deploy.0 = Release|Win32
+ {FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release-LGInput|x64.ActiveCfg = Release|x64
+ {FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release-LGInput|x64.Build.0 = Release|x64
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|x64.ActiveCfg = Debug|x64
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|x64.Build.0 = Debug|x64
- {0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|x86.ActiveCfg = Debug|Win32
- {0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|x86.Build.0 = Debug|Win32
+ {0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug-LGInput|x64.ActiveCfg = Debug|x64
+ {0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug-LGInput|x64.Build.0 = Debug|x64
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|x64.ActiveCfg = Release|x64
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|x64.Build.0 = Release|x64
- {0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|x86.ActiveCfg = Release|Win32
- {0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|x86.Build.0 = Release|Win32
+ {0045D7AD-3F26-4B87-81CB-78D18839596D}.Release-LGInput|x64.ActiveCfg = Release|x64
+ {0045D7AD-3F26-4B87-81CB-78D18839596D}.Release-LGInput|x64.Build.0 = Release|x64
{23EC8370-5F3B-4D18-8C31-E89A154F3666}.Debug|x64.ActiveCfg = Debug|x64
{23EC8370-5F3B-4D18-8C31-E89A154F3666}.Debug|x64.Build.0 = Debug|x64
- {23EC8370-5F3B-4D18-8C31-E89A154F3666}.Debug|x86.ActiveCfg = Debug|Win32
- {23EC8370-5F3B-4D18-8C31-E89A154F3666}.Debug|x86.Build.0 = Debug|Win32
+ {23EC8370-5F3B-4D18-8C31-E89A154F3666}.Debug-LGInput|x64.ActiveCfg = Debug|x64
+ {23EC8370-5F3B-4D18-8C31-E89A154F3666}.Debug-LGInput|x64.Build.0 = Debug|x64
{23EC8370-5F3B-4D18-8C31-E89A154F3666}.Release|x64.ActiveCfg = Release|x64
{23EC8370-5F3B-4D18-8C31-E89A154F3666}.Release|x64.Build.0 = Release|x64
- {23EC8370-5F3B-4D18-8C31-E89A154F3666}.Release|x86.ActiveCfg = Release|Win32
- {23EC8370-5F3B-4D18-8C31-E89A154F3666}.Release|x86.Build.0 = Release|Win32
+ {23EC8370-5F3B-4D18-8C31-E89A154F3666}.Release-LGInput|x64.ActiveCfg = Release|x64
+ {23EC8370-5F3B-4D18-8C31-E89A154F3666}.Release-LGInput|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/idd/LGIdd/LGIdd.vcxproj b/idd/LGIdd/LGIdd.vcxproj
index eb0c3bc5..3edb4197 100644
--- a/idd/LGIdd/LGIdd.vcxproj
+++ b/idd/LGIdd/LGIdd.vcxproj
@@ -18,6 +18,14 @@
Release
x64
+
+ Debug-LGInput
+ x64
+
+
+ Release-LGInput
+ x64
+
@@ -109,7 +117,6 @@
-
@@ -121,8 +128,14 @@
12.0
Debug
Win32
+ Debug
+ Release
+ $(Configuration)
+ true
LGIdd
10.0.26100.0
+ $([MSBuild]::NormalizeDirectory('$(MSBuildProjectDirectory)\..'))
+ $(LGDriverSolutionDir)
WindowsUserModeDriver10.0
@@ -134,11 +147,11 @@
DynamicLibrary
Universal
-
+
DynamicLibrary
Universal
-
+
DynamicLibrary
Universal
@@ -167,7 +180,7 @@
25
<_NT_TARGET_VERSION>0xA000005
-
+
Windows10
true
2
@@ -180,7 +193,7 @@
<_NT_TARGET_VERSION>0xA000005
WindowsUserModeDriver10.0
-
+
Windows10
false
2
@@ -208,14 +221,18 @@
DbgengRemoteDebugger
true
-
+
DbgengRemoteDebugger
true
-
+
DbgengRemoteDebugger
true
+
+ $([MSBuild]::NormalizeDirectory('$(LGDriverSolutionDir)$(Platform)\$(LGBaseConfiguration)'))
+ $([MSBuild]::NormalizeDirectory('$(LGDriverSolutionDir)$(LGBaseConfiguration)'))
+
true
@@ -246,7 +263,7 @@
SHA1
-
+
true
true
@@ -261,7 +278,7 @@
SHA1
-
+
true
true
@@ -278,13 +295,58 @@
+
+
+
+
+
+
+
+ {2477b25b-cb62-4ad9-a260-ce5f00d77eeb}
+ false
+ false
+ LGInput
+ Configuration=$(LGBaseConfiguration);Platform=$(Platform);SolutionDir=$(LGDriverSolutionDir)
+
{ffcc376c-4d98-4b50-b431-e1bbc9c67e65}
+ Configuration=$(LGBaseConfiguration);Platform=$(Platform);SolutionDir=$(LGDriverSolutionDir)
+
+ false
+ CustomCommand
+ %ComSpec% /d /s /c LGIddInstall.exe install LGIdd LGInput
+ %ComSpec% /d /s /c LGIddInstall.exe install LGInput LGIdd
+ $(PackageDir)LGIddInstall.exe
+
+
+
+
+
+
+
+
+ PackageTestSign
+ PackageProductionSign
+ Inf2Cat
+
+
+
+
+
diff --git a/idd/LGIdd/LGIdd.vcxproj.filters b/idd/LGIdd/LGIdd.vcxproj.filters
index 8c7e3b6f..03dd0305 100644
--- a/idd/LGIdd/LGIdd.vcxproj.filters
+++ b/idd/LGIdd/LGIdd.vcxproj.filters
@@ -193,9 +193,6 @@
Platform
-
- Utilities
-
diff --git a/idd/LGIdd/capture/CHardwareFrameProcessor.cpp b/idd/LGIdd/capture/CHardwareFrameProcessor.cpp
index bcad171e..abd4a742 100644
--- a/idd/LGIdd/capture/CHardwareFrameProcessor.cpp
+++ b/idd/LGIdd/capture/CHardwareFrameProcessor.cpp
@@ -21,7 +21,7 @@
#include "capture/CHardwareFrameProcessor.h"
#include "capture/CFrameProcessorUtil.h"
#include "transport/IFrameTransport.h"
-#include "util/CSRWLock.h"
+#include "CSRWLock.h"
#include "CDebug.h"
#include
diff --git a/idd/LGIdd/capture/CSoftwareFrameProcessor.cpp b/idd/LGIdd/capture/CSoftwareFrameProcessor.cpp
index 4699d610..fad3575c 100644
--- a/idd/LGIdd/capture/CSoftwareFrameProcessor.cpp
+++ b/idd/LGIdd/capture/CSoftwareFrameProcessor.cpp
@@ -21,8 +21,8 @@
#include "capture/CSoftwareFrameProcessor.h"
#include "capture/CFrameProcessorUtil.h"
#include "capture/FramePipeline.h"
+#include "CSRWLock.h"
#include "transport/IFrameTransport.h"
-#include "util/CSRWLock.h"
#include "CDebug.h"
#include
diff --git a/idd/LGIdd/capture/CSwapChainProcessor.cpp b/idd/LGIdd/capture/CSwapChainProcessor.cpp
index 2d6010b2..d40baeb9 100644
--- a/idd/LGIdd/capture/CSwapChainProcessor.cpp
+++ b/idd/LGIdd/capture/CSwapChainProcessor.cpp
@@ -20,13 +20,13 @@
#include "capture/CSwapChainProcessor.h"
#include "capture/CFrameProcessorUtil.h"
+#include "CSRWLock.h"
#include "display/IddCxCompat.h"
#include "display/CDeviceContext.h"
#include "display/CMonitorContext.h"
#include "platform/CPlatformInfo.h"
#include "transport/IFrameTransport.h"
#include "transport/IControlTransport.h"
-#include "util/CSRWLock.h"
#include
#include
diff --git a/idd/LGIdd/display/CDisplayConfiguration.cpp b/idd/LGIdd/display/CDisplayConfiguration.cpp
index 3be72330..a2fc511e 100644
--- a/idd/LGIdd/display/CDisplayConfiguration.cpp
+++ b/idd/LGIdd/display/CDisplayConfiguration.cpp
@@ -21,7 +21,7 @@
#include "display/CDisplayConfiguration.h"
#include "CDebug.h"
-#include "util/CSRWLock.h"
+#include "CSRWLock.h"
#include
#include
diff --git a/idd/LGIddInstall/LGIddInstall.c b/idd/LGIddInstall/LGIddInstall.c
index dcd60c05..abd38bc9 100644
--- a/idd/LGIddInstall/LGIddInstall.c
+++ b/idd/LGIddInstall/LGIddInstall.c
@@ -36,17 +36,76 @@
#define LGIDD_CLASS_GUID GUID_DEVCLASS_DISPLAY
#define LGIDD_CLASS_NAME L"Display"
+#define LGIDD_NAME L"LGIdd"
#define LGIDD_HWID L"Root\\LGIdd"
#define LGIDD_HWID_MULTI_SZ (LGIDD_HWID "\0")
#define LGIDD_INF_NAME L"LGIdd.inf"
+#define LGIDD_PACKAGE_DIR L"LGIdd"
+#define LGINPUT_CLASS_GUID GUID_DEVCLASS_HIDCLASS
+#define LGINPUT_CLASS_NAME L"HIDClass"
+#define LGINPUT_NAME L"LGInput"
+#define LGINPUT_HWID L"Root\\LGInput"
+#define LGINPUT_HWID_MULTI_SZ (LGINPUT_HWID "\0")
+#define LGINPUT_INF_NAME L"LGInput.inf"
+#define LGINPUT_PACKAGE_DIR L"LGInput"
#define LGIDD_REGKEY L"Software\\LookingGlass\\IDD"
+#define DEVICE_COUNT 2
+
+typedef struct DeviceDesc
+{
+ const GUID *classGuid;
+ LPCWSTR className;
+ LPCWSTR name;
+ LPCWSTR hardwareId;
+ LPCWSTR hardwareIdMultiSz;
+ DWORD hardwareIdMultiSzSize;
+ LPCWSTR infName;
+ LPCWSTR packageDir;
+}
+DeviceDesc;
+
+static const DeviceDesc LGIDD_DEVICE =
+{
+ .classGuid = &LGIDD_CLASS_GUID,
+ .className = LGIDD_CLASS_NAME,
+ .name = LGIDD_NAME,
+ .hardwareId = LGIDD_HWID,
+ .hardwareIdMultiSz = LGIDD_HWID_MULTI_SZ,
+ .hardwareIdMultiSzSize = sizeof LGIDD_HWID_MULTI_SZ,
+ .infName = LGIDD_INF_NAME,
+ .packageDir = LGIDD_PACKAGE_DIR,
+};
+
+static const DeviceDesc LGINPUT_DEVICE =
+{
+ .classGuid = &LGINPUT_CLASS_GUID,
+ .className = LGINPUT_CLASS_NAME,
+ .name = LGINPUT_NAME,
+ .hardwareId = LGINPUT_HWID,
+ .hardwareIdMultiSz = LGINPUT_HWID_MULTI_SZ,
+ .hardwareIdMultiSzSize = sizeof LGINPUT_HWID_MULTI_SZ,
+ .infName = LGINPUT_INF_NAME,
+ .packageDir = LGINPUT_PACKAGE_DIR,
+};
void usage(wchar_t *program)
{
- wprintf(L"Usage: %s \n", program);
+ wprintf(L"Usage: %s install [LGIdd LGInput|LGInput LGIdd]\n", program);
+ wprintf(L" %s uninstall\n", program);
exit(2);
}
+const DeviceDesc *getDeviceByName(LPCWSTR name)
+{
+ if (!_wcsicmp(name, LGIDD_DEVICE.name))
+ return &LGIDD_DEVICE;
+
+ if (!_wcsicmp(name, LGINPUT_DEVICE.name))
+ return &LGINPUT_DEVICE;
+
+ return NULL;
+}
+
void debugWinError(const wchar_t *desc, HRESULT status)
{
wchar_t *buffer;
@@ -154,11 +213,18 @@ DWORD deleteKeyTreeHKLM()
return ec;
}
-typedef bool (*IDD_FOUND_PROC)(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDevInfo, void *pContext);
+typedef bool (*DEVICE_FOUND_PROC)(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDevInfo, void *pContext);
-bool findIddDevice(IDD_FOUND_PROC procFound, void *pContext)
+typedef struct DeviceRemovalContext
{
- HDEVINFO hDevInfo = SetupDiGetClassDevsW(&LGIDD_CLASS_GUID, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
+ LPBOOL pbNeedRestart;
+ bool success;
+}
+DeviceRemovalContext;
+
+bool findDevice(const DeviceDesc *device, DEVICE_FOUND_PROC procFound, void *pContext)
+{
+ HDEVINFO hDevInfo = SetupDiGetClassDevsW(device->classGuid, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
debugWinError(L"SetupDiGetClassDevsW", GetLastError());
@@ -205,7 +271,7 @@ bool findIddDevice(IDD_FOUND_PROC procFound, void *pContext)
for (LPWSTR lpHwId = lpBuffer; *lpHwId; lpHwId += wcslen(lpBuffer) + 1)
{
- if (!lstrcmpiW(lpHwId, LGIDD_HWID))
+ if (!lstrcmpiW(lpHwId, device->hardwareId))
{
found = true;
break;
@@ -232,24 +298,24 @@ enum DeviceCreated {
DEVICE_UNKNOWN,
};
-bool isIddDeviceCreatedEnum(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDevInfo, void *pContext)
+bool isDeviceCreatedEnum(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDevInfo, void *pContext)
{
enum DeviceCreated *result = pContext;
*result = DEVICE_CREATED;
return false;
}
-enum DeviceCreated isIddDeviceCreated()
+enum DeviceCreated isDeviceCreated(const DeviceDesc *device)
{
enum DeviceCreated result = DEVICE_UNKNOWN;
- if (findIddDevice(isIddDeviceCreatedEnum, &result) && result == DEVICE_UNKNOWN)
+ if (findDevice(device, isDeviceCreatedEnum, &result) && result == DEVICE_UNKNOWN)
result = DEVICE_NOT_CREATED;
return result;
}
-bool createIddDevice(void)
+bool createDevice(const DeviceDesc *device)
{
- HDEVINFO hDevInfo = SetupDiCreateDeviceInfoList(&LGIDD_CLASS_GUID, NULL);
+ HDEVINFO hDevInfo = SetupDiCreateDeviceInfoList(device->classGuid, NULL);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
debugWinError(L"SetupDiCreateDeviceInfoList", GetLastError());
@@ -257,13 +323,14 @@ bool createIddDevice(void)
}
SP_DEVINFO_DATA devInfo = { .cbSize = sizeof devInfo, 0 };
- if (!SetupDiCreateDeviceInfoW(hDevInfo, LGIDD_CLASS_NAME, &LGIDD_CLASS_GUID, NULL, NULL, DICD_GENERATE_ID, &devInfo))
+ if (!SetupDiCreateDeviceInfoW(hDevInfo, device->className, device->classGuid, NULL, NULL, DICD_GENERATE_ID, &devInfo))
{
debugWinError(L"SetupDiCreateDeviceInfoW", GetLastError());
goto fail;
}
- if (!SetupDiSetDeviceRegistryPropertyW(hDevInfo, &devInfo, SPDRP_HARDWAREID, (PBYTE) LGIDD_HWID_MULTI_SZ, sizeof LGIDD_HWID_MULTI_SZ))
+ if (!SetupDiSetDeviceRegistryPropertyW(hDevInfo, &devInfo, SPDRP_HARDWAREID,
+ (PBYTE) device->hardwareIdMultiSz, device->hardwareIdMultiSzSize))
{
debugWinError(L"SetupDiSetDeviceRegistryPropertyW", GetLastError());
goto fail;
@@ -282,9 +349,9 @@ fail:
return false;
}
-bool destroyIddDeviceEnum(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDevInfo, void* pContext)
+bool destroyDeviceEnum(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDevInfo, void *pContext)
{
- LPBOOL pbNeedRestart = pContext;
+ DeviceRemovalContext *context = pContext;
BOOL bNeedRestart;
WCHAR szInfPath[MAX_PATH] = { 0 };
@@ -312,40 +379,39 @@ bool destroyIddDeviceEnum(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDevInfo, void* pC
uninstall:
if (DiUninstallDevice(NULL, hDevInfo, pDevInfo, 0, &bNeedRestart))
- *pbNeedRestart |= bNeedRestart;
+ *context->pbNeedRestart |= bNeedRestart;
else
{
debugWinError(L"DiUninstallDevice", GetLastError());
+ context->success = false;
return true;
}
if (*szInfPath)
{
if (DiUninstallDriverW(NULL, szInfPath, 0, &bNeedRestart))
- *pbNeedRestart |= bNeedRestart;
+ *context->pbNeedRestart |= bNeedRestart;
else
+ {
debugWinError(L"DiUninstallDriverW", GetLastError());
- }
-
- DWORD ec = deleteKeyTreeHKLM();
- if (ec != ERROR_SUCCESS)
- {
- debugWinError(L"deleteKeyTreeHKLM failed", ec);
- // this is non-fatal
+ context->success = false;
+ }
}
return true;
}
-void destroyIddDevice(LPBOOL pbNeedRestart)
+bool destroyDevice(const DeviceDesc *device, LPBOOL pbNeedRestart)
{
- findIddDevice(destroyIddDeviceEnum, pbNeedRestart);
+ DeviceRemovalContext context = { pbNeedRestart, true };
+
+ return findDevice(device, destroyDeviceEnum, &context) && context.success;
}
-bool getIddInfPath(LPWSTR lpszInf)
+bool getInfPath(const DeviceDesc *device, LPWSTR lpszInf)
{
WCHAR szDir[MAX_PATH];
- WCHAR szInf[MAX_PATH];
+ WCHAR szPackageDir[MAX_PATH];
if (!GetModuleFileNameW(NULL, szDir, MAX_PATH))
{
@@ -354,7 +420,7 @@ bool getIddInfPath(LPWSTR lpszInf)
}
*PathFindFileNameW(szDir) = 0;
- if (!PathCombineW(lpszInf, szDir, LGIDD_INF_NAME))
+ if (!PathCombineW(lpszInf, szDir, device->infName))
{
debugWinError(L"PathCombineW", GetLastError());
return false;
@@ -362,18 +428,28 @@ bool getIddInfPath(LPWSTR lpszInf)
if (!PathFileExistsW(lpszInf))
{
- fwprintf(stderr, L"INF file does not exist: %s\n", szInf);
- return false;
+ if (!PathCombineW(szPackageDir, szDir, device->packageDir) ||
+ !PathCombineW(lpszInf, szPackageDir, device->infName))
+ {
+ debugWinError(L"PathCombineW", GetLastError());
+ return false;
+ }
+
+ if (!PathFileExistsW(lpszInf))
+ {
+ fwprintf(stderr, L"INF file does not exist: %s\n", lpszInf);
+ return false;
+ }
}
return true;
}
-bool installIddInf(PBOOL pbNeedRestart)
+bool installInf(const DeviceDesc *device, PBOOL pbNeedRestart)
{
WCHAR szInf[MAX_PATH];
- if (!getIddInfPath(szInf))
+ if (!getInfPath(device, szInf))
return false;
if (!DiInstallDriverW(NULL, szInf, DIIRFLAG_FORCE_INF, pbNeedRestart))
@@ -385,41 +461,75 @@ bool installIddInf(PBOOL pbNeedRestart)
return true;
}
-void install()
+void install(const DeviceDesc *const devices[DEVICE_COUNT])
{
- switch (isIddDeviceCreated())
+ enum DeviceCreated created[DEVICE_COUNT];
+
+ for (size_t i = 0; i < DEVICE_COUNT; ++i)
{
- case DEVICE_NOT_CREATED:
- wprintf(L"Creating LGIdd device: %s...\n", LGIDD_HWID);
- if (!createIddDevice())
+ created[i] = isDeviceCreated(devices[i]);
+ if (created[i] == DEVICE_UNKNOWN)
exit(1);
+ }
- // fallthrough
- case DEVICE_CREATED:
- _putws(L"Preparing registry key...");
- if (!ensureKeyWithAce())
- exit(1);
-
- _putws(L"Installing INF...");
- BOOL bNeedRestart;
- if (!installIddInf(&bNeedRestart))
- exit(1);
-
- if (bNeedRestart)
- {
- _putws(L"Restart required to complete installation");
- exit(12);
- }
- break;
- case DEVICE_UNKNOWN:
+ _putws(L"Preparing registry key...");
+ if (!ensureKeyWithAce())
exit(1);
+
+ BOOL bNeedRestart = FALSE;
+ BOOL bInfNeedRestart = FALSE;
+
+ for (size_t i = 0; i < DEVICE_COUNT; ++i)
+ {
+ const DeviceDesc *device = devices[i];
+
+ if (created[i] == DEVICE_NOT_CREATED)
+ {
+ wprintf(L"Preinstalling %s INF...\n", device->name);
+ bInfNeedRestart = FALSE;
+ if (!installInf(device, &bInfNeedRestart))
+ exit(1);
+ bNeedRestart |= bInfNeedRestart;
+
+ wprintf(L"Creating %s device: %s...\n", device->name, device->hardwareId);
+ if (!createDevice(device))
+ exit(1);
+ }
+
+ wprintf(L"Installing %s INF...\n", device->name);
+ bInfNeedRestart = FALSE;
+ if (!installInf(device, &bInfNeedRestart))
+ exit(1);
+ bNeedRestart |= bInfNeedRestart;
+ }
+
+ if (bNeedRestart)
+ {
+ _putws(L"Restart required to complete installation");
+ exit(12);
}
}
void uninstall()
{
BOOL bNeedRestart = 0;
- destroyIddDevice(&bNeedRestart);
+ bool success = true;
+
+ _putws(L"Uninstalling LGInput...");
+ success &= destroyDevice(&LGINPUT_DEVICE, &bNeedRestart);
+
+ _putws(L"Uninstalling LGIdd...");
+ success &= destroyDevice(&LGIDD_DEVICE, &bNeedRestart);
+
+ DWORD ec = deleteKeyTreeHKLM();
+ if (ec != ERROR_SUCCESS)
+ {
+ debugWinError(L"deleteKeyTreeHKLM failed", ec);
+ // this is non-fatal
+ }
+
+ if (!success)
+ exit(1);
if (bNeedRestart)
{
@@ -432,13 +542,32 @@ int wmain(int argc, wchar_t **argv)
{
_setmode(_fileno(stderr), _O_U16TEXT);
- if (argc != 2)
+ if (argc < 2)
usage(argv[0]);
if (!wcscmp(argv[1], L"install"))
- install();
+ {
+ const DeviceDesc *devices[DEVICE_COUNT] = { &LGIDD_DEVICE, &LGINPUT_DEVICE };
+
+ if (argc != 2)
+ {
+ if (argc != 4)
+ usage(argv[0]);
+
+ devices[0] = getDeviceByName(argv[2]);
+ devices[1] = getDeviceByName(argv[3]);
+ if (!devices[0] || !devices[1] || devices[0] == devices[1])
+ usage(argv[0]);
+ }
+
+ install(devices);
+ }
else if (!wcscmp(argv[1], L"uninstall"))
+ {
+ if (argc != 2)
+ usage(argv[0]);
uninstall();
+ }
else
usage(argv[0]);
}
diff --git a/idd/LGInput/CHIDDevice.cpp b/idd/LGInput/CHIDDevice.cpp
new file mode 100644
index 00000000..edad1cc3
--- /dev/null
+++ b/idd/LGInput/CHIDDevice.cpp
@@ -0,0 +1,400 @@
+/**
+ * Looking Glass
+ * Copyright © 2017-2026 The Looking Glass Authors
+ * https://looking-glass.io
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "CHIDDevice.h"
+
+#include "CSRWLock.h"
+#include "HIDReports.h"
+
+#include
+
+static constexpr USHORT LG_INPUT_VENDOR_ID = 0x0000;
+static constexpr USHORT LG_INPUT_PRODUCT_ID = 0x0000;
+static constexpr USHORT LG_INPUT_VERSION = 0x0001;
+static constexpr size_t REPORT_QUEUE_LENGTH = 64;
+
+struct HIDQueuedReport
+{
+ UCHAR data[sizeof(HIDKeyboardReport)];
+ size_t size;
+};
+
+struct HIDDeviceContext
+{
+ WDFQUEUE reportQueue;
+ HID_DEVICE_ATTRIBUTES attributes;
+ HID_DESCRIPTOR descriptor;
+ UCHAR keyboardLeds;
+ SRWLOCK reportLock;
+ bool active;
+ bool stopping;
+ size_t reportHead;
+ size_t reportCount;
+ HIDQueuedReport reports[REPORT_QUEUE_LENGTH];
+};
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(HIDDeviceContext, HIDGetDeviceContext);
+
+static SRWLOCK s_deviceLock = SRWLOCK_INIT;
+static HIDDeviceContext * s_device = nullptr;
+
+EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL HIDEvtIoDeviceControl;
+EVT_WDF_OBJECT_CONTEXT_CLEANUP HIDEvtReportQueueCleanup;
+
+static NTSTATUS CopyToRequest(
+ _In_ WDFREQUEST request,
+ _In_reads_bytes_(size) const void * buffer,
+ _In_ size_t size)
+{
+ WDFMEMORY memory;
+ NTSTATUS status = WdfRequestRetrieveOutputMemory(request, &memory);
+ if (!NT_SUCCESS(status))
+ return status;
+
+ size_t bufferSize;
+ WdfMemoryGetBuffer(memory, &bufferSize);
+ if (bufferSize < size)
+ return STATUS_BUFFER_TOO_SMALL;
+
+ status = WdfMemoryCopyFromBuffer(
+ memory, 0, const_cast(buffer), size);
+ if (NT_SUCCESS(status))
+ WdfRequestSetInformation(request, size);
+
+ return status;
+}
+
+static NTSTATUS GetOutputReport(
+ _In_ WDFREQUEST request,
+ _Out_ HID_XFER_PACKET * packet)
+{
+ WDFMEMORY memory;
+ NTSTATUS status = WdfRequestRetrieveOutputMemory(request, &memory);
+ if (!NT_SUCCESS(status))
+ return status;
+
+ // mshidumdf carries the report ID in the output buffer length.
+ size_t outputBufferLength;
+ WdfMemoryGetBuffer(memory, &outputBufferLength);
+ packet->reportId = static_cast(outputBufferLength);
+
+ status = WdfRequestRetrieveInputMemory(request, &memory);
+ if (!NT_SUCCESS(status))
+ return status;
+
+ size_t reportSize;
+ packet->reportBuffer =
+ static_cast(WdfMemoryGetBuffer(memory, &reportSize));
+ packet->reportBufferLen = static_cast(reportSize);
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS SetOutputReport(
+ _In_ WDFQUEUE queue,
+ _In_ WDFREQUEST request)
+{
+ HID_XFER_PACKET packet = {};
+ NTSTATUS status = GetOutputReport(request, &packet);
+ if (!NT_SUCCESS(status))
+ return status;
+
+ if (packet.reportId != HID_REPORT_ID_KEYBOARD ||
+ packet.reportBufferLen < sizeof(HIDKeyboardLedsReport))
+ return STATUS_INVALID_PARAMETER;
+
+ const HIDKeyboardLedsReport * report =
+ reinterpret_cast(packet.reportBuffer);
+ if (report->reportId != HID_REPORT_ID_KEYBOARD)
+ return STATUS_INVALID_PARAMETER;
+
+ WDFDEVICE device = WdfIoQueueGetDevice(queue);
+ HIDGetDeviceContext(device)->keyboardLeds = report->leds;
+ WdfRequestSetInformation(request, sizeof(*report));
+ return STATUS_SUCCESS;
+}
+
+static void PopReport(
+ _Inout_ HIDDeviceContext * context,
+ _Out_ HIDQueuedReport * report)
+{
+ *report = context->reports[context->reportHead];
+ context->reportHead =
+ (context->reportHead + 1) % REPORT_QUEUE_LENGTH;
+ --context->reportCount;
+}
+
+static NTSTATUS QueueReport(
+ _Inout_ HIDDeviceContext * context,
+ _In_reads_bytes_(size) const void * data,
+ _In_ size_t size)
+{
+ if (context->reportCount == REPORT_QUEUE_LENGTH)
+ return STATUS_BUFFER_OVERFLOW;
+
+ const size_t index =
+ (context->reportHead + context->reportCount) % REPORT_QUEUE_LENGTH;
+ HIDQueuedReport * report = &context->reports[index];
+ CopyMemory(report->data, data, size);
+ report->size = size;
+ ++context->reportCount;
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS ReadReport(
+ _In_ HIDDeviceContext * context,
+ _In_ WDFREQUEST request,
+ _Out_ bool * complete)
+{
+ HIDQueuedReport report = {};
+ bool haveReport = false;
+ *complete = true;
+
+ NTSTATUS status;
+ {
+ CSRWExclusiveLock lock(&context->reportLock);
+ if (context->stopping || !context->active)
+ status = STATUS_DEVICE_NOT_READY;
+ else if (context->reportCount)
+ {
+ PopReport(context, &report);
+ haveReport = true;
+ status = STATUS_SUCCESS;
+ }
+ else
+ {
+ status = WdfRequestForwardToIoQueue(request, context->reportQueue);
+ *complete = !NT_SUCCESS(status);
+ }
+ }
+
+ if (haveReport)
+ status = CopyToRequest(request, report.data, report.size);
+ return status;
+}
+
+static NTSTATUS ActivateDevice(_Inout_ HIDDeviceContext * context)
+{
+ CSRWExclusiveLock lock(&context->reportLock);
+ if (context->stopping)
+ return STATUS_DEVICE_NOT_READY;
+
+ WdfIoQueueStart(context->reportQueue);
+ context->active = true;
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS DeactivateDevice(_Inout_ HIDDeviceContext * context)
+{
+ CSRWExclusiveLock lock(&context->reportLock);
+ if (context->stopping)
+ return STATUS_DEVICE_NOT_READY;
+
+ context->active = false;
+ context->reportHead = 0;
+ context->reportCount = 0;
+ WdfIoQueuePurgeSynchronously(context->reportQueue);
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS CreateQueues(_In_ WDFDEVICE device)
+{
+ WDF_IO_QUEUE_CONFIG queueConfig;
+ WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(
+ &queueConfig, WdfIoQueueDispatchParallel);
+ queueConfig.EvtIoDeviceControl = HIDEvtIoDeviceControl;
+
+ NTSTATUS status = WdfIoQueueCreate(
+ device, &queueConfig, WDF_NO_OBJECT_ATTRIBUTES, WDF_NO_HANDLE);
+ if (!NT_SUCCESS(status))
+ return status;
+
+ WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, WdfIoQueueDispatchManual);
+
+ WDF_OBJECT_ATTRIBUTES queueAttributes;
+ WDF_OBJECT_ATTRIBUTES_INIT(&queueAttributes);
+ queueAttributes.EvtCleanupCallback = HIDEvtReportQueueCleanup;
+ return WdfIoQueueCreate(
+ device,
+ &queueConfig,
+ &queueAttributes,
+ &HIDGetDeviceContext(device)->reportQueue);
+}
+
+NTSTATUS CHIDDevice::Create(_Inout_ PWDFDEVICE_INIT deviceInit)
+{
+ WdfFdoInitSetFilter(deviceInit);
+
+ WDF_OBJECT_ATTRIBUTES attributes;
+ WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, HIDDeviceContext);
+
+ WDFDEVICE device;
+ NTSTATUS status = WdfDeviceCreate(&deviceInit, &attributes, &device);
+ if (!NT_SUCCESS(status))
+ return status;
+
+ HIDDeviceContext * context = HIDGetDeviceContext(device);
+ RtlZeroMemory(context, sizeof(*context));
+ InitializeSRWLock(&context->reportLock);
+ context->active = true;
+
+ context->attributes.Size =
+ static_cast(sizeof(context->attributes));
+ context->attributes.VendorID = LG_INPUT_VENDOR_ID;
+ context->attributes.ProductID = LG_INPUT_PRODUCT_ID;
+ context->attributes.VersionNumber = LG_INPUT_VERSION;
+
+ context->descriptor.bLength =
+ static_cast(sizeof(context->descriptor));
+ context->descriptor.bDescriptorType = HID_HID_DESCRIPTOR_TYPE;
+ context->descriptor.bcdHID = 0x0111;
+ context->descriptor.bCountry = 0;
+ context->descriptor.bNumDescriptors = 1;
+ auto & reportDescriptor = context->descriptor.DescriptorList[0];
+ reportDescriptor.bReportType = HID_REPORT_DESCRIPTOR_TYPE;
+ reportDescriptor.wReportLength =
+ static_cast(HIDGetReportDescriptorSize());
+
+ status = CreateQueues(device);
+ if (!NT_SUCCESS(status))
+ return status;
+
+ {
+ CSRWExclusiveLock lock(&s_deviceLock);
+ s_device = context;
+ }
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS CHIDDevice::SubmitReport(
+ _In_reads_bytes_(size) const void * report,
+ _In_ size_t size)
+{
+ if (!report || !size)
+ return STATUS_INVALID_PARAMETER;
+
+ const UCHAR reportId = *static_cast(report);
+ if (HIDGetInputReportSize(reportId) != size)
+ return STATUS_INVALID_PARAMETER;
+
+ CSRWSharedLock deviceLock(&s_deviceLock);
+ HIDDeviceContext * context = s_device;
+ if (!context)
+ return STATUS_DEVICE_NOT_READY;
+
+ WDFREQUEST request = nullptr;
+ NTSTATUS status;
+ {
+ CSRWExclusiveLock reportLock(&context->reportLock);
+ if (context->stopping || !context->active)
+ status = STATUS_DEVICE_NOT_READY;
+ else
+ {
+ status = WdfIoQueueRetrieveNextRequest(context->reportQueue, &request);
+ if (status == STATUS_NO_MORE_ENTRIES)
+ {
+ request = nullptr;
+ status = QueueReport(context, report, size);
+ }
+ }
+ }
+
+ if (request)
+ {
+ status = CopyToRequest(request, report, size);
+ WdfRequestComplete(request, status);
+ }
+
+ return status;
+}
+
+VOID HIDEvtIoDeviceControl(
+ _In_ WDFQUEUE queue,
+ _In_ WDFREQUEST request,
+ _In_ size_t outputBufferLength,
+ _In_ size_t inputBufferLength,
+ _In_ ULONG ioControlCode)
+{
+ UNREFERENCED_PARAMETER(outputBufferLength);
+ UNREFERENCED_PARAMETER(inputBufferLength);
+
+ HIDDeviceContext * context =
+ HIDGetDeviceContext(WdfIoQueueGetDevice(queue));
+ NTSTATUS status;
+ bool complete = true;
+
+ switch (ioControlCode)
+ {
+ case IOCTL_HID_GET_DEVICE_DESCRIPTOR:
+ status = CopyToRequest(
+ request, &context->descriptor, context->descriptor.bLength);
+ break;
+
+ case IOCTL_HID_GET_DEVICE_ATTRIBUTES:
+ status = CopyToRequest(
+ request, &context->attributes, sizeof(context->attributes));
+ break;
+
+ case IOCTL_HID_GET_REPORT_DESCRIPTOR:
+ status = CopyToRequest(
+ request, HIDGetReportDescriptor(), HIDGetReportDescriptorSize());
+ break;
+
+ case IOCTL_HID_READ_REPORT:
+ status = ReadReport(context, request, &complete);
+ break;
+
+ case IOCTL_HID_WRITE_REPORT:
+ case IOCTL_UMDF_HID_SET_OUTPUT_REPORT:
+ status = SetOutputReport(queue, request);
+ break;
+
+ case IOCTL_HID_ACTIVATE_DEVICE:
+ status = ActivateDevice(context);
+ break;
+
+ case IOCTL_HID_DEACTIVATE_DEVICE:
+ status = DeactivateDevice(context);
+ break;
+
+ default:
+ status = STATUS_NOT_SUPPORTED;
+ break;
+ }
+
+ if (complete)
+ WdfRequestComplete(request, status);
+}
+
+VOID HIDEvtReportQueueCleanup(_In_ WDFOBJECT object)
+{
+ HIDDeviceContext * context =
+ HIDGetDeviceContext(WdfIoQueueGetDevice((WDFQUEUE)object));
+
+ CSRWExclusiveLock deviceLock(&s_deviceLock);
+ if (s_device == context)
+ s_device = nullptr;
+
+ {
+ CSRWExclusiveLock reportLock(&context->reportLock);
+ context->stopping = true;
+ context->reportHead = 0;
+ context->reportCount = 0;
+ }
+}
diff --git a/idd/LGInput/CHIDDevice.h b/idd/LGInput/CHIDDevice.h
new file mode 100644
index 00000000..86385a28
--- /dev/null
+++ b/idd/LGInput/CHIDDevice.h
@@ -0,0 +1,33 @@
+/**
+ * Looking Glass
+ * Copyright © 2017-2026 The Looking Glass Authors
+ * https://looking-glass.io
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#pragma once
+
+#include
+#include
+
+class CHIDDevice
+{
+public:
+ static NTSTATUS Create(_Inout_ PWDFDEVICE_INIT deviceInit);
+ static NTSTATUS SubmitReport(
+ _In_reads_bytes_(size) const void * report,
+ _In_ size_t size);
+};
diff --git a/idd/LGInput/Driver.cpp b/idd/LGInput/Driver.cpp
new file mode 100644
index 00000000..a8c624ab
--- /dev/null
+++ b/idd/LGInput/Driver.cpp
@@ -0,0 +1,95 @@
+/**
+ * Looking Glass
+ * Copyright © 2017-2026 The Looking Glass Authors
+ * https://looking-glass.io
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Driver.h"
+#include "Driver.tmh"
+
+#include "CDebug.h"
+#include "CHIDDevice.h"
+
+NTSTATUS DriverEntry(
+ _In_ PDRIVER_OBJECT DriverObject,
+ _In_ PUNICODE_STRING RegistryPath)
+{
+ g_debug.Init(L"looking-glass-input");
+ DEBUG_INFO("Looking Glass Input Driver");
+
+#if UMDF_VERSION_MAJOR == 2 && UMDF_VERSION_MINOR == 0
+ WPP_INIT_TRACING(MYDRIVER_TRACING_ID);
+#else
+ WPP_INIT_TRACING(DriverObject, RegistryPath);
+#endif
+
+ TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
+
+ WDF_DRIVER_CONFIG config;
+ WDF_DRIVER_CONFIG_INIT(&config, LGInputEvtDeviceAdd);
+
+ WDF_OBJECT_ATTRIBUTES attributes;
+ WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+ attributes.EvtCleanupCallback = LGInputEvtDriverContextCleanup;
+
+ const NTSTATUS status = WdfDriverCreate(
+ DriverObject, RegistryPath, &attributes, &config, WDF_NO_HANDLE);
+ if (!NT_SUCCESS(status))
+ {
+ TraceEvents(
+ TRACE_LEVEL_ERROR,
+ TRACE_DRIVER,
+ "WdfDriverCreate failed %!STATUS!",
+ status);
+#if UMDF_VERSION_MAJOR == 2 && UMDF_VERSION_MINOR == 0
+ WPP_CLEANUP();
+#else
+ WPP_CLEANUP(DriverObject);
+#endif
+ return status;
+ }
+
+ TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit");
+ return status;
+}
+
+NTSTATUS LGInputEvtDeviceAdd(
+ _In_ WDFDRIVER Driver,
+ _Inout_ PWDFDEVICE_INIT DeviceInit)
+{
+ UNREFERENCED_PARAMETER(Driver);
+
+ TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
+ const NTSTATUS status = CHIDDevice::Create(DeviceInit);
+ if (!NT_SUCCESS(status))
+ DEBUG_ERROR_HR(status, "Failed to create the HID input device");
+ TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit");
+ return status;
+}
+
+VOID LGInputEvtDriverContextCleanup(_In_ WDFOBJECT DriverObject)
+{
+ UNREFERENCED_PARAMETER(DriverObject);
+
+ TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
+
+#if UMDF_VERSION_MAJOR == 2 && UMDF_VERSION_MINOR == 0
+ WPP_CLEANUP();
+#else
+ WPP_CLEANUP(WdfDriverWdmGetDriverObject((WDFDRIVER)DriverObject));
+#endif
+}
diff --git a/idd/LGInput/Driver.h b/idd/LGInput/Driver.h
new file mode 100644
index 00000000..5099b136
--- /dev/null
+++ b/idd/LGInput/Driver.h
@@ -0,0 +1,33 @@
+/**
+ * Looking Glass
+ * Copyright © 2017-2026 The Looking Glass Authors
+ * https://looking-glass.io
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#pragma once
+
+#include
+#include
+
+#include "Trace.h"
+
+EXTERN_C_START
+DRIVER_INITIALIZE DriverEntry;
+EXTERN_C_END
+
+EVT_WDF_DRIVER_DEVICE_ADD LGInputEvtDeviceAdd;
+EVT_WDF_OBJECT_CONTEXT_CLEANUP LGInputEvtDriverContextCleanup;
diff --git a/idd/LGInput/HIDReports.cpp b/idd/LGInput/HIDReports.cpp
new file mode 100644
index 00000000..77507061
--- /dev/null
+++ b/idd/LGInput/HIDReports.cpp
@@ -0,0 +1,151 @@
+/**
+ * Looking Glass
+ * Copyright © 2017-2026 The Looking Glass Authors
+ * https://looking-glass.io
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "HIDReports.h"
+
+static const uint8_t REPORT_DESCRIPTOR[] =
+{
+ // Absolute tablet, report ID 1.
+ 0x05, 0x01, // Usage Page (Generic Desktop)
+ 0x09, 0x02, // Usage (Mouse)
+ 0xA1, 0x01, // Collection (Application)
+ 0x85, HID_REPORT_ID_TABLET,
+ 0x09, 0x01, // Usage (Pointer)
+ 0xA1, 0x00, // Collection (Physical)
+ 0x05, 0x09, // Usage Page (Button)
+ 0x19, 0x01, // Usage Minimum (Button 1)
+ 0x29, 0x05, // Usage Maximum (Button 5)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x75, 0x01, // Report Size (1)
+ 0x95, 0x05, // Report Count (5)
+ 0x81, 0x02, // Input (Data, Variable, Absolute)
+ 0x75, 0x03, // Report Size (3)
+ 0x95, 0x01, // Report Count (1)
+ 0x81, 0x03, // Input (Constant)
+ 0x05, 0x01, // Usage Page (Generic Desktop)
+ 0x09, 0x30, // Usage (X)
+ 0x09, 0x31, // Usage (Y)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xFF, 0x7F, // Logical Maximum (32767)
+ 0x75, 0x10, // Report Size (16)
+ 0x95, 0x02, // Report Count (2)
+ 0x81, 0x02, // Input (Data, Variable, Absolute)
+ 0xC0, // End Collection
+ 0xC0, // End Collection
+
+ // Relative mouse, report ID 2.
+ 0x05, 0x01, // Usage Page (Generic Desktop)
+ 0x09, 0x02, // Usage (Mouse)
+ 0xA1, 0x01, // Collection (Application)
+ 0x85, HID_REPORT_ID_MOUSE,
+ 0x09, 0x01, // Usage (Pointer)
+ 0xA1, 0x00, // Collection (Physical)
+ 0x05, 0x09, // Usage Page (Button)
+ 0x19, 0x01, // Usage Minimum (Button 1)
+ 0x29, 0x05, // Usage Maximum (Button 5)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x75, 0x01, // Report Size (1)
+ 0x95, 0x05, // Report Count (5)
+ 0x81, 0x02, // Input (Data, Variable, Absolute)
+ 0x75, 0x03, // Report Size (3)
+ 0x95, 0x01, // Report Count (1)
+ 0x81, 0x03, // Input (Constant)
+ 0x05, 0x01, // Usage Page (Generic Desktop)
+ 0x09, 0x30, // Usage (X)
+ 0x09, 0x31, // Usage (Y)
+ 0x16, 0x00, 0x80, // Logical Minimum (-32768)
+ 0x26, 0xFF, 0x7F, // Logical Maximum (32767)
+ 0x75, 0x10, // Report Size (16)
+ 0x95, 0x02, // Report Count (2)
+ 0x81, 0x06, // Input (Data, Variable, Relative)
+ 0x09, 0x38, // Usage (Wheel)
+ 0x15, 0x81, // Logical Minimum (-127)
+ 0x25, 0x7F, // Logical Maximum (127)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x01, // Report Count (1)
+ 0x81, 0x06, // Input (Data, Variable, Relative)
+ 0xC0, // End Collection
+ 0xC0, // End Collection
+
+ // Keyboard, report ID 3.
+ 0x05, 0x01, // Usage Page (Generic Desktop)
+ 0x09, 0x06, // Usage (Keyboard)
+ 0xA1, 0x01, // Collection (Application)
+ 0x85, HID_REPORT_ID_KEYBOARD,
+ 0x05, 0x07, // Usage Page (Keyboard)
+ 0x19, 0xE0, // Usage Minimum (Left Control)
+ 0x29, 0xE7, // Usage Maximum (Right GUI)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x75, 0x01, // Report Size (1)
+ 0x95, 0x08, // Report Count (8)
+ 0x81, 0x02, // Input (Data, Variable, Absolute)
+ 0x95, 0x01, // Report Count (1)
+ 0x75, 0x08, // Report Size (8)
+ 0x81, 0x03, // Input (Constant)
+ 0x05, 0x08, // Usage Page (LEDs)
+ 0x19, 0x01, // Usage Minimum (Num Lock)
+ 0x29, 0x05, // Usage Maximum (Kana)
+ 0x95, 0x05, // Report Count (5)
+ 0x75, 0x01, // Report Size (1)
+ 0x91, 0x02, // Output (Data, Variable, Absolute)
+ 0x95, 0x01, // Report Count (1)
+ 0x75, 0x03, // Report Size (3)
+ 0x91, 0x03, // Output (Constant)
+ 0x05, 0x07, // Usage Page (Keyboard)
+ 0x19, 0x00, // Usage Minimum (0)
+ 0x2A, 0xE7, 0x00, // Usage Maximum (231)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xE7, 0x00, // Logical Maximum (231)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x06, // Report Count (6)
+ 0x81, 0x00, // Input (Data, Array, Absolute)
+ 0xC0, // End Collection
+};
+
+const uint8_t * HIDGetReportDescriptor()
+{
+ return REPORT_DESCRIPTOR;
+}
+
+size_t HIDGetReportDescriptorSize()
+{
+ return sizeof(REPORT_DESCRIPTOR);
+}
+
+size_t HIDGetInputReportSize(uint8_t reportId)
+{
+ switch (reportId)
+ {
+ case HID_REPORT_ID_TABLET:
+ return sizeof(HIDTabletReport);
+
+ case HID_REPORT_ID_MOUSE:
+ return sizeof(HIDMouseReport);
+
+ case HID_REPORT_ID_KEYBOARD:
+ return sizeof(HIDKeyboardReport);
+
+ default:
+ return 0;
+ }
+}
diff --git a/idd/LGInput/HIDReports.h b/idd/LGInput/HIDReports.h
new file mode 100644
index 00000000..0282ade2
--- /dev/null
+++ b/idd/LGInput/HIDReports.h
@@ -0,0 +1,75 @@
+/**
+ * Looking Glass
+ * Copyright © 2017-2026 The Looking Glass Authors
+ * https://looking-glass.io
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#pragma once
+
+#include
+#include
+
+enum HIDReportId : uint8_t
+{
+ HID_REPORT_ID_TABLET = 1,
+ HID_REPORT_ID_MOUSE = 2,
+ HID_REPORT_ID_KEYBOARD = 3,
+};
+
+#pragma pack(push, 1)
+
+struct HIDTabletReport
+{
+ uint8_t reportId;
+ uint8_t buttons;
+ uint16_t x;
+ uint16_t y;
+};
+
+struct HIDMouseReport
+{
+ uint8_t reportId;
+ uint8_t buttons;
+ int16_t x;
+ int16_t y;
+ int8_t wheel;
+};
+
+struct HIDKeyboardReport
+{
+ uint8_t reportId;
+ uint8_t modifiers;
+ uint8_t reserved;
+ uint8_t keys[6];
+};
+
+struct HIDKeyboardLedsReport
+{
+ uint8_t reportId;
+ uint8_t leds;
+};
+
+#pragma pack(pop)
+
+static_assert(sizeof(HIDTabletReport) == 6);
+static_assert(sizeof(HIDMouseReport) == 7);
+static_assert(sizeof(HIDKeyboardReport) == 9);
+static_assert(sizeof(HIDKeyboardLedsReport) == 2);
+
+const uint8_t * HIDGetReportDescriptor();
+size_t HIDGetReportDescriptorSize();
+size_t HIDGetInputReportSize(uint8_t reportId);
diff --git a/idd/LGInput/LGInput.inf b/idd/LGInput/LGInput.inf
new file mode 100644
index 00000000..14c733b4
--- /dev/null
+++ b/idd/LGInput/LGInput.inf
@@ -0,0 +1,75 @@
+;
+; LGInput.inf
+;
+
+[Version]
+Signature="$Windows NT$"
+Class=HIDClass
+ClassGuid={745A17A0-74D3-11D0-B6FE-00A0C90F57DA}
+Provider=%ManufacturerName%
+CatalogFile=LGInput.cat
+DriverVer=0.1
+PnpLockdown=1
+
+[Manufacturer]
+%ManufacturerName%=Standard,NT$ARCH$.10.0
+
+[Standard.NT$ARCH$.10.0]
+%DeviceName%=LGInput_Install,Root\LGInput
+
+[LGInput_Install.NT]
+CopyFiles=UMDriverCopy
+
+[LGInput_Install.NT.hw]
+AddReg=LGInput_HardwareSettings
+
+[LGInput_HardwareSettings]
+HKR,,"LowerFilters",0x00010008,"WUDFRd"
+
+[LGInput_Install.NT.Services]
+AddService=WUDFRd,0x000001f8,WUDFRD_ServiceInstall
+AddService=mshidumdf,0x000001fa,mshidumdf_ServiceInstall
+
+[mshidumdf_ServiceInstall]
+ServiceType=1
+StartType=3
+ErrorControl=1
+ServiceBinary=%12%\mshidumdf.sys
+
+[WUDFRD_ServiceInstall]
+DisplayName=%WudfRdDisplayName%
+ServiceType=1
+StartType=3
+ErrorControl=1
+ServiceBinary=%12%\WUDFRd.sys
+
+[LGInput_Install.NT.Wdf]
+UmdfService=LGInput,LGInput_ServiceInstall
+UmdfServiceOrder=LGInput
+UmdfKernelModeClientPolicy=AllowKernelModeClients
+UmdfFileObjectPolicy=AllowNullAndUnknownFileObjects
+UmdfMethodNeitherAction=Copy
+UmdfFsContextUsePolicy=CanUseFsContext2
+UmdfHostProcessSharing=ProcessSharingDisabled
+
+[LGInput_ServiceInstall]
+UmdfLibraryVersion=$UMDFVERSION$
+ServiceBinary=%12%\UMDF\LGInput.dll
+
+[DestinationDirs]
+UMDriverCopy=12,UMDF
+
+[UMDriverCopy]
+LGInput.dll
+
+[SourceDisksNames]
+1=%DiskName%
+
+[SourceDisksFiles]
+LGInput.dll=1
+
+[Strings]
+ManufacturerName="Looking Glass"
+DiskName="LGInput Installation Disk"
+DeviceName="Looking Glass"
+WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector"
diff --git a/idd/LGInput/LGInput.vcxproj b/idd/LGInput/LGInput.vcxproj
new file mode 100644
index 00000000..1923159e
--- /dev/null
+++ b/idd/LGInput/LGInput.vcxproj
@@ -0,0 +1,104 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+
+ Common\CDebug.cpp
+
+
+
+
+
+
+
+ Common\CDebug.h
+
+
+ Common\CSRWLock.h
+
+
+
+
+
+
+
+
+
+
+ {2477B25B-CB62-4AD9-A260-CE5F00D77EEB}
+ LGInput
+ 10.0.26100.0
+ 2
+ Debug
+ Win32
+
+
+
+ Windows10
+ Universal
+ UMDF
+ WindowsUserModeDriver10.0
+ DynamicLibrary
+ 25
+ 25
+ <_NT_TARGET_VERSION>0xA000005
+ Spectre
+
+
+ true
+
+
+ false
+
+
+
+
+
+
+
+
+ LGInput
+ DbgengRemoteDebugger
+ true
+
+
+
+ true
+ true
+ Trace.h
+ /EHsc /D_ATL_NO_WIN_SUPPORT %(AdditionalOptions)
+ $(ProjectDir)..\LGCommon;$(DDK_INC_PATH);%(AdditionalIncludeDirectories)
+
+
+ %(AdditionalDependencies);OneCoreUAP.lib
+
+
+ SHA1
+
+
+
+
+
+
+
+
diff --git a/idd/LGInput/LGInput.vcxproj.filters b/idd/LGInput/LGInput.vcxproj.filters
new file mode 100644
index 00000000..d47340fd
--- /dev/null
+++ b/idd/LGInput/LGInput.vcxproj.filters
@@ -0,0 +1,54 @@
+
+
+
+
+ {B48E885A-BB8C-4DD5-8145-587B360AAB3B}
+
+
+ {D9688549-A0BE-46E6-8FAB-AE454D90E0FA}
+
+
+ {938E49D6-F954-4EBE-80AB-E0F67E677F27}
+
+
+
+
+ Driver
+
+
+
+
+ Common
+
+
+ Common
+
+
+ Driver
+
+
+ Driver
+
+
+ HID
+
+
+ Driver
+
+
+
+
+ Common
+
+
+ Driver
+
+
+ Driver
+
+
+ HID
+
+
+
diff --git a/idd/LGInput/Trace.h b/idd/LGInput/Trace.h
new file mode 100644
index 00000000..f4354ceb
--- /dev/null
+++ b/idd/LGInput/Trace.h
@@ -0,0 +1,50 @@
+/**
+ * Looking Glass
+ * Copyright © 2017-2026 The Looking Glass Authors
+ * https://looking-glass.io
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#pragma once
+
+#define WPP_CONTROL_GUIDS \
+ WPP_DEFINE_CONTROL_GUID( \
+ LGInputTraceGuid, (1d279434,7f85,42cd,b4b9,9a3d6f5cb774), \
+ \
+ WPP_DEFINE_BIT(MYDRIVER_ALL_INFO) \
+ WPP_DEFINE_BIT(TRACE_DRIVER))
+
+#define WPP_FLAG_LEVEL_LOGGER(flag, level) \
+ WPP_LEVEL_LOGGER(flag)
+
+#define WPP_FLAG_LEVEL_ENABLED(flag, level) \
+ (WPP_LEVEL_ENABLED(flag) && \
+ WPP_CONTROL(WPP_BIT_ ## flag).Level >= level)
+
+#define WPP_LEVEL_FLAGS_LOGGER(lvl, flags) \
+ WPP_LEVEL_LOGGER(flags)
+
+#define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) \
+ (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl)
+
+// begin_wpp config
+// FUNC Trace{FLAG=MYDRIVER_ALL_INFO}(LEVEL, MSG, ...);
+// FUNC TraceEvents(LEVEL, FLAGS, MSG, ...);
+// end_wpp
+
+#if UMDF_VERSION_MAJOR == 2 && UMDF_VERSION_MINOR == 0
+ #define MYDRIVER_TRACING_ID L"Microsoft\\UMDF2.0\\LGInput V1.0"
+#endif
diff --git a/idd/installer.nsi b/idd/installer.nsi
index ddb81f1a..8c6bd5dc 100644
--- a/idd/installer.nsi
+++ b/idd/installer.nsi
@@ -59,7 +59,7 @@ InstallDir "$PROGRAMFILES64\Looking Glass (IDD)"
;Install and uninstall pages
!insertmacro MUI_PAGE_WELCOME
-!insertmacro MUI_PAGE_LICENSE "LICENSE.txt"
+!insertmacro MUI_PAGE_LICENSE "LICENSE"
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_INSTFILES
@@ -172,13 +172,16 @@ Section "!Indirect Display Driver (IDD)" Section1
DetailPrint "Extracting IDD"
SetOutPath $INSTDIR
File lgidd.cat
+ File /oname=LGInput.cat "LGInput\LGInput.cat"
File LGIdd.dll
+ File /oname=LGInput.dll "LGInput\LGInput.dll"
File LGIdd.inf
+ File /oname=LGInput.inf "LGInput\LGInput.inf"
File LGIddHelper.exe
File LGIddInstall.exe
DetailPrint "Extracting support files"
- File LICENSE.txt
+ File /oname=LICENSE.txt LICENSE
WriteUninstaller $INSTDIR\uninstaller.exe
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
@@ -232,8 +235,11 @@ Section "Uninstall" Section6
Delete "$INSTDIR\ivshmem.sys"
Delete "$INSTDIR\ivshmem.pdb"
Delete "$INSTDIR\lgidd.cat"
+ Delete "$INSTDIR\LGInput.cat"
Delete "$INSTDIR\LGIdd.dll"
+ Delete "$INSTDIR\LGInput.dll"
Delete "$INSTDIR\LGIdd.inf"
+ Delete "$INSTDIR\LGInput.inf"
Delete "$INSTDIR\LGIddHelper.exe"
Delete "$INSTDIR\LGIddInstall.exe"
Delete "$INSTDIR\LICENSE.txt"