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 #include "api.h"
31 #include "DXContext.h"
32 #include "utility.h"
33 #include "shell_defines.h"
34 #include "resource.h"
35 #define COMPILE_MULTIMON_STUBS 1
36 #include <multimon.h>
37 #include <strsafe.h>
38 
39 // note: added WS_EX_CONTROLPARENT and WS_TABSTOP for embedwnd so window frame will pass on KB commands to us, if it has focus & receives them.
40 //       however, it is still not working.  Maksim says he needs to use GetNextDlgTabItem() and then it will work.
41 //       aha- had to remove WS_EX_CONTROLPARENT and WS_OVERLAPPED.  Should now work with winamp 5.5 build 1620.
42 #define MY_EXT_WINDOW_STYLE (m_current_mode.m_skin ? 0/*WS_EX_CONTROLPARENT*/ : ((m_current_mode.screenmode==DESKTOP) ? (WS_EX_TOOLWINDOW) : 0)) // note: changed from TOOLWINDOW to APPWINDOW b/c we wanted the plugin to appear in the taskbar.
43 #define SKINNED_WS (WS_VISIBLE|WS_CHILDWINDOW/*|WS_OVERLAPPED*/|WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_TABSTOP)
44 #define MY_WINDOW_STYLE (m_current_mode.m_skin ? SKINNED_WS : ((m_current_mode.screenmode==FAKE_FULLSCREEN || m_current_mode.screenmode==DESKTOP) ? WS_POPUP : WS_OVERLAPPEDWINDOW))   // note: WS_POPUP (by itself) removes all borders, captions, etc.
45 
46 #include "vis.h"
47 extern winampVisModule mod1;
48 
DXContext(HWND hWndWinamp,HINSTANCE hInstance,LPCWSTR szClassName,LPCSTR szWindowCaption,WNDPROC pProc,LONG_PTR uWindowLong,int minimize_winamp,wchar_t * szIniFile)49 DXContext::DXContext(HWND hWndWinamp,HINSTANCE hInstance,LPCWSTR szClassName,LPCSTR szWindowCaption,WNDPROC pProc,LONG_PTR uWindowLong, int minimize_winamp, wchar_t* szIniFile)
50 {
51 	m_classAtom = 0;
52 	m_szWindowCaption[0] = 0;
53 	m_hwnd = NULL;
54 	m_lpD3D = NULL;
55 	m_lpDevice = NULL;
56 	m_hmod_d3d9 = NULL;
57 	m_hmod_d3dx9 = NULL;
58 	m_zFormat = D3DFMT_UNKNOWN;
59 	for (int i=0; i<MAX_DXC_ADAPTERS; i++)
60 		m_orig_windowed_mode_format[i] = D3DFMT_UNKNOWN;
61 	m_ordinal_adapter = D3DADAPTER_DEFAULT;
62 	m_ignore_wm_destroy = 0;
63 	m_hwnd_winamp = hWndWinamp;
64 	m_minimize_winamp = minimize_winamp;
65 	m_winamp_minimized = 0;
66 	m_truly_exiting = 0;
67 	m_bpp = 0;
68 	m_frame_delay = 0;
69 	StringCbCopyW(m_szIniFile, sizeof(m_szIniFile), szIniFile);
70 	memset(&myWindowState,0,sizeof(myWindowState));
71 	m_szDriver[0] = 0;
72 	m_szDesc[0] = 0;
73 
74 	WNDCLASSW wc = {0};
75 
76 	// clear the error register
77 
78 	m_lastErr = S_OK;
79 
80 	// clear the active flag
81 
82 	m_ready=FALSE;
83 
84 	// Set up and register window class
85 
86 	wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; // CS_DBLCLKS lets the window receive WM_LBUTTONDBLCLK, for toggling fullscreen mode...
87 	wc.lpfnWndProc = (WNDPROC) pProc;
88 	wc.cbWndExtra = sizeof(DWORD);
89 	wc.hInstance = hInstance;
90 	wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PLUGIN_ICON));//NULL;
91 	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
92 	wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
93 	wc.lpszMenuName = NULL;
94 	wc.lpszClassName = szClassName;
95 	m_classAtom = RegisterClassW(&wc);
96 	if (!m_classAtom)
97 	{
98 		wchar_t title[64];
99 		int y = GetLastError();
100 		m_lastErr = DXC_ERR_REGWIN;
101 		MessageBoxW(m_hwnd, WASABI_API_LNGSTRINGW(IDS_UNABLE_TO_REGISTER_WINDOW_CLASS),
102 				    WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
103 				    MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
104 		Internal_CleanUp();
105 		return;
106 	}
107 
108 	StringCbCopy(m_szWindowCaption, sizeof(m_szWindowCaption), szWindowCaption);
109 	m_hInstance = hInstance;
110 	m_uWindowLong = uWindowLong;
111 }
112 
~DXContext()113 DXContext::~DXContext()
114 {
115 	Internal_CleanUp();
116 }
117 
Internal_CleanUp()118 void DXContext::Internal_CleanUp()
119 {
120 	// clear active flag
121 	m_ready=FALSE;
122 
123 	// release 3D interfaces
124 	SafeRelease(m_lpDevice);
125 	SafeRelease(m_lpD3D);
126 
127 	// destroy the window
128 	if (m_truly_exiting)
129 	{
130 		// somebody else will destroy the window for us!
131 		m_hwnd = NULL;
132 		if (m_hmod_d3d9)
133 		{
134 			FreeLibrary(m_hmod_d3d9);
135 			m_hmod_d3d9 = NULL;
136 		}
137 
138 		if (m_hmod_d3dx9)
139 		{
140 			m_hmod_d3dx9 = NULL;
141 		}
142 	}
143 
144 	if (myWindowState.me)
145 	{
146 		DestroyWindow(myWindowState.me);
147 		myWindowState.me = NULL;
148 		m_hwnd = NULL;
149 	}
150 	else if (m_hwnd)
151 	{
152 		DestroyWindow(m_hwnd);
153 		m_hwnd = NULL;
154 	}
155 
156 	// unregister window class.  note: only works if window is already destroyed!
157 	if (m_classAtom)
158 	{
159 		UnregisterClass(MAKEINTATOM(m_classAtom), m_hInstance);
160 		m_classAtom = 0;
161 	}
162 
163 	RestoreWinamp();
164 }
165 
GetSnappedClientSize()166 void DXContext::GetSnappedClientSize()
167 {
168 	// Call this whenever you set m_REAL_client_width/height while in windowed mode,
169 	// to compute an appropriate (oversized) internal canvas size.  At the end of each
170 	// frame, for display, the canvas will be centered & cropped.
171 	m_client_width  = m_REAL_client_width;
172 	m_client_height = m_REAL_client_height;
173 #if (SNAP_WINDOWED_MODE_BLOCKSIZE >= 1)
174 	if (m_current_mode.screenmode == WINDOWED)
175 	{
176 		// oversize it - then we'll just crop - so onscreen text has no stretching  :)
177 		m_client_width  = max(1, (m_REAL_client_width  + 31)/32)*32;
178 		m_client_height = max(1, (m_REAL_client_height + 31)/32)*32;
179 	}
180 #endif
181 }
182 
TestFormat(int ordinal_adapter,D3DFORMAT fmt)183 BOOL DXContext::TestFormat(int ordinal_adapter, D3DFORMAT fmt)
184 {
185 	if (D3D_OK==m_lpD3D->CheckDeviceType(ordinal_adapter,D3DDEVTYPE_HAL,fmt,fmt,FALSE))
186 		return TRUE;
187 	return FALSE;
188 }
189 
TestDepth(int ordinal_adapter,D3DFORMAT fmt)190 BOOL DXContext::TestDepth(int ordinal_adapter, D3DFORMAT fmt)
191 {
192 	if (D3D_OK!=m_lpD3D->CheckDeviceFormat(ordinal_adapter,D3DDEVTYPE_HAL,m_current_mode.display_mode.Format,
193 	                                       D3DUSAGE_DEPTHSTENCIL,D3DRTYPE_SURFACE,fmt))
194 		return FALSE;
195 	if (D3D_OK!=m_lpD3D->CheckDepthStencilMatch(ordinal_adapter,D3DDEVTYPE_HAL,
196 	    m_current_mode.display_mode.Format,m_current_mode.display_mode.Format,fmt))
197 		return FALSE;
198 	return TRUE;
199 }
200 
CheckAndCorrectFullscreenDispMode(int ordinal_adapter,D3DDISPLAYMODE * pdm)201 int DXContext::CheckAndCorrectFullscreenDispMode(int ordinal_adapter, D3DDISPLAYMODE *pdm)
202 {
203 	// given the user's choice of fullscreen display mode,
204 	// go through all the display modes available to the currently-selected adapter
205 	// and find the best match.
206 
207 	// returns 1 if it altered pdm to the best match,
208 	// or 0 if it was able to find a perfect match.
209 
210 	// if it returns 1, you might want to notify the user.
211 
212 
213 #define MAX_DISPLAY_MODES 4096
214 	D3DDISPLAYMODE list[MAX_DISPLAY_MODES];
215 	int nCount = min(m_lpD3D->GetAdapterModeCount(ordinal_adapter, D3DFMT_A8R8G8B8), MAX_DISPLAY_MODES);
216 	int nValid = 0;
217 	for (int i=0; i<nCount; i++)
218 		if (m_lpD3D->EnumAdapterModes(ordinal_adapter, D3DFMT_A8R8G8B8, i, &list[nValid]) == D3D_OK)
219 			nValid++;
220 
221 	// do many passes through the set until we find a match,
222 	// each time relaxing more constraints.
223 	// outline of the passes:
224 
225 	int bpp_desired = 0;
226 	switch (pdm->Format)
227 	{
228 	case D3DFMT_R8G8B8  : bpp_desired = 32; break;
229 	case D3DFMT_A8R8G8B8: bpp_desired = 32; break;
230 	case D3DFMT_X8R8G8B8: bpp_desired = 32; break;
231 	case D3DFMT_R5G6B5  : bpp_desired = 16; break;
232 	case D3DFMT_X1R5G5B5: bpp_desired = 16; break;
233 	case D3DFMT_A1R5G5B5: bpp_desired = 16; break;
234 	case D3DFMT_A4R4G4B4: bpp_desired = 16; break;
235 	case D3DFMT_R3G3B2  : bpp_desired =  8; break;
236 	case D3DFMT_A8R3G3B2: bpp_desired = 16; break;
237 	case D3DFMT_X4R4G4B4: bpp_desired = 16; break;
238 	}
239 
240 	// rep   MATCH:
241 	//  0. w,h,r,f
242 	//  1. w,h,-,f
243 	//  2. w,h,r,-         pass:
244 	//  3. w,h,-,-           -on pass 0, for 'f', match exact format
245 	//  4. 8,6,r,f           -on pass 1, for 'f', just match # of bits per pixel
246 	//  5. 8,6,-,f              (more relaxed match)
247 	//  6. 8,6,r,-
248 	//  7. 8,6,-,-
249 	//  8. -,-,r,f
250 	//  9. -,-,-,f
251 	// 10. -,-,r,-
252 	// 11. -,-,-,-
253 	int found = 0;
254 	for (int rep=0; rep<12 && !found; rep++)
255 	{
256 		for (int pass=0; pass<2 && !found; pass++)
257 		{
258 			for (i=0; i<nValid && !found; i++)
259 			{
260 				bool bMatch = true;
261 
262 				int bpp_this_mode = 0;
263 				switch (list[i].Format)
264 				{
265 				case D3DFMT_R8G8B8  : bpp_this_mode = 32; break;
266 				case D3DFMT_A8R8G8B8: bpp_this_mode = 32; break;
267 				case D3DFMT_X8R8G8B8: bpp_this_mode = 32; break;
268 				case D3DFMT_R5G6B5  : bpp_this_mode = 16; break;
269 				case D3DFMT_X1R5G5B5: bpp_this_mode = 16; break;
270 				case D3DFMT_A1R5G5B5: bpp_this_mode = 16; break;
271 				case D3DFMT_A4R4G4B4: bpp_this_mode = 16; break;
272 				case D3DFMT_R3G3B2  : bpp_this_mode =  8; break;
273 				case D3DFMT_A8R3G3B2: bpp_this_mode = 16; break;
274 				case D3DFMT_X4R4G4B4: bpp_this_mode = 16; break;
275 				}
276 
277 				if (rep < 4)
278 				{
279 					if (pdm->Width != list[i].Width)
280 						bMatch = false;
281 					if (pdm->Height != list[i].Height)
282 						bMatch = false;
283 				}
284 				else if (rep < 8)
285 				{
286 					if (DEFAULT_FULLSCREEN_WIDTH != list[i].Width)
287 						bMatch = false;
288 					if (DEFAULT_FULLSCREEN_HEIGHT != list[i].Height)
289 						bMatch = false;
290 				}
291 
292 				if (((rep/2)%2)==0)
293 				{
294 					if (pass==0 && pdm->Format != list[i].Format)
295 						bMatch = false;
296 					else if (pass==1 && bpp_desired != bpp_this_mode)
297 						bMatch = false;
298 				}
299 
300 				if (((rep%2)==0) && pdm->RefreshRate != list[i].RefreshRate)
301 				{
302 					bMatch = false;
303 				}
304 
305 				if (bMatch)
306 				{
307 					memcpy(pdm, &list[i], sizeof(D3DDISPLAYMODE));
308 					found = 1;
309 					if (rep != 0 || pass != 0)
310 					{
311 						return 1;
312 					}
313 				}
314 			}
315 		}
316 	}
317 	return 0;
318 }
319 
MyMonitorEnumProc(HMONITOR hMonitor,HDC hdcMonitor,LPRECT lprcMonitor,LPARAM dwData)320 BOOL CALLBACK MyMonitorEnumProc(
321   HMONITOR hMonitor,  // handle to display monitor
322   HDC hdcMonitor,     // handle to monitor DC
323   LPRECT lprcMonitor, // monitor intersection rectangle
324   LPARAM dwData       // data
325 )
326 {
327 	RECT* p = (RECT*)dwData;
328 	if (hMonitor)
329 	{
330 		MONITORINFO mi;
331 		mi.cbSize = sizeof(mi);
332 		if (GetMonitorInfo(hMonitor, &mi))
333 		{
334 			p->top    = min(p->top   , mi.rcMonitor.top);
335 			p->left   = min(p->left  , mi.rcMonitor.left);
336 			p->right  = max(p->right , mi.rcMonitor.right);
337 			p->bottom = max(p->bottom, mi.rcMonitor.bottom);
338 		}
339 	}
340 
341 	return TRUE;
342 }
343 
GetWindowedModeAutoSize(int iteration)344 int DXContext::GetWindowedModeAutoSize(int iteration)
345 {
346 	// note: requires 'm_monitor_rect' has been set!
347 
348 	// generically determine size of window, for windowed mode:
349 	int x = m_monitor_rect.right-m_monitor_rect.left;
350 	int y = m_monitor_rect.bottom-m_monitor_rect.top;
351 
352 	// if running in horz/vert-span multi-display mode, base the window size on
353 	// an actual display size, not the giant double-sized monitor.  Also, position
354 	// the window on the same monitor that Winamp is on.
355 	if (x >= y*2)
356 	{
357 		x /= 2;
358 
359 		// move window to same display that Winamp is on:
360 		WINDOWPLACEMENT wp;
361 		wp.length = sizeof(wp);
362 		if (GetWindowPlacement(m_hwnd_winamp, &wp))
363 		{
364 			int winamp_center_x = (wp.rcNormalPosition.right + wp.rcNormalPosition.left)/2;
365 			if (winamp_center_x > x)
366 			{
367 				m_monitor_rect.left += x;
368 				m_monitor_rect.right += x;
369 			}
370 		}
371 	}
372 	else if (y > x*4/3)
373 	{
374 		y /= 2;
375 
376 		// move window to same display that Winamp is on:
377 		WINDOWPLACEMENT wp;
378 		wp.length = sizeof(wp);
379 		if (GetWindowPlacement(m_hwnd_winamp, &wp))
380 		{
381 			int winamp_center_y = (wp.rcNormalPosition.top + wp.rcNormalPosition.bottom)/2;
382 			if (winamp_center_y > y)
383 			{
384 				m_monitor_rect.top += y;
385 				m_monitor_rect.bottom += y;
386 			}
387 		}
388 	}
389 
390 	int size = min(x, y);
391 	size = (int)(size*DEFAULT_WINDOW_SIZE);
392 	size = (size/64 - iteration)*64;
393 	if (size < 64)
394 		size = 64;
395 
396 	return size;
397 }
398 
WriteSafeWindowPos()399 void DXContext::WriteSafeWindowPos()
400 {
401 	if (m_current_mode.screenmode == WINDOWED)
402 	{
403 		WritePrivateProfileIntW(64,     L"nMainWndTop",    m_szIniFile, L"settings");
404 		WritePrivateProfileIntW(64,     L"nMainWndLeft",   m_szIniFile, L"settings");
405 		WritePrivateProfileIntW(64+256, L"nMainWndRight",  m_szIniFile, L"settings");
406 		WritePrivateProfileIntW(64+256, L"nMainWndBottom", m_szIniFile, L"settings");
407 		WritePrivateProfileIntW(64,     L"avs_wx",m_szIniFile,L"settings");
408 		WritePrivateProfileIntW(64,     L"avs_wy",m_szIniFile,L"settings");
409 		WritePrivateProfileIntW(256, L"avs_ww",m_szIniFile,L"settings");
410 		WritePrivateProfileIntW(256, L"avs_wh",m_szIniFile,L"settings");
411 	}
412 }
413 
414 // {0000000A-000C-0010-FF7B-01014263450C}
415 const GUID avs_guid =
416   { 10, 12, 16, { 255, 123, 1, 1, 66, 99, 69, 12 } };
417 
Internal_Init(DXCONTEXT_PARAMS * pParams,BOOL bFirstInit)418 BOOL DXContext::Internal_Init(DXCONTEXT_PARAMS *pParams, BOOL bFirstInit)
419 {
420 	memcpy(&m_current_mode, pParams, sizeof(DXCONTEXT_PARAMS));
421 	memset(&myWindowState,0,sizeof(myWindowState));
422 
423 	// various checks
424 	if (m_current_mode.screenmode != WINDOWED)
425 		m_current_mode.m_skin = 0;
426 
427 	// 1. destroy old window
428 	if (m_hwnd)
429 	{
430 		m_ignore_wm_destroy = 1;
431 		DestroyWindow(m_hwnd);
432 		m_ignore_wm_destroy = 0;
433 		m_hwnd = NULL;
434 	}
435 
436 	// 2. CHECK TO MAKE SURE DIRECTX/DDRAW IS INSTALLED
437 	if (bFirstInit)
438 	{
439 		// Test for DirectX 9 + start it
440 		// note: if you don't call LoadLibrary here, and you're on a system
441 		//       where DX9 is missing, Direct3DCreate8() might crash; so call it.
442 		int d3d9_already_loaded = (GetModuleHandle("d3d9.dll") != NULL) ? 1 : 0;
443 		if (!d3d9_already_loaded)
444 			m_hmod_d3d9 = LoadLibrary("d3d9.dll");
445 
446 		if ((!d3d9_already_loaded && !m_hmod_d3d9) ||
447 		    !(m_lpD3D = Direct3DCreate9(D3D_SDK_VERSION))
448 		   )
449 		{
450 			MissingDirectX(NULL);
451 			m_lastErr = DXC_ERR_CREATE3D;
452 			return FALSE;
453 		}
454 
455 		if (!m_hmod_d3dx9)
456 			m_hmod_d3dx9 = FindD3DX9(m_hwnd_winamp);
457 
458 		if ((!m_hmod_d3dx9))
459 		{
460 			MissingDirectX(NULL);
461 			m_lastErr = DXC_ERR_CREATE3D;
462 			return FALSE;
463 		}
464 	}
465 
466 	// 3. get the smallest single rectangle that encloses ALL the monitors on the desktop:
467 	SetRect(&m_all_monitors_rect, 0, 0, 0, 0);
468 	EnumDisplayMonitors(NULL, NULL, MyMonitorEnumProc, (LPARAM)&m_all_monitors_rect);
469 
470 	// 4. some DirectX- / DDraw-specific stuff.  Also determine hPluginMonitor.
471 	HMONITOR hPluginMonitor = NULL;
472 	{
473 		D3DADAPTER_IDENTIFIER9 temp;
474 
475 		// find the ordinal # of the adapter whose GUID matches what the user picked from the config panel,
476 		// and whose DeviceName matches as well.
477 		// if no match found, use D3DADAPTER_DEFAULT.
478 		m_ordinal_adapter = D3DADAPTER_DEFAULT;
479 		int nAdapters = m_lpD3D->GetAdapterCount();
480 		{
481 			for (int i=0; i<nAdapters; i++)
482 			{
483 				if ((m_lpD3D->GetAdapterIdentifier(i, /*D3DENUM_NO_WHQL_LEVEL*/ 0, &temp) == D3D_OK) &&
484 				    (memcmp(&temp.DeviceIdentifier, &m_current_mode.adapter_guid, sizeof(GUID))==0) &&
485 				    !strcmp(temp.DeviceName, m_current_mode.adapter_devicename)
486 				   )
487 				{
488 					m_ordinal_adapter = i;
489 					break;
490 				}
491 			}
492 		}
493 
494 		if (m_lpD3D->GetAdapterIdentifier(m_ordinal_adapter, /*D3DENUM_NO_WHQL_LEVEL*/ 0, &temp) == D3D_OK)
495 		{
496 			StringCbCopy(m_szDriver, sizeof(m_szDriver), temp.Driver);
497 			StringCbCopy(m_szDesc, sizeof(m_szDesc), temp.Description);
498 		}
499 
500 		int caps_ok = 0;
501 		int caps_tries = 0;
502 		int changed_fs_disp_mode;
503 
504 		// try to get the device caps for the adapter selected from the config panel.
505 		// if GetDeviceCaps() fails, it's probably because the adapter has been
506 		// removed from the system (or disabled), so we try again with other adapter(s).
507 		do
508 		{
509 			changed_fs_disp_mode = 0;
510 
511 			SetRect(&m_monitor_rect, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
512 
513 			// get bounding rect of the monitor attached to the adapter (to assist w/window positioning)
514 			// note: in vert/horz span setups (psuedo-multimon),
515 			//       this will be 2048x768 or 1024x1536 or something like that.
516 			hPluginMonitor = m_lpD3D->GetAdapterMonitor(m_ordinal_adapter);
517 			/*if (hPluginMonitor)
518 			{
519 			    MONITORINFO mi;
520 			    mi.cbSize = sizeof(mi);
521 			    if (GetMonitorInfo(hPluginMonitor, &mi))
522 			    {
523 			        memcpy(&m_monitor_rect, &mi.rcMonitor, sizeof(RECT));
524 			        memcpy(&m_monitor_work_rect, &mi.rcWork, sizeof(RECT));
525 			    }
526 			}*/
527 
528 			if (bFirstInit)
529 			{
530 				for (int i=0; i<min(nAdapters, MAX_DXC_ADAPTERS); i++)
531 				{
532 					// if this is the first call to Init, get the display mode's original color format,
533 					// before we go changing it:
534 					D3DDISPLAYMODE d3ddm;
535 					if (FAILED(m_lpD3D->GetAdapterDisplayMode(i, &d3ddm)))
536 					{
537 						d3ddm.Format = D3DFMT_UNKNOWN;
538 					}
539 					m_orig_windowed_mode_format[i] = d3ddm.Format;
540 				}
541 			}
542 
543 			// figure out pixel (color) format for back buffer: (m_current_mode.display_mode.Format)
544 			if (m_current_mode.screenmode!=FULLSCREEN && m_ordinal_adapter < MAX_DXC_ADAPTERS)
545 				m_current_mode.display_mode.Format = m_orig_windowed_mode_format[m_ordinal_adapter];
546 			// else
547 			// for fullscreen, use what they gave us
548 
549 			if (m_current_mode.display_mode.Format == D3DFMT_UNKNOWN ||
550 			    !TestFormat(m_ordinal_adapter, m_current_mode.display_mode.Format))
551 			{
552 				// if they try to run the plugin without ever running the config panel
553 				// first (& pressing OK), then the fullscreen pixelformat hasn't been
554 				// chosen... so we try all the possilibities until one works:
555 				if (TestFormat(m_ordinal_adapter,D3DFMT_A8R8G8B8)) m_current_mode.display_mode.Format = D3DFMT_A8R8G8B8;
556 				else if (TestFormat(m_ordinal_adapter,D3DFMT_X8R8G8B8)) m_current_mode.display_mode.Format = D3DFMT_X8R8G8B8;
557 				else if (TestFormat(m_ordinal_adapter,D3DFMT_R8G8B8)) m_current_mode.display_mode.Format = D3DFMT_R8G8B8  ;
558 				else if (TestFormat(m_ordinal_adapter,D3DFMT_R5G6B5)) m_current_mode.display_mode.Format = D3DFMT_R5G6B5  ;
559 				else if (TestFormat(m_ordinal_adapter,D3DFMT_X1R5G5B5)) m_current_mode.display_mode.Format = D3DFMT_X1R5G5B5;
560 				else if (TestFormat(m_ordinal_adapter,D3DFMT_A1R5G5B5)) m_current_mode.display_mode.Format = D3DFMT_A1R5G5B5;
561 				else if (TestFormat(m_ordinal_adapter,D3DFMT_A4R4G4B4)) m_current_mode.display_mode.Format = D3DFMT_A4R4G4B4;
562 				else if (TestFormat(m_ordinal_adapter,D3DFMT_X4R4G4B4)) m_current_mode.display_mode.Format = D3DFMT_X4R4G4B4;
563 			}
564 
565 			if (m_current_mode.display_mode.Format==D3DFMT_UNKNOWN)
566 			{
567 				wchar_t title[64];
568 				m_lastErr = DXC_ERR_FORMAT;
569 				MessageBoxW(m_hwnd, WASABI_API_LNGSTRINGW(IDS_DIRECTX_INIT_FAILED),
570 						    WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
571 						    MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
572 				return FALSE;
573 			}
574 
575 			if (m_current_mode.screenmode == FULLSCREEN)
576 				changed_fs_disp_mode = CheckAndCorrectFullscreenDispMode(m_ordinal_adapter, &m_current_mode.display_mode);
577 
578 			// figure out pixel format of the z-buffer: (m_zFormat)
579 			m_zFormat = D3DFMT_UNKNOWN;
580 			/*
581 			if      (TestDepth(m_ordinal_adapter,D3DFMT_D32         )) m_zFormat=D3DFMT_D32;
582 			else if (TestDepth(m_ordinal_adapter,D3DFMT_D24S8       )) m_zFormat=D3DFMT_D24S8;
583 			else if (TestDepth(m_ordinal_adapter,D3DFMT_D24X4S4     )) m_zFormat=D3DFMT_D24X4S4;
584 			else if (TestDepth(m_ordinal_adapter,D3DFMT_D24X8       )) m_zFormat=D3DFMT_D24X8;
585 			else if (TestDepth(m_ordinal_adapter,D3DFMT_D16         )) m_zFormat=D3DFMT_D16;
586 			else if (TestDepth(m_ordinal_adapter,D3DFMT_D15S1       )) m_zFormat=D3DFMT_D15S1;
587 			else if (TestDepth(m_ordinal_adapter,D3DFMT_D16_LOCKABLE)) m_zFormat=D3DFMT_D16_LOCKABLE;
588 			*/
589 
590 			// get device caps:
591 			memset(&m_caps, 0, sizeof(m_caps));
592 			if (FAILED(m_lpD3D->GetDeviceCaps(m_ordinal_adapter, D3DDEVTYPE_HAL, &m_caps)))
593 			{
594 				// that adapter was found in the system, but it might be disabled
595 				// (i.e. 'extend my Windows desktop onto this monitor') is unchecked)
596 				// so, try other adapters (try all sequentially).
597 
598 				if (caps_tries < nAdapters)
599 				{
600 					// try again, this time using the default adapter:
601 					m_ordinal_adapter = caps_tries;
602 					caps_tries++;
603 				}
604 				else
605 				{
606 					wchar_t title[64];
607 					m_lastErr = DXC_ERR_CAPSFAIL;
608 					MessageBoxW(m_hwnd, WASABI_API_LNGSTRINGW(IDS_DXC_ERR_CAPSFAIL),
609 							    WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
610 							    MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
611 					return FALSE;
612 				}
613 			}
614 			else
615 			{
616 				caps_ok = 1;
617 			}
618 		}
619 		while (!caps_ok);
620 
621 		if (changed_fs_disp_mode)
622 		{
623 			wchar_t title[64];
624 			MessageBoxW(m_hwnd, WASABI_API_LNGSTRINGW(IDS_FS_DISPLAY_MODE_SELECTED_IS_INVALID),
625 					    WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_WARNING, title, 64),
626 					    MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
627 		}
628 
629 		switch (m_current_mode.display_mode.Format)
630 		{
631 		case D3DFMT_R8G8B8  : m_bpp = 32; break;
632 		case D3DFMT_A8R8G8B8: m_bpp = 32; break;
633 		case D3DFMT_X8R8G8B8: m_bpp = 32; break;
634 		case D3DFMT_R5G6B5  : m_bpp = 16; break;
635 		case D3DFMT_X1R5G5B5: m_bpp = 16; break;
636 		case D3DFMT_A1R5G5B5: m_bpp = 16; break;
637 		case D3DFMT_A8R3G3B2: m_bpp = 16; break;
638 		case D3DFMT_A4R4G4B4: m_bpp = 16; break;
639 		case D3DFMT_X4R4G4B4: m_bpp = 16; break;
640 		case D3DFMT_R3G3B2  : m_bpp =  8; break; // misleading?  implies a palette...
641 		}
642 	}
643 
644 	// 5. set m_monitor_rect and m_monitor_work_rect.
645 	if (hPluginMonitor)
646 	{
647 		MONITORINFO mi;
648 		mi.cbSize = sizeof(mi);
649 		if (GetMonitorInfo(hPluginMonitor, &mi))
650 		{
651 			m_monitor_rect = mi.rcMonitor;
652 			m_monitor_rect_orig = mi.rcMonitor;
653 			m_monitor_work_rect = mi.rcWork;
654 			m_monitor_work_rect_orig = mi.rcWork;
655 		}
656 	}
657 
658 	// 6. embedded window stuff [where the plugin window is integrated w/winamp]
659 	if (m_current_mode.m_skin)
660 	{
661 		// set up the window's position on screen
662 		// note that we'd prefer to set the CLIENT size we want, but we can't, so we'll just do
663 		// this here, and later, adjust the client rect size to what's left...
664 		int size = GetWindowedModeAutoSize(0);  // note: requires 'm_monitor_rect' has been set!
665 		myWindowState.r.left   = GetPrivateProfileIntW(L"settings",L"avs_wx",64,m_szIniFile);
666 		myWindowState.r.top    = GetPrivateProfileIntW(L"settings",L"avs_wy",64,m_szIniFile);
667 		myWindowState.r.right  = myWindowState.r.left + GetPrivateProfileIntW(L"settings",L"avs_ww",size+24,m_szIniFile);
668 		myWindowState.r.bottom = myWindowState.r.top  + GetPrivateProfileIntW(L"settings",L"avs_wh",size+40,m_szIniFile);
669 
670 		// only works on winamp 2.90+!
671 		int success = 0;
672 		if (GetWinampVersion(mod1.hwndParent) >= 0x2900)
673 		{
674 			SET_EMBED_GUID((&myWindowState), avs_guid);
675 			myWindowState.flags |= EMBED_FLAGS_NOTRANSPARENCY;
676 			HWND (*e)(embedWindowState *v);
677 			*(void**)&e = (void *)SendMessage(mod1.hwndParent,WM_WA_IPC,(LPARAM)0,IPC_GET_EMBEDIF);
678 			if (e)
679 			{
680 				m_current_mode.parent_window = e(&myWindowState);
681 				if (m_current_mode.parent_window)
682 				{
683 					SetWindowText(m_current_mode.parent_window, m_szWindowCaption);
684 					success = 1;
685 				}
686 			}
687 		}
688 
689 		if (!success)
690 			m_current_mode.m_skin = 0;
691 	}
692 
693 	// remember the client rect that was originally desired...
694 	RECT windowed_mode_desired_client_rect;
695 	windowed_mode_desired_client_rect.top    = GetPrivateProfileIntW(L"settings",L"nMainWndTop",-1,m_szIniFile);
696 	windowed_mode_desired_client_rect.left   = GetPrivateProfileIntW(L"settings",L"nMainWndLeft",-1,m_szIniFile);
697 	windowed_mode_desired_client_rect.right  = GetPrivateProfileIntW(L"settings",L"nMainWndRight",-1,m_szIniFile);
698 	windowed_mode_desired_client_rect.bottom = GetPrivateProfileIntW(L"settings",L"nMainWndBottom",-1,m_szIniFile);
699 
700 	// ...and in case windowed mode init fails severely,
701 	// set it up to try next time for a simple 256x256 window.
702 	WriteSafeWindowPos();
703 
704 	// 7. create the window, if not already created
705 	if (!m_hwnd)
706 	{
707 		m_hwnd = CreateWindowEx(
708 		           MY_EXT_WINDOW_STYLE, // extended style
709 		           MAKEINTATOM(m_classAtom), // class
710 		           m_szWindowCaption, // caption
711 		           MY_WINDOW_STYLE, // style
712 		           0, // left
713 		           0, // top
714 		           256,  // temporary width
715 		           256,  // temporary height
716 		           m_current_mode.parent_window,  // parent window
717 		           NULL, // menu
718 		           m_hInstance, // instance
719 		           (LPVOID)m_uWindowLong
720 		         ); // parms
721 
722 		if (!m_hwnd)
723 		{
724 			wchar_t title[64];
725 			m_lastErr = DXC_ERR_CREATEWIN;
726 			MessageBoxW(m_hwnd, WASABI_API_LNGSTRINGW(IDS_CREATEWINDOW_FAILED),
727 					    WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
728 					    MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
729 			return FALSE;
730 		}
731 
732 		SendMessage(m_hwnd_winamp, WM_WA_IPC, (WPARAM)m_hwnd, IPC_SETVISWND);
733 
734 		if (m_current_mode.m_skin)
735 		{
736 			if (GetWinampVersion(mod1.hwndParent) < 0x5051)
737 				ShowWindow(m_current_mode.parent_window,SW_SHOWNA); // showing the parent wnd will make it size the child, too
738 			else
739 				SendMessage(m_current_mode.parent_window, WM_USER+102, 0, 0); // benski> major hack alert. winamp's embedwnd will call ShowWindow in response.  SendMessage moves us over to the main thread (we're currently sitting on the viz thread)
740 		}
741 	}
742 
743 	// 8. minimize winamp before creating devices & such, so there aren't
744 	//    any confusing window-focus issues
745 	MinimizeWinamp(hPluginMonitor);
746 
747 	// 9. loop to try and create the window.
748 	//      if in windowed mode and not enough vidmem, it will try again w/smaller window
749 	//      (repeatedly, until window client size would be < 64)
750 	int iteration = 0;
751 	int device_ok = 0;
752 	do
753 	{
754 		// set the window position
755 		if (m_current_mode.screenmode==DESKTOP ||
756 		    m_current_mode.screenmode==FAKE_FULLSCREEN)
757 		{
758 			int x = m_monitor_rect.right - m_monitor_rect.left;
759 			int y = m_monitor_rect.bottom - m_monitor_rect.top;
760 
761 			if (x >= y*2)
762 			{
763 				// (pseudo-multimon modes like 2048x768)
764 				int mid = (m_monitor_rect.left + m_monitor_rect.right)/2;
765 				if (m_current_mode.m_dualhead_horz==1) // show on left side
766 					m_monitor_rect.right = mid;
767 				else if (m_current_mode.m_dualhead_horz==2) // show on right side
768 					m_monitor_rect.left = mid;
769 			}
770 			else if (y > x*4/3)
771 			{
772 				// (pseudo-multimon modes like 1024x1536)
773 				int mid = (m_monitor_rect.top + m_monitor_rect.bottom)/2;
774 				if (m_current_mode.m_dualhead_vert==1) // show on top half
775 					m_monitor_rect.bottom = mid;
776 				else if (m_current_mode.m_dualhead_vert==2) // show on bottom half
777 					m_monitor_rect.top = mid;
778 			}
779 
780 			// recompute width & height (into x,y):
781 			x = m_monitor_rect.right - m_monitor_rect.left;
782 			y = m_monitor_rect.bottom - m_monitor_rect.top;
783 
784 			m_client_width  = x;
785 			m_client_height = y;
786 			m_window_width  = x;
787 			m_window_height = y;
788 
789 			if (m_current_mode.screenmode == DESKTOP)
790 			{
791 				// note: we initially hide the window, and then
792 				// only display it once the desktop is all nice & ready.
793 				// see CPluginShell::DrawAndDisplay().
794 
795 				RECT r = m_monitor_rect;
796 
797 				// if possible, shrink the desktop window so it doesn't cover the taskbar.
798 				HWND hTaskbar = FindWindow("Shell_TrayWnd", "");
799 				if (hTaskbar)
800 				{
801 					RECT taskbar;
802 					GetWindowRect(hTaskbar, &taskbar);
803 					int tbw = taskbar.right - taskbar.left;
804 					int tbh = taskbar.bottom-taskbar.top;
805 
806 					if (taskbar.bottom == m_monitor_rect.bottom &&
807 					    taskbar.left == m_monitor_rect.left &&
808 					    taskbar.right == m_monitor_rect.right)
809 					{
810 						r.bottom -= tbh;
811 					}
812 					else if (taskbar.top == m_monitor_rect.top &&
813 					         taskbar.left == m_monitor_rect.left &&
814 					         taskbar.right == m_monitor_rect.right)
815 					{
816 						r.top += tbh;
817 					}
818 					else if (taskbar.left == m_monitor_rect.left &&
819 					         taskbar.top == m_monitor_rect.top &&
820 					         taskbar.bottom == m_monitor_rect.bottom)
821 					{
822 						r.left += tbw;
823 					}
824 					else if (taskbar.right == m_monitor_rect.right &&
825 					         taskbar.top == m_monitor_rect.top &&
826 					         taskbar.bottom == m_monitor_rect.bottom)
827 					{
828 						r.right -= tbw;
829 					}
830 
831 					m_client_width  = r.right - r.left;
832 					m_client_height = r.bottom - r.top;
833 					m_REAL_client_width = m_client_width;
834 					m_REAL_client_height = m_client_height;
835 					m_window_width  = m_client_width;
836 					m_window_height = m_client_height;
837 
838 					//...ok, but text is squished - some w/h is not right...
839 
840 				}
841 
842 				SetWindowPos(m_hwnd,HWND_BOTTOM,r.left,r.top,r.right-r.left,r.bottom-r.top,SWP_HIDEWINDOW);
843 			}
844 			else // FAKE_FULLSCREEN
845 			{
846 				if (memcmp(&m_all_monitors_rect, &m_monitor_rect, sizeof(RECT))==0)
847 				{
848 					// there's only one display, and it's entirely covered
849 					// by the plugin -> PUT THE PLUGIN ABOVE THE TASKBAR
850 					// -> normally, if the user clicked another window,
851 					//      it would pop the taskbar to the top; but we don't
852 					//      have to worry about that here, since we're taking
853 					//      up the whole screen.
854 					// -> don't worry about making the text, etc. avoid
855 					//      the taskbar in this case (see DrawAndDisplay())
856 					// -> DO worry about hiding the mouse cursor in this case
857 					//      (see WM_SETCURSOR handler)
858 
859 					m_fake_fs_covers_all = 1;
860 					//SetWindowPos(m_hwnd,HWND_TOPMOST,m_monitor_rect.left,m_monitor_rect.top,m_window_width,m_window_height,SWP_SHOWWINDOW);
861 				}
862 				else
863 				{
864 					// there is space to work outside of the plugin window.
865 					// -> here we pretty much have to let the taskbar stay on
866 					//   top, because it really likes to be there; i.e.,
867 					//   if you click any other window, it automatically
868 					//   pops up again.
869 					// -> therefore, TRY TO KEEP THE WINDOW ON BOTTOM
870 					//      (below the taskbar). (see PushWindowToBack)
871 					// -> don't worry about hiding the mouse cursor in this case
872 					//      (see WM_SETCURSOR handler)
873 					// -> DO worry about making the text, etc. avoid
874 					//      the taskbar in this case (see DrawAndDisplay())
875 
876 					// (note that if taskbar is in the way, they can move it,
877 					//   since there are other monitors available)
878 
879 					m_fake_fs_covers_all = 0;
880 					//SetWindowPos(m_hwnd,HWND_TOP,m_monitor_rect.left,m_monitor_rect.top,m_window_width,m_window_height,SWP_SHOWWINDOW);
881 				}
882 
883 				SetWindowPos(m_hwnd,HWND_TOPMOST,m_monitor_rect.left,m_monitor_rect.top,m_window_width,m_window_height,SWP_SHOWWINDOW);
884 			}
885 		}
886 		else if (m_current_mode.screenmode == FULLSCREEN)
887 		{
888 			int x = m_current_mode.display_mode.Width ;
889 			int y = m_current_mode.display_mode.Height;
890 			int cx = m_monitor_rect.right - m_monitor_rect.left;
891 			int cy = m_monitor_rect.bottom - m_monitor_rect.top;
892 
893 			// test #1
894 			if (x >= y*2 || y > x*4/3)     // tackle problem of vert/horz spans
895 			{
896 				wchar_t title[64];
897 				int ret = MessageBoxW(m_hwnd, WASABI_API_LNGSTRINGW(IDS_TRYING_TO_ENTER_FS_MODE_WITH_MULTIPLE_DISPLAYS),
898 									  WASABI_API_LNGSTRINGW_BUF(IDS_TIP, title, 64),
899 									  MB_OKCANCEL|MB_SETFOREGROUND|MB_TOPMOST);
900 				if (ret==IDCANCEL)
901 				{
902 					m_lastErr = DXC_ERR_USER_CANCELED;
903 					return FALSE;
904 				}
905 			}
906 
907 			// test #2
908 			if ((cx >= cy*2 && x < y*2) || (cy > cx*4/3 && y <= x*4/3))
909 			{
910 				wchar_t title[64];
911 				int ret = MessageBoxW(m_hwnd, WASABI_API_LNGSTRINGW(IDS_TRYING_TO_ENTER_FS_MODE_WITH_MULTIPLE_DISPLAYS_2),
912 									  WASABI_API_LNGSTRINGW_BUF(IDS_TIP, title, 64),
913 									  MB_OKCANCEL|MB_SETFOREGROUND|MB_TOPMOST);
914 				if (ret==IDCANCEL)
915 				{
916 					m_lastErr = DXC_ERR_USER_CANCELED;
917 					return FALSE;
918 				}
919 			}
920 
921 			m_client_width  = x;
922 			m_client_height = y;
923 			m_window_width  = x;
924 			m_window_height = y;
925 			SetWindowPos(m_hwnd,HWND_TOPMOST,m_monitor_rect.left,m_monitor_rect.top,m_window_width,m_window_height,SWP_SHOWWINDOW);
926 		}
927 		else // WINDOWED
928 		{
929 			RECT margin;
930 			if (m_current_mode.m_skin)
931 			{
932 				RECT r1, r2;
933 				GetWindowRect(m_current_mode.parent_window, &r1);
934 				GetWindowRect(m_hwnd , &r2);
935 				margin.left  = r2.left - r1.left;
936 				margin.right = r1.right - r2.right;
937 				margin.top   = r2.top - r1.top;
938 				margin.bottom= r1.bottom - r2.bottom;
939 			}
940 			else
941 			{
942 				RECT r1;
943 				SetRect(&r1, 0, 0, 256, 256);
944 				AdjustWindowRect(&r1, MY_WINDOW_STYLE, 0);
945 				margin.left  = 0 - r1.left;
946 				margin.right = r1.right - 256;
947 				margin.top   = 0 - r1.top;
948 				margin.bottom= r1.bottom - 256;
949 			}
950 
951 			int autosize = 1;
952 
953 			RECT r = windowed_mode_desired_client_rect;
954 			if (iteration==0 && r.top != -1 && r.left != -1 && r.bottom != -1 && r.right != -1)
955 			{
956 				// use prev. window coordinates:
957 				m_REAL_client_width  = r.right - r.left;
958 				m_REAL_client_height = r.bottom - r.top;
959 				GetSnappedClientSize();
960 				if (m_current_mode.m_skin) // check this here in case they got a non-aligned size by resizing when "integrated with winamp" was unchecked, then checked it & ran the plugin...
961 				{
962 					// STRANGE ALIGNMENTS FOR THE WINDOW FRAME: (required by winamp 2):
963 					// the window frame's width must be divisible by 25, and height by 29.
964 					if (GetWinampVersion(mod1.hwndParent) < 0x4000) // ... winamp 5 doesn't have this prob.  (test vs. 0x4000 because winamp5 betas have version tags like 0x4987)
965 					{
966 						m_REAL_client_width  = ((m_client_width + margin.left + margin.right)/25)*25 - margin.left - margin.right;
967 						m_REAL_client_height = ((m_client_height + margin.top + margin.bottom)/29)*29 - margin.top - margin.bottom;
968 						GetSnappedClientSize();
969 					}
970 				}
971 
972 				// transform screen-space CLIENT rect into screen-space WINDOW rect
973 				r.top    = windowed_mode_desired_client_rect.top    - margin.top;
974 				r.left   = windowed_mode_desired_client_rect.left   - margin.left;
975 				r.right  = r.left + margin.left + m_REAL_client_width  + margin.right;
976 				r.bottom = r.top  + margin.top  + m_REAL_client_height + margin.bottom;
977 
978 				// make sure the window is entirely visible on the selected monitor;
979 				//   otherwise, autosize/place it.
980 				// (note that this test is only appled 1) at startup, and 2) after a resize/max/restore.
981 				//  this test is not applied when merely moving the window.)
982 				if (r.top    >= m_monitor_work_rect.top &&
983 				    r.left   >= m_monitor_work_rect.left &&
984 				    r.right  <= m_monitor_work_rect.right &&
985 				    r.bottom <= m_monitor_work_rect.bottom)
986 				{
987 					if (m_current_mode.m_skin)
988 					{
989 						m_window_width  = m_REAL_client_width ; // m_window_width/height are for OUR borderless window, not the embedwnd parent frame.
990 						m_window_height = m_REAL_client_height;
991 						SetWindowPos(m_current_mode.parent_window,HWND_NOTOPMOST, r.left, r.top, r.right-r.left, r.bottom-r.top, /*SWP_SHOWWINDOW|*//*SWP_ASYNCWINDOWPOS*/0);
992 						SetWindowPos(m_hwnd ,HWND_NOTOPMOST, windowed_mode_desired_client_rect.left,
993 						             windowed_mode_desired_client_rect.top,
994 						             m_REAL_client_width,
995 						             m_REAL_client_height,
996 						             SWP_SHOWWINDOW);
997 					}
998 					else
999 					{
1000 						m_window_width  = r.right - r.left;
1001 						m_window_height = r.bottom - r.top;
1002 						SetWindowPos(m_hwnd,HWND_NOTOPMOST,r.left,r.top,m_window_width,m_window_height,SWP_SHOWWINDOW);
1003 					}
1004 
1005 					autosize = 0;
1006 				}
1007 			}
1008 
1009 			if (autosize)
1010 			{
1011 				int size = GetWindowedModeAutoSize(iteration); // note: requires 'm_monitor_rect' has been set!
1012 
1013 				m_REAL_client_width  = size;
1014 				m_REAL_client_height = size;
1015 				GetSnappedClientSize();
1016 
1017 				if (m_current_mode.m_skin)
1018 				{
1019 					// STRANGE ALIGNMENTS FOR THE WINDOW FRAME: (required by winamp 2):
1020 					// the window frame's width must be divisible by 25, and height by 29.
1021 					if (GetWinampVersion(mod1.hwndParent) < 0x4000) // ... winamp 5 doesn't have this prob.  (test vs. 0x4000 because winamp5 betas have version tags like 0x4987)
1022 					{
1023 						m_REAL_client_width  = ((m_client_width + margin.left + margin.right)/25)*25 - margin.left - margin.right;
1024 						m_REAL_client_height = ((m_client_height + margin.top + margin.bottom)/29)*29 - margin.top - margin.bottom;
1025 						GetSnappedClientSize();
1026 					}
1027 
1028 					m_window_width  = m_client_width ; // m_window_width/height are for OUR [borderless] window, not the parent window (which is the embedwnd frame).
1029 					m_window_height = m_client_height;
1030 					SetWindowPos(m_current_mode.parent_window,HWND_NOTOPMOST, m_monitor_work_rect.left+32, m_monitor_work_rect.top+32, m_client_width + margin.left + margin.right, m_client_height + margin.top + margin.bottom, /*SWP_SHOWWINDOW|*//*SWP_ASYNCWINDOWPOS*/0);
1031 					SetWindowPos(m_hwnd ,HWND_NOTOPMOST, m_monitor_work_rect.left+32 + margin.left, m_monitor_work_rect.top+32 + margin.top, m_client_width, m_client_height, SWP_SHOWWINDOW);
1032 				}
1033 				else
1034 				{
1035 					SetRect(&r, 0, 0, size, size);
1036 					AdjustWindowRect(&r, MY_WINDOW_STYLE, 0);
1037 
1038 					m_window_width  = r.right - r.left;
1039 					m_window_height = r.bottom - r.top;
1040 
1041 					SetWindowPos(m_hwnd,HWND_NOTOPMOST, m_monitor_work_rect.left+32, m_monitor_work_rect.top+32, m_window_width, m_window_height, SWP_SHOWWINDOW);
1042 				}
1043 			}
1044 		}
1045 
1046 		m_frame_delay = 1;      // set this to 2 if you use triple buffering!
1047 
1048 		{
1049 			m_current_mode.display_mode.Width  = m_client_width;
1050 			m_current_mode.display_mode.Height = m_client_height;
1051 
1052 			// set up m_d3dpp (presentation parameters):
1053 			ZeroMemory(&m_d3dpp,sizeof(m_d3dpp));
1054 			m_d3dpp.Windowed         = (m_current_mode.screenmode==FULLSCREEN) ? 0 : 1;
1055 			m_d3dpp.BackBufferFormat = m_current_mode.display_mode.Format;
1056 			m_d3dpp.BackBufferWidth  = m_client_width;
1057 			m_d3dpp.BackBufferHeight = m_client_height;
1058 			m_d3dpp.BackBufferCount  = m_current_mode.nbackbuf;
1059 			if (m_current_mode.screenmode==FULLSCREEN)
1060 				m_d3dpp.SwapEffect   = D3DSWAPEFFECT_DISCARD;//D3DSWAPEFFECT_FLIP;
1061 			else    // windowed or fake FS
1062 				m_d3dpp.SwapEffect   = (m_current_mode.allow_page_tearing) ? D3DSWAPEFFECT_DISCARD : D3DSWAPEFFECT_COPY;//D3DSWAPEFFECT_DISCARD;//D3DSWAPEFFECT_FLIP;
1063 			// note: multisampling is only allowed if swapeffect is DISCARD!
1064 			m_d3dpp.MultiSampleType  = (m_d3dpp.SwapEffect==D3DSWAPEFFECT_DISCARD) ? m_current_mode.multisamp : D3DMULTISAMPLE_NONE;
1065 			//m_d3dpp.hDeviceWindow  = m_hwnd;
1066 			if (m_current_mode.screenmode==FULLSCREEN)
1067 			{
1068 				m_d3dpp.FullScreen_RefreshRateInHz = m_current_mode.display_mode.RefreshRate;//D3DPRESENT_RATE_DEFAULT;
1069 				m_d3dpp.PresentationInterval       = m_current_mode.allow_page_tearing ? D3DPRESENT_INTERVAL_IMMEDIATE : D3DPRESENT_INTERVAL_ONE;//D3DPRESENT_INTERVAL_IMMEDIATE;//D3DPRESENT_INTERVAL_ONE;
1070 			}
1071 			if (m_zFormat != D3DFMT_UNKNOWN)
1072 			{
1073 				m_d3dpp.EnableAutoDepthStencil=TRUE;
1074 				m_d3dpp.AutoDepthStencilFormat=m_zFormat;
1075 			}
1076 
1077 			// finally, create the device:
1078 			HRESULT hRes;
1079 			if (FAILED(hRes = m_lpD3D->CreateDevice(
1080 			                    m_ordinal_adapter,
1081 			                    D3DDEVTYPE_HAL,
1082 			                    m_hwnd,
1083 			                    (m_caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) ? D3DCREATE_MIXED_VERTEXPROCESSING : D3DCREATE_SOFTWARE_VERTEXPROCESSING,
1084 			                    &m_d3dpp,
1085 			                    &m_lpDevice)))
1086 			{
1087 				int code = LOWORD(hRes);
1088 
1089 				wchar_t str[1024];
1090 				if (code==2156) //D3DERR_NOTAVAILABLE
1091 				{
1092 					m_lastErr = DXC_ERR_CREATEDEV_NOT_AVAIL;
1093 
1094 					wchar_t str[2048];
1095 					WASABI_API_LNGSTRINGW_BUF(IDS_UNABLE_TO_CREATE_DIRECTX_DEVICE, str, 2048);
1096 
1097 					if (m_current_mode.screenmode == FULLSCREEN)
1098 						StringCbCatW(str, sizeof(str), WASABI_API_LNGSTRINGW(IDS_OLDER_DISPLAY_ADAPTER_CATENATION));
1099 					else
1100 						StringCbCatW(str, sizeof(str), WASABI_API_LNGSTRINGW(IDS_OLDER_DISPLAY_ADAPTER_CATENATION_2));
1101 
1102 					MessageBoxW(m_hwnd,str,
1103 							   WASABI_API_LNGSTRINGW(IDS_MILKDROP_ERROR),
1104 							   MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
1105 					return FALSE;
1106 				}
1107 				else if (m_current_mode.screenmode==WINDOWED && m_client_width>64)
1108 				{
1109 					// DO NOTHING; try again w/smaller window
1110 				}
1111 				else if (m_current_mode.screenmode != WINDOWED || m_client_width <= 64)
1112 				{
1113 					// usually, code==2154 here, which is D3DERR_OUTOFVIDEOMEMORY
1114 					m_lastErr = DXC_ERR_CREATEDEV_PROBABLY_OUTOFVIDEOMEMORY;
1115 					StringCbPrintfW(str, sizeof(str), WASABI_API_LNGSTRINGW(IDS_DIRECTX_INIT_FAILED_X), LOWORD(hRes));
1116 
1117 					// NOTE: *A 'SUGGESTION' SCREEN SHOULD APPEAR NEXT, PROVIDED BY THE CALLER*
1118 					MessageBoxW(m_hwnd, str,
1119 							    WASABI_API_LNGSTRINGW(IDS_MILKDROP_ERROR),
1120 							    MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
1121 					return FALSE;
1122 				}
1123 			}
1124 			else
1125 			{
1126 				device_ok = 1;
1127 			}
1128 		}
1129 
1130 		iteration++;
1131 	}
1132 	while (!device_ok);
1133 
1134 	// set initial viewport
1135 	SetViewport();
1136 
1137 	// for desktop mode, push window to back again:
1138 	if (m_current_mode.screenmode==DESKTOP)
1139 		SetWindowPos(m_hwnd,HWND_BOTTOM,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
1140 
1141 	if (m_current_mode.m_skin)
1142 	{
1143 		if (GetWinampVersion(mod1.hwndParent) < 0x5051)
1144 			SetFocus(m_current_mode.parent_window);
1145 		else
1146 			PostMessage(m_current_mode.parent_window, WM_USER+103, 0, 0);
1147 
1148 		//SetActiveWindow(m_current_mode.parent_window);
1149 		//SetForegroundWindow(m_current_mode.parent_window);
1150 	}
1151 
1152 	/*if (m_current_mode.screenmode == WINDOWED)
1153 		SaveWindow();*/
1154 
1155 	// return success
1156 	m_ready = TRUE;
1157 	// benski> a little hack to get the window size correct. it seems to work
1158 	if (m_current_mode.screenmode==WINDOWED)
1159 		PostMessage(m_hwnd, WM_USER+555, 0, 0);
1160 	return TRUE;
1161 }
1162 
StartOrRestartDevice(DXCONTEXT_PARAMS * pParams)1163 BOOL DXContext::StartOrRestartDevice(DXCONTEXT_PARAMS *pParams)
1164 {
1165 	// call this to [re]initialize the DirectX environment with new parameters.
1166 	// examples: startup; toggle windowed/fullscreen mode; change fullscreen resolution;
1167 	//   and so on.
1168 	// be sure to clean up all your DirectX stuff first (textures, vertex buffers,
1169 	//   D3DX allocations, etc.) and reallocate it afterwards!
1170 
1171 	// note: for windowed mode, 'pParams->disp_mode' (w/h/r/f) is ignored.
1172 
1173 	// destroy old window
1174 	if (myWindowState.me)
1175 	{
1176 		m_ignore_wm_destroy = 1;
1177 		if (m_current_mode.screenmode == WINDOWED)
1178 			SaveWindow();
1179 		DestroyWindow(myWindowState.me);
1180 		myWindowState.me = NULL;
1181 		m_ignore_wm_destroy = 0;
1182 		m_hwnd=0;
1183 	}
1184 	else if (m_hwnd)
1185 	{
1186 		SendMessage(m_hwnd_winamp, WM_WA_IPC, NULL, IPC_SETVISWND);
1187 		m_ignore_wm_destroy = 1;
1188 		DestroyWindow(m_hwnd);
1189 		m_ignore_wm_destroy = 0;
1190 		m_hwnd = NULL;
1191 	}
1192 
1193 	if (!m_ready)
1194 	{
1195 		// first-time init: create a fresh new device
1196 		return Internal_Init(pParams, TRUE);
1197 	}
1198 	else
1199 	{
1200 		// re-init: preserve the DX9 object (m_lpD3D),
1201 		// but destroy and re-create the DX9 device (m_lpDevice).
1202 		m_ready = FALSE;
1203 
1204 		SafeRelease(m_lpDevice);
1205 		// but leave the D3D object!
1206 
1207 		RestoreWinamp();
1208 		return Internal_Init(pParams, FALSE);
1209 	}
1210 }
1211 
OnUserResizeWindow(RECT * new_window_rect,RECT * new_client_rect)1212 BOOL DXContext::OnUserResizeWindow(RECT *new_window_rect, RECT *new_client_rect)
1213 {
1214 	// call this function on WM_EXITSIZEMOVE when running windowed.
1215 	// don't bother calling this when fullscreen.
1216 	// be sure to clean up all your DirectX stuff first (textures, vertex buffers,
1217 	//   D3DX allocations, etc.) and reallocate it afterwards!
1218 
1219 	if (!m_ready || (m_current_mode.screenmode != WINDOWED))
1220 		return FALSE;
1221 
1222 	if ((m_client_width  == new_client_rect->right - new_client_rect->left) &&
1223 	    (m_client_height == new_client_rect->bottom - new_client_rect->top) &&
1224 	    (m_window_width  == new_window_rect->right - new_window_rect->left) &&
1225 	    (m_window_height == new_window_rect->bottom - new_window_rect->top))
1226 	{
1227 		return TRUE;
1228 	}
1229 
1230 	m_ready = FALSE;
1231 
1232 	m_window_width  = new_window_rect->right  - new_window_rect->left;
1233 	m_window_height = new_window_rect->bottom - new_window_rect->top;
1234 	m_REAL_client_width  = new_client_rect->right  - new_client_rect->left;
1235 	m_REAL_client_height = new_client_rect->bottom - new_client_rect->top;
1236 	GetSnappedClientSize();  //sets m_client_width/height, but with snapping, if in windowed mode.
1237 
1238 	m_d3dpp.BackBufferWidth  = m_client_width;
1239 	m_d3dpp.BackBufferHeight = m_client_height;
1240 	if (m_lpDevice->Reset(&m_d3dpp) != D3D_OK)
1241 	{
1242 		WriteSafeWindowPos();
1243 
1244 		wchar_t title[64];
1245 		MessageBoxW(m_hwnd, WASABI_API_LNGSTRINGW(IDS_WINDOW_RESIZE_FAILED),
1246 				    WASABI_API_LNGSTRINGW_BUF(IDS_OUT_OF_VIDEO_MEMORY, title, 64),
1247 				    MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
1248 
1249 		m_lastErr = DXC_ERR_RESIZEFAILED;
1250 		return FALSE;
1251 	}
1252 
1253 	SetViewport();
1254 	m_ready = TRUE;
1255 	return TRUE;
1256 }
1257 
SetViewport()1258 void DXContext::SetViewport()
1259 {
1260 	D3DVIEWPORT9 v;
1261 	v.X = 0;
1262 	v.Y = 0;
1263 	v.Width = m_client_width;
1264 	v.Height = m_client_height;
1265 	v.MinZ = 0.0f;
1266 	v.MaxZ = 1.0f;
1267 	m_lpDevice->SetViewport(&v);
1268 }
1269 
MinimizeWinamp(HMONITOR hPluginMonitor)1270 void DXContext::MinimizeWinamp(HMONITOR hPluginMonitor)
1271 {
1272 	// minimize Winamp window
1273 
1274 	HMONITOR hWinampMon = MonitorFromWindow(m_hwnd_winamp, MONITOR_DEFAULTTONEAREST);
1275 	HMONITOR hPluginMon = hPluginMonitor;//MonitorFromWindow(m_hwnd, MONITOR_DEFAULTTONEAREST);//m_lpD3D->GetAdapterMonitor(ordinal_adapter);
1276 
1277 	if ((m_current_mode.screenmode == FULLSCREEN || m_current_mode.screenmode == FAKE_FULLSCREEN) &&
1278 	    (m_minimize_winamp) &&
1279 	    (hWinampMon && hPluginMon && hPluginMon==hWinampMon) &&
1280 	    (!m_winamp_minimized)
1281 	   )
1282 	{
1283 		// nitpicky check: if we're in fake fullscreen mode
1284 		// and are only going to display on half the screen,
1285 		// don't minimize Winamp.
1286 		if (m_current_mode.screenmode == FAKE_FULLSCREEN)
1287 		{
1288 			int x = m_monitor_rect.right - m_monitor_rect.left;
1289 			int y = m_monitor_rect.bottom - m_monitor_rect.top;
1290 			if ((x >= y*2 && m_current_mode.m_dualhead_horz != 0) ||
1291 			    (y > x*4/3 && m_current_mode.m_dualhead_vert != 0))
1292 			{
1293 				return;
1294 			}
1295 		}
1296 
1297 		ShowWindow(m_hwnd_winamp, SW_MINIMIZE);
1298 		// also restore the focus to the plugin window, since this will steal it:
1299 		SetFocus(m_hwnd);
1300 		SetActiveWindow(m_hwnd);
1301 		SetForegroundWindow(m_hwnd);
1302 		m_winamp_minimized = 1;
1303 	}
1304 }
1305 
RestoreWinamp()1306 void DXContext::RestoreWinamp()
1307 {
1308 	if (m_winamp_minimized)
1309 	{
1310 		ShowWindow(m_hwnd_winamp, SW_RESTORE);
1311 		m_winamp_minimized = 0;
1312 	}
1313 }
1314 
UpdateMonitorWorkRect()1315 void DXContext::UpdateMonitorWorkRect()
1316 {
1317 	// get active monitor's bounding rectangle (to assist w/window positioning)
1318 	// note: in vert/horz span setups (psuedo-multimon),
1319 	//       this will be 2048x768 or 1024x1536 or something like that.
1320 
1321 	// calling this each frame allows you to detect when the taskbar
1322 	// moves around on the screen (from edge to edge), and rearrange
1323 	// the visual elements accordingly, so nothing is obscured.
1324 
1325 	HMONITOR hMon = MonitorFromWindow(m_hwnd, MONITOR_DEFAULTTONEAREST);//m_lpD3D->GetAdapterMonitor(m_ordinal_adapter);
1326 	if (hMon)
1327 	{
1328 		MONITORINFO mi;
1329 		mi.cbSize = sizeof(mi);
1330 		if (GetMonitorInfo(hMon, &mi))
1331 		{
1332 			m_monitor_work_rect = mi.rcWork;
1333 			m_monitor_work_rect_orig = mi.rcWork;
1334 
1335 			// if the monitor rect we're using is the same as the
1336 			// whole area of the monitor, there's no need to update it...
1337 			//if (memcmp(&mi.rcMonitor, &m_monitor_rect, sizeof(RECT))==0)
1338 			//    return;
1339 
1340 			// otherwise, we're doing a half-screen special case
1341 			// and are running in some pseudo-multimon res like
1342 			// 2048x768 or 1024x1536, but only using half of it
1343 			// (i.e. fake fullscreen or desktop mode)
1344 
1345 			// therefore... we need to update the work-area rectangle
1346 			// to reflect which half of the screen it's on.
1347 
1348 			if (m_monitor_rect.left == mi.rcMonitor.left)
1349 				m_monitor_work_rect.left = mi.rcWork.left;
1350 			else
1351 				m_monitor_work_rect.left = m_monitor_rect.left + (mi.rcWork.left - mi.rcMonitor.left);
1352 
1353 			if (m_monitor_rect.top == mi.rcMonitor.top)
1354 				m_monitor_work_rect.top = mi.rcWork.top;
1355 			else
1356 				m_monitor_work_rect.top = m_monitor_rect.top + (mi.rcWork.top - mi.rcMonitor.top);
1357 
1358 			if (m_monitor_rect.right == mi.rcMonitor.right)
1359 				m_monitor_work_rect.right = mi.rcWork.right;
1360 			else
1361 				m_monitor_work_rect.right = m_monitor_rect.right;
1362 
1363 			if (m_monitor_rect.bottom == mi.rcMonitor.bottom)
1364 				m_monitor_work_rect.bottom = mi.rcWork.bottom;
1365 			else
1366 				m_monitor_work_rect.bottom = m_monitor_rect.bottom;
1367 		}
1368 	}
1369 }
1370 
SaveWindow()1371 void DXContext::SaveWindow()
1372 {
1373 	if (m_current_mode.screenmode == WINDOWED)
1374 	{
1375 		RECT c;
1376 		GetClientRect(m_hwnd, &c);
1377 
1378 		// convert client rect from client coords to screen coords:
1379 		// (window rect is already in screen coords...)
1380 		POINT p;
1381 		p.x = c.left;
1382 		p.y = c.top;
1383 		if (ClientToScreen(m_hwnd, &p))
1384 		{
1385 			c.left += p.x;
1386 			c.right += p.x;
1387 			c.top += p.y;
1388 			c.bottom += p.y;
1389 		}
1390 
1391 		// save bounds for window CLIENT area, but in screen coords
1392 		WritePrivateProfileIntW(c.top,   L"nMainWndTop",    m_szIniFile, L"settings");
1393 		WritePrivateProfileIntW(c.left,  L"nMainWndLeft",   m_szIniFile, L"settings");
1394 		WritePrivateProfileIntW(c.right, L"nMainWndRight",  m_szIniFile, L"settings");
1395 		WritePrivateProfileIntW(c.bottom,L"nMainWndBottom", m_szIniFile, L"settings");
1396 
1397 		// also save bounds for embedwnd
1398 		if (m_current_mode.m_skin && myWindowState.me)
1399 		{
1400 			WritePrivateProfileIntW(myWindowState.r.left,L"avs_wx",m_szIniFile,L"settings");
1401 			WritePrivateProfileIntW(myWindowState.r.top ,L"avs_wy",m_szIniFile,L"settings");
1402 			WritePrivateProfileIntW(myWindowState.r.right-myWindowState.r.left,L"avs_ww",m_szIniFile,L"settings");
1403 			WritePrivateProfileIntW(myWindowState.r.bottom-myWindowState.r.top,L"avs_wh",m_szIniFile,L"settings");
1404 		}
1405 		else if (!m_current_mode.m_skin && m_hwnd)
1406 		{
1407 			RECT r;
1408 			GetWindowRect(m_hwnd, &r);
1409 			WritePrivateProfileIntW(r.left,L"avs_wx",m_szIniFile,L"settings");
1410 			WritePrivateProfileIntW(r.top ,L"avs_wy",m_szIniFile,L"settings");
1411 			WritePrivateProfileIntW(r.right-r.left,L"avs_ww",m_szIniFile,L"settings");
1412 			WritePrivateProfileIntW(r.bottom-r.top,L"avs_wh",m_szIniFile,L"settings");
1413 		}
1414 	}
1415 }