Skip to content

Commit

Permalink
Merge pull request #3 from HunterZ/razer
Browse files Browse the repository at this point in the history
Razer
  • Loading branch information
HunterZ authored Oct 30, 2018
2 parents 3159de0 + 8e9aa9d commit 2335fd0
Show file tree
Hide file tree
Showing 23 changed files with 2,373 additions and 17 deletions.
4 changes: 0 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@
*.tlog
*.user
.vs
CUESDK*
Corsair*.h
Debug/
LFX2.h
LogitechLEDLib.*
Release/
UniLight*.zip
UniLight.exe
Expand Down
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# UniLight
Synchronize Corsair CUE, Dell/Alienware LightFX/AlienFX, and/or Logitech Gaming LED devices with Windows accent/colorization color
Synchronize Corsair CUE, Dell/Alienware LightFX/AlienFX, Logitech LED, and/or Razer Chroma devices with Windows accent/colorization color

## Demo
https://youtu.be/I3xfJtbLqgA
Expand All @@ -10,21 +10,21 @@ I have an Alienware 17R3 laptop and have been using AlienFX WinTheme to sync the
## Usage
Simply launch UniLight and forget about it. Configure Windows to launch it at startup if you want.

UniLight appears as a system tray icon that looks like a color wheel, and listens for changes to accent color or other conditions that may affect LED color synchronization. On startup and whenever any relevant changes are detected, it calls the LightFX/AlienFX and Logitech Gaming LED APIs to set all RGB LEDs to the current accent color.
UniLight appears as a system tray icon that looks like a color wheel, and listens for changes to accent color or other conditions that may affect LED color synchronization. On startup and whenever any relevant changes are detected, it calls the various APIs to set all RGB LEDs to the current accent color.

Hovering the mouse cursor over the UniLight tray icon will produce a tooltip containing status information, including the current and last accent colors, and whether they were successfully applied to Alienware and/or Logitech devices.
Hovering the mouse cursor over the UniLight tray icon will produce a tooltip containing status information, including the current and last accent colors, and whether they were successfully applied to each of the vendor APIs.

Right-clicking the tray icon will bring up a context menu with self-explanatory selections, while left-clicking performs a manual color synchronization.

## Troubleshooting
UniLight comes with DLLs for the various supported APIs. These are mainly included so that users without software from one or more of the supported vendors can still use UniLight. If you have trouble with the included DLLs, it's highly recommended that you try deleting/renaming the included DLLs for which you may have system-level counterparts installed.
UniLight may comes with a Corsair CUE DLL because they make me link a loader library into UniLight that prevents it from working at all when the DLL is not present. If you are a Corsair CUE user and have issues, you may want to try renaming/deleting the included DLL to ensure that the one included with your driver version is used instead.

Dell/Alienware LightFX/AlienFX DLLs are not included, because the fact that that SDK requires that I manually load the DLL anyway means that I can just disable support if the DLL is not available at the OS level.
DLLs for the other APIs are not included. UniLight simply skips over any APIs for which it fails to load/initialize a DLL.

## System Requirements
I'm not 100% sure about these. I tried to implement the API access in such a way that it will fail gracefully if a hardware vendor API is not supported on your system. UniLight will also attempt to re-apply the color on every color change, so that there is some hope of avoiding having to restart UniLight if relevant peripherals are (re)connected after UniLight has already been launched. I haven't tried to make it any more aggressive because I have to poll both APIs (neither provides a notification callback mechanism) and I don't want UniLight to have a noticeable performance impact on gaming or other tasks.
I'm not 100% sure about these. I tried to implement the API access in such a way that it will fail gracefully if a hardware vendor API is not supported on your system. UniLight will also attempt to re-apply the color on every color change, so that there is some hope of avoiding having to restart UniLight if relevant peripherals are (re)connected after UniLight has already been launched. I haven't tried to make it any more aggressive because I have to poll all APIs (none provide a notification callback mechanism) and I don't want UniLight to have a noticeable performance impact on gaming or other tasks.

