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 }