From be89fa7bf6a1257b0fc63f043a3db015634fabd2 Mon Sep 17 00:00:00 2001 From: Quantum Date: Sun, 2 Jun 2013 22:14:25 -0400 Subject: [PATCH] Initial Commit. Signed-off-by: Xiaomao Chen --- .gitignore | 8 + Makefile | 55 ++++++ include/MainWindow.hpp | 42 +++++ include/Window.hpp | 65 +++++++ include/resource.h | 2 + keyboard.bmp | Bin 0 -> 65590 bytes keyboard.ico | Bin 0 -> 15086 bytes keyboard.rc | 137 ++++++++++++++ keymap.bmp | Bin 0 -> 102478 bytes src/Keyboard.cpp | 22 +++ src/MainWindow.cpp | 395 +++++++++++++++++++++++++++++++++++++++++ src/Window.cpp | 88 +++++++++ 12 files changed, 814 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 include/MainWindow.hpp create mode 100644 include/Window.hpp create mode 100644 include/resource.h create mode 100644 keyboard.bmp create mode 100644 keyboard.ico create mode 100644 keyboard.rc create mode 100644 keymap.bmp create mode 100644 src/Keyboard.cpp create mode 100644 src/MainWindow.cpp create mode 100644 src/Window.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46885eb --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.obj +*.exe +*.res +/build +/dist +/test +/old +/*.pdb diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5ccff7e --- /dev/null +++ b/Makefile @@ -0,0 +1,55 @@ +SRCDIR=src +INCDIR=include + +CXX=cl /nologo +LD=link /nologo +CXXFLAGS=/c /O1 /I$(INCDIR) /W4 /Zi /DWIN32_LEAN_AND_MEAN /DWINVER=0x0501 /D_WIN32_WINNT=0x0501 /DUNICODE /D_UNICODE +LDFLAGS=/subsystem:windows /debug /manifest /incremental:no /opt:REF +LDFLAGS=$(LDFLAGS) "/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'" +RC=rc /nologo +RCFLAGS=/i$(INCDIR) +LIBS= + +!IFDEF DEBUG +BUILD=Debug +CXXFLAGS=$(CXXFLAGS) /DDEBUG +LDFLAGS=$(LDFLAGS) +!ELSE +BUILD=Release +LDFLAGS=$(LDFLAGS) +!ENDIF + +OUTDIR=build\$(BUILD) +DISTDIR=dist\$(BUILD) +FILES=$(OUTDIR)\Keyboard.obj \ + $(OUTDIR)\MainWindow.obj \ + $(OUTDIR)\Window.obj \ + $(OUTDIR)\keyboard.res + +all: initdir $(DISTDIR)\Keyboard.exe + +initdir: + if not exist build md build + if not exist $(OUTDIR) md $(OUTDIR) + if not exist build md dist + if not exist $(DISTDIR) md $(DISTDIR) + +$(INCDIR)\MainWindow.hpp: $(INCDIR)\Window.hpp + +$(SRCDIR)\MainWindow.cpp: $(INCDIR)\MainWindow.hpp +$(SRCDIR)\Keyboard.cpp: $(INCDIR)\MainWindow.hpp +$(SRCDIR)\Window.cpp: $(INCDIR)\Window.hpp +keyboard.rc: keyboard.ico keymap.bmp + +$(OUTDIR)\keyboard.res: keyboard.rc + $(RC) $(RCFLAGS) /fo$@ $** + +{$(SRCDIR)}.cpp{$(OUTDIR)}.obj:: + $(CXX) $(CXXFLAGS) /Fo$(OUTDIR)\ /Fd$(OUTDIR)\ $< + +{$(SRCDIR)}.c{$(OUTDIR)}.obj:: + $(CXX) $(CXXFLAGS) /Fo$(OUTDIR)\ /Fd$(OUTDIR)\ $< + +$(DISTDIR)\Keyboard.exe: $(FILES) + $(LD) /out:$@ $(LDFLAGS) $** $(LIBS) + mt.exe -nologo -manifest $@.manifest -outputresource:$@;1 && del $@.manifest || set ERRORLEVEL=0 diff --git a/include/MainWindow.hpp b/include/MainWindow.hpp new file mode 100644 index 0000000..61bb231 --- /dev/null +++ b/include/MainWindow.hpp @@ -0,0 +1,42 @@ +#pragma once +#ifndef id68C60171_0140_4DE1_B7255EFF557A74F9 +#define id68C60171_0140_4DE1_B7255EFF557A74F9 + +#include + +#include +#include +#include + +class MainWindow : public Window { +public: + virtual LPCTSTR ClassName() { return TEXT("MusicKeyboardMain"); } + static MainWindow *Create(LPCTSTR szTitle); +protected: + LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); + LRESULT OnCreate(); + LRESULT OnDestroy(); + bool Play(WPARAM wCode, LPARAM lParam, bool down); + void OnPaint(); + void PaintContent(PAINTSTRUCT *pps); + BOOL WinRegisterClass(WNDCLASS *pwc); + WORD GetQWERTYKeyCode(WORD wKeyCode); + WORD GetRealKeyCode(WORD wQWERTYCode); + + virtual HICON GetIcon(); + + HWND m_volumeLabel, m_volumeBar; + HWND m_forceLabel, m_forceBar; + HWND m_instruLabel, m_instruSelect; + int m_instrument, m_volume, m_force; + HMIDIOUT m_midi; + bool isQWERTY; + HKL hklQWERTY; +private: + HFONT hFont; + HBRUSH hBrush; + HBITMAP hKeyboardLayout; + HDC hdcKeyboard; +}; + +#endif diff --git a/include/Window.hpp b/include/Window.hpp new file mode 100644 index 0000000..ef96bda --- /dev/null +++ b/include/Window.hpp @@ -0,0 +1,65 @@ +#pragma once +#ifndef idC3714409_EABC_45DE_B5338EC60149AE87 +#define idC3714409_EABC_45DE_B5338EC60149AE87 + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +inline void MakeRect(RECT &rect, int x, int y, int cx, int cy) +{ + rect.left = x; + rect.top = y; + rect.right = x + cx; + rect.bottom = y + cy; +} + +inline void MakeRectRight(RECT &rect, int x, int y, int cx, int cy) +{ + MakeRect(rect, x - cx, y, cx, cy); +} + +inline void MakeRectBottom(RECT &rect, int x, int y, int cx, int cy) +{ + MakeRect(rect, x, y - cy, cx, cy); +} + +inline void MakeRectBottomRight(RECT &rect, int x, int y, int cx, int cy) +{ + MakeRect(rect, x - cx, y - cy, cx, cy); +} + +class Window { +public: + HWND GetHWND() { return m_hwnd; } + static HINSTANCE GetInstance() { + static HINSTANCE instance = (HINSTANCE) GetModuleHandle(NULL); + return instance; // Doesn't change + } +protected: + virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); + virtual void PaintContent(PAINTSTRUCT *pps) { UNREFERENCED_PARAMETER(pps); } + virtual LPCTSTR ClassName() = 0; + virtual HICON GetIcon() { return NULL; } + virtual BOOL WinRegisterClass(WNDCLASS *pwc) { + return RegisterClass(pwc); + } + virtual ~Window(); + + HWND WinCreateWindow(DWORD dwExStyle, LPCTSTR pszName, + DWORD dwStyle, int x, int y, int cx, + int cy, HWND hwndParent, HMENU hmenu); +private: + virtual void Register(); + void OnPaint(); + void OnPrintClient(HDC hdc); + + static LRESULT CALLBACK + s_WndProc(HWND hwnd, UINT uMsg, + WPARAM wParam, LPARAM lParam); +protected: + HWND m_hwnd; +}; + +#endif // header diff --git a/include/resource.h b/include/resource.h new file mode 100644 index 0000000..3b97a01 --- /dev/null +++ b/include/resource.h @@ -0,0 +1,2 @@ +#define RID_ICON 1 +#define RID_KEYBOARD 2 diff --git a/keyboard.bmp b/keyboard.bmp new file mode 100644 index 0000000000000000000000000000000000000000..13cfed2a4a3a0da523909e1ebcb59518496e1cc1 GIT binary patch literal 65590 zcmeHQ3Aj#G+g{CsBqS-KK{8i{1}alCqEr%^C#5+lQbJ5+s0_&v6_R*Y&!t$9~T_d+lM}_qv~9t-aqCZ5o>+4F5HTT>yK< zvm;D3eB!?&%-oZWnEN^Qf2;>$JrL`GSP#T{Al3u19*FfotOsH}5bJ?h55#&P)&sE~ zi1k3M2Vy-C>w#Dg#Cjms1F;^6^+2o#Vm%P+fmjd3dLY&Vu^x!^K&%I1JrL`GSP#T{ zAl3u19*FfotOsH}5bJ?h55#&P)&sE~i1k3M2Vy<&|E&kwwQFY@Hf(6>)Tv`?)~s2~ z&OiTr`CaYWwN2f+bxpl`^~{AAUMOvvG-+b4yz)xZqD2dH_0?CK>#n=b+;GDUrgiJq zrcIkR@|izhfBp65#v5-mZQHgrJl`$1++sR(=wLc_>}c+|;||lMOBZwZ-FKUN@4eUD zfB*gFp@$wa-MV!%k38~-7(YMu*kh)9_wMG2C!R1*J@u4%`st_5v(G*&_S|#NndhH> z-n{U_3#M1EUZ!{N-sYv3UNU|9^eNH)zhU&By57ehe_Z;W*j;hO73Q+bE;E;2da1eO zl1ohE#*Iyb1`W(b7hNRVMvWTD@Axh_@eS05&Vt?0NcbWU{yH9X`;DHAO`-dNXSa5&v!3X7U{5$`~pUDmKLis^nJo@OP z@;h$%Gyg_Db?Vef&PiTgb=6ho;)^df=bUqnIrY?2&1t8dW-3*xWG=Yi0#m;vvY?q7TDwQ}z~SJ$pxeID~#d7ZrW&Ye4(JMX+x z{!WhTeeimpeDX=tqel&8ShM%%`7zYCikyGc$VhX!GTlUy6P8)mP@5Z@!WKE{Q)J!@NHB4aYI@ zX8-UW`8VD#HG$)vTaNwcejpFnN8|$gg2sNP@iY5~`_T9~yl=B+%}mRdE#>^w2kHb( z`NA!KPi~TD>}Ou9?#sV(fAWq-Ua66H{2SZx+{BFcRI64kbMCq4nzPP2OYH2k&ldc7 ze`>r>)j4jdf7Pp3H#KV1kbS9?d9a zuZ{dC2Z%H8o%g8ssvO|&bu5H6fn$Qlps9ZFzS%$A(!%?*AHsd0yy2Gp#x4J@R#M+` zKlTZY{iMdeBe(c>o?p34?9MyyJVV^61LT6@sQ8EPlb?w_+mHt(ae&9x`()p#@w_zh zF5GAQyY`9p2m3<%jeQq3{+`^?{?dNXe&RhgZ{EDvIN|u9-ckpt5&TDe%KrC~##&jTNS!!d_}SJj|*-{QPKhYft33ay{4 z1}&Tf9sDkA@Yh4&3r>L+c7-mDhJAr+9H|DK90@=FLC>B&-whki`6dnAAAsXm0sku_ zws;v@+P`n#zG9UtSMGi0nP>Kf-uFKJ^wViCRH;(sSsJ+TEV%G2uID4`VV*;;a_?z~nN|mxzt5y}OR;`*1 z8;=`pdJbI2p@|bGK5)!2$NXNgVnqwyiSc^E#(qAWL9ahU|8G71_~R}3Dt5*hXIM4< zPCrL)!Epcr)mY*V?@K=9~SIHw|aMljqUg=FiG+-Es`^T6ivKq40oQ zB>y;W$Zc+Q+=P#*$KaO7;k8pg;6p9qa{m}?tH&tm8lwA!*8vTigYi3?=hu7D@vd9# zL&Y zXsp>@Eqn}KiyC;m`-As~ReEReATx5r`{uiEzcfEi``-Na+Z^-L^r>d{%;{#{Z?nrH zm)eV5+0x)!qU=QrEehES7CeFV6yyHzZGzi9_uOLUiqZT=jeY9IQ z3tuY+zh@Co$hE^)S@=FN_(!V-4YOT3cedOjm$IF)ANQpph7p7Aak~;eItQ_b#W=Ry zB0lyFIjE(eR=A686!LMfC zym@BkFEh-%IWx_iUuRzR>#U#c`qfMA2G~a0nx%Hr+GVh%cJsPrc8h0Q*DteM*Dtr* zHZ1dOxsBhl0ysZr+oC@g%$;RdEuAm6df9wSTeIBRT5Q*@SYX#h+4_|WEN#W&xzg`l zyLPej=lo(9&zmK-Wd1B0wxtX3GmP74Tefhv9sk`o7QD~w*|UfJ_2+5!*Iy!S&dh0c z4t^Kdk0tCg9o*|`OP4NfhYfwlPMbWzPW@rL*fejeKl|HBsZ__Y*_| zIZj879B#iE^QC8_L!a@xufMcozaH(|n6F0Lunm0uHOn}Fd>!%8hj!G*BXGQru>Ht> zjIG+p5g*x+BZiBO_-Huxb@maq{M*c#GrFx_waCn$Ki|xn`IA{N?^m;A@xn`!<2Twv z`!cQF4=aKl$g=hzE!)~dYB~4}v$>w-VGBD{U=cf5#M^dvegfKOpgj%8kz;trI)rWb zST^!_dv|BZ{q=dNmp!;I746fV9mKK3cs%cza;)(2f@|EoaV5u3;pB;9?4hDmF?%4* z9`ejTu3V!Y+uO=DyZe8oPcMnDHm_TN>q&wo+e7=4?ZN#i*ajB-oQl6i+V8tkEOZ3= zc&*LPSZ9mO{IeunK1w)=M{T4xD%|E>hP54Hj0kbdCxSNquAd7JItf~~MPu|4^5 zR&6&vi{-`HU7qdCjkC1EoNYEMGkw^G^(zGb*|UBD{&UQlRm)4QS-Et7(e5mJU{AI^ zus7Qt{2jJ02lojp0^SFJGwonL%-aHYVsGtEJ8Hy-;sbIsps|>rRAj9r0%(TD5_U+69KeNDI1dOVHW_G>fi@XvlL@|Of@|R0UbOf4R)pqsV60dOWv^_=g7eRnXX+Cbcg&Fjw{d6PI&HdUB*bB-Hkq? z4|)E%XK@YGG_*ayHn4+iqo(78oab0kN8xXtTL;*Kq5<;vyHBqi*y8L3G&GdiAzR2{wO_=#ie1Fy+L#$)kY*P~qu+NIb% zu)X=IVy+!HT9}45Wi~osx#n{<3WZ^ijO01`x*P>;kxp{0rH>#JRpZ?jtAhLXq4lE&m(Y&obK51 zc8T>9p(l<*;E`~M?a7BIEx4C>vKAHKK89e14le!_QlXLb_WCHq`*d?FW} zxwgSE$Y~y9XI_Tn-M8O2 zTjqy=x7oCDo!K6@864PV7A;)3IUVC)GywQ#f(K3qR09GIsDCKPNtf}w2jlno9zA5t zq$KU|_yZk)1~?s{euVK?|Kc=+T0p-tY2tXPBdl7v%;7KG2{eTG7eGhoW1{_x_!?+I zYSMOU(SZ-%fA1jqL;QsUz~9#q@P{1Og?WwO2Sshod_KX@DJ;W za_Dzx34Pw1gWj;HV$V^>K{&LV z?P0{-=aC#oxvX1qnf@F2=VxEHcW1WQvnxkx{;O84H0#%`HCs1tG&{C!HA|K(9-NYt zAoyp5@Q>zz#s`iA=@KXG0d}8$^07tD#l~&j6zGTNM??dFzw-s~1L6x1U#Kte{K(S9 z3nbs3``fSZA=D3V{6}ek^CK?K@M8_pfDD_S5^pg-acJ1EAw@YEiJ}2{J_p?Rcid49 zI2|B%iXZ!nb!g@^=}Ft5A4#GCg@R$=ON!|TwZrpAdkQj92k9;`#Ktwtef)(3z%HyI zksKm+4mWB5`;{@ow8;}iLtOhv4yhJV&(yc+{Sg1$?DX8 z1@cL>r+$Qdh!;cT3I`ZNFcyf^0LGZxwrr4NOc*~d!iR_tz%g7L;WR*F3B_My)XcO* zsl^N){PzCrbmC9_h>Rm7mWcKt=yTN&>Kbckn3uAdDLVrH;rWnoJV6Z!{D{M!eNPQx zEs0|yar?%=he#ab@Yi@m@bh94ho2k&u3YSMh};qW;2uN^sR^4y z9CG7d&+q(L_!v&ph&_*ypPSX1`0oti&-F7aR;)1V)~+#I-~+b92drAPYHVt9qQf6N z;P?;n0mK0s3j#mU0nq^ZfOL%M6sgN%E)qB*jzoL<4&Q&2fWO8Ojsxi!r>Pe6qn5|W zNKJ4W5EVmc9HHZ%Z8e6Z1~6a2Ite>qz^nVoA;tiK2DlhN<4E-b0S8>)XX6;mxBB#X z$>yXd0Dmuz48;)IRxrSr(>OxM9Q%zmyZ-(9;u;eC7{cW-%w+=p=rM#xGJf54FqS&{ z=%ejx175Xx8Sy@cB$j{%XilT}^BUB4qc3Q=S?PIu3UiMI4@_ZRra>Ga;~zCr;J@6g zg%8-gX@mHHWlNXbxnp~r;O}dIhhY}*(|l0!fv_I{59l-d_wOe)`=o^Jf zLG){+1~?y}8p3)5cq#J-Y02?X_($du5<^HV$T~$3N0LLVllJS|cVA{&LR1U^9Vn&& zk#Ph$#5{;|KZ6Iq1^itM5gkK193%4(;Q+=QIk0B+a;e#TIr?+dGm-)y;^iRDhq(64 zXPC=4%ygS>zx#Hq%pYK`E{H)i4)y$q&jB8T_!4)>=pllJcmC?uzB-l zvu5=wvvI?E6SsANGZt0T6MQIqnCHVp3uWYoqH0ah5FP{b(=xY{lz@7raHn`4;bKtr11^qa9!jI8P(xmNsrNo=h~|-^0rUgF z-*JHcC&(kh8j=kDa_;AoQ6r^2^GNX_Y%6|%aiq45^kf+>@!b4EpvlwRxB3$**=`dDu<{a${`*9JjTx444ape*;cVH%*!$b z`8hKF`QMl^V?+a79fK8*j)?GbM9*X{JAoQTrTN+ z06F0Nhtq)QS|c@p_`mnwyIg-^weo#AH3|T@hiT1n(#-y10XB~NeMoL1- zd4%Mm;at-B5ORRI=vMHTIoG$}jE&Hba17!55YZm>LDWH)Z&CL-_q=l1B5Xk;qJC%{LiNL8;JD*Blwlu4{g63i+~#$Hzs8_$47>5K z`1$Qa_%Zg<_`$`XkuiwRKd&Ylfj{nnJcjmKJBqc|6Tss>=}I{vt^8)$%Q z>-dvyf1Enmo_OMksELh3`$W%&xI9MNhxw-*QXcafp#AL~_QW6BpO<5H@7^7OKi7+| zT*+F{IE&>5G79jSDzpnwf2IlR^Blf2xZZ9#0)EGkbLv_IAlIixN z;UCJHs};)@2l$KcakXgSPS}S4N7WGaJ++T{8*0#K?{ZMpkDwM6@W=6h@r%Qd8pgU2 z*DNJ&-{kub^&vigL`!(?5C_RcTm!U!41A%7eWpPTz~txW$$I)Q{#^6VI)JMMA`jfU zh4DbEty?zZK3oitCVo8G)dk@LGSv@cM8^RxcH&&%ud`=~4@pNH>2tu#C5e9|2M|Me zK7_yLdac24za60;5w!$hFENCNzwbk!Bka2#J)YzGy&}f(E{D+h%*Yrr;E%&kFht+6 zZqIdzoX^(rANUWQkMQc4JSX~GjeWlm@h|6nCQq6u`D1h)TeXM0llVnq5FNj$VPRac z=KJ!?eQefY^uyIcLmcvs;jV)<-;555e?EMFK79Y~UAw~2`=6NyvKA_hrxL>w)IHt=y z(u-JQn1*qr<3FfFh3BOM|DhZp{=J}`SWCY@2|mch4=#@&hGLS37S|7I2=VKK9F%pp zEgRPg{@JJ@xpw4F*oQd$RC|;^T%X0Ydh{Q}J{&`63>wyuFb8;yxpQVq>^E`zcfuck z9#ra>p8xUmhix?mRu1N5rtjLBpM7FJ#(#cJh8z3v{d_L6qF>IZ-!tc?LGW(O*T1k zM??%6aKP0f9dEom0&N8cv}gSg>qGY??uY~aE{2dARCEriK1BM^wI}{~#zJy0%#RoI zAL03I^&b)akM=LV_=50fxY~-$YCQfOt%AG?Ju?C6-yUIaEJPTIR1pMAwNI& z8z1|e!)w2CL27}>16T*xylJD<0+%jX(iORYk3W3?@lR(>IL-M0@dMNV;P0?>_>KB_ zq|{*IQA&h6T=o}hVXz#puawO6-lWfQhd+Kzu+b4k)exsZIkt9^_>^|HsWG|=4A&8&<`mEm5<_}z8AFgij2*BZW&e(CTY?+{ ze#zkojYisgL~kbf!@fW!}SZE+qI4KKhf>8Gt+h#=4F?{b(lP8e_l?O$;&;w_ACBh zfBm(@fE!Q?M(ihf;ELtT=OY#n48w7N<3N-Kc=+@B6QH|X13zQNbPxaH^<<|Vi~&>w zT>QZPoA=usc@~6gl2kvmj^ttp)enci)*+}LwUi_4oJ5I0<;m^E}asI#hhkBV=i@89J z0k$CzL|*VhB5Hvy4opHUn1nTi&JSoDNF04XAXuhJ|MJ<4cZUu0@%QscjUggx37SVZ z4s1g1$QsZB){z-M`nAMhO#4o9s+IUfSulh_l_a};qcio_kbX5t^j4^bQde(FQ8 zRzkS@$RiJ9Em6E{C)yFJLkbQ7hSC<;6Myk)cA))tIQ%*GGcoqF&CZ=W{}uH9-?%PNe1I1NY~L1F2Qh&S zGywPu2Q(Ip(g4%}7zgk=@LVZtaERgJJr20G#ry|-fu{iyL*PCb_hAi>{bADgGX4V% zaQN$7gvJmY|Ed8zH=b>z|1HAWCf|Pu{(=Q-5K;Jh8bBPWUyS{qf4+y8e*`tCX#e5P z>Gi$ze;Vpm(n(ggFht z->qw<1~}{$|C02B*TM1k+JFJj9p;}7f8T#ZaL4%{9|vGCY}inFc5*e=FuL5sTMz5w z&o<5!f5i|rJ9%EHXV2$tdh!m@f1d-|-fv6&ahgZJC;4+;?)RAQH^d+Lzw`ZnLi?2y ztOc%Ky&67Xz2t%P0qfSSdp8MVSn+p0AQ^tZQyU!{cze* z+<&l*Xn=A6?YZ^}YdC!T1OMTzPXi7m)uPEgt{=l1pd$JaUpwFfG=?Dl>OcJYh1Zw7 z4?cg&HAoJB&v(#&MC2dT9EX3Hd+?)D|J=4^qsN_4{Gb?m`G@L<<4>C9^&WfdvG$v9 z#-MJDcAoEW9H3@-v8(1U-uQRzSvN}W9F70ZorMbTA8rw|aSf2~1DFROCb$M$n=G{e z!~n^Wae(STCdNO_X?YrQ|9GikPM-XOkAFlAiN1{Hj?(~gIo(3DJkLodon*iKa?HowfE2|)#nS+Z=TdA2#!a9B zE)IC;p$EAR2i!@JajdnZa15y&2>Su{M#@${qHlScn6RcUilg^!-HFHX{El?mt}J!|5<}kI&ySc1XqCW3=x$ z9CrkN+%xBlS$Eiux|3_Gv4=aa;_v)cz#mWhf57<-dpYNmW~O)b@rUovMc!Xn_~*4>@ki~PK3`&h&8P`62Ecsa-SIoN%Qy~e zfW`ufKV#m84I9|YFT2!UdP!rML#ID$gnGyoO`F&j&6{CO+c)S}jAi04+Tr}bhIOlL zyPMkD);Hc@+hbd~@|m&*gU`<1j5RPFpsN$GHYB=^B$^Y#A2G09yl4TR0V!I!VwumK z=p2&moL;5K^UFP-f6mf)9~{k0%=mG#9sd5i@@zQPE6bWQKL7qPo~QkE)CfEK zml@JF%mIhJiU1V zK#Cpw)*yT1RTtR~*Ir_8N83BwG__sZU1{&VrJ23|))w}mj;&;Dx;#MfSHGiU*~Oph z5%*yoViDpN!QaIlCD$N1M|@6|(`@Tz7uhz=FSc!4Hn8nlHni)40y>*CsYyipF|-3!8z#`Vs# zmtI)iUQ)lhy64&kb+?mcFqSV2k4W7@lOrlya8f|q6PEkJN#4J`NF=#;h2K;c^m8tkKbVnl76u} z)8^RS8T0I(%!PJO)?&Lidns(Wo%{`QLCGH@wWBBEygtv|W%H9}+k)gdwjgD$EliyU zn=e+7GT%=9_5c%NHGwvtY&$7+fI>V-KnQqfIO|#2?8DW9adBJgV|{>4LL3;@fHXf3NU^W? zf7yQa#-sMTcb~CSKkj8`ebrC+uyWc^yJ6OFyL{R(c^;Z|U5Opk2S5Wp8veeWJn}`m zWYSx9&5U>L7Vsxw@fS9A@t9w=O85A0lHr%xQ|@E5Ied?V-X z>e|`9-M>5bTWJqszeBs%+e3TsE|tAoU|a2>-?!SCQ?ZT-`meenTt(Y^@4m}cIlVmg z+l=?FY=`Z@I|bqc!_RSRCfm(egAg(P$@LK5q4U#``$79v|DEsuqk6yMe7LRF0woW` zvtrv&3&b;`3F-rU4FHCz9tZetnroWYx9y;rw_Q*Dzrx-F{ib$z?|hx@aerI!r|)Bp zx5Gd19p6tFXK#Y;QtP{Qy2d_v*Y&c0uSeS3S02CB4t%zgtx&$4AJ1w1$>k8#{~-5> z-?5F)5%1?3B(6aS&tXPtM+%->oM*4SqP`v4`)>QeEBD(G10J%U4D4pVc&oepYVZ?w z?9iv}I|F)pafjmQ{0(E;dv0lN#|(MOehbb|_@IZKG`y#sJmN(=1^k~ns+S%4?i+sW zpqfJt)TnX3JqNyH`e%LY&!hX=nP2v^v%cwojnGI?U0ur^4qHZD}ShSSx)=! zkH8_Hz^a1b(9}xc})B=j(4-PQjI;+yD@LQE_o%7GK z^)IMqFRFF6Z3urx|8`}gn)ajsxXfF=!fBD5V>=g}b+GdSw z*;bd=McWf(enor-c&Bj(^Db9^U@V+!lM>z@RXyEa z-K3tq4m`L4F-U910WBB<(Em5Ky?Z|G`yJN~8h~?F!MpP4uesmN*IXj8%dOYpxnXd> zf{)U8!RVxn`;!aE?V zRkAhCIn&lUk9?>CKHwc@<;&PsEn5cod)h%Pt5(K)?$G7}w5eUaimfXgswy06Q0H8G z%E_fHpD$M55rKb*1LP*&Be{S2^dC#aU+PcrspQR+$&+kl=rDQLp!PWududE|>4oRp z%PuJSQ zl$dxt)&=c~#^2Qhq2<8xzI*SsRnM$|v3#0U4iHb}0RP6k#?K=|_+Qqzp{-sOoH_dp z(U3?ERI{g*J6YZ_L+uF1k6M4gc#yF{ez5+_+H+91;#x+nA!_X*Mb?_o4_{cbn$LlT z;98@)E*2-YLk7PIt#Pq~j>YwBS78hvAsR=0Bi5JHr#UW=J9TSR#aalggO6yNVtc*V zQ}CxoUW)z68!>QAx71DgCgq#r9*lp9`yX`prvrPAeQ1AvzQXsHw|HlxTN8kJVa)?k z8~#2K`s#B4vAouV83(*G_-z?a)qpekO>I3Ncq#|ZuXcvqJJuO^?Hq47mOFRq2+mY2 z&Vd^AB~|Sir;wtc2^>7Yy z;3Dz>I>7vkJmB{8Pe1nf7Q~M~;N5=5piV)Ys2h$m0dL5MmRDX9p&J^j+=zMtYYo&i z?x#lXkVE{r+2sv{H_A8haUqQZ*AL*i{tC|bGw;XPr}pFfFaEB7d}qAW0uckS76|QU zE%5#ws0D`OfMkqE<-oF~i=pwS*t5>4VCmn;0Ugui0Q~^piNrZt=a+*x;03(1kukt| zp6}rPj3rbDh*RHJUh&Tr&OL(uNkzY<@EO9Qv17*+!=D@=XOsiO@y;P$E5{-^K%NN? zfGc^x7-lMbgwq=jf3zPuWUwuD!tt`NXb1M?{^Wt$gZFmvbR*ymud{sl^7drpe!?+m z2lIVrF88ONJoDtEzUDX$&=|-0f9U+I$g!UV?avXv{deKd(>japCecEK1Jre5$yy+vZ&A)@{_x(r!+^!9PB*{-YK6}M#1Q1u zQ%^qWcmo`@U4#a}2e6N^{_DUOUwmFW(Hb!1owwzj;-46EK(omK;!a#^ zfq#4+P-9Lx7w~@pbM%Zy>dF3YUupsE;@Zw$?boLmZ@4Z1-D*#-a7t)jr#IvSwS>nQ z{rM-Jc2M73til*q;#|bI$on1%@OL?1;olJVM{|eYBO>d!hj!mB4+zcRRWr)*GQu%Xsl)MSBin0rtPf58N_$2;(1U zfWx00Kut`ZLB#js6uCGhs%GWsKQZAu2N{z(o$=yS=s+DW|EN{HYA9C;a?UHUzV(cX z<%BQvY0Lx288Kkbs4B2(x9G?zhLQcr%IK|av$86SW-90 z1JMI=q2{^bZ!}L){2Mg5So|OPrTdaQ)DYeF@B7l>@yESE`yUJNcXK^|w7$OtSpW2| zQc_a9`9S1>tOdr!9f3Fyt~F8vBn|+E?AvqCt_m$LCu4;9wd#Q61kiEDfHS857>XSn zCRev?9^#CPAJrEyeyE7&s-ks+ee3$y;m`5U=ZA(38|LHh+Jtizj^z~F1Mg<#_@~ZD zy~4{?6xZvnZYF&5a}J&h_s86y_u}gY{FCs&`&fEo@?oqQ;{IXIgndUT)DJcHRDUC5S^C&% z0P*Md4d5McqW{7E9{!>M4inZQi9dZ)m;=1|39hyW16Dyl8Xa3r?5%F`iHMc@x%hj%OO{KzxSpje!G8#9w(s zEV-tD_&eVeaDZ_qaqZsiAs-LLax&JXGxsO{;+wpExxeGhnRY1his*Qg_lEA2SctyH z=Zta(+xzbBTue8@ehu3Hc!q~R>wWb7|4Hpvt>F6tB?dq~Nc@riov>`#(mm7w=7P)v zTrS}9!8uriRqmA1wj%I99kC!ee(pvBvGU-7526tcP$v#|EY&i->XdR=lCc7T;unj)PBX^)dI0D)XM|u1HgyL9RGs9uK__lplX#f zF=tn%L=K!@p)A(Qodhj+<6mnCc>kF2hWW!8o*xJ}P{Ec%?)dpVqHGp|1IiOlVY`Kn0$FXV+*XOf-6W*6KE2jsQaGZ*k z@6ijlBRBcoU-VzxPx(bIkT*1*pL1yLo`M)v@z2iATHwY$V}DQkQSZg~z$r}su`Qn+ zceTI`ejd1V>C%T-3k*2mVd?pR4!5+ory`%DAD|8hre01^<@8e}2E?4QV5xqC+Y05O z8D8uV%>m{WgWh<hY_fA-m~zk?WD6T&q?n>-(ooSb|no)M{wWs2~m^Z3!h<1Q4)P$~`@9^>0^L+97 zXM#WLTii#rLwUpPbI&{_V?T2IJ5ECb4&nVRXN7$~wSUHp8Gi?~f9e>n7U-T0;Cv8$ z0OG>sg1`C!FP4oD3k74M;XN8SNP{3Bvi9AD4F{ja>DiNvSGKg=?{Z|$%QmtfIH3N9T&Rk+-+ePy?%%bE)c>rk%thd}5&WV3 z@CA6EPriJg2|v5P!Tou5Ud0IKjhTV?p*YF*>tSMZ|h1iyx=} zKcG1Q$90*~C*mF1*ZX5rap8Nj=r_1OV?o9WQcHmE;Fj+PQh%;(bes`Wj(>bNHlIJ; z&ujDJlc-uHwWbl)tEMBZ&EU7+a-4zItKVP_;o^sK$Qv31{~+HW zCO-aN4)`&iAHh4|iUfbHQ-!oi+tjIDOY#%p7w`{rpmL=OzRu|WYbEzT(&aNAU&1_K z-h%gL6w?eoQ^uT^yoxq*;^ua(ea-Oi1^i^}N9{*_|Lk<+eHoJXyPDtsrS>cSek~An z0Oo{vUhIfv%a)~b{D|;4zQL`4$tRS4aUFk(^)?VVgStju?AQazr*Rj@Dls#bvZm)Z$8J*Pn^cM+5>q| ztHyag{`3LV7G4``S8iX&7v{C(0PWnWl|8-i=0wnD-n`$$_p=Xlzc6R`dEjfW2KcKl zLtEkx?O)2VPy99S%g@XEKh=K4pS3`p4-_AOHA0vR7|HP;9S5+lxdw|m&%CYtDJMsA z0QmD+hwr}oPWoA}fo@Q9nO~7Fj2u6DRXk*GdQNb@t=|$;6HZkSUy8sv}5~r z|GIq5@Y^KYvyh|u$A;dE&VXa&S<}le5uFij;yHlFu%SbwejnB&t#6St*S2cmb4ECZ zHgCQ4rr^(*f%y#g3mdsK_qRF4d}(yq*&abVJF1b_N|)cpQi+OMSXYk_!1T=PKa!L;}Y9{^wL#b|wA>J1-IszeU(_k3pBKV3!gncfaew>@u&8SQ8SWlY5Ey*5&9C= z7~=Zev&c=gu8A>8Kf^e_4A%Xq7I8l{{%+dTDG}qJ`g7D#N6C2@^9WynyKKn^7{9+a zxF+Xkbo^&$XTRX^_k90fe6~w5{43k&S|Ha6V=kau*w?z6pu>~va2fj+;{f!X_%fej zoF2v#-`^m$179;jzM%9;C&>2%$o`rKU_84xz{LP_=l;fbxE-LMklX~Cl=k@I{E_Ch+~PS2AAepCd5XSQBu@erU|I`0U+w$E2oV!y0rMf}vFRWYJ-Ggcj@aMbmd4JBwL~0D%G{5Q! z;h4L2kHen+XJ%xqLj1>fF{19n`CiQT{FmZ>#a*|fM~_Afz*?Z@fl>=xCHS)zpfP}l z=hdxRiUyvHT)=VQBtItPT1r7bG?84*)GNKuWb2z7-K-epS3{v z0OSR)GT-*&K>AJ(AHEZpK7e_hZ~)_)IC5+g1HQB0=M4IqnqB&&Qj$A{`9kfb-FauH zV1GGBp#L2IxHi6rClj%%cRh}idOhs(C5so!_*Wkz9PqS&pJ&a2CSgB^XOg8KDN`nU#|LM?N#OvlpE(S)zGfQU$(EkR zx)103vj3}be>7kCY#{OHx=_Xds0Urb{+4>MuK`XsnU68nCkGgFkq3$^f6w=Y`uKaC zIluaO{(iczV%4%`i$E{D{oVLM42bc=>p=d$m9-`3lU!|5#xwROK68HkRq$6HhW!lv z4)sCDKY74+yD?v-FK0a?%oqBEPw^~Cc>h%^SBUSI7&GK!gbU!nS(VQaZk9Cuq5W&Y z0VDW(zCRc9J-vJPj)vjC_A}=H1b@Cen6+U1d?dalyd{RZ?tA`9cbkK7&{sRV*GGz5^7b;5T`i&$w%=ofhPU<IdgJbbp__;`>GW zYYYC2{kYZ#+7G_|9kBgv$7s@|iHv_219Cnvj_X8k=GfCbKx!}E7~=PGQ0r;Y9H3wQ z^pj6~Oo`FJfv*SqDhIsykv=uZ>l|N_IQ~Tgl4b0^_10SxsNu>*pD*<3FwAEQ{>&#t zhrF1CT5$1&^^4)(qD2e2o`5ew4AZ>Xl@a^%IvO`_gxvOM(HG@_ZpokJ%a-}t62>2R zZ@@d34Bx@vYW{ei*FQ}Al|;bF#ei-ui0cAN<6S|&(+Bu|S8{UYpQjQ;v zM-5o{K*$S*Ab3i*p|8CiQp;!PQ2aLeZH`M-M}A#i+bIR zU6AMX<2;+gKOK2LYJUGP?N|KAj~^de4-kiU25;lf)5Cr4`Z3Y67RQ*Lm^u!?7r-CT z7jQgtUO%YSCR)rZ;CoPx4KyI+H&{wL;38i_yI2-;ODBSt-1Z;J-p(PCzbX2;(W%59$${d_p^=)jqS*_2h?}WOFxM< zigHXne~_CdVvX1!AAg?%p2j@>cz55wFm6PxLvnxB3dv)+FS&p%b3>mmI5*ZFOH32q zk35k5(dMhKz7)Oi@%M7S^z`(N;DI6jjQyGSalQv@8UInZhcAZlKaKvM;~%jXHLEIe znCggW>mUZJGk^a4I^b}fJMQQ>gkzl?P#sVWV4V!#NqZOKvWDZveb;pSxN%LJHfgd# z_EWw<1IPm&1K;!c@zg0(TFw3Kw^s1;t_z#M`Cxa zEQa&*8fg3(?}Rg>M}J}PZgJE4#v3124vH3e+~WKm*6Me9;_>e0@$QcqIXEA|&4>RD^SC7PZdpuFK>RG)j zRy?f_##MhceAn?E_uqe?!F!O+0}tG99(?ctgZHsEfcCu^ZCHc<&eKmnb^3G9J!|lN zQ3m=eeMtWCoMPZ3HHqsK$q9b`ZO$C?@y8zVqVx*xzj;Mub_ zH(Y=H&(~jn-7n3XHJjVKd0_baHC;M)GI;jT;CXQL&o6wYlCiUJ0I?vxgAd~$bwAGc z{FAkx9KgGFWZWa>0)LZ)ztk`q^8?Jq8hkG-$FjM(eS3qxFj#kKYF<#ooO{mMre@6x z4Bi)PuD|YDgSg9d>D<}i`*#emFn4sk-QXR-rfHMQ4aS+d=)(F2?-w@LT;0mtdTR&s zFfal(=4-sOlzFJ|&eI;mprS?OC3!)^cM=gBgZTr~>5e<(oJ}sj%sX~H)3{LsgZC7h z_U&#m#1!xH#r522I(53k;Jqz)7VdJnhT63*Ftuyd#QqIUtClSc-e+Jsq=h zljAgOaIvX{`{R9HP@}qOc=1K}{Z-&~duZGv=FK+;nV+Ct#Glv8u|pn^1H}LDc$b@w z)Bog_?+fGmVmLP(HpYj;h7L8i-F7Sbu8Fy@emzsKZe3HiPHj^Q{YMU50IQGRUv}vw z2J7+7b=O{NuD$jegRz6p*O+EkT`BnU@9ghOu_Yf+Pm;frQ~1tj87KTsbAFc*jqfoH z8{cIB-7vS`cAK=j>dGtRoW!UBaN(9lE@EA{T*EcEo~yw%tob*WG;Sn3sb8-yuD7ni z+ELt3U1@XW6-`ac7R}|FuEO>EF*!-yXaXMMJ&*?Tn~W#qy2)AclRQNYNH|MAD90K> zV;sktilz)5GUN}h`~UL}@U6hUuEBE?(DyqHziV#XxN&B}gb89;|7j*poM@&_ohr{V zayx0#B(cepCz~l#rufFcF&F+3#;tt*ahkNNRjbw?cGIj`vkY=CgSi@s1ISH0D_~Ho zM;!SP{PFt+-~A#sV#El;zrXk1dj`*(nZAAd8oV#WVC{jy_gctvcP}B1z_W&?fB$~a z@;43QSNS`h_rO?w95F0nLd1oPAsI(7wqaa@?-#>3e;oVvF_@?P!|RUi5bJ?h55#&P z)&sE~i1k3M2Vy-C>w#Dg#Cjms1F;^6^+2o#Vm%P+fmjd3dLY&Vu^x!^K&%I1JrL`G MSP#T{prju79}|3$i2wiq literal 0 HcmV?d00001 diff --git a/keyboard.ico b/keyboard.ico new file mode 100644 index 0000000000000000000000000000000000000000..55bdb3b0f708d8068a74f56c2ac5e8fb1459b20f GIT binary patch literal 15086 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 +#include + +RID_ICON ICON keyboard.ico +RID_KEYBOARD BITMAP keymap.bmp + +STRINGTABLE LANGUAGE 0x09, 0x01 // en-US +BEGIN + 0xAE00, "Acoustic Grand Piano" + 0xAE01, "Bright Acoustic Piano" + 0xAE02, "Electric Grand Piano" + 0xAE03, "Honky-tonk Piano" + 0xAE04, "Rhodes Piano" + 0xAE05, "Chorused Piano" + 0xAE06, "Harpsichord" + 0xAE07, "Clavichord" + 0xAE08, "Celesta" + 0xAE09, "Glockenspiel" + 0xAE0A, "Music box" + 0xAE0B, "Vibraphone" + 0xAE0C, "Marimba" + 0xAE0D, "Xylophone" + 0xAE0E, "Tubular Bells" + 0xAE0F, "Dulcimer" + 0xAE10, "Hammond Organ" + 0xAE11, "Percussive Organ" + 0xAE12, "Rock Organ" + 0xAE13, "Church Organ" + 0xAE14, "Reed Organ" + 0xAE15, "Accordian" + 0xAE16, "Harmonica" + 0xAE17, "Tango Accordian" + 0xAE18, "Acoustic Guitar (nylon)" + 0xAE19, "Acoustic Guitar (steel)" + 0xAE1A, "Electric Guitar (jazz)" + 0xAE1B, "Electric Guitar (clean)" + 0xAE1C, "Electric Guitar (muted)" + 0xAE1D, "Overdriven Guitar" + 0xAE1E, "Distortion Guitar" + 0xAE1F, "Guitar Harmonics" + 0xAE20, "Acoustic Bass" + 0xAE21, "Electric Bass(finger)" + 0xAE22, "Electric Bass (pick)" + 0xAE23, "Fretless Bass" + 0xAE24, "Slap Bass 1" + 0xAE25, "Slap Bass 2" + 0xAE26, "Synth Bass 1" + 0xAE27, "Synth Bass 2" + 0xAE28, "Violin" + 0xAE29, "Viola" + 0xAE2A, "Cello" + 0xAE2B, "Contrabass" + 0xAE2C, "Tremolo Strings" + 0xAE2D, "Pizzicato Strings" + 0xAE2E, "Orchestral Harp" + 0xAE2F, "Timpani" + 0xAE30, "String Ensemble 1" + 0xAE31, "String Ensemble 2" + 0xAE32, "Synth Strings 1" + 0xAE33, "Synth Strings 2" + 0xAE34, "Choir Aahs" + 0xAE35, "Voice Oohs" + 0xAE36, "Synth Voice" + 0xAE37, "Orchestra Hit" + 0xAE38, "Trumpet" + 0xAE39, "Trombone" + 0xAE3A, "Tuba" + 0xAE3B, "Muted Trumpet" + 0xAE3C, "French Horn" + 0xAE3D, "Brass Section" + 0xAE3E, "Synth Brass 1" + 0xAE3F, "Synth Brass 2" + 0xAE40, "Soprano Sax" + 0xAE41, "Alto Sax" + 0xAE42, "Tenor Sax" + 0xAE43, "Baritone Sax" + 0xAE44, "Oboe" + 0xAE45, "English Horn" + 0xAE46, "Bassoon" + 0xAE47, "Clarinet" + 0xAE48, "Piccolo" + 0xAE49, "Flute" + 0xAE4A, "Recorder" + 0xAE4B, "Pan Flute" + 0xAE4C, "Bottle Blow" + 0xAE4D, "Shakuhachi" + 0xAE4E, "Whistle" + 0xAE4F, "Ocarina" + 0xAE50, "Lead 1 (square)" + 0xAE51, "Lead 2 (sawtooth)" + 0xAE52, "Lead 3 (caliope lead)" + 0xAE53, "Lead 4 (chiff lead)" + 0xAE54, "Lead 5 (charang)" + 0xAE55, "Lead 6 (voice)" + 0xAE56, "Lead 7 (fifths)" + 0xAE57, "87 Lead 8 (bass+lead) " + 0xAE58, "Pad 1 (new age)" + 0xAE59, "Pad 2 (warm)" + 0xAE5A, "Pad 3 (polysynth)" + 0xAE5B, "Pad 4 (choir)" + 0xAE5C, "Pad 5 (bowed)" + 0xAE5D, "Pad 6 (metallic)" + 0xAE5E, "Pad 7 (halo)" + 0xAE5F, "Pad 8 (sweep)" + 0xAE60, "FX 1 (rain)" + 0xAE61, "FX 2 (soundtrack)" + 0xAE62, "FX 3 (crystal)" + 0xAE63, "FX 4 (atmosphere)" + 0xAE64, "FX 5 (brightness)" + 0xAE65, "FX 6 (goblins)" + 0xAE66, "FX 7 (echoes)" + 0xAE67, "FX 8 (sci-fi)" + 0xAE68, "Sitar" + 0xAE69, "Banjo" + 0xAE6A, "Shamisen" + 0xAE6B, "Koto" + 0xAE6C, "Kalimba" + 0xAE6D, "Bagpipe" + 0xAE6E, "Fiddle" + 0xAE6F, "Shanai" + 0xAE70, "Tinkle Bell" + 0xAE71, "Agogo" + 0xAE72, "Steel Drums" + 0xAE73, "Woodblock" + 0xAE74, "Taiko Drum" + 0xAE75, "Melodic Tom" + 0xAE76, "Synth Drum" + 0xAE77, "Reverse Cymbal" + 0xAE78, "Guitar Fret Noise" + 0xAE79, "Breath Noise" + 0xAE7A, "Seashore" + 0xAE7B, "Bird Tweet" + 0xAE7C, "Telephone Ring" + 0xAE7D, "Helicopter" + 0xAE7E, "Applause" + 0xAE7F, "Gunshot" +END diff --git a/keymap.bmp b/keymap.bmp new file mode 100644 index 0000000000000000000000000000000000000000..567402a89e34be46b4dd58ccb964211ffc3dceb9 GIT binary patch literal 102478 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 +#include + +#pragma comment(lib, "comctl32.lib") + +int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) { + UNREFERENCED_PARAMETER(hInstance); + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(lpCmdLine); + InitCommonControls(); + + MainWindow *win = MainWindow::Create(L"Music Keyboard"); + if (win) { + ShowWindow(win->GetHWND(), nCmdShow); + MSG msg; + while (GetMessage(&msg, NULL, 0, 0) > 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + return 0; +} diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp new file mode 100644 index 0000000..09499ef --- /dev/null +++ b/src/MainWindow.cpp @@ -0,0 +1,395 @@ +#include + +#include +#include +#include + +#define LEFT(x, y, cx, cy) x, y, cx, cy +#define RIGHT(x, y, cx, cy) (x - cx), y, cx, cy +#define BOTTOM(x, y, cx, cy) x, (y - cy), cx, cy +#define BOTTOMRIGHT(x, y, cx, cy) (x - cx), (y - cy), cx, cy +#define KEYBOARD_IMAGE 0xAA00 +#define KEYBOARD_VOLUME 0xAA01 +#define KEYBOARD_FORCE 0xAA02 +#define KEYBOARD_INSTRUMENT 0xAA03 +#define MIDI_MESSAGE(handle, code, arg1, arg2) \ + midiOutShortMsg(handle, ((arg2 & 0x7F) << 16) |\ + ((arg1 & 0x7F) << 8) | (code & 0xFF)) + +#pragma comment(lib, "winmm.lib") + +DWORD rgbWindowBackground; + +static char keymap[256]; + +BOOL MainWindow::WinRegisterClass(WNDCLASS *pwc) +{ + pwc->style = CS_HREDRAW | CS_VREDRAW; + return __super::WinRegisterClass(pwc); +} + +BOOL EnableCloseButton(const HWND hwnd, const BOOL bState) +{ + HMENU hMenu; + UINT dwExtra; + + if (hwnd == NULL) + return FALSE; + if ((hMenu = GetSystemMenu(hwnd, FALSE)) == NULL) + return FALSE; + dwExtra = bState ? MF_ENABLED : (MF_DISABLED | MF_GRAYED); + return EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | dwExtra) != -1; +} + +LRESULT MainWindow::OnCreate() +{ + NONCLIENTMETRICS ncmMetrics = { sizeof(NONCLIENTMETRICS) }; + RECT client; + + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncmMetrics, 0); + GetClientRect(m_hwnd, &client); + + hFont = CreateFontIndirect(&ncmMetrics.lfMessageFont); + rgbWindowBackground = GetSysColor(COLOR_WINDOW); + hBrush = GetSysColorBrush(COLOR_WINDOW); + hKeyboardLayout = (HBITMAP) LoadImage(GetInstance(), MAKEINTRESOURCE(RID_KEYBOARD), + IMAGE_BITMAP, 0, 0, LR_LOADTRANSPARENT); + hdcKeyboard = CreateCompatibleDC(GetDC(m_hwnd)); + SelectBitmap(hdcKeyboard, hKeyboardLayout); + + // Children + m_volumeLabel = CreateWindow(WC_STATIC, L"Volume:", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 0, 0, 0, 0, + m_hwnd, NULL, GetInstance(), NULL); + m_forceLabel = CreateWindow(WC_STATIC, L"Force:", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 0, 0, 0, 0, + m_hwnd, NULL, GetInstance(), NULL); + m_instruLabel = CreateWindow(WC_STATIC, L"Instrument:", + WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, 0, 0, 0, 0, + m_hwnd, NULL, GetInstance(), NULL); + + m_volumeBar = CreateWindow(TRACKBAR_CLASS, NULL, + WS_CHILD | WS_VISIBLE | TBS_NOTICKS, 0, 0, 0, 0, + m_hwnd, (HMENU) KEYBOARD_VOLUME, GetInstance(), NULL); + m_forceBar = CreateWindow(TRACKBAR_CLASS, NULL, + WS_CHILD | WS_VISIBLE | TBS_NOTICKS, 0, 0, 0, 0, + m_hwnd, (HMENU) KEYBOARD_FORCE, GetInstance(), NULL); + m_instruSelect = CreateWindow(WC_COMBOBOX, NULL, WS_CHILD | WS_VISIBLE | + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL, 0, 0, 0, 0, + m_hwnd, (HMENU) KEYBOARD_INSTRUMENT, GetInstance(), NULL); + + SendMessage(m_volumeBar, TBM_SETRANGEMIN, FALSE, 0x0000); + SendMessage(m_volumeBar, TBM_SETRANGEMAX, FALSE, 0xFFFF); + SendMessage(m_forceBar, TBM_SETRANGE, FALSE, 127 << 16); + SendMessage(m_volumeBar, TBM_SETPOS, FALSE, 0xFFFF); + SendMessage(m_forceBar, TBM_SETPOS, FALSE, 64); + m_force = 64; + m_volume = 0xFFFF; + + WCHAR buf[MAX_PATH]; + int piano; + SendMessage(m_instruSelect, CB_INITSTORAGE, 128, 4096); + for (int i = 0; i < 128; ++i) { + int id; + LoadString(GetInstance(), 0xAE00 | i, buf, MAX_PATH); + id = SendMessage(m_instruSelect, CB_ADDSTRING, 0, (LPARAM) buf); + SendMessage(m_instruSelect, CB_SETITEMDATA, id, i); + } + LoadString(GetInstance(), 0xAE00, buf, MAX_PATH); + piano = SendMessage(m_instruSelect, CB_FINDSTRING, 0, (LPARAM) buf); + SendMessage(m_instruSelect, CB_SETCURSEL, piano, 0); +#define SETFONT(hwnd) PostMessage(hwnd, WM_SETFONT, (WPARAM) hFont, (LPARAM) TRUE) + SETFONT(m_volumeLabel); + SETFONT(m_volumeBar); + SETFONT(m_forceLabel); + SETFONT(m_forceBar); + SETFONT(m_instruLabel); + SETFONT(m_instruSelect); +#undef SETFONT + + if (midiOutOpen(&m_midi, 0, NULL, NULL, CALLBACK_NULL) != MMSYSERR_NOERROR) + MessageBox(m_hwnd, L"Failed to open MIDI device!", L"Fatal Error", MB_ICONERROR); + + keymap[VK_OEM_3] = 54; // `~ key + keymap['Q'] = 55; + keymap[VK_TAB] = 55; + keymap['A'] = 57; + keymap['Z'] = 57; + keymap['W'] = 56; + keymap['E'] = 58; + keymap['S'] = 59; + keymap['X'] = 59; + keymap['D'] = 60; + keymap['C'] = 60; + keymap['R'] = 61; + keymap['F'] = 62; + keymap['V'] = 62; + keymap['T'] = 63; + keymap['G'] = 64; + keymap['B'] = 64; + keymap['H'] = 65; + keymap['N'] = 65; + keymap['U'] = 66; + keymap['J'] = 67; + keymap['M'] = 67; + keymap['I'] = 68; + keymap['K'] = 69; + keymap[VK_OEM_COMMA] = 69; + keymap['O'] = 70; + keymap['L'] = 71; + keymap[VK_OEM_PERIOD] = 71; + keymap[VK_OEM_1] = 72; // ;: + keymap[VK_OEM_2] = 72; // /? + keymap[VK_OEM_7] = 74; // '" + keymap[VK_OEM_4] = 73; // [ + keymap[VK_OEM_6] = 75; // ] + keymap[VK_RETURN] = 76; + keymap[VK_OEM_5] = 77; // \| + keymap[VK_BACK] = 77; + keymap[0x31] = 57; + keymap[0x32] = 59; + keymap[0x33] = 60; + keymap[0x34] = 62; + keymap[0x35] = 64; + keymap[0x36] = 65; + keymap[0x37] = 67; + keymap[0x38] = 69; + keymap[0x39] = 71; + keymap[0x30] = 72; + keymap[VK_OEM_MINUS] = 74; + keymap[VK_OEM_PLUS] = 76; + PostMessage(m_hwnd, WM_INPUTLANGCHANGE, 0, 0); + + SetTimer(m_hwnd, 0xDE00, 1000, NULL); + return 0; +} + +LRESULT MainWindow::OnDestroy() +{ + DestroyWindow(m_volumeLabel); + DestroyWindow(m_volumeBar); + DestroyWindow(m_forceLabel); + DestroyWindow(m_forceBar); + DestroyWindow(m_instruLabel); + DestroyWindow(m_instruSelect); + midiOutClose(m_midi); + return 0; +} + +HICON MainWindow::GetIcon() +{ + return LoadIcon(GetInstance(), MAKEINTRESOURCE(RID_ICON)); +} + +void MainWindow::PaintContent(PAINTSTRUCT *pps) +{ + BITMAP bm; + int cx, cy, tx, ty, x, y; + double ratio; + RECT client; + GetClientRect(m_hwnd, &client); + GetObject(hKeyboardLayout, sizeof(BITMAP), &bm); + cx = client.right - 24, cy = client.bottom - 117; + ratio = ((double) bm.bmWidth) / ((double) bm.bmHeight); + if (cx / ratio > cy) + tx = (int) (cy * ratio), ty = (int) cy; + else + tx = (int) cx, ty = (int) (cx / ratio); + x = 12 + (cx - tx) / 2; + y = 12 + (cy - ty) / 2; + StretchBlt(pps->hdc, x, y, tx, ty, hdcKeyboard, 0, 0, bm.bmWidth, bm.bmHeight, SRCAND); +} + +void MainWindow::OnPaint() +{ + PAINTSTRUCT ps; + BeginPaint(m_hwnd, &ps); + PaintContent(&ps); + EndPaint(m_hwnd, &ps); +} + +WORD MainWindow::GetQWERTYKeyCode(WORD wKeyCode) +{ + if (isQWERTY) + return wKeyCode; + UINT uiScan = MapVirtualKey(wKeyCode, MAPVK_VK_TO_VSC); + if (!uiScan) + return wKeyCode; + UINT uiQWERTY = MapVirtualKeyEx(uiScan, MAPVK_VSC_TO_VK, hklQWERTY); + if (uiScan) + return (WORD) uiQWERTY; + else + return wKeyCode; +} + +WORD MainWindow::GetRealKeyCode(WORD wQWERTYCode) +{ + if (isQWERTY) + return wQWERTYCode; + UINT uiScan = MapVirtualKeyEx(wQWERTYCode, MAPVK_VK_TO_VSC, hklQWERTY); + if (!uiScan) + return wQWERTYCode; + UINT uiKeyCode = MapVirtualKey(uiScan, MAPVK_VSC_TO_VK); + if (uiScan) + return (WORD) uiKeyCode; + else + return wQWERTYCode; +} + +int GetMIDINote(WPARAM wCode) +{ + int state = 0; + int note; + if (GetKeyState(VK_CONTROL) < 0) + state |= 0x001; + if (GetKeyState(VK_SHIFT) < 0) + state |= 0x010; + if (GetKeyState(VK_MENU) < 0) + state |= 0x100; + + note = keymap[wCode]; + switch (state) { + case 0x001: + note -= 24; + break; + case 0x010: + note += 24; + break; + case 0x100: + note += 48; + break; + } + return note; +} + +bool MainWindow::Play(WPARAM wCode, LPARAM lParam, bool down) +{ + int note; + wCode = GetQWERTYKeyCode((WORD) wCode); + if (wCode > 255 || !keymap[wCode] || (down && (lParam & 0x40000000))) + return false; + + note = GetMIDINote(wCode); + if (down) { + int num = note; + while (num > 24) + num -= 24; + while (num < 0x7F) { + if (num != note) + MIDI_MESSAGE(m_midi, 0x90, num, 0); + num += 24; + } + } + if (down) + MIDI_MESSAGE(m_midi, 0x90, note, m_force); + else + MIDI_MESSAGE(m_midi, 0x90, note, 0); + return true; +} + +LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) { + case WM_CREATE: + return OnCreate(); + case WM_DESTROY: + return OnDestroy(); + case WM_NCDESTROY: + PostQuitMessage(0); + break; + case WM_PAINT: + OnPaint(); + return 0; + case WM_SIZE: { + RECT client; + HDWP hdwp; + GetClientRect(m_hwnd, &client); +#define REPOS(hwnd, k) hdwp = DeferWindowPos(hdwp, hwnd, 0, k, SWP_NOACTIVATE|SWP_NOZORDER) + hdwp = BeginDeferWindowPos(14); + REPOS(m_volumeLabel, BOTTOM(12, client.bottom - 12, 70, 25)); + REPOS(m_volumeBar, BOTTOM(82, client.bottom - 12, client.right - 94, 25)); + REPOS(m_forceLabel, BOTTOM(12, client.bottom - 42, 70, 25)); + REPOS(m_forceBar, BOTTOM(82, client.bottom - 42, client.right - 94, 25)); + REPOS(m_instruLabel, BOTTOM(12, client.bottom - 72, 70, 25)); + REPOS(m_instruSelect, BOTTOM(82, client.bottom - 72, client.right - 94, 25)); + EndDeferWindowPos(hdwp); +#undef REPOS + return 0; + } + case WM_COMMAND: + switch (HIWORD(wParam)) { + case CBN_SELCHANGE: + switch (LOWORD(wParam)) { + case KEYBOARD_INSTRUMENT: + m_instrument = SendMessage((HWND) lParam, CB_GETITEMDATA, + SendMessage((HWND) lParam, CB_GETCURSEL, 0, 0), 0); + MIDI_MESSAGE(m_midi, 0xC0, m_instrument, 0); + } + } + break; + case WM_HSCROLL: + switch (LOWORD(wParam)) { + case TB_THUMBPOSITION: + if (lParam == (LPARAM) m_volumeBar) { + m_volume = HIWORD(wParam); + midiOutSetVolume(m_midi, m_volume); + } else if (lParam == (LPARAM) m_forceBar) + m_force = HIWORD(wParam); + break; + } + break; + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + if (Play(wParam, lParam, true)) + return 0; + break; + case WM_KEYUP: + case WM_SYSKEYUP: + if (Play(wParam, lParam, false)) + return 0; + break; + case WM_LBUTTONDOWN: + SetFocus(m_hwnd); + return 0; + case WM_MOUSEWHEEL: + if (GetFocus() != m_forceBar) + SendMessage(m_forceBar, WM_MOUSEWHEEL, + -GET_WHEEL_DELTA_WPARAM(wParam) * 2 << 16 | + GET_KEYSTATE_WPARAM(wParam), lParam); + case WM_INPUTLANGCHANGE: { + TCHAR buf[KL_NAMELENGTH]; + GetKeyboardLayoutName(buf); + isQWERTY = lstrcmpi(buf, L"00000409") == 0; + if (!isQWERTY && !hklQWERTY) + hklQWERTY = LoadKeyboardLayout(L"00000409", KLF_NOTELLSHELL); + } + case WM_CHAR: + case WM_DEADCHAR: + case WM_SYSCHAR: + case WM_SYSDEADCHAR: + return 0; + case WM_ENTERSIZEMOVE: + SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, GetWindowLongPtr(m_hwnd, GWL_EXSTYLE) | WS_EX_COMPOSITED); + return 0; + case WM_EXITSIZEMOVE: + SetWindowLongPtr(m_hwnd, GWL_EXSTYLE, GetWindowLongPtr(m_hwnd, GWL_EXSTYLE) & ~WS_EX_COMPOSITED); + return 0; + } + return __super::HandleMessage(uMsg, wParam, lParam); +} + +MainWindow *MainWindow::Create(LPCTSTR szTitle) +{ + MainWindow *self = new MainWindow(); + RECT client = {0, 0, 622, 286}; + AdjustWindowRect(&client, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, FALSE); + if (self && + self->WinCreateWindow(0, + szTitle, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, + CW_USEDEFAULT, CW_USEDEFAULT, client.right - client.left, + client.bottom - client.top, NULL, NULL)) { + return self; + } + delete self; + return NULL; +} diff --git a/src/Window.cpp b/src/Window.cpp new file mode 100644 index 0000000..17815e5 --- /dev/null +++ b/src/Window.cpp @@ -0,0 +1,88 @@ +#include + +#pragma comment(lib, "user32.lib") +#pragma comment(lib, "gdi32.lib") + +HWND Window::WinCreateWindow(DWORD dwExStyle, LPCTSTR pszName, + DWORD dwStyle, int x, int y, int cx, + int cy, HWND hwndParent, HMENU hmenu) +{ + Register(); + return CreateWindowEx(dwExStyle, ClassName(), pszName, dwStyle, + x, y, cx, cy, hwndParent, hmenu, GetInstance(), + this); +} + +Window::~Window() {} + +void Window::Register() +{ + WNDCLASS wc; + wc.style = 0; + wc.lpfnWndProc = Window::s_WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = GetInstance(); + wc.hIcon = GetIcon(); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = CreateSolidBrush(RGB(0xF0, 0xF0, 0xF0)); + wc.lpszMenuName = NULL; + wc.lpszClassName = ClassName(); + + WinRegisterClass(&wc); +} + +LRESULT CALLBACK +Window::s_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + Window *self; + if (uMsg == WM_NCCREATE) { + LPCREATESTRUCT lpcs = (LPCREATESTRUCT) lParam; + self = (Window *)(lpcs->lpCreateParams); + self->m_hwnd = hwnd; + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM) self); + } else { + self = (Window *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + } + if (self) { + return self->HandleMessage(uMsg, wParam, lParam); + } else { + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } +} + +LRESULT Window::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) { + case WM_NCDESTROY: { + LRESULT lres; + lres = DefWindowProc(m_hwnd, uMsg, wParam, lParam); + SetWindowLongPtr(m_hwnd, GWLP_USERDATA, 0); + delete this; + return lres; + } + case WM_PAINT: + OnPaint(); + return 0; + case WM_PRINTCLIENT: + OnPrintClient(reinterpret_cast(wParam)); + return 0; + } + return DefWindowProc(m_hwnd, uMsg, wParam, lParam); +} + +void Window::OnPaint() +{ + PAINTSTRUCT ps; + BeginPaint(m_hwnd, &ps); + PaintContent(&ps); + EndPaint(m_hwnd, &ps); +} + +void Window::OnPrintClient(HDC hdc) +{ + PAINTSTRUCT ps; + ps.hdc = hdc; + GetClientRect(m_hwnd, &ps.rcPaint); + PaintContent(&ps); +}