TRAYCTL.C 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526
  1. #define PLUGIN_NAME L"Nullsoft Tray Control"
  2. #define PLUGIN_VERSION L"2.49"
  3. // Winamp general purpose plug-in mini-SDK
  4. // Copyright (C) 1997, Justin Frankel/Nullsoft
  5. // Modifications and useability enhancements by DrO aka Darren Owen 2006-2014
  6. #include <windows.h>
  7. #include <commctrl.h>
  8. #include <shlwapi.h>
  9. #include "../winamp/gen.h"
  10. #include "../winamp/wa_ipc.h"
  11. #include "../winamp/ipc_pe.h"
  12. #include "resource.h"
  13. #include "winampcmd.h"
  14. #include "api__gen_tray.h"
  15. #include <strsafe.h>
  16. #ifndef _DEBUG
  17. BOOL WINAPI _DllMainCRTStartup(HINSTANCE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
  18. {
  19. DisableThreadLibraryCalls(hInst);
  20. return TRUE;
  21. }
  22. #endif
  23. #define NUM_ICONS 8
  24. #define FOURWAY_NUM 7
  25. #define SYSTRAY_ICON_BASE 1024
  26. // used for Win7+ usage inorder to get the direct location of the icon instead of the prior hacks (thanks MS!)
  27. typedef HRESULT (WINAPI *SHELL_NOTIFYICONGETRECT)(const NOTIFYICONIDENTIFIER* identifier, RECT* iconLocation);
  28. SHELL_NOTIFYICONGETRECT g_Shell_NotifyIconGetRect = 0;
  29. // Use a guid to uniquely identify our icon
  30. class __declspec(uuid("B4E5FE9B-6A22-450e-9565-941EF50CFEEB")) CompactIcon;
  31. // wasabi based services for localisation support
  32. api_service *WASABI_API_SVC = 0;
  33. api_language *WASABI_API_LNG = 0;
  34. HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
  35. int config_enabled = 0,
  36. xporhigher = 0,
  37. custom_enabled = 0,
  38. winver = 0,
  39. flip = 0,
  40. on = 1,
  41. update_file = 0,
  42. isX64 = 0,
  43. no_uninstall = 1,
  44. dlg_init = 0;
  45. UINT s_uTaskbarRestart=0;
  46. HWND configwnd = 0;
  47. WNDPROC lpWndProcOld = 0;
  48. HICON Icons[NUM_ICONS] = {0}, dummyIcon = 0;
  49. HBITMAP compact = 0;
  50. fileinfo2W file = {0};
  51. wchar_t ico_pack[MAX_PATH] = {0},
  52. ico_pack_base[MAX_PATH] = {0},
  53. ico_pack_safe[MAX_PATH] = {0},
  54. wa_path[MAX_PATH] = {0},
  55. *ini_file = 0,
  56. szDescription[256] = {0};
  57. int tips[NUM_ICONS] = {
  58. IDS_PREVIOUS_TRACK,
  59. IDS_PLAY_PAUSE,
  60. IDS_STOP,
  61. IDS_NEXT_TRACK,
  62. IDS_OPEN_FILE,
  63. IDS_COMPACT_MODE,
  64. IDS_DECREASE_VOLUME,
  65. IDS_INCREASE_VOLUME,
  66. };
  67. int tips_ex[NUM_ICONS] = {
  68. IDS_HOLD_CTRL,
  69. IDS_HOLD_CTRL,
  70. IDS_HOLD_SHIFT,
  71. -1,
  72. -1,
  73. // IDS_WIN2K_PLUS,
  74. -1,
  75. IDS_CTRL_TO_DECREASE,
  76. IDS_CTRL_TO_INCREASE,
  77. };
  78. LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
  79. BOOL CALLBACK ConfigProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam);
  80. HICON CreateInternalIcon(void);
  81. int FileExists(char* filename);
  82. void config(void);
  83. void quit(void);
  84. int init(void);
  85. void config_write();
  86. void config_read();
  87. extern "C" winampGeneralPurposePlugin plugin =
  88. {
  89. GPPHDR_VER_U,
  90. "nullsoft(gen_tray.dll)",
  91. init,
  92. config,
  93. quit,
  94. };
  95. extern "C" __declspec(dllexport) winampGeneralPurposePlugin * winampGetGeneralPurposePlugin() { return &plugin; }
  96. HWND GetPlaylistWnd(HWND winamp){
  97. HWND pl_wnd = 0;
  98. // get the playlist editor window (either v2.9x method or the older
  99. // for compatibility incase < 2.9x are used
  100. if(SendMessage(winamp,WM_WA_IPC,0,IPC_GETVERSION) >= 0x2900)
  101. {
  102. pl_wnd = (HWND)SendMessage(winamp,WM_WA_IPC,IPC_GETWND_PE,IPC_GETWND);
  103. }
  104. if(!pl_wnd)
  105. {
  106. pl_wnd = FindWindow(L"Winamp PE",0);
  107. }
  108. return pl_wnd;
  109. }
  110. void FormCompactText(wchar_t* szTip, int szTipLength){
  111. int got = 0;
  112. // only update if we really have to (to better mimick Winamp's title behaviour, etc)
  113. // otherwise we query in all cases which can often reflect what appears to be the wrong information since
  114. // the current playlist entry is altered, etc on playlist modification hence an incorrect observation
  115. if(!update_file){
  116. update_file = 1;
  117. file.fileindex = (int)SendMessage(GetPlaylistWnd(plugin.hwndParent),WM_WA_IPC,IPC_PE_GETCURINDEX,0);
  118. got = (int)SendMessage(GetPlaylistWnd(plugin.hwndParent),WM_WA_IPC,IPC_PE_GETINDEXTITLEW,(LPARAM)&file);
  119. }
  120. // if it returns 0 then track information was received
  121. if(!got && file.filetitle[0]){
  122. int time = (int)SendMessage(plugin.hwndParent,WM_WA_IPC,0,IPC_GETOUTPUTTIME);
  123. wchar_t buf[MAX_PATH*2] = {0}, temp[1024] = {0}, *t = temp, *p = 0;
  124. int over = 0, state = 0, blah = 0;
  125. wchar_t stateStr[32] = {0};
  126. switch(SendMessage(plugin.hwndParent,WM_WA_IPC,0,IPC_ISPLAYING)){
  127. case 0:
  128. WASABI_API_LNGSTRINGW_BUF(IDS_STOPPED_STR,stateStr,32);
  129. state = lstrlen(stateStr);
  130. break;
  131. case 3:
  132. WASABI_API_LNGSTRINGW_BUF(IDS_PAUSED_STR,stateStr,32);
  133. state = lstrlen(stateStr);
  134. break;
  135. }
  136. p = file.filetitle;
  137. while(p && *p){
  138. *t++ = *p++;
  139. if(*(p-1) == '&'){
  140. *t++ = '&';
  141. *t++ = '&';
  142. }
  143. }
  144. *t = 0;
  145. StringCchPrintf(buf,MAX_PATH*2,L"%d. %s",(file.fileindex)+1,file.filetitle);
  146. over = lstrlen(buf);
  147. if(over > szTipLength - 1){over = szTipLength - 1;}
  148. lstrcpyn(szTip,buf,szTipLength);
  149. if(time != -1){
  150. time = time/1000;
  151. if(file.filelength[0]){
  152. StringCchPrintf(buf,MAX_PATH*2,L" [%02d:%02d/%s]",(time/60),time%60,file.filelength);
  153. blah = lstrlen(buf);
  154. }
  155. else{
  156. StringCchPrintf(buf,MAX_PATH*2,L" [%02d:%02d]",(time/60),time%60);
  157. blah = lstrlen(buf);
  158. }
  159. }
  160. if((over + blah + state) > szTipLength){
  161. int adj = szTipLength-blah-state-1;
  162. szTip[adj] = 0;
  163. szTip[adj-1] = L'.';
  164. szTip[adj-2] = L'.';
  165. szTip[adj-3] = L'.';
  166. }
  167. if(time != -1){
  168. StringCchCat(szTip,szTipLength,buf);
  169. }
  170. if(state){
  171. StringCchCat(szTip,szTipLength,stateStr);
  172. }
  173. }
  174. // fall back to the Winamp version just incase
  175. else{
  176. wchar_t temp[16] = {0};
  177. StringCchPrintf(temp,16,L"%X",SendMessage(plugin.hwndParent,WM_WA_IPC,0,IPC_GETVERSION));
  178. StringCchPrintf(szTip,szTipLength,L"Winamp %c.%s",temp[0],&temp[2]);
  179. }
  180. }
  181. void free_icons(void){
  182. int i = 0;
  183. for (i = 0; i < NUM_ICONS; i++)
  184. {
  185. if( Icons[i] ) {
  186. DestroyIcon(Icons[i]);
  187. Icons[i] = 0;
  188. }
  189. }
  190. if(dummyIcon) {
  191. DestroyIcon(dummyIcon);
  192. dummyIcon = 0;
  193. }
  194. if(compact) {
  195. DeleteObject(compact);
  196. }
  197. }
  198. void do_icons(int force)
  199. {
  200. static int l=0;
  201. int i=NUM_ICONS;
  202. if (l == config_enabled && !force) return;
  203. if( force ) free_icons();
  204. while (i--)
  205. {
  206. if (l & (1<<i))
  207. {
  208. NOTIFYICONDATAW tnid={0};
  209. tnid.cbSize=sizeof(NOTIFYICONDATAW);
  210. tnid.hWnd=plugin.hwndParent;
  211. tnid.uID=i+SYSTRAY_ICON_BASE;
  212. Shell_NotifyIcon(NIM_DELETE, &tnid);
  213. }
  214. }
  215. l=config_enabled;
  216. if(!on) {return;}
  217. // have to do XP+ specific changes here in order for the icon addition order to appear as expected and not back to front!
  218. for (i = (xporhigher?(NUM_ICONS-1):0); (xporhigher?i>-1:i < NUM_ICONS); (xporhigher?i --:i ++))
  219. {
  220. if (config_enabled & (1<<i))
  221. {
  222. // check if an icon pack has not been set or it's not a valid file that's being passed
  223. // if so then need to use the default icons
  224. if (!ico_pack[0] || !PathFileExists(ico_pack) )
  225. {
  226. if (!Icons[i]) Icons[i] = LoadIcon(plugin.hDllInstance,MAKEINTRESOURCE(IDI_ICON1+i));
  227. if (i == 5) {
  228. compact = (HBITMAP)LoadImage(plugin.hDllInstance,MAKEINTRESOURCE(IDB_BITMAP1),IMAGE_BITMAP,0,0,LR_SHARED);
  229. Icons[i] = CreateInternalIcon();
  230. }
  231. }
  232. else
  233. {
  234. wchar_t* icpb = 0;
  235. lstrcpyn(ico_pack_base,ico_pack,ARRAYSIZE(ico_pack_base));
  236. icpb = ico_pack_base + lstrlen(ico_pack_base) - 1;
  237. while(icpb && *icpb && *icpb != L'\\'){icpb = CharPrev(ico_pack,icpb);}
  238. if (icpb) *icpb = 0;
  239. if (!Icons[i]){
  240. wchar_t entry[MAX_PATH] = {0}, buf[MAX_PATH] = {0};
  241. int compact_loaded = 0;
  242. StringCchPrintf(entry,MAX_PATH,L"ico%d",i+1);
  243. GetPrivateProfileString(L"tray icon pack",entry,buf,buf,ARRAYSIZE(buf),ico_pack);
  244. StringCchPrintf(entry,MAX_PATH,L"%s\\%s",ico_pack_base,buf);
  245. Icons[i] = (HICON)LoadImage(0,entry,(i != 5?IMAGE_ICON:IMAGE_BITMAP),0,0,LR_LOADFROMFILE|LR_SHARED);
  246. if (i == 5) {
  247. compact = (HBITMAP)Icons[i];
  248. if(compact) {
  249. compact_loaded = 1;
  250. }
  251. }
  252. // if this fails then we use the built-in versions
  253. if (!Icons[i]) Icons[i] = LoadIcon(plugin.hDllInstance,MAKEINTRESOURCE(IDI_ICON1+i));
  254. if (i == 5) {
  255. if (!compact_loaded) {
  256. compact = (HBITMAP)LoadImage(plugin.hDllInstance,MAKEINTRESOURCE(IDB_BITMAP1),IMAGE_BITMAP,0,0,LR_SHARED);
  257. }
  258. Icons[i] = CreateInternalIcon();
  259. }
  260. }
  261. }
  262. {
  263. NOTIFYICONDATAW tnid={0};
  264. tnid.cbSize=sizeof(NOTIFYICONDATAW);
  265. tnid.hWnd=plugin.hwndParent;
  266. tnid.uID=i+SYSTRAY_ICON_BASE;
  267. tnid.uFlags=NIF_ICON | NIF_TIP | NIF_MESSAGE | NIF_GUID;
  268. tnid.uCallbackMessage=WM_USER + 2707;
  269. tnid.hIcon=Icons[i];
  270. if(i != 5)
  271. StringCchPrintf(tnid.szTip,sizeof(tnid.szTip)/sizeof(wchar_t),L"%s - Winamp",WASABI_API_LNGSTRINGW(tips[i]));
  272. else
  273. FormCompactText(tnid.szTip,(sizeof(tnid.szTip)/sizeof(wchar_t)));
  274. Shell_NotifyIcon(NIM_ADD, &tnid);
  275. }
  276. }
  277. }
  278. }
  279. void config(void)
  280. {
  281. if(!IsWindow(configwnd))
  282. WASABI_API_DIALOGBOXW(IDD_DIALOG1,0,ConfigProc);
  283. else
  284. SetActiveWindow(configwnd);
  285. }
  286. void quit(void)
  287. {
  288. config_enabled=0;
  289. do_icons(0);
  290. free_icons();
  291. }
  292. BOOL CALLBACK FindTrayWnd(HWND hwnd, LPARAM lParam)
  293. {
  294. wchar_t szClassName[256] = {0};
  295. GetClassName(hwnd, szClassName, 255); // Did we find the Main System Tray? If so, then get its size and quit
  296. if (!lstrcmpi(szClassName, L"TrayNotifyWnd"))
  297. {
  298. HWND* pWnd = (HWND*)lParam;
  299. *pWnd = hwnd;
  300. return FALSE;
  301. }
  302. //Original code I found on Internet were seeking here for system clock and it was assumming that clock is on the right side of tray.
  303. //After that calculated size of tray was adjusted by removing space occupied by clock.
  304. //This is not a good idea - some clocks are ABOVE or somewhere else on the screen. I found that is far safer to just ignore clock space.
  305. return TRUE;
  306. }
  307. BOOL CALLBACK FindToolBarInTrayWnd(HWND hwnd, LPARAM lParam)
  308. {
  309. wchar_t szClassName[256] = {0};
  310. GetClassName(hwnd, szClassName, 255); // Did we find the Main System Tray? If so, then get its size and quit
  311. if (!lstrcmpi(szClassName, L"ToolbarWindow32"))
  312. {
  313. HWND* pWnd = (HWND*)lParam;
  314. *pWnd = hwnd;
  315. return FALSE;
  316. }
  317. return TRUE;
  318. }
  319. HWND GetTrayNotifyWnd(BOOL a_bSeekForEmbedToolbar)
  320. {
  321. HWND hWndTrayNotifyWnd = 0, hWndShellTrayWnd = FindWindow(L"Shell_TrayWnd", 0);
  322. if (hWndShellTrayWnd)
  323. {
  324. EnumChildWindows(hWndShellTrayWnd, FindTrayWnd, (LPARAM)&hWndTrayNotifyWnd);
  325. if(hWndTrayNotifyWnd && IsWindow(hWndTrayNotifyWnd))
  326. {
  327. HWND hWndToolBarWnd = 0;
  328. EnumChildWindows(hWndTrayNotifyWnd, FindToolBarInTrayWnd, (LPARAM)&hWndToolBarWnd);
  329. if(hWndToolBarWnd)
  330. {
  331. return hWndToolBarWnd;
  332. }
  333. }
  334. return hWndTrayNotifyWnd;
  335. }
  336. return hWndShellTrayWnd;
  337. }
  338. typedef BOOL (WINAPI *ISWOW64PROCESS)(HANDLE hProcess,PBOOL Wow64Process);
  339. BOOL IsRunningX64(void){
  340. ISWOW64PROCESS iswow64process = (ISWOW64PROCESS)GetProcAddress(GetModuleHandle(L"kernel32"),"IsWow64Process");
  341. if (iswow64process) {
  342. BOOL Wow64Process = 0;
  343. if(iswow64process(GetCurrentProcess(),&Wow64Process)){
  344. return Wow64Process;
  345. }
  346. }
  347. return FALSE;
  348. }
  349. struct TRAYDATA
  350. {
  351. HWND hwnd;
  352. UINT uID;
  353. UINT uCallbackMessage;
  354. DWORD Reserved[2];
  355. HICON hIcon;
  356. };
  357. typedef struct _TBBUTTON64 {
  358. int iBitmap;
  359. int idCommand;
  360. BYTE fsState;
  361. BYTE fsStyle;
  362. BYTE bReserved[6]; // padding for alignment
  363. DWORD_PTR dwData;
  364. INT_PTR iString;
  365. } TBBUTTON64, NEAR* PTBBUTTON64, *LPTBBUTTON64;
  366. typedef const TBBUTTON64 *LPCTBBUTTON64;
  367. // this is used on Win7+ installs where the OS has a direct api to allow for the querying of the icon position
  368. // most likely added natively due to the notification area fly out
  369. BOOL NotifyIconGetRect(LPRECT a_rcIcon){
  370. if(g_Shell_NotifyIconGetRect){
  371. NOTIFYICONIDENTIFIER niid = {sizeof(NOTIFYICONIDENTIFIER),plugin.hwndParent,SYSTRAY_ICON_BASE+5,0};
  372. return SUCCEEDED(g_Shell_NotifyIconGetRect(&niid,a_rcIcon));
  373. }
  374. return FALSE;
  375. }
  376. //First tracking method: attaches to Tray process and reads data directly, is fast and reliable but will fail if user uses non standard tray software
  377. //It was suggested by Neal Andrews with VB example: http://www.codeproject.com/shell/ctrayiconposition.asp?select=999036&forumid=14631&df=100#xx999036xx
  378. //Ported to C++ by Ireneusz Zielinski
  379. //Made vaguely 64-bit compatible in v2.2 of this plugin
  380. BOOL FindOutPositionOfIconDirectly(HWND a_hWndOwner, const int a_iButtonID, LPRECT a_rcIcon)
  381. {
  382. if(!NotifyIconGetRect(a_rcIcon))
  383. {
  384. DWORD dwTrayProcessID = -1, tbSize = (isX64?sizeof(TBBUTTON64):sizeof(TBBUTTON));
  385. HANDLE hTrayProc = NULL;
  386. int iButtonsCount = 0, iButton = 0;
  387. LPVOID lpData = 0, lpData2 = 0;
  388. BOOL bIconFound = FALSE;
  389. //first of all let's find a Tool bar control embed in Tray window
  390. HWND hWndTray = GetTrayNotifyWnd(TRUE);
  391. if (hWndTray == NULL)
  392. {
  393. return FALSE;
  394. }
  395. //now we have to get an ID of the parent process for system tray
  396. GetWindowThreadProcessId(hWndTray, &dwTrayProcessID);
  397. if(!dwTrayProcessID)
  398. {
  399. return FALSE;
  400. }
  401. // need to use the older PROCESS_ALL_ACCESS define as it otherwise causes
  402. // this to fail on all XP machines but will still work ok on Vista / Win7
  403. #define PROCESS_ALL_ACCESS_XP (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF)
  404. hTrayProc = OpenProcess(PROCESS_ALL_ACCESS_XP/*PROCESS_ALL_ACCESS*/, 0, dwTrayProcessID);
  405. if(hTrayProc == NULL)
  406. {
  407. return FALSE;
  408. }
  409. //now we check how many buttons is there - should be more than 0
  410. iButtonsCount = (int)SendMessage(hWndTray, TB_BUTTONCOUNT, 0, 0);
  411. //We want to get data from another process - it's not possible to just send messages like TB_GETBUTTON with a localy
  412. //allocated buffer for return data. Pointer to localy allocated data has no usefull meaning in a context of another
  413. //process (since Win95) - so we need to allocate some memory inside Tray process.
  414. //We allocate sizeof(TBBUTTON) bytes of memory - because TBBUTTON is the biggest structure we will fetch. But this buffer
  415. //will be also used to get smaller pieces of data like RECT structures.
  416. lpData = VirtualAllocEx(hTrayProc, NULL, tbSize, MEM_COMMIT, PAGE_READWRITE);
  417. if(lpData == NULL || iButtonsCount < 1)
  418. {
  419. CloseHandle(hTrayProc);
  420. return FALSE;
  421. }
  422. for(iButton = 0; iButton < iButtonsCount; iButton++)
  423. {
  424. HWND hWndOfIconOwner = 0;
  425. int iIconId = 0;
  426. //first let's read TBUTTON information about each button in a task bar of tray
  427. SIZE_T dwBytesRead = -1;
  428. TBBUTTON64 buttonData64 = {0};
  429. TBBUTTON buttonData = {0};
  430. TRAYDATA traydata = {0};
  431. SendMessage(hWndTray, TB_GETBUTTON, iButton, (LPARAM)lpData);
  432. ReadProcessMemory(hTrayProc, lpData, (isX64?(LPVOID)&buttonData64:&buttonData), tbSize, &dwBytesRead);
  433. if(dwBytesRead < tbSize)
  434. {
  435. continue;
  436. }
  437. // now let's read extra data associated with each button: there will be a HWND of the window that created an icon and icon ID
  438. ReadProcessMemory(hTrayProc, (LPCVOID)(isX64?buttonData64.dwData:buttonData.dwData), (LPVOID)&traydata, sizeof(TRAYDATA), &dwBytesRead);
  439. if(dwBytesRead < sizeof(TRAYDATA))
  440. {
  441. continue;
  442. }
  443. // will get the hwnd and icon id of the 'button' being checked factoring for x86 and x64 structures
  444. if(!isX64)
  445. {
  446. hWndOfIconOwner = traydata.hwnd;
  447. iIconId = traydata.uID;
  448. }
  449. else
  450. {
  451. LPARAM *tb = (LPARAM*)&traydata;
  452. hWndOfIconOwner = (HWND)tb[0];
  453. iIconId = (int)tb[2];
  454. }
  455. if(hWndOfIconOwner != a_hWndOwner || iIconId != a_iButtonID)
  456. {
  457. continue;
  458. }
  459. //we found our icon - in WinXP/Vista+ it could be hidden - let's check it:
  460. if(buttonData.fsState & TBSTATE_HIDDEN)
  461. {
  462. break;
  463. }
  464. //now just ask a tool bar of rectangle of our icon
  465. SendMessage(hWndTray, TB_GETITEMRECT, iButton, (LPARAM)lpData);
  466. ReadProcessMemory(hTrayProc, lpData, a_rcIcon, sizeof(RECT), &dwBytesRead);
  467. if(dwBytesRead < sizeof(RECT))
  468. {
  469. continue;
  470. }
  471. MapWindowPoints(hWndTray, NULL, (LPPOINT)a_rcIcon, 2);
  472. bIconFound = TRUE;
  473. break;
  474. }
  475. VirtualFreeEx(hTrayProc, lpData, 0, MEM_RELEASE);
  476. VirtualFreeEx(hTrayProc, lpData2, 0, MEM_RELEASE);
  477. CloseHandle(hTrayProc);
  478. return bIconFound;
  479. }
  480. return TRUE;
  481. }
  482. HICON CreateInternalIcon(void)
  483. {
  484. HICON hGrayIcon = 0;
  485. HDC hMainDC = 0, hMemDC1 = 0, hMemDC2 = 0;
  486. BITMAP bmp = {0};
  487. ICONINFO csII = {0}, csGrayII = {0};
  488. // destroy the old version of the icon where possible otherwise we'll get a resource leak
  489. // which can have a nasty effect if allowed to grow too large
  490. if(Icons[5]){
  491. DestroyIcon(Icons[5]);
  492. }
  493. // create a dummy base icon with which to work on (saves having to bundle a blank on in the dll)
  494. if(!dummyIcon){dummyIcon = CreateIcon(plugin.hDllInstance,32,32,1,32,0,0);}
  495. if(!GetIconInfo(dummyIcon,&csII)){return 0;}
  496. if(!(hMainDC = GetDC(plugin.hwndParent)) || !(hMemDC1 = CreateCompatibleDC(hMainDC)) || !(hMemDC2 = CreateCompatibleDC(hMainDC))){
  497. return 0;
  498. }
  499. if(GetObject(csII.hbmColor,sizeof(BITMAP),&bmp))
  500. {
  501. int width = 0, height = 0;
  502. csGrayII.hbmColor = CreateBitmap((width = csII.xHotspot*2),(height = csII.yHotspot*2),bmp.bmPlanes,bmp.bmBitsPixel,0);
  503. if(csGrayII.hbmColor){
  504. int is_playing = (int)SendMessage(plugin.hwndParent,WM_WA_IPC,0,IPC_ISPLAYING), dwLoopY = 0, dwLoopX = 0;
  505. // this is used for the temporary bitmap where the mask is created for the transparency (magic pink fun)
  506. HBITMAP hAndMask = CreateCompatibleBitmap(hMemDC2,width,height);
  507. HDC hAndMaskDC = CreateCompatibleDC(hMemDC2);
  508. SelectObject(hAndMaskDC,hAndMask);
  509. HBITMAP hOldBmp1 = (HBITMAP)SelectObject(hMemDC1,csII.hbmColor);
  510. HBITMAP hOldBmp2 = (HBITMAP)SelectObject(hMemDC2,csGrayII.hbmColor);
  511. SetStretchBltMode(hMemDC2,COLORONCOLOR);
  512. // play or pause or 'blank' image would go here
  513. SelectObject(hMemDC1,compact);
  514. // not the most elegant code but it'll correctly select the play/pause icon as needed based on
  515. // the current playback state and it's flip play/pause state as appropriately
  516. StretchBlt(hMemDC2,0,0,16,16,hMemDC1,(!is_playing?0:(is_playing!=1?(flip?32:0):32)),0,8,8,SRCCOPY);
  517. // open or stop image would go here
  518. StretchBlt(hMemDC2,16,0,16,16,hMemDC1,(!is_playing?8:40),0,8,8,SRCCOPY);
  519. // previous track image
  520. StretchBlt(hMemDC2,0,16,16,16,hMemDC1,16,0,8,8,SRCCOPY);
  521. // next track image
  522. StretchBlt(hMemDC2,16,16,16,16,hMemDC1,24,0,8,8,SRCCOPY);
  523. // process the image now that we've generated it
  524. for(dwLoopX=0;dwLoopX<width;++dwLoopX)
  525. {
  526. for(dwLoopY=0;dwLoopY<height;++dwLoopY)
  527. {
  528. COLORREF MainBitPixel = GetPixel(hMemDC2,dwLoopX,dwLoopY);
  529. // checks for magic pink and then will remove it and clear/set the relevant areas in the image mask
  530. if(MainBitPixel == 0xff00ff)
  531. {
  532. SetPixel(hAndMaskDC,dwLoopX,dwLoopY,RGB(255,255,255));
  533. SetPixel(hMemDC2,dwLoopX,dwLoopY,RGB(0,0,0));
  534. }
  535. else
  536. {
  537. SetPixel(hAndMaskDC,dwLoopX,dwLoopY,RGB(0,0,0));
  538. SetPixel(hMemDC2,dwLoopX,dwLoopY,MainBitPixel);
  539. }
  540. }
  541. }
  542. // set the mask for the transparent areas, etc
  543. csGrayII.hbmMask = hAndMask;
  544. DeleteDC(hAndMaskDC);
  545. SelectObject(hMemDC1,hOldBmp1);
  546. SelectObject(hMemDC2,hOldBmp2);
  547. csGrayII.fIcon = 1;
  548. hGrayIcon = CreateIconIndirect(&csGrayII);
  549. DeleteObject(hAndMask);
  550. }
  551. DeleteObject(csGrayII.hbmColor);
  552. DeleteObject(csGrayII.hbmMask);
  553. }
  554. DeleteObject(csII.hbmColor);
  555. DeleteObject(csII.hbmMask);
  556. DeleteDC(hMemDC1);
  557. DeleteDC(hMemDC2);
  558. ReleaseDC(plugin.hwndParent,hMainDC);
  559. return hGrayIcon;
  560. }
  561. LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  562. {
  563. // this will detect the start of playback and allow us to query for the new compact mode text
  564. // done in 2.1+ to resolve issues with the compact mode text not updating correctly in all cases
  565. if(lParam == IPC_PLAYING_FILE){
  566. update_file=0;
  567. }
  568. if((lParam == IPC_CB_MISC && wParam == IPC_CB_MISC_STATUS)){
  569. if (config_enabled & (1<<5)) {
  570. NOTIFYICONDATAW tnid={0};
  571. tnid.cbSize=sizeof(NOTIFYICONDATAW);
  572. tnid.hWnd=plugin.hwndParent;
  573. tnid.uID=1029;
  574. tnid.uFlags=NIF_ICON|NIF_TIP;
  575. tnid.hIcon=(Icons[5] = CreateInternalIcon());
  576. // force an update if stopping and the playlist is clear
  577. if(!SendMessage(hwnd,WM_WA_IPC,0,IPC_ISPLAYING)){
  578. if(!SendMessage(hwnd,WM_WA_IPC,0,IPC_GETLISTLENGTH)){
  579. file.filetitle[0] = 0;
  580. update_file=0;
  581. }
  582. }
  583. FormCompactText(tnid.szTip,(sizeof(tnid.szTip)/sizeof(wchar_t)));
  584. Shell_NotifyIcon(NIM_MODIFY,&tnid);
  585. }
  586. }
  587. if(message == WM_TIMER && wParam == 64){
  588. if (config_enabled & (1<<5)) {
  589. if(SendMessage(hwnd,WM_WA_IPC,0,IPC_ISPLAYING) == 3){
  590. NOTIFYICONDATAW tnid={0};
  591. tnid.cbSize=sizeof(NOTIFYICONDATAW);
  592. flip = !flip;
  593. tnid.hWnd=plugin.hwndParent;
  594. tnid.uID=1029;
  595. tnid.uFlags=NIF_ICON|NIF_TIP;
  596. tnid.hIcon=(Icons[5] = CreateInternalIcon());
  597. FormCompactText(tnid.szTip,(sizeof(tnid.szTip)/sizeof(wchar_t)));
  598. Shell_NotifyIcon(NIM_MODIFY,&tnid);
  599. }
  600. else
  601. {
  602. NOTIFYICONDATAW tnid={0};
  603. // this resets the play/pause flashing so it's in a known state when not paused
  604. tnid.cbSize=sizeof(NOTIFYICONDATAW);
  605. tnid.hWnd=plugin.hwndParent;
  606. tnid.uID=1029;
  607. tnid.uFlags=(flip?NIF_ICON:0)|NIF_TIP;
  608. // only re-create the icon when it's needed to be done otherwise, just update the tooltip
  609. if(flip)
  610. {
  611. tnid.hIcon=(Icons[5] = CreateInternalIcon());
  612. }
  613. flip = 0;
  614. FormCompactText(tnid.szTip,sizeof(tnid.szTip)/sizeof(wchar_t));
  615. Shell_NotifyIcon(NIM_MODIFY,&tnid);
  616. }
  617. }
  618. }
  619. if (message == WM_USER+2707)
  620. {
  621. switch (LOWORD(lParam))
  622. {
  623. case WM_LBUTTONDOWN:
  624. if (config_enabled) switch (LOWORD(wParam))
  625. {
  626. // previous icon
  627. case 1024:
  628. {
  629. int a;
  630. if ((a= (int)SendMessage(hwnd,WM_USER,0,IPC_ISPLAYING)) == 0) // not playing, let's hit prev
  631. {
  632. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON1,0);
  633. }
  634. else if (a != 3) // restart or full previous action
  635. {
  636. if ((GetKeyState(VK_CONTROL)&0x1000) && SendMessage(hwnd,WM_USER,0,IPC_GETOUTPUTTIME) > 2000 )
  637. {
  638. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON2,0); // restart (only on a ctrl+click)
  639. }
  640. else
  641. {
  642. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON1,0); // move to the previous track and then start
  643. }
  644. }
  645. else
  646. { // prev
  647. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON1,0);
  648. }
  649. }
  650. return 0;
  651. // play/pause icon
  652. case 1025:
  653. if ((GetKeyState(VK_CONTROL)&0x1000) ) // restart the current track
  654. {
  655. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON4,0);
  656. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON2,0);
  657. }
  658. else
  659. {
  660. // do play/pause switching to maintain current usability of the plugin
  661. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON2+(SendMessage(hwnd,WM_USER,0,IPC_ISPLAYING) == 1),0);
  662. }
  663. return 0;
  664. // stop icon
  665. case 1026:
  666. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON4 + ((GetKeyState(VK_SHIFT) & 0x1000)?100:0) ,0);
  667. return 0;
  668. // next icon
  669. case 1027:
  670. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON5,0);
  671. return 0;
  672. // open file(s) icon
  673. case 1028:
  674. SetForegroundWindow(hwnd);
  675. if (GetKeyState(VK_CONTROL) & (1<<15))
  676. SendMessage(hwnd,WM_COMMAND,WINAMP_FILE_LOC,0);
  677. else if (GetKeyState(VK_SHIFT) & (1<<15))
  678. SendMessage(hwnd,WM_COMMAND,WINAMP_FILE_DIR,0);
  679. else
  680. SendMessage(hwnd,WM_COMMAND,WINAMP_FILE_PLAY,0);
  681. return 0;
  682. // 4way mode handling, etc
  683. case 1029:
  684. {
  685. RECT rc = {0}, r = {0};
  686. // Note: this isn't compatible with hidden icons on Win7 (still to be fixed for v2.4)
  687. if(FindOutPositionOfIconDirectly(hwnd,1029,&rc))
  688. {
  689. int i = 0,
  690. // on at least Win7 (possibly earlier) the icon size is different than a fixed size
  691. // so for v2.3 we're just going to split the icon based on the reported size rather
  692. // than assuming it is a 16x16 (really 18x18 icon) as Win7's taskbar is different!
  693. height = (rc.bottom-rc.top)/2,
  694. width = (rc.right-rc.left)/2,
  695. x[4] = {0,width,0,width},
  696. y[4] = {0,0,height,height};
  697. POINT pt = {0};
  698. GetCursorPos(&pt);
  699. for(i = 0; i < 4; i++)
  700. {
  701. CopyRect(&r,&rc);
  702. r.right = r.left+x[i]+width;
  703. r.bottom = r.top+y[i]+height;
  704. if(PtInRect(&r,pt))
  705. {
  706. switch(i)
  707. {
  708. // play/pause icon
  709. case 0:
  710. if ((GetKeyState(VK_CONTROL)&0x1000) ) // restart the current track
  711. {
  712. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON4,0);
  713. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON2,0);
  714. }
  715. else
  716. {
  717. // do play/pause switching to maintain current usability of the plugin
  718. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON2+(SendMessage(hwnd,WM_USER,0,IPC_ISPLAYING) == 1),0);
  719. }
  720. break;
  721. // open file(s) / stop icon
  722. case 1:
  723. if(!SendMessage(hwnd,WM_USER,0,IPC_ISPLAYING)){
  724. SetForegroundWindow(hwnd);
  725. if (GetKeyState(VK_CONTROL) & (1<<15))
  726. SendMessage(hwnd,WM_COMMAND,WINAMP_FILE_LOC,0);
  727. else if (GetKeyState(VK_SHIFT) & (1<<15))
  728. SendMessage(hwnd,WM_COMMAND,WINAMP_FILE_DIR,0);
  729. else
  730. SendMessage(hwnd,WM_COMMAND,WINAMP_FILE_PLAY,0);
  731. }
  732. else{
  733. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON4 + ((GetKeyState(VK_SHIFT) & 0x1000)?100:0) ,0);
  734. }
  735. break;
  736. // previous icon
  737. case 2:
  738. {
  739. int a;
  740. if ((a= (int)SendMessage(hwnd,WM_USER,0,IPC_ISPLAYING)) == 0) // not playing, let's hit prev
  741. {
  742. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON1,0);
  743. }
  744. else if (a != 3) // restart or full previous action
  745. {
  746. if ((GetKeyState(VK_CONTROL)&0x1000) && SendMessage(hwnd,WM_USER,0,IPC_GETOUTPUTTIME) > 2000 )
  747. {
  748. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON2,0); // restart (only on a ctrl+click)
  749. }
  750. else
  751. {
  752. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON1,0); // move to the previous track and then start
  753. }
  754. }
  755. else
  756. { // prev
  757. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON1,0);
  758. }
  759. }
  760. break;
  761. // next icon
  762. case 3:
  763. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON5,0);
  764. break;
  765. }
  766. break;
  767. }
  768. }
  769. }
  770. }
  771. return 0;
  772. // vol down
  773. case 1030:
  774. {
  775. int curvol = (int)SendMessage(hwnd,WM_WA_IPC,-666,IPC_SETVOLUME)-((GetKeyState(VK_CONTROL)&0x1000)?30:10);
  776. if(curvol<0){curvol = 0;}
  777. SendMessage(hwnd,WM_WA_IPC,curvol,IPC_SETVOLUME);
  778. }
  779. return 0;
  780. // vol up
  781. case 1031:
  782. {
  783. int curvol = (int)SendMessage(hwnd,WM_WA_IPC,-666,IPC_SETVOLUME)+((GetKeyState(VK_CONTROL)&0x1000)?30:10);
  784. if(curvol>255){curvol = 255;}
  785. SendMessage(hwnd,WM_WA_IPC,curvol,IPC_SETVOLUME);
  786. }
  787. return 0;
  788. }
  789. break;
  790. case WM_RBUTTONDOWN:
  791. if (config_enabled) switch (LOWORD(wParam))
  792. {
  793. // previousicon
  794. case 1024:
  795. {
  796. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON5,0);
  797. }
  798. break;
  799. // next icon
  800. case 1027:
  801. {
  802. int a;
  803. if ((a= (int)SendMessage(hwnd,WM_USER,0,IPC_ISPLAYING)) == 0) // not playing, let's hit prev
  804. {
  805. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON1,0);
  806. }
  807. else if (a != 3) // restart or full previous action
  808. {
  809. if ((GetKeyState(VK_CONTROL)&0x1000) && SendMessage(hwnd,WM_USER,0,IPC_GETOUTPUTTIME) > 2000 )
  810. {
  811. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON2,0); // restart (only on a ctrl+click)
  812. }
  813. else
  814. {
  815. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON1,0); // move to the previous track and then start
  816. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON2,0);
  817. }
  818. }
  819. else
  820. { // prev
  821. SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON1,0);
  822. }
  823. }
  824. break;
  825. // vol down
  826. case 1031:
  827. {
  828. int curvol = (int)SendMessage(hwnd,WM_WA_IPC,-666,IPC_SETVOLUME)-((GetKeyState(VK_CONTROL)&0x1000)?30:10);
  829. if(curvol<0){curvol = 0;}
  830. SendMessage(hwnd,WM_WA_IPC,curvol,IPC_SETVOLUME);
  831. }
  832. return 0;
  833. // vol up
  834. case 1030:
  835. {
  836. int curvol = (int)SendMessage(hwnd,WM_WA_IPC,-666,IPC_SETVOLUME)+((GetKeyState(VK_CONTROL)&0x1000)?30:10);
  837. if(curvol>255){curvol = 255;}
  838. SendMessage(hwnd,WM_WA_IPC,curvol,IPC_SETVOLUME);
  839. }
  840. return 0;
  841. }
  842. break;
  843. }
  844. }
  845. {
  846. int ret = (int)CallWindowProc(lpWndProcOld,hwnd,message,wParam,lParam);
  847. // do this after passing the main batch of messages onto Winamp/rest of the subclass chain so
  848. // that Winamp will restore its tray icon first and then we do ours (otherwise it looks silly)
  849. if(message == s_uTaskbarRestart)
  850. {
  851. // have to force the icons to be displayed since there are none in the tray at this point
  852. do_icons(1);
  853. }
  854. return ret;
  855. }
  856. }
  857. // GetWindowsVersionRunningOnCompact(...)
  858. //
  859. // Function to get the version of windows being run on
  860. //
  861. // Optionally a 'short version[2]' can be passed into the
  862. // function as 'GetWindowsVersionRunningOnCompact(version)'
  863. // which allows the OS version to be returned for the user to
  864. // be able to make use of
  865. //
  866. int GetWindowsVersionRunningOnCompact(DWORD* version)
  867. {
  868. OSVERSIONINFO osvi = {sizeof(OSVERSIONINFO),0};
  869. int ver_detect = -1;
  870. // Win9x detection
  871. //
  872. // Windows 95 - Major 4 & Minor 0 ver_detect = 1
  873. // Windows 98 - Major 4 & Minor 10 ver_detect = 2
  874. // Windows ME - Major 4 & Minor 90 ver_detect = 3
  875. //
  876. // Win NT detection
  877. //
  878. // Windows NT 3.51 - Major 3 & Minor 51 ver_detect = 4
  879. // Windows NT 4 - Major 4 & Minor 0 ver_detect = 5
  880. // Windows 2000 - Major 5 & Minor 0 ver_detect = 6
  881. // Windows XP - Major 5 & Minor 1 ver_detect = 7
  882. // Windows Server 2003 - Major 5 & Minor 2 ver_detect = 8
  883. // Windows Vista - Major 6 & Minor 0 ver_detect = 9
  884. // Windows 7 - Major 6 & Minor 1 ver_detect = 9
  885. // Windows 8 - Major 6 & Minor 2 ver_detect = 9
  886. // Windows 8.1 - Major 6 & Minor 3 ver_detect = 9
  887. // Windows 10 - Major 10 & Minor 0 ver_detect = 10
  888. // Windows 11 - Major 11 & Minor 0 ver_detect = 11
  889. //
  890. // Unknown OS version ver_detect = -1
  891. GetVersionEx(&osvi);
  892. // is it a Win9x platform that we are running on?
  893. if(osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
  894. {
  895. // Windows 98 (4.10)
  896. if(osvi.dwMinorVersion == 10){
  897. ver_detect = 2;
  898. }
  899. // Windows ME (4.90)
  900. else if(osvi.dwMinorVersion == 90){
  901. ver_detect = 3;
  902. }
  903. // Windows 95 (4.0)
  904. else {
  905. ver_detect = 1;
  906. }
  907. }
  908. // is it a WinNT platform that we are running on?
  909. else if(osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
  910. {
  911. // Windows NT 4 (4.0)
  912. if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0)
  913. {
  914. ver_detect = 5;
  915. }
  916. else if(osvi.dwMajorVersion == 5)
  917. {
  918. // Windows XP (5.1)
  919. if(osvi.dwMinorVersion == 1)
  920. {
  921. ver_detect = 7;
  922. }
  923. // Windows Server 2003 (5.2)
  924. else if(osvi.dwMinorVersion == 2)
  925. {
  926. ver_detect = 8;
  927. }
  928. // Windows 2000 (5.0)
  929. else
  930. {
  931. ver_detect = 6;
  932. }
  933. }
  934. // Windows Vista/7/8/8.1 (6.0)
  935. else if(osvi.dwMajorVersion == 6)
  936. {
  937. ver_detect = 9;
  938. }
  939. // Windows 10 (10.0)
  940. else if(osvi.dwMajorVersion == 10)
  941. {
  942. ver_detect = 10;
  943. }
  944. // Windows 11 (11.0)
  945. else if(osvi.dwMajorVersion == 11)
  946. {
  947. ver_detect = 11;
  948. }
  949. // Windows NT 3.51 (3.51)
  950. else
  951. {
  952. ver_detect = 4;
  953. }
  954. }
  955. else
  956. {
  957. ver_detect = -1;
  958. }
  959. // copies the value into the structure
  960. if(version)
  961. {
  962. *version = MAKELONG(osvi.dwMinorVersion,osvi.dwMajorVersion);
  963. }
  964. return ver_detect;
  965. }
  966. void GetWinampPath(void)
  967. {
  968. wchar_t* p = wa_path;
  969. p += GetModuleFileName(0,wa_path,ARRAYSIZE(wa_path)) - 1;
  970. while(p && *p && *p != L'\\'){p = CharPrev(wa_path,p);}
  971. if (p) *p = 0;
  972. }
  973. int init(void)
  974. {
  975. // loader so that we can get the localisation service api for use
  976. WASABI_API_SVC = (api_service*)SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_API_SERVICE);
  977. if (WASABI_API_SVC == (api_service*)1) WASABI_API_SVC = NULL;
  978. if (!WASABI_API_SVC || WASABI_API_SVC == (api_service *)1)
  979. return GEN_INIT_FAILURE;
  980. xporhigher = ((winver=GetWindowsVersionRunningOnCompact(0))>6);
  981. isX64 = IsRunningX64();
  982. s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
  983. g_Shell_NotifyIconGetRect = (SHELL_NOTIFYICONGETRECT)GetProcAddress(GetModuleHandle(L"SHELL32"),"Shell_NotifyIconGetRect");
  984. waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID);
  985. if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
  986. // need to have this initialised before we try to do anything with localisation features
  987. WASABI_API_START_LANG(plugin.hDllInstance,GenTrayLangGUID);
  988. StringCchPrintf(szDescription, ARRAYSIZE(szDescription),
  989. WASABI_API_LNGSTRINGW(IDS_NULLSOFT_TRAY_CONTROL), PLUGIN_VERSION);
  990. plugin.description = (char*)szDescription;
  991. GetWinampPath();
  992. config_read();
  993. if (IsWindowUnicode(plugin.hwndParent))
  994. lpWndProcOld = (WNDPROC)SetWindowLongPtrW(plugin.hwndParent,GWLP_WNDPROC,(LPARAM)WndProc);
  995. else
  996. lpWndProcOld = (WNDPROC)SetWindowLongPtrA(plugin.hwndParent,GWLP_WNDPROC,(LPARAM)WndProc);
  997. do_icons(0);
  998. return 0;
  999. }
  1000. static HCURSOR link_hand_cursor;
  1001. LRESULT link_handlecursor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1002. {
  1003. LRESULT ret = CallWindowProcW((WNDPROC)GetPropW(hwndDlg, L"link_proc"), hwndDlg, uMsg, wParam, lParam);
  1004. // override the normal cursor behaviour so we have a hand to show it is a link
  1005. if(uMsg == WM_SETCURSOR)
  1006. {
  1007. if((HWND)wParam == hwndDlg)
  1008. {
  1009. if(!link_hand_cursor)
  1010. {
  1011. link_hand_cursor = LoadCursor(NULL, IDC_HAND);
  1012. }
  1013. SetCursor(link_hand_cursor);
  1014. return TRUE;
  1015. }
  1016. }
  1017. return ret;
  1018. }
  1019. void link_startsubclass(HWND hwndDlg, UINT id){
  1020. HWND ctrl = GetDlgItem(hwndDlg, id);
  1021. if(!GetPropW(ctrl, L"link_proc"))
  1022. {
  1023. SetPropW(ctrl, L"link_proc",
  1024. (HANDLE)SetWindowLongPtrW(ctrl, GWLP_WNDPROC, (LONG_PTR)link_handlecursor));
  1025. }
  1026. }
  1027. BOOL CALLBACK ConfigProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)
  1028. {
  1029. switch (uMsg)
  1030. {
  1031. case WM_INITDIALOG:
  1032. {
  1033. int count = CB_ERR;
  1034. configwnd = hwndDlg;
  1035. dlg_init = 1;
  1036. link_startsubclass(hwndDlg,IDC_LINK);
  1037. CheckDlgButton(hwndDlg,IDC_ONOFF,on?BST_CHECKED:BST_UNCHECKED);
  1038. for (int i = 0; i < NUM_ICONS; i++)
  1039. {
  1040. wchar_t str[512] = {0}, tmp[256] = {0}, tmp2[256] = {0};
  1041. CheckDlgButton(hwndDlg,IDC_PREV+i,(config_enabled&(1<<i))?BST_CHECKED:BST_UNCHECKED);
  1042. StringCchPrintf(str,512,L"%s %s",
  1043. WASABI_API_LNGSTRINGW_BUF(tips[i],tmp2,256),
  1044. (tips_ex[i]!=-1?WASABI_API_LNGSTRINGW_BUF(tips_ex[i],tmp,256):L""));
  1045. SetDlgItemText(hwndDlg,IDC_PREV+i,str);
  1046. EnableWindow(GetDlgItem(hwndDlg,IDC_PREV+i),on);
  1047. }
  1048. EnableWindow(GetDlgItem(hwndDlg,IDC_COMBO2),on);
  1049. EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON1),custom_enabled && on);
  1050. EnableWindow(GetDlgItem(hwndDlg,IDC_EDIT1),custom_enabled && on);
  1051. SetDlgItemText(hwndDlg,IDC_EDIT1,ico_pack_safe);
  1052. dlg_init = 0;
  1053. WIN32_FIND_DATA findfile = {0};
  1054. wchar_t use[MAX_PATH] = {0};
  1055. StringCchPrintf(use,MAX_PATH,L"%s\\Plugins\\Tray_Control\\*.*",wa_path);
  1056. HANDLE hFind = FindFirstFile(use, &findfile);
  1057. while(hFind && hFind != INVALID_HANDLE_VALUE)
  1058. {
  1059. if(findfile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
  1060. WIN32_FIND_DATA subfile = {0};
  1061. StringCchPrintf(use,MAX_PATH,L"%s\\Plugins\\Tray_Control\\%s\\*.icp",
  1062. wa_path,findfile.cFileName,findfile.cFileName);
  1063. HANDLE hsubFind = FindFirstFile(use,&subfile);
  1064. while(hsubFind && hsubFind != INVALID_HANDLE_VALUE){
  1065. wchar_t icpname[MAX_PATH] = {0};
  1066. StringCchPrintf(icpname,MAX_PATH,
  1067. L"%s\\Plugins\\Tray_Control\\%s\\%s",
  1068. wa_path,findfile.cFileName,subfile.cFileName);
  1069. if(PathFileExists(icpname))
  1070. {
  1071. // need to ideally make this one work with CharPrev(..)
  1072. wchar_t* p = subfile.cFileName + lstrlen(subfile.cFileName) - 1;
  1073. while(p && *p && *p != L'.'){p = CharPrevW(subfile.cFileName, p);}
  1074. if (p) *p = 0;
  1075. if(!lstrcmpiW(findfile.cFileName,subfile.cFileName))
  1076. {
  1077. SendDlgItemMessageW(hwndDlg,IDC_COMBO2,CB_ADDSTRING,0,(LPARAM)subfile.cFileName);
  1078. }
  1079. else
  1080. {
  1081. wchar_t str[MAX_PATH] = {0};
  1082. int insertpos = 0;
  1083. StringCchPrintf(str,MAX_PATH,L"%s\\%s",findfile.cFileName,subfile.cFileName);
  1084. insertpos = (int)SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_ADDSTRING,0,(LPARAM)str);
  1085. if(insertpos != CB_ERR)
  1086. {
  1087. SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_SETITEMDATA,insertpos,1);
  1088. }
  1089. }
  1090. }
  1091. // if there are no more files then stop the search
  1092. if(hsubFind && !FindNextFile(hsubFind, &subfile))
  1093. {
  1094. FindClose(hsubFind);
  1095. hsubFind = 0;
  1096. }
  1097. }
  1098. }
  1099. // if there are no more files then stop the search
  1100. if(hFind && !FindNextFileW(hFind, &findfile))
  1101. {
  1102. FindClose(hFind);
  1103. hFind = 0;
  1104. }
  1105. }
  1106. EnableWindow(GetDlgItem(hwndDlg,IDC_EDIT1),custom_enabled);
  1107. EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON1),custom_enabled);
  1108. EnableWindow(GetDlgItem(hwndDlg,IDC_PREV6),winver>=6 && on);
  1109. SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_INSERTSTRING,0,
  1110. (LPARAM)WASABI_API_LNGSTRINGW(IDS_DEFAULT_ICONS));
  1111. SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_INSERTSTRING,
  1112. (count = (int)SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_GETCOUNT,0,0)),
  1113. (LPARAM)WASABI_API_LNGSTRINGW(IDS_CUSTOM_ICON_PACK));
  1114. if (!custom_enabled)
  1115. {
  1116. if(ico_pack[0])
  1117. {
  1118. wchar_t base[MAX_PATH] = {0}, *pack = 0, use[MAX_PATH] = {0};
  1119. StringCchPrintf(use,MAX_PATH,L"%s\\Plugins\\Tray_Control\\",wa_path);
  1120. lstrcpyn(base,ico_pack,ARRAYSIZE(base));
  1121. if(StrStrI(base,use))
  1122. {
  1123. pack = PathFindFileName(base);
  1124. if (pack && *pack)
  1125. {
  1126. PathRemoveExtension(pack);
  1127. }
  1128. }
  1129. count = (int)SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_FINDSTRINGEXACT,0,(LPARAM)pack);
  1130. if(count == CB_ERR)
  1131. {
  1132. count = 0;
  1133. }
  1134. }
  1135. else
  1136. {
  1137. count = 0;
  1138. }
  1139. }
  1140. SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_SETCURSEL,count,0);
  1141. SetFocus(GetDlgItem(hwndDlg,IDC_COMBO2));
  1142. }
  1143. break;
  1144. // mimicks the get... links in the winamp preferences
  1145. case WM_DRAWITEM:
  1146. {
  1147. DRAWITEMSTRUCT* lpdis = (DRAWITEMSTRUCT*)lParam;
  1148. if(lpdis->CtlID == IDC_LINK)
  1149. {
  1150. HFONT OldFont = 0;
  1151. LOGFONT lf = {0};
  1152. RECT rc = lpdis->rcItem, r = {0};
  1153. POINT pt = {0};
  1154. int in = 0;
  1155. wchar_t gicpStr[128] = {0};
  1156. WASABI_API_LNGSTRINGW_BUF(IDS_GET_ICON_PACKS,gicpStr,128);
  1157. GetObject(GetCurrentObject(lpdis->hDC,OBJ_FONT),sizeof(lf),&lf);
  1158. lf.lfUnderline = TRUE;
  1159. OldFont = (HFONT)SelectObject(lpdis->hDC,CreateFontIndirect(&lf));
  1160. // Calculate needed size of the control
  1161. DrawText(lpdis->hDC,gicpStr,-1,&rc,DT_VCENTER|DT_SINGLELINE|DT_CALCRECT);
  1162. // Make some more room so the focus rect won't cut letters off
  1163. rc.right = min(rc.right + 2, lpdis->rcItem.right);
  1164. GetWindowRect(lpdis->hwndItem,&r);
  1165. GetCursorPos(&pt);
  1166. in = PtInRect(&r,pt);
  1167. SetTextColor(lpdis->hDC,(COLORREF)RGB((in?255:0),0,(!in?255:0)));
  1168. // Draw the text
  1169. DrawText(lpdis->hDC,gicpStr,-1,&rc,DT_CENTER|DT_VCENTER|DT_SINGLELINE);
  1170. DeleteObject(SelectObject(lpdis->hDC, OldFont));
  1171. }
  1172. }
  1173. break;
  1174. case WM_COMMAND:
  1175. if (LOWORD(wParam) >= IDC_PREV && LOWORD(wParam) <= IDC_PREV+NUM_ICONS)
  1176. {
  1177. config_enabled=0;
  1178. for (int i = 0; i < NUM_ICONS; i++)
  1179. if (IsDlgButtonChecked(hwndDlg,IDC_PREV+i))
  1180. config_enabled |= 1<<i;
  1181. do_icons(0);
  1182. }
  1183. else if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
  1184. {
  1185. GetDlgItemText(hwndDlg,IDC_EDIT1,ico_pack_safe,ARRAYSIZE(ico_pack_safe));
  1186. config_write();
  1187. EndDialog(hwndDlg,0);
  1188. configwnd = 0;
  1189. }
  1190. else if (LOWORD(wParam) == IDC_ONOFF)
  1191. {
  1192. on = (IsDlgButtonChecked(hwndDlg,LOWORD(wParam))==BST_CHECKED);
  1193. for (int i = 0; i < NUM_ICONS; i++)
  1194. {
  1195. EnableWindow(GetDlgItem(hwndDlg,IDC_PREV+i),on);
  1196. }
  1197. EnableWindow(GetDlgItem(hwndDlg,IDC_PREV6),winver>=6 && on);
  1198. EnableWindow(GetDlgItem(hwndDlg,IDC_COMBO2),on);
  1199. EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON1),custom_enabled && on);
  1200. EnableWindow(GetDlgItem(hwndDlg,IDC_EDIT1),custom_enabled && on);
  1201. do_icons(1);
  1202. }
  1203. else if (LOWORD(wParam) == ID_INFO)
  1204. {
  1205. wchar_t str[2560] = {0}, str1[2048] = {0};
  1206. StringCchPrintf(str, 2560, WASABI_API_LNGSTRINGW_BUF(IDS_CONFIG_INFO, str1, 2048), wa_path);
  1207. MessageBoxW(hwndDlg,str,szDescription,0);
  1208. }
  1209. else if (LOWORD(wParam) == IDC_LINK && HIWORD(wParam) == BN_CLICKED)
  1210. {
  1211. SendMessage(plugin.hwndParent,WM_WA_IPC,(WPARAM)"https://winampheritage.com/plugin/nullsoft-tray-control-plug-in-icon-pack/222396",IPC_OPEN_URL);
  1212. }
  1213. else if (LOWORD(wParam) == IDC_BUTTON1)
  1214. {
  1215. OPENFILENAME of = {sizeof(OPENFILENAME),0};
  1216. wchar_t titleStr[128], filterStr[128] = {0};
  1217. of.hwndOwner = hwndDlg;
  1218. of.hInstance = plugin.hDllInstance;
  1219. of.lpstrFilter = WASABI_API_LNGSTRINGW_BUF(IDS_OFD_FILTER_STR,filterStr,128);
  1220. StringCchCat(filterStr+lstrlen(filterStr)+1,128,L"*.icp");
  1221. of.nMaxCustFilter = 64;
  1222. of.lpstrFile = ico_pack;
  1223. of.nMaxFile = ARRAYSIZE(ico_pack);
  1224. of.lpstrTitle = WASABI_API_LNGSTRINGW_BUF(IDS_OFD_TITLE_STR,titleStr,128);
  1225. of.nMaxFileTitle = lstrlen(of.lpstrTitle);
  1226. of.Flags = OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT|OFN_EXPLORER|OFN_PATHMUSTEXIST|OFN_CREATEPROMPT|OFN_ENABLESIZING|OFN_ALLOWMULTISELECT;
  1227. of.lpstrDefExt = L"icp";
  1228. if(GetOpenFileName(&of))
  1229. {
  1230. SetDlgItemText(hwndDlg,IDC_EDIT1,ico_pack);
  1231. do_icons(1);
  1232. }
  1233. }
  1234. else if (LOWORD(wParam) == IDC_EDIT1 && HIWORD(wParam)==EN_CHANGE)
  1235. {
  1236. if(!dlg_init)
  1237. {
  1238. GetDlgItemText(hwndDlg,IDC_EDIT1,ico_pack,ARRAYSIZE(ico_pack));
  1239. do_icons(1);
  1240. }
  1241. }
  1242. else if (LOWORD(wParam) == IDC_COMBO2 && HIWORD(wParam)== CBN_SELCHANGE)
  1243. {
  1244. wchar_t buf[MAX_PATH] = {0};
  1245. int cursel = (int)SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_GETCURSEL,0,0);
  1246. SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_GETLBTEXT,cursel,(LPARAM)buf);
  1247. custom_enabled=0;
  1248. if(cursel && cursel != CB_ERR)
  1249. {
  1250. if(cursel != (SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_GETCOUNT,0,0)-1))
  1251. {
  1252. // this will detect if it was a multi-entry style icon pack and form as required
  1253. if(!SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_GETITEMDATA,cursel,0))
  1254. {
  1255. StringCchPrintf(ico_pack,MAX_PATH,L"%s\\Plugins\\Tray_Control\\%s\\%s.icp",wa_path,buf,buf);
  1256. }
  1257. else
  1258. {
  1259. StringCchPrintf(ico_pack,MAX_PATH,L"%s\\Plugins\\Tray_Control\\%s.icp",wa_path,buf);
  1260. }
  1261. }
  1262. else
  1263. {
  1264. custom_enabled=1;
  1265. GetDlgItemText(hwndDlg,IDC_EDIT1,ico_pack,ARRAYSIZE(ico_pack));
  1266. }
  1267. }
  1268. else
  1269. {
  1270. ico_pack[0] = 0;
  1271. }
  1272. EnableWindow(GetDlgItem(hwndDlg,IDC_EDIT1),custom_enabled);
  1273. EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON1),custom_enabled);
  1274. do_icons(1);
  1275. }
  1276. break;
  1277. }
  1278. return FALSE;
  1279. }
  1280. void config_read(void)
  1281. {
  1282. // this will only correctly work with Winamp 2.9+/5.x+
  1283. // see IPC_GETINIFILE for a way to query the location of Winamp.ini correctly
  1284. // whatever version of Winamp is being run on
  1285. // as of v2.41 this now uses IPC_GETINIFILEW though it's trivial to use
  1286. // IPC_GETINIFILE as a fallback on older clients (built for 5.58+)
  1287. ini_file=(wchar_t*)SendMessage(plugin.hwndParent,WM_USER,0,IPC_GETINIFILEW);
  1288. config_enabled = GetPrivateProfileInt(PLUGIN_NAME,L"BEN",config_enabled,ini_file);
  1289. custom_enabled = GetPrivateProfileInt(PLUGIN_NAME,L"custom",custom_enabled,ini_file);
  1290. on = GetPrivateProfileInt(PLUGIN_NAME,L"on",on,ini_file);
  1291. GetPrivateProfileString(PLUGIN_NAME,L"ico_pack",ico_pack,ico_pack,ARRAYSIZE(ico_pack),ini_file);
  1292. GetPrivateProfileString(PLUGIN_NAME,L"ico_pack_safe",ico_pack_safe,ico_pack_safe,ARRAYSIZE(ico_pack_safe),ini_file);
  1293. }
  1294. void config_write(void)
  1295. {
  1296. if(!no_uninstall) return;
  1297. wchar_t string[32] = {0};
  1298. StringCchPrintf(string,32,L"%d",config_enabled);
  1299. WritePrivateProfileString(PLUGIN_NAME,L"BEN",string,ini_file);
  1300. WritePrivateProfileString(PLUGIN_NAME,L"ico_pack",ico_pack,ini_file);
  1301. WritePrivateProfileString(PLUGIN_NAME,L"ico_pack_safe",ico_pack_safe,ini_file);
  1302. StringCchPrintf(string,32,L"%d",custom_enabled);
  1303. WritePrivateProfileString(PLUGIN_NAME,L"custom",string,ini_file);
  1304. StringCchPrintf(string,32,L"%d",on);
  1305. WritePrivateProfileString(PLUGIN_NAME,L"on",string,ini_file);
  1306. }
  1307. #ifdef __cplusplus
  1308. extern "C" {
  1309. #endif
  1310. __declspec(dllexport) int winampUninstallPlugin(HINSTANCE hDllInst, HWND hwndDlg, int param){
  1311. // prompt to remove our settings with default as no (just incase)
  1312. if(MessageBoxW(hwndDlg,WASABI_API_LNGSTRINGW(IDS_DO_YOU_ALSO_WANT_TO_REMOVE_SETTINGS),
  1313. szDescription,MB_YESNO|MB_DEFBUTTON2) == IDYES)
  1314. {
  1315. WritePrivateProfileString(PLUGIN_NAME,0,0,ini_file);
  1316. no_uninstall = 0;
  1317. }
  1318. // as we're doing too much in subclasses, etc we cannot allow for on-the-fly removal so need to do a normal reboot
  1319. return GEN_PLUGIN_UNINSTALL_REBOOT;
  1320. }
  1321. #ifdef __cplusplus
  1322. }
  1323. #endif