UniLight should load the AlienFX and/or Logitech Gaming LED DLLs that you have installed as part of Alienware Command Center (minimum version unknown; I'm currently on 4.5.19) and Logitech Gaming Software (version 8.55 or higher required for LED support), respectively. Also, the binary distribution of this program is 32-bit for maximum compatibility (I'm running it on 64-bit systems, so I know it works there).
UniLight should load the AlienFX, Logitech Gaming LED, and/or Razer Chroma DLLs that you have installed as part of Alienware Command Center (minimum version unknown; I'm currently on 4.5.19), Logitech Gaming Software (version 8.55 or higher required for LED support), and/or Razer Synapse (tested with 3.3.1018.10194 / Chroma SDK 2.21.1), respectively. Also, the binary distribution of this program is 32-bit for maximum compatibility (I'm running it on 64-bit systems, so I know it works there).

As of version 1.1, UniLight is completely event-driven and no longer polls the Windows accent color on a timer. The result should be extremely minimal CPU usage.

Expand All @@ -34,8 +34,9 @@ This project was created with:
* Alienware AlienFX 1.0 SDK (formerly Dell LightFX): http://www.dell.com/support/home/us/en/19/drivers/driversdetails?driverId=T5GGP
* Corsair CUE SDK (Protocol version 4): http://forum.corsair.com/v3/showthread.php?t=156813
* Logitech LED Illumination SDK: https://www.logitechg.com/en-us/developers
* Razer Chrome SDK: https://developer.razer.com/

## License
All code contained in this repository and binaries built from it are covered by the MIT open source license. See the LICENSE file for details. Usage of this code must be attributed to GitHub user HunterZ.

Included DLLs are owned by the hardware vendor companies (Corsair, Dell/Alienware, Logitech).
Included DLLs are owned by the hardware vendor companies (Corsair, Dell/Alienware, Logitech, Razer). I assert no rights or support responsibilities for these.
2 changes: 1 addition & 1 deletion UniLight/CUEUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#include "CUEUtil.h"

#include "CUESDK.h"
#include "cots/corsair/CUESDK.h"
#include <tchar.h>
#include <vector>

Expand Down
2 changes: 1 addition & 1 deletion UniLight/LFXUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#include "LFXUtil.h"

#include "LFX2.h"
#include "cots/dell/LFX2.h"
#include <tchar.h>
#include <windows.h>

Expand Down
2 changes: 0 additions & 2 deletions UniLight/LFXUtil.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ namespace LFXUtil

virtual ~LFXUtilC();

// set LFX color to given RGB value
// returns true on success, false on failure
ResultT SetLFXColor(unsigned char red, unsigned char green, unsigned char blue);
};
}
2 changes: 1 addition & 1 deletion UniLight/LLEDUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#include "LLEDUtil.h"

#include "LogitechLEDLib.h"
#include "cots/logitech/LogitechLEDLib.h"
#include <tchar.h>

namespace
Expand Down
11 changes: 11 additions & 0 deletions UniLight/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ typedef struct _iobuf FILE;
#include "CUEUtil.h"
#include "LFXUtil.h"
#include "LLEDUtil.h"
#include "RZCUtil.h"
#include "resource.h"
#include "UniLight.h"

Expand Down Expand Up @@ -50,16 +51,19 @@ namespace
CUEUtil::CUEUtilC cueUtil;
LFXUtil::LFXUtilC lfxUtil;
LLEDUtil::LLEDUtilC llledUtil;
RZCUtil::RZCUtilC rzcUtil;
COLORREF lastColor(0);
ResultT cueStatus(false, _T("Not yet initialized"));
ResultT lfxStatus(false, _T("Not yet initialized"));
ResultT lledStatus(false, _T("Not yet initialized"));
ResultT rzcStatus(false, _T("Not yet initialized"));
const std::wstring sSuccess(_T("SUCCESS"));
const std::wstring sFailure(_T("FAILURE"));
const std::wstring sIndent2(_T("\n\t\t"));
const std::wstring sCUEStatus(_T("\n\n\tCorsair CUE:"));
const std::wstring sLFXStatus(_T("\n\n\tDell/Alienware AlienFX/LightFX:"));
const std::wstring sLLEDStatus(_T("\n\n\tLogitech LED:"));
const std::wstring sRZCStatus(_T("\n\n\tRazer Chroma:"));
const wchar_t cSuccess(0x2714); // check mark
const wchar_t cFailure(0x2716); // cross mark
}
Expand Down Expand Up @@ -108,6 +112,9 @@ void ShowStatus(HWND hwnd)
status += sLLEDStatus
+ sIndent2 + (lledStatus.first ? sSuccess : sFailure)
+ sIndent2 + lledStatus.second;
status += sRZCStatus
+ sIndent2 + (rzcStatus.first ? sSuccess : sFailure)
+ sIndent2 + rzcStatus.second;
MessageBox(hwnd, status.c_str(), _T("UniLight detailed status"), NULL);
}

