1 /*
2   LICENSE
3   -------
4 Copyright 2005-2013 Nullsoft, Inc.
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without modification,
8 are permitted provided that the following conditions are met:
9 
10   * Redistributions of source code must retain the above copyright notice,
11     this list of conditions and the following disclaimer.
12 
13   * Redistributions in binary form must reproduce the above copyright notice,
14     this list of conditions and the following disclaimer in the documentation
15     and/or other materials provided with the distribution.
16 
17   * Neither the name of Nullsoft nor the names of its contributors may be used to
18     endorse or promote products derived from this software without specific prior written permission.
19 
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
21 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
22 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
26 IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
27 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29 
30 /*
31     TO DO
32     -----
33     -done/v1.06:
34         -(nothing yet)
35         -
36         -
37     -to do/v1.06:
38         -FFT: high freq. data kinda sucks because of the 8-bit samples we get in;
39             look for justin to put 16-bit vis data into wa5.
40         -make an 'advanced view' button on config panel; hide complicated stuff
41             til they click that.
42         -put an asterisk(*) next to the 'max framerate' values that
43             are ideal (given the current windows display mode or selected FS dispmode).
44         -or add checkbox: "smart sync"
45             -> matches FPS limit to nearest integer divisor of refresh rate.
46         -debug.txt/logging support!
47         -audio: make it a DSP plugin? then we could get the complete, continuous waveform
48             and overlap our waveform windows, so we'd never miss a brief high note.
49         -bugs:
50             -vms plugins sometimes freeze after a several-minute pause; I've seen it
51                 with most of them.  hard to repro, though.
52             -running FS on monitor 2, hit ALT-TAB -> minimizes!!!
53                 -but only if you let go of TAB first.  Let go of ALT first and it's fine!
54                 -> means it's related to the keyup...
55             -fix delayloadhelper leak; one for each launch to config panel/plugin.
56             -also, delayload(d3d9.dll) still leaks, if plugin has error initializing and
57                 quits by returning false from PluginInitialize().
58         -add config panel option to ignore fake-fullscreen tips
59             -"tip" boxes in dxcontext.cpp
60             -"notice" box on WM_ACTIVATEAPP?
61         -desktop mode:
62             -icon context menus: 'send to', 'cut', and 'copy' links do nothing.
63                 -http://netez.com/2xExplorer/shellFAQ/bas_context.html
64             -create a 2nd texture to render all icon text labels into
65                 (they're the sole reason that desktop mode is slow)
66             -in UpdateIconBitmaps, don't read the whole bitmap and THEN
67                 realize it's a dupe; try to compare icon filename+index or somethign?
68             -DRAG AND DROP.  COMPLICATED; MANY DETAILS.
69                 -http://netez.com/2xExplorer/shellFAQ/adv_drag.html
70                 -http://www.codeproject.com/shell/explorerdragdrop.asp
71                 -hmm... you can't drag icons between the 2 desktops (ugh)
72             -multiple delete/open/props/etc
73             -delete + enter + arrow keys.
74             -try to solve mysteries w/ShellExecuteEx() and desktop *shortcuts* (*.lnk).
75             -(notice that when icons are selected, they get modulated by the
76                 highlight color, when they should be blended 50% with that color.)
77 
78     ---------------------------
79     final touches:
80         -Tests:
81             -make sure desktop still functions/responds properly when winamp paused
82             -desktop mode + multimon:
83                 -try desktop mode on all monitors
84                 -try moving taskbar around; make sure icons are in the
85                     right place, that context menus (general & for
86                     specific icons) pop up in the right place, and that
87                     text-off-left-edge is ok.
88                 -try setting the 2 monitors to different/same resolutions
89         -check tab order of config panel controls!
90         -Clean All
91         -build in release mode to include in the ZIP
92         -leave only one file open in workspace: README.TXT.
93         -TEMPORARILY "ATTRIB -R" ALL FILES BEFORE ZIPPING THEM!
94 
95     ---------------------------
96     KEEP IN VIEW:
97         -EMBEDWND:
98             -kiv: on resize of embedwnd, it's out of our control; winamp
99                 resizes the child every time the mouse position changes,
100                 and we have to cleanup & reallocate everything, b/c we
101                 can't tell when the resize begins & ends.
102                 [justin said he'd fix in wa5, though]
103             -kiv: with embedded windows of any type (plugin, playlist, etc.)
104                 you can't place the winamp main wnd over them.
105             -kiv: embedded windows are child windows and don't get the
106                 WM_SETFOCUS or WM_KILLFOCUS messages when they get or lose
107                 the focus.  (For a workaround, see milkdrop & scroll lock key.)
108             -kiv: tiny bug (IGNORE): when switching between embedwnd &
109                 no-embedding, the window gets scooted a tiny tiny bit.
110         -kiv: fake fullscreen mode w/multiple monitors: there is no way
111             to keep the taskbar from popping up [potentially overtop of
112             the plugin] when you click on something besides the plugin.
113             To get around this, use true fullscreen mode.
114         -kiv: max_fps implementation assumptions:
115             -that most computers support high-precision timer
116             -that no computers [regularly] sleep for more than 1-2 ms
117                 when you call Sleep(1) after timeBeginPeriod(1).
118         -reminder: if vms_desktop.dll's interface needs changed,
119             it will have to be renamed!  (version # upgrades are ok
120             as long as it won't break on an old version; if the
121             new functionality is essential, rename the DLL.)
122 
123     ---------------------------
124     REMEMBER:
125         -GF2MX + GF4 have icon scooting probs in desktop mode
126             (when taskbar is on upper or left edge of screen)
127         -Radeon is the one w/super slow text probs @ 1280x1024.
128             (it goes unstable after you show playlist AND helpscr; -> ~1 fps)
129         -Mark's win98 machine has hidden cursor (in all modes),
130             but no one else seems to have this problem.
131         -links:
132             -win2k-only-style desktop mode: (uses VirtualAllocEx, vs. DLL Injection)
133                 http://www.digiwar.com/scripts/renderpage.php?section=2&subsection=2
134             -http://www.experts-exchange.com/Programming/Programming_Platforms/Win_Prog/Q_20096218.html
135 */
136 
137 #include "api.h"
138 #include "pluginshell.h"
139 #include "utility.h"
140 #include "defines.h"
141 #include "shell_defines.h"
142 #include "resource.h"
143 #include "vis.h"
144 #include <multimon.h>
145 #include "../Winamp/wa_ipc.h"
146 #include "../nu/AutoCharFn.h"
147 #include <mmsystem.h>
148 #pragma comment(lib,"winmm.lib")    // for timeGetTime
149 
150 // STATE VALUES & VERTEX FORMATS FOR HELP SCREEN TEXTURE:
151 #define TEXT_SURFACE_NOT_READY  0
152 #define TEXT_SURFACE_REQUESTED  1
153 #define TEXT_SURFACE_READY      2
154 #define TEXT_SURFACE_ERROR      3
155 typedef struct _HELPVERTEX
156 {
157 	float x, y;      // screen position
158 	float z;         // Z-buffer depth
159 	DWORD Diffuse;   // diffuse color. also acts as filler; aligns struct to 16 bytes (good for random access/indexed prims)
160 	float tu, tv;    // texture coordinates for texture #0
161 } HELPVERTEX, *LPHELPVERTEX;
162 #define HELP_VERTEX_FORMAT (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1)
163 typedef struct _SIMPLEVERTEX
164 {
165 	float x, y;      // screen position
166 	float z;         // Z-buffer depth
167 	DWORD Diffuse;   // diffuse color. also acts as filler; aligns struct to 16 bytes (good for random access/indexed prims)
168 } SIMPLEVERTEX, *LPSIMPLEVERTEX;
169 #define SIMPLE_VERTEX_FORMAT (D3DFVF_XYZ | D3DFVF_DIFFUSE)
170 
171 extern wchar_t* g_szHelp;
172 extern int g_szHelp_W;
173 extern winampVisModule mod1;
174 
175 // resides in vms_desktop.dll/lib:
176 void getItemData(int x);
177 
178 
CPluginShell()179 CPluginShell::CPluginShell()
180 {
181 	// this should remain empty!
182 }
183 
~CPluginShell()184 CPluginShell::~CPluginShell()
185 {
186 	// this should remain empty!
187 }
188 
GetScreenMode()189 eScrMode  CPluginShell::GetScreenMode()
190 {
191 	return m_screenmode;
192 };
GetFrame()193 int       CPluginShell::GetFrame()
194 {
195 	return m_frame;
196 };
GetTime()197 float     CPluginShell::GetTime()
198 {
199 	return m_time;
200 };
GetFps()201 float     CPluginShell::GetFps()
202 {
203 	return m_fps;
204 };
GetPluginWindow()205 HWND      CPluginShell::GetPluginWindow()
206 {
207 	if (m_lpDX) return m_lpDX->GetHwnd();       else return NULL;
208 };
GetWidth()209 int       CPluginShell::GetWidth()
210 {
211 	if (m_lpDX) return m_lpDX->m_client_width;  else return 0;
212 };
GetHeight()213 int       CPluginShell::GetHeight()
214 {
215 	if (m_lpDX) return m_lpDX->m_client_height; else return 0;
216 };
GetCanvasMarginX()217 int       CPluginShell::GetCanvasMarginX()
218 {
219 	if (m_lpDX && m_screenmode==WINDOWED) return (m_lpDX->m_client_width  - m_lpDX->m_REAL_client_width)/2; else return 0;
220 };
GetCanvasMarginY()221 int       CPluginShell::GetCanvasMarginY()
222 {
223 	if (m_lpDX && m_screenmode==WINDOWED) return (m_lpDX->m_client_height - m_lpDX->m_REAL_client_height)/2; else return 0;
224 };
GetWinampWindow()225 HWND      CPluginShell::GetWinampWindow()
226 {
227 	return m_hWndWinamp;
228 };
GetInstance()229 HINSTANCE CPluginShell::GetInstance()
230 {
231 	return m_hInstance;
232 };
GetPluginsDirPath()233 wchar_t* CPluginShell::GetPluginsDirPath()
234 {
235 	return m_szPluginsDirPath;
236 };
GetConfigIniFile()237 wchar_t* CPluginShell::GetConfigIniFile()
238 {
239 	return m_szConfigIniFile;
240 };
GetConfigIniFileA()241 char* CPluginShell::GetConfigIniFileA()
242 {
243 	return m_szConfigIniFileA;
244 }
GetFontHeight(eFontIndex idx)245 int       CPluginShell::GetFontHeight(eFontIndex idx)
246 {
247 	if (idx >= 0 && idx < NUM_BASIC_FONTS + NUM_EXTRA_FONTS) return m_fontinfo[idx].nSize; else return 0;
248 };
GetBitDepth()249 int       CPluginShell::GetBitDepth()
250 {
251 	return m_lpDX->GetBitDepth();
252 };
GetDevice()253 LPDIRECT3DDEVICE9 CPluginShell::GetDevice()
254 {
255 	if (m_lpDX) return m_lpDX->m_lpDevice; else return NULL;
256 };
GetCaps()257 D3DCAPS9* CPluginShell::GetCaps()
258 {
259 	if (m_lpDX) return &(m_lpDX->m_caps);  else return NULL;
260 };
GetBackBufFormat()261 D3DFORMAT CPluginShell::GetBackBufFormat()
262 {
263 	if (m_lpDX) return m_lpDX->m_current_mode.display_mode.Format; else return D3DFMT_UNKNOWN;
264 };
GetBackBufZFormat()265 D3DFORMAT CPluginShell::GetBackBufZFormat()
266 {
267 	if (m_lpDX) return m_lpDX->GetZFormat(); else return D3DFMT_UNKNOWN;
268 };
GetFont(eFontIndex idx)269 LPD3DXFONT CPluginShell::GetFont(eFontIndex idx)
270 {
271 	if (idx >= 0 && idx < NUM_BASIC_FONTS + NUM_EXTRA_FONTS) return m_d3dx_font[idx]; else return NULL;
272 };
GetDriverFilename()273 char* CPluginShell::GetDriverFilename()
274 {
275 	if (m_lpDX) return m_lpDX->GetDriver(); else return NULL;
276 };
GetDriverDescription()277 char* CPluginShell::GetDriverDescription()
278 {
279 	if (m_lpDX) return m_lpDX->GetDesc(); else return NULL;
280 };
281 
InitNondx9Stuff()282 int CPluginShell::InitNondx9Stuff()
283 {
284 	timeBeginPeriod(1);
285 	m_fftobj.Init(576, NUM_FREQUENCIES);
286 	if (!InitGDIStuff()) return false;
287 	return AllocateMyNonDx9Stuff();
288 }
289 
CleanUpNondx9Stuff()290 void CPluginShell::CleanUpNondx9Stuff()
291 {
292 	timeEndPeriod(1);
293 	CleanUpMyNonDx9Stuff();
294 	CleanUpGDIStuff();
295 	m_fftobj.CleanUp();
296 }
297 
InitGDIStuff()298 int CPluginShell::InitGDIStuff()
299 {
300 	wchar_t title[64];
301 	// note: messagebox parent window should be NULL here, because lpDX is still NULL!
302 	for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++)
303 	{
304 		if (!(m_font[i] = CreateFontW(m_fontinfo[i].nSize, 0, 0, 0, m_fontinfo[i].bBold ? 900 : 400, m_fontinfo[i].bItalic, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, m_fontinfo[i].bAntiAliased ? ANTIALIASED_QUALITY : DEFAULT_QUALITY, DEFAULT_PITCH, m_fontinfo[i].szFace)))
305 		{
306 			MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_CREATING_GDI_FONTS),
307 					    WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
308 					    MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
309 			return false;
310 		}
311 	}
312 
313 	if (!(m_main_menu = WASABI_API_LOADMENU(IDR_WINDOWED_CONTEXT_MENU)))
314 	{
315 		MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_LOADING_MAIN_MENU),
316 				    WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
317 				    MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
318 		return false;
319 	}
320 
321 	if (!(m_context_menu = GetSubMenu(m_main_menu, 0)))
322 	{
323 		MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_LOADING_CONTEXT_MENU),
324 				    WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
325 				    MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
326 		return false;
327 	}
328 
329 	return true;
330 }
331 
CleanUpGDIStuff()332 void CPluginShell::CleanUpGDIStuff()
333 {
334 	for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++)
335 	{
336 		if (m_font[i])
337 		{
338 			DeleteObject(m_font[i]);
339 			m_font[i] = NULL;
340 		}
341 	}
342 
343 	/*if (m_context_menu)
344 	{
345 	    DestroyMenu(m_context_menu);
346 	    m_context_menu = NULL;
347 	}*/
348 
349 	if (m_main_menu)
350 	{
351 		DestroyMenu(m_main_menu);
352 		m_main_menu = NULL;
353 	}
354 
355 	//CleanUpMyGDIStuff();
356 }
357 
InitVJStuff(RECT * pClientRect)358 int CPluginShell::InitVJStuff(RECT* pClientRect)
359 {
360 	wchar_t title[64];
361 	// Init VJ mode (second window for text):
362 	if (m_vj_mode)
363 	{
364 		DWORD dwStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_SYSMENU;
365 		POINT upper_left_corner;
366 		upper_left_corner.x = 0;
367 		upper_left_corner.y = 0;
368 
369 		// Create direct 3d & get some infos
370 		if (!(m_vjd3d9 = Direct3DCreate9(D3D_SDK_VERSION)))
371 		{
372 			MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_CREATING_DIRECT3D_DEVICE_FOR_VJ_MODE),
373 					    WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64), MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
374 			return false;
375 		}
376 
377 		// Get ordinal adapter # for the currently-selected Windowed Mode display adapter
378 		int ordinal_adapter = D3DADAPTER_DEFAULT;
379 		int nAdapters = m_vjd3d9->GetAdapterCount();
380 		for (int i=0; i<nAdapters; i++)
381 		{
382 			D3DADAPTER_IDENTIFIER9 temp;
383 			if ((m_vjd3d9->GetAdapterIdentifier(i, /*D3DENUM_NO_WHQL_LEVEL*/ 0, &temp) == D3D_OK) &&
384 			    (memcmp(&temp.DeviceIdentifier, &m_adapter_guid_windowed, sizeof(GUID))==0))
385 			{
386 				ordinal_adapter = i;
387 				break;
388 			}
389 		}
390 
391 		// Get current display mode for windowed-mode adapter:
392 		D3DDISPLAYMODE dm;
393 		if (D3D_OK != m_vjd3d9->GetAdapterDisplayMode(ordinal_adapter, &dm))
394 		{
395 			MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_VJ_MODE_INIT_ERROR),
396 					    WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
397 					    MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
398 			return false;
399 		}
400 
401 		// And get the upper-left corner of the monitor for it:
402 		HMONITOR hMon = m_vjd3d9->GetAdapterMonitor(ordinal_adapter);
403 		if (hMon)
404 		{
405 			MONITORINFO mi;
406 			mi.cbSize = sizeof(mi);
407 			if (GetMonitorInfo(hMon, &mi))
408 			{
409 				upper_left_corner.x = mi.rcWork.left;
410 				upper_left_corner.y = mi.rcWork.top;
411 			}
412 		}
413 
414 		// CREATE THE WINDOW
415 
416 		RECT rect;
417 		if (pClientRect)
418 		{
419 			rect = *pClientRect;
420 			AdjustWindowRect(&rect, dwStyle, 0); // convert client->wnd
421 		}
422 		else
423 		{
424 			SetRect(&rect, 0, 0, 384, 384);
425 			AdjustWindowRect(&rect, dwStyle, 0); // convert client->wnd
426 
427 			rect.right  -= rect.left;
428 			rect.left   = 0;
429 			rect.bottom -= rect.top;
430 			rect.top    = 0;
431 
432 			rect.top    += upper_left_corner.y+32;
433 			rect.left   += upper_left_corner.x+32;
434 			rect.right  += upper_left_corner.x+32;
435 			rect.bottom += upper_left_corner.y+32;
436 		}
437 
438 		WNDCLASS wc = {0};
439 		wc.lpfnWndProc = VJModeWndProc;				// our window procedure
440 		wc.hInstance = GetInstance();	// hInstance of DLL
441 		wc.hIcon = LoadIcon(GetInstance(), MAKEINTRESOURCE(IDI_PLUGIN_ICON));
442 		wc.lpszClassName = TEXT_WINDOW_CLASSNAME;			// our window class name
443 		wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; // CS_DBLCLKS lets the window receive WM_LBUTTONDBLCLK, for toggling fullscreen mode...
444 		wc.cbWndExtra = sizeof(DWORD);
445 		wc.hCursor = LoadCursor(NULL, IDC_ARROW);
446 		wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
447 
448 		if (!RegisterClass(&wc))
449 		{
450 			MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_REGISTERING_WINDOW_CLASS_FOR_TEXT_WINDOW),
451 					    WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
452 					    MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
453 			return false;
454 		}
455 		m_bTextWindowClassRegistered = true;
456 
457 		//DWORD nThreadID;
458 		//CreateThread(NULL, 0, TextWindowThread, &rect, 0, &nThreadID);
459 
460 		// Create the text window
461 		m_hTextWnd = CreateWindowEx(
462 		               0,
463 		               TEXT_WINDOW_CLASSNAME,				// our window class name
464 		               TEXT_WINDOW_CLASSNAME,				// use description for a window title
465 		               dwStyle,
466 		               rect.left, rect.top,								// screen position (read from config)
467 		               rect.right - rect.left, rect.bottom - rect.top,  // width & height of window (need to adjust client area later)
468 		               NULL,								// parent window (winamp main window)
469 		               NULL,								// no menu
470 		               GetInstance(),						// hInstance of DLL
471 		               NULL
472 		             ); // no window creation data
473 
474 		if (!m_hTextWnd)
475 		{
476 			MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_CREATING_VJ_WINDOW),
477 					   WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
478 					   MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
479 			return false;
480 		}
481 
482 		SetWindowLongPtr(m_hTextWnd, GWLP_USERDATA, (LONG_PTR)this);
483 
484 		GetClientRect(m_hTextWnd, &rect);
485 		m_nTextWndWidth  = rect.right-rect.left;
486 		m_nTextWndHeight = rect.bottom-rect.top;
487 
488 
489 		// Create the device
490 		D3DPRESENT_PARAMETERS pres_param;
491 		ZeroMemory(&pres_param,sizeof(pres_param));
492 		pres_param.BackBufferCount = 0;
493 		pres_param.BackBufferFormat = dm.Format;
494 		pres_param.BackBufferWidth  = rect.right - rect.left;
495 		pres_param.BackBufferHeight = rect.bottom - rect.top;
496 		pres_param.hDeviceWindow = m_hTextWnd;
497 		pres_param.AutoDepthStencilFormat = D3DFMT_D16;
498 		pres_param.EnableAutoDepthStencil = FALSE;
499 		pres_param.SwapEffect = D3DSWAPEFFECT_DISCARD;
500 		pres_param.MultiSampleType = D3DMULTISAMPLE_NONE;
501 		pres_param.Flags = 0;
502 		pres_param.FullScreen_RefreshRateInHz = 0;
503 		pres_param.PresentationInterval       = D3DPRESENT_INTERVAL_IMMEDIATE;//D3DPRESENT_INTERVAL_ONE;//D3DPRESENT_INTERVAL_IMMEDIATE;//m_current_mode.allow_page_tearing ? D3DPRESENT_INTERVAL_IMMEDIATE : D3DPRESENT_INTERVAL_ONE;//D3DPRESENT_INTERVAL_IMMEDIATE;//D3DPRESENT_INTERVAL_ONE;
504 		//pres_param.FullScreen_PresentationInterval = 0;
505 		pres_param.Windowed = TRUE;
506 
507 		HRESULT hr;
508 		if (D3D_OK != (hr = m_vjd3d9->CreateDevice(ordinal_adapter,//D3DADAPTER_DEFAULT,
509 		                    D3DDEVTYPE_HAL,
510 		                    m_hTextWnd,
511 		                    D3DCREATE_SOFTWARE_VERTEXPROCESSING,
512 		                    &pres_param,
513 		                    &m_vjd3d9_device)))
514 		{
515 			m_vjd3d9_device = NULL;
516 			MessageBoxW(m_lpDX->GetHwnd(), WASABI_API_LNGSTRINGW(IDS_ERROR_CREATING_D3D_DEVICE_FOR_VJ_MODE),
517 					   WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
518 					   MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
519 			return false;
520 		}
521 
522 		if (!AllocateFonts(m_vjd3d9_device))
523 			return false;
524 
525 		if (m_fix_slow_text)    // note that when not doing vj mode, m_lpDDSText is allocated in AllocateDX9Stuff
526 			AllocateTextSurface();
527 
528 		m_text.Finish();
529 		m_text.Init(m_vjd3d9_device, m_lpDDSText, 0);
530 
531 		m_bClearVJWindow = true;
532 	}
533 
534 	return true;
535 }
536 
CleanUpVJStuff()537 void CPluginShell::CleanUpVJStuff()
538 {
539 	// ALWAYS set the textures to NULL before releasing textures,
540 	// otherwise they might still have a hanging reference!
541 	if (m_lpDX && m_lpDX->m_lpDevice)
542 	{
543 		for (int i=0; i<16; i++)
544 			m_lpDX->m_lpDevice->SetTexture(i, NULL);
545 	}
546 
547 	if (m_vjd3d9_device)
548 	{
549 		for (int i=0; i<16; i++)
550 			m_vjd3d9_device->SetTexture(i, NULL);
551 	}
552 
553 	if (!m_vj_mode)
554 		return;
555 
556 	// clean up VJ mode
557 	{
558 		CleanUpFonts();
559 		SafeRelease(m_lpDDSText);
560 
561 		SafeRelease(m_vjd3d9_device);
562 		SafeRelease(m_vjd3d9);
563 
564 		if (m_hTextWnd)
565 		{
566 			//dumpmsg("Finish: destroying text window");
567 			DestroyWindow(m_hTextWnd);
568 			m_hTextWnd = NULL;
569 			//dumpmsg("Finish: text window destroyed");
570 		}
571 
572 		if (m_bTextWindowClassRegistered)
573 		{
574 			//dumpmsg("Finish: unregistering text window class");
575 			UnregisterClass(TEXT_WINDOW_CLASSNAME,GetInstance()); // unregister window class
576 			m_bTextWindowClassRegistered = false;
577 			//dumpmsg("Finish: text window class unregistered");
578 		}
579 	}
580 }
581 
AllocateFonts(IDirect3DDevice9 * pDevice)582 int CPluginShell::AllocateFonts(IDirect3DDevice9* pDevice)
583 {
584 	// Create D3DX system font:
585 	for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++)
586 		if (pCreateFontW(pDevice,  //m_font[i],
587 		                   m_fontinfo[i].nSize,
588 		                   m_fontinfo[i].nSize*4/10,
589 		                   m_fontinfo[i].bBold ? 900 : 400,
590 		                   1,  // mip levels
591 		                   m_fontinfo[i].bItalic,
592 		                   DEFAULT_CHARSET,
593 		                   OUT_DEFAULT_PRECIS,
594 		                   m_fontinfo[i].bAntiAliased ? ANTIALIASED_QUALITY : DEFAULT_QUALITY,
595 		                   DEFAULT_PITCH,
596 		                   m_fontinfo[i].szFace,
597 		                   &m_d3dx_font[i]
598 		                  ) != D3D_OK)
599 		{
600 			wchar_t title[64];
601 			MessageBoxW(m_lpDX ? m_lpDX->GetHwnd() : NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_CREATING_D3DX_FONTS),
602 					    WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
603 					    MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
604 			return false;
605 		}
606 
607 	// get actual font heights
608 	for (i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++)
609 	{
610 		RECT r;
611 		SetRect(&r, 0, 0, 1024, 1024);
612 		int h = m_d3dx_font[i]->DrawText(NULL, "M", -1, &r, DT_CALCRECT, 0xFFFFFFFF);
613 		if (h>0) m_fontinfo[i].nSize = h;
614 	}
615 
616 	return true;
617 }
618 
CleanUpFonts()619 void CPluginShell::CleanUpFonts()
620 {
621 	for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++)
622 		SafeRelease(m_d3dx_font[i]);
623 }
624 
AllocateTextSurface()625 void CPluginShell::AllocateTextSurface()
626 {
627 	IDirect3DDevice9 *pDevice = m_vjd3d9_device ? m_vjd3d9_device : GetDevice();
628 	int w = m_vjd3d9_device ? m_nTextWndWidth  : GetWidth() ;
629 	int h = m_vjd3d9_device ? m_nTextWndHeight : GetHeight();
630 
631 	if (D3D_OK != pCreateTexture(pDevice, w, h, 1, D3DUSAGE_RENDERTARGET, GetBackBufFormat(), D3DPOOL_DEFAULT, &m_lpDDSText))
632 		m_lpDDSText = NULL; // OK if there's not enough mem for it!
633 	else
634 	{
635 		// if m_lpDDSText doesn't cover enough of screen, cancel it.
636 		D3DSURFACE_DESC desc;
637 		if (D3D_OK == m_lpDDSText->GetLevelDesc(0, &desc))
638 		{
639 			if ((desc.Width  < 256 && w >= 256)  ||
640 			    (desc.Height < 256 && h >= 256)  ||
641 			    (desc.Width /(float)w < 0.74f) ||
642 			    (desc.Height/(float)h < 0.74f)
643 			   )
644 			{
645 				m_lpDDSText->Release();
646 				m_lpDDSText = NULL;
647 			}
648 		}
649 	}
650 }
651 
AllocateDX9Stuff()652 int CPluginShell::AllocateDX9Stuff()
653 {
654 	if (!m_vj_mode)
655 	{
656 		AllocateFonts(m_lpDX->m_lpDevice);
657 		if (m_fix_slow_text)    // note that when not doing vj mode, m_lpDDSText is allocated in AllocateDX9Stuff
658 			AllocateTextSurface();
659 	}
660 
661 	/*
662 	// Create D3DX system font:
663 	for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++)
664 	    if (pCreateFontW(m_lpDX->m_lpDevice,
665 					   m_fontinfo[i].nSize,
666 					   m_fontinfo[i].nSize*4/10,
667 	                       m_fontinfo[i].bBold ? 900 : 400,
668 		               0,  // mip levels
669 					   m_fontinfo[i].bItalic,
670 					   DEFAULT_CHARSET,
671 					   OUT_DEFAULT_PRECIS,
672 					   m_fontinfo[i].bAntiAliased ? ANTIALIASED_QUALITY : DEFAULT_QUALITY,
673 					   DEFAULT_PITCH,
674 					   m_fontinfo[i].szFace,
675 					   &m_d3dx_font[i]
676 					   ) != D3D_OK)
677 	    {
678 	        MessageBox(m_lpDX->GetHwnd(), "Error creating D3DX fonts", "ERROR", MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
679 	        return false;
680 	    }
681 
682 	// get actual font heights
683 	for (i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++)
684 	{
685 	    RECT r;
686 	    SetRect(&r, 0, 0, 1024, 1024);
687 	    int h = m_d3dx_font[i]->DrawText(NULL, "M", -1, &r, DT_CALCRECT, 0xFFFFFFFF);
688 	    if (h>0) m_fontinfo[i].nSize = h;
689 	}
690 	*/
691 
692 	if (m_screenmode == DESKTOP)
693 		if (!InitDesktopMode())
694 			return false;
695 
696 	int ret = AllocateMyDX9Stuff();
697 
698 	// invalidate various 'caches' here:
699 	m_playlist_top_idx = -1;    // invalidating playlist cache forces recompute of playlist width
700 	//m_icon_list.clear();      // clear desktop mode icon list, so it has to read the bitmaps back in
701 
702 	if (!m_vj_mode)
703 	{
704 		m_text.Finish();
705 		m_text.Init(GetDevice(), m_lpDDSText, 1);
706 	}
707 
708 	return ret;
709 }
710 
CleanUpDX9Stuff(int final_cleanup)711 void CPluginShell::CleanUpDX9Stuff(int final_cleanup)
712 {
713 	// ALWAYS unbind the textures before releasing textures,
714 	// otherwise they might still have a hanging reference!
715 	if (m_lpDX && m_lpDX->m_lpDevice)
716 	{
717 		for (int i=0; i<16; i++)
718 			m_lpDX->m_lpDevice->SetTexture(i, NULL);
719 	}
720 
721 	if (m_screenmode == DESKTOP)
722 		CleanUpDesktopMode();
723 
724 	if (!m_vj_mode)
725 	{
726 		for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++)
727 			SafeRelease(m_d3dx_font[i]);
728 		SafeRelease(m_lpDDSText);
729 	}
730 
731 	CleanUpMyDX9Stuff(final_cleanup);
732 }
733 
OnUserResizeTextWindow()734 void CPluginShell::OnUserResizeTextWindow()
735 {
736 	// Update window properties
737 	RECT w, c;
738 	GetWindowRect(m_hTextWnd, &w);
739 	GetClientRect(m_hTextWnd, &c);
740 
741 	WINDOWPLACEMENT wp;
742 	ZeroMemory(&wp, sizeof(wp));
743 	wp.length = sizeof(wp);
744 	GetWindowPlacement(m_hTextWnd, &wp);
745 
746 	// convert client rect from client coords to screen coords:
747 	// (window rect is already in screen coords...)
748 	POINT p;
749 	p.x = c.left;
750 	p.y = c.top;
751 	if (ClientToScreen(m_hTextWnd, &p))
752 	{
753 		c.left += p.x;
754 		c.right += p.x;
755 		c.top += p.y;
756 		c.bottom += p.y;
757 	}
758 
759 	if (wp.showCmd != SW_SHOWMINIMIZED)
760 	{
761 		if (m_nTextWndWidth  != c.right-c.left ||
762 		    m_nTextWndHeight != c.bottom-c.top)
763 		{
764 			CleanUpVJStuff();
765 			if (!InitVJStuff(&c))
766 			{
767 				SuggestHowToFreeSomeMem();
768 				m_lpDX->m_ready = false;   // flag to exit
769 				return;
770 			}
771 		}
772 
773 		// save the new window position:
774 		//if (wp.showCmd==SW_SHOWNORMAL)
775 		//    SaveTextWindowPos();
776 	}
777 }
778 
OnUserResizeWindow()779 void CPluginShell::OnUserResizeWindow()
780 {
781 	// Update window properties
782 	RECT w, c;
783 	GetWindowRect(m_lpDX->GetHwnd(), &w);
784 	GetClientRect(m_lpDX->GetHwnd(), &c);
785 
786 	WINDOWPLACEMENT wp;
787 	ZeroMemory(&wp, sizeof(wp));
788 	wp.length = sizeof(wp);
789 	GetWindowPlacement(m_lpDX->GetHwnd(), &wp);
790 
791 	// convert client rect from client coords to screen coords:
792 	// (window rect is already in screen coords...)
793 	POINT p;
794 	p.x = c.left;
795 	p.y = c.top;
796 	if (ClientToScreen(m_lpDX->GetHwnd(), &p))
797 	{
798 		c.left += p.x;
799 		c.right += p.x;
800 		c.top += p.y;
801 		c.bottom += p.y;
802 	}
803 
804 	if (wp.showCmd != SW_SHOWMINIMIZED)
805 	{
806 		int new_REAL_client_w = c.right-c.left;
807 		int new_REAL_client_h = c.bottom-c.top;
808 
809 		// kiv: could we just resize when the *snapped* w/h changes?  slightly more ideal...
810 		if (m_lpDX->m_REAL_client_width  != new_REAL_client_w ||
811 		    m_lpDX->m_REAL_client_height != new_REAL_client_h)
812 		{
813 			//CleanUpVJStuff();
814 			CleanUpDX9Stuff(0);
815 			if (!m_lpDX->OnUserResizeWindow(&w, &c))
816 			{
817 				// note: a basic warning messagebox will have already been given.
818 				// now suggest specific advice on how to regain more video memory:
819 				SuggestHowToFreeSomeMem();
820 				return;
821 			}
822 			if (!AllocateDX9Stuff())
823 			{
824 				m_lpDX->m_ready = false;   // flag to exit
825 				return;
826 			}
827 			/*if (!InitVJStuff())
828 			{
829 			    m_lpDX->m_ready = false;   // flag to exit
830 			    return;
831 			}*/
832 		}
833 
834 		// save the new window position:
835 		if (wp.showCmd==SW_SHOWNORMAL)
836 			m_lpDX->SaveWindow();
837 	}
838 }
839 
StuffParams(DXCONTEXT_PARAMS * pParams)840 void CPluginShell::StuffParams(DXCONTEXT_PARAMS *pParams)
841 {
842 	pParams->screenmode   = m_screenmode;
843 	pParams->display_mode = m_disp_mode_fs;
844 	pParams->nbackbuf     = 1;
845 	pParams->m_dualhead_horz = m_dualhead_horz;
846 	pParams->m_dualhead_vert = m_dualhead_vert;
847 	pParams->m_skin = (m_screenmode==WINDOWED) ? m_skin : 0;
848 	switch (m_screenmode)
849 	{
850 	case WINDOWED:
851 		pParams->allow_page_tearing = m_allow_page_tearing_w;
852 		pParams->adapter_guid       = m_adapter_guid_windowed;
853 		pParams->multisamp          = m_multisample_windowed;
854 		strcpy(pParams->adapter_devicename, m_adapter_devicename_windowed);
855 		break;
856 	case FULLSCREEN:
857 	case FAKE_FULLSCREEN:
858 		pParams->allow_page_tearing = m_allow_page_tearing_fs;
859 		pParams->adapter_guid       = m_adapter_guid_fullscreen;
860 		pParams->multisamp          = m_multisample_fullscreen;
861 		strcpy(pParams->adapter_devicename, m_adapter_devicename_fullscreen);
862 		break;
863 	case DESKTOP:
864 		pParams->allow_page_tearing = m_allow_page_tearing_dm;
865 		pParams->adapter_guid       = m_adapter_guid_desktop;
866 		pParams->multisamp          = m_multisample_desktop;
867 		strcpy(pParams->adapter_devicename, m_adapter_devicename_desktop);
868 		break;
869 	}
870 	pParams->parent_window = (m_screenmode==DESKTOP) ? m_hWndDesktopListView : NULL;
871 }
872 
ToggleDesktop()873 void CPluginShell::ToggleDesktop()
874 {
875 	CleanUpDX9Stuff(0);
876 
877 	switch (m_screenmode)
878 	{
879 	case WINDOWED:
880 	case FULLSCREEN:
881 	case FAKE_FULLSCREEN:
882 		m_screenmode = DESKTOP;
883 		break;
884 	case DESKTOP:
885 		m_screenmode = WINDOWED;
886 		break;
887 	}
888 
889 	DXCONTEXT_PARAMS params;
890 	StuffParams(&params);
891 
892 	if (!m_lpDX->StartOrRestartDevice(&params))
893 	{
894 		// note: a basic warning messagebox will have already been given.
895 		if (m_lpDX->m_lastErr == DXC_ERR_CREATEDEV_PROBABLY_OUTOFVIDEOMEMORY)
896 			SuggestHowToFreeSomeMem();
897 		return;
898 	}
899 
900 	if (!AllocateDX9Stuff())
901 	{
902 		m_lpDX->m_ready = false;   // flag to exit
903 		return;
904 	}
905 
906 	SetForegroundWindow(m_lpDX->GetHwnd());
907 	SetActiveWindow(m_lpDX->GetHwnd());
908 	SetFocus(m_lpDX->GetHwnd());
909 }
910 
911 #define IPC_IS_PLAYING_VIDEO 501 // from wa_ipc.h
912 #define IPC_SET_VIS_FS_FLAG 631 // a vis should send this message with 1/as param to notify winamp that it has gone to or has come back from fullscreen mode
913 
ToggleFullScreen()914 void CPluginShell::ToggleFullScreen()
915 {
916 	CleanUpDX9Stuff(0);
917 
918 	switch (m_screenmode)
919 	{
920 	case DESKTOP:
921 	case WINDOWED:
922 		m_screenmode = m_fake_fullscreen_mode ? FAKE_FULLSCREEN : FULLSCREEN;
923 		if (m_screenmode == FULLSCREEN && SendMessage(GetWinampWindow(), WM_WA_IPC, 0, IPC_IS_PLAYING_VIDEO) > 1)
924 		{
925 			m_screenmode = FAKE_FULLSCREEN;
926 		}
927 		SendMessage(GetWinampWindow(), WM_WA_IPC, 1, IPC_SET_VIS_FS_FLAG);
928 		break;
929 	case FULLSCREEN:
930 	case FAKE_FULLSCREEN:
931 		m_screenmode = WINDOWED;
932 		SendMessage(GetWinampWindow(), WM_WA_IPC, 0, IPC_SET_VIS_FS_FLAG);
933 		break;
934 	}
935 
936 	DXCONTEXT_PARAMS params;
937 	StuffParams(&params);
938 
939 	if (!m_lpDX->StartOrRestartDevice(&params))
940 	{
941 		// note: a basic warning messagebox will have already been given.
942 		if (m_lpDX->m_lastErr == DXC_ERR_CREATEDEV_PROBABLY_OUTOFVIDEOMEMORY)
943 			SuggestHowToFreeSomeMem();
944 		return;
945 	}
946 
947 	if (!AllocateDX9Stuff())
948 	{
949 		m_lpDX->m_ready = false;   // flag to exit
950 		return;
951 	}
952 
953 	SetForegroundWindow(m_lpDX->GetHwnd());
954 	SetActiveWindow(m_lpDX->GetHwnd());
955 	SetFocus(m_lpDX->GetHwnd());
956 }
957 
ToggleHelp()958 void CPluginShell::ToggleHelp()
959 {
960 	m_show_help = 1-m_show_help;
961 	int ret = CheckMenuItem(m_context_menu, ID_SHOWHELP, MF_BYCOMMAND | (m_show_help ? MF_CHECKED : MF_UNCHECKED));
962 }
963 
TogglePlaylist()964 void CPluginShell::TogglePlaylist()
965 {
966 	m_show_playlist = 1-m_show_playlist;
967 	m_playlist_top_idx = -1;    // <- invalidates playlist cache
968 	int ret = CheckMenuItem(m_context_menu, ID_SHOWPLAYLIST, MF_BYCOMMAND | (m_show_playlist ? MF_CHECKED : MF_UNCHECKED));
969 }
970 
InitDirectX()971 int CPluginShell::InitDirectX()
972 {
973 	m_lpDX = new DXContext(m_hWndWinamp,m_hInstance,CLASSNAME,WINDOWCAPTION,CPluginShell::WindowProc,(LONG_PTR)this, m_minimize_winamp, m_szConfigIniFile);
974 
975 	if (!m_lpDX)
976 	{
977 		wchar_t title[64];
978 		MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_UNABLE_TO_INIT_DXCONTEXT),
979 				    WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
980 				    MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
981 		return FALSE;
982 	}
983 
984 	if (m_lpDX->m_lastErr != S_OK)
985 	{
986 		// warning messagebox will have already been given
987 		delete m_lpDX;
988 		return FALSE;
989 	}
990 
991 	// initialize graphics
992 	DXCONTEXT_PARAMS params;
993 	StuffParams(&params);
994 
995 	if (!m_lpDX->StartOrRestartDevice(&params))
996 	{
997 		// note: a basic warning messagebox will have already been given.
998 
999 		if (m_lpDX->m_lastErr == DXC_ERR_CREATEDEV_PROBABLY_OUTOFVIDEOMEMORY)
1000 		{
1001 			// suggest specific advice on how to regain more video memory:
1002 			SuggestHowToFreeSomeMem();
1003 		}
1004 
1005 		delete m_lpDX;
1006 		m_lpDX = NULL;
1007 		return FALSE;
1008 	}
1009 
1010 	return TRUE;
1011 }
1012 
CleanUpDirectX()1013 void CPluginShell::CleanUpDirectX()
1014 {
1015 	SafeDelete(m_lpDX);
1016 }
1017 
PluginPreInitialize(HWND hWinampWnd,HINSTANCE hWinampInstance)1018 int CPluginShell::PluginPreInitialize(HWND hWinampWnd, HINSTANCE hWinampInstance)
1019 {
1020 	// PROTECTED CONFIG PANEL SETTINGS (also see 'private' settings, below)
1021 	m_start_fullscreen      = 0;
1022 	m_start_desktop         = 0;
1023 	m_fake_fullscreen_mode  = 0;
1024 	m_max_fps_fs            = 30;
1025 	m_max_fps_dm            = 30;
1026 	m_max_fps_w             = 30;
1027 	m_show_press_f1_msg     = 1;
1028 	m_allow_page_tearing_w  = 1;
1029 	m_allow_page_tearing_fs = 0;
1030 	m_allow_page_tearing_dm = 0;
1031 	m_minimize_winamp       = 1;
1032 	m_desktop_show_icons    = 1;
1033 	m_desktop_textlabel_boxes = 1;
1034 	m_desktop_manual_icon_scoot = 0;
1035 	m_desktop_555_fix       = 2;
1036 	m_dualhead_horz         = 2;
1037 	m_dualhead_vert         = 1;
1038 	m_save_cpu              = 1;
1039 	m_skin                  = 1;
1040 	m_fix_slow_text         = 0;
1041 
1042 	// initialize font settings:
1043 	wcscpy(m_fontinfo[SIMPLE_FONT    ].szFace,        SIMPLE_FONT_DEFAULT_FACE);
1044 	m_fontinfo[SIMPLE_FONT    ].nSize        = SIMPLE_FONT_DEFAULT_SIZE ;
1045 	m_fontinfo[SIMPLE_FONT    ].bBold        = SIMPLE_FONT_DEFAULT_BOLD ;
1046 	m_fontinfo[SIMPLE_FONT    ].bItalic      = SIMPLE_FONT_DEFAULT_ITAL ;
1047 	m_fontinfo[SIMPLE_FONT    ].bAntiAliased = SIMPLE_FONT_DEFAULT_AA   ;
1048 	wcscpy(m_fontinfo[DECORATIVE_FONT].szFace,        DECORATIVE_FONT_DEFAULT_FACE);
1049 	m_fontinfo[DECORATIVE_FONT].nSize        = DECORATIVE_FONT_DEFAULT_SIZE;
1050 	m_fontinfo[DECORATIVE_FONT].bBold        = DECORATIVE_FONT_DEFAULT_BOLD;
1051 	m_fontinfo[DECORATIVE_FONT].bItalic      = DECORATIVE_FONT_DEFAULT_ITAL;
1052 	m_fontinfo[DECORATIVE_FONT].bAntiAliased = DECORATIVE_FONT_DEFAULT_AA  ;
1053 	wcscpy(m_fontinfo[HELPSCREEN_FONT].szFace,        HELPSCREEN_FONT_DEFAULT_FACE);
1054 	m_fontinfo[HELPSCREEN_FONT].nSize        = HELPSCREEN_FONT_DEFAULT_SIZE;
1055 	m_fontinfo[HELPSCREEN_FONT].bBold        = HELPSCREEN_FONT_DEFAULT_BOLD;
1056 	m_fontinfo[HELPSCREEN_FONT].bItalic      = HELPSCREEN_FONT_DEFAULT_ITAL;
1057 	m_fontinfo[HELPSCREEN_FONT].bAntiAliased = HELPSCREEN_FONT_DEFAULT_AA  ;
1058 	wcscpy(m_fontinfo[PLAYLIST_FONT  ].szFace,        PLAYLIST_FONT_DEFAULT_FACE);
1059 	m_fontinfo[PLAYLIST_FONT  ].nSize        = PLAYLIST_FONT_DEFAULT_SIZE;
1060 	m_fontinfo[PLAYLIST_FONT  ].bBold        = PLAYLIST_FONT_DEFAULT_BOLD;
1061 	m_fontinfo[PLAYLIST_FONT  ].bItalic      = PLAYLIST_FONT_DEFAULT_ITAL;
1062 	m_fontinfo[PLAYLIST_FONT  ].bAntiAliased = PLAYLIST_FONT_DEFAULT_AA  ;
1063 
1064 #if (NUM_EXTRA_FONTS >= 1)
1065 	wcscpy(m_fontinfo[NUM_BASIC_FONTS + 0].szFace,        EXTRA_FONT_1_DEFAULT_FACE);
1066 	m_fontinfo[NUM_BASIC_FONTS + 0].nSize        = EXTRA_FONT_1_DEFAULT_SIZE;
1067 	m_fontinfo[NUM_BASIC_FONTS + 0].bBold        = EXTRA_FONT_1_DEFAULT_BOLD;
1068 	m_fontinfo[NUM_BASIC_FONTS + 0].bItalic      = EXTRA_FONT_1_DEFAULT_ITAL;
1069 	m_fontinfo[NUM_BASIC_FONTS + 0].bAntiAliased = EXTRA_FONT_1_DEFAULT_AA;
1070 #endif
1071 #if (NUM_EXTRA_FONTS >= 2)
1072 	wcscpy(m_fontinfo[NUM_BASIC_FONTS + 1].szFace,        EXTRA_FONT_2_DEFAULT_FACE);
1073 	m_fontinfo[NUM_BASIC_FONTS + 1].nSize        = EXTRA_FONT_2_DEFAULT_SIZE;
1074 	m_fontinfo[NUM_BASIC_FONTS + 1].bBold        = EXTRA_FONT_2_DEFAULT_BOLD;
1075 	m_fontinfo[NUM_BASIC_FONTS + 1].bItalic      = EXTRA_FONT_2_DEFAULT_ITAL;
1076 	m_fontinfo[NUM_BASIC_FONTS + 1].bAntiAliased = EXTRA_FONT_2_DEFAULT_AA;
1077 #endif
1078 #if (NUM_EXTRA_FONTS >= 3)
1079 	strcpy(m_fontinfo[NUM_BASIC_FONTS + 2].szFace,        EXTRA_FONT_3_DEFAULT_FACE);
1080 	m_fontinfo[NUM_BASIC_FONTS + 2].nSize        = EXTRA_FONT_3_DEFAULT_SIZE;
1081 	m_fontinfo[NUM_BASIC_FONTS + 2].bBold        = EXTRA_FONT_3_DEFAULT_BOLD;
1082 	m_fontinfo[NUM_BASIC_FONTS + 2].bItalic      = EXTRA_FONT_3_DEFAULT_ITAL;
1083 	m_fontinfo[NUM_BASIC_FONTS + 2].bAntiAliased = EXTRA_FONT_3_DEFAULT_AA;
1084 #endif
1085 #if (NUM_EXTRA_FONTS >= 4)
1086 	strcpy(m_fontinfo[NUM_BASIC_FONTS + 3].szFace,        EXTRA_FONT_4_DEFAULT_FACE);
1087 	m_fontinfo[NUM_BASIC_FONTS + 3].nSize        = EXTRA_FONT_4_DEFAULT_SIZE;
1088 	m_fontinfo[NUM_BASIC_FONTS + 3].bBold        = EXTRA_FONT_4_DEFAULT_BOLD;
1089 	m_fontinfo[NUM_BASIC_FONTS + 3].bItalic      = EXTRA_FONT_4_DEFAULT_ITAL;
1090 	m_fontinfo[NUM_BASIC_FONTS + 3].bAntiAliased = EXTRA_FONT_4_DEFAULT_AA;
1091 #endif
1092 #if (NUM_EXTRA_FONTS >= 5)
1093 	strcpy(m_fontinfo[NUM_BASIC_FONTS + 4].szFace,        EXTRA_FONT_5_DEFAULT_FACE);
1094 	m_fontinfo[NUM_BASIC_FONTS + 4].nSize        = EXTRA_FONT_5_DEFAULT_SIZE;
1095 	m_fontinfo[NUM_BASIC_FONTS + 4].bBold        = EXTRA_FONT_5_DEFAULT_BOLD;
1096 	m_fontinfo[NUM_BASIC_FONTS + 4].bItalic      = EXTRA_FONT_5_DEFAULT_ITAL;
1097 	m_fontinfo[NUM_BASIC_FONTS + 4].bAntiAliased = EXTRA_FONT_5_DEFAULT_AA;
1098 #endif
1099 
1100 	m_disp_mode_fs.Width = DEFAULT_FULLSCREEN_WIDTH;
1101 	m_disp_mode_fs.Height = DEFAULT_FULLSCREEN_HEIGHT;
1102 	m_disp_mode_fs.Format = D3DFMT_UNKNOWN;
1103 	m_disp_mode_fs.RefreshRate = 60;
1104 	// better yet - in case there is no config INI file saved yet, use the current display mode (if detectable) as the default fullscreen res:
1105 	DEVMODE dm;
1106 	dm.dmSize = sizeof(dm);
1107 	dm.dmDriverExtra = 0;
1108 	if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm))
1109 	{
1110 		m_disp_mode_fs.Width  = dm.dmPelsWidth;
1111 		m_disp_mode_fs.Height = dm.dmPelsHeight;
1112 		m_disp_mode_fs.RefreshRate = dm.dmDisplayFrequency;
1113 		m_disp_mode_fs.Format = (dm.dmBitsPerPel==16) ? D3DFMT_R5G6B5 : D3DFMT_X8R8G8B8;
1114 	}
1115 
1116 	// PROTECTED STRUCTURES/POINTERS
1117 	for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++)
1118 		m_d3dx_font[i] = NULL;
1119 	m_d3dx_desktop_font = NULL;
1120 	m_lpDDSText = NULL;
1121 	ZeroMemory(&m_sound, sizeof(td_soundinfo));
1122 	for (int ch=0; ch<2; ch++)
1123 		for (int i=0; i<3; i++)
1124 		{
1125 			m_sound.infinite_avg[ch][i] = m_sound.avg[ch][i] = m_sound.med_avg[ch][i] = m_sound.long_avg[ch][i] = 1.0f;
1126 		}
1127 
1128 	// GENERAL PRIVATE STUFF
1129 	//m_screenmode: set at end (derived setting)
1130 	m_frame = 0;
1131 	m_time = 0;
1132 	m_fps = 30;
1133 	m_hWndWinamp = hWinampWnd;
1134 	m_hInstance = hWinampInstance;
1135 	m_lpDX = NULL;
1136 	m_szPluginsDirPath[0] = 0;  // will be set further down
1137 	m_szConfigIniFile[0] = 0;  // will be set further down
1138 	// m_szPluginsDirPath:
1139 
1140 	wchar_t *p;
1141 
1142 	if (hWinampWnd
1143 	    && (p = (wchar_t *)SendMessage(hWinampWnd, WM_WA_IPC, 0, IPC_GETPLUGINDIRECTORYW))
1144 	    && p != (wchar_t *)1)
1145 	{
1146 		swprintf(m_szPluginsDirPath, L"%s\\", p);
1147 	}
1148 	else
1149 	{
1150 		// get path to INI file & read in prefs/settings right away, so DumpMsg works!
1151 		GetModuleFileNameW(m_hInstance, m_szPluginsDirPath, MAX_PATH);
1152 		wchar_t *p = m_szPluginsDirPath + wcslen(m_szPluginsDirPath);
1153 		while (p >= m_szPluginsDirPath && *p != L'\\') p--;
1154 		if (++p >= m_szPluginsDirPath) *p = 0;
1155 	}
1156 
1157 	if (hWinampWnd
1158 	    && (p = (wchar_t *)SendMessage(hWinampWnd, WM_WA_IPC, 0, IPC_GETINIDIRECTORYW))
1159 	    && p != (wchar_t *)1)
1160 	{
1161 		// load settings as well as coping with moving old settings to a contained folder
1162 		wchar_t m_szOldConfigIniFile[MAX_PATH] = {0}, temp[MAX_PATH] = {0}, temp2[MAX_PATH] = {0};
1163 		swprintf(m_szOldConfigIniFile, L"%s\\Plugins\\%s", p, INIFILE);
1164 		swprintf(m_szConfigIniFile, L"%s\\Plugins\\%s%s", p, SUBDIR, INIFILE);
1165 		swprintf(temp, L"%s\\Plugins\\%s", p, SUBDIR);
1166 		swprintf(temp2, L"%s\\Plugins\\", p);
1167 		CreateDirectoryW(temp, NULL);
1168 
1169 		if (PathFileExistsW(m_szOldConfigIniFile) && !PathFileExistsW(m_szConfigIniFile))
1170 		{
1171 			MoveFileW(m_szOldConfigIniFile, m_szConfigIniFile);
1172 
1173 			wchar_t m_szMsgIniFile[MAX_PATH] = {0}, m_szNewMsgIniFile[MAX_PATH] = {0},
1174 					m_szImgIniFile[MAX_PATH] = {0}, m_szNewImgIniFile[MAX_PATH] = {0},
1175 					m_szAdaptersFile[MAX_PATH] = {0}, m_szNewAdaptersFile[MAX_PATH] = {0};
1176    			swprintf(m_szMsgIniFile, L"%s%s", temp2, MSG_INIFILE);
1177 			swprintf(m_szNewMsgIniFile, L"%s%s", temp, MSG_INIFILE);
1178 			swprintf(m_szImgIniFile, L"%s%s", temp2, IMG_INIFILE);
1179 			swprintf(m_szNewImgIniFile, L"%s%s", temp, IMG_INIFILE);
1180 			swprintf(m_szAdaptersFile, L"%s%s", temp2, ADAPTERSFILE);
1181 			swprintf(m_szNewAdaptersFile, L"%s%s", temp, ADAPTERSFILE);
1182 
1183 			MoveFileW(m_szImgIniFile, m_szNewImgIniFile);
1184 			MoveFileW(m_szMsgIniFile, m_szNewMsgIniFile);
1185 			MoveFileW(m_szAdaptersFile, m_szNewAdaptersFile);
1186 		}
1187 	}
1188 	else
1189 	{
1190 		swprintf(m_szConfigIniFile, L"%s%s", m_szPluginsDirPath, INIFILE);
1191 	}
1192 	lstrcpyn(m_szConfigIniFileA,AutoCharFn(m_szConfigIniFile),MAX_PATH);
1193 
1194 	// PRIVATE CONFIG PANEL SETTINGS
1195 	m_multisample_fullscreen = D3DMULTISAMPLE_NONE;
1196 	m_multisample_desktop = D3DMULTISAMPLE_NONE;
1197 	m_multisample_windowed = D3DMULTISAMPLE_NONE;
1198 	ZeroMemory(&m_adapter_guid_fullscreen, sizeof(GUID));
1199 	ZeroMemory(&m_adapter_guid_desktop , sizeof(GUID));
1200 	ZeroMemory(&m_adapter_guid_windowed , sizeof(GUID));
1201 	m_adapter_devicename_windowed[0]   = 0;
1202 	m_adapter_devicename_fullscreen[0] = 0;
1203 	m_adapter_devicename_desktop[0]    = 0;
1204 
1205 
1206 	// PRIVATE RUNTIME SETTINGS
1207 	m_lost_focus = 0;
1208 	m_hidden     = 0;
1209 	m_resizing   = 0;
1210 	m_show_help  = 0;
1211 	m_show_playlist = 0;
1212 	m_playlist_pos = 0;
1213 	m_playlist_pageups = 0;
1214 	m_playlist_top_idx = -1;
1215 	m_playlist_btm_idx = -1;
1216 	// m_playlist_width_pixels will be considered invalid whenever 'm_playlist_top_idx' is -1.
1217 	// m_playlist[256][256] will be considered invalid whenever 'm_playlist_top_idx' is -1.
1218 	m_exiting    = 0;
1219 	m_upper_left_corner_y = 0;
1220 	m_lower_left_corner_y = 0;
1221 	m_upper_right_corner_y = 0;
1222 	m_lower_right_corner_y = 0;
1223 	m_left_edge = 0;
1224 	m_right_edge = 0;
1225 	m_force_accept_WM_WINDOWPOSCHANGING = 0;
1226 
1227 	// PRIVATE - GDI STUFF
1228 	m_main_menu     = NULL;
1229 	m_context_menu  = NULL;
1230 	for (i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++)
1231 		m_font[i] = NULL;
1232 	m_font_desktop = NULL;
1233 
1234 	// PRIVATE - DESKTOP MODE STUFF
1235 	m_icon_list.clear();
1236 	for (i=0; i<MAX_ICON_TEXTURES; i++)
1237 		m_desktop_icons_texture[i] = NULL;
1238 	FindDesktopWindows(&m_hWndProgMan, &m_hWndDesktop, &m_hWndDesktopListView);
1239 	GetDesktopFolder(m_szDesktopFolder);
1240 	m_desktop_icon_size       = 32;
1241 	m_desktop_dragging        = 0;   // '1' when user is dragging icons around
1242 	m_desktop_box             = 0;   // '1' when user is drawing a box
1243 	m_desktop_wc_registered   = 0;
1244 	m_desktop_bk_color        = 0xFF000000 | BGR2RGB(::GetSysColor(COLOR_BACKGROUND));
1245 	m_desktop_text_color      = 0xFF000000 | BGR2RGB(SendMessage(m_hWndDesktopListView, LVM_GETTEXTCOLOR, 0, 0));
1246 	m_desktop_sel_color       = 0xFF000000 | BGR2RGB(::GetSysColor(COLOR_HIGHLIGHT));
1247 	m_desktop_sel_text_color  = 0xFF000000 | BGR2RGB(::GetSysColor(COLOR_HIGHLIGHTTEXT));
1248 	m_desktop_icon_state      = 0;
1249 	m_desktop_icon_count      = 0;
1250 	m_desktop_icon_update_frame = 0;
1251 	m_desktop_icons_disabled  = 0;
1252 	m_vms_desktop_loaded      = 0;
1253 	m_desktop_hook_set        = 0;
1254 
1255 	// PRIVATE - MORE TIMEKEEPING
1256 	m_last_raw_time = 0;
1257 	memset(m_time_hist, 0, sizeof(m_time_hist));
1258 	m_time_hist_pos = 0;
1259 	if (!QueryPerformanceFrequency(&m_high_perf_timer_freq))
1260 		m_high_perf_timer_freq.QuadPart = 0;
1261 	m_prev_end_of_frame.QuadPart = 0;
1262 
1263 	// PRIVATE AUDIO PROCESSING DATA
1264 	//(m_fftobj needs no init)
1265 	memset(m_oldwave[0], 0, sizeof(float)*576);
1266 	memset(m_oldwave[1], 0, sizeof(float)*576);
1267 	m_prev_align_offset[0] = 0;
1268 	m_prev_align_offset[1] = 0;
1269 	m_align_weights_ready = 0;
1270 
1271 	// SEPARATE TEXT WINDOW (FOR VJ MODE)
1272 	m_vj_mode       = 0;
1273 	m_hidden_textwnd = 0;
1274 	m_resizing_textwnd = 0;
1275 	m_hTextWnd		= NULL;
1276 	m_nTextWndWidth = 0;
1277 	m_nTextWndHeight = 0;
1278 	m_bTextWindowClassRegistered = false;
1279 	m_vjd3d9        = NULL;
1280 	m_vjd3d9_device = NULL;
1281 
1282 	//-----
1283 
1284 	m_screenmode = NOT_YET_KNOWN;
1285 
1286 	OverrideDefaults();
1287 	ReadConfig();
1288 
1289 	if (m_start_fullscreen)
1290 	{
1291 		m_screenmode = m_fake_fullscreen_mode ? FAKE_FULLSCREEN : FULLSCREEN;
1292 		if (m_screenmode == FULLSCREEN && SendMessage(GetWinampWindow(), WM_WA_IPC, 0, IPC_IS_PLAYING_VIDEO) > 1)
1293 		{
1294 			m_screenmode = FAKE_FULLSCREEN;
1295 		}
1296 	}
1297 	else if (m_start_desktop)
1298 		m_screenmode = DESKTOP;
1299 	else
1300 		m_screenmode = WINDOWED;
1301 
1302 	MyPreInitialize();
1303 	MyReadConfig();
1304 
1305 	//-----
1306 
1307 	return TRUE;
1308 }
1309 
PluginInitialize()1310 int CPluginShell::PluginInitialize()
1311 {
1312 	// note: initialize GDI before DirectX.  Also separate them because
1313 	// when we change windowed<->fullscreen, or lose the device and restore it,
1314 	// we don't want to mess with any (persistent) GDI stuff.
1315 
1316 	if (!InitDirectX())        return FALSE;  // gives its own error messages
1317 	if (!InitNondx9Stuff())    return FALSE;  // gives its own error messages
1318 	if (!AllocateDX9Stuff())   return FALSE;  // gives its own error messages
1319 	if (!InitVJStuff())        return FALSE;
1320 
1321 	return TRUE;
1322 }
1323 
PluginQuit()1324 void CPluginShell::PluginQuit()
1325 {
1326 	CleanUpVJStuff();
1327 	CleanUpDX9Stuff(1);
1328 	CleanUpNondx9Stuff();
1329 	CleanUpDirectX();
1330 
1331 	SetFocus(m_hWndWinamp);
1332 	SetActiveWindow(m_hWndWinamp);
1333 	SetForegroundWindow(m_hWndWinamp);
1334 }
1335 
BuildSettingName(wchar_t * name,int number)1336 wchar_t* BuildSettingName(wchar_t* name, int number){
1337 static wchar_t temp[64];
1338 	swprintf(temp, L"%s%d", name, number);
1339 	return temp;
1340 }
1341 
READ_FONT(int n)1342 void CPluginShell::READ_FONT(int n){
1343 	GetPrivateProfileStringW(L"settings",BuildSettingName(L"szFontFace",n),m_fontinfo[n].szFace,m_fontinfo[n].szFace,sizeof(m_fontinfo[n].szFace), m_szConfigIniFile);
1344 	m_fontinfo[n].nSize   = GetPrivateProfileIntW(L"settings",BuildSettingName(L"nFontSize",n),m_fontinfo[n].nSize  ,m_szConfigIniFile);
1345 	m_fontinfo[n].bBold   = GetPrivateProfileIntW(L"settings",BuildSettingName(L"bFontBold",n),m_fontinfo[n].bBold  ,m_szConfigIniFile);
1346 	m_fontinfo[n].bItalic = GetPrivateProfileIntW(L"settings",BuildSettingName(L"bFontItalic",n),m_fontinfo[n].bItalic,m_szConfigIniFile);
1347 	m_fontinfo[n].bAntiAliased = GetPrivateProfileIntW(L"settings",BuildSettingName(L"bFontAA",n),m_fontinfo[n].bItalic,m_szConfigIniFile);
1348 }
1349 
ReadConfig()1350 void CPluginShell::ReadConfig()
1351 {
1352 	int old_ver    = GetPrivateProfileIntW(L"settings",L"version"   ,-1,m_szConfigIniFile);
1353 	int old_subver = GetPrivateProfileIntW(L"settings",L"subversion",-1,m_szConfigIniFile);
1354 
1355 	// nuke old settings from prev. version:
1356 	if (old_ver < INT_VERSION)
1357 		return;
1358 	else if (old_subver < INT_SUBVERSION)
1359 		return;
1360 
1361 	//D3DMULTISAMPLE_TYPE m_multisample_fullscreen;
1362 	//D3DMULTISAMPLE_TYPE m_multisample_desktop;
1363 	//D3DMULTISAMPLE_TYPE m_multisample_windowed;
1364 	m_multisample_fullscreen      = (D3DMULTISAMPLE_TYPE)GetPrivateProfileIntW(L"settings",L"multisample_fullscreen",m_multisample_fullscreen,m_szConfigIniFile);
1365 	m_multisample_desktop         = (D3DMULTISAMPLE_TYPE)GetPrivateProfileIntW(L"settings",L"multisample_desktop",m_multisample_desktop,m_szConfigIniFile);
1366 	m_multisample_windowed        = (D3DMULTISAMPLE_TYPE)GetPrivateProfileIntW(L"settings",L"multisample_windowed"  ,m_multisample_windowed  ,m_szConfigIniFile);
1367 
1368 	//GUID m_adapter_guid_fullscreen
1369 	//GUID m_adapter_guid_desktop
1370 	//GUID m_adapter_guid_windowed
1371 	char str[256];
1372 	GetPrivateProfileString("settings","adapter_guid_fullscreen","",str,sizeof(str)-1,m_szConfigIniFileA);
1373 	TextToGuid(str, &m_adapter_guid_fullscreen);
1374 	GetPrivateProfileString("settings","adapter_guid_desktop","",str,sizeof(str)-1,m_szConfigIniFileA);
1375 	TextToGuid(str, &m_adapter_guid_desktop);
1376 	GetPrivateProfileString("settings","adapter_guid_windowed","",str,sizeof(str)-1,m_szConfigIniFileA);
1377 	TextToGuid(str, &m_adapter_guid_windowed);
1378 	GetPrivateProfileString("settings","adapter_devicename_fullscreen","",m_adapter_devicename_fullscreen,sizeof(m_adapter_devicename_fullscreen)-1,m_szConfigIniFileA);
1379 	GetPrivateProfileString("settings","adapter_devicename_desktop",   "",m_adapter_devicename_desktop   ,sizeof(m_adapter_devicename_desktop)-1,m_szConfigIniFileA);
1380 	GetPrivateProfileString("settings","adapter_devicename_windowed",  "",m_adapter_devicename_windowed  ,sizeof(m_adapter_devicename_windowed)-1,m_szConfigIniFileA);
1381 
1382 	// FONTS
1383 	READ_FONT(0);
1384 	READ_FONT(1);
1385 	READ_FONT(2);
1386 	READ_FONT(3);
1387 #if (NUM_EXTRA_FONTS >= 1)
1388 	READ_FONT(4);
1389 #endif
1390 #if (NUM_EXTRA_FONTS >= 2)
1391 	READ_FONT(5);
1392 #endif
1393 #if (NUM_EXTRA_FONTS >= 3)
1394 	READ_FONT(6);
1395 #endif
1396 #if (NUM_EXTRA_FONTS >= 4)
1397 	READ_FONT(7);
1398 #endif
1399 #if (NUM_EXTRA_FONTS >= 5)
1400 	READ_FONT(8);
1401 #endif
1402 
1403 	m_start_fullscreen     = GetPrivateProfileIntW(L"settings",L"start_fullscreen",m_start_fullscreen,m_szConfigIniFile);
1404 	m_start_desktop        = GetPrivateProfileIntW(L"settings",L"start_desktop"   ,m_start_desktop   ,m_szConfigIniFile);
1405 	m_fake_fullscreen_mode = GetPrivateProfileIntW(L"settings",L"fake_fullscreen_mode",m_fake_fullscreen_mode,m_szConfigIniFile);
1406 	m_max_fps_fs           = GetPrivateProfileIntW(L"settings",L"max_fps_fs",m_max_fps_fs,m_szConfigIniFile);
1407 	m_max_fps_dm           = GetPrivateProfileIntW(L"settings",L"max_fps_dm",m_max_fps_dm,m_szConfigIniFile);
1408 	m_max_fps_w            = GetPrivateProfileIntW(L"settings",L"max_fps_w" ,m_max_fps_w ,m_szConfigIniFile);
1409 	m_show_press_f1_msg    = GetPrivateProfileIntW(L"settings",L"show_press_f1_msg",m_show_press_f1_msg,m_szConfigIniFile);
1410 	m_allow_page_tearing_w = GetPrivateProfileIntW(L"settings",L"allow_page_tearing_w",m_allow_page_tearing_w,m_szConfigIniFile);
1411 	m_allow_page_tearing_fs= GetPrivateProfileIntW(L"settings",L"allow_page_tearing_fs",m_allow_page_tearing_fs,m_szConfigIniFile);
1412 	m_allow_page_tearing_dm= GetPrivateProfileIntW(L"settings",L"allow_page_tearing_dm",m_allow_page_tearing_dm,m_szConfigIniFile);
1413 	m_minimize_winamp      = GetPrivateProfileIntW(L"settings",L"minimize_winamp",m_minimize_winamp,m_szConfigIniFile);
1414 	m_desktop_show_icons   = GetPrivateProfileIntW(L"settings",L"desktop_show_icons",m_desktop_show_icons,m_szConfigIniFile);
1415 	m_desktop_textlabel_boxes = GetPrivateProfileIntW(L"settings",L"desktop_textlabel_boxes",m_desktop_textlabel_boxes,m_szConfigIniFile);
1416 	m_desktop_manual_icon_scoot = GetPrivateProfileIntW(L"settings",L"desktop_manual_icon_scoot",m_desktop_manual_icon_scoot,m_szConfigIniFile);
1417 	m_desktop_555_fix      = GetPrivateProfileIntW(L"settings",L"desktop_555_fix",m_desktop_555_fix,m_szConfigIniFile);
1418 	m_dualhead_horz        = GetPrivateProfileIntW(L"settings",L"dualhead_horz",m_dualhead_horz,m_szConfigIniFile);
1419 	m_dualhead_vert        = GetPrivateProfileIntW(L"settings",L"dualhead_vert",m_dualhead_vert,m_szConfigIniFile);
1420 	m_save_cpu             = GetPrivateProfileIntW(L"settings",L"save_cpu",m_save_cpu,m_szConfigIniFile);
1421 	m_skin                 = GetPrivateProfileIntW(L"settings",L"skin",m_skin,m_szConfigIniFile);
1422 	m_fix_slow_text        = GetPrivateProfileIntW(L"settings",L"fix_slow_text",m_fix_slow_text,m_szConfigIniFile);
1423 	m_vj_mode              = GetPrivateProfileBoolW(L"settings",L"vj_mode",m_vj_mode,m_szConfigIniFile);
1424 
1425 	//D3DDISPLAYMODE m_fs_disp_mode
1426 	m_disp_mode_fs.Width           = GetPrivateProfileIntW(L"settings",L"disp_mode_fs_w", m_disp_mode_fs.Width           ,m_szConfigIniFile);
1427 	m_disp_mode_fs.Height           = GetPrivateProfileIntW(L"settings",L"disp_mode_fs_h",m_disp_mode_fs.Height          ,m_szConfigIniFile);
1428 	m_disp_mode_fs.RefreshRate = GetPrivateProfileIntW(L"settings",L"disp_mode_fs_r",m_disp_mode_fs.RefreshRate,m_szConfigIniFile);
1429 	m_disp_mode_fs.Format      = (D3DFORMAT)GetPrivateProfileIntW(L"settings",L"disp_mode_fs_f",m_disp_mode_fs.Format     ,m_szConfigIniFile);
1430 
1431 	// note: we don't call MyReadConfig() yet, because we
1432 	// want to completely finish CPluginShell's preinit (and ReadConfig)
1433 	// before calling CPlugin's preinit and ReadConfig.
1434 }
1435 
WRITE_FONT(int n)1436 void CPluginShell::WRITE_FONT(int n){
1437 	WritePrivateProfileStringW(L"settings",BuildSettingName(L"szFontFace",n),m_fontinfo[n].szFace,m_szConfigIniFile);
1438 	WritePrivateProfileIntW(m_fontinfo[n].bBold,  BuildSettingName(L"bFontBold",n),   m_szConfigIniFile, L"settings");
1439 	WritePrivateProfileIntW(m_fontinfo[n].bItalic,BuildSettingName(L"bFontItalic",n), m_szConfigIniFile, L"settings");
1440 	WritePrivateProfileIntW(m_fontinfo[n].nSize,  BuildSettingName(L"nFontSize",n),   m_szConfigIniFile, L"settings");
1441 	WritePrivateProfileIntW(m_fontinfo[n].bAntiAliased, BuildSettingName(L"bFontAA",n),m_szConfigIniFile, L"settings");
1442 }
1443 
WriteConfig()1444 void CPluginShell::WriteConfig()
1445 {
1446 	//D3DMULTISAMPLE_TYPE m_multisample_fullscreen;
1447 	//D3DMULTISAMPLE_TYPE m_multisample_desktop;
1448 	//D3DMULTISAMPLE_TYPE m_multisample_windowed;
1449 	WritePrivateProfileIntW((int)m_multisample_fullscreen,L"multisample_fullscreen",m_szConfigIniFile,L"settings");
1450 	WritePrivateProfileIntW((int)m_multisample_desktop   ,L"multisample_desktop"   ,m_szConfigIniFile,L"settings");
1451 	WritePrivateProfileIntW((int)m_multisample_windowed  ,L"multisample_windowed"  ,m_szConfigIniFile,L"settings");
1452 
1453 	//GUID m_adapter_guid_fullscreen
1454 	//GUID m_adapter_guid_desktop
1455 	//GUID m_adapter_guid_windowed
1456 	char str[256];
1457 	GuidToText(&m_adapter_guid_fullscreen, str, sizeof(str));
1458 	WritePrivateProfileString("settings","adapter_guid_fullscreen",str,m_szConfigIniFileA);
1459 	GuidToText(&m_adapter_guid_desktop, str, sizeof(str));
1460 	WritePrivateProfileString("settings","adapter_guid_desktop",str,m_szConfigIniFileA);
1461 	GuidToText(&m_adapter_guid_windowed,   str, sizeof(str));
1462 	WritePrivateProfileString("settings","adapter_guid_windowed"  ,str,m_szConfigIniFileA);
1463 	WritePrivateProfileString("settings","adapter_devicename_fullscreen",m_adapter_devicename_fullscreen,m_szConfigIniFileA);
1464 	WritePrivateProfileString("settings","adapter_devicename_desktop"   ,m_adapter_devicename_desktop   ,m_szConfigIniFileA);
1465 	WritePrivateProfileString("settings","adapter_devicename_windowed"  ,m_adapter_devicename_windowed  ,m_szConfigIniFileA);
1466 
1467 	// FONTS
1468 	WRITE_FONT(0);
1469 	WRITE_FONT(1);
1470 	WRITE_FONT(2);
1471 	WRITE_FONT(3);
1472 #if (NUM_EXTRA_FONTS >= 1)
1473 	WRITE_FONT(4);
1474 #endif
1475 #if (NUM_EXTRA_FONTS >= 2)
1476 	WRITE_FONT(5);
1477 #endif
1478 #if (NUM_EXTRA_FONTS >= 3)
1479 	WRITE_FONT(6);
1480 #endif
1481 #if (NUM_EXTRA_FONTS >= 4)
1482 	WRITE_FONT(7);
1483 #endif
1484 #if (NUM_EXTRA_FONTS >= 5)
1485 	WRITE_FONT(8);
1486 #endif
1487 
1488 	WritePrivateProfileIntW(m_start_fullscreen,L"start_fullscreen",m_szConfigIniFile,L"settings");
1489 	WritePrivateProfileIntW(m_start_desktop   ,L"start_desktop"   ,m_szConfigIniFile,L"settings");
1490 	WritePrivateProfileIntW(m_fake_fullscreen_mode,L"fake_fullscreen_mode",m_szConfigIniFile,L"settings");
1491 	WritePrivateProfileIntW(m_max_fps_fs,L"max_fps_fs",m_szConfigIniFile,L"settings");
1492 	WritePrivateProfileIntW(m_max_fps_dm,L"max_fps_dm",m_szConfigIniFile,L"settings");
1493 	WritePrivateProfileIntW(m_max_fps_w ,L"max_fps_w" ,m_szConfigIniFile,L"settings");
1494 	WritePrivateProfileIntW(m_show_press_f1_msg,L"show_press_f1_msg",m_szConfigIniFile,L"settings");
1495 	WritePrivateProfileIntW(m_allow_page_tearing_w,L"allow_page_tearing_w",m_szConfigIniFile,L"settings");
1496 	WritePrivateProfileIntW(m_allow_page_tearing_fs,L"allow_page_tearing_fs",m_szConfigIniFile,L"settings");
1497 	WritePrivateProfileIntW(m_allow_page_tearing_dm,L"allow_page_tearing_dm",m_szConfigIniFile,L"settings");
1498 	WritePrivateProfileIntW(m_minimize_winamp,L"minimize_winamp",m_szConfigIniFile,L"settings");
1499 	WritePrivateProfileIntW(m_desktop_show_icons,L"desktop_show_icons",m_szConfigIniFile,L"settings");
1500 	WritePrivateProfileIntW(m_desktop_textlabel_boxes,L"desktop_textlabel_boxes",m_szConfigIniFile,L"settings");
1501 	WritePrivateProfileIntW(m_desktop_manual_icon_scoot,L"desktop_manual_icon_scoot",m_szConfigIniFile,L"settings");
1502 	WritePrivateProfileIntW(m_desktop_555_fix,L"desktop_555_fix",m_szConfigIniFile,L"settings");
1503 	WritePrivateProfileIntW(m_dualhead_horz,L"dualhead_horz",m_szConfigIniFile,L"settings");
1504 	WritePrivateProfileIntW(m_dualhead_vert,L"dualhead_vert",m_szConfigIniFile,L"settings");
1505 	WritePrivateProfileIntW(m_save_cpu,L"save_cpu",m_szConfigIniFile,L"settings");
1506 	WritePrivateProfileIntW(m_skin,L"skin",m_szConfigIniFile,L"settings");
1507 	WritePrivateProfileIntW(m_fix_slow_text,L"fix_slow_text",m_szConfigIniFile,L"settings");
1508 	WritePrivateProfileIntW(m_vj_mode,L"vj_mode",m_szConfigIniFile,L"settings");
1509 
1510 	//D3DDISPLAYMODE m_fs_disp_mode
1511 	WritePrivateProfileIntW(m_disp_mode_fs.Width          ,L"disp_mode_fs_w",m_szConfigIniFile,L"settings");
1512 	WritePrivateProfileIntW(m_disp_mode_fs.Height          ,L"disp_mode_fs_h",m_szConfigIniFile,L"settings");
1513 	WritePrivateProfileIntW(m_disp_mode_fs.RefreshRate,L"disp_mode_fs_r",m_szConfigIniFile,L"settings");
1514 	WritePrivateProfileIntW(m_disp_mode_fs.Format     ,L"disp_mode_fs_f",m_szConfigIniFile,L"settings");
1515 
1516 	WritePrivateProfileIntW(INT_VERSION            ,L"version"    ,m_szConfigIniFile,L"settings");
1517 	WritePrivateProfileIntW(INT_SUBVERSION         ,L"subversion" ,m_szConfigIniFile,L"settings");
1518 
1519 	// finally, save the plugin's unique settings:
1520 	MyWriteConfig();
1521 }
1522 
1523 //----------------------------------------------------------------------
1524 //----------------------------------------------------------------------
1525 //----------------------------------------------------------------------
1526 
PluginRender(unsigned char * pWaveL,unsigned char * pWaveR)1527 int CPluginShell::PluginRender(unsigned char *pWaveL, unsigned char *pWaveR)//, unsigned char *pSpecL, unsigned char *pSpecR)
1528 {
1529 	// return FALSE here to tell Winamp to terminate the plugin
1530 
1531 	if (!m_lpDX || !m_lpDX->m_ready)
1532 	{
1533 		// note: 'm_ready' will go false when a device reset fatally fails
1534 		//       (for example, when user resizes window, or toggles fullscreen.)
1535 		m_exiting = 1;
1536 		return false;   // EXIT THE PLUGIN
1537 	}
1538 
1539 	if (m_hTextWnd)
1540 		m_lost_focus = ((GetFocus() != GetPluginWindow()) && (GetFocus() != m_hTextWnd));
1541 	else
1542 		m_lost_focus = (GetFocus() != GetPluginWindow());
1543 
1544 	if ((m_screenmode==WINDOWED   && m_hidden) ||
1545 	    (m_screenmode==FULLSCREEN && m_lost_focus) ||
1546 	    (m_screenmode==WINDOWED   && m_resizing)
1547 	   )
1548 	{
1549 		Sleep(30);
1550 		return true;
1551 	}
1552 
1553 	// test for lost device
1554 	// (this happens when device is fullscreen & user alt-tabs away,
1555 	//  or when monitor power-saving kicks in)
1556 	HRESULT hr = m_lpDX->m_lpDevice->TestCooperativeLevel();
1557 	if (hr == D3DERR_DEVICENOTRESET)
1558 	{
1559 		// device WAS lost, and is now ready to be reset (and come back online):
1560 		CleanUpDX9Stuff(0);
1561 		if (m_lpDX->m_lpDevice->Reset(&m_lpDX->m_d3dpp) != D3D_OK)
1562 		{
1563 			// note: a basic warning messagebox will have already been given.
1564 			// now suggest specific advice on how to regain more video memory:
1565 			if (m_lpDX->m_lastErr == DXC_ERR_CREATEDEV_PROBABLY_OUTOFVIDEOMEMORY)
1566 				SuggestHowToFreeSomeMem();
1567 			return false;  // EXIT THE PLUGIN
1568 		}
1569 		if (!AllocateDX9Stuff())
1570 			return false;  // EXIT THE PLUGIN
1571 	}
1572 	else if (hr != D3D_OK)
1573 	{
1574 		// device is lost, and not yet ready to come back; sleep.
1575 		Sleep(30);
1576 		return true;
1577 	}
1578 
1579 	if (m_vjd3d9_device)
1580 	{
1581 		HRESULT hr = m_vjd3d9_device->TestCooperativeLevel();
1582 		if (hr == D3DERR_DEVICENOTRESET)
1583 		{
1584 			RECT c;
1585 			GetClientRect(m_hTextWnd, &c);
1586 
1587 			POINT p;
1588 			p.x = c.left;
1589 			p.y = c.top;
1590 			if (ClientToScreen(m_hTextWnd, &p))
1591 			{
1592 				c.left += p.x;
1593 				c.right += p.x;
1594 				c.top += p.y;
1595 				c.bottom += p.y;
1596 			}
1597 
1598 			CleanUpVJStuff();
1599 			if (!InitVJStuff(&c))
1600 				return false;  // EXIT THE PLUGIN
1601 		}
1602 	}
1603 
1604 	if (m_screenmode==DESKTOP)
1605 	{
1606 		PushWindowToJustBeforeDesktop(GetPluginWindow());
1607 	}
1608 
1609 	DoTime();
1610 	AnalyzeNewSound(pWaveL, pWaveR);
1611 	AlignWaves();
1612 
1613 	DrawAndDisplay(0);
1614 
1615 	EnforceMaxFPS();
1616 
1617 	m_frame++;
1618 
1619 	return true;
1620 }
1621 
PushWindowToJustBeforeDesktop(HWND h)1622 void CPluginShell::PushWindowToJustBeforeDesktop(HWND h)
1623 {
1624 	// if our window isn't already at the bottom of the Z order,
1625 	// freshly send it to HWND_BOTTOM.
1626 
1627 	// this usually gives us the Program Manager window:
1628 	HWND hWndBottom = GetWindow(h, GW_HWNDLAST);
1629 
1630 	// then, bottommost 'normal' window is usually the one just in front of it:
1631 	if (hWndBottom == m_hWndProgMan)
1632 		hWndBottom = GetWindow(hWndBottom, GW_HWNDPREV);
1633 
1634 	if (hWndBottom != h)
1635 	{
1636 		m_force_accept_WM_WINDOWPOSCHANGING = 1;
1637 		SetWindowPos(h, HWND_BOTTOM, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE);
1638 		m_force_accept_WM_WINDOWPOSCHANGING = 0;
1639 	}
1640 
1641 	/*
1642 	HWND hDesktopBkgWnd = FindWindow("SHELLDLL_DefView", "");
1643 	if (hDesktopBkgWnd)
1644 	{
1645 	    HWND hWndInFrontOfIcons = GetWindow(h, GW_HWNDPREV);
1646 	    if (hWndInFrontOfIcons != h)
1647 	    {
1648 	        m_force_accept_WM_WINDOWPOSCHANGING = 1;
1649 	        SetWindowPos(hDesktopBkgWnd, h, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE);
1650 	        m_force_accept_WM_WINDOWPOSCHANGING = 0;
1651 	    }
1652 	}
1653 	*/
1654 
1655 
1656 }
1657 
DrawAndDisplay(int redraw)1658 void CPluginShell::DrawAndDisplay(int redraw)
1659 {
1660 	int cx = m_vjd3d9_device ? m_nTextWndWidth  : m_lpDX->m_client_width;
1661 	int cy = m_vjd3d9_device ? m_nTextWndHeight : m_lpDX->m_client_height;
1662 	if (m_lpDDSText)
1663 	{
1664 		D3DSURFACE_DESC desc;
1665 		if (D3D_OK == m_lpDDSText->GetLevelDesc(0, &desc))
1666 		{
1667 			cx = min(cx, (int)desc.Width);
1668 			cy = min(cy, (int)desc.Height);
1669 		}
1670 	}
1671 	m_upper_left_corner_y  = TEXT_MARGIN + GetCanvasMarginY();
1672 	m_upper_right_corner_y = TEXT_MARGIN + GetCanvasMarginY();
1673 	m_lower_left_corner_y  = cy - TEXT_MARGIN - GetCanvasMarginY();
1674 	m_lower_right_corner_y = cy - TEXT_MARGIN - GetCanvasMarginY();
1675 	m_left_edge            = TEXT_MARGIN + GetCanvasMarginX();
1676 	m_right_edge           = cx - TEXT_MARGIN - GetCanvasMarginX();
1677 
1678 	/*if (m_screenmode == DESKTOP || m_screenmode == FAKE_FULLSCREEN)
1679 	{
1680 	    // check if taskbar is above plugin window;
1681 	    // if so, scoot text & icons out of the way.
1682 	    //     [...should always be true for Desktop Mode,
1683 	    //         but it's like this for code simplicity.]
1684 	    int taskbar_is_above_plugin_window = 1;
1685 	    HWND h = FindWindow("Shell_TrayWnd", NULL);
1686 	    while (h) //(..shouldn't be very many windows to iterate through here)
1687 	    {
1688 	        h = GetWindow(h, GW_HWNDPREV);
1689 	        if (h == GetPluginWindow())
1690 	        {
1691 	            taskbar_is_above_plugin_window = 0;
1692 	            break;
1693 	        }
1694 	    }
1695 
1696 	    if (taskbar_is_above_plugin_window)
1697 	    {
1698 	        // respect the taskbar area; make sure the text, desktop icons, etc.
1699 	        // don't appear underneath it.
1700 	        //m_upper_left_corner_y  += m_lpDX->m_monitor_work_rect.top - m_lpDX->m_monitor_rect.top;
1701 	        //m_upper_right_corner_y += m_lpDX->m_monitor_work_rect.top - m_lpDX->m_monitor_rect.top;
1702 	        //m_lower_left_corner_y  -= m_lpDX->m_monitor_rect.bottom - m_lpDX->m_monitor_work_rect.bottom;
1703 	        //m_lower_right_corner_y -= m_lpDX->m_monitor_rect.bottom - m_lpDX->m_monitor_work_rect.bottom;
1704 	        //m_left_edge  += m_lpDX->m_monitor_work_rect.left - m_lpDX->m_monitor_rect.left;
1705 	        //m_right_edge -= m_lpDX->m_monitor_rect.right - m_lpDX->m_monitor_work_rect.right;
1706 	        m_lpDX->UpdateMonitorWorkRect();
1707 	        m_upper_left_corner_y  = max(m_upper_left_corner_y , m_lpDX->m_monitor_work_rect.top - m_lpDX->m_monitor_rect.top + TEXT_MARGIN + GetCanvasMarginY());
1708 	        m_upper_right_corner_y = max(m_upper_right_corner_y, m_lpDX->m_monitor_work_rect.top - m_lpDX->m_monitor_rect.top + TEXT_MARGIN + GetCanvasMarginY());
1709 	        m_lower_left_corner_y  = min(m_lower_left_corner_y , m_lpDX->m_client_height - (m_lpDX->m_monitor_rect.bottom - m_lpDX->m_monitor_work_rect.bottom) - TEXT_MARGIN - GetCanvasMarginY());
1710 	        m_lower_right_corner_y = min(m_lower_right_corner_y, m_lpDX->m_client_height - (m_lpDX->m_monitor_rect.bottom - m_lpDX->m_monitor_work_rect.bottom) - TEXT_MARGIN - GetCanvasMarginY());
1711 	        m_left_edge  = max(m_left_edge , m_lpDX->m_monitor_work_rect.left - m_lpDX->m_monitor_rect.left + TEXT_MARGIN + GetCanvasMarginX() );
1712 	        m_right_edge = min(m_right_edge, m_lpDX->m_client_width - (m_lpDX->m_monitor_rect.right - m_lpDX->m_monitor_work_rect.right) - TEXT_MARGIN + GetCanvasMarginX());
1713 	    }
1714 	}*/
1715 
1716 	if (D3D_OK==m_lpDX->m_lpDevice->BeginScene())
1717 	{
1718 		MyRenderFn(redraw);
1719 
1720 		PrepareFor2DDrawing_B(GetDevice(), GetWidth(), GetHeight());
1721 
1722 		RenderDesktop();
1723 		if (!m_vjd3d9_device)   // in VJ mode, this renders to different context, so do it after BeginScene() on 2nd device.
1724 			RenderBuiltInTextMsgs();    // to m_lpDDSText?
1725 		MyRenderUI(&m_upper_left_corner_y, &m_upper_right_corner_y, &m_lower_left_corner_y, &m_lower_right_corner_y, m_left_edge, m_right_edge);
1726 		RenderPlaylist();
1727 
1728 		if (!m_vjd3d9_device)
1729 			m_text.DrawNow();
1730 
1731 		m_lpDX->m_lpDevice->EndScene();
1732 	}
1733 
1734 	// VJ Mode:
1735 	if (m_vj_mode && m_vjd3d9_device && !m_hidden_textwnd && D3D_OK==m_vjd3d9_device->BeginScene())
1736 	{
1737 		if (!m_lpDDSText || m_bClearVJWindow)
1738 			m_vjd3d9_device->Clear(0, 0, D3DCLEAR_TARGET, 0xFF000000, 1.0f, 0);
1739 		m_bClearVJWindow = false;
1740 		// note: when using debug DX runtime, textwnd will flash red/green after frame 4, if no text is drawn on a frame!
1741 
1742 		RenderBuiltInTextMsgs();
1743 
1744 		PrepareFor2DDrawing_B(m_vjd3d9_device, m_nTextWndWidth, m_nTextWndHeight);
1745 
1746 		m_text.DrawNow();
1747 
1748 		m_vjd3d9_device->EndScene();
1749 	}
1750 
1751 	if (m_screenmode == DESKTOP)
1752 	{
1753 		// window is hidden after creation, until 1st frame is ready to go;
1754 		// now that it's ready, we show it.
1755 		// see dxcontext::Internal_Init()'s call to SetWindowPos() for the DESKTOP case.
1756 		if (!IsWindowVisible(GetPluginWindow()))
1757 			ShowWindow(GetPluginWindow(), SW_SHOWNORMAL);
1758 	}
1759 
1760 	if (m_screenmode == WINDOWED && (m_lpDX->m_client_width != m_lpDX->m_REAL_client_width || m_lpDX->m_client_height != m_lpDX->m_REAL_client_height))
1761 	{
1762 		int real_w = m_lpDX->m_REAL_client_width;   // real client size, in pixels
1763 		int real_h = m_lpDX->m_REAL_client_height;
1764 		int fat_w = m_lpDX->m_client_width;         // oversized VS canvas size, in pixels
1765 		int fat_h = m_lpDX->m_client_height;
1766 		int extra_w = fat_w - real_w;
1767 		int extra_h = fat_h - real_h;
1768 		RECT src, dst;
1769 		SetRect(&src, extra_w/2, extra_h/2, extra_w/2 + real_w, extra_h/2 + real_h);
1770 		SetRect(&dst, 0, 0, real_w, real_h);
1771 		m_lpDX->m_lpDevice->Present(&src, &dst,NULL,NULL);
1772 	}
1773 	else
1774 		m_lpDX->m_lpDevice->Present(NULL,NULL,NULL,NULL);
1775 
1776 	if (m_vjd3d9_device && !m_hidden_textwnd)
1777 		m_vjd3d9_device->Present(NULL,NULL,NULL,NULL);
1778 }
1779 
EnforceMaxFPS()1780 void CPluginShell::EnforceMaxFPS()
1781 {
1782 	int max_fps;
1783 	switch (m_screenmode)
1784 	{
1785 	case WINDOWED:        max_fps = m_max_fps_w;  break;
1786 	case FULLSCREEN:      max_fps = m_max_fps_fs; break;
1787 	case FAKE_FULLSCREEN: max_fps = m_max_fps_fs; break;
1788 	case DESKTOP:         max_fps = m_max_fps_dm; break;
1789 	}
1790 
1791 	if (max_fps <= 0)
1792 		return;
1793 
1794 	float fps_lo = (float)max_fps;
1795 	float fps_hi = (float)max_fps;
1796 
1797 	if (m_save_cpu)
1798 	{
1799 		// Find the optimal lo/hi bounds for the fps
1800 		// that will result in a maximum difference,
1801 		// in the time for a single frame, of 0.003 seconds -
1802 		// the assumed granularity for Sleep(1) -
1803 
1804 		// Using this range of acceptable fps
1805 		// will allow us to do (sloppy) fps limiting
1806 		// using only Sleep(1), and never the
1807 		// second half of it: Sleep(0) in a tight loop,
1808 		// which sucks up the CPU (whereas Sleep(1)
1809 		// leaves it idle).
1810 
1811 		// The original equation:
1812 		//   1/(max_fps*t1) = 1/(max*fps/t1) - 0.003
1813 		// where:
1814 		//   t1 > 0
1815 		//   max_fps*t1 is the upper range for fps
1816 		//   max_fps/t1 is the lower range for fps
1817 
1818 		float a = 1;
1819 		float b = -0.003f * max_fps;
1820 		float c = -1.0f;
1821 		float det = b*b - 4*a*c;
1822 		if (det>0)
1823 		{
1824 			float t1 = (-b + sqrtf(det)) / (2*a);
1825 			//float t2 = (-b - sqrtf(det)) / (2*a);
1826 
1827 			if (t1 > 1.0f)
1828 			{
1829 				fps_lo = max_fps / t1;
1830 				fps_hi = max_fps * t1;
1831 				// verify: now [1.0f/fps_lo - 1.0f/fps_hi] should equal 0.003 seconds.
1832 				// note: allowing tolerance to go beyond these values for
1833 				// fps_lo and fps_hi would gain nothing.
1834 			}
1835 		}
1836 	}
1837 
1838 	if (m_high_perf_timer_freq.QuadPart > 0)
1839 	{
1840 		LARGE_INTEGER t;
1841 		QueryPerformanceCounter(&t);
1842 
1843 		if (m_prev_end_of_frame.QuadPart != 0)
1844 		{
1845 			int ticks_to_wait_lo = (int)((float)m_high_perf_timer_freq.QuadPart / (float)fps_hi);
1846 			int ticks_to_wait_hi = (int)((float)m_high_perf_timer_freq.QuadPart / (float)fps_lo);
1847 			int done = 0;
1848 			int loops = 0;
1849 			do
1850 			{
1851 				QueryPerformanceCounter(&t);
1852 
1853 				__int64 t2 = t.QuadPart - m_prev_end_of_frame.QuadPart;
1854 				if (t2 > 2147483000)
1855 					done = 1;
1856 				if (t.QuadPart < m_prev_end_of_frame.QuadPart)    // time wrap
1857 					done = 1;
1858 
1859 				// this is sloppy - if your freq. is high, this can overflow (to a (-) int) in just a few minutes
1860 				// but it's ok, we have protection for that above.
1861 				int ticks_passed = (int)(t.QuadPart - m_prev_end_of_frame.QuadPart);
1862 				if (ticks_passed >= ticks_to_wait_lo)
1863 					done = 1;
1864 
1865 				if (!done)
1866 				{
1867 					// if > 0.01s left, do Sleep(1), which will actually sleep some
1868 					//   steady amount of up to 3 ms (depending on the OS),
1869 					//   and do so in a nice way (cpu meter drops; laptop battery spared).
1870 					// otherwise, do a few Sleep(0)'s, which just give up the timeslice,
1871 					//   but don't really save cpu or battery, but do pass a tiny
1872 					//   amount of time.
1873 
1874 					//if (ticks_left > (int)m_high_perf_timer_freq.QuadPart/500)
1875 					if (ticks_to_wait_hi - ticks_passed > (int)m_high_perf_timer_freq.QuadPart/100)
1876 						Sleep(5);
1877 					else if (ticks_to_wait_hi - ticks_passed > (int)m_high_perf_timer_freq.QuadPart/1000)
1878 						Sleep(1);
1879 					else
1880 						for (int i=0; i<10; i++)
1881 							Sleep(0);  // causes thread to give up its timeslice
1882 				}
1883 			}
1884 			while (!done);
1885 		}
1886 
1887 		m_prev_end_of_frame = t;
1888 	}
1889 	else
1890 	{
1891 		Sleep(1000/max_fps);
1892 	}
1893 }
1894 
DoTime()1895 void CPluginShell::DoTime()
1896 {
1897 	if (m_frame==0)
1898 	{
1899 		m_fps = 30;
1900 		m_time = 0;
1901 		m_time_hist_pos = 0;
1902 	}
1903 
1904 	double new_raw_time;
1905 	float elapsed;
1906 
1907 	if (m_high_perf_timer_freq.QuadPart != 0)
1908 	{
1909 		// get high-precision time
1910 		// precision: usually from 1..6 us (MICROseconds), depending on the cpu speed.
1911 		// (higher cpu speeds tend to have better precision here)
1912 		LARGE_INTEGER t;
1913 		if (!QueryPerformanceCounter(&t))
1914 		{
1915 			m_high_perf_timer_freq.QuadPart = 0;   // something went wrong (exception thrown) -> revert to crappy timer
1916 		}
1917 		else
1918 		{
1919 			new_raw_time = (double)t.QuadPart;
1920 			elapsed = (float)((new_raw_time - m_last_raw_time)/(double)m_high_perf_timer_freq.QuadPart);
1921 		}
1922 	}
1923 
1924 	if (m_high_perf_timer_freq.QuadPart == 0)
1925 	{
1926 		// get low-precision time
1927 		// precision: usually 1 ms (MILLIsecond) for win98, and 10 ms for win2k.
1928 		new_raw_time = (double)(timeGetTime()*0.001);
1929 		elapsed = (float)(new_raw_time - m_last_raw_time);
1930 	}
1931 
1932 	m_last_raw_time = new_raw_time;
1933 	int slots_to_look_back = (m_high_perf_timer_freq.QuadPart==0) ? TIME_HIST_SLOTS : TIME_HIST_SLOTS/2;
1934 
1935 	m_time += 1.0f/m_fps;
1936 
1937 	// timekeeping goals:
1938 	//    1. keep 'm_time' increasing SMOOTHLY: (smooth animation depends on it)
1939 	//          m_time += 1.0f/m_fps;     // where m_fps is a bit damped
1940 	//    2. keep m_time_hist[] 100% accurate (except for filtering out pauses),
1941 	//       so that when we look take the difference between two entries,
1942 	//       we get the real amount of time that passed between those 2 frames.
1943 	//          m_time_hist[i] = m_last_raw_time + elapsed_corrected;
1944 
1945 	if (m_frame > TIME_HIST_SLOTS)
1946 	{
1947 		if (m_fps < 60.0f)
1948 			slots_to_look_back = (int)(slots_to_look_back*(0.1f + 0.9f*(m_fps/60.0f)));
1949 
1950 		if (elapsed > 5.0f/m_fps || elapsed > 1.0f || elapsed < 0)
1951 			elapsed = 1.0f / 30.0f;
1952 
1953 		float old_hist_time = m_time_hist[(m_time_hist_pos - slots_to_look_back + TIME_HIST_SLOTS) % TIME_HIST_SLOTS];
1954 		float new_hist_time = m_time_hist[(m_time_hist_pos - 1 + TIME_HIST_SLOTS) % TIME_HIST_SLOTS]
1955 		                      + elapsed;
1956 
1957 		m_time_hist[m_time_hist_pos] = new_hist_time;
1958 		m_time_hist_pos = (m_time_hist_pos+1) % TIME_HIST_SLOTS;
1959 
1960 		float new_fps = slots_to_look_back / (float)(new_hist_time - old_hist_time);
1961 		float damping = (m_high_perf_timer_freq.QuadPart==0) ? 0.93f : 0.87f;
1962 
1963 		// damp heavily, so that crappy timer precision doesn't make animation jerky
1964 		if (fabsf(m_fps - new_fps) > 3.0f)
1965 			m_fps = new_fps;
1966 		else
1967 			m_fps = damping*m_fps + (1-damping)*new_fps;
1968 	}
1969 	else
1970 	{
1971 		float damping = (m_high_perf_timer_freq.QuadPart==0) ? 0.8f : 0.6f;
1972 
1973 		if (m_frame < 2)
1974 			elapsed = 1.0f / 30.0f;
1975 		else if (elapsed > 1.0f || elapsed < 0)
1976 			elapsed = 1.0f / m_fps;
1977 
1978 		float old_hist_time = m_time_hist[0];
1979 		float new_hist_time = m_time_hist[(m_time_hist_pos - 1 + TIME_HIST_SLOTS) % TIME_HIST_SLOTS]
1980 		                      + elapsed;
1981 
1982 		m_time_hist[m_time_hist_pos] = new_hist_time;
1983 		m_time_hist_pos = (m_time_hist_pos+1) % TIME_HIST_SLOTS;
1984 
1985 		if (m_frame > 0)
1986 		{
1987 			float new_fps = (m_frame) / (new_hist_time - old_hist_time);
1988 			m_fps = damping*m_fps + (1-damping)*new_fps;
1989 		}
1990 	}
1991 
1992 	// Synchronize the audio and video by telling Winamp how many milliseconds we want the audio data,
1993 	// before it's actually audible.  If we set this to the amount of time it takes to display 1 frame
1994 	// (1/fps), the video and audio should be perfectly synchronized.
1995 	if (m_fps < 2.0f)
1996 		mod1.latencyMs = 500;
1997 	else if (m_fps > 125.0f)
1998 		mod1.latencyMs = 8;
1999 	else
2000 		mod1.latencyMs = (int)(1000.0f/m_fps*m_lpDX->m_frame_delay + 0.5f);
2001 }
2002 
AnalyzeNewSound(unsigned char * pWaveL,unsigned char * pWaveR)2003 void CPluginShell::AnalyzeNewSound(unsigned char *pWaveL, unsigned char *pWaveR)
2004 {
2005 	// we get 576 samples in from winamp.
2006 	// the output of the fft has 'num_frequencies' samples,
2007 	//   and represents the frequency range 0 hz - 22,050 hz.
2008 	// usually, plugins only use half of this output (the range 0 hz - 11,025 hz),
2009 	//   since >10 khz doesn't usually contribute much.
2010 
2011 	int i;
2012 
2013 	float temp_wave[2][576];
2014 
2015 	int old_i = 0;
2016 	for (i=0; i<576; i++)
2017 	{
2018 		m_sound.fWaveform[0][i] = (float)((pWaveL[i] ^ 128) - 128);
2019 		m_sound.fWaveform[1][i] = (float)((pWaveR[i] ^ 128) - 128);
2020 
2021 		// simulating single frequencies from 200 to 11,025 Hz:
2022 		//float freq = 1.0f + 11050*(GetFrame() % 100)*0.01f;
2023 		//m_sound.fWaveform[0][i] = 10*sinf(i*freq*6.28f/44100.0f);
2024 
2025 		// damp the input into the FFT a bit, to reduce high-frequency noise:
2026 		temp_wave[0][i] = 0.5f*(m_sound.fWaveform[0][i] + m_sound.fWaveform[0][old_i]);
2027 		temp_wave[1][i] = 0.5f*(m_sound.fWaveform[1][i] + m_sound.fWaveform[1][old_i]);
2028 		old_i = i;
2029 	}
2030 
2031 	m_fftobj.time_to_frequency_domain(temp_wave[0], m_sound.fSpectrum[0]);
2032 	m_fftobj.time_to_frequency_domain(temp_wave[1], m_sound.fSpectrum[1]);
2033 
2034 	// sum (left channel) spectrum up into 3 bands
2035 	// [note: the new ranges do it so that the 3 bands are equally spaced, pitch-wise]
2036 	float min_freq = 200.0f;
2037 	float max_freq = 11025.0f;
2038 	float net_octaves = (logf(max_freq/min_freq) / logf(2.0f));     // 5.7846348455575205777914165223593
2039 	float octaves_per_band = net_octaves / 3.0f;                    // 1.9282116151858401925971388407864
2040 	float mult = powf(2.0f, octaves_per_band); // each band's highest freq. divided by its lowest freq.; 3.805831305510122517035102576162
2041 	// [to verify: min_freq * mult * mult * mult should equal max_freq.]
2042 	for (int ch=0; ch<2; ch++)
2043 	{
2044 		for (i=0; i<3; i++)
2045 		{
2046 			// old guesswork code for this:
2047 			//   float exp = 2.1f;
2048 			//   int start = (int)(NUM_FREQUENCIES*0.5f*powf(i/3.0f, exp));
2049 			//   int end   = (int)(NUM_FREQUENCIES*0.5f*powf((i+1)/3.0f, exp));
2050 			// results:
2051 			//          old range:      new range (ideal):
2052 			//   bass:  0-1097          200-761
2053 			//   mids:  1097-4705       761-2897
2054 			//   treb:  4705-11025      2897-11025
2055 			int start = (int)(NUM_FREQUENCIES * min_freq*powf(mult, (float)i)/11025.0f);
2056 			int end   = (int)(NUM_FREQUENCIES * min_freq*powf(mult, (float)(i+1))/11025.0f);
2057 			if (start < 0) start = 0;
2058 			if (end > NUM_FREQUENCIES) end = NUM_FREQUENCIES;
2059 
2060 			m_sound.imm[ch][i] = 0;
2061 			for (int j=start; j<end; j++)
2062 				m_sound.imm[ch][i] += m_sound.fSpectrum[ch][j];
2063 			m_sound.imm[ch][i] /= (float)(end-start);
2064 		}
2065 	}
2066 
2067 	// some code to find empirical long-term averages for imm[0..2]:
2068 	/*{
2069 	    static float sum[3];
2070 	    static int count = 0;
2071 
2072 	    #define FRAMES_PER_SONG 300     // should be at least 200!
2073 
2074 	    if (m_frame < FRAMES_PER_SONG)
2075 	    {
2076 	        sum[0] = sum[1] = sum[2] = 0;
2077 	        count = 0;
2078 	    }
2079 	    else
2080 	    {
2081 	        if (m_frame%FRAMES_PER_SONG == 0)
2082 	        {
2083 	            char buf[256];
2084 	            sprintf(buf, "%.4f, %.4f, %.4f     (%d samples / ~%d songs)\n",
2085 	                sum[0]/(float)(count),
2086 	                sum[1]/(float)(count),
2087 	                sum[2]/(float)(count),
2088 	                count,
2089 	                count/(FRAMES_PER_SONG-10)
2090 	            );
2091 	            OutputDebugString(buf);
2092 
2093 	            // skip to next song
2094 	            PostMessage(m_hWndWinamp,WM_COMMAND,40048,0);
2095 	        }
2096 	        else if (m_frame%FRAMES_PER_SONG == 5)
2097 	        {
2098 	            // then advance to 0-2 minutes into the song:
2099 	            PostMessage(m_hWndWinamp,WM_USER,(20 + (warand()%65) + (rand()%65))*1000,106);
2100 	        }
2101 	        else if (m_frame%FRAMES_PER_SONG >= 10)
2102 	        {
2103 	            sum[0] += m_sound.imm[0];
2104 	            sum[1] += m_sound.imm[1];
2105 	            sum[2] += m_sound.imm[2];
2106 	            count++;
2107 	        }
2108 	    }
2109 	}*/
2110 
2111 	// multiply by long-term, empirically-determined inverse averages:
2112 	// (for a trial of 244 songs, 10 seconds each, somewhere in the 2nd or 3rd minute,
2113 	//  the average levels were: 0.326781557	0.38087377	0.199888934
2114 	for (ch=0; ch<2; ch++)
2115 	{
2116 		m_sound.imm[ch][0] /= 0.326781557f;//0.270f;
2117 		m_sound.imm[ch][1] /= 0.380873770f;//0.343f;
2118 		m_sound.imm[ch][2] /= 0.199888934f;//0.295f;
2119 	}
2120 
2121 	// do temporal blending to create attenuated and super-attenuated versions
2122 	for (ch=0; ch<2; ch++)
2123 	{
2124 		for (i=0; i<3; i++)
2125 		{
2126 			// m_sound.avg[i]
2127 			{
2128 				float avg_mix;
2129 				if (m_sound.imm[ch][i] > m_sound.avg[ch][i])
2130 					avg_mix = AdjustRateToFPS(0.2f, 14.0f, m_fps);
2131 				else
2132 					avg_mix = AdjustRateToFPS(0.5f, 14.0f, m_fps);
2133 				m_sound.avg[ch][i] = m_sound.avg[ch][i]*avg_mix + m_sound.imm[ch][i]*(1-avg_mix);
2134 			}
2135 
2136 			// m_sound.med_avg[i]
2137 			// m_sound.long_avg[i]
2138 			{
2139 				float med_mix  = 0.91f;//0.800f + 0.11f*powf(t, 0.4f);    // primarily used for velocity_damping
2140 				float long_mix = 0.96f;//0.800f + 0.16f*powf(t, 0.2f);    // primarily used for smoke plumes
2141 				med_mix  = AdjustRateToFPS(med_mix, 14.0f, m_fps);
2142 				long_mix = AdjustRateToFPS(long_mix, 14.0f, m_fps);
2143 				m_sound.med_avg[ch][i]  =  m_sound.med_avg[ch][i]*(med_mix) + m_sound.imm[ch][i]*(1-med_mix);
2144 				m_sound.long_avg[ch][i] = m_sound.long_avg[ch][i]*(long_mix) + m_sound.imm[ch][i]*(1-long_mix);
2145 			}
2146 		}
2147 	}
2148 }
2149 
PrepareFor2DDrawing_B(IDirect3DDevice9 * pDevice,int w,int h)2150 void CPluginShell::PrepareFor2DDrawing_B(IDirect3DDevice9 *pDevice, int w, int h)
2151 {
2152 	// New 2D drawing area will have x,y coords in the range <-1,-1> .. <1,1>
2153 	//         +--------+ Y=-1
2154 	//         |        |
2155 	//         | screen |             Z=0: front of scene
2156 	//         |        |             Z=1: back of scene
2157 	//         +--------+ Y=1
2158 	//       X=-1      X=1
2159 	// NOTE: After calling this, be sure to then call (at least):
2160 	//  1. SetVertexShader()
2161 	//  2. SetTexture(), if you need it
2162 	// before rendering primitives!
2163 	// Also, be sure your sprites have a z coordinate of 0.
2164 
2165 	pDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
2166 	pDevice->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
2167 	pDevice->SetRenderState(D3DRS_ZFUNC,     D3DCMP_LESSEQUAL);
2168 	pDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
2169 	pDevice->SetRenderState(D3DRS_FILLMODE,  D3DFILL_SOLID);
2170 	pDevice->SetRenderState(D3DRS_FOGENABLE, FALSE);
2171 	pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
2172 	pDevice->SetRenderState(D3DRS_CLIPPING, TRUE);
2173 	pDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
2174 	pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
2175 	pDevice->SetRenderState(D3DRS_LOCALVIEWER, FALSE);
2176 	pDevice->SetRenderState(D3DRS_COLORVERTEX, TRUE);
2177 
2178 	pDevice->SetTexture(0, NULL);
2179 	pDevice->SetTexture(1, NULL);
2180 	pDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);//D3DTEXF_LINEAR);
2181 	pDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_POINT);//D3DTEXF_LINEAR);
2182 	pDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
2183 	pDevice->SetTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
2184 	pDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
2185 	pDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
2186 	pDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);
2187 	pDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
2188 
2189 	pDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
2190 	pDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
2191 	pDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
2192 
2193 	pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
2194 
2195 	// set up for 2D drawing:
2196 	{
2197 		D3DXMATRIX Ortho2D;
2198 		D3DXMATRIX Identity;
2199 
2200 		pMatrixOrthoLH(&Ortho2D, (float)w, (float)h, 0.0f, 1.0f);
2201 		D3DXMatrixIdentity(&Identity);
2202 
2203 		pDevice->SetTransform(D3DTS_PROJECTION, &Ortho2D);
2204 		pDevice->SetTransform(D3DTS_WORLD, &Identity);
2205 		pDevice->SetTransform(D3DTS_VIEW, &Identity);
2206 	}
2207 }
2208 
DrawDarkTranslucentBox(RECT * pr)2209 void CPluginShell::DrawDarkTranslucentBox(RECT* pr)
2210 {
2211 	// 'pr' is the rectangle that some text will occupy;
2212 	// a black box will be drawn around it, plus a bit of extra margin space.
2213 
2214 	if (m_vjd3d9_device)
2215 		return;
2216 
2217 	m_lpDX->m_lpDevice->SetVertexShader(NULL);
2218 	m_lpDX->m_lpDevice->SetPixelShader(NULL);
2219 	m_lpDX->m_lpDevice->SetFVF(SIMPLE_VERTEX_FORMAT);
2220 	m_lpDX->m_lpDevice->SetTexture(0, NULL);
2221 
2222 	m_lpDX->m_lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
2223 	m_lpDX->m_lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
2224 	m_lpDX->m_lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
2225 
2226 	m_lpDX->m_lpDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
2227 	m_lpDX->m_lpDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
2228 	m_lpDX->m_lpDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
2229 	m_lpDX->m_lpDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
2230 	m_lpDX->m_lpDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
2231 
2232 	// set up a quad
2233 	SIMPLEVERTEX verts[4];
2234 	for (int i=0; i<4; i++)
2235 	{
2236 		verts[i].x = (i%2==0) ? (float)(-m_lpDX->m_client_width /2  + pr->left)  :
2237 		             (float)(-m_lpDX->m_client_width /2  + pr->right);
2238 		verts[i].y = (i/2==0) ? (float)-(-m_lpDX->m_client_height/2 + pr->bottom)  :
2239 		             (float)-(-m_lpDX->m_client_height/2 + pr->top);
2240 		verts[i].z = 0;
2241 		verts[i].Diffuse = (m_screenmode==DESKTOP) ? 0xE0000000 : 0xD0000000;
2242 	}
2243 
2244 	m_lpDX->m_lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, verts, sizeof(SIMPLEVERTEX));
2245 
2246 	// undo unusual state changes:
2247 	m_lpDX->m_lpDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
2248 	m_lpDX->m_lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
2249 }
2250 
RenderBuiltInTextMsgs()2251 void CPluginShell::RenderBuiltInTextMsgs()
2252 {
2253 	int _show_press_f1_NOW = (m_show_press_f1_msg && m_time < PRESS_F1_DUR);
2254 
2255 	{
2256 		RECT r;
2257 
2258 		if (m_show_help)
2259 		{
2260 			int y = m_upper_left_corner_y;
2261 
2262 			SetRect(&r, 0, 0, GetWidth(), GetHeight());
2263 			if(!g_szHelp_W)
2264 				m_d3dx_font[HELPSCREEN_FONT]->DrawTextA(NULL, (char*)g_szHelp, -1, &r, DT_CALCRECT, 0xFFFFFFFF);
2265 			else
2266 				m_d3dx_font[HELPSCREEN_FONT]->DrawTextW(NULL, g_szHelp, -1, &r, DT_CALCRECT, 0xFFFFFFFF);
2267 
2268 			r.top += m_upper_left_corner_y;
2269 			r.left += m_left_edge;
2270 			r.right += m_left_edge + PLAYLIST_INNER_MARGIN*2;
2271 			r.bottom += m_upper_left_corner_y + PLAYLIST_INNER_MARGIN*2;
2272 			DrawDarkTranslucentBox(&r);
2273 
2274 			r.top += PLAYLIST_INNER_MARGIN;
2275 			r.left += PLAYLIST_INNER_MARGIN;
2276 			r.right -= PLAYLIST_INNER_MARGIN;
2277 			r.bottom -= PLAYLIST_INNER_MARGIN;
2278 			if(!g_szHelp_W)
2279 				m_d3dx_font[HELPSCREEN_FONT]->DrawTextA(NULL, (char*)g_szHelp, -1, &r, 0, 0xFFFFFFFF);
2280 			else
2281 				m_d3dx_font[HELPSCREEN_FONT]->DrawTextW(NULL, g_szHelp, -1, &r, 0, 0xFFFFFFFF);
2282 
2283 			m_upper_left_corner_y += r.bottom-r.top + PLAYLIST_INNER_MARGIN*3;
2284 		}
2285 
2286 		// render 'Press F1 for Help' message in lower-right corner:
2287 		if (_show_press_f1_NOW)
2288 		{
2289 			int dx = (int)(160.0f * powf(m_time/(float)(PRESS_F1_DUR), (float)(PRESS_F1_EXP)));
2290 			SetRect(&r, m_left_edge, m_lower_right_corner_y - GetFontHeight(DECORATIVE_FONT), m_right_edge + dx, m_lower_right_corner_y);
2291 			m_lower_right_corner_y -= m_d3dx_font[DECORATIVE_FONT]->DrawTextW(NULL, WASABI_API_LNGSTRINGW(IDS_PRESS_F1_MSG), -1, &r, DT_RIGHT, 0xFFFFFFFF);
2292 		}
2293 	}
2294 }
2295 
RenderPlaylist()2296 void CPluginShell::RenderPlaylist()
2297 {
2298 	// draw playlist:
2299 	if (m_show_playlist)
2300 	{
2301 		RECT r;
2302 		int nSongs = SendMessage(m_hWndWinamp,WM_USER, 0, 124);
2303 		int now_playing = SendMessage(m_hWndWinamp,WM_USER, 0, 125);
2304 
2305 		if (nSongs <= 0)
2306 		{
2307 			m_show_playlist = 0;
2308 		}
2309 		else
2310 		{
2311 			int playlist_vert_pixels = m_lower_left_corner_y - m_upper_left_corner_y;
2312 			int disp_lines  = min(MAX_SONGS_PER_PAGE, (playlist_vert_pixels - PLAYLIST_INNER_MARGIN*2) / GetFontHeight(PLAYLIST_FONT));
2313 			int total_pages = (nSongs) / disp_lines;
2314 
2315 			if (disp_lines<=0)
2316 				return;
2317 
2318 			// apply PgUp/PgDn keypresses since last time
2319 			m_playlist_pos -= m_playlist_pageups * disp_lines;
2320 			m_playlist_pageups = 0;
2321 
2322 			if (m_playlist_pos < 0)
2323 				m_playlist_pos = 0;
2324 			if (m_playlist_pos >= nSongs)
2325 				m_playlist_pos = nSongs-1;
2326 
2327 			// NOTE: 'dwFlags' is used for both DDRAW and DX9
2328 			DWORD dwFlags   = DT_SINGLELINE;// | DT_NOPREFIX | DT_WORD_ELLIPSIS;
2329 			DWORD color;
2330 
2331 			int cur_page    = (m_playlist_pos) / disp_lines;
2332 			int cur_line    = (m_playlist_pos + disp_lines - 1) % disp_lines;
2333 			int new_top_idx = cur_page * disp_lines;
2334 			int new_btm_idx = new_top_idx + disp_lines;
2335 			wchar_t buf[1024] = {0};
2336 
2337 			// ask winamp for the song names, but DO IT BEFORE getting the DC,
2338 			// otherwise vaio will crash (~DDRAW port).
2339 			if (m_playlist_top_idx != new_top_idx ||
2340 			    m_playlist_btm_idx != new_btm_idx)
2341 			{
2342 				for (int i=0; i<disp_lines; i++)
2343 				{
2344 					int j = new_top_idx + i;
2345 					if (j < nSongs)
2346 					{
2347 						// clip max len. of song name to 240 chars, to prevent overflows
2348 						lstrcpynW(buf, (wchar_t*)SendMessage(m_hWndWinamp, WM_USER, j, IPC_GETPLAYLISTTITLEW), 240);
2349 						wsprintfW(m_playlist[i], L"%d. %s ", j+1, buf);  // leave an extra space @ end, so italicized fonts don't get clipped
2350 					}
2351 				}
2352 			}
2353 
2354 			// update playlist cache, if necessary:
2355 			if (m_playlist_top_idx != new_top_idx ||
2356 			    m_playlist_btm_idx != new_btm_idx)
2357 			{
2358 				m_playlist_top_idx = new_top_idx;
2359 				m_playlist_btm_idx = new_btm_idx;
2360 				m_playlist_width_pixels = 0;
2361 
2362 				int max_w = min(m_right_edge - m_left_edge, m_lpDX->m_client_width - TEXT_MARGIN*2 - PLAYLIST_INNER_MARGIN*2);
2363 
2364 				for (int i=0; i<disp_lines; i++)
2365 				{
2366 					int j = new_top_idx + i;
2367 					if (j < nSongs)
2368 					{
2369 						// clip max len. of song name to 240 chars, to prevent overflows
2370 						//strcpy(buf, (char*)SendMessage(m_hWndWinamp, WM_USER, j, 212));
2371 						//buf[240] = 0;
2372 						//sprintf(m_playlist[i], "%d. %s ", j+1, buf);  // leave an extra space @ end, so italicized fonts don't get clipped
2373 
2374 						SetRect(&r, 0, 0, max_w, 1024);
2375 						m_d3dx_font[PLAYLIST_FONT]->DrawTextW(NULL, m_playlist[i], -1, &r, dwFlags | DT_CALCRECT, 0xFFFFFFFF);
2376 						int w = r.right-r.left;
2377 						if (w>0)
2378 							m_playlist_width_pixels = max(m_playlist_width_pixels, w);
2379 					}
2380 					else
2381 					{
2382 						m_playlist[i][0] = 0;
2383 					}
2384 				}
2385 
2386 				if (m_playlist_width_pixels == 0 ||
2387 				    m_playlist_width_pixels > max_w)
2388 					m_playlist_width_pixels = max_w;
2389 			}
2390 
2391 			int start = max(0, (cur_page)*disp_lines);
2392 			int end   = min(nSongs, (cur_page+1)*disp_lines);
2393 
2394 			// draw dark box around where the playlist will go:
2395 
2396 			RECT r;
2397 			r.top    = m_upper_left_corner_y;
2398 			r.left   = m_left_edge;
2399 			r.right  = m_left_edge + m_playlist_width_pixels + PLAYLIST_INNER_MARGIN*2;
2400 			r.bottom = m_upper_left_corner_y + (end-start)*GetFontHeight(PLAYLIST_FONT) + PLAYLIST_INNER_MARGIN*2;
2401 			DrawDarkTranslucentBox(&r);
2402 
2403 			//m_d3dx_font[PLAYLIST_FONT]->Begin();
2404 
2405 			// draw playlist text
2406 			int y = m_upper_left_corner_y + PLAYLIST_INNER_MARGIN;
2407 			for (int i=start; i<end; i++)
2408 			{
2409 				SetRect(&r, m_left_edge + PLAYLIST_INNER_MARGIN, y, m_left_edge + PLAYLIST_INNER_MARGIN + m_playlist_width_pixels, y + GetFontHeight(PLAYLIST_FONT));
2410 
2411 				if (m_lpDX->GetBitDepth() == 8)
2412 					color = (i==m_playlist_pos) ?
2413 					        (i==now_playing ? 0xFFFFFFFF : 0xFFFFFFFF) :
2414 							        (i==now_playing ? 0xFFFFFFFF : 0xFF707070);
2415 				else
2416 					color = (i==m_playlist_pos) ?
2417 					        (i==now_playing ? PLAYLIST_COLOR_BOTH : PLAYLIST_COLOR_HILITE_TRACK) :
2418 							        (i==now_playing ? PLAYLIST_COLOR_PLAYING_TRACK : PLAYLIST_COLOR_NORMAL);
2419 
2420 				y += m_d3dx_font[PLAYLIST_FONT]->DrawTextW(NULL, m_playlist[i-start], -1, &r, dwFlags, color);
2421 			}
2422 
2423 			//m_d3dx_font[PLAYLIST_FONT]->End();
2424 		}
2425 	}
2426 }
2427 
SuggestHowToFreeSomeMem()2428 void CPluginShell::SuggestHowToFreeSomeMem()
2429 {
2430 	// This function is called when the plugin runs out of video memory;
2431 	//   it lets you show a messagebox to the user so you can (intelligently)
2432 	//   suggest how to free up some video memory, based on what settings
2433 	//   they've chosen.
2434 
2435 	wchar_t str[1024];
2436 
2437 	if (m_lpDX->m_current_mode.multisamp != D3DMULTISAMPLE_NONE)
2438 	{
2439 		if (m_lpDX->m_current_mode.screenmode == WINDOWED)
2440 			WASABI_API_LNGSTRINGW_BUF(IDS_TO_FREE_UP_SOME_MEMORY_RESTART_WINAMP_THEN_GO_TO_CONFIG, str, 2048);
2441 		else if (m_lpDX->m_current_mode.screenmode == FAKE_FULLSCREEN)
2442 			WASABI_API_LNGSTRINGW_BUF(IDS_TO_FREE_UP_SOME_MEMORY_RESTART_WINAMP_THEN_GO_TO_CONFIG_2, str, 2048);
2443 		else
2444 			WASABI_API_LNGSTRINGW_BUF(IDS_TO_FREE_UP_SOME_MEMORY_RESTART_WINAMP_THEN_GO_TO_CONFIG_3, str, 2048);
2445 	}
2446 	else
2447 		if (m_lpDX->m_current_mode.screenmode == FULLSCREEN)  // true fullscreen
2448 			WASABI_API_LNGSTRINGW_BUF(IDS_TO_FREE_UP_VIDEO_MEMORY, str, 2048);
2449 		else    // windowed, desktop mode, or fake fullscreen
2450 			WASABI_API_LNGSTRINGW_BUF(IDS_TO_FREE_UP_VIDEO_MEMORY, str, 2048);
2451 
2452 	MessageBoxW(m_lpDX->GetHwnd(), str, WASABI_API_LNGSTRINGW(IDS_MILKDROP_SUGGESTION), MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
2453 }
2454 
WindowProc(HWND hWnd,unsigned uMsg,WPARAM wParam,LPARAM lParam)2455 LRESULT CALLBACK CPluginShell::WindowProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam)
2456 {
2457 	//if (uMsg==WM_GETDLGCODE)
2458 	//    return DLGC_WANTALLKEYS|DLGC_WANTCHARS|DLGC_WANTMESSAGE;    // this tells the embedwnd that we want keypresses to flow through to our client wnd.
2459 
2460 	if (uMsg == WM_CREATE)
2461 	{
2462 		CREATESTRUCT *create = (CREATESTRUCT *)lParam;
2463 		SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)create->lpCreateParams);
2464 	}
2465 
2466 	CPluginShell* p = (CPluginShell*)GetWindowLongPtr(hWnd,GWLP_USERDATA);
2467 	if (p)
2468 		return p->PluginShellWindowProc(hWnd, uMsg, wParam, lParam);
2469 	else
2470 		return DefWindowProcW(hWnd, uMsg, wParam, lParam);
2471 }
2472 
PluginShellWindowProc(HWND hWnd,unsigned uMsg,WPARAM wParam,LPARAM lParam)2473 LRESULT CPluginShell::PluginShellWindowProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam)
2474 {
2475 	USHORT mask = 1 << (sizeof(SHORT)*8 - 1);
2476 	//bool bShiftHeldDown = (GetKeyState(VK_SHIFT) & mask) != 0;
2477 	bool bCtrlHeldDown  = (GetKeyState(VK_CONTROL) & mask) != 0;
2478 	//bool bAltHeldDown: most keys come in under WM_SYSKEYDOWN when ALT is depressed.
2479 
2480 	int i;
2481 #ifdef _DEBUG
2482 	char caption[256] = "WndProc: frame 0, ";
2483 	if (m_frame > 0)
2484 	{
2485 		float time = m_time;
2486 		int hours = (int)(time/3600);
2487 		time -= hours*3600;
2488 		int minutes = (int)(time/60);
2489 		time -= minutes*60;
2490 		int seconds = (int)time;
2491 		time -= seconds;
2492 		int dsec = (int)(time*100);
2493 		sprintf(caption, "WndProc: frame %d, t=%dh:%02dm:%02d.%02ds, ", m_frame, hours, minutes, seconds, dsec);
2494 	}
2495 
2496 	if (uMsg != WM_MOUSEMOVE &&
2497 	    uMsg != WM_NCHITTEST &&
2498 	    uMsg != WM_SETCURSOR &&
2499 	    uMsg != WM_COPYDATA &&
2500 	    uMsg != WM_USER)
2501 		OutputDebugMessage(caption, hWnd, uMsg, wParam, lParam);
2502 #endif
2503 
2504 	switch (uMsg)
2505 	{
2506 	case WM_USER:
2507 		if (m_screenmode == DESKTOP)
2508 		{
2509 			// this function resides in vms_desktop.dll;
2510 			// its response will come later, via the WM_COPYDATA
2511 			// message (See below).
2512 			//KIV: **THIS CALL CRASHES EXPLORER IN VISTA**
2513 			getItemData(wParam);
2514 			return 0;
2515 		}
2516 		break;
2517 
2518 	case WM_COPYDATA:
2519 		if (m_screenmode == DESKTOP)
2520 		{
2521 			// this message is vms_desktop.dll's response to
2522 			// our call to getItemData().
2523 			PCOPYDATASTRUCT c = (PCOPYDATASTRUCT)lParam;
2524 			if (c && (c->cbData % sizeof(icon_t) == 0))
2525 			{
2526 				icon_t *pNewIcons = (icon_t*)c->lpData;
2527 
2528 				EnterCriticalSection(&m_desktop_cs);
2529 
2530 				if (m_desktop_icon_state == 1 && (c->dwData & 0x80000000))  // if doing a total refresh...
2531 				{
2532 					// ...we build the list from zero
2533 					int len = c->dwData & 0xFFFF;
2534 					for (int i=0; i<len; i++)
2535 						m_icon_list.push_back(pNewIcons[i]);
2536 				}
2537 				else if (m_desktop_icon_state == 3 && !(c->dwData & 0x80000000))
2538 				{
2539 					// otherwise, we alter existing things in the list:
2540 					IconList::iterator p;
2541 					int start = c->dwData & 0xFFFF;
2542 					int len   = c->dwData >> 16;
2543 
2544 					int i = 0;
2545 					for (p = m_icon_list.begin(); p != m_icon_list.end() && i<start; p++)
2546 						i++;
2547 					for (; p != m_icon_list.end() && i<start+len; p++)
2548 					{
2549 						p->x = pNewIcons[i-start].x;
2550 						p->y = pNewIcons[i-start].y;
2551 						memcpy(p->name, pNewIcons[i-start].name, sizeof(p->name));
2552 						memcpy(p->pidl, pNewIcons[i-start].pidl, sizeof(p->pidl));
2553 						i++;
2554 					}
2555 
2556 					m_desktop_icon_state = 2;
2557 					m_desktop_icon_update_frame = GetFrame();
2558 				}
2559 
2560 				LeaveCriticalSection(&m_desktop_cs);
2561 			}
2562 
2563 			return 0;
2564 		}
2565 		break;
2566 
2567 	case WM_ERASEBKGND:
2568 		// Repaint window when song is paused and image needs to be repainted:
2569 		if (SendMessage(m_hWndWinamp,WM_USER,0,104)!=1 && m_lpDX && m_lpDX->m_lpDevice && GetFrame() > 0)    // WM_USER/104 return codes: 1=playing, 3=paused, other=stopped
2570 		{
2571 			m_lpDX->m_lpDevice->Present(NULL,NULL,NULL,NULL);
2572 			return 0;
2573 		}
2574 		break;
2575 
2576 	case WM_WINDOWPOSCHANGING:
2577 		if (
2578 		  m_screenmode == DESKTOP
2579 		  && (!m_force_accept_WM_WINDOWPOSCHANGING)
2580 		  && m_lpDX && m_lpDX->m_ready
2581 		)
2582 		{
2583 			// unless we requested it ourselves or it's init time,
2584 			// prevent the fake desktop window from moving around
2585 			// in the Z order!  (i.e., keep it on the bottom)
2586 
2587 			// without this code, when you click on the 'real' desktop
2588 			// in a multimon setup, any windows that are overtop of the
2589 			// 'fake' desktop will flash, since they'll be covered
2590 			// up by the fake desktop window (but then shown again on
2591 			// the next frame, when we detect that the fake desktop
2592 			// window isn't on bottom & send it back to the bottom).
2593 
2594 			LPWINDOWPOS pwp = (LPWINDOWPOS)lParam;
2595 			if (pwp)
2596 				pwp->flags |= SWP_NOOWNERZORDER | SWP_NOZORDER;
2597 		}
2598 		if (m_screenmode==WINDOWED && m_lpDX && m_lpDX->m_ready && m_lpDX->m_current_mode.m_skin)
2599 			m_lpDX->SaveWindow();
2600 		break;
2601 	case WM_NCACTIVATE:
2602 		// *Very Important Handler!*
2603 		//    -Without this code, the app would not work properly when running in true
2604 		//     fullscreen mode on multiple monitors; it would auto-minimize whenever the
2605 		//     user clicked on a window in another display.
2606 		if (wParam == 0 &&
2607 		    m_screenmode == FULLSCREEN &&
2608 		    m_frame > 0 &&
2609 		    !m_exiting &&
2610 		    m_lpDX &&
2611 		    m_lpDX->m_ready
2612 		    && m_lpDX->m_lpD3D &&
2613 		    m_lpDX->m_lpD3D->GetAdapterCount() > 1
2614 		   )
2615 		{
2616 			return 0;
2617 		}
2618 		break;
2619 
2620 	case WM_DESTROY:
2621 		// note: don't post quit message here if the window is being destroyed
2622 		// and re-created on a switch between windowed & FAKE fullscreen modes.
2623 		if (!m_lpDX->TempIgnoreDestroyMessages())
2624 		{
2625 			// this is a final exit, and not just destroy-then-recreate-the-window.
2626 			// so, flag DXContext so it knows that someone else
2627 			// will take care of destroying the window!
2628 			m_lpDX->OnTrulyExiting();
2629 			PostQuitMessage(0);
2630 		}
2631 		return FALSE;
2632 		break;
2633 		// benski> a little hack to get the window size correct. it seems to work
2634 	case WM_USER+555:
2635 		if (m_lpDX && m_lpDX->m_ready && m_screenmode==WINDOWED && !m_resizing)
2636 		{
2637 			OnUserResizeWindow();
2638 			m_lpDX->SaveWindow();
2639 		}
2640 		break;
2641 	case WM_SIZE:
2642 		// clear or set activity flag to reflect focus
2643 		if (m_lpDX && m_lpDX->m_ready && m_screenmode==WINDOWED && !m_resizing)
2644 		{
2645 			m_hidden = (SIZE_MAXHIDE==wParam || SIZE_MINIMIZED==wParam) ? TRUE : FALSE;
2646 
2647 			if (SIZE_MAXIMIZED==wParam || SIZE_RESTORED==wParam) // the window has been maximized or restored
2648 				OnUserResizeWindow();
2649 		}
2650 		break;
2651 
2652 	case WM_ENTERSIZEMOVE:
2653 		m_resizing = 1;
2654 		break;
2655 
2656 	case WM_EXITSIZEMOVE:
2657 		if (m_lpDX && m_lpDX->m_ready && m_screenmode==WINDOWED)
2658 			OnUserResizeWindow();
2659 		m_resizing = 0;
2660 		break;
2661 
2662 	case WM_GETMINMAXINFO:
2663 	{
2664 		// don't let the window get too small
2665 		MINMAXINFO* p = (MINMAXINFO*)lParam;
2666 		if (p->ptMinTrackSize.x < 64)
2667 			p->ptMinTrackSize.x = 64;
2668 		p->ptMinTrackSize.y = p->ptMinTrackSize.x*3/4;
2669 	}
2670 	return 0;
2671 
2672 	case WM_MOUSEMOVE:
2673 		if (m_screenmode==DESKTOP && (m_desktop_dragging==1 || m_desktop_box==1))
2674 		{
2675 			m_desktop_drag_curpos.x = LOWORD(lParam);
2676 			m_desktop_drag_curpos.y = HIWORD(lParam);
2677 			if (m_desktop_box==1)
2678 			{
2679 				// update selection based on box coords
2680 				RECT box, temp;
2681 				box.left   = min(m_desktop_drag_curpos.x, m_desktop_drag_startpos.x);
2682 				box.right  = max(m_desktop_drag_curpos.x, m_desktop_drag_startpos.x);
2683 				box.top    = min(m_desktop_drag_curpos.y, m_desktop_drag_startpos.y);
2684 				box.bottom = max(m_desktop_drag_curpos.y, m_desktop_drag_startpos.y);
2685 
2686 				IconList::iterator p;
2687 				for (p = m_icon_list.begin(); p != m_icon_list.end(); p++)
2688 				{
2689 					p->selected = 0;
2690 
2691 					if (IntersectRect(&temp, &box, &p->label_rect))
2692 						p->selected = 1;
2693 					else if (IntersectRect(&temp, &box, &p->icon_rect))
2694 						p->selected = 1;
2695 				}
2696 			}
2697 
2698 			// repaint window manually, if winamp is paused
2699 			if (SendMessage(m_hWndWinamp,WM_USER,0,104) != 1)
2700 			{
2701 				PushWindowToJustBeforeDesktop(GetPluginWindow());
2702 				DrawAndDisplay(1);
2703 			}
2704 
2705 			//return 0;
2706 		}
2707 		break;
2708 
2709 	case WM_LBUTTONUP:
2710 		if (m_screenmode==DESKTOP)
2711 		{
2712 			if (m_desktop_dragging)
2713 			{
2714 				m_desktop_dragging = 0;
2715 
2716 				// move selected item(s) to new cursor position
2717 				int dx = LOWORD(lParam) - m_desktop_drag_startpos.x;
2718 				int dy = HIWORD(lParam) - m_desktop_drag_startpos.y;
2719 
2720 				if (dx!=0 || dy!=0)
2721 				{
2722 					int idx=0;
2723 					IconList::iterator p;
2724 					for (p = m_icon_list.begin(); p != m_icon_list.end(); p++)
2725 					{
2726 						if (p->selected)
2727 						{
2728 							SendMessage(m_hWndDesktopListView, LVM_SETITEMPOSITION, idx, MAKELPARAM(p->x + dx, p->y + dy));
2729 							p->x += dx;
2730 							p->y += dy;
2731 						}
2732 						idx++;
2733 					}
2734 				}
2735 
2736 				// repaint window manually, if winamp is paused
2737 				if (SendMessage(m_hWndWinamp,WM_USER,0,104) != 1)
2738 				{
2739 					PushWindowToJustBeforeDesktop(GetPluginWindow());
2740 					DrawAndDisplay(1);
2741 				}
2742 			}
2743 
2744 			if (m_desktop_box)
2745 			{
2746 				m_desktop_box = 0;
2747 
2748 				// repaint window manually, if winamp is paused
2749 				if (SendMessage(m_hWndWinamp,WM_USER,0,104) != 1)
2750 				{
2751 					PushWindowToJustBeforeDesktop(GetPluginWindow());
2752 					DrawAndDisplay(1);
2753 				}
2754 			}
2755 
2756 			//return 0;
2757 		}
2758 		break;
2759 
2760 	case WM_USER + 1666:
2761 		if (wParam == 1 && lParam == 15)
2762 		{
2763 			if (m_screenmode == FULLSCREEN || m_screenmode == FAKE_FULLSCREEN)
2764 				ToggleFullScreen();
2765 		}
2766 		return 0;
2767 	case WM_LBUTTONDOWN:
2768 	case WM_LBUTTONDBLCLK:
2769 	case WM_RBUTTONDOWN:
2770 	case WM_RBUTTONUP:
2771 		// Toggle between Fullscreen and Windowed modes on double-click
2772 		// note: this requires the 'CS_DBLCLKS' windowclass style!
2773 		if (m_screenmode != DESKTOP)
2774 		{
2775 			SetFocus(hWnd);
2776 			if (uMsg==WM_LBUTTONDBLCLK && m_frame>0)
2777 			{
2778 				ToggleFullScreen();
2779 				return 0;
2780 			}
2781 		}
2782 		else
2783 		{
2784 			POINT pt;
2785 			pt.x = LOWORD(lParam);
2786 			pt.y = HIWORD(lParam);
2787 
2788 			int done = 0;
2789 
2790 			for (int pass=0; pass<2 && !done; pass++)
2791 			{
2792 				IconList::iterator p;
2793 				for (p = m_icon_list.begin(); p != m_icon_list.end(); p++)
2794 				{
2795 					RECT *pr = (pass==0) ? &p->icon_rect : &p->label_rect;
2796 					int bottom_extend = (pass==0) ? 3 : 0; // accepts clicks in the 3-pixel gap between the icon and the text label.
2797 					if (pt.x >= pr->left &&
2798 					    pt.x <= pr->right &&
2799 					    pt.y >= pr->top &&
2800 					    pt.y <= pr->bottom + bottom_extend)
2801 					{
2802 						switch (uMsg)
2803 						{
2804 						case WM_RBUTTONUP:
2805 							//pt.x += m_lpDX->m_monitor_rect.left;
2806 							//pt.y += m_lpDX->m_monitor_rect.top;
2807 							DoExplorerMenu(GetPluginWindow(), (LPITEMIDLIST)p->pidl, pt);
2808 							break;
2809 						case WM_LBUTTONDBLCLK:
2810 						{
2811 							char buf[MAX_PATH];
2812 							sprintf(buf, "%s\\%s", m_szDesktopFolder, p->name);
2813 							ExecutePidl((LPITEMIDLIST)p->pidl, buf, m_szDesktopFolder, GetPluginWindow());
2814 						}
2815 						break;
2816 						case WM_LBUTTONDOWN:
2817 							m_desktop_dragging = 1;
2818 							memcpy(m_desktop_drag_pidl, p->pidl, sizeof(m_desktop_drag_pidl));
2819 							m_desktop_drag_startpos.x = LOWORD(lParam);
2820 							m_desktop_drag_startpos.y = HIWORD(lParam);
2821 							m_desktop_drag_curpos.x = LOWORD(lParam);
2822 							m_desktop_drag_curpos.y = HIWORD(lParam);
2823 							if (!(wParam & MK_CONTROL)) // if CTRL not held down
2824 							{
2825 								if (!p->selected)
2826 								{
2827 									DeselectDesktop();
2828 									p->selected = 1;
2829 								}
2830 							}
2831 							else
2832 							{
2833 								p->selected = 1-p->selected;
2834 							}
2835 							break;
2836 						case WM_RBUTTONDOWN:
2837 							DeselectDesktop();
2838 							p->selected = 1;
2839 							break;
2840 						}
2841 
2842 						done = 1;
2843 						break;
2844 					}
2845 				}
2846 			}
2847 
2848 			if (!done)
2849 			{
2850 				// deselect all, unless they're CTRL+clicking and missed an icon.
2851 				if (uMsg!=WM_LBUTTONDOWN || !(wParam & MK_CONTROL))
2852 					DeselectDesktop();
2853 
2854 				if (uMsg==WM_RBUTTONUP)// || uMsg==WM_RBUTTONDOWN)
2855 				{
2856 					// note: can't use GetMenu and TrackPopupMenu here because the hwnd param to TrackPopupMenu must belong to current application.
2857 
2858 					// (before sending coords to desktop window, xform them into its client coords:)
2859 					POINT pt;
2860 					pt.x = LOWORD(lParam);
2861 					pt.y = HIWORD(lParam);
2862 					ScreenToClient(m_hWndDesktopListView, &pt);
2863 					lParam = MAKELPARAM(pt.x + m_lpDX->m_monitor_rect.left, pt.y + m_lpDX->m_monitor_rect.top);
2864 
2865 					PostMessage(m_hWndDesktopListView, uMsg, wParam, lParam);
2866 					//PostMessage(m_hWndDesktopListView, WM_CONTEXTMENU, (WPARAM)m_hWndDesktopListView, lParam);
2867 				}
2868 				else if (uMsg==WM_LBUTTONDOWN)
2869 				{
2870 					m_desktop_box = 1;
2871 					m_desktop_drag_startpos.x = LOWORD(lParam);
2872 					m_desktop_drag_startpos.y = HIWORD(lParam);
2873 					m_desktop_drag_curpos.x = LOWORD(lParam);
2874 					m_desktop_drag_curpos.y = HIWORD(lParam);
2875 				}
2876 			}
2877 
2878 			// repaint window manually, if winamp is paused
2879 			if (SendMessage(m_hWndWinamp,WM_USER,0,104) != 1)
2880 			{
2881 				PushWindowToJustBeforeDesktop(GetPluginWindow());
2882 				DrawAndDisplay(1);
2883 			}
2884 
2885 			//return 0;
2886 		}
2887 		break;
2888 
2889 	case WM_SETFOCUS:
2890 		// note: this msg never comes in when embedwnd is used, but that's ok, because that's only
2891 		// in Windowed mode, and m_lost_focus only makes us sleep when fullscreen.
2892 		m_lost_focus = 0;
2893 		break;
2894 
2895 	case WM_KILLFOCUS:
2896 		// note: this msg never comes in when embedwnd is used, but that's ok, because that's only
2897 		// in Windowed mode, and m_lost_focus only makes us sleep when fullscreen.
2898 		m_lost_focus = 1;
2899 		break;
2900 
2901 	case WM_SETCURSOR:
2902 		if (
2903 		  (m_screenmode == FULLSCREEN) ||
2904 		  (m_screenmode == FAKE_FULLSCREEN && m_lpDX->m_fake_fs_covers_all)
2905 		)
2906 		{
2907 			// hide the cursor
2908 			SetCursor(NULL);
2909 			return TRUE; // prevent Windows from setting cursor to window class cursor
2910 		}
2911 		break;
2912 
2913 	case WM_NCHITTEST:
2914 		// Prevent the user from selecting the menu in fullscreen mode
2915 		if (m_screenmode != WINDOWED)
2916 			return HTCLIENT;
2917 		break;
2918 
2919 	case WM_SYSCOMMAND:
2920 		// Prevent *moving/sizing* and *entering standby mode* when in fullscreen mode
2921 		switch (wParam)
2922 		{
2923 		case SC_MOVE:
2924 		case SC_SIZE:
2925 		case SC_MAXIMIZE:
2926 		case SC_KEYMENU:
2927 			if (m_screenmode != WINDOWED)
2928 				return 1;
2929 			break;
2930 		case SC_MONITORPOWER:
2931 			if (m_screenmode == FULLSCREEN || m_screenmode == FAKE_FULLSCREEN)
2932 				return 1;
2933 			break;
2934 		}
2935 		break;
2936 
2937 	case WM_CONTEXTMENU:
2938 		// launch popup context menu.  see handler for WM_COMMAND also.
2939 		if (m_screenmode == DESKTOP)
2940 		{
2941 			// note: execution should never reach this point,
2942 			// because we don't pass WM_RBUTTONUP to DefWindowProc
2943 			// when in desktop mode!
2944 			return 0;
2945 		}
2946 		else if (m_screenmode == WINDOWED)    // context menus only allowed in ~windowed modes
2947 		{
2948 			TrackPopupMenuEx(m_context_menu, TPM_VERTICAL, LOWORD(lParam), HIWORD(lParam), hWnd, NULL);
2949 			return 0;
2950 		}
2951 		break;
2952 
2953 	case WM_COMMAND:
2954 		// handle clicks on items on context menu.
2955 		if (m_screenmode == WINDOWED)
2956 		{
2957 			switch (LOWORD(wParam))
2958 			{
2959 			case ID_QUIT:
2960 				m_exiting = 1;
2961 				PostMessage(hWnd, WM_CLOSE, 0, 0);
2962 				return 0;
2963 			case ID_GO_FS:
2964 				if (m_frame > 0)
2965 					ToggleFullScreen();
2966 				return 0;
2967 			case ID_DESKTOP_MODE:
2968 				if (m_frame > 0)
2969 					ToggleDesktop();
2970 				return 0;
2971 			case ID_SHOWHELP:
2972 				ToggleHelp();
2973 				return 0;
2974 			case ID_SHOWPLAYLIST:
2975 				TogglePlaylist();
2976 				return 0;
2977 			}
2978 			// then allow the plugin to override any command:
2979 			if (MyWindowProc(hWnd, uMsg, wParam, lParam) == 0)
2980 				return 0;
2981 		}
2982 		break;
2983 
2984 		/*
2985 		KEY HANDLING: the basic idea:
2986 		    -in all cases, handle or capture:
2987 		        -ZXCVBRS, zxcvbrs
2988 		            -also make sure it's case-insensitive!  (lowercase come through only as WM_CHAR; uppercase come in as both)
2989 		        -(ALT+ENTER)
2990 		        -(F1, ESC, UP, DN, Left, Right, SHIFT+l/r)
2991 		        -(P for playlist)
2992 		            -when playlist showing: steal J, HOME, END, PGUP, PGDN, UP, DOWN, ESC
2993 		        -(BLOCK J, L)
2994 		    -when integrated with winamp (using embedwnd), also handle these keys:
2995 		        -j, l, L, CTRL+L [windowed mode only!]
2996 		        -CTRL+P, CTRL+D
2997 		        -CTRL+TAB
2998 		        -ALT-E
2999 		        -ALT+F (main menu)
3000 		        -ALT+3 (id3)
3001 		*/
3002 
3003 	case WM_SYSKEYDOWN:
3004 		if (wParam==VK_RETURN && m_frame > 0)
3005 		{
3006 			ToggleFullScreen();
3007 			return 0;
3008 		}
3009 		// if in embedded mode (using winamp skin), pass ALT+ keys on to winamp
3010 		// ex: ALT+E, ALT+F, ALT+3...
3011 		if (m_screenmode==WINDOWED && m_lpDX->m_current_mode.m_skin)
3012 			return PostMessage(m_hWndWinamp, uMsg, wParam, lParam); // force-pass to winamp; required for embedwnd
3013 		break;
3014 
3015 	case WM_SYSKEYUP:
3016 		if (m_screenmode==WINDOWED && m_lpDX->m_current_mode.m_skin)
3017 			return PostMessage(m_hWndWinamp, uMsg, wParam, lParam); // force-pass to winamp; required for embedwnd
3018 		break;
3019 
3020 	case WM_SYSCHAR:
3021 		if ((wParam=='k' || wParam=='K'))
3022 		{
3023 			OnAltK();
3024 			return 0;
3025 		}
3026 		if ((wParam=='d' || wParam=='D') && m_frame > 0)
3027 		{
3028 			ToggleDesktop();
3029 			return 0;
3030 		}
3031 		break;
3032 
3033 	case WM_CHAR:
3034 		// if playlist is showing, steal p/j keys from the plugin:
3035 		if (m_show_playlist)
3036 		{
3037 			switch (wParam)
3038 			{
3039 			case 'j':
3040 			case 'J':
3041 				m_playlist_pos = SendMessage(m_hWndWinamp,WM_USER, 0, 125);
3042 				return 0;
3043 			default:
3044 			{
3045 				int nSongs = SendMessage(m_hWndWinamp,WM_USER, 0, 124);
3046 				int found = 0;
3047 				int orig_pos = m_playlist_pos;
3048 				int inc = (wParam>='A' && wParam<='Z') ? -1 : 1;
3049 				while (1)
3050 				{
3051 					if (inc==1 && m_playlist_pos >= nSongs-1)
3052 						break;
3053 					if (inc==-1 && m_playlist_pos <= 0)
3054 						break;
3055 
3056 					m_playlist_pos += inc;
3057 
3058 					char buf[32];
3059 					strncpy(buf, (char*)SendMessage(m_hWndWinamp, WM_USER, m_playlist_pos, 212), 31);
3060 					buf[31] = 0;
3061 
3062 					// remove song # and period from beginning
3063 					char *p = buf;
3064 					while (*p >= '0' && *p <= '9') p++;
3065 					if (*p == '.' && *(p+1) == ' ')
3066 					{
3067 						p += 2;
3068 						int pos = 0;
3069 						while (*p != 0)
3070 						{
3071 							buf[pos++] = *p;
3072 							p++;
3073 						}
3074 						buf[pos++] = 0;
3075 					}
3076 
3077 					int wParam2 = (wParam>='A' && wParam<='Z') ? (wParam + 'a'-'A') : (wParam + 'A'-'a');
3078 					if (buf[0]==wParam || buf[0]==wParam2)
3079 					{
3080 						found = 1;
3081 						break;
3082 					}
3083 				}
3084 
3085 				if (!found)
3086 					m_playlist_pos = orig_pos;
3087 			}
3088 			return 0;
3089 			}
3090 		}
3091 
3092 		// then allow the plugin to override any keys:
3093 		if (MyWindowProc(hWnd, uMsg, wParam, lParam) == 0)
3094 			return 0;
3095 
3096 		// finally, default key actions:
3097 		if (wParam == keyMappings[5] || wParam == keyMappings[6])	// 'z' or 'Z'
3098 		{
3099 			PostMessage(m_hWndWinamp,WM_COMMAND,40044,0);
3100 			return 0;
3101 		}
3102 		else
3103 			{
3104 			switch (wParam)
3105 			{
3106 				// WINAMP PLAYBACK CONTROL KEYS:
3107 			case 'x':
3108 			case 'X':
3109 				PostMessage(m_hWndWinamp,WM_COMMAND,40045,0);
3110 				return 0;
3111 			case 'c':
3112 			case 'C':
3113 				PostMessage(m_hWndWinamp,WM_COMMAND,40046,0);
3114 				return 0;
3115 			case 'v':
3116 			case 'V':
3117 				PostMessage(m_hWndWinamp,WM_COMMAND,40047,0);
3118 				return 0;
3119 			case 'b':
3120 			case 'B':
3121 				PostMessage(m_hWndWinamp,WM_COMMAND,40048,0);
3122 				return 0;
3123 			case 's':
3124 			case 'S':
3125 				//if (SendMessage(m_hWndWinamp,WM_USER,0,250))
3126 				//    sprintf(m_szUserMessage, "shuffle is now OFF");    // shuffle was on
3127 				//else
3128 				//    sprintf(m_szUserMessage, "shuffle is now ON");    // shuffle was off
3129 
3130 				// toggle shuffle
3131 				PostMessage(m_hWndWinamp,WM_COMMAND,40023,0);
3132 				return 0;
3133 			case 'r':
3134 			case 'R':
3135 				// toggle repeat
3136 				PostMessage(m_hWndWinamp,WM_COMMAND,40022,0);
3137 				return 0;
3138 			case 'p':
3139 			case 'P':
3140 				TogglePlaylist();
3141 				return 0;
3142 			case 'l':
3143 				// note that this is actually correct; when you hit 'l' from the
3144 				// MAIN winamp window, you get an "open files" dialog; when you hit
3145 				// 'l' from the playlist editor, you get an "add files to playlist" dialog.
3146 				// (that sends IDC_PLAYLIST_ADDMP3==1032 to the playlist, which we can't
3147 				//  do from here.)
3148 				PostMessage(m_hWndWinamp,WM_COMMAND,40029,0);
3149 				return 0;
3150 			case 'L':
3151 				PostMessage(m_hWndWinamp,WM_COMMAND,40187,0);
3152 				return 0;
3153 			case 'j':
3154 				PostMessage(m_hWndWinamp,WM_COMMAND,40194,0);
3155 				return 0;
3156 			}
3157 
3158 			return 0;//DefWindowProc(hWnd,uMsg,wParam,lParam);
3159 		}
3160 		break;  // end case WM_CHAR
3161 
3162 	case WM_KEYUP:
3163 
3164 		// allow the plugin to override any keys:
3165 		if (MyWindowProc(hWnd, uMsg, wParam, lParam) == 0)
3166 			return 0;
3167 
3168 		/*
3169 		switch(wParam)
3170 		{
3171 		case VK_SOMETHING:
3172 		    ...
3173 		    break;
3174 		}
3175 		*/
3176 
3177 		return 0;
3178 		break;
3179 
3180 	case WM_KEYDOWN:
3181 		if (m_show_playlist)
3182 		{
3183 			switch (wParam)
3184 			{
3185 			case VK_ESCAPE:
3186 				if(m_show_playlist)
3187 					TogglePlaylist();
3188 				//m_show_playlist = 0;
3189 				return 0;
3190 
3191 			case VK_UP:
3192 			{
3193 				int nRepeat = lParam & 0xFFFF;
3194 				if (GetKeyState(VK_SHIFT) & mask)
3195 					m_playlist_pos -= 10*nRepeat;
3196 				else
3197 					m_playlist_pos -= nRepeat;
3198 			}
3199 			return 0;
3200 
3201 			case VK_DOWN:
3202 			{
3203 				int nRepeat = lParam & 0xFFFF;
3204 				if (GetKeyState(VK_SHIFT) & mask)
3205 					m_playlist_pos += 10*nRepeat;
3206 				else
3207 					m_playlist_pos += nRepeat;
3208 			}
3209 			return 0;
3210 
3211 			case VK_HOME:
3212 				m_playlist_pos = 0;
3213 				return 0;
3214 
3215 			case VK_END:
3216 				m_playlist_pos = SendMessage(m_hWndWinamp,WM_USER, 0, 124) - 1;
3217 				return 0;
3218 
3219 			case VK_PRIOR:
3220 				if (GetKeyState(VK_SHIFT) & mask)
3221 					m_playlist_pageups += 10;
3222 				else
3223 					m_playlist_pageups++;
3224 				return 0;
3225 
3226 			case VK_NEXT:
3227 				if (GetKeyState(VK_SHIFT) & mask)
3228 					m_playlist_pageups -= 10;
3229 				else
3230 					m_playlist_pageups--;
3231 				return 0;
3232 
3233 			case VK_RETURN:
3234 				SendMessage(m_hWndWinamp,WM_USER, m_playlist_pos, 121);	// set sel
3235 				SendMessage(m_hWndWinamp,WM_COMMAND, 40045, 0);	// play it
3236 				return 0;
3237 			}
3238 		}
3239 
3240 		// allow the plugin to override any keys:
3241 		if (MyWindowProc(hWnd, uMsg, wParam, lParam) == 0)
3242 			return 0;
3243 
3244 		switch (wParam)
3245 		{
3246 		case VK_F1:
3247 			m_show_press_f1_msg = 0;
3248 			ToggleHelp();
3249 			return 0;
3250 
3251 		case VK_ESCAPE:
3252 			if (m_show_help)
3253 				ToggleHelp();
3254 			else
3255 			{
3256 				if (m_screenmode == FAKE_FULLSCREEN || m_screenmode == FULLSCREEN)
3257 				{
3258 					ToggleFullScreen();
3259 				}
3260 				else if (m_screenmode == DESKTOP)
3261 				{
3262 					ToggleDesktop();
3263 				}
3264 				// exit the program on escape
3265 				//m_exiting = 1;
3266 				//PostMessage(hWnd, WM_CLOSE, 0, 0);
3267 			}
3268 			return 0;
3269 
3270 		case VK_UP:
3271 			// increase volume
3272 		{
3273 			int nRepeat = lParam & 0xFFFF;
3274 			for (i=0; i<nRepeat*2; i++) PostMessage(m_hWndWinamp,WM_COMMAND,40058,0);
3275 		}
3276 		return 0;
3277 
3278 		case VK_DOWN:
3279 			// decrease volume
3280 		{
3281 			int nRepeat = lParam & 0xFFFF;
3282 			for (i=0; i<nRepeat*2; i++) PostMessage(m_hWndWinamp,WM_COMMAND,40059,0);
3283 		}
3284 		return 0;
3285 
3286 		case VK_LEFT:
3287 		case VK_RIGHT:
3288 		{
3289 			bool bShiftHeldDown = (GetKeyState(VK_SHIFT) & mask) != 0;
3290 			int cmd = (wParam == VK_LEFT) ? 40144 : 40148;
3291 			int nRepeat = lParam & 0xFFFF;
3292 			int reps = (bShiftHeldDown) ? 6*nRepeat : 1*nRepeat;
3293 
3294 			for (int i=0; i<reps; i++)
3295 				PostMessage(m_hWndWinamp,WM_COMMAND,cmd,0);
3296 		}
3297 		return 0;
3298 		default:
3299 			// pass CTRL+A thru CTRL+Z, and also CTRL+TAB, to winamp, *if we're in windowed mode* and using an embedded window.
3300 			// be careful though; uppercase chars come both here AND to WM_CHAR handler,
3301 			//   so we have to eat some of them here, to avoid them from acting twice.
3302 			if (m_screenmode==WINDOWED && m_lpDX && m_lpDX->m_current_mode.m_skin)
3303 			{
3304 				if (bCtrlHeldDown && ((wParam >= 'A' && wParam <= 'Z') || wParam==VK_TAB))
3305 				{
3306 					PostMessage(m_hWndWinamp, uMsg, wParam, lParam);
3307 					return 0;
3308 				}
3309 			}
3310 			return 0;
3311 		}
3312 
3313 		return 0;
3314 		break;
3315 	}
3316 
3317 	return MyWindowProc(hWnd, uMsg, wParam, lParam);//DefWindowProc(hWnd, uMsg, wParam, lParam);
3318 	//return 0L;
3319 }
3320 
DesktopWndProc(HWND hWnd,unsigned uMsg,WPARAM wParam,LPARAM lParam)3321 LRESULT CALLBACK CPluginShell::DesktopWndProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam)
3322 {
3323     CPluginShell* p = (CPluginShell*)GetWindowLongPtr(hWnd,GWLP_USERDATA);
3324 	if (p)
3325 		return p->PluginShellDesktopWndProc(hWnd, uMsg, wParam, lParam);
3326 	else
3327 		return DefWindowProc(hWnd, uMsg, wParam, lParam);
3328 }
3329 
PluginShellDesktopWndProc(HWND hWnd,unsigned uMsg,WPARAM wParam,LPARAM lParam)3330 LRESULT CPluginShell::PluginShellDesktopWndProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam)
3331 {
3332 	//#ifdef _DEBUG
3333 	//    OutputDebugMessage("kbfocus", hWnd, uMsg, wParam, lParam);
3334 	//#endif
3335 
3336 	switch (uMsg)
3337 	{
3338 	case WM_KEYDOWN:
3339 	case WM_KEYUP:
3340 	case WM_CHAR:
3341 	case WM_SYSCHAR:
3342 	case WM_SYSKEYDOWN:
3343 	case WM_SYSKEYUP:
3344 		//PostMessage(GetPluginWindow(), uMsg, wParam, lParam);
3345 		PluginShellWindowProc(GetPluginWindow(), uMsg, wParam, lParam);
3346 		return 0;
3347 		break;
3348 	}
3349 
3350 	return DefWindowProc(hWnd, uMsg, wParam, lParam);
3351 }
3352 
AlignWaves()3353 void CPluginShell::AlignWaves()
3354 {
3355 	// align waves, using recursive (mipmap-style) least-error matching
3356 	// note: NUM_WAVEFORM_SAMPLES must be between 32 and 576.
3357 
3358 	int align_offset[2] = { 0, 0 };
3359 
3360 #if (NUM_WAVEFORM_SAMPLES < 576) // [don't let this code bloat our DLL size if it's not going to be used]
3361 
3362 	int nSamples = NUM_WAVEFORM_SAMPLES;
3363 
3364 #define MAX_OCTAVES 10
3365 
3366 	int octaves = (int)floorf(logf((float)(576-nSamples))/logf(2.0f));
3367 	if (octaves < 4)
3368 		return;
3369 	if (octaves > MAX_OCTAVES)
3370 		octaves = MAX_OCTAVES;
3371 
3372 	for (int ch=0; ch<2; ch++)
3373 	{
3374 		// only worry about matching the lower 'nSamples' samples
3375 		float temp_new[MAX_OCTAVES][576];
3376 		float temp_old[MAX_OCTAVES][576];
3377 		static float temp_weight[MAX_OCTAVES][576];
3378 		static int   first_nonzero_weight[MAX_OCTAVES];
3379 		static int   last_nonzero_weight[MAX_OCTAVES];
3380 		int spls[MAX_OCTAVES];
3381 		int space[MAX_OCTAVES];
3382 
3383 		memcpy(temp_new[0], m_sound.fWaveform[ch], sizeof(float)*576);
3384 		memcpy(temp_old[0], &m_oldwave[ch][m_prev_align_offset[ch]], sizeof(float)*nSamples);
3385 		spls[0] = 576;
3386 		space[0] = 576 - nSamples;
3387 
3388 		// potential optimization: could reuse (instead of recompute) mip levels for m_oldwave[2][]?
3389 		for (int octave=1; octave<octaves; octave++)
3390 		{
3391 			spls[octave] = spls[octave-1]/2;
3392 			space[octave] = space[octave-1]/2;
3393 			for (int n=0; n<spls[octave]; n++)
3394 			{
3395 				temp_new[octave][n] = 0.5f*(temp_new[octave-1][n*2] + temp_new[octave-1][n*2+1]);
3396 				temp_old[octave][n] = 0.5f*(temp_old[octave-1][n*2] + temp_old[octave-1][n*2+1]);
3397 			}
3398 		}
3399 
3400 		if (!m_align_weights_ready)
3401 		{
3402 			m_align_weights_ready = 1;
3403 			for (octave=0; octave<octaves; octave++)
3404 			{
3405 				int compare_samples = spls[octave] - space[octave];
3406 				for (int n=0; n<compare_samples; n++)
3407 				{
3408 					// start with pyramid-shaped pdf, from 0..1..0
3409 					if (n < compare_samples/2)
3410 						temp_weight[octave][n] = n*2/(float)compare_samples;
3411 					else
3412 						temp_weight[octave][n] = (compare_samples-1 - n)*2/(float)compare_samples;
3413 
3414 					// TWEAK how much the center matters, vs. the edges:
3415 					temp_weight[octave][n] = (temp_weight[octave][n] - 0.8f)*5.0f + 0.8f;
3416 
3417 					// clip:
3418 					if (temp_weight[octave][n]>1) temp_weight[octave][n] = 1;
3419 					if (temp_weight[octave][n]<0) temp_weight[octave][n] = 0;
3420 				}
3421 
3422 				n = 0;
3423 				while (temp_weight[octave][n] == 0 && n < compare_samples)
3424 					n++;
3425 				first_nonzero_weight[octave] = n;
3426 
3427 				n = compare_samples-1;
3428 				while (temp_weight[octave][n] == 0 && n >= 0)
3429 					n--;
3430 				last_nonzero_weight[octave] = n;
3431 			}
3432 		}
3433 
3434 		int n1 = 0;
3435 		int n2 = space[octaves-1];
3436 		for (octave = octaves-1; octave>=0; octave--)
3437 		{
3438 			// for example:
3439 			//  space[octave] == 4
3440 			//  spls[octave] == 36
3441 			//  (so we test 32 samples, w/4 offsets)
3442 			int compare_samples = spls[octave]-space[octave];
3443 
3444 			int lowest_err_offset = -1;
3445 			float lowest_err_amount = 0;
3446 			for (int n=n1; n<n2; n++)
3447 			{
3448 				float err_sum = 0;
3449 				//for (int i=0; i<compare_samples; i++)
3450 				for (int i=first_nonzero_weight[octave]; i<=last_nonzero_weight[octave]; i++)
3451 				{
3452 					float x = (temp_new[octave][i+n] - temp_old[octave][i]) * temp_weight[octave][i];
3453 					if (x>0)
3454 						err_sum += x;
3455 					else
3456 						err_sum -= x;
3457 				}
3458 
3459 				if (lowest_err_offset == -1 || err_sum < lowest_err_amount)
3460 				{
3461 					lowest_err_offset = n;
3462 					lowest_err_amount = err_sum;
3463 				}
3464 			}
3465 
3466 			// now use 'lowest_err_offset' to guide bounds of search in next octave:
3467 			//  space[octave] == 8
3468 			//  spls[octave] == 72
3469 			//     -say 'lowest_err_offset' was 2
3470 			//     -that corresponds to samples 4 & 5 of the next octave
3471 			//     -also, expand about this by 2 samples?  YES.
3472 			//  (so we'd test 64 samples, w/8->4 offsets)
3473 			if (octave > 0)
3474 			{
3475 				n1 = lowest_err_offset*2  -1;
3476 				n2 = lowest_err_offset*2+2+1;
3477 				if (n1 < 0) n1=0;
3478 				if (n2 > space[octave-1]) n2 = space[octave-1];
3479 			}
3480 			else
3481 				align_offset[ch] = lowest_err_offset;
3482 		}
3483 	}
3484 #endif
3485 	memcpy(m_oldwave[0], m_sound.fWaveform[0], sizeof(float)*576);
3486 	memcpy(m_oldwave[1], m_sound.fWaveform[1], sizeof(float)*576);
3487 	m_prev_align_offset[0] = align_offset[0];
3488 	m_prev_align_offset[1] = align_offset[1];
3489 
3490 	// finally, apply the results: modify m_sound.fWaveform[2][0..576]
3491 	// by scooting the aligned samples so that they start at m_sound.fWaveform[2][0].
3492 	for (ch=0; ch<2; ch++)
3493 		if (align_offset[ch]>0)
3494 		{
3495 			for (int i=0; i<nSamples; i++)
3496 				m_sound.fWaveform[ch][i] = m_sound.fWaveform[ch][i+align_offset[ch]];
3497 			// zero the rest out, so it's visually evident that these samples are now bogus:
3498 			memset(&m_sound.fWaveform[ch][nSamples], 0, (576-nSamples)*sizeof(float));
3499 		}
3500 }
3501 
VJModeWndProc(HWND hWnd,unsigned uMsg,WPARAM wParam,LPARAM lParam)3502 LRESULT CALLBACK CPluginShell::VJModeWndProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam)
3503 {
3504   CPluginShell* p = (CPluginShell*)GetWindowLongPtr(hWnd,GWLP_USERDATA);
3505 	if (p)
3506 		return p->PluginShellVJModeWndProc(hWnd, uMsg, wParam, lParam);
3507 	else
3508 		return DefWindowProc(hWnd, uMsg, wParam, lParam);
3509 }
3510 
PluginShellVJModeWndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)3511 LRESULT CPluginShell::PluginShellVJModeWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
3512 {
3513 #ifdef _DEBUG
3514 	if (message != WM_MOUSEMOVE &&
3515 	    message != WM_NCHITTEST &&
3516 	    message != WM_SETCURSOR &&
3517 	    message != WM_COPYDATA &&
3518 	    message != WM_USER)
3519 	{
3520 		char caption[256] = "VJWndProc: frame 0, ";
3521 		if (m_frame > 0)
3522 		{
3523 			float time = m_time;
3524 			int hours = (int)(time/3600);
3525 			time -= hours*3600;
3526 			int minutes = (int)(time/60);
3527 			time -= minutes*60;
3528 			int seconds = (int)time;
3529 			time -= seconds;
3530 			int dsec = (int)(time*100);
3531 			sprintf(caption, "VJWndProc: frame %d, t=%dh:%02dm:%02d.%02ds, ", m_frame, hours, minutes, seconds, dsec);
3532 		}
3533 		OutputDebugMessage(caption, hwnd, message, wParam, lParam);
3534 	}
3535 #endif
3536 
3537 	switch (message)
3538 	{
3539 	case WM_KEYDOWN:
3540 	case WM_KEYUP:
3541 	case WM_CHAR:
3542 	case WM_SYSKEYDOWN:
3543 	case WM_SYSKEYUP:
3544 	case WM_SYSCHAR:
3545 		// pass keystrokes on to plugin!
3546 		return PluginShellWindowProc(GetPluginWindow(),message,wParam,lParam);
3547 
3548 	case WM_ERASEBKGND:
3549 		// Repaint window when song is paused and image needs to be repainted:
3550 		if (SendMessage(m_hWndWinamp,WM_USER,0,104)!=1 && m_vjd3d9_device && GetFrame() > 0)    // WM_USER/104 return codes: 1=playing, 3=paused, other=stopped
3551 		{
3552 			m_vjd3d9_device->Present(NULL,NULL,NULL,NULL);
3553 			return 0;
3554 		}
3555 		break;
3556 
3557 		/*
3558 		case WM_WINDOWPOSCHANGING:
3559 		if (m_screenmode == DESKTOP)
3560 		{
3561 		    LPWINDOWPOS pwp = (LPWINDOWPOS)lParam;
3562 		    if (pwp)
3563 		        pwp->flags |= SWP_NOOWNERZORDER | SWP_NOZORDER;
3564 		}
3565 		break;
3566 
3567 		case WM_ACTIVATEAPP:
3568 		// *Very Important Handler!*
3569 		//    -Without this code, the app would not work properly when running in true
3570 		//     fullscreen mode on multiple monitors; it would auto-minimize whenever the
3571 		//     user clicked on a window in another display.
3572 		if (wParam == 1 &&
3573 		    m_screenmode == DESKTOP &&
3574 		    m_frame > 0 &&
3575 		    !m_exiting
3576 		   )
3577 		{
3578 		    return 0;
3579 		}
3580 		break;
3581 
3582 		/*
3583 		case WM_NCACTIVATE:
3584 		// *Very Important Handler!*
3585 		//    -Without this code, the app would not work properly when running in true
3586 		//     fullscreen mode on multiple monitors; it would auto-minimize whenever the
3587 		//     user clicked on a window in another display.
3588 		// (NOTE: main window also handles this message this way)
3589 		if (wParam == 0 &&
3590 		    m_screenmode == FULLSCREEN &&
3591 		    m_frame > 0 &&
3592 		    !m_exiting &&
3593 		    m_lpDX &&
3594 		    m_lpDX->m_ready
3595 		    && m_lpDX->m_lpD3D &&
3596 		    m_lpDX->m_lpD3D->GetAdapterCount() > 1
3597 		    )
3598 		{
3599 		    return 0;
3600 		}
3601 		break;
3602 		*/
3603 
3604 		/*
3605 		case WM_ACTIVATEAPP:
3606 		if (wParam == 1 &&
3607 		    m_screenmode == DESKTOP &&
3608 		    m_frame > 0 &&
3609 		    !m_exiting &&
3610 		    m_vjd3d9_device
3611 		   )
3612 		{
3613 		    return 0;
3614 		}
3615 		break;
3616 		*/
3617 
3618 		/*
3619 		case WM_WINDOWPOSCHANGING:
3620 		if (
3621 		    m_screenmode == DESKTOP
3622 		    && (!m_force_accept_WM_WINDOWPOSCHANGING)
3623 		    && m_lpDX && m_lpDX->m_ready
3624 		   )
3625 		{
3626 		    // unless we requested it ourselves or it's init time,
3627 		    // prevent the fake desktop window from moving around
3628 		    // in the Z order!  (i.e., keep it on the bottom)
3629 
3630 		    // without this code, when you click on the 'real' desktop
3631 		    // in a multimon setup, any windows that are overtop of the
3632 		    // 'fake' desktop will flash, since they'll be covered
3633 		    // up by the fake desktop window (but then shown again on
3634 		    // the next frame, when we detect that the fake desktop
3635 		    // window isn't on bottom & send it back to the bottom).
3636 
3637 		    LPWINDOWPOS pwp = (LPWINDOWPOS)lParam;
3638 		    if (pwp)
3639 		        pwp->flags |= SWP_NOOWNERZORDER | SWP_NOZORDER;
3640 		}
3641 		break;
3642 		*/
3643 
3644 	case WM_CLOSE:
3645 		// if they close the VJ window (by some means other than ESC key),
3646 		// this will make the graphics window close, too.
3647 		m_exiting = 1;
3648 		if (GetPluginWindow())
3649 			PostMessage(GetPluginWindow(), WM_CLOSE, 0, 0);
3650 		break;
3651 
3652 	case WM_GETMINMAXINFO:
3653 	{
3654 		// don't let the window get too small
3655 		MINMAXINFO* p = (MINMAXINFO*)lParam;
3656 		if (p->ptMinTrackSize.x < 64)
3657 			p->ptMinTrackSize.x = 64;
3658 		p->ptMinTrackSize.y = p->ptMinTrackSize.x*3/4;
3659 	}
3660 	return 0;
3661 
3662 	case WM_SIZE:
3663 		// clear or set activity flag to reflect focus
3664 		if (m_vjd3d9_device && !m_resizing_textwnd)
3665 		{
3666 			m_hidden_textwnd = (SIZE_MAXHIDE==wParam || SIZE_MINIMIZED==wParam) ? TRUE : FALSE;
3667 
3668 			if (SIZE_MAXIMIZED==wParam || SIZE_RESTORED==wParam) // the window has been maximized or restored
3669 				OnUserResizeTextWindow();
3670 		}
3671 		break;
3672 
3673 	case WM_ENTERSIZEMOVE:
3674 		m_resizing_textwnd = 1;
3675 		break;
3676 
3677 	case WM_EXITSIZEMOVE:
3678 		if (m_vjd3d9_device)
3679 			OnUserResizeTextWindow();
3680 		m_resizing_textwnd = 0;
3681 		break;
3682 	}
3683 
3684 	return DefWindowProc(hwnd, message, wParam, lParam);
3685 }