├── .gitattributes ├── .gitignore ├── Pad2Screen.sln ├── Pad2Screen ├── Driver.c ├── Driver.h ├── Pad2Screen.inf ├── Pad2Screen.vcxproj ├── Pad2Screen.vcxproj.filters └── Trace.h └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /Pad2Screen.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28010.2041 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Pad2Screen", "Pad2Screen\Pad2Screen.vcxproj", "{B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|ARM = Debug|ARM 11 | Debug|ARM64 = Debug|ARM64 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|ARM = Release|ARM 15 | Release|ARM64 = Release|ARM64 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Debug|ARM.ActiveCfg = Debug|ARM 21 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Debug|ARM.Build.0 = Debug|ARM 22 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Debug|ARM.Deploy.0 = Debug|ARM 23 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Debug|ARM64.ActiveCfg = Debug|ARM64 24 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Debug|ARM64.Build.0 = Debug|ARM64 25 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Debug|ARM64.Deploy.0 = Debug|ARM64 26 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Debug|x64.ActiveCfg = Debug|x64 27 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Debug|x64.Build.0 = Debug|x64 28 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Debug|x64.Deploy.0 = Debug|x64 29 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Debug|x86.ActiveCfg = Debug|Win32 30 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Debug|x86.Build.0 = Debug|Win32 31 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Debug|x86.Deploy.0 = Debug|Win32 32 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Release|ARM.ActiveCfg = Release|ARM 33 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Release|ARM.Build.0 = Release|ARM 34 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Release|ARM.Deploy.0 = Release|ARM 35 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Release|ARM64.ActiveCfg = Release|ARM64 36 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Release|ARM64.Build.0 = Release|ARM64 37 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Release|ARM64.Deploy.0 = Release|ARM64 38 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Release|x64.ActiveCfg = Release|x64 39 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Release|x64.Build.0 = Release|x64 40 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Release|x64.Deploy.0 = Release|x64 41 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Release|x86.ActiveCfg = Release|Win32 42 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Release|x86.Build.0 = Release|Win32 43 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB}.Release|x86.Deploy.0 = Release|Win32 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(ExtensibilityGlobals) = postSolution 49 | SolutionGuid = {CB1BCBA3-CF19-44B4-AF98-1FB110FE26AE} 50 | EndGlobalSection 51 | EndGlobal 52 | -------------------------------------------------------------------------------- /Pad2Screen/Driver.c: -------------------------------------------------------------------------------- 1 | #include "driver.h" 2 | #include "driver.tmh" 3 | #include 4 | 5 | #ifdef ALLOC_PRAGMA 6 | #pragma alloc_text (PAGE, P2S_GetHidReportDescriptorInputMode) 7 | #pragma alloc_text (PAGE, P2S_PatchHidReportDescriptor) 8 | #pragma alloc_text (PAGE, P2S_IoctlHidSetFeatureCompletionRoutine) 9 | #pragma alloc_text (PAGE, P2S_SetToPrecisionTouchpadMode) 10 | #pragma alloc_text (PAGE, P2S_ForwardIoctlCompletionRoutine) 11 | #pragma alloc_text (PAGE, P2S_EvtIoDeviceControl) 12 | #pragma alloc_text (PAGE, P2S_EvtDriverContextCleanup) 13 | #pragma alloc_text (PAGE, P2S_EvtDeviceAdd) 14 | #pragma alloc_text (INIT, DriverEntry) 15 | #endif 16 | 17 | // HID report descriptor constants 18 | #define HID_TYPE_COLLECTION 0xA1 19 | #define HID_TYPE_END_COLLECTION 0xC0 20 | #define HID_TYPE_USAGE_PAGE 0x05 21 | #define HID_TYPE_USAGE 0x09 22 | #define HID_TYPE_REPORT_ID 0x85 23 | #define HID_TYPE_REPORT_SIZE 0x75 24 | #define HID_TYPE_REPORT_COUNT 0x95 25 | #define HID_TYPE_FEATURE 0xB1 26 | #define HID_USAGE_PAGE_DIGITIZERS 0x0D 27 | #define HID_USAGE_TOUCHPAD 0x05 28 | #define HID_USAGE_TOUCHSCREEN 0x04 29 | #define HID_USAGE_CONFIGURATION 0x0E 30 | #define HID_USAGE_INPUT_MODE 0x52 31 | 32 | bool 33 | P2S_GetHidReportDescriptorInputMode( 34 | _In_ byte *descriptor, 35 | _In_ size_t descriptorLen, 36 | _Out_ byte *inputModeReportId, 37 | _Out_ size_t *inputModeReportSize) 38 | { 39 | int depth = 0; 40 | byte usagePage = 0; 41 | byte reportId = 0; 42 | byte reportSize = 0; 43 | byte reportCount = 0; 44 | byte lastUsage = 0; 45 | bool inConfigTlc = false; 46 | 47 | for (size_t i = 0; i < descriptorLen;) { 48 | byte type = descriptor[i++]; 49 | int size = type & 3; 50 | if (size == 3) { 51 | size++; 52 | } 53 | byte *value = &descriptor[i]; 54 | i += size; 55 | 56 | // WARNING: The following code makes a ton of assumptions 57 | // to avoid having to parse the HID report descriptor. 58 | // It assumes that there will only be a single field in 59 | // the "input mode" usage. This is b/c Microsoft specifies 60 | // the field should contain either 0 or 3, and there aren't 61 | // many ways to achieve that. It also doesn't support 62 | // push/pop/pretty much any other aspect of HID. 63 | if (type == HID_TYPE_COLLECTION) { 64 | depth++; 65 | if (depth == 1 && usagePage == HID_USAGE_PAGE_DIGITIZERS && lastUsage == HID_USAGE_CONFIGURATION) { 66 | inConfigTlc = true; 67 | } 68 | } else if (type == HID_TYPE_END_COLLECTION) { 69 | depth--; 70 | } else if (type == HID_TYPE_USAGE_PAGE) { 71 | usagePage = *value; 72 | } else if (type == HID_TYPE_USAGE) { 73 | lastUsage = *value; 74 | } else if (type == HID_TYPE_REPORT_ID) { 75 | reportId = *value; 76 | } else if (type == HID_TYPE_REPORT_SIZE) { 77 | reportSize = *value; 78 | } else if (type == HID_TYPE_REPORT_COUNT) { 79 | reportCount = *value; 80 | } else if (inConfigTlc && type == HID_TYPE_FEATURE && lastUsage == HID_USAGE_INPUT_MODE) { 81 | *inputModeReportSize = (reportSize + 7) / 8; 82 | *inputModeReportId = reportId; 83 | return true; 84 | } 85 | } 86 | 87 | return false; 88 | } 89 | 90 | bool 91 | P2S_PatchHidReportDescriptor( 92 | _Inout_ byte *descriptor, 93 | _In_ size_t descriptorLen) 94 | { 95 | // Warning: the following fields are required in touchscreen drivers 96 | // but are not required in touchpad drivers. Currently the "required" 97 | // fields are actually optional, but that might change in the future. 98 | // 99 | // In-range (page 0x0D, usage 0x32) 100 | // Contact count maximum (page 0x0D, usage 0x55) 101 | // 102 | // Touchscreen spec: 103 | // https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/supporting-usages-in-multitouch-digitizer-drivers 104 | // 105 | // Touchpad spec: 106 | // https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-precision-touchpad-required-hid-top-level-collections 107 | 108 | // Below is a really dumb HID "parser" that only recognizes page/usage 109 | // and skips everything else. 110 | int depth = 0; 111 | byte usagePage = 0; 112 | bool patched = false; 113 | for (size_t i = 0; i < descriptorLen;) { 114 | byte type = descriptor[i++]; 115 | int size = type & 3; 116 | if (size == 3) { 117 | size++; 118 | } 119 | byte *value = &descriptor[i]; 120 | i += size; 121 | 122 | if (type == HID_TYPE_COLLECTION) { 123 | depth++; 124 | } else if (type == HID_TYPE_END_COLLECTION) { 125 | depth--; 126 | } else if (type == HID_TYPE_USAGE_PAGE) { 127 | usagePage = *value; 128 | } else if (depth == 0 && type == HID_TYPE_USAGE) { 129 | if (usagePage == HID_USAGE_PAGE_DIGITIZERS && *value == HID_USAGE_TOUCHPAD) { 130 | *value = HID_USAGE_TOUCHSCREEN; 131 | patched = true; 132 | } 133 | } 134 | } 135 | 136 | return patched; 137 | } 138 | 139 | const char * 140 | P2S_IoctlCodeToString( 141 | _In_ unsigned long ioControlCode) 142 | { 143 | switch (ioControlCode) { 144 | case IOCTL_HID_GET_DEVICE_DESCRIPTOR: 145 | return "IOCTL_HID_GET_DEVICE_DESCRIPTOR"; 146 | case IOCTL_HID_GET_DEVICE_ATTRIBUTES: 147 | return "IOCTL_HID_GET_DEVICE_ATTRIBUTES"; 148 | case IOCTL_HID_GET_REPORT_DESCRIPTOR: 149 | return "IOCTL_HID_GET_REPORT_DESCRIPTOR"; 150 | case IOCTL_HID_READ_REPORT: 151 | return "IOCTL_HID_READ_REPORT"; 152 | case IOCTL_HID_WRITE_REPORT: 153 | return "IOCTL_HID_WRITE_REPORT"; 154 | case IOCTL_HID_GET_FEATURE: 155 | return "IOCTL_HID_GET_FEATURE"; 156 | case IOCTL_HID_SET_FEATURE: 157 | return "IOCTL_HID_SET_FEATURE"; 158 | case IOCTL_HID_GET_INPUT_REPORT: 159 | return "IOCTL_HID_GET_INPUT_REPORT"; 160 | case IOCTL_HID_SET_OUTPUT_REPORT: 161 | return "IOCTL_HID_SET_OUTPUT_REPORT"; 162 | case IOCTL_UMDF_HID_GET_FEATURE: 163 | return "IOCTL_UMDF_HID_GET_FEATURE"; 164 | case IOCTL_UMDF_HID_SET_FEATURE: 165 | return "IOCTL_UMDF_HID_SET_FEATURE"; 166 | case IOCTL_UMDF_HID_GET_INPUT_REPORT: 167 | return "IOCTL_UMDF_HID_GET_INPUT_REPORT"; 168 | case IOCTL_UMDF_HID_SET_OUTPUT_REPORT: 169 | return "IOCTL_UMDF_HID_SET_OUTPUT_REPORT"; 170 | case IOCTL_HID_GET_STRING: 171 | return "IOCTL_HID_GET_STRING"; 172 | case IOCTL_HID_GET_INDEXED_STRING: 173 | return "IOCTL_HID_GET_INDEXED_STRING"; 174 | case IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST: 175 | return "IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST"; 176 | case IOCTL_HID_ACTIVATE_DEVICE: 177 | return "IOCTL_HID_ACTIVATE_DEVICE"; 178 | case IOCTL_HID_DEACTIVATE_DEVICE: 179 | return "IOCTL_HID_DEACTIVATE_DEVICE"; 180 | case IOCTL_GET_PHYSICAL_DESCRIPTOR: 181 | return "IOCTL_GET_PHYSICAL_DESCRIPTOR"; 182 | default: 183 | return ""; 184 | } 185 | } 186 | 187 | void 188 | P2S_IoctlHidSetFeatureCompletionRoutine( 189 | _In_ WDFREQUEST request, 190 | _In_ WDFIOTARGET target, 191 | _In_ PWDF_REQUEST_COMPLETION_PARAMS completionParams, 192 | _In_ WDFCONTEXT context) 193 | { 194 | NTSTATUS status = STATUS_SUCCESS; 195 | 196 | UNREFERENCED_PARAMETER(target); 197 | UNREFERENCED_PARAMETER(completionParams); 198 | UNREFERENCED_PARAMETER(context); 199 | PAGED_CODE(); 200 | TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "Enter %!FUNC!()"); 201 | 202 | // Log any errors that occurred 203 | status = WdfRequestGetStatus(request); 204 | if (!NT_SUCCESS(status)) { 205 | TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "ioctl(IOCTL_HID_SET_FEATURE) failed: %!STATUS!", status); 206 | } 207 | 208 | // Destroy the request since we're done with it now 209 | WdfObjectDelete(request); 210 | TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "Exit %!FUNC!() -> %!STATUS!", status); 211 | } 212 | 213 | NTSTATUS 214 | P2S_SetToPrecisionTouchpadMode( 215 | _In_ WDFIOTARGET target, 216 | _In_ byte *descriptor, 217 | _In_ size_t descriptorLen) 218 | { 219 | NTSTATUS status = STATUS_SUCCESS; 220 | WDFREQUEST request = NULL; 221 | WDFMEMORY inputMemory = NULL; 222 | byte reportId; 223 | size_t reportSize; 224 | void *inputBuffer; 225 | byte *report; 226 | 227 | TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "Enter %!FUNC!()"); 228 | 229 | // As per the precision touchpad spec, touchpads shall start 230 | // in mouse emulation mode and be set to precision mode via 231 | // a HID feature report. Since Windows thinks we're a touchscreen, 232 | // we should manually send this report to the touchpad driver. 233 | 234 | // Determine report ID and the size of the feature field 235 | if (!P2S_GetHidReportDescriptorInputMode(descriptor, descriptorLen, &reportId, &reportSize)) { 236 | TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "P2S_GetHidReportDescriptorInputMode() did not find input mode"); 237 | goto exit; 238 | } 239 | 240 | // Create ioctl request 241 | status = WdfRequestCreate(NULL, target, &request); 242 | if (!NT_SUCCESS(status)) { 243 | TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "WdfRequestCreate() failed: %!STATUS!", status); 244 | goto exit; 245 | } 246 | 247 | // Allocate buffer for request 248 | status = WdfMemoryCreate(NULL, PagedPool, 0, reportSize + 1, &inputMemory, &inputBuffer); 249 | if (!NT_SUCCESS(status)) { 250 | TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "WdfMemoryCreate() failed: %!STATUS!", status); 251 | goto exit; 252 | } 253 | 254 | // Fill out the buffer. First byte is the report ID, remaining bytes 255 | // combine to form the request. We assumed that the report contains 256 | // only a single field (the input mode), and that it's the first field 257 | // before any padding. 258 | report = inputBuffer; 259 | report[0] = reportId; 260 | report[1] = 3; 261 | for (size_t i = 0; i < reportSize; ++i) { 262 | report[i] = 0; 263 | } 264 | 265 | // Assign buffer to request 266 | status = WdfIoTargetFormatRequestForIoctl(target, request, IOCTL_HID_SET_FEATURE, inputMemory, NULL, NULL, NULL); 267 | if (!NT_SUCCESS(status)) { 268 | TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "WdfIoTargetFormatRequestForIoctl() failed: %!STATUS!", status); 269 | goto exit; 270 | } 271 | 272 | // Memory is owned by the request now 273 | inputMemory = NULL; 274 | 275 | // Asynchronously send the ioctl request 276 | WdfRequestSetCompletionRoutine(request, P2S_IoctlHidSetFeatureCompletionRoutine, NULL); 277 | if (!WdfRequestSend(request, target, NULL)) { 278 | status = WdfRequestGetStatus(request); 279 | goto exit; 280 | } 281 | 282 | // Request will be cleaned up in the completion routine 283 | request = NULL; 284 | 285 | exit: 286 | if (request != NULL) { 287 | WdfObjectDelete(request); 288 | } 289 | if (inputMemory != NULL) { 290 | WdfObjectDelete(inputMemory); 291 | } 292 | TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "Exit %!FUNC!() -> %!STATUS!", status); 293 | return status; 294 | } 295 | 296 | void 297 | P2S_ForwardIoctlCompletionRoutine( 298 | _In_ WDFREQUEST request, 299 | _In_ WDFIOTARGET target, 300 | _In_ PWDF_REQUEST_COMPLETION_PARAMS completionParams, 301 | _In_ WDFCONTEXT context) 302 | { 303 | NTSTATUS status = STATUS_SUCCESS; 304 | WDFMEMORY outputBuffer = NULL; 305 | byte *buf; 306 | byte *descriptor; 307 | size_t descriptorLen; 308 | 309 | UNREFERENCED_PARAMETER(target); 310 | UNREFERENCED_PARAMETER(context); 311 | PAGED_CODE(); 312 | TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "Enter %!FUNC!(%s)", P2S_IoctlCodeToString(completionParams->Parameters.Ioctl.IoControlCode)); 313 | 314 | // If the real ioctl failed, don't try to patch 315 | status = completionParams->IoStatus.Status; 316 | if (!NT_SUCCESS(status)) { 317 | TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "Forwarded ioctl() failed: %!STATUS!", status); 318 | goto exit; 319 | } 320 | 321 | // Only patch requests for the HID report descriptor 322 | if (completionParams->Parameters.Ioctl.IoControlCode != IOCTL_HID_GET_REPORT_DESCRIPTOR) { 323 | goto exit; 324 | } 325 | 326 | // Get the buffer that the report descriptor 327 | // was copied to 328 | outputBuffer = completionParams->Parameters.Ioctl.Output.Buffer; 329 | buf = WdfMemoryGetBuffer(outputBuffer, NULL); 330 | 331 | // Descriptor starts at buf offset (or at least it should) 332 | // Information holds the length of the descriptor 333 | descriptor = buf + completionParams->Parameters.Ioctl.Output.Offset; 334 | descriptorLen = completionParams->IoStatus.Information; 335 | if (P2S_PatchHidReportDescriptor(descriptor, descriptorLen)) { 336 | // If the device is indeed a precision touchpad, change it 337 | // from mouse emulation mode to precision touchpad mode. 338 | // Ignore errors and pray that the device works even if the 339 | // ioctl fails. 340 | P2S_SetToPrecisionTouchpadMode(target, descriptor, descriptorLen); 341 | } else { 342 | TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "P2S_PatchHidReportDescriptor() did not find touchpad usage"); 343 | } 344 | 345 | exit: 346 | // Pass the results up to our caller 347 | WdfRequestCompleteWithInformation(request, status, completionParams->IoStatus.Information); 348 | TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "Exit %!FUNC!() -> %!STATUS!", status); 349 | } 350 | 351 | void 352 | P2S_EvtIoDeviceControl( 353 | _In_ WDFQUEUE queue, 354 | _In_ WDFREQUEST request, 355 | _In_ size_t outputBufferLength, 356 | _In_ size_t inputBufferLength, 357 | _In_ unsigned long ioControlCode) 358 | { 359 | NTSTATUS status = STATUS_SUCCESS; 360 | WDFDEVICE device = NULL; 361 | WDFIOTARGET target = NULL; 362 | WDFMEMORY inputMemory = NULL; 363 | WDFMEMORY outputMemory = NULL; 364 | 365 | UNREFERENCED_PARAMETER(outputBufferLength); 366 | UNREFERENCED_PARAMETER(inputBufferLength); 367 | UNREFERENCED_PARAMETER(ioControlCode); 368 | PAGED_CODE(); 369 | TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "Enter %!FUNC!(%s)", P2S_IoctlCodeToString(ioControlCode)); 370 | 371 | // Get a reference to the real driver that will 372 | // be handling the ioctl request. 373 | device = WdfIoQueueGetDevice(queue); 374 | target = WdfDeviceGetIoTarget(device); 375 | if (target == NULL) { 376 | TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "WdfDeviceGetIoTarget() == NULL"); 377 | status = STATUS_NOT_SUPPORTED; 378 | goto exit; 379 | } 380 | 381 | // Format request. Apparently WdfRequestFormatRequestUsingCurrentType() 382 | // is a big fat phony and doesn't actually work here. Following code is 383 | // taken from the kbfiltr sample driver. 384 | status = WdfRequestRetrieveInputMemory(request, &inputMemory); 385 | if (!NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL) { 386 | TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "WdfRequestRetrieveInputMemory() failed: %!STATUS!", status); 387 | goto exit; 388 | } 389 | status = WdfRequestRetrieveOutputMemory(request, &outputMemory); 390 | if (!NT_SUCCESS(status) && status != STATUS_BUFFER_TOO_SMALL) { 391 | TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "WdfRequestRetrieveOutputMemory() failed: %!STATUS!", status); 392 | goto exit; 393 | } 394 | status = WdfIoTargetFormatRequestForInternalIoctl(target, request, ioControlCode, inputMemory, NULL, outputMemory, NULL); 395 | if (!NT_SUCCESS(status)) { 396 | TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "WdfIoTargetFormatRequestForInternalIoctl() failed: %!STATUS!", status); 397 | goto exit; 398 | } 399 | 400 | // Forward the request to the real driver 401 | WdfRequestSetCompletionRoutine(request, P2S_ForwardIoctlCompletionRoutine, NULL); 402 | if (!WdfRequestSend(request, target, NULL)) { 403 | status = WdfRequestGetStatus(request); 404 | TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "WdfRequestSend() failed: %!STATUS!", status); 405 | goto exit; 406 | } 407 | 408 | status = STATUS_SUCCESS; 409 | 410 | exit: 411 | if (!NT_SUCCESS(status)) { 412 | WdfRequestComplete(request, status); 413 | } 414 | TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "Exit %!FUNC!() -> %!STATUS!", status); 415 | } 416 | 417 | NTSTATUS 418 | P2S_EvtDeviceAdd( 419 | _In_ WDFDRIVER driver, 420 | _Inout_ PWDFDEVICE_INIT deviceInit) 421 | { 422 | NTSTATUS status = STATUS_SUCCESS; 423 | WDFDEVICE device = NULL; 424 | WDF_OBJECT_ATTRIBUTES deviceAttributes; 425 | WDF_IO_QUEUE_CONFIG queueConfig; 426 | 427 | UNREFERENCED_PARAMETER(driver); 428 | PAGED_CODE(); 429 | TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "Enter %!FUNC!()"); 430 | 431 | // Set filter mode to get all non-ioctl requests to be 432 | // automatically forwarded to the real device driver 433 | WdfFdoInitSetFilter(deviceInit); 434 | 435 | // Create device object 436 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT); 437 | status = WdfDeviceCreate(&deviceInit, &deviceAttributes, &device); 438 | if (!NT_SUCCESS(status)) { 439 | TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "WdfDeviceCreate() failed: %!STATUS!", status); 440 | goto exit; 441 | } 442 | 443 | // Create I/O queue in parallel mode since we don't 444 | // have any global state to worry about 445 | WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchParallel); 446 | queueConfig.EvtIoInternalDeviceControl = P2S_EvtIoDeviceControl; 447 | status = WdfIoQueueCreate(device, &queueConfig, NULL, NULL); 448 | if (!NT_SUCCESS(status)) { 449 | TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "WdfIoQueueCreate() failed: %!STATUS!", status); 450 | goto exit; 451 | } 452 | 453 | exit: 454 | TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "Exit %!FUNC!() -> %!STATUS!", status); 455 | return status; 456 | } 457 | 458 | void 459 | P2S_EvtDriverContextCleanup( 460 | _In_ WDFOBJECT driverObject) 461 | { 462 | PAGED_CODE(); 463 | 464 | // stop tracing 465 | WPP_CLEANUP(WdfDriverWdmGetDriverObject((WDFDRIVER)driverObject)); 466 | } 467 | 468 | NTSTATUS 469 | DriverEntry( 470 | _In_ PDRIVER_OBJECT driverObject, 471 | _In_ PUNICODE_STRING registryPath) 472 | { 473 | NTSTATUS status = STATUS_SUCCESS; 474 | WDF_OBJECT_ATTRIBUTES attributes; 475 | WDF_DRIVER_CONFIG config; 476 | 477 | // Initialize tracing 478 | WPP_INIT_TRACING(driverObject, registryPath); 479 | 480 | TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "Enter %!FUNC!()"); 481 | 482 | // Set up callbacks 483 | WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 484 | attributes.EvtCleanupCallback = P2S_EvtDriverContextCleanup; 485 | WDF_DRIVER_CONFIG_INIT(&config, P2S_EvtDeviceAdd); 486 | 487 | // Create driver object 488 | status = WdfDriverCreate(driverObject, registryPath, &attributes, &config, NULL); 489 | if (!NT_SUCCESS(status)) { 490 | TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "WdfDriverCreate() failed: %!STATUS!", status); 491 | goto exit; 492 | } 493 | 494 | exit: 495 | // Stop tracing if driver failed to init 496 | if (!NT_SUCCESS(status)) { 497 | WPP_CLEANUP(driverObject); 498 | } 499 | TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "Exit %!FUNC!()"); 500 | return status; 501 | } 502 | -------------------------------------------------------------------------------- /Pad2Screen/Driver.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "trace.h" 6 | 7 | EXTERN_C_START 8 | 9 | typedef struct _DEVICE_CONTEXT { 10 | ULONG PrivateDeviceData; 11 | } DEVICE_CONTEXT, *PDEVICE_CONTEXT; 12 | 13 | WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT, DeviceGetContext) 14 | 15 | typedef unsigned char byte; 16 | typedef enum { false, true } bool; 17 | 18 | typedef bool 19 | P2S_GET_HID_REPORT_DESCRIPTOR_INPUT_MODE( 20 | _In_ byte *descriptor, 21 | _In_ size_t descriptorLen, 22 | _Out_ byte *inputModeReportId, 23 | _Out_ size_t *inputModeReportSize); 24 | 25 | typedef bool 26 | P2S_PATCH_HID_REPORT_DESCRIPTOR( 27 | _Inout_ byte *descriptor, 28 | _In_ size_t descriptorLen); 29 | 30 | typedef NTSTATUS 31 | P2S_SET_TO_PRECISION_TOUCHPAD_MODE( 32 | _In_ WDFIOTARGET target, 33 | _In_ byte *descriptor, 34 | _In_ size_t descriptorLen); 35 | 36 | P2S_GET_HID_REPORT_DESCRIPTOR_INPUT_MODE P2S_GetHidReportDescriptorInputMode; 37 | P2S_PATCH_HID_REPORT_DESCRIPTOR P2S_PatchHidReportDescriptor; 38 | EVT_WDF_REQUEST_COMPLETION_ROUTINE P2S_IoctlHidSetFeatureCompletionRoutine; 39 | P2S_SET_TO_PRECISION_TOUCHPAD_MODE P2S_SetToPrecisionTouchpadMode; 40 | EVT_WDF_REQUEST_COMPLETION_ROUTINE P2S_ForwardIoctlCompletionRoutine; 41 | EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL P2S_EvtIoDeviceControl; 42 | EVT_WDF_OBJECT_CONTEXT_CLEANUP P2S_EvtDriverContextCleanup; 43 | EVT_WDF_DRIVER_DEVICE_ADD P2S_EvtDeviceAdd; 44 | DRIVER_INITIALIZE DriverEntry; 45 | 46 | EXTERN_C_END 47 | -------------------------------------------------------------------------------- /Pad2Screen/Pad2Screen.inf: -------------------------------------------------------------------------------- 1 | ; 2 | ; Pad2Screen.inf 3 | ; 4 | 5 | [Version] 6 | Signature = "$WINDOWS NT$" 7 | Class = HIDClass 8 | ClassGuid = {745a17a0-74d3-11d0-b6fe-00a0c90f57da} 9 | Provider = %ManufacturerName% 10 | CatalogFile = Pad2Screen.cat 11 | DriverVer = 12 | 13 | [DestinationDirs] 14 | DefaultDestDir = 12 15 | Pad2Screen_CoInstaller_CopyFiles = 11 16 | 17 | [SourceDisksNames] 18 | 1 = %DiskName% 19 | 20 | [SourceDisksFiles] 21 | Pad2Screen.sys = 1 22 | WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll = 1 23 | 24 | [Manufacturer] 25 | %ManufacturerName% = Standard, NT$ARCH$ 26 | 27 | [Standard.NT$ARCH$] 28 | %Pad2Screen.DeviceDesc% = Pad2Screen, Root\Pad2Screen 29 | 30 | [Pad2Screen.NT] 31 | CopyFiles = Drivers_Dir 32 | 33 | [Pad2Screen.NT.HW] 34 | AddReg = Pad2Screen_AddReg 35 | 36 | [Pad2Screen.NT.Services] 37 | AddService = Pad2Screen, 0, Pad2Screen_Service_Inst 38 | AddService = mshidkmdf, 0x00000002, mshidkmdf_Service_Inst 39 | 40 | [Pad2Screen.NT.CoInstallers] 41 | AddReg = Pad2Screen_CoInstaller_AddReg 42 | CopyFiles = Pad2Screen_CoInstaller_CopyFiles 43 | 44 | [Pad2Screen.NT.Wdf] 45 | KmdfService = Pad2Screen, Pad2Screen_wdfsect 46 | 47 | [Pad2Screen_Service_Inst] 48 | DisplayName = %Pad2Screen.SVCDESC% 49 | ServiceType = 1 ; SERVICE_KERNEL_DRIVER 50 | StartType = 3 ; SERVICE_DEMAND_START 51 | ErrorControl = 1 ; SERVICE_ERROR_NORMAL 52 | ServiceBinary = %12%\Pad2Screen.sys 53 | 54 | [mshidkmdf_Service_Inst] 55 | ServiceType = 1 ; SERVICE_KERNEL_DRIVER 56 | StartType = 3 ; SERVICE_DEMAND_START 57 | ErrorControl = 1 ; SERVICE_ERROR_NORMAL 58 | ServiceBinary = %12%\mshidkmdf.sys 59 | 60 | [Drivers_Dir] 61 | Pad2Screen.sys 62 | 63 | [Pad2Screen_AddReg] 64 | HKR, , "LowerFilters", 0x00010008, "Pad2Screen" 65 | 66 | [Pad2Screen_wdfsect] 67 | KmdfLibraryVersion = $KMDFVERSION$ 68 | 69 | [Pad2Screen_CoInstaller_AddReg] 70 | HKR, , CoInstallers32, 0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" 71 | 72 | [Pad2Screen_CoInstaller_CopyFiles] 73 | WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll 74 | 75 | [Strings] 76 | ManufacturerName = "Andrew Sun" 77 | DiskName = "Pad2Screen Installation Disk" 78 | Pad2Screen.DeviceDesc = "Pad2Screen Device" 79 | Pad2Screen.SVCDESC = "Pad2Screen Service" 80 | -------------------------------------------------------------------------------- /Pad2Screen/Pad2Screen.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | Debug 22 | ARM 23 | 24 | 25 | Release 26 | ARM 27 | 28 | 29 | Debug 30 | ARM64 31 | 32 | 33 | Release 34 | ARM64 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | {B9AB30F4-DDCE-47CC-9C3F-F3620EAB2CBB} 49 | {497e31cb-056b-4f31-abb8-447fd55ee5a5} 50 | v4.5 51 | 12.0 52 | Debug 53 | Win32 54 | Pad2Screen 55 | 56 | 57 | 58 | Windows10 59 | true 60 | WindowsKernelModeDriver10.0 61 | Driver 62 | KMDF 63 | Desktop 64 | 65 | 66 | Windows10 67 | false 68 | WindowsKernelModeDriver10.0 69 | Driver 70 | KMDF 71 | Desktop 72 | 73 | 74 | Windows10 75 | true 76 | WindowsKernelModeDriver10.0 77 | Driver 78 | KMDF 79 | Desktop 80 | 81 | 82 | Windows10 83 | false 84 | WindowsKernelModeDriver10.0 85 | Driver 86 | KMDF 87 | Desktop 88 | 89 | 90 | Windows10 91 | true 92 | WindowsKernelModeDriver10.0 93 | Driver 94 | KMDF 95 | Universal 96 | 97 | 98 | Windows10 99 | false 100 | WindowsKernelModeDriver10.0 101 | Driver 102 | KMDF 103 | Universal 104 | 105 | 106 | Windows10 107 | true 108 | WindowsKernelModeDriver10.0 109 | Driver 110 | KMDF 111 | Universal 112 | 113 | 114 | Windows10 115 | false 116 | WindowsKernelModeDriver10.0 117 | Driver 118 | KMDF 119 | Universal 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | DbgengKernelDebugger 131 | 132 | 133 | DbgengKernelDebugger 134 | 135 | 136 | DbgengKernelDebugger 137 | 138 | 139 | DbgengKernelDebugger 140 | 141 | 142 | DbgengKernelDebugger 143 | 144 | 145 | DbgengKernelDebugger 146 | 147 | 148 | DbgengKernelDebugger 149 | 150 | 151 | DbgengKernelDebugger 152 | 153 | 154 | 155 | true 156 | true 157 | trace.h 158 | true 159 | 160 | 161 | 162 | 163 | true 164 | true 165 | trace.h 166 | true 167 | 168 | 169 | 170 | 171 | true 172 | true 173 | trace.h 174 | true 175 | 176 | 177 | 178 | 179 | true 180 | true 181 | trace.h 182 | true 183 | 184 | 185 | 186 | 187 | true 188 | true 189 | trace.h 190 | true 191 | 192 | 193 | 194 | 195 | true 196 | true 197 | trace.h 198 | true 199 | 200 | 201 | 202 | 203 | true 204 | true 205 | trace.h 206 | true 207 | 208 | 209 | 210 | 211 | true 212 | true 213 | trace.h 214 | true 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | -------------------------------------------------------------------------------- /Pad2Screen/Pad2Screen.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {8E41214B-6785-4CFE-B992-037D68949A14} 18 | inf;inv;inx;mof;mc; 19 | 20 | 21 | 22 | 23 | Driver Files 24 | 25 | 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | 35 | 36 | Source Files 37 | 38 | 39 | -------------------------------------------------------------------------------- /Pad2Screen/Trace.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Module Name: 4 | 5 | Trace.h 6 | 7 | Abstract: 8 | 9 | Header file for the debug tracing related function defintions and macros. 10 | 11 | Environment: 12 | 13 | Kernel mode 14 | 15 | --*/ 16 | 17 | // 18 | // Define the tracing flags. 19 | // 20 | // Tracing GUID - 620fa26a-bf3b-4f09-8e9d-ca326703612e 21 | // 22 | 23 | #define WPP_CONTROL_GUIDS \ 24 | WPP_DEFINE_CONTROL_GUID( \ 25 | Pad2ScreenTraceGuid, (620fa26a,bf3b,4f09,8e9d,ca326703612e), \ 26 | \ 27 | WPP_DEFINE_BIT(MYDRIVER_ALL_INFO) \ 28 | WPP_DEFINE_BIT(TRACE_DRIVER) \ 29 | WPP_DEFINE_BIT(TRACE_DEVICE) \ 30 | WPP_DEFINE_BIT(TRACE_QUEUE) \ 31 | ) 32 | 33 | #define WPP_FLAG_LEVEL_LOGGER(flag, level) \ 34 | WPP_LEVEL_LOGGER(flag) 35 | 36 | #define WPP_FLAG_LEVEL_ENABLED(flag, level) \ 37 | (WPP_LEVEL_ENABLED(flag) && \ 38 | WPP_CONTROL(WPP_BIT_ ## flag).Level >= level) 39 | 40 | #define WPP_LEVEL_FLAGS_LOGGER(lvl,flags) \ 41 | WPP_LEVEL_LOGGER(flags) 42 | 43 | #define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) \ 44 | (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl) 45 | 46 | // 47 | // WPP orders static parameters before dynamic parameters. To support the Trace function 48 | // defined below which sets FLAGS=MYDRIVER_ALL_INFO, a custom macro must be defined to 49 | // reorder the arguments to what the .tpl configuration file expects. 50 | // 51 | #define WPP_RECORDER_FLAGS_LEVEL_ARGS(flags, lvl) WPP_RECORDER_LEVEL_FLAGS_ARGS(lvl, flags) 52 | #define WPP_RECORDER_FLAGS_LEVEL_FILTER(flags, lvl) WPP_RECORDER_LEVEL_FLAGS_FILTER(lvl, flags) 53 | 54 | // 55 | // This comment block is scanned by the trace preprocessor to define our 56 | // Trace function. 57 | // 58 | // begin_wpp config 59 | // FUNC Trace{FLAGS=MYDRIVER_ALL_INFO}(LEVEL, MSG, ...); 60 | // FUNC TraceEvents(LEVEL, FLAGS, MSG, ...); 61 | // end_wpp 62 | // 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pad2Screen 2 | 3 | A Windows kernel driver that converts any Windows precision touchpad into a 4 | touchscreen (i.e. gives you absolute cursor positioning). 5 | 6 | This project was scrapped due to the huge variety of Windows touchpad drivers, 7 | some of which are worse than others. It was initially intended to be a lower 8 | filter for mshidkmdf, but as it turns out, not all touchpad drivers even use 9 | it. This code has never been tested, do not expect support for it. I am 10 | releasing the code in the hopes that someone will find it useful. For a working 11 | alternative to this project, see 12 | [AbsoluteTouchEx](https://github.com/apsun/AbsoluteTouchEx). 13 | 14 | ## How does it work? 15 | 16 | Touchpads send their their input data over HID (human interface device). 17 | In the Windows kernel, there exists a generic driver which takes a HID report 18 | (essentially a packet) and a HID report descriptor (similar to a struct 19 | definition in C) from any input device, in our case a touchpad, and 20 | translates the data to an event that gets delivered to your application. 21 | 22 | How does Windows know that the HID reports are coming from a touchpad? 23 | That's easy - just look at the HID report descriptor. It contains a "usage" 24 | field that tells the HID driver what the source of the data is - mouse, 25 | keyboard, touchpad, touchscreen, or something else. 26 | 27 | Where does Pad2Screen come in? In a nutshell, it sits between the HID and 28 | touchpad drivers and hooks the report descriptor to change the usage field 29 | from "touchpad" to "touchscreen". Everything else remains the same, and it 30 | just so happens that the report formats are similar enough to "just work" out 31 | of the box, on current Windows versions at least. 32 | --------------------------------------------------------------------------------