Expand All @@ -126,6 +133,9 @@ void UpdateColor(const COLORREF curColor)
// set Logitech LED color
lledStatus = llledUtil.SetLLEDColor(red, green, blue);

// set Razer Chroma color
rzcStatus = rzcUtil.SetRZCColor(red, green, blue);

// set tooltip
std::wstringstream s;
s << "UniLight status:";
Expand All @@ -134,6 +144,7 @@ void UpdateColor(const COLORREF curColor)
s << "\nCorsCUE: " << (cueStatus.first ? cSuccess : cFailure);
s << "\nLightFX: " << (lfxStatus.first ? cSuccess : cFailure);
s << "\nLogiLED: " << (lledStatus.first ? cSuccess : cFailure);
s << "\nRzrChrm: " << (rzcStatus.first ? cSuccess : cFailure);
StringCchCopy(notifyIconData.szTip, sizeof(notifyIconData.szTip), s.str().c_str());
Shell_NotifyIcon(NIM_MODIFY, &notifyIconData);

Expand Down
222 changes: 222 additions & 0 deletions UniLight/RZCUtil.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
// UniLight by HunterZ

#include "RZCUtil.h"

#include <tchar.h>
#include <vector>
#include <windows.h> // COLORREF, HMODULE
#include "cots/razer/RzChromaSDKDefines.h"
#include "cots/razer/RzChromaSDKTypes.h"
#include "cots/razer/RzErrors.h"

//#ifdef _WIN64
//#define CHROMASDKDLL _T("RzChromaSDK64.dll")
//#else
#define CHROMASDKDLL _T("RzChromaSDK.dll")
//#endif

