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