Source file: /~heha/Mikrocontroller/LEDs/WS2812.zip/software/WS2812.cpp

/* Ansteuerung einer Kette von RGB-Leuchtdioden mit WS2812-Protokoll via HID-USB
 * h#s 10/15
 */

#include <windows.h>
#include <windowsx.h>
#include <shlwapi.h>
#include <commctrl.h>
#include <setupapi.h>
#include <devguid.h>
extern "C"{
#include <hidsdi.h>
#include <hidpi.h>
}

#pragma intrinsic(memcpy)

static struct TConfig{
 POINTS WinPos;		// Fensterposition des Dialogs
 BYTE HidNo;		// Nummer des HID-USB, 0 = erstes
 BYTE Idx;		// LED-Nummer
 COLORREF custcolors[16];
}Config;

HINSTANCE hInstance;

#define elemof(x) (sizeof(x)/sizeof((x)[0]))
#define T(x) TEXT(x)

/******************************
 * Hardwarenahe Kommunikation *
 ******************************/

static SP_CLASSIMAGELIST_DATA ild;

static struct WS2812{
 HANDLE hDev;
 PHIDP_PREPARSED_DATA pd;
 HIDP_CAPS hidcaps;
 BYTE *report;		// genügend großer Transferpuffer
 void Enum(HWND hCombo);
 void Open() {Enum(0);getfeature();}
 void Close();
 int getfeature();
 int setfeature();
 int setfeature(int);
 COLORREF getRGB(int);
 BYTE getRGB(int,int);
 void setRGB(int,COLORREF);
 void setRGB(int,int,BYTE);
 int end() const;
 void limit(BYTE&) const;
}ws2812;