namespace
{
typedef std::vector<RZEFFECTID> EffectListT;

typedef RZRESULT(*INIT)(void);
typedef RZRESULT(*UNINIT)(void);
typedef RZRESULT(*CREATEEFFECT)(RZDEVICEID DeviceId, ChromaSDK::EFFECT_TYPE Effect, PRZPARAM pParam, RZEFFECTID *pEffectId);
typedef RZRESULT(*CREATEKEYBOARDEFFECT)(ChromaSDK::Keyboard::EFFECT_TYPE Effect, PRZPARAM pParam, RZEFFECTID *pEffectId);
typedef RZRESULT(*CREATEHEADSETEFFECT)(ChromaSDK::Headset::EFFECT_TYPE Effect, PRZPARAM pParam, RZEFFECTID *pEffectId);
typedef RZRESULT(*CREATEMOUSEPADEFFECT)(ChromaSDK::Mousepad::EFFECT_TYPE Effect, PRZPARAM pParam, RZEFFECTID *pEffectId);
typedef RZRESULT(*CREATEMOUSEEFFECT)(ChromaSDK::Mouse::EFFECT_TYPE Effect, PRZPARAM pParam, RZEFFECTID *pEffectId);
typedef RZRESULT(*CREATEKEYPADEFFECT)(ChromaSDK::Keypad::EFFECT_TYPE Effect, PRZPARAM pParam, RZEFFECTID *pEffectId);
typedef RZRESULT(*CREATECHROMALINKEFFECT)(ChromaSDK::ChromaLink::EFFECT_TYPE Effect, PRZPARAM pParam, RZEFFECTID *pEffectId);
typedef RZRESULT(*SETEFFECT)(RZEFFECTID EffectId);
typedef RZRESULT(*DELETEEFFECT)(RZEFFECTID EffectId);
typedef RZRESULT(*REGISTEREVENTNOTIFICATION)(HWND hWnd);
typedef RZRESULT(*UNREGISTEREVENTNOTIFICATION)(void);
typedef RZRESULT(*QUERYDEVICE)(RZDEVICEID DeviceId, ChromaSDK::DEVICE_INFO_TYPE &DeviceInfo);

INIT Init(NULL);
UNINIT UnInit(NULL);
CREATEEFFECT CreateEffect(NULL);
CREATEKEYBOARDEFFECT CreateKeyboardEffect(NULL);
CREATEMOUSEEFFECT CreateMouseEffect(NULL);
CREATEHEADSETEFFECT CreateHeadsetEffect(NULL);
CREATEMOUSEPADEFFECT CreateMousepadEffect(NULL);
CREATEKEYPADEFFECT CreateKeypadEffect(NULL);
CREATECHROMALINKEFFECT CreateChromaLinkEffect(NULL);
SETEFFECT SetEffect(NULL);
DELETEEFFECT DeleteEffect(NULL);
QUERYDEVICE QueryDevice(NULL);

bool RZC_INITIALIZED(false);
HMODULE DLL_HANDLE(NULL);
EffectListT ACTIVE_EFFECT_LIST;

ResultT InitRZC()
{
if (RZC_INITIALIZED)
return ResultT(true, _T("Already initialized"));

// Razer is stupid and forces us to manually load the DLL and bind its functions
DLL_HANDLE = ::LoadLibrary(CHROMASDKDLL);
if (DLL_HANDLE == NULL)
return ResultT(false, _T("LoadLibrary() failed"));

INIT Init((INIT)::GetProcAddress(DLL_HANDLE, "Init"));
if (Init == NULL)
return ResultT(false, _T("Failed to find Init() in DLL"));

if (Init() != RZRESULT_SUCCESS)
return ResultT(false, _T("DLL Init() failed"));

CreateEffect = (CREATEEFFECT)::GetProcAddress(DLL_HANDLE, "CreateEffect");
CreateKeyboardEffect = (CREATEKEYBOARDEFFECT)::GetProcAddress(DLL_HANDLE, "CreateKeyboardEffect");
CreateMouseEffect = (CREATEMOUSEEFFECT)::GetProcAddress(DLL_HANDLE, "CreateMouseEffect");
CreateMousepadEffect = (CREATEMOUSEPADEFFECT)::GetProcAddress(DLL_HANDLE, "CreateMousepadEffect");
CreateKeypadEffect = (CREATEKEYPADEFFECT)::GetProcAddress(DLL_HANDLE, "CreateKeypadEffect");
CreateHeadsetEffect = (CREATEHEADSETEFFECT)::GetProcAddress(DLL_HANDLE, "CreateHeadsetEffect");
CreateChromaLinkEffect = (CREATECHROMALINKEFFECT)::GetProcAddress(DLL_HANDLE, "CreateChromaLinkEffect");
SetEffect = (SETEFFECT)GetProcAddress(DLL_HANDLE, "SetEffect");
DeleteEffect = (DELETEEFFECT)GetProcAddress(DLL_HANDLE, "DeleteEffect");
if (CreateEffect == NULL)
return ResultT(false, _T("Failed to find CreateEffect() in DLL"));
if (CreateKeyboardEffect == NULL)
return ResultT(false, _T("Failed to find CreateKeyboardEffect() in DLL"));
if (CreateMouseEffect == NULL)
return ResultT(false, _T("Failed to find CreateMouseEffect() in DLL"));
if (CreateMousepadEffect == NULL)
return ResultT(false, _T("Failed to find CreateMousematEffect() in DLL"));
if (CreateKeypadEffect == NULL)
return ResultT(false, _T("Failed to find CreateKeypadEffect() in DLL"));
if (CreateHeadsetEffect == NULL)
return ResultT(false, _T("Failed to find CreateHeadsetEffect() in DLL"));
if (CreateChromaLinkEffect == NULL)
return ResultT(false, _T("Failed to find CreateChromaLinkEffect() in DLL"));
if (SetEffect == NULL)
return ResultT(false, _T("Failed to find SetEffect() in DLL"));
if (DeleteEffect == NULL)
return ResultT(false, _T("Failed to find DeleteEffect() in DLL"));

RZC_INITIALIZED = true;
return ResultT(true, _T("InitRZC() success"));
}

void DeleteEffects()
{
if (RZC_INITIALIZED)
{
for (EffectListT::iterator i(ACTIVE_EFFECT_LIST.begin());
i != ACTIVE_EFFECT_LIST.end();
++i)
{
DeleteEffect(*i);
}
}
ACTIVE_EFFECT_LIST.clear();
}
}

