├── KeyboardBacklight.h ├── crosecservice.vcxproj.filters ├── crosecservice.sln ├── crosecservice.cpp ├── croskbhid.h ├── KeyboardBacklight.cpp ├── .gitignore ├── crosecservice.vcxproj └── client.cpp /KeyboardBacklight.h: -------------------------------------------------------------------------------- 1 | class KeyboardBacklight { 2 | public: 3 | INT IdleTimeout = 5000; 4 | 5 | private: 6 | BOOL HasKeyboardBacklight = FALSE; 7 | BOOL IsLaptopMode = TRUE; 8 | INT InternalBrightness = 100; 9 | INT CurrentBrightness = 100; 10 | INT BrightnessFadeStep = 0; 11 | INT BrightnessFadeTarget = 100; 12 | BOOL BacklightEnabled = TRUE; 13 | 14 | std::thread task; 15 | 16 | pcroskbhid_client client; 17 | 18 | public: 19 | KeyboardBacklight(pcroskbhid_client client); 20 | ~KeyboardBacklight(); 21 | 22 | BOOL CheckIsLaptopMode(); 23 | BOOL GetBacklightEnabled(); 24 | void SetBacklightEnabled(BOOL enabled); 25 | INT GetBrightness(); 26 | void FadeSet(INT brightness); 27 | void BrightnessInc(); 28 | void BrightnessDec(); 29 | 30 | private: 31 | void FadeSetInternal(INT brightness); 32 | void FadeBrightnessStep(); 33 | void InstantlySet(INT Brightness); 34 | }; -------------------------------------------------------------------------------- /crosecservice.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;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 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | -------------------------------------------------------------------------------- /crosecservice.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31829.152 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crosecservice", "crosecservice.vcxproj", "{25357B70-BB72-4B8E-9334-19F6BA5999BB}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {25357B70-BB72-4B8E-9334-19F6BA5999BB}.Debug|x64.ActiveCfg = Debug|x64 17 | {25357B70-BB72-4B8E-9334-19F6BA5999BB}.Debug|x64.Build.0 = Debug|x64 18 | {25357B70-BB72-4B8E-9334-19F6BA5999BB}.Debug|x86.ActiveCfg = Debug|Win32 19 | {25357B70-BB72-4B8E-9334-19F6BA5999BB}.Debug|x86.Build.0 = Debug|Win32 20 | {25357B70-BB72-4B8E-9334-19F6BA5999BB}.Release|x64.ActiveCfg = Release|x64 21 | {25357B70-BB72-4B8E-9334-19F6BA5999BB}.Release|x64.Build.0 = Release|x64 22 | {25357B70-BB72-4B8E-9334-19F6BA5999BB}.Release|x86.ActiveCfg = Release|Win32 23 | {25357B70-BB72-4B8E-9334-19F6BA5999BB}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {4F0E98D9-AC19-42AB-B6E9-EF9F9261E0A2} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /crosecservice.cpp: -------------------------------------------------------------------------------- 1 | // crosecservice.cpp : This file contains the 'main' function. Program execution begins and ends there. 2 | // 3 | 4 | #include 5 | #include 6 | #include 7 | #include "croskbhid.h" 8 | #include "KeyboardBacklight.h" 9 | 10 | #include 11 | 12 | BOOL isTabletConvertible; 13 | 14 | void checkConvertible() { 15 | isTabletConvertible = FALSE; 16 | 17 | HDEVINFO hdevinfo = SetupDiGetClassDevsW(NULL, LR"(ACPI\GOOG0006)", 18 | NULL, DIGCF_ALLCLASSES); 19 | if (hdevinfo == INVALID_HANDLE_VALUE) 20 | { 21 | DWORD err = GetLastError(); 22 | return; 23 | } 24 | 25 | SP_DEVINFO_DATA devinfo; 26 | devinfo.cbSize = sizeof(devinfo); 27 | if (!SetupDiEnumDeviceInfo(hdevinfo, 0, &devinfo)) 28 | { 29 | DWORD err = GetLastError(); 30 | return; 31 | } 32 | 33 | isTabletConvertible = TRUE; 34 | } 35 | 36 | int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) 37 | { 38 | checkConvertible(); 39 | 40 | pcroskbhid_client client = croskbhid_alloc(); 41 | BOOL connect = croskbhid_connect(client); 42 | printf("Connected? %d\n", connect); 43 | if (!connect) { 44 | return 0; 45 | } 46 | 47 | KeyboardBacklight backlight(client); 48 | 49 | CrosKBHIDRemapperMediaReport report = { 0 }; 50 | 51 | while (TRUE) { 52 | croskbhid_read_keyboard(client, &report); 53 | 54 | if (report.ReportID == REPORTID_MEDIA) { 55 | if (report.ControlCode == 0) 56 | continue; 57 | 58 | if (report.ControlCode & CROSKBHID_KBLT_UP) { 59 | backlight.BrightnessInc(); 60 | } 61 | if (report.ControlCode & CROSKBHID_KBLT_DN) { 62 | backlight.BrightnessDec(); 63 | } 64 | if (report.ControlCode & CROSKBHID_KBLT_TOGGLE) { 65 | backlight.SetBacklightEnabled(!backlight.GetBacklightEnabled()); 66 | } 67 | } 68 | } 69 | 70 | croskbhid_disconnect(client); 71 | } -------------------------------------------------------------------------------- /croskbhid.h: -------------------------------------------------------------------------------- 1 | #if !defined(_CROSKBHIDREMAPPER_COMMON_H_) 2 | #define _CROSKBHIDREMAPPER_COMMON_H_ 3 | 4 | typedef struct _croskbhid_client_t* pcroskbhid_client; 5 | 6 | 7 | // 8 | //These are the device attributes returned by vmulti in response 9 | // to IOCTL_HID_GET_DEVICE_ATTRIBUTES. 10 | // 11 | 12 | #define CROSKBHIDREMAPPER_PID 0x0303 13 | #define CROSKBHIDREMAPPER_VID 0x18D1 14 | #define CROSKBHIDREMAPPER_VERSION 0x0004 15 | 16 | #define CROSKBLIGHT_PID 0x0002 17 | #define CROSKBLIGHT_VID 0x18D1 18 | #define CROSKBLIGHT_VERSION 0x0001 19 | 20 | // 21 | // These are the report ids 22 | // 23 | 24 | #define REPORTID_KBLIGHT 0x01 25 | 26 | #define REPORTID_KEYBOARD 0x07 27 | #define REPORTID_MEDIA 0x08 28 | #define REPORTID_SETTINGS 0x09 29 | 30 | // 31 | // Keyboard specific report infomation 32 | // 33 | 34 | #define CROSKBHID_BRIGHTNESS_UP 0x01 35 | #define CROSKBHID_BRIGHTNESS_DN 0x02 36 | #define CROSKBHID_KBLT_UP 0x04 37 | #define CROSKBHID_KBLT_DN 0x08 38 | #define CROSKBHID_KBLT_TOGGLE 0x10 39 | 40 | #pragma pack(1) 41 | typedef struct _CROSKBHIDREMAPPER_MEDIA_REPORT 42 | { 43 | 44 | BYTE ReportID; 45 | 46 | BYTE ControlCode; 47 | 48 | BYTE Reserved; 49 | 50 | } CrosKBHIDRemapperMediaReport; 51 | 52 | #pragma pack() 53 | 54 | #pragma pack() 55 | 56 | // 57 | // Feature report infomation 58 | // 59 | 60 | #define DEVICE_MODE_MOUSE 0x00 61 | #define DEVICE_MODE_SINGLE_INPUT 0x01 62 | #define DEVICE_MODE_MULTI_INPUT 0x02 63 | 64 | #pragma pack(1) 65 | typedef struct _CROSKBHIDREMAPPER_FEATURE_REPORT 66 | { 67 | 68 | BYTE ReportID; 69 | 70 | BYTE DeviceMode; 71 | 72 | BYTE DeviceIdentifier; 73 | 74 | } CrosKBHIDRemapperFeatureReport; 75 | 76 | #pragma pack(1) 77 | typedef struct _CROSKBHIDREMAPPER_SETTINGS_REPORT 78 | { 79 | 80 | BYTE ReportID; 81 | 82 | BYTE SettingsRegister; 83 | 84 | BYTE SettingsValue; 85 | 86 | } CrosKBHIDRemapperSettingsReport; 87 | #pragma pack() 88 | 89 | #pragma pack(1) 90 | typedef struct _CROSKBLIGHT_GETLIGHT_REPORT 91 | { 92 | 93 | BYTE ReportID; 94 | 95 | BYTE Brightness; 96 | 97 | } CrosKBLightGetLightReport; 98 | #pragma pack() 99 | 100 | #pragma pack(1) 101 | typedef struct _CROSKBLIGHT_SETTINGS_REPORT 102 | { 103 | 104 | BYTE ReportID; 105 | 106 | BYTE SetBrightness; 107 | 108 | BYTE Brightness; 109 | 110 | } CrosKBLightSettingsReport; 111 | #pragma pack() 112 | 113 | pcroskbhid_client croskbhid_alloc(void); 114 | 115 | void croskbhid_free(pcroskbhid_client vmulti); 116 | 117 | BOOL croskbhid_connect(pcroskbhid_client vmulti); 118 | 119 | void croskbhid_disconnect(pcroskbhid_client vmulti); 120 | 121 | BOOL croskblight_connect(pcroskbhid_client croskbhid); 122 | void croskblight_disconnect(pcroskbhid_client croskbhid); 123 | 124 | BOOL croskbhid_read_keyboard(pcroskbhid_client vmulti, CrosKBHIDRemapperMediaReport* pReport); 125 | 126 | BOOL croskbhid_write_keyboard(pcroskbhid_client vmulti, CrosKBHIDRemapperMediaReport* pReport); 127 | 128 | BOOL croskblight_read_message(pcroskbhid_client vmulti, CrosKBLightSettingsReport* pReport); 129 | 130 | BOOL croskblight_write_message(pcroskbhid_client vmulti, CrosKBLightSettingsReport* pReport); 131 | 132 | #endif 133 | #pragma once 134 | -------------------------------------------------------------------------------- /KeyboardBacklight.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "croskbhid.h" 5 | #include "KeyboardBacklight.h" 6 | 7 | KeyboardBacklight::KeyboardBacklight(pcroskbhid_client client) { 8 | this->client = client; 9 | this->HasKeyboardBacklight = croskblight_connect(client); 10 | 11 | if (this->HasKeyboardBacklight) { 12 | this->task = std::thread(&KeyboardBacklight::FadeBrightnessStep, this); 13 | } 14 | 15 | this->InstantlySet(this->InternalBrightness); 16 | } 17 | 18 | KeyboardBacklight::~KeyboardBacklight() { 19 | if (this->HasKeyboardBacklight) { 20 | this->HasKeyboardBacklight = FALSE; 21 | this->task.join(); 22 | 23 | croskblight_disconnect(client); 24 | } 25 | } 26 | 27 | BOOL KeyboardBacklight::GetBacklightEnabled() { 28 | return BacklightEnabled; 29 | } 30 | 31 | void KeyboardBacklight::SetBacklightEnabled(BOOL enabled) { 32 | this->BacklightEnabled = enabled; 33 | this->InstantlySet(this->InternalBrightness); 34 | } 35 | 36 | INT KeyboardBacklight::GetBrightness() { 37 | return this->CurrentBrightness; 38 | } 39 | 40 | void KeyboardBacklight::FadeSetInternal(INT brightness) { 41 | this->BrightnessFadeStep = abs(this->InternalBrightness - brightness) / 6; //do over 6 steps[300 ms] 42 | this->BrightnessFadeTarget = brightness; 43 | } 44 | 45 | void KeyboardBacklight::FadeSet(INT brightness) { 46 | this->CurrentBrightness = brightness; 47 | this->BacklightEnabled = TRUE; 48 | this->FadeSetInternal(brightness); 49 | } 50 | 51 | void KeyboardBacklight::BrightnessInc() { 52 | INT DesiredBrightness = this->BrightnessFadeTarget; 53 | DesiredBrightness += 10; 54 | if (DesiredBrightness > 100) { 55 | DesiredBrightness = 100; 56 | } 57 | this->FadeSet(DesiredBrightness); 58 | } 59 | 60 | void KeyboardBacklight::BrightnessDec() { 61 | INT DesiredBrightness = this->BrightnessFadeTarget; 62 | DesiredBrightness -= 10; 63 | if (DesiredBrightness < 0) { 64 | DesiredBrightness = 0; 65 | } 66 | this->FadeSet(DesiredBrightness); 67 | } 68 | 69 | void KeyboardBacklight::FadeBrightnessStep() { 70 | INT TimerAdjustment = 0; 71 | while (this->HasKeyboardBacklight) { 72 | if ((50 - TimerAdjustment) > 0) { 73 | Sleep(50 - TimerAdjustment); 74 | } 75 | 76 | ULONGLONG Start = GetTickCount64(); 77 | 78 | if (this->InternalBrightness == this->BrightnessFadeTarget && 79 | this->IsLaptopMode == CheckIsLaptopMode()) { 80 | LASTINPUTINFO lastInput; 81 | lastInput.cbSize = sizeof(lastInput); 82 | GetLastInputInfo(&lastInput); 83 | 84 | DWORD currentTick = GetTickCount(); 85 | if (currentTick - lastInput.dwTime > this->IdleTimeout || lastInput.dwTime > currentTick) { 86 | if (this->BacklightEnabled) 87 | this->FadeSetInternal(0); 88 | } 89 | else if (this->CurrentBrightness != 0 && this->InternalBrightness == 0) { 90 | if (this->BacklightEnabled) 91 | this->FadeSetInternal(this->CurrentBrightness); 92 | } 93 | 94 | TimerAdjustment = (INT)(GetTickCount64() - Start); 95 | 96 | continue; 97 | } 98 | 99 | INT DesiredBrightness = this->InternalBrightness; 100 | INT BrightnessFadeTarget = this->BrightnessFadeTarget; 101 | INT BrightnessFadeStep = this->BrightnessFadeStep; 102 | 103 | if ((DesiredBrightness < BrightnessFadeTarget) && (DesiredBrightness + BrightnessFadeStep) > BrightnessFadeTarget) 104 | { 105 | DesiredBrightness = BrightnessFadeTarget; 106 | } 107 | else if ((DesiredBrightness > BrightnessFadeTarget) && (DesiredBrightness - BrightnessFadeStep) < BrightnessFadeTarget) 108 | { 109 | DesiredBrightness = BrightnessFadeTarget; 110 | } 111 | else if (DesiredBrightness < BrightnessFadeTarget) { 112 | DesiredBrightness = DesiredBrightness + BrightnessFadeStep; 113 | } 114 | else if (DesiredBrightness > BrightnessFadeTarget) { 115 | DesiredBrightness = DesiredBrightness - BrightnessFadeStep; 116 | } 117 | 118 | this->InstantlySet(DesiredBrightness); 119 | 120 | TimerAdjustment = (INT)(GetTickCount64() - Start); 121 | } 122 | } 123 | 124 | extern BOOL isTabletConvertible; 125 | 126 | BOOL KeyboardBacklight::CheckIsLaptopMode() { 127 | if (!isTabletConvertible) { 128 | return TRUE; 129 | } 130 | return (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) != 0); 131 | } 132 | 133 | void KeyboardBacklight::InstantlySet(INT brightness) { 134 | if (brightness > 100) 135 | brightness = 100; 136 | else if (brightness < 0) 137 | brightness = 0; 138 | this->InternalBrightness = brightness; 139 | 140 | CrosKBLightSettingsReport report = { 0 }; 141 | report.ReportID = REPORTID_KBLIGHT; 142 | report.SetBrightness = 1; 143 | report.Brightness = this->BacklightEnabled ? brightness : 0; 144 | 145 | this->IsLaptopMode = CheckIsLaptopMode(); 146 | if (!this->IsLaptopMode) { 147 | report.Brightness = 0; 148 | } 149 | 150 | croskblight_write_message(client, &report); 151 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015/2017 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # Visual Studio 2017 auto generated files 34 | Generated\ Files/ 35 | 36 | # MSTest test Results 37 | [Tt]est[Rr]esult*/ 38 | [Bb]uild[Ll]og.* 39 | 40 | # NUNIT 41 | *.VisualState.xml 42 | TestResult.xml 43 | 44 | # Build Results of an ATL Project 45 | [Dd]ebugPS/ 46 | [Rr]eleasePS/ 47 | dlldata.c 48 | 49 | # Benchmark Results 50 | BenchmarkDotNet.Artifacts/ 51 | 52 | # .NET Core 53 | project.lock.json 54 | project.fragment.lock.json 55 | artifacts/ 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_h.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *_wpftmp.csproj 81 | *.log 82 | *.vspscc 83 | *.vssscc 84 | .builds 85 | *.pidb 86 | *.svclog 87 | *.scc 88 | 89 | # Chutzpah Test files 90 | _Chutzpah* 91 | 92 | # Visual C++ cache files 93 | ipch/ 94 | *.aps 95 | *.ncb 96 | *.opendb 97 | *.opensdf 98 | *.sdf 99 | *.cachefile 100 | *.VC.db 101 | *.VC.VC.opendb 102 | 103 | # Visual Studio profiler 104 | *.psess 105 | *.vsp 106 | *.vspx 107 | *.sap 108 | 109 | # Visual Studio Trace Files 110 | *.e2e 111 | 112 | # TFS 2012 Local Workspace 113 | $tf/ 114 | 115 | # Guidance Automation Toolkit 116 | *.gpState 117 | 118 | # ReSharper is a .NET coding add-in 119 | _ReSharper*/ 120 | *.[Rr]e[Ss]harper 121 | *.DotSettings.user 122 | 123 | # JustCode is a .NET coding add-in 124 | .JustCode 125 | 126 | # TeamCity is a build add-in 127 | _TeamCity* 128 | 129 | # DotCover is a Code Coverage Tool 130 | *.dotCover 131 | 132 | # AxoCover is a Code Coverage Tool 133 | .axoCover/* 134 | !.axoCover/settings.json 135 | 136 | # Visual Studio code coverage results 137 | *.coverage 138 | *.coveragexml 139 | 140 | # NCrunch 141 | _NCrunch_* 142 | .*crunch*.local.xml 143 | nCrunchTemp_* 144 | 145 | # MightyMoose 146 | *.mm.* 147 | AutoTest.Net/ 148 | 149 | # Web workbench (sass) 150 | .sass-cache/ 151 | 152 | # Installshield output folder 153 | [Ee]xpress/ 154 | 155 | # DocProject is a documentation generator add-in 156 | DocProject/buildhelp/ 157 | DocProject/Help/*.HxT 158 | DocProject/Help/*.HxC 159 | DocProject/Help/*.hhc 160 | DocProject/Help/*.hhk 161 | DocProject/Help/*.hhp 162 | DocProject/Help/Html2 163 | DocProject/Help/html 164 | 165 | # Click-Once directory 166 | publish/ 167 | 168 | # Publish Web Output 169 | *.[Pp]ublish.xml 170 | *.azurePubxml 171 | # Note: Comment the next line if you want to checkin your web deploy settings, 172 | # but database connection strings (with potential passwords) will be unencrypted 173 | *.pubxml 174 | *.publishproj 175 | 176 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 177 | # checkin your Azure Web App publish settings, but sensitive information contained 178 | # in these scripts will be unencrypted 179 | PublishScripts/ 180 | 181 | # NuGet Packages 182 | *.nupkg 183 | # The packages folder can be ignored because of Package Restore 184 | **/[Pp]ackages/* 185 | # except build/, which is used as an MSBuild target. 186 | !**/[Pp]ackages/build/ 187 | # Uncomment if necessary however generally it will be regenerated when needed 188 | #!**/[Pp]ackages/repositories.config 189 | # NuGet v3's project.json files produces more ignorable files 190 | *.nuget.props 191 | *.nuget.targets 192 | 193 | # Microsoft Azure Build Output 194 | csx/ 195 | *.build.csdef 196 | 197 | # Microsoft Azure Emulator 198 | ecf/ 199 | rcf/ 200 | 201 | # Windows Store app package directories and files 202 | AppPackages/ 203 | BundleArtifacts/ 204 | Package.StoreAssociation.xml 205 | _pkginfo.txt 206 | *.appx 207 | 208 | # Visual Studio cache files 209 | # files ending in .cache can be ignored 210 | *.[Cc]ache 211 | # but keep track of directories ending in .cache 212 | !*.[Cc]ache/ 213 | 214 | # Others 215 | ClientBin/ 216 | ~$* 217 | *~ 218 | *.dbmdl 219 | *.dbproj.schemaview 220 | *.jfm 221 | *.pfx 222 | *.publishsettings 223 | orleans.codegen.cs 224 | 225 | # Including strong name files can present a security risk 226 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 227 | #*.snk 228 | 229 | # Since there are multiple workflows, uncomment next line to ignore bower_components 230 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 231 | #bower_components/ 232 | 233 | # RIA/Silverlight projects 234 | Generated_Code/ 235 | 236 | # Backup & report files from converting an old project file 237 | # to a newer Visual Studio version. Backup files are not needed, 238 | # because we have git ;-) 239 | _UpgradeReport_Files/ 240 | Backup*/ 241 | UpgradeLog*.XML 242 | UpgradeLog*.htm 243 | ServiceFabricBackup/ 244 | *.rptproj.bak 245 | 246 | # SQL Server files 247 | *.mdf 248 | *.ldf 249 | *.ndf 250 | 251 | # Business Intelligence projects 252 | *.rdl.data 253 | *.bim.layout 254 | *.bim_*.settings 255 | *.rptproj.rsuser 256 | 257 | # Microsoft Fakes 258 | FakesAssemblies/ 259 | 260 | # GhostDoc plugin setting file 261 | *.GhostDoc.xml 262 | 263 | # Node.js Tools for Visual Studio 264 | .ntvs_analysis.dat 265 | node_modules/ 266 | 267 | # Visual Studio 6 build log 268 | *.plg 269 | 270 | # Visual Studio 6 workspace options file 271 | *.opt 272 | 273 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 274 | *.vbw 275 | 276 | # Visual Studio LightSwitch build output 277 | **/*.HTMLClient/GeneratedArtifacts 278 | **/*.DesktopClient/GeneratedArtifacts 279 | **/*.DesktopClient/ModelManifest.xml 280 | **/*.Server/GeneratedArtifacts 281 | **/*.Server/ModelManifest.xml 282 | _Pvt_Extensions 283 | 284 | # Paket dependency manager 285 | .paket/paket.exe 286 | paket-files/ 287 | 288 | # FAKE - F# Make 289 | .fake/ 290 | 291 | # JetBrains Rider 292 | .idea/ 293 | *.sln.iml 294 | 295 | # CodeRush personal settings 296 | .cr/personal 297 | 298 | # Python Tools for Visual Studio (PTVS) 299 | __pycache__/ 300 | *.pyc 301 | 302 | # Cake - Uncomment if you are using it 303 | # tools/** 304 | # !tools/packages.config 305 | 306 | # Tabs Studio 307 | *.tss 308 | 309 | # Telerik's JustMock configuration file 310 | *.jmconfig 311 | 312 | # BizTalk build output 313 | *.btp.cs 314 | *.btm.cs 315 | *.odx.cs 316 | *.xsd.cs 317 | 318 | # OpenCover UI analysis results 319 | OpenCover/ 320 | 321 | # Azure Stream Analytics local run output 322 | ASALocalRun/ 323 | 324 | # MSBuild Binary and Structured Log 325 | *.binlog 326 | 327 | # NVidia Nsight GPU debugger configuration file 328 | *.nvuser 329 | 330 | # MFractors (Xamarin productivity tool) working folder 331 | .mfractor/ 332 | 333 | # Local History for Visual Studio 334 | .localhistory/ 335 | -------------------------------------------------------------------------------- /crosecservice.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 | 22 | 16.0 23 | Win32Proj 24 | {25357b70-bb72-4b8e-9334-19f6ba5999bb} 25 | crosecservice 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | 92 | 93 | Windows 94 | true 95 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;hid.lib;%(AdditionalDependencies) 96 | 97 | 98 | 99 | 100 | Level3 101 | true 102 | true 103 | true 104 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 105 | true 106 | 107 | 108 | Windows 109 | true 110 | true 111 | true 112 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;hid.lib;%(AdditionalDependencies) 113 | 114 | 115 | 116 | 117 | Level3 118 | true 119 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 120 | true 121 | 122 | 123 | Windows 124 | true 125 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;hid.lib;%(AdditionalDependencies) 126 | 127 | 128 | 129 | 130 | Level3 131 | true 132 | true 133 | true 134 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 135 | true 136 | 137 | 138 | Windows 139 | true 140 | true 141 | true 142 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;hid.lib;%(AdditionalDependencies) 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "croskbhid.h" 8 | 9 | #if __GNUC__ 10 | #define __in 11 | #define __in_ecount(x) 12 | typedef void* PVOID; 13 | typedef PVOID HDEVINFO; 14 | WINHIDSDI BOOL WINAPI HidD_SetOutputReport(HANDLE, PVOID, ULONG); 15 | #endif 16 | 17 | typedef struct _croskbhid_client_t 18 | { 19 | HANDLE hKeyboard; 20 | HANDLE hSettings; 21 | } croskbhid_client_t; 22 | 23 | // 24 | // Function prototypes 25 | // 26 | 27 | HANDLE 28 | SearchMatchingHwID( 29 | USHORT vendorID, 30 | USHORT productID, 31 | USAGE myUsagePage, 32 | USAGE myUsage 33 | ); 34 | 35 | HANDLE 36 | OpenDeviceInterface( 37 | HDEVINFO HardwareDeviceInfo, 38 | PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, 39 | USHORT vendorID, 40 | USHORT productID, 41 | USAGE myUsagePage, 42 | USAGE myUsage 43 | ); 44 | 45 | BOOLEAN 46 | CheckIfOurDevice( 47 | HANDLE file, 48 | USHORT vendorID, 49 | USHORT productID, 50 | USAGE myUsagePage, 51 | USAGE myUsage 52 | ); 53 | 54 | BOOL 55 | HidOutput( 56 | BOOL useSetOutputReport, 57 | HANDLE file, 58 | PCHAR buffer, 59 | ULONG bufferSize 60 | ); 61 | 62 | // 63 | // Copied this structure from hidport.h 64 | // 65 | 66 | typedef struct _HID_DEVICE_ATTRIBUTES { 67 | 68 | ULONG Size; 69 | // 70 | // sizeof (struct _HID_DEVICE_ATTRIBUTES) 71 | // 72 | // 73 | // Vendor ids of this hid device 74 | // 75 | USHORT VendorID; 76 | USHORT ProductID; 77 | USHORT VersionNumber; 78 | USHORT Reserved[11]; 79 | 80 | } HID_DEVICE_ATTRIBUTES, * PHID_DEVICE_ATTRIBUTES; 81 | 82 | static USHORT TpVendorID = 0; 83 | 84 | USHORT getVendorID() { 85 | return TpVendorID; 86 | } 87 | 88 | // 89 | // Implementation 90 | // 91 | 92 | pcroskbhid_client croskbhid_alloc(void) 93 | { 94 | return (pcroskbhid_client)malloc(sizeof(croskbhid_client_t)); 95 | } 96 | 97 | void croskbhid_free(pcroskbhid_client croskbhid) 98 | { 99 | free(croskbhid); 100 | } 101 | 102 | BOOL croskbhid_connect(pcroskbhid_client croskbhid) 103 | { 104 | // 105 | // Find the HID devices 106 | // 107 | 108 | croskbhid->hKeyboard = SearchMatchingHwID(CROSKBHIDREMAPPER_VID, CROSKBHIDREMAPPER_PID, 0x000C, 0x0001); 109 | if (croskbhid->hKeyboard == INVALID_HANDLE_VALUE || croskbhid->hKeyboard == NULL) 110 | { 111 | croskbhid_disconnect(croskbhid); 112 | return FALSE; 113 | } 114 | 115 | // 116 | // Set the buffer count to 10 on the setting HID 117 | // 118 | 119 | if (!HidD_SetNumInputBuffers(croskbhid->hKeyboard, 10)) 120 | { 121 | printf("failed HidD_SetNumInputBuffers %d\n", GetLastError()); 122 | croskbhid_disconnect(croskbhid); 123 | return FALSE; 124 | } 125 | return TRUE; 126 | } 127 | 128 | BOOL croskblight_connect(pcroskbhid_client croskbhid) { 129 | croskbhid->hSettings = SearchMatchingHwID(CROSKBLIGHT_VID, CROSKBLIGHT_PID, 0xff00, 0x0001); 130 | if (croskbhid->hSettings == INVALID_HANDLE_VALUE || croskbhid->hSettings == NULL) 131 | { 132 | croskbhid->hSettings = NULL; 133 | return FALSE; 134 | } 135 | 136 | if (!HidD_SetNumInputBuffers(croskbhid->hSettings, 10)) 137 | { 138 | printf("failed HidD_SetNumInputBuffers %d\n", GetLastError()); 139 | croskbhid_disconnect(croskbhid); 140 | return FALSE; 141 | } 142 | return TRUE; 143 | } 144 | 145 | void croskblight_disconnect(pcroskbhid_client croskbhid) 146 | { 147 | if (croskbhid->hSettings != NULL) 148 | CloseHandle(croskbhid->hSettings); 149 | croskbhid->hSettings = NULL; 150 | } 151 | 152 | void croskbhid_disconnect(pcroskbhid_client croskbhid) 153 | { 154 | if (croskbhid->hKeyboard != NULL) 155 | CloseHandle(croskbhid->hKeyboard); 156 | croskbhid->hKeyboard = NULL; 157 | 158 | if (croskbhid->hSettings != NULL) 159 | CloseHandle(croskbhid->hSettings); 160 | croskbhid->hSettings = NULL; 161 | } 162 | 163 | BOOL croskbhid_read_keyboard(pcroskbhid_client vmulti, CrosKBHIDRemapperMediaReport* pReport) 164 | { 165 | ULONG bytesRead; 166 | 167 | // 168 | // Read the report 169 | // 170 | 171 | if (!ReadFile(vmulti->hKeyboard, pReport, sizeof(CrosKBHIDRemapperMediaReport), &bytesRead, NULL)) 172 | { 173 | return FALSE; 174 | } 175 | 176 | return TRUE; 177 | } 178 | 179 | BOOL croskbhid_write_keyboard(pcroskbhid_client croskbhid, CrosKBHIDRemapperMediaReport* pReport) 180 | { 181 | ULONG bytesWritten; 182 | 183 | // 184 | // Write the report 185 | // 186 | 187 | if (!WriteFile(croskbhid->hKeyboard, pReport, sizeof(CrosKBHIDRemapperMediaReport), &bytesWritten, NULL)) 188 | { 189 | return FALSE; 190 | } 191 | 192 | return TRUE; 193 | } 194 | 195 | BOOL croskblight_read_message(pcroskbhid_client vmulti, CrosKBLightSettingsReport* pReport) 196 | { 197 | ULONG bytesRead; 198 | 199 | // 200 | // Read the report 201 | // 202 | 203 | if (!ReadFile(vmulti->hSettings, pReport, sizeof(CrosKBLightSettingsReport), &bytesRead, NULL)) 204 | { 205 | return FALSE; 206 | } 207 | 208 | return TRUE; 209 | } 210 | 211 | BOOL croskblight_write_message(pcroskbhid_client croskbhid, CrosKBLightSettingsReport* pReport) 212 | { 213 | ULONG bytesWritten; 214 | 215 | // 216 | // Write the report 217 | // 218 | 219 | if (!WriteFile(croskbhid->hSettings, pReport, sizeof(CrosKBLightSettingsReport), &bytesWritten, NULL)) 220 | { 221 | return FALSE; 222 | } 223 | 224 | return TRUE; 225 | } 226 | 227 | HANDLE 228 | SearchMatchingHwID( 229 | USHORT vendorID, 230 | USHORT productID, 231 | USAGE myUsagePage, 232 | USAGE myUsage 233 | ) 234 | { 235 | HDEVINFO hardwareDeviceInfo; 236 | SP_DEVICE_INTERFACE_DATA deviceInterfaceData; 237 | SP_DEVINFO_DATA devInfoData; 238 | GUID hidguid; 239 | int i; 240 | 241 | HidD_GetHidGuid(&hidguid); 242 | 243 | hardwareDeviceInfo = 244 | SetupDiGetClassDevs((LPGUID)&hidguid, 245 | NULL, 246 | NULL, // Define no 247 | (DIGCF_PRESENT | 248 | DIGCF_INTERFACEDEVICE)); 249 | 250 | if (INVALID_HANDLE_VALUE == hardwareDeviceInfo) 251 | { 252 | printf("SetupDiGetClassDevs failed: %x\n", GetLastError()); 253 | return INVALID_HANDLE_VALUE; 254 | } 255 | 256 | deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); 257 | 258 | devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); 259 | 260 | // 261 | // Enumerate devices of this interface class 262 | // 263 | 264 | printf("\n....looking for our HID device (with UP=0x%x " 265 | "and Usage=0x%x)\n", myUsagePage, myUsage); 266 | 267 | for (i = 0; SetupDiEnumDeviceInterfaces(hardwareDeviceInfo, 268 | 0, // No care about specific PDOs 269 | (LPGUID)&hidguid, 270 | i, // 271 | &deviceInterfaceData); 272 | i++) 273 | { 274 | 275 | // 276 | // Open the device interface and Check if it is our device 277 | // by matching the Usage page and Usage from Hid_Caps. 278 | // If this is our device then send the hid request. 279 | // 280 | 281 | HANDLE file = OpenDeviceInterface(hardwareDeviceInfo, &deviceInterfaceData, vendorID, productID, myUsagePage, myUsage); 282 | 283 | if (file != INVALID_HANDLE_VALUE) 284 | { 285 | SetupDiDestroyDeviceInfoList(hardwareDeviceInfo); 286 | return file; 287 | } 288 | 289 | // 290 | //device was not found so loop around. 291 | // 292 | 293 | } 294 | 295 | printf("Failure: Could not find our HID device \n"); 296 | 297 | SetupDiDestroyDeviceInfoList(hardwareDeviceInfo); 298 | 299 | return INVALID_HANDLE_VALUE; 300 | } 301 | 302 | HANDLE 303 | OpenDeviceInterface( 304 | HDEVINFO hardwareDeviceInfo, 305 | PSP_DEVICE_INTERFACE_DATA deviceInterfaceData, 306 | USHORT vendorID, 307 | USHORT productID, 308 | USAGE myUsagePage, 309 | USAGE myUsage 310 | ) 311 | { 312 | PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = NULL; 313 | 314 | DWORD predictedLength = 0; 315 | DWORD requiredLength = 0; 316 | HANDLE file = INVALID_HANDLE_VALUE; 317 | 318 | SetupDiGetDeviceInterfaceDetail( 319 | hardwareDeviceInfo, 320 | deviceInterfaceData, 321 | NULL, // probing so no output buffer yet 322 | 0, // probing so output buffer length of zero 323 | &requiredLength, 324 | NULL 325 | ); // not interested in the specific dev-node 326 | 327 | predictedLength = requiredLength; 328 | 329 | deviceInterfaceDetailData = 330 | (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(predictedLength); 331 | 332 | if (!deviceInterfaceDetailData) 333 | { 334 | printf("Error: OpenDeviceInterface: malloc failed\n"); 335 | goto cleanup; 336 | } 337 | 338 | deviceInterfaceDetailData->cbSize = 339 | sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); 340 | 341 | if (!SetupDiGetDeviceInterfaceDetail( 342 | hardwareDeviceInfo, 343 | deviceInterfaceData, 344 | deviceInterfaceDetailData, 345 | predictedLength, 346 | &requiredLength, 347 | NULL)) 348 | { 349 | printf("Error: SetupDiGetInterfaceDeviceDetail failed\n"); 350 | free(deviceInterfaceDetailData); 351 | goto cleanup; 352 | } 353 | 354 | file = CreateFile(deviceInterfaceDetailData->DevicePath, 355 | GENERIC_READ | GENERIC_WRITE, 356 | FILE_SHARE_READ | FILE_SHARE_WRITE, 357 | NULL, // no SECURITY_ATTRIBUTES structure 358 | OPEN_EXISTING, // No special create flags 359 | 0, // No special attributes 360 | NULL); // No template file 361 | 362 | if (INVALID_HANDLE_VALUE == file) { 363 | printf("Error: CreateFile failed: %d\n", GetLastError()); 364 | goto cleanup; 365 | } 366 | 367 | if (CheckIfOurDevice(file, vendorID, productID, myUsagePage, myUsage)) { 368 | 369 | goto cleanup; 370 | 371 | } 372 | 373 | CloseHandle(file); 374 | 375 | file = INVALID_HANDLE_VALUE; 376 | 377 | cleanup: 378 | 379 | free(deviceInterfaceDetailData); 380 | 381 | return file; 382 | 383 | } 384 | 385 | 386 | BOOLEAN 387 | CheckIfOurDevice( 388 | HANDLE file, 389 | USHORT vendorID, 390 | USHORT productID, 391 | USAGE myUsagePage, 392 | USAGE myUsage) 393 | { 394 | PHIDP_PREPARSED_DATA Ppd = NULL; // The opaque parser info describing this device 395 | HIDD_ATTRIBUTES Attributes; // The Attributes of this hid device. 396 | HIDP_CAPS Caps; // The Capabilities of this hid device. 397 | BOOLEAN result = FALSE; 398 | 399 | if (!HidD_GetPreparsedData(file, &Ppd)) 400 | { 401 | printf("Error: HidD_GetPreparsedData failed \n"); 402 | goto cleanup; 403 | } 404 | 405 | if (!HidD_GetAttributes(file, &Attributes)) 406 | { 407 | printf("Error: HidD_GetAttributes failed \n"); 408 | goto cleanup; 409 | } 410 | 411 | if (Attributes.VendorID == vendorID && Attributes.ProductID == productID) 412 | { 413 | TpVendorID = Attributes.VendorID; 414 | 415 | if (!HidP_GetCaps(Ppd, &Caps)) 416 | { 417 | printf("Error: HidP_GetCaps failed \n"); 418 | goto cleanup; 419 | } 420 | 421 | if ((Caps.UsagePage == myUsagePage) && (Caps.Usage == myUsage)) 422 | { 423 | printf("Success: Found my device.. \n"); 424 | result = TRUE; 425 | } 426 | } 427 | 428 | cleanup: 429 | 430 | if (Ppd != NULL) 431 | { 432 | HidD_FreePreparsedData(Ppd); 433 | } 434 | 435 | return result; 436 | } 437 | 438 | BOOL 439 | HidOutput( 440 | BOOL useSetOutputReport, 441 | HANDLE file, 442 | PCHAR buffer, 443 | ULONG bufferSize 444 | ) 445 | { 446 | ULONG bytesWritten; 447 | if (useSetOutputReport) 448 | { 449 | // 450 | // Send Hid report thru HidD_SetOutputReport API 451 | // 452 | 453 | if (!HidD_SetOutputReport(file, buffer, bufferSize)) 454 | { 455 | printf("failed HidD_SetOutputReport %d\n", GetLastError()); 456 | return FALSE; 457 | } 458 | } 459 | else 460 | { 461 | if (!WriteFile(file, buffer, bufferSize, &bytesWritten, NULL)) 462 | { 463 | printf("failed WriteFile %d\n", GetLastError()); 464 | return FALSE; 465 | } 466 | } 467 | 468 | return TRUE; 469 | } --------------------------------------------------------------------------------