void WS2812::Enum(HWND hCombo) {
 COMBOBOXEXITEM cbei;
 TCHAR buf[12];
 int n;
 if (hCombo) {
  cbei.mask=CBEIF_TEXT|CBEIF_IMAGE|CBEIF_SELECTEDIMAGE;
  cbei.pszText=buf;
  cbei.iItem=0;
  SetupDiGetClassImageIndex(&ild,(LPGUID)&GUID_DEVCLASS_HIDCLASS,&cbei.iImage);
  cbei.iSelectedImage=cbei.iImage;
  ComboBox_ResetContent(hCombo);
  SetWindowText(hCombo,NULL);
 }else{
  n=Config.HidNo+1;
 }

 GUID hidGuid;
 HidD_GetHidGuid(&hidGuid);
 HDEVINFO devs=SetupDiGetClassDevs(&hidGuid,NULL,0,DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
 if (devs!=INVALID_HANDLE_VALUE) {
  SP_DEVICE_INTERFACE_DATA di;
  for (DWORD i=0; di.cbSize=sizeof di,SetupDiEnumDeviceInterfaces(devs,0,&hidGuid,i,&di); i++) {
   struct{
    SP_DEVICE_INTERFACE_DETAIL_DATA d;
    TCHAR space[MAX_PATH];
   }d;
   d.d.cbSize=sizeof d.d;
   if (SetupDiGetDeviceInterfaceDetail(devs,&di,&d.d,sizeof d,NULL,NULL)) {
    HANDLE h=CreateFile(d.d.DevicePath,GENERIC_READ|GENERIC_WRITE,
      FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);
    if (h!=INVALID_HANDLE_VALUE) {
     PHIDP_PREPARSED_DATA pd;
     if (HidD_GetPreparsedData(h,&pd)) {
      HIDP_CAPS hidcaps;
      if ((int)HidP_GetCaps(pd,&hidcaps)>=0
      && hidcaps.InputReportByteLength==0
      && hidcaps.OutputReportByteLength==5
      && hidcaps.FeatureReportByteLength>=8) {
       if (hCombo) {
        wsprintf(buf,T("HID%u"),cbei.iItem);
        SendMessage(hCombo,CBEM_INSERTITEM,0,(LPARAM)&cbei);
        if (cbei.iItem==Config.HidNo) ComboBox_SetCurSel(hCombo,cbei.iItem);
        cbei.iItem++;
       }else if (!--n) {	// gewünschtes passendes PnPSA gefunden
        hDev=h;
        this->pd=pd;
        this->hidcaps=hidcaps;
        report=(BYTE*)LocalAlloc(LPTR,hidcaps.FeatureReportByteLength);
        goto raus;
       }
      }
      HidD_FreePreparsedData(pd);
     }
     CloseHandle(h);
    }
   }
  }
raus:
  SetupDiDestroyDeviceInfoList(devs);
 }
}

int WS2812::getfeature() {
 return HidD_GetFeature(hDev,report,hidcaps.FeatureReportByteLength);
}

int WS2812::setfeature() {
 return HidD_SetFeature(hDev,report,hidcaps.FeatureReportByteLength);
}

int WS2812::setfeature(int idx) {
 BYTE buf[5];
 buf[0]=0;
 memcpy(1+buf,1+report+idx*3,3);
 buf[4]=idx;
 return HidD_SetOutputReport(hDev,buf,5);
}

void WS2812::Close() {
 if (report) {LocalFree(report); report=NULL;}
 if (pd) {HidD_FreePreparsedData(pd); pd=NULL;}
 if (hDev) {CloseHandle(hDev); hDev=0;}
}

COLORREF WS2812::getRGB(int i) {
 return RGB(getRGB(i,0),getRGB(i,1),getRGB(i,2));
}
BYTE WS2812::getRGB(int i,int c) {
 if (!(c&2)) c^=1;	// Reihenfolge GRB
 return report?report[1+i*3+c]:0;
}
void WS2812::setRGB(int i,COLORREF c) {
 setRGB(i,0,GetRValue(c));
 setRGB(i,1,GetGValue(c));
 setRGB(i,2,GetBValue(c));
}
void WS2812::setRGB(int i,int c,BYTE b) {
 if (!(c&2)) c^=1;	// Reihenfolge GRB
 if (report) report[1+i*3+c]=b;
}
int WS2812::end() const{
 return hidcaps.FeatureReportByteLength/3-1;
}
void WS2812::limit(BYTE&b) const{
 if (b>end()) b=end();
}

/************************
 * Konfigurationsdialog *
 ************************/

// Lädt Konfigurationsdaten aus Registrierung
static void LoadConfig() {
 HKEY key;
 if (!RegOpenKeyEx(HKEY_CURRENT_USER,T("Software\\h#s\\WS2812"),
   0,KEY_QUERY_VALUE,&key)) {
  DWORD size=sizeof(Config);
  RegQueryValueEx(key,T("Config"),NULL,NULL,(LPBYTE)&Config,&size);
  RegCloseKey(key);
 }
}

// Speichert Konfigurationsdaten in Registrierung
static void SaveConfig(HWND Wnd) {
 HKEY key;
 if (RegCreateKeyEx(HKEY_CURRENT_USER,T("Software\\h#s\\WS2812"),
   0,NULL,REG_OPTION_NON_VOLATILE,KEY_SET_VALUE,NULL,&key,NULL)) return;
 TCHAR s[128];
 RegSetValue(key,NULL,REG_SZ,s,GetWindowText(Wnd,s,elemof(s))*sizeof(TCHAR));
 RegSetValueEx(key,T("Config"),0,REG_BINARY,(LPBYTE)&Config,sizeof(Config));
 RegCloseKey(key);
}

static INT_PTR CALLBACK SetupDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
 switch (Msg) {
  case WM_INITDIALOG: {
   ild.cbSize=sizeof(ild);
   SetupDiGetClassImageList(&ild);
   SendDlgItemMessage(Wnd,110,CBEM_SETIMAGELIST,0,(LPARAM)ild.ImageList);
   SendMessage(Wnd,WM_TIMER,2,0);
  }return TRUE;

  case WM_COMMAND: switch (LOBYTE(wParam)) {
   case IDOK:
    Config.HidNo=(BYTE)SendDlgItemMessage(Wnd,110,CB_GETCURSEL,0,0);
   case IDCANCEL: {
    EndDialog(Wnd,wParam);
   }break;
  }break;

  case WM_TIMER: {
   KillTimer(Wnd,wParam);
   ws2812.Enum(GetDlgItem(Wnd,110));
  }break;

  case WM_DEVICECHANGE: {
   SetTimer(Wnd,1,1500,NULL);
  }break;

  case WM_DESTROY: {
   SetupDiDestroyClassImageList(&ild);
  }break;
 }
 return FALSE;
}

/***************
 * Haupfenster *
 ***************/
static void RetrieveWinPos(HWND Wnd) {
 WINDOWPLACEMENT wp;
 wp.length=sizeof(wp);
 GetWindowPlacement(Wnd,&wp);
 Config.WinPos.x=(short)wp.rcNormalPosition.left;
 Config.WinPos.y=(short)wp.rcNormalPosition.top;
}

static void setRGB(HWND Wnd, int i, BYTE b, BYTE mask=0x83) {
 if (mask&1)	// Slider
   SendDlgItemMessage(Wnd,20+i,TBM_SETPOS,TRUE,255-b);
 if (mask&2)	// Edit + UpDown
   SetDlgItemInt(Wnd,24+i,b,FALSE);
 if (mask&0x80 && ws2812.report) {	// Ausgabe
  ws2812.setRGB(Config.Idx,i,b);
  ws2812.setfeature(Config.Idx);
 }
}