namespace RZCUtil
{
RZCUtilC::~RZCUtilC()
{
if (!ACTIVE_EFFECT_LIST.empty())
DeleteEffects();

if (!RZC_INITIALIZED) return;

UNINIT UnInit = (UNINIT)::GetProcAddress(DLL_HANDLE, "UnInit");
if (UnInit != NULL)
UnInit();

::FreeLibrary(DLL_HANDLE);
DLL_HANDLE = NULL;

RZC_INITIALIZED = false;
}

ResultT RZCUtilC::SetRZCColor(unsigned char red, unsigned char green, unsigned char blue)
{
if (!RZC_INITIALIZED)
{
// perform lazy initialization
// abort if initialization fails
const ResultT& result(InitRZC());
if (!result.first)
return result;
}

// clean up previously-activated effects
if (!ACTIVE_EFFECT_LIST.empty())
DeleteEffects();

const COLORREF colorRef(RGB(red, green, blue));

// Razer requires calling a separate effect creation function per device
// type because reasons
// Valid effect(s) will be gathered into a vector for subsequent activation
EffectListT effectList;
RZEFFECTID effectId;
// ChromaLink
{
ChromaSDK::ChromaLink::STATIC_EFFECT_TYPE chromaLinkParam = { colorRef };
if (CreateChromaLinkEffect(ChromaSDK::ChromaLink::CHROMA_STATIC, &chromaLinkParam, &effectId) == RZRESULT_SUCCESS)
effectList.push_back(effectId);
}
// Headset
{
ChromaSDK::Headset::STATIC_EFFECT_TYPE headsetParam = { colorRef };
if (CreateHeadsetEffect(ChromaSDK::Headset::CHROMA_STATIC, &headsetParam, &effectId) == RZRESULT_SUCCESS)
effectList.push_back(effectId);
}
// Keyboard
{
ChromaSDK::Keyboard::STATIC_EFFECT_TYPE keyboardParam = { colorRef };
if (CreateKeyboardEffect(ChromaSDK::Keyboard::CHROMA_STATIC, &keyboardParam, &effectId) == RZRESULT_SUCCESS)
effectList.push_back(effectId);
}
// Keypad
{
ChromaSDK::Keypad::STATIC_EFFECT_TYPE keypadParam = { colorRef };
if (CreateKeypadEffect(ChromaSDK::Keypad::CHROMA_STATIC, &keypadParam, &effectId) == RZRESULT_SUCCESS)
effectList.push_back(effectId);
}
// Mouse
{
ChromaSDK::Mouse::STATIC_EFFECT_TYPE mouseParam = { ChromaSDK::Mouse::RZLED_ALL, colorRef };
if (CreateMouseEffect(ChromaSDK::Mouse::CHROMA_STATIC, &mouseParam, &effectId) == RZRESULT_SUCCESS)
effectList.push_back(effectId);
}
// Mousepad
{
ChromaSDK::Mousepad::STATIC_EFFECT_TYPE mousepadParam = { colorRef };
if (CreateMousepadEffect(ChromaSDK::Mousepad::CHROMA_STATIC, &mousepadParam, &effectId) == RZRESULT_SUCCESS)
effectList.push_back(effectId);
}

// abort if nothing was created (no devices connected?)
if (effectList.empty())
return ResultT(false, _T("SetRZColor() failed to create any effects"));

// activate effects
std::size_t successCount(0);
for (EffectListT::iterator i(effectList.begin());
i != effectList.end();
++i)
{
if (SetEffect(*i) == RZRESULT_SUCCESS)
++successCount;
}

// store effects regardless of whether they activated, as we still need to
// clean them up later
ACTIVE_EFFECT_LIST.swap(effectList);

if (!successCount)
return ResultT(false, _T("SetRZCColor() failed to activate any effects"));

if (successCount < ACTIVE_EFFECT_LIST.size())
return ResultT(true, _T("SetRZCColor() partial success"));

return ResultT(true, _T("SetRZCColor() success"));
}
}
20 changes: 20 additions & 0 deletions UniLight/RZCUtil.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// UniLight by HunterZ

#pragma once

#include "UniLight.h" // ResultT

// utilities for working with Razer Chroma API
namespace RZCUtil
{
// class for working with Razer Chroma API
// no constructor because lazy initialization is used
class RZCUtilC
{
public:

virtual ~RZCUtilC();

ResultT SetRZCColor(unsigned char red, unsigned char green, unsigned char blue);
};
}
Loading

0 comments on commit 2335fd0

Please sign in to comment.