Jump to content
Corsair Community

How to get K95's G key presses through USB without CUE software?


ygolohcisp
 Share

Recommended Posts

How can I get the K95's G key presses through USB without CUE software?

 

The K95 shows up as these HID devices:

ID 1b1c:1b11 \\?\hid#vid_1b1c&pid_1b11&mi_01&col01#8&2dd5e1ac&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} Corsair K95 RGB Gaming Keyboard
ID 1b1c:1b11 \\?\hid#vid_1b1c&pid_1b11&mi_01&col02#8&2dd5e1ac&0&0001#{4d1e55b2-f16f-11cf-88cb-001111000030} Corsair K95 RGB Gaming Keyboard
ID 1b1c:1b11 \\?\hid#vid_1b1c&pid_1b11&mi_01&col03#8&2dd5e1ac&0&0002#{4d1e55b2-f16f-11cf-88cb-001111000030} Corsair K95 RGB Gaming Keyboard
ID 1b1c:1b11 \\?\hid#vid_1b1c&pid_1b11&mi_01&col04#8&2dd5e1ac&0&0003#{4d1e55b2-f16f-11cf-88cb-001111000030} Corsair K95 RGB Gaming Keyboard
ID 1b1c:1b11 \\?\hid#vid_1b1c&pid_1b11&mi_02#8&9fea3ea&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} Corsair K95 RGB Gaming Keyboard
ID 1b1c:1b11 \\?\hid#vid_1b1c&pid_1b11&mi_00#8&16125567&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} Corsair K95 RGB Gaming Keyboard

 

But when I try to open any one of these for reading, I get an error.

 

What's the correct way to read from it to get the G key presses? And which of these HID devices is used for them?

 

I'm already successfully sending the LED data via libusb but now I still need to get G key presses. They are sent via normal HID, right?

Link to comment
Share on other sites

I captured these packets with wireshark/UsbPcap when pressing and releasing each G key one after another (G1 to G18).

The key release packets all look the same (not key specific) for some reason, only the keypress packets are key-specific..

 

zPDffVK2QM2z9OGcpW9niw.png

 

So I have to use 0x82 for wIndex, but what should I use for bmRequestType, bRequest and wValue when calling libusb_control_transfer()?

http://libusb.sourceforge.net/api-1.0/group__syncio.html#gadb11f7a761bd12fc77a07f4568d56f38

Link to comment
Share on other sites

I tried that but it's not working. I can't open the HID device with this hidapi lib.

 

Apparently Windows doesn't allow to open keyboards/mice for security reasons:

https://github.com/signal11/hidapi/issues/294#issuecomment-345071855

 

But isn't your C# program running in userspace, not as a driver in kernel space?

 

Yes it is running in userspace. I've no idea how windows 8 handles such things but for me (win 7) it works fine, and as far as i know on win 10 it works too.

 

 

The key release packets all look the same (not key specific) for some reason, only the keypress packets are key-specific..

 

Yes, this is expected. The keyboard always sends data containing isPressed-flags for every key. Therefore after releasing a key (if you only pressed one) it's always 0 only. if you press multiple keys and release one you'll still see the remaining ones as 1.

That's what is parsed here:

https://github.com/DarthAffe/CUE.NET.Input/blob/master/Input/DeviceInput.cs#L90

 

 

So I have to use 0x82 for wIndex, but what should I use for bmRequestType, bRequest and wValue when calling libusb_control_transfer()?

Sorry i can't help with that. I've no idea how that lowlevel usb-libraries work.

Edited by Darth Affe
Link to comment
Share on other sites

Thanks for explaining the keypress msgs.

 

I also tried with the hidapi lib again, but apparently userspace programs on Windows are not allowed (by the OS) to read HID devices that are keyboards or mice, for security reasons:

https://github.com/signal11/hidapi/issues/247#issuecomment-161152387

 

So how did you get your program to open the keyboard's HID device while running in userspace (without having to override the Corsair HID driver)? :)

Link to comment
Share on other sites

So how did you get your program to open the keyboard's HID device while running in userspace (without having to override the Corsair HID driver)? :)

 

I've no idea. Like I said this might be a win 8 issue.

The only thing I can do to help with this is to give you the application I used to test back when I worked with this. I works 100% for me (just tested it again). If it doesn't work for you it's likely a os problem, if it does you're doing something wrong :p

 

Just run it, open a notepad and press some keys. It should light up the pressed keys on the keyboard. (Don't press keys in the console-window of the application except you want to close it.)

InputTest.zip

Link to comment
Share on other sites

  • 2 weeks later...
For me InputTest.exe crashes at start with an exception, even after I installed .NET and the VC++ redistributable :(

This has been on my back burner, but I've been dying to try it

 

just tried it and it works on win10x64, but again as he said, you need to be inside a notepad. I had 2.20.xxx installed with the SDK enabled.

 

If i was outside of notepad and hit the G keys it does not necessarily work and may crash, but ultimately, i know this is going to be a cool thing...I'm excited to look at how Darth Affe set this up and go from there

Link to comment
Share on other sites

The only thing I can do to help with this is to give you the application I used to test back when I worked with this. I works 100% for me (just tested it again). If it doesn't work for you it's likely a os problem, if it does you're doing something wrong :p

 

Could you maybe upload the source of InputTest.exe somewhere so I can translate it into the language I'm using and try if that works? :)

I'd really appreciate it.

Link to comment
Share on other sites

Could you maybe upload the source of InputTest.exe somewhere so I can translate it into the language I'm using and try if that works? :)

I'd really appreciate it.

 

hey did you run it with CUE running and FULLY Loaded up? I tried again without cue running, got an exception in the command line. I tried running after running CUE but before loading all the devices and got a different error.

 

Maybe just try again? I am going to try to take a closer look at what this code does and see if I can extrapolate for my purposes. I really just was looking for a way to fill in the gaps with CUE, like decision making macros on the Gkeys that can in and of themselves test for modifiers being pressed or something else happening at the same time to make them context sensitive/intelligent. Right now I have to often remap these G keys to an autohotkey recognizable key which limits my other possibilities on the keyboard at the same time (there are only so many Fkeys and in photoshop for example you have to sort of reserve fkeys and modifier states for actions - it gets crowded quickly).

 

I think this would be the fix, bc there is clearly a way to see when the G keys are depressed...and he showed us how so i can go from here.

Link to comment
Share on other sites

If you only need the G-keys you can use the original SDK or CUE.NET since there is an event for them for a few weeks now.

 

I think I will try that. I did download your repository last night for InputTest / SimpleDevTest and got it to compile after some fiddling on Visual Studio 2017, I had to comment out a couple lines and I didn't get too far.

 

It asked me to use the more modern .net targets which seemed to work finally when i opened the input project before the simple dev test. It wasn't getting where the namespace came from for CUE.Net.Input. The .net4.5 targets lower than version 4.5.2 don't seem to be able to be installed in VS Community '17, but it seems to play ok with the newer version.

 

 

In the meantime, while I will definitely look into the Cue.net original SDK, i am going to hotfix this for myself a different route by making 18 executables in AHK that are linked to in CUE on each G key. The script/exe polls for whether a modifier key is pressed at execution and save out the keystate into an ini and i can read that back in with my primary script. Tricky part is telling Script 1 when to update and fire a hotkey. I suppose I could poll every second or so the same ini file for state changes and erase it after each iteration, but that is slow and even on an nvme its probably not a great method. I'm probably going to see if i can just save the keystate in memory someplace and read it back in periodically (which is i am sure less taxing on the system and should be faster).

 

Obviously this is hacky and not the best approach, I am sure the SDK will prove to be a better method.

Link to comment
Share on other sites

So for anyone else looking to do this, and I am sure there are many, I have provided the following:

 

http://smithany.com/register_callback.exe

which is basically a modified version of the newer example D.A. suggested I pursue earlier:

http://smithany.com/register_callback.cpp

 

(sorry that's probably a terrible way to go about it, but as i understand c++ doesn't love switch statements based on strings in a straightforward manner - i could probably switch based on the key rather than the string compared keylist at the top...might be better method but I am out of my depth)

 

Also, my autohotkey that runs along side it is:

http://smithany.com/suitableRemap.ahk

 

Which so far, although largely untested, appears to not stomp keyboard stuff as I gave it a quick brown fox jumps over the lazy dog and the top rows and fkeys and got no alerts...ymmv and I may have a problem in the future using these reserve keycodes, but it seems to be a working proof of concept that will be helpful for me.

Link to comment
Share on other sites

:D:If anybody cares, my code was bad as posted and linked above...down and up events were both triggering the fake keypress.

 

That said, there were two ways to fix, one you could do this hacky thing in AHK:

global ultraProfile:=0
global noRepeat:=1
SC0C1::
;MsgBox G1
if(ultraProfile && !noRepeat){
	Send, stuff
	noRepeat:=!noRepeat
	Sleep,500
}
noRepeat:=!noRepeat
Return
SC0EA::
;MsgBox M10
ultraProfile:=1
noRepeat:=0
return

 

or better yet what appears to work is also check for the boolean in register_callback with the short circuit AND with the keyId (which is also better i assume than my earlier posted character string comparison)

	if(keyId == CorsairKeyKb_G1 && pressed)
	{
		keybd_event(VK_KANA, 0xC1, 0, 0);
	}

and so on

Edited by hastegag
typo
Link to comment
Share on other sites

Quick follow up to this, assuming you always have certain keys that are statically lit a certain way, you can tell from the SDK what profile you have currently active by polling a specific key.

 

Might not seem that helpful, but if you do have macros outside of CUE, it can be a good error checking process to ensure the profile is active if they either work hand in hand or if they might otherwise stomp each other etc.

 

Below is an excerpt from the register_callback.cpp provided in the SDK, with some basic edits. Looks for all red brightness on the Q key in this example, polled for when G1 is hit:

 

		auto ledId = CLK_Q;
	CorsairLedColor ledColor = {ledId, 0, 0, 0};
	CorsairGetLedsColors(1, &ledColor); //fills the rgb above
	if (keyId == CorsairKeyKb_G1 && pressed)//keyIdStrings.at(keyId) == "CorsairKeyKb_G1")
	{
		if (ledColor.r == 255)
		{

			keybd_event(VK_CONTROL, 0x11, 0, 0); // PRESS CONTROL
			keybd_event(VK_KANA, 0xC1, 0, 0); // PRESS FUNKY
			keybd_event(VK_CONTROL, 0x11, KEYEVENTF_KEYUP, 0); //KEYEVENTF_KEYUP DWORD FLAG TO RELEASE CTRL
		}
		else
		{
			keybd_event(VK_KANA, 0xC1, 0, 0);
		}
	}

 

The above would send control + some key recognized elsewhere. I realized I have more than 6 profiles, and since I'd be potentially using modifiers inside of those profiles and recognizing them elsewhere, sending ctrl/shift/alt modified offkeyboard keypresses is not that constructive. Better alternative is to write to a file and read it back in when called to detect for the active profile in CUE. I guess you could pass this through memory, but i'm not sure how to do that other than polling for the PID and then using a consistent offset maybe). Better approach is more or less below:

 

#include <fstream>
...
		if (ledColor.r == 255)
		{
			//WRITE to FILE OR MEMORY as you may have more than a half dozen profiles
			std::ofstream myfile;
			myfile.open("Profile.txt");
			myfile << "Default";
			myfile.close();
		}
		else
		{
			keybd_event(VK_KANA, 0xC1, 0, 0);
		}

 

 

Refactored code for my purposes at:

http://www.smithany.com/register_callback.cpp

 

Made a function for outputting the active profile in CUE, but with less lines etc

Edited by hastegag
better method / code refactored again
Link to comment
Share on other sites

  • 4 months later...

Not sure if anyone cares, but I finally figured out how to get AutoHotkey to read from a memory address to avoid hitting the harddrive a million extra times a year to talk from CPP to Autohotkey without writing a custom DLL that's registered (think that works too). There are a million examples out there, but I swear I was doing everything correctly and tried a hundred different ways and it just wasn't working reliably, but the below works:

 

Example below:

global profilePointer:=0
;other code
ReadMemory(MADDRESS,PID)
{
VarSetCapacity(MVALUE,4,0)
ProcessHandle := DllCall("OpenProcess", "Int", 24, "Char", 0, "UInt", PID, "UInt")
DllCall("ReadProcessMemory", "UInt", ProcessHandle, "Ptr", MADDRESS, "Ptr", &MVALUE, "Uint",4)
Loop 4
result += *(&MVALUE + A_Index-1) << 8*(A_Index-1)
return, result
}
ProfileName(readNumber)
{
sendBackProfileName:=""
if(readNumber=0)
{
	sendBackProfileName:="Default"
}
if(readNumber=1)
{
	sendBackProfileName:="UltraMod"
}
if(readNumber=2)
{
	sendBackProfileName:="Sketchup"
}
if(readNumber=3)
{
	sendBackProfileName:="ACrucial***"
}
if(readNumber=4)
{
	sendBackProfileName:="Dreamweaver"
}
if(readNumber=5)
{
	sendBackProfileName:="Outlook"
}
if(readNumber=6)
{
	sendBackProfileName:="Word"
}
if(readNumber=7)
{
	sendBackProfileName:="Photoshop"
}
if(readNumber=8)
{
	sendBackProfileName:="Bridge"
}
if(readNumber=9)
{
	sendBackProfileName:="Illustrator"
}
if(readNumber=10)
{
	sendBackProfileName:="Excel"
}
return sendBackProfileName
}
getCurProfile()
{
Process, Exist, register_callback.exe
PID:=ErrorLevel
if(profilePointer=0)
{
	FileRead, profilePointer, C:\CUESDK\examples\register_callback\x64\Release\Profile.txt
	;MsgBox here? ;insurance on writes
profilePointer := "0x"+profilePointer ;frontloaded zeros are ok but need to register string as hex
}
Return ProfileName(ReadMemory(profilePointer,PID))
}
;othercode
SC0C1::
;MsgBox G1
profile := getCurProfile()
if(profile == "Photoshop"){
	Click 390,42 ;Align Top G1
}
Return
^SC0C1::
;MsgBox Ctrl G1
       ;do something else
Return

alongside

www.smithany.com/register_callbackImproved.cpp

 

A lot to fix still, need to figure out how to clock how long something was depressed and figure in a faux repeat rate for G and M keys, thinking the Chrono include can clock the time between one thing and another.

 

Also need to maybe setup a side profile polling periodically even if no key is pressed if I end up toying with some RGB integration (not a priority for me atm, as I dont currently have a lot of supporting non corsair hardware tho)

Edited by hastegag
Important correction / clarification in AHK code
Link to comment
Share on other sites

 Share

×
×
  • Create New...