static void open(HWND Wnd) {
 ws2812.Open();
 SendDlgItemMessage(Wnd,15,UDM_SETRANGE32,0,ws2812.end());
 ws2812.limit(Config.Idx);
 SetDlgItemInt(Wnd,14,Config.Idx,FALSE);
}

INT_PTR CALLBACK MainDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam){
 switch (Msg) {
  case WM_INITDIALOG: {
   LoadConfig();
   HICON ico=LoadIcon(hInstance,MAKEINTRESOURCE(100));
   SetClassLongPtr(Wnd,GCLP_HICON,(LONG_PTR)ico);
   SetClassLongPtr(Wnd,GCLP_HICONSM,(LONG_PTR)ico);
   SetWindowPos(Wnd,0,Config.WinPos.x,Config.WinPos.y,0,0,SWP_NOSIZE|SWP_NOZORDER);
   STARTUPINFO si;
   si.cb=sizeof(si);
   GetStartupInfo(&si);
   ShowWindow(Wnd,si.wShowWindow);
   for (UINT i=0; i<3; i++) {
    SendDlgItemMessage(Wnd,20+i,TBM_SETRANGE,FALSE,MAKELONG(0,255));	// Slider-Bereich
    SendDlgItemMessage(Wnd,20+i,TBM_SETTICFREQ,15,0);
    SendDlgItemMessage(Wnd,28+i,UDM_SETRANGE32,0,255);
   }
   open(Wnd);
  }return TRUE;
  
  case WM_VSCROLL: {
   UINT id=GetWindowID((HWND)lParam);
   if (id<16) break;
   if (id<24) setRGB(Wnd,id&3,(BYTE)(255-SendMessage((HWND)lParam,TBM_GETPOS,0,0)),0x82);
  }break;

  case WM_NOTIFY: {
   NMHDR*h=(NMHDR*)lParam;
   if (h->idFrom>=16 && h->code==UDN_DELTAPOS) {
    NMUPDOWN*ud=(NMUPDOWN*)h;
    setRGB(Wnd,h->idFrom&3,ud->iPos+ud->iDelta,0x81);
   }
  }break;

  case WM_COMMAND: switch (wParam) {
   case MAKELONG(14,EN_UPDATE): {
    Config.Idx=(BYTE)GetDlgItemInt(Wnd,14,NULL,FALSE);
    ws2812.limit(Config.Idx);
    for (UINT i=0; i<3; i++) setRGB(Wnd,i,ws2812.getRGB(Config.Idx,i),0x03);
   }break;
   case MAKELONG(24,EN_UPDATE):
   case MAKELONG(25,EN_UPDATE):
   case MAKELONG(26,EN_UPDATE): if (SendMessage((HWND)lParam,EM_GETMODIFY,0,0)) {
    BYTE b=(BYTE)GetDlgItemInt(Wnd,LOBYTE(wParam),NULL,FALSE);
    setRGB(Wnd,LOBYTE(wParam)&3,b,0x81);
    SendMessage((HWND)lParam,EM_SETMODIFY,FALSE,0);
   }break;
   case 1: {
    CHOOSECOLOR cs;
    cs.lStructSize=sizeof cs;
    cs.hwndOwner=Wnd;
    cs.lpCustColors=Config.custcolors;
    cs.Flags=CC_ANYCOLOR|CC_FULLOPEN;
    if (ws2812.report) {
     cs.rgbResult=ws2812.getRGB(Config.Idx);
     cs.Flags|=CC_RGBINIT;
    }
    if (ChooseColor(&cs) && ws2812.report) {
     ws2812.setRGB(Config.Idx,cs.rgbResult);
     SendMessage(Wnd,WM_COMMAND,MAKELONG(14,EN_UPDATE),0);
     ws2812.setfeature(Config.Idx);
    }
   }break;
   case 11: if (DialogBox(hInstance,MAKEINTRESOURCE(101),Wnd,SetupDlgProc)==IDOK) {
    ws2812.Close();
    open(Wnd);
   }break;
   case 2: {
    ws2812.Close();
    RetrieveWinPos(Wnd);
    SaveConfig(Wnd);
    EndDialog(Wnd,wParam);
   }break;
  }break;

  case WM_ENDSESSION: if (wParam) {
   RetrieveWinPos(Wnd);
   SaveConfig(Wnd);
  }break;

 }
 return FALSE;
}

INT_PTR CALLBACK WinMainCRTStartup() {
 hInstance=GetModuleHandle(NULL);
 InitCommonControls();
 ExitProcess((UINT)DialogBox(hInstance,MAKEINTRESOURCE(100),0,MainDlgProc));
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded