aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Visualization/vis_milk2/pluginshell.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Visualization/vis_milk2/pluginshell.cpp')
-rw-r--r--Src/Plugins/Visualization/vis_milk2/pluginshell.cpp3693
1 files changed, 3693 insertions, 0 deletions
diff --git a/Src/Plugins/Visualization/vis_milk2/pluginshell.cpp b/Src/Plugins/Visualization/vis_milk2/pluginshell.cpp
new file mode 100644
index 00000000..cd79a410
--- /dev/null
+++ b/Src/Plugins/Visualization/vis_milk2/pluginshell.cpp
@@ -0,0 +1,3693 @@
+/*
+ LICENSE
+ -------
+Copyright 2005-2013 Nullsoft, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of Nullsoft nor the names of its contributors may be used to
+ endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ TO DO
+ -----
+ -done/v1.06:
+ -(nothing yet)
+ -
+ -
+ -to do/v1.06:
+ -FFT: high freq. data kinda sucks because of the 8-bit samples we get in;
+ look for justin to put 16-bit vis data into wa5.
+ -make an 'advanced view' button on config panel; hide complicated stuff
+ til they click that.
+ -put an asterisk(*) next to the 'max framerate' values that
+ are ideal (given the current windows display mode or selected FS dispmode).
+ -or add checkbox: "smart sync"
+ -> matches FPS limit to nearest integer divisor of refresh rate.
+ -debug.txt/logging support!
+ -audio: make it a DSP plugin? then we could get the complete, continuous waveform
+ and overlap our waveform windows, so we'd never miss a brief high note.
+ -bugs:
+ -vms plugins sometimes freeze after a several-minute pause; I've seen it
+ with most of them. hard to repro, though.
+ -running FS on monitor 2, hit ALT-TAB -> minimizes!!!
+ -but only if you let go of TAB first. Let go of ALT first and it's fine!
+ -> means it's related to the keyup...
+ -fix delayloadhelper leak; one for each launch to config panel/plugin.
+ -also, delayload(d3d9.dll) still leaks, if plugin has error initializing and
+ quits by returning false from PluginInitialize().
+ -add config panel option to ignore fake-fullscreen tips
+ -"tip" boxes in dxcontext.cpp
+ -"notice" box on WM_ACTIVATEAPP?
+ -desktop mode:
+ -icon context menus: 'send to', 'cut', and 'copy' links do nothing.
+ -http://netez.com/2xExplorer/shellFAQ/bas_context.html
+ -create a 2nd texture to render all icon text labels into
+ (they're the sole reason that desktop mode is slow)
+ -in UpdateIconBitmaps, don't read the whole bitmap and THEN
+ realize it's a dupe; try to compare icon filename+index or somethign?
+ -DRAG AND DROP. COMPLICATED; MANY DETAILS.
+ -http://netez.com/2xExplorer/shellFAQ/adv_drag.html
+ -http://www.codeproject.com/shell/explorerdragdrop.asp
+ -hmm... you can't drag icons between the 2 desktops (ugh)
+ -multiple delete/open/props/etc
+ -delete + enter + arrow keys.
+ -try to solve mysteries w/ShellExecuteEx() and desktop *shortcuts* (*.lnk).
+ -(notice that when icons are selected, they get modulated by the
+ highlight color, when they should be blended 50% with that color.)
+
+ ---------------------------
+ final touches:
+ -Tests:
+ -make sure desktop still functions/responds properly when winamp paused
+ -desktop mode + multimon:
+ -try desktop mode on all monitors
+ -try moving taskbar around; make sure icons are in the
+ right place, that context menus (general & for
+ specific icons) pop up in the right place, and that
+ text-off-left-edge is ok.
+ -try setting the 2 monitors to different/same resolutions
+ -check tab order of config panel controls!
+ -Clean All
+ -build in release mode to include in the ZIP
+ -leave only one file open in workspace: README.TXT.
+ -TEMPORARILY "ATTRIB -R" ALL FILES BEFORE ZIPPING THEM!
+
+ ---------------------------
+ KEEP IN VIEW:
+ -EMBEDWND:
+ -kiv: on resize of embedwnd, it's out of our control; winamp
+ resizes the child every time the mouse position changes,
+ and we have to cleanup & reallocate everything, b/c we
+ can't tell when the resize begins & ends.
+ [justin said he'd fix in wa5, though]
+ -kiv: with embedded windows of any type (plugin, playlist, etc.)
+ you can't place the winamp main wnd over them.
+ -kiv: embedded windows are child windows and don't get the
+ WM_SETFOCUS or WM_KILLFOCUS messages when they get or lose
+ the focus. (For a workaround, see milkdrop & scroll lock key.)
+ -kiv: tiny bug (IGNORE): when switching between embedwnd &
+ no-embedding, the window gets scooted a tiny tiny bit.
+ -kiv: fake fullscreen mode w/multiple monitors: there is no way
+ to keep the taskbar from popping up [potentially overtop of
+ the plugin] when you click on something besides the plugin.
+ To get around this, use true fullscreen mode.
+ -kiv: max_fps implementation assumptions:
+ -that most computers support high-precision timer
+ -that no computers [regularly] sleep for more than 1-2 ms
+ when you call Sleep(1) after timeBeginPeriod(1).
+ -reminder: if vms_desktop.dll's interface needs changed,
+ it will have to be renamed! (version # upgrades are ok
+ as long as it won't break on an old version; if the
+ new functionality is essential, rename the DLL.)
+
+ ---------------------------
+ REMEMBER:
+ -GF2MX + GF4 have icon scooting probs in desktop mode
+ (when taskbar is on upper or left edge of screen)
+ -Radeon is the one w/super slow text probs @ 1280x1024.
+ (it goes unstable after you show playlist AND helpscr; -> ~1 fps)
+ -Mark's win98 machine has hidden cursor (in all modes),
+ but no one else seems to have this problem.
+ -links:
+ -win2k-only-style desktop mode: (uses VirtualAllocEx, vs. DLL Injection)
+ http://www.digiwar.com/scripts/renderpage.php?section=2&subsection=2
+ -http://www.experts-exchange.com/Programming/Programming_Platforms/Win_Prog/Q_20096218.html
+*/
+
+#include "api__vis_milk2.h"
+#include "pluginshell.h"
+#include "utility.h"
+#include "defines.h"
+#include "shell_defines.h"
+#include "resource.h"
+#include "vis.h"
+#include <multimon.h>
+#include "../Winamp/wa_ipc.h"
+#include "../nu/AutoCharFn.h"
+#include <mmsystem.h>
+#pragma comment(lib,"winmm.lib") // for timeGetTime
+
+// STATE VALUES & VERTEX FORMATS FOR HELP SCREEN TEXTURE:
+#define TEXT_SURFACE_NOT_READY 0
+#define TEXT_SURFACE_REQUESTED 1
+#define TEXT_SURFACE_READY 2
+#define TEXT_SURFACE_ERROR 3
+typedef struct _HELPVERTEX
+{
+ float x, y; // screen position
+ float z; // Z-buffer depth
+ DWORD Diffuse; // diffuse color. also acts as filler; aligns struct to 16 bytes (good for random access/indexed prims)
+ float tu, tv; // texture coordinates for texture #0
+} HELPVERTEX, *LPHELPVERTEX;
+#define HELP_VERTEX_FORMAT (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1)
+typedef struct _SIMPLEVERTEX
+{
+ float x, y; // screen position
+ float z; // Z-buffer depth
+ DWORD Diffuse; // diffuse color. also acts as filler; aligns struct to 16 bytes (good for random access/indexed prims)
+} SIMPLEVERTEX, *LPSIMPLEVERTEX;
+#define SIMPLE_VERTEX_FORMAT (D3DFVF_XYZ | D3DFVF_DIFFUSE)
+
+extern wchar_t* g_szHelp;
+extern int g_szHelp_W;
+extern winampVisModule mod1;
+
+// resides in vms_desktop.dll/lib:
+void getItemData(int x);
+
+
+CPluginShell::CPluginShell()
+{
+ // this should remain empty!
+}
+
+CPluginShell::~CPluginShell()
+{
+ // this should remain empty!
+}
+
+eScrMode CPluginShell::GetScreenMode()
+{
+ return m_screenmode;
+}
+int CPluginShell::GetFrame()
+{
+ return m_frame;
+}
+float CPluginShell::GetTime()
+{
+ return m_time;
+}
+float CPluginShell::GetFps()
+{
+ return m_fps;
+}
+HWND CPluginShell::GetPluginWindow()
+{
+ if (m_lpDX) return m_lpDX->GetHwnd(); else return NULL;
+}
+int CPluginShell::GetWidth()
+{
+ if (m_lpDX) return m_lpDX->m_client_width; else return 0;
+}
+int CPluginShell::GetHeight()
+{
+ if (m_lpDX) return m_lpDX->m_client_height; else return 0;
+}
+int CPluginShell::GetCanvasMarginX()
+{
+ if (m_lpDX && m_screenmode==WINDOWED) return (m_lpDX->m_client_width - m_lpDX->m_REAL_client_width)/2; else return 0;
+}
+int CPluginShell::GetCanvasMarginY()
+{
+ if (m_lpDX && m_screenmode==WINDOWED) return (m_lpDX->m_client_height - m_lpDX->m_REAL_client_height)/2; else return 0;
+}
+HWND CPluginShell::GetWinampWindow()
+{
+ return m_hWndWinamp;
+}
+HINSTANCE CPluginShell::GetInstance()
+{
+ return m_hInstance;
+}
+wchar_t* CPluginShell::GetPluginsDirPath()
+{
+ return m_szPluginsDirPath;
+}
+wchar_t* CPluginShell::GetConfigIniFile()
+{
+ return m_szConfigIniFile;
+}
+char* CPluginShell::GetConfigIniFileA()
+{
+ return m_szConfigIniFileA;
+}
+int CPluginShell::GetFontHeight(eFontIndex idx)
+{
+ if (idx >= 0 && idx < NUM_BASIC_FONTS + NUM_EXTRA_FONTS) return m_fontinfo[idx].nSize; else return 0;
+}
+int CPluginShell::GetBitDepth()
+{
+ return m_lpDX->GetBitDepth();
+}
+LPDIRECT3DDEVICE9 CPluginShell::GetDevice()
+{
+ if (m_lpDX) return m_lpDX->m_lpDevice; else return NULL;
+}
+D3DCAPS9* CPluginShell::GetCaps()
+{
+ if (m_lpDX) return &(m_lpDX->m_caps); else return NULL;
+}
+D3DFORMAT CPluginShell::GetBackBufFormat()
+{
+ if (m_lpDX) return m_lpDX->m_current_mode.display_mode.Format; else return D3DFMT_UNKNOWN;
+}
+D3DFORMAT CPluginShell::GetBackBufZFormat()
+{
+ if (m_lpDX) return m_lpDX->GetZFormat(); else return D3DFMT_UNKNOWN;
+}
+LPD3DXFONT CPluginShell::GetFont(eFontIndex idx)
+{
+ if (idx >= 0 && idx < NUM_BASIC_FONTS + NUM_EXTRA_FONTS) return m_d3dx_font[idx]; else return NULL;
+}
+char* CPluginShell::GetDriverFilename()
+{
+ if (m_lpDX) return m_lpDX->GetDriver(); else return NULL;
+}
+char* CPluginShell::GetDriverDescription()
+{
+ if (m_lpDX) return m_lpDX->GetDesc(); else return NULL;
+}
+
+int CPluginShell::InitNondx9Stuff()
+{
+ timeBeginPeriod(1);
+ m_fftobj.Init(576, NUM_FREQUENCIES);
+ if (!InitGDIStuff()) return false;
+ return AllocateMyNonDx9Stuff();
+}
+
+void CPluginShell::CleanUpNondx9Stuff()
+{
+ timeEndPeriod(1);
+ CleanUpMyNonDx9Stuff();
+ CleanUpGDIStuff();
+ m_fftobj.CleanUp();
+}
+
+int CPluginShell::InitGDIStuff()
+{
+ wchar_t title[64];
+ // note: messagebox parent window should be NULL here, because lpDX is still NULL!
+ for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++)
+ {
+ 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)))
+ {
+ MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_CREATING_GDI_FONTS),
+ WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
+ MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
+ return false;
+ }
+ }
+
+ if (!(m_main_menu = WASABI_API_LOADMENU(IDR_WINDOWED_CONTEXT_MENU)))
+ {
+ MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_LOADING_MAIN_MENU),
+ WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
+ MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
+ return false;
+ }
+
+ if (!(m_context_menu = GetSubMenu(m_main_menu, 0)))
+ {
+ MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_LOADING_CONTEXT_MENU),
+ WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
+ MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
+ return false;
+ }
+
+ return true;
+}
+
+void CPluginShell::CleanUpGDIStuff()
+{
+ for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++)
+ {
+ if (m_font[i])
+ {
+ DeleteObject(m_font[i]);
+ m_font[i] = NULL;
+ }
+ }
+
+ /*if (m_context_menu)
+ {
+ DestroyMenu(m_context_menu);
+ m_context_menu = NULL;
+ }*/
+
+ if (m_main_menu)
+ {
+ DestroyMenu(m_main_menu);
+ m_main_menu = NULL;
+ }
+
+ //CleanUpMyGDIStuff();
+}
+
+int CPluginShell::InitVJStuff(RECT* pClientRect)
+{
+ wchar_t title[64];
+ // Init VJ mode (second window for text):
+ if (m_vj_mode)
+ {
+ DWORD dwStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_SYSMENU;
+ POINT upper_left_corner;
+ upper_left_corner.x = 0;
+ upper_left_corner.y = 0;
+
+ // Create direct 3d & get some infos
+ if (!(m_vjd3d9 = Direct3DCreate9(D3D_SDK_VERSION)))
+ {
+ MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_CREATING_DIRECT3D_DEVICE_FOR_VJ_MODE),
+ WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64), MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
+ return false;
+ }
+
+ // Get ordinal adapter # for the currently-selected Windowed Mode display adapter
+ int ordinal_adapter = D3DADAPTER_DEFAULT;
+ int nAdapters = m_vjd3d9->GetAdapterCount();
+ for (int i=0; i<nAdapters; i++)
+ {
+ D3DADAPTER_IDENTIFIER9 temp;
+ if ((m_vjd3d9->GetAdapterIdentifier(i, /*D3DENUM_NO_WHQL_LEVEL*/ 0, &temp) == D3D_OK) &&
+ (memcmp(&temp.DeviceIdentifier, &m_adapter_guid_windowed, sizeof(GUID))==0))
+ {
+ ordinal_adapter = i;
+ break;
+ }
+ }
+
+ // Get current display mode for windowed-mode adapter:
+ D3DDISPLAYMODE dm;
+ if (D3D_OK != m_vjd3d9->GetAdapterDisplayMode(ordinal_adapter, &dm))
+ {
+ MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_VJ_MODE_INIT_ERROR),
+ WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
+ MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
+ return false;
+ }
+
+ // And get the upper-left corner of the monitor for it:
+ HMONITOR hMon = m_vjd3d9->GetAdapterMonitor(ordinal_adapter);
+ if (hMon)
+ {
+ MONITORINFO mi;
+ mi.cbSize = sizeof(mi);
+ if (GetMonitorInfo(hMon, &mi))
+ {
+ upper_left_corner.x = mi.rcWork.left;
+ upper_left_corner.y = mi.rcWork.top;
+ }
+ }
+
+ // CREATE THE WINDOW
+
+ RECT rect;
+ if (pClientRect)
+ {
+ rect = *pClientRect;
+ AdjustWindowRect(&rect, dwStyle, 0); // convert client->wnd
+ }
+ else
+ {
+ SetRect(&rect, 0, 0, 384, 384);
+ AdjustWindowRect(&rect, dwStyle, 0); // convert client->wnd
+
+ rect.right -= rect.left;
+ rect.left = 0;
+ rect.bottom -= rect.top;
+ rect.top = 0;
+
+ rect.top += upper_left_corner.y+32;
+ rect.left += upper_left_corner.x+32;
+ rect.right += upper_left_corner.x+32;
+ rect.bottom += upper_left_corner.y+32;
+ }
+
+ WNDCLASS wc = {0};
+ wc.lpfnWndProc = VJModeWndProc; // our window procedure
+ wc.hInstance = GetInstance(); // hInstance of DLL
+ wc.hIcon = LoadIcon(GetInstance(), MAKEINTRESOURCE(IDI_PLUGIN_ICON));
+ wc.lpszClassName = TEXT_WINDOW_CLASSNAME; // our window class name
+ wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; // CS_DBLCLKS lets the window receive WM_LBUTTONDBLCLK, for toggling fullscreen mode...
+ wc.cbWndExtra = sizeof(DWORD);
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
+
+ if (!RegisterClass(&wc))
+ {
+ MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_REGISTERING_WINDOW_CLASS_FOR_TEXT_WINDOW),
+ WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
+ MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
+ return false;
+ }
+ m_bTextWindowClassRegistered = true;
+
+ //DWORD nThreadID;
+ //CreateThread(NULL, 0, TextWindowThread, &rect, 0, &nThreadID);
+
+ // Create the text window
+ m_hTextWnd = CreateWindowEx(
+ 0,
+ TEXT_WINDOW_CLASSNAME, // our window class name
+ TEXT_WINDOW_CLASSNAME, // use description for a window title
+ dwStyle,
+ rect.left, rect.top, // screen position (read from config)
+ rect.right - rect.left, rect.bottom - rect.top, // width & height of window (need to adjust client area later)
+ NULL, // parent window (winamp main window)
+ NULL, // no menu
+ GetInstance(), // hInstance of DLL
+ NULL
+ ); // no window creation data
+
+ if (!m_hTextWnd)
+ {
+ MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_CREATING_VJ_WINDOW),
+ WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
+ MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
+ return false;
+ }
+
+ SetWindowLongPtr(m_hTextWnd, GWLP_USERDATA, (LONG_PTR)this);
+
+ GetClientRect(m_hTextWnd, &rect);
+ m_nTextWndWidth = rect.right-rect.left;
+ m_nTextWndHeight = rect.bottom-rect.top;
+
+
+ // Create the device
+ D3DPRESENT_PARAMETERS pres_param;
+ ZeroMemory(&pres_param,sizeof(pres_param));
+ pres_param.BackBufferCount = 0;
+ pres_param.BackBufferFormat = dm.Format;
+ pres_param.BackBufferWidth = rect.right - rect.left;
+ pres_param.BackBufferHeight = rect.bottom - rect.top;
+ pres_param.hDeviceWindow = m_hTextWnd;
+ pres_param.AutoDepthStencilFormat = D3DFMT_D16;
+ pres_param.EnableAutoDepthStencil = FALSE;
+ pres_param.SwapEffect = D3DSWAPEFFECT_DISCARD;
+ pres_param.MultiSampleType = D3DMULTISAMPLE_NONE;
+ pres_param.Flags = 0;
+ pres_param.FullScreen_RefreshRateInHz = 0;
+ 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;
+ //pres_param.FullScreen_PresentationInterval = 0;
+ pres_param.Windowed = TRUE;
+
+ HRESULT hr;
+ if (D3D_OK != (hr = m_vjd3d9->CreateDevice(ordinal_adapter,//D3DADAPTER_DEFAULT,
+ D3DDEVTYPE_HAL,
+ m_hTextWnd,
+ D3DCREATE_SOFTWARE_VERTEXPROCESSING,
+ &pres_param,
+ &m_vjd3d9_device)))
+ {
+ m_vjd3d9_device = NULL;
+ MessageBoxW(m_lpDX->GetHwnd(), WASABI_API_LNGSTRINGW(IDS_ERROR_CREATING_D3D_DEVICE_FOR_VJ_MODE),
+ WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
+ MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
+ return false;
+ }
+
+ if (!AllocateFonts(m_vjd3d9_device))
+ return false;
+
+ if (m_fix_slow_text) // note that when not doing vj mode, m_lpDDSText is allocated in AllocateDX9Stuff
+ AllocateTextSurface();
+
+ m_text.Finish();
+ m_text.Init(m_vjd3d9_device, m_lpDDSText, 0);
+
+ m_bClearVJWindow = true;
+ }
+
+ return true;
+}
+
+void CPluginShell::CleanUpVJStuff()
+{
+ // ALWAYS set the textures to NULL before releasing textures,
+ // otherwise they might still have a hanging reference!
+ if (m_lpDX && m_lpDX->m_lpDevice)
+ {
+ for (int i=0; i<16; i++)
+ m_lpDX->m_lpDevice->SetTexture(i, NULL);
+ }
+
+ if (m_vjd3d9_device)
+ {
+ for (int i=0; i<16; i++)
+ m_vjd3d9_device->SetTexture(i, NULL);
+ }
+
+ if (!m_vj_mode)
+ return;
+
+ // clean up VJ mode
+ {
+ CleanUpFonts();
+ SafeRelease(m_lpDDSText);
+
+ SafeRelease(m_vjd3d9_device);
+ SafeRelease(m_vjd3d9);
+
+ if (m_hTextWnd)
+ {
+ //dumpmsg("Finish: destroying text window");
+ DestroyWindow(m_hTextWnd);
+ m_hTextWnd = NULL;
+ //dumpmsg("Finish: text window destroyed");
+ }
+
+ if (m_bTextWindowClassRegistered)
+ {
+ //dumpmsg("Finish: unregistering text window class");
+ UnregisterClass(TEXT_WINDOW_CLASSNAME,GetInstance()); // unregister window class
+ m_bTextWindowClassRegistered = false;
+ //dumpmsg("Finish: text window class unregistered");
+ }
+ }
+}
+
+int CPluginShell::AllocateFonts(IDirect3DDevice9* pDevice)
+{
+ // Create D3DX system font:
+ for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++)
+ if (pCreateFontW(pDevice, //m_font[i],
+ m_fontinfo[i].nSize,
+ m_fontinfo[i].nSize*4/10,
+ m_fontinfo[i].bBold ? 900 : 400,
+ 1, // mip levels
+ m_fontinfo[i].bItalic,
+ DEFAULT_CHARSET,
+ OUT_DEFAULT_PRECIS,
+ m_fontinfo[i].bAntiAliased ? ANTIALIASED_QUALITY : DEFAULT_QUALITY,
+ DEFAULT_PITCH,
+ m_fontinfo[i].szFace,
+ &m_d3dx_font[i]
+ ) != D3D_OK)
+ {
+ wchar_t title[64];
+ MessageBoxW(m_lpDX ? m_lpDX->GetHwnd() : NULL, WASABI_API_LNGSTRINGW(IDS_ERROR_CREATING_D3DX_FONTS),
+ WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
+ MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
+ return false;
+ }
+
+ // get actual font heights
+ for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++)
+ {
+ RECT r;
+ SetRect(&r, 0, 0, 1024, 1024);
+ int h = m_d3dx_font[i]->DrawText(NULL, "M", -1, &r, DT_CALCRECT, 0xFFFFFFFF);
+ if (h>0) m_fontinfo[i].nSize = h;
+ }
+
+ return true;
+}
+
+void CPluginShell::CleanUpFonts()
+{
+ for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++)
+ SafeRelease(m_d3dx_font[i]);
+}
+
+void CPluginShell::AllocateTextSurface()
+{
+ IDirect3DDevice9 *pDevice = m_vjd3d9_device ? m_vjd3d9_device : GetDevice();
+ int w = m_vjd3d9_device ? m_nTextWndWidth : GetWidth() ;
+ int h = m_vjd3d9_device ? m_nTextWndHeight : GetHeight();
+
+ if (D3D_OK != pCreateTexture(pDevice, w, h, 1, D3DUSAGE_RENDERTARGET, GetBackBufFormat(), D3DPOOL_DEFAULT, &m_lpDDSText))
+ m_lpDDSText = NULL; // OK if there's not enough mem for it!
+ else
+ {
+ // if m_lpDDSText doesn't cover enough of screen, cancel it.
+ D3DSURFACE_DESC desc;
+ if (D3D_OK == m_lpDDSText->GetLevelDesc(0, &desc))
+ {
+ if ((desc.Width < 256 && w >= 256) ||
+ (desc.Height < 256 && h >= 256) ||
+ (desc.Width /(float)w < 0.74f) ||
+ (desc.Height/(float)h < 0.74f)
+ )
+ {
+ m_lpDDSText->Release();
+ m_lpDDSText = NULL;
+ }
+ }
+ }
+}
+
+int CPluginShell::AllocateDX9Stuff()
+{
+ if (!m_vj_mode)
+ {
+ AllocateFonts(m_lpDX->m_lpDevice);
+ if (m_fix_slow_text) // note that when not doing vj mode, m_lpDDSText is allocated in AllocateDX9Stuff
+ AllocateTextSurface();
+ }
+
+ /*
+ // Create D3DX system font:
+ for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++)
+ if (pCreateFontW(m_lpDX->m_lpDevice,
+ m_fontinfo[i].nSize,
+ m_fontinfo[i].nSize*4/10,
+ m_fontinfo[i].bBold ? 900 : 400,
+ 0, // mip levels
+ m_fontinfo[i].bItalic,
+ DEFAULT_CHARSET,
+ OUT_DEFAULT_PRECIS,
+ m_fontinfo[i].bAntiAliased ? ANTIALIASED_QUALITY : DEFAULT_QUALITY,
+ DEFAULT_PITCH,
+ m_fontinfo[i].szFace,
+ &m_d3dx_font[i]
+ ) != D3D_OK)
+ {
+ MessageBox(m_lpDX->GetHwnd(), "Error creating D3DX fonts", "ERROR", MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
+ return false;
+ }
+
+ // get actual font heights
+ for (i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++)
+ {
+ RECT r;
+ SetRect(&r, 0, 0, 1024, 1024);
+ int h = m_d3dx_font[i]->DrawText(NULL, "M", -1, &r, DT_CALCRECT, 0xFFFFFFFF);
+ if (h>0) m_fontinfo[i].nSize = h;
+ }
+ */
+
+ if (m_screenmode == DESKTOP)
+ if (!InitDesktopMode())
+ return false;
+
+ int ret = AllocateMyDX9Stuff();
+
+ // invalidate various 'caches' here:
+ m_playlist_top_idx = -1; // invalidating playlist cache forces recompute of playlist width
+ //m_icon_list.clear(); // clear desktop mode icon list, so it has to read the bitmaps back in
+
+ if (!m_vj_mode)
+ {
+ m_text.Finish();
+ m_text.Init(GetDevice(), m_lpDDSText, 1);
+ }
+
+ return ret;
+}
+
+void CPluginShell::CleanUpDX9Stuff(int final_cleanup)
+{
+ // ALWAYS unbind the textures before releasing textures,
+ // otherwise they might still have a hanging reference!
+ if (m_lpDX && m_lpDX->m_lpDevice)
+ {
+ for (int i=0; i<16; i++)
+ m_lpDX->m_lpDevice->SetTexture(i, NULL);
+ }
+
+ if (m_screenmode == DESKTOP)
+ CleanUpDesktopMode();
+
+ if (!m_vj_mode)
+ {
+ for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++)
+ SafeRelease(m_d3dx_font[i]);
+ SafeRelease(m_lpDDSText);
+ }
+
+ CleanUpMyDX9Stuff(final_cleanup);
+}
+
+void CPluginShell::OnUserResizeTextWindow()
+{
+ // Update window properties
+ RECT w, c;
+ GetWindowRect(m_hTextWnd, &w);
+ GetClientRect(m_hTextWnd, &c);
+
+ WINDOWPLACEMENT wp;
+ ZeroMemory(&wp, sizeof(wp));
+ wp.length = sizeof(wp);
+ GetWindowPlacement(m_hTextWnd, &wp);
+
+ // convert client rect from client coords to screen coords:
+ // (window rect is already in screen coords...)
+ POINT p;
+ p.x = c.left;
+ p.y = c.top;
+ if (ClientToScreen(m_hTextWnd, &p))
+ {
+ c.left += p.x;
+ c.right += p.x;
+ c.top += p.y;
+ c.bottom += p.y;
+ }
+
+ if (wp.showCmd != SW_SHOWMINIMIZED)
+ {
+ if (m_nTextWndWidth != c.right-c.left ||
+ m_nTextWndHeight != c.bottom-c.top)
+ {
+ CleanUpVJStuff();
+ if (!InitVJStuff(&c))
+ {
+ SuggestHowToFreeSomeMem();
+ m_lpDX->m_ready = false; // flag to exit
+ return;
+ }
+ }
+
+ // save the new window position:
+ //if (wp.showCmd==SW_SHOWNORMAL)
+ // SaveTextWindowPos();
+ }
+}
+
+void CPluginShell::OnUserResizeWindow()
+{
+ // Update window properties
+ RECT w, c;
+ GetWindowRect(m_lpDX->GetHwnd(), &w);
+ GetClientRect(m_lpDX->GetHwnd(), &c);
+
+ WINDOWPLACEMENT wp;
+ ZeroMemory(&wp, sizeof(wp));
+ wp.length = sizeof(wp);
+ GetWindowPlacement(m_lpDX->GetHwnd(), &wp);
+
+ // convert client rect from client coords to screen coords:
+ // (window rect is already in screen coords...)
+ POINT p;
+ p.x = c.left;
+ p.y = c.top;
+ if (ClientToScreen(m_lpDX->GetHwnd(), &p))
+ {
+ c.left += p.x;
+ c.right += p.x;
+ c.top += p.y;
+ c.bottom += p.y;
+ }
+
+ if (wp.showCmd != SW_SHOWMINIMIZED)
+ {
+ int new_REAL_client_w = c.right-c.left;
+ int new_REAL_client_h = c.bottom-c.top;
+
+ // kiv: could we just resize when the *snapped* w/h changes? slightly more ideal...
+ if (m_lpDX->m_REAL_client_width != new_REAL_client_w ||
+ m_lpDX->m_REAL_client_height != new_REAL_client_h)
+ {
+ //CleanUpVJStuff();
+ CleanUpDX9Stuff(0);
+ if (!m_lpDX->OnUserResizeWindow(&w, &c))
+ {
+ // note: a basic warning messagebox will have already been given.
+ // now suggest specific advice on how to regain more video memory:
+ SuggestHowToFreeSomeMem();
+ return;
+ }
+ if (!AllocateDX9Stuff())
+ {
+ m_lpDX->m_ready = false; // flag to exit
+ return;
+ }
+ /*if (!InitVJStuff())
+ {
+ m_lpDX->m_ready = false; // flag to exit
+ return;
+ }*/
+ }
+
+ // save the new window position:
+ if (wp.showCmd==SW_SHOWNORMAL)
+ m_lpDX->SaveWindow();
+ }
+}
+
+void CPluginShell::StuffParams(DXCONTEXT_PARAMS *pParams)
+{
+ pParams->screenmode = m_screenmode;
+ pParams->display_mode = m_disp_mode_fs;
+ pParams->nbackbuf = 1;
+ pParams->m_dualhead_horz = m_dualhead_horz;
+ pParams->m_dualhead_vert = m_dualhead_vert;
+ pParams->m_skin = (m_screenmode==WINDOWED) ? m_skin : 0;
+ switch (m_screenmode)
+ {
+ case WINDOWED:
+ pParams->allow_page_tearing = m_allow_page_tearing_w;
+ pParams->adapter_guid = m_adapter_guid_windowed;
+ pParams->multisamp = m_multisample_windowed;
+ strcpy(pParams->adapter_devicename, m_adapter_devicename_windowed);
+ break;
+ case FULLSCREEN:
+ case FAKE_FULLSCREEN:
+ pParams->allow_page_tearing = m_allow_page_tearing_fs;
+ pParams->adapter_guid = m_adapter_guid_fullscreen;
+ pParams->multisamp = m_multisample_fullscreen;
+ strcpy(pParams->adapter_devicename, m_adapter_devicename_fullscreen);
+ break;
+ case DESKTOP:
+ pParams->allow_page_tearing = m_allow_page_tearing_dm;
+ pParams->adapter_guid = m_adapter_guid_desktop;
+ pParams->multisamp = m_multisample_desktop;
+ strcpy(pParams->adapter_devicename, m_adapter_devicename_desktop);
+ break;
+ }
+ pParams->parent_window = (m_screenmode==DESKTOP) ? m_hWndDesktopListView : NULL;
+}
+
+void CPluginShell::ToggleDesktop()
+{
+ CleanUpDX9Stuff(0);
+
+ switch (m_screenmode)
+ {
+ case WINDOWED:
+ case FULLSCREEN:
+ case FAKE_FULLSCREEN:
+ m_screenmode = DESKTOP;
+ break;
+ case DESKTOP:
+ m_screenmode = WINDOWED;
+ break;
+ }
+
+ DXCONTEXT_PARAMS params;
+ StuffParams(&params);
+
+ if (!m_lpDX->StartOrRestartDevice(&params))
+ {
+ // note: a basic warning messagebox will have already been given.
+ if (m_lpDX->m_lastErr == DXC_ERR_CREATEDEV_PROBABLY_OUTOFVIDEOMEMORY)
+ SuggestHowToFreeSomeMem();
+ return;
+ }
+
+ if (!AllocateDX9Stuff())
+ {
+ m_lpDX->m_ready = false; // flag to exit
+ return;
+ }
+
+ SetForegroundWindow(m_lpDX->GetHwnd());
+ SetActiveWindow(m_lpDX->GetHwnd());
+ SetFocus(m_lpDX->GetHwnd());
+}
+
+#define IPC_IS_PLAYING_VIDEO 501 // from wa_ipc.h
+#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
+
+void CPluginShell::ToggleFullScreen()
+{
+ CleanUpDX9Stuff(0);
+
+ switch (m_screenmode)
+ {
+ case DESKTOP:
+ case WINDOWED:
+ m_screenmode = m_fake_fullscreen_mode ? FAKE_FULLSCREEN : FULLSCREEN;
+ if (m_screenmode == FULLSCREEN && SendMessage(GetWinampWindow(), WM_WA_IPC, 0, IPC_IS_PLAYING_VIDEO) > 1)
+ {
+ m_screenmode = FAKE_FULLSCREEN;
+ }
+ SendMessage(GetWinampWindow(), WM_WA_IPC, 1, IPC_SET_VIS_FS_FLAG);
+ break;
+ case FULLSCREEN:
+ case FAKE_FULLSCREEN:
+ m_screenmode = WINDOWED;
+ SendMessage(GetWinampWindow(), WM_WA_IPC, 0, IPC_SET_VIS_FS_FLAG);
+ break;
+ }
+
+ DXCONTEXT_PARAMS params;
+ StuffParams(&params);
+
+ if (!m_lpDX->StartOrRestartDevice(&params))
+ {
+ // note: a basic warning messagebox will have already been given.
+ if (m_lpDX->m_lastErr == DXC_ERR_CREATEDEV_PROBABLY_OUTOFVIDEOMEMORY)
+ SuggestHowToFreeSomeMem();
+ return;
+ }
+
+ if (!AllocateDX9Stuff())
+ {
+ m_lpDX->m_ready = false; // flag to exit
+ return;
+ }
+
+ SetForegroundWindow(m_lpDX->GetHwnd());
+ SetActiveWindow(m_lpDX->GetHwnd());
+ SetFocus(m_lpDX->GetHwnd());
+}
+
+void CPluginShell::ToggleHelp()
+{
+ m_show_help = 1-m_show_help;
+ int ret = CheckMenuItem(m_context_menu, ID_SHOWHELP, MF_BYCOMMAND | (m_show_help ? MF_CHECKED : MF_UNCHECKED));
+}
+
+void CPluginShell::TogglePlaylist()
+{
+ m_show_playlist = 1-m_show_playlist;
+ m_playlist_top_idx = -1; // <- invalidates playlist cache
+ int ret = CheckMenuItem(m_context_menu, ID_SHOWPLAYLIST, MF_BYCOMMAND | (m_show_playlist ? MF_CHECKED : MF_UNCHECKED));
+}
+
+int CPluginShell::InitDirectX()
+{
+ m_lpDX = new DXContext(m_hWndWinamp,m_hInstance,CLASSNAME,WINDOWCAPTION,CPluginShell::WindowProc,(LONG_PTR)this, m_minimize_winamp, m_szConfigIniFile);
+
+ if (!m_lpDX)
+ {
+ wchar_t title[64];
+ MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_UNABLE_TO_INIT_DXCONTEXT),
+ WASABI_API_LNGSTRINGW_BUF(IDS_MILKDROP_ERROR, title, 64),
+ MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
+ return FALSE;
+ }
+
+ if (m_lpDX->m_lastErr != S_OK)
+ {
+ // warning messagebox will have already been given
+ delete m_lpDX;
+ return FALSE;
+ }
+
+ // initialize graphics
+ DXCONTEXT_PARAMS params;
+ StuffParams(&params);
+
+ if (!m_lpDX->StartOrRestartDevice(&params))
+ {
+ // note: a basic warning messagebox will have already been given.
+
+ if (m_lpDX->m_lastErr == DXC_ERR_CREATEDEV_PROBABLY_OUTOFVIDEOMEMORY)
+ {
+ // suggest specific advice on how to regain more video memory:
+ SuggestHowToFreeSomeMem();
+ }
+
+ delete m_lpDX;
+ m_lpDX = NULL;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void CPluginShell::CleanUpDirectX()
+{
+ SafeDelete(m_lpDX);
+}
+
+int CPluginShell::PluginPreInitialize(HWND hWinampWnd, HINSTANCE hWinampInstance)
+{
+ // PROTECTED CONFIG PANEL SETTINGS (also see 'private' settings, below)
+ m_start_fullscreen = 0;
+ m_start_desktop = 0;
+ m_fake_fullscreen_mode = 0;
+ m_max_fps_fs = 30;
+ m_max_fps_dm = 30;
+ m_max_fps_w = 30;
+ m_show_press_f1_msg = 1;
+ m_allow_page_tearing_w = 1;
+ m_allow_page_tearing_fs = 0;
+ m_allow_page_tearing_dm = 0;
+ m_minimize_winamp = 1;
+ m_desktop_show_icons = 1;
+ m_desktop_textlabel_boxes = 1;
+ m_desktop_manual_icon_scoot = 0;
+ m_desktop_555_fix = 2;
+ m_dualhead_horz = 2;
+ m_dualhead_vert = 1;
+ m_save_cpu = 1;
+ m_skin = 1;
+ m_fix_slow_text = 0;
+
+ // initialize font settings:
+ wcscpy(m_fontinfo[SIMPLE_FONT ].szFace, SIMPLE_FONT_DEFAULT_FACE);
+ m_fontinfo[SIMPLE_FONT ].nSize = SIMPLE_FONT_DEFAULT_SIZE ;
+ m_fontinfo[SIMPLE_FONT ].bBold = SIMPLE_FONT_DEFAULT_BOLD ;
+ m_fontinfo[SIMPLE_FONT ].bItalic = SIMPLE_FONT_DEFAULT_ITAL ;
+ m_fontinfo[SIMPLE_FONT ].bAntiAliased = SIMPLE_FONT_DEFAULT_AA ;
+ wcscpy(m_fontinfo[DECORATIVE_FONT].szFace, DECORATIVE_FONT_DEFAULT_FACE);
+ m_fontinfo[DECORATIVE_FONT].nSize = DECORATIVE_FONT_DEFAULT_SIZE;
+ m_fontinfo[DECORATIVE_FONT].bBold = DECORATIVE_FONT_DEFAULT_BOLD;
+ m_fontinfo[DECORATIVE_FONT].bItalic = DECORATIVE_FONT_DEFAULT_ITAL;
+ m_fontinfo[DECORATIVE_FONT].bAntiAliased = DECORATIVE_FONT_DEFAULT_AA ;
+ wcscpy(m_fontinfo[HELPSCREEN_FONT].szFace, HELPSCREEN_FONT_DEFAULT_FACE);
+ m_fontinfo[HELPSCREEN_FONT].nSize = HELPSCREEN_FONT_DEFAULT_SIZE;
+ m_fontinfo[HELPSCREEN_FONT].bBold = HELPSCREEN_FONT_DEFAULT_BOLD;
+ m_fontinfo[HELPSCREEN_FONT].bItalic = HELPSCREEN_FONT_DEFAULT_ITAL;
+ m_fontinfo[HELPSCREEN_FONT].bAntiAliased = HELPSCREEN_FONT_DEFAULT_AA ;
+ wcscpy(m_fontinfo[PLAYLIST_FONT ].szFace, PLAYLIST_FONT_DEFAULT_FACE);
+ m_fontinfo[PLAYLIST_FONT ].nSize = PLAYLIST_FONT_DEFAULT_SIZE;
+ m_fontinfo[PLAYLIST_FONT ].bBold = PLAYLIST_FONT_DEFAULT_BOLD;
+ m_fontinfo[PLAYLIST_FONT ].bItalic = PLAYLIST_FONT_DEFAULT_ITAL;
+ m_fontinfo[PLAYLIST_FONT ].bAntiAliased = PLAYLIST_FONT_DEFAULT_AA ;
+
+#if (NUM_EXTRA_FONTS >= 1)
+ wcscpy(m_fontinfo[NUM_BASIC_FONTS + 0].szFace, EXTRA_FONT_1_DEFAULT_FACE);
+ m_fontinfo[NUM_BASIC_FONTS + 0].nSize = EXTRA_FONT_1_DEFAULT_SIZE;
+ m_fontinfo[NUM_BASIC_FONTS + 0].bBold = EXTRA_FONT_1_DEFAULT_BOLD;
+ m_fontinfo[NUM_BASIC_FONTS + 0].bItalic = EXTRA_FONT_1_DEFAULT_ITAL;
+ m_fontinfo[NUM_BASIC_FONTS + 0].bAntiAliased = EXTRA_FONT_1_DEFAULT_AA;
+#endif
+#if (NUM_EXTRA_FONTS >= 2)
+ wcscpy(m_fontinfo[NUM_BASIC_FONTS + 1].szFace, EXTRA_FONT_2_DEFAULT_FACE);
+ m_fontinfo[NUM_BASIC_FONTS + 1].nSize = EXTRA_FONT_2_DEFAULT_SIZE;
+ m_fontinfo[NUM_BASIC_FONTS + 1].bBold = EXTRA_FONT_2_DEFAULT_BOLD;
+ m_fontinfo[NUM_BASIC_FONTS + 1].bItalic = EXTRA_FONT_2_DEFAULT_ITAL;
+ m_fontinfo[NUM_BASIC_FONTS + 1].bAntiAliased = EXTRA_FONT_2_DEFAULT_AA;
+#endif
+#if (NUM_EXTRA_FONTS >= 3)
+ strcpy(m_fontinfo[NUM_BASIC_FONTS + 2].szFace, EXTRA_FONT_3_DEFAULT_FACE);
+ m_fontinfo[NUM_BASIC_FONTS + 2].nSize = EXTRA_FONT_3_DEFAULT_SIZE;
+ m_fontinfo[NUM_BASIC_FONTS + 2].bBold = EXTRA_FONT_3_DEFAULT_BOLD;
+ m_fontinfo[NUM_BASIC_FONTS + 2].bItalic = EXTRA_FONT_3_DEFAULT_ITAL;
+ m_fontinfo[NUM_BASIC_FONTS + 2].bAntiAliased = EXTRA_FONT_3_DEFAULT_AA;
+#endif
+#if (NUM_EXTRA_FONTS >= 4)
+ strcpy(m_fontinfo[NUM_BASIC_FONTS + 3].szFace, EXTRA_FONT_4_DEFAULT_FACE);
+ m_fontinfo[NUM_BASIC_FONTS + 3].nSize = EXTRA_FONT_4_DEFAULT_SIZE;
+ m_fontinfo[NUM_BASIC_FONTS + 3].bBold = EXTRA_FONT_4_DEFAULT_BOLD;
+ m_fontinfo[NUM_BASIC_FONTS + 3].bItalic = EXTRA_FONT_4_DEFAULT_ITAL;
+ m_fontinfo[NUM_BASIC_FONTS + 3].bAntiAliased = EXTRA_FONT_4_DEFAULT_AA;
+#endif
+#if (NUM_EXTRA_FONTS >= 5)
+ strcpy(m_fontinfo[NUM_BASIC_FONTS + 4].szFace, EXTRA_FONT_5_DEFAULT_FACE);
+ m_fontinfo[NUM_BASIC_FONTS + 4].nSize = EXTRA_FONT_5_DEFAULT_SIZE;
+ m_fontinfo[NUM_BASIC_FONTS + 4].bBold = EXTRA_FONT_5_DEFAULT_BOLD;
+ m_fontinfo[NUM_BASIC_FONTS + 4].bItalic = EXTRA_FONT_5_DEFAULT_ITAL;
+ m_fontinfo[NUM_BASIC_FONTS + 4].bAntiAliased = EXTRA_FONT_5_DEFAULT_AA;
+#endif
+
+ m_disp_mode_fs.Width = DEFAULT_FULLSCREEN_WIDTH;
+ m_disp_mode_fs.Height = DEFAULT_FULLSCREEN_HEIGHT;
+ m_disp_mode_fs.Format = D3DFMT_UNKNOWN;
+ m_disp_mode_fs.RefreshRate = 60;
+ // better yet - in case there is no config INI file saved yet, use the current display mode (if detectable) as the default fullscreen res:
+ DEVMODE dm;
+ dm.dmSize = sizeof(dm);
+ dm.dmDriverExtra = 0;
+ if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm))
+ {
+ m_disp_mode_fs.Width = dm.dmPelsWidth;
+ m_disp_mode_fs.Height = dm.dmPelsHeight;
+ m_disp_mode_fs.RefreshRate = dm.dmDisplayFrequency;
+ m_disp_mode_fs.Format = (dm.dmBitsPerPel==16) ? D3DFMT_R5G6B5 : D3DFMT_X8R8G8B8;
+ }
+
+ // PROTECTED STRUCTURES/POINTERS
+ int i = 0;
+ for (int i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++)
+ m_d3dx_font[i] = NULL;
+ m_d3dx_desktop_font = NULL;
+ m_lpDDSText = NULL;
+ ZeroMemory(&m_sound, sizeof(td_soundinfo));
+ for (int ch=0; ch<2; ch++)
+ for (i=0; i<3; i++)
+ {
+ 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;
+ }
+
+ // GENERAL PRIVATE STUFF
+ //m_screenmode: set at end (derived setting)
+ m_frame = 0;
+ m_time = 0;
+ m_fps = 30;
+ m_hWndWinamp = hWinampWnd;
+ m_hInstance = hWinampInstance;
+ m_lpDX = NULL;
+ m_szPluginsDirPath[0] = 0; // will be set further down
+ m_szConfigIniFile[0] = 0; // will be set further down
+ // m_szPluginsDirPath:
+
+ wchar_t *p;
+
+ if (hWinampWnd
+ && (p = (wchar_t *)SendMessage(hWinampWnd, WM_WA_IPC, 0, IPC_GETPLUGINDIRECTORYW))
+ && p != (wchar_t *)1)
+ {
+ swprintf(m_szPluginsDirPath, L"%s\\", p);
+ }
+ else
+ {
+ // get path to INI file & read in prefs/settings right away, so DumpMsg works!
+ GetModuleFileNameW(m_hInstance, m_szPluginsDirPath, MAX_PATH);
+ wchar_t *p = m_szPluginsDirPath + wcslen(m_szPluginsDirPath);
+ while (p >= m_szPluginsDirPath && *p != L'\\') p--;
+ if (++p >= m_szPluginsDirPath) *p = 0;
+ }
+
+ if (hWinampWnd
+ && (p = (wchar_t *)SendMessage(hWinampWnd, WM_WA_IPC, 0, IPC_GETINIDIRECTORYW))
+ && p != (wchar_t *)1)
+ {
+ // load settings as well as coping with moving old settings to a contained folder
+ wchar_t m_szOldConfigIniFile[MAX_PATH] = {0}, temp[MAX_PATH] = {0}, temp2[MAX_PATH] = {0};
+ swprintf(m_szOldConfigIniFile, L"%s\\Plugins\\%s", p, INIFILE);
+ swprintf(m_szConfigIniFile, L"%s\\Plugins\\%s%s", p, SUBDIR, INIFILE);
+ swprintf(temp, L"%s\\Plugins\\%s", p, SUBDIR);
+ swprintf(temp2, L"%s\\Plugins\\", p);
+ CreateDirectoryW(temp, NULL);
+
+ if (PathFileExistsW(m_szOldConfigIniFile) && !PathFileExistsW(m_szConfigIniFile))
+ {
+ MoveFileW(m_szOldConfigIniFile, m_szConfigIniFile);
+
+ wchar_t m_szMsgIniFile[MAX_PATH] = {0}, m_szNewMsgIniFile[MAX_PATH] = {0},
+ m_szImgIniFile[MAX_PATH] = {0}, m_szNewImgIniFile[MAX_PATH] = {0},
+ m_szAdaptersFile[MAX_PATH] = {0}, m_szNewAdaptersFile[MAX_PATH] = {0};
+ swprintf(m_szMsgIniFile, L"%s%s", temp2, MSG_INIFILE);
+ swprintf(m_szNewMsgIniFile, L"%s%s", temp, MSG_INIFILE);
+ swprintf(m_szImgIniFile, L"%s%s", temp2, IMG_INIFILE);
+ swprintf(m_szNewImgIniFile, L"%s%s", temp, IMG_INIFILE);
+ swprintf(m_szAdaptersFile, L"%s%s", temp2, ADAPTERSFILE);
+ swprintf(m_szNewAdaptersFile, L"%s%s", temp, ADAPTERSFILE);
+
+ MoveFileW(m_szImgIniFile, m_szNewImgIniFile);
+ MoveFileW(m_szMsgIniFile, m_szNewMsgIniFile);
+ MoveFileW(m_szAdaptersFile, m_szNewAdaptersFile);
+ }
+ }
+ else
+ {
+ swprintf(m_szConfigIniFile, L"%s%s", m_szPluginsDirPath, INIFILE);
+ }
+ lstrcpyn(m_szConfigIniFileA,AutoCharFn(m_szConfigIniFile),MAX_PATH);
+
+ // PRIVATE CONFIG PANEL SETTINGS
+ m_multisample_fullscreen = D3DMULTISAMPLE_NONE;
+ m_multisample_desktop = D3DMULTISAMPLE_NONE;
+ m_multisample_windowed = D3DMULTISAMPLE_NONE;
+ ZeroMemory(&m_adapter_guid_fullscreen, sizeof(GUID));
+ ZeroMemory(&m_adapter_guid_desktop , sizeof(GUID));
+ ZeroMemory(&m_adapter_guid_windowed , sizeof(GUID));
+ m_adapter_devicename_windowed[0] = 0;
+ m_adapter_devicename_fullscreen[0] = 0;
+ m_adapter_devicename_desktop[0] = 0;
+
+
+ // PRIVATE RUNTIME SETTINGS
+ m_lost_focus = 0;
+ m_hidden = 0;
+ m_resizing = 0;
+ m_show_help = 0;
+ m_show_playlist = 0;
+ m_playlist_pos = 0;
+ m_playlist_pageups = 0;
+ m_playlist_top_idx = -1;
+ m_playlist_btm_idx = -1;
+ // m_playlist_width_pixels will be considered invalid whenever 'm_playlist_top_idx' is -1.
+ // m_playlist[256][256] will be considered invalid whenever 'm_playlist_top_idx' is -1.
+ m_exiting = 0;
+ m_upper_left_corner_y = 0;
+ m_lower_left_corner_y = 0;
+ m_upper_right_corner_y = 0;
+ m_lower_right_corner_y = 0;
+ m_left_edge = 0;
+ m_right_edge = 0;
+ m_force_accept_WM_WINDOWPOSCHANGING = 0;
+
+ // PRIVATE - GDI STUFF
+ m_main_menu = NULL;
+ m_context_menu = NULL;
+ for (i=0; i<NUM_BASIC_FONTS + NUM_EXTRA_FONTS; i++)
+ m_font[i] = NULL;
+ m_font_desktop = NULL;
+
+ // PRIVATE - DESKTOP MODE STUFF
+ m_icon_list.clear();
+ for (i=0; i<MAX_ICON_TEXTURES; i++)
+ m_desktop_icons_texture[i] = NULL;
+ FindDesktopWindows(&m_hWndProgMan, &m_hWndDesktop, &m_hWndDesktopListView);
+ GetDesktopFolder(m_szDesktopFolder);
+ m_desktop_icon_size = 32;
+ m_desktop_dragging = 0; // '1' when user is dragging icons around
+ m_desktop_box = 0; // '1' when user is drawing a box
+ m_desktop_wc_registered = 0;
+ m_desktop_bk_color = 0xFF000000 | BGR2RGB(::GetSysColor(COLOR_BACKGROUND));
+ m_desktop_text_color = 0xFF000000 | BGR2RGB(SendMessage(m_hWndDesktopListView, LVM_GETTEXTCOLOR, 0, 0));
+ m_desktop_sel_color = 0xFF000000 | BGR2RGB(::GetSysColor(COLOR_HIGHLIGHT));
+ m_desktop_sel_text_color = 0xFF000000 | BGR2RGB(::GetSysColor(COLOR_HIGHLIGHTTEXT));
+ m_desktop_icon_state = 0;
+ m_desktop_icon_count = 0;
+ m_desktop_icon_update_frame = 0;
+ m_desktop_icons_disabled = 0;
+ m_vms_desktop_loaded = 0;
+ m_desktop_hook_set = 0;
+
+ // PRIVATE - MORE TIMEKEEPING
+ m_last_raw_time = 0;
+ memset(m_time_hist, 0, sizeof(m_time_hist));
+ m_time_hist_pos = 0;
+ if (!QueryPerformanceFrequency(&m_high_perf_timer_freq))
+ m_high_perf_timer_freq.QuadPart = 0;
+ m_prev_end_of_frame.QuadPart = 0;
+
+ // PRIVATE AUDIO PROCESSING DATA
+ //(m_fftobj needs no init)
+ memset(m_oldwave[0], 0, sizeof(float)*576);
+ memset(m_oldwave[1], 0, sizeof(float)*576);
+ m_prev_align_offset[0] = 0;
+ m_prev_align_offset[1] = 0;
+ m_align_weights_ready = 0;
+
+ // SEPARATE TEXT WINDOW (FOR VJ MODE)
+ m_vj_mode = 0;
+ m_hidden_textwnd = 0;
+ m_resizing_textwnd = 0;
+ m_hTextWnd = NULL;
+ m_nTextWndWidth = 0;
+ m_nTextWndHeight = 0;
+ m_bTextWindowClassRegistered = false;
+ m_vjd3d9 = NULL;
+ m_vjd3d9_device = NULL;
+
+ //-----
+
+ m_screenmode = NOT_YET_KNOWN;
+
+ OverrideDefaults();
+ ReadConfig();
+
+ if (m_start_fullscreen)
+ {
+ m_screenmode = m_fake_fullscreen_mode ? FAKE_FULLSCREEN : FULLSCREEN;
+ if (m_screenmode == FULLSCREEN && SendMessage(GetWinampWindow(), WM_WA_IPC, 0, IPC_IS_PLAYING_VIDEO) > 1)
+ {
+ m_screenmode = FAKE_FULLSCREEN;
+ }
+ }
+ else if (m_start_desktop)
+ m_screenmode = DESKTOP;
+ else
+ m_screenmode = WINDOWED;
+
+ MyPreInitialize();
+ MyReadConfig();
+
+ //-----
+
+ return TRUE;
+}
+
+int CPluginShell::PluginInitialize()
+{
+ // note: initialize GDI before DirectX. Also separate them because
+ // when we change windowed<->fullscreen, or lose the device and restore it,
+ // we don't want to mess with any (persistent) GDI stuff.
+
+ if (!InitDirectX()) return FALSE; // gives its own error messages
+ if (!InitNondx9Stuff()) return FALSE; // gives its own error messages
+ if (!AllocateDX9Stuff()) return FALSE; // gives its own error messages
+ if (!InitVJStuff()) return FALSE;
+
+ return TRUE;
+}
+
+void CPluginShell::PluginQuit()
+{
+ CleanUpVJStuff();
+ CleanUpDX9Stuff(1);
+ CleanUpNondx9Stuff();
+ CleanUpDirectX();
+
+ SetFocus(m_hWndWinamp);
+ SetActiveWindow(m_hWndWinamp);
+ SetForegroundWindow(m_hWndWinamp);
+}
+
+wchar_t* BuildSettingName(wchar_t* name, int number){
+static wchar_t temp[64];
+ swprintf(temp, L"%s%d", name, number);
+ return temp;
+}
+
+void CPluginShell::READ_FONT(int n){
+ GetPrivateProfileStringW(L"settings",BuildSettingName(L"szFontFace",n),m_fontinfo[n].szFace,m_fontinfo[n].szFace,sizeof(m_fontinfo[n].szFace), m_szConfigIniFile);
+ m_fontinfo[n].nSize = GetPrivateProfileIntW(L"settings",BuildSettingName(L"nFontSize",n),m_fontinfo[n].nSize ,m_szConfigIniFile);
+ m_fontinfo[n].bBold = GetPrivateProfileIntW(L"settings",BuildSettingName(L"bFontBold",n),m_fontinfo[n].bBold ,m_szConfigIniFile);
+ m_fontinfo[n].bItalic = GetPrivateProfileIntW(L"settings",BuildSettingName(L"bFontItalic",n),m_fontinfo[n].bItalic,m_szConfigIniFile);
+ m_fontinfo[n].bAntiAliased = GetPrivateProfileIntW(L"settings",BuildSettingName(L"bFontAA",n),m_fontinfo[n].bItalic,m_szConfigIniFile);
+}
+
+void CPluginShell::ReadConfig()
+{
+ int old_ver = GetPrivateProfileIntW(L"settings",L"version" ,-1,m_szConfigIniFile);
+ int old_subver = GetPrivateProfileIntW(L"settings",L"subversion",-1,m_szConfigIniFile);
+
+ // nuke old settings from prev. version:
+ if (old_ver < INT_VERSION)
+ return;
+ else if (old_subver < INT_SUBVERSION)
+ return;
+
+ //D3DMULTISAMPLE_TYPE m_multisample_fullscreen;
+ //D3DMULTISAMPLE_TYPE m_multisample_desktop;
+ //D3DMULTISAMPLE_TYPE m_multisample_windowed;
+ m_multisample_fullscreen = (D3DMULTISAMPLE_TYPE)GetPrivateProfileIntW(L"settings",L"multisample_fullscreen",m_multisample_fullscreen,m_szConfigIniFile);
+ m_multisample_desktop = (D3DMULTISAMPLE_TYPE)GetPrivateProfileIntW(L"settings",L"multisample_desktop",m_multisample_desktop,m_szConfigIniFile);
+ m_multisample_windowed = (D3DMULTISAMPLE_TYPE)GetPrivateProfileIntW(L"settings",L"multisample_windowed" ,m_multisample_windowed ,m_szConfigIniFile);
+
+ //GUID m_adapter_guid_fullscreen
+ //GUID m_adapter_guid_desktop
+ //GUID m_adapter_guid_windowed
+ char str[256];
+ GetPrivateProfileString("settings","adapter_guid_fullscreen","",str,sizeof(str)-1,m_szConfigIniFileA);
+ TextToGuid(str, &m_adapter_guid_fullscreen);
+ GetPrivateProfileString("settings","adapter_guid_desktop","",str,sizeof(str)-1,m_szConfigIniFileA);
+ TextToGuid(str, &m_adapter_guid_desktop);
+ GetPrivateProfileString("settings","adapter_guid_windowed","",str,sizeof(str)-1,m_szConfigIniFileA);
+ TextToGuid(str, &m_adapter_guid_windowed);
+ GetPrivateProfileString("settings","adapter_devicename_fullscreen","",m_adapter_devicename_fullscreen,sizeof(m_adapter_devicename_fullscreen)-1,m_szConfigIniFileA);
+ GetPrivateProfileString("settings","adapter_devicename_desktop", "",m_adapter_devicename_desktop ,sizeof(m_adapter_devicename_desktop)-1,m_szConfigIniFileA);
+ GetPrivateProfileString("settings","adapter_devicename_windowed", "",m_adapter_devicename_windowed ,sizeof(m_adapter_devicename_windowed)-1,m_szConfigIniFileA);
+
+ // FONTS
+ READ_FONT(0);
+ READ_FONT(1);
+ READ_FONT(2);
+ READ_FONT(3);
+#if (NUM_EXTRA_FONTS >= 1)
+ READ_FONT(4);
+#endif
+#if (NUM_EXTRA_FONTS >= 2)
+ READ_FONT(5);
+#endif
+#if (NUM_EXTRA_FONTS >= 3)
+ READ_FONT(6);
+#endif
+#if (NUM_EXTRA_FONTS >= 4)
+ READ_FONT(7);
+#endif
+#if (NUM_EXTRA_FONTS >= 5)
+ READ_FONT(8);
+#endif
+
+ m_start_fullscreen = GetPrivateProfileIntW(L"settings",L"start_fullscreen",m_start_fullscreen,m_szConfigIniFile);
+ m_start_desktop = GetPrivateProfileIntW(L"settings",L"start_desktop" ,m_start_desktop ,m_szConfigIniFile);
+ m_fake_fullscreen_mode = GetPrivateProfileIntW(L"settings",L"fake_fullscreen_mode",m_fake_fullscreen_mode,m_szConfigIniFile);
+ m_max_fps_fs = GetPrivateProfileIntW(L"settings",L"max_fps_fs",m_max_fps_fs,m_szConfigIniFile);
+ m_max_fps_dm = GetPrivateProfileIntW(L"settings",L"max_fps_dm",m_max_fps_dm,m_szConfigIniFile);
+ m_max_fps_w = GetPrivateProfileIntW(L"settings",L"max_fps_w" ,m_max_fps_w ,m_szConfigIniFile);
+ m_show_press_f1_msg = GetPrivateProfileIntW(L"settings",L"show_press_f1_msg",m_show_press_f1_msg,m_szConfigIniFile);
+ m_allow_page_tearing_w = GetPrivateProfileIntW(L"settings",L"allow_page_tearing_w",m_allow_page_tearing_w,m_szConfigIniFile);
+ m_allow_page_tearing_fs= GetPrivateProfileIntW(L"settings",L"allow_page_tearing_fs",m_allow_page_tearing_fs,m_szConfigIniFile);
+ m_allow_page_tearing_dm= GetPrivateProfileIntW(L"settings",L"allow_page_tearing_dm",m_allow_page_tearing_dm,m_szConfigIniFile);
+ m_minimize_winamp = GetPrivateProfileIntW(L"settings",L"minimize_winamp",m_minimize_winamp,m_szConfigIniFile);
+ m_desktop_show_icons = GetPrivateProfileIntW(L"settings",L"desktop_show_icons",m_desktop_show_icons,m_szConfigIniFile);
+ m_desktop_textlabel_boxes = GetPrivateProfileIntW(L"settings",L"desktop_textlabel_boxes",m_desktop_textlabel_boxes,m_szConfigIniFile);
+ m_desktop_manual_icon_scoot = GetPrivateProfileIntW(L"settings",L"desktop_manual_icon_scoot",m_desktop_manual_icon_scoot,m_szConfigIniFile);
+ m_desktop_555_fix = GetPrivateProfileIntW(L"settings",L"desktop_555_fix",m_desktop_555_fix,m_szConfigIniFile);
+ m_dualhead_horz = GetPrivateProfileIntW(L"settings",L"dualhead_horz",m_dualhead_horz,m_szConfigIniFile);
+ m_dualhead_vert = GetPrivateProfileIntW(L"settings",L"dualhead_vert",m_dualhead_vert,m_szConfigIniFile);
+ m_save_cpu = GetPrivateProfileIntW(L"settings",L"save_cpu",m_save_cpu,m_szConfigIniFile);
+ m_skin = GetPrivateProfileIntW(L"settings",L"skin",m_skin,m_szConfigIniFile);
+ m_fix_slow_text = GetPrivateProfileIntW(L"settings",L"fix_slow_text",m_fix_slow_text,m_szConfigIniFile);
+ m_vj_mode = GetPrivateProfileBoolW(L"settings",L"vj_mode",m_vj_mode,m_szConfigIniFile);
+
+ //D3DDISPLAYMODE m_fs_disp_mode
+ m_disp_mode_fs.Width = GetPrivateProfileIntW(L"settings",L"disp_mode_fs_w", m_disp_mode_fs.Width ,m_szConfigIniFile);
+ m_disp_mode_fs.Height = GetPrivateProfileIntW(L"settings",L"disp_mode_fs_h",m_disp_mode_fs.Height ,m_szConfigIniFile);
+ m_disp_mode_fs.RefreshRate = GetPrivateProfileIntW(L"settings",L"disp_mode_fs_r",m_disp_mode_fs.RefreshRate,m_szConfigIniFile);
+ m_disp_mode_fs.Format = (D3DFORMAT)GetPrivateProfileIntW(L"settings",L"disp_mode_fs_f",m_disp_mode_fs.Format ,m_szConfigIniFile);
+
+ // note: we don't call MyReadConfig() yet, because we
+ // want to completely finish CPluginShell's preinit (and ReadConfig)
+ // before calling CPlugin's preinit and ReadConfig.
+}
+
+void CPluginShell::WRITE_FONT(int n){
+ WritePrivateProfileStringW(L"settings",BuildSettingName(L"szFontFace",n),m_fontinfo[n].szFace,m_szConfigIniFile);
+ WritePrivateProfileIntW(m_fontinfo[n].bBold, BuildSettingName(L"bFontBold",n), m_szConfigIniFile, L"settings");
+ WritePrivateProfileIntW(m_fontinfo[n].bItalic,BuildSettingName(L"bFontItalic",n), m_szConfigIniFile, L"settings");
+ WritePrivateProfileIntW(m_fontinfo[n].nSize, BuildSettingName(L"nFontSize",n), m_szConfigIniFile, L"settings");
+ WritePrivateProfileIntW(m_fontinfo[n].bAntiAliased, BuildSettingName(L"bFontAA",n),m_szConfigIniFile, L"settings");
+}
+
+void CPluginShell::WriteConfig()
+{
+ //D3DMULTISAMPLE_TYPE m_multisample_fullscreen;
+ //D3DMULTISAMPLE_TYPE m_multisample_desktop;
+ //D3DMULTISAMPLE_TYPE m_multisample_windowed;
+ WritePrivateProfileIntW((int)m_multisample_fullscreen,L"multisample_fullscreen",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW((int)m_multisample_desktop ,L"multisample_desktop" ,m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW((int)m_multisample_windowed ,L"multisample_windowed" ,m_szConfigIniFile,L"settings");
+
+ //GUID m_adapter_guid_fullscreen
+ //GUID m_adapter_guid_desktop
+ //GUID m_adapter_guid_windowed
+ char str[256];
+ GuidToText(&m_adapter_guid_fullscreen, str, sizeof(str));
+ WritePrivateProfileString("settings","adapter_guid_fullscreen",str,m_szConfigIniFileA);
+ GuidToText(&m_adapter_guid_desktop, str, sizeof(str));
+ WritePrivateProfileString("settings","adapter_guid_desktop",str,m_szConfigIniFileA);
+ GuidToText(&m_adapter_guid_windowed, str, sizeof(str));
+ WritePrivateProfileString("settings","adapter_guid_windowed" ,str,m_szConfigIniFileA);
+ WritePrivateProfileString("settings","adapter_devicename_fullscreen",m_adapter_devicename_fullscreen,m_szConfigIniFileA);
+ WritePrivateProfileString("settings","adapter_devicename_desktop" ,m_adapter_devicename_desktop ,m_szConfigIniFileA);
+ WritePrivateProfileString("settings","adapter_devicename_windowed" ,m_adapter_devicename_windowed ,m_szConfigIniFileA);
+
+ // FONTS
+ WRITE_FONT(0);
+ WRITE_FONT(1);
+ WRITE_FONT(2);
+ WRITE_FONT(3);
+#if (NUM_EXTRA_FONTS >= 1)
+ WRITE_FONT(4);
+#endif
+#if (NUM_EXTRA_FONTS >= 2)
+ WRITE_FONT(5);
+#endif
+#if (NUM_EXTRA_FONTS >= 3)
+ WRITE_FONT(6);
+#endif
+#if (NUM_EXTRA_FONTS >= 4)
+ WRITE_FONT(7);
+#endif
+#if (NUM_EXTRA_FONTS >= 5)
+ WRITE_FONT(8);
+#endif
+
+ WritePrivateProfileIntW(m_start_fullscreen,L"start_fullscreen",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_start_desktop ,L"start_desktop" ,m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_fake_fullscreen_mode,L"fake_fullscreen_mode",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_max_fps_fs,L"max_fps_fs",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_max_fps_dm,L"max_fps_dm",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_max_fps_w ,L"max_fps_w" ,m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_show_press_f1_msg,L"show_press_f1_msg",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_allow_page_tearing_w,L"allow_page_tearing_w",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_allow_page_tearing_fs,L"allow_page_tearing_fs",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_allow_page_tearing_dm,L"allow_page_tearing_dm",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_minimize_winamp,L"minimize_winamp",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_desktop_show_icons,L"desktop_show_icons",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_desktop_textlabel_boxes,L"desktop_textlabel_boxes",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_desktop_manual_icon_scoot,L"desktop_manual_icon_scoot",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_desktop_555_fix,L"desktop_555_fix",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_dualhead_horz,L"dualhead_horz",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_dualhead_vert,L"dualhead_vert",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_save_cpu,L"save_cpu",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_skin,L"skin",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_fix_slow_text,L"fix_slow_text",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_vj_mode,L"vj_mode",m_szConfigIniFile,L"settings");
+
+ //D3DDISPLAYMODE m_fs_disp_mode
+ WritePrivateProfileIntW(m_disp_mode_fs.Width ,L"disp_mode_fs_w",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_disp_mode_fs.Height ,L"disp_mode_fs_h",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_disp_mode_fs.RefreshRate,L"disp_mode_fs_r",m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(m_disp_mode_fs.Format ,L"disp_mode_fs_f",m_szConfigIniFile,L"settings");
+
+ WritePrivateProfileIntW(INT_VERSION ,L"version" ,m_szConfigIniFile,L"settings");
+ WritePrivateProfileIntW(INT_SUBVERSION ,L"subversion" ,m_szConfigIniFile,L"settings");
+
+ // finally, save the plugin's unique settings:
+ MyWriteConfig();
+}
+
+//----------------------------------------------------------------------
+//----------------------------------------------------------------------
+//----------------------------------------------------------------------
+
+int CPluginShell::PluginRender(unsigned char *pWaveL, unsigned char *pWaveR)//, unsigned char *pSpecL, unsigned char *pSpecR)
+{
+ // return FALSE here to tell Winamp to terminate the plugin
+
+ if (!m_lpDX || !m_lpDX->m_ready)
+ {
+ // note: 'm_ready' will go false when a device reset fatally fails
+ // (for example, when user resizes window, or toggles fullscreen.)
+ m_exiting = 1;
+ return false; // EXIT THE PLUGIN
+ }
+
+ if (m_hTextWnd)
+ m_lost_focus = ((GetFocus() != GetPluginWindow()) && (GetFocus() != m_hTextWnd));
+ else
+ m_lost_focus = (GetFocus() != GetPluginWindow());
+
+ if ((m_screenmode==WINDOWED && m_hidden) ||
+ (m_screenmode==FULLSCREEN && m_lost_focus) ||
+ (m_screenmode==WINDOWED && m_resizing)
+ )
+ {
+ Sleep(30);
+ return true;
+ }
+
+ // test for lost device
+ // (this happens when device is fullscreen & user alt-tabs away,
+ // or when monitor power-saving kicks in)
+ HRESULT hr = m_lpDX->m_lpDevice->TestCooperativeLevel();
+ if (hr == D3DERR_DEVICENOTRESET)
+ {
+ // device WAS lost, and is now ready to be reset (and come back online):
+ CleanUpDX9Stuff(0);
+ if (m_lpDX->m_lpDevice->Reset(&m_lpDX->m_d3dpp) != D3D_OK)
+ {
+ // note: a basic warning messagebox will have already been given.
+ // now suggest specific advice on how to regain more video memory:
+ if (m_lpDX->m_lastErr == DXC_ERR_CREATEDEV_PROBABLY_OUTOFVIDEOMEMORY)
+ SuggestHowToFreeSomeMem();
+ return false; // EXIT THE PLUGIN
+ }
+ if (!AllocateDX9Stuff())
+ return false; // EXIT THE PLUGIN
+ }
+ else if (hr != D3D_OK)
+ {
+ // device is lost, and not yet ready to come back; sleep.
+ Sleep(30);
+ return true;
+ }
+
+ if (m_vjd3d9_device)
+ {
+ HRESULT hr = m_vjd3d9_device->TestCooperativeLevel();
+ if (hr == D3DERR_DEVICENOTRESET)
+ {
+ RECT c;
+ GetClientRect(m_hTextWnd, &c);
+
+ POINT p;
+ p.x = c.left;
+ p.y = c.top;
+ if (ClientToScreen(m_hTextWnd, &p))
+ {
+ c.left += p.x;
+ c.right += p.x;
+ c.top += p.y;
+ c.bottom += p.y;
+ }
+
+ CleanUpVJStuff();
+ if (!InitVJStuff(&c))
+ return false; // EXIT THE PLUGIN
+ }
+ }
+
+ if (m_screenmode==DESKTOP)
+ {
+ PushWindowToJustBeforeDesktop(GetPluginWindow());
+ }
+
+ DoTime();
+ AnalyzeNewSound(pWaveL, pWaveR);
+ AlignWaves();
+
+ DrawAndDisplay(0);
+
+ EnforceMaxFPS();
+
+ m_frame++;
+
+ return true;
+}
+
+void CPluginShell::PushWindowToJustBeforeDesktop(HWND h)
+{
+ // if our window isn't already at the bottom of the Z order,
+ // freshly send it to HWND_BOTTOM.
+
+ // this usually gives us the Program Manager window:
+ HWND hWndBottom = GetWindow(h, GW_HWNDLAST);
+
+ // then, bottommost 'normal' window is usually the one just in front of it:
+ if (hWndBottom == m_hWndProgMan)
+ hWndBottom = GetWindow(hWndBottom, GW_HWNDPREV);
+
+ if (hWndBottom != h)
+ {
+ m_force_accept_WM_WINDOWPOSCHANGING = 1;
+ SetWindowPos(h, HWND_BOTTOM, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE);
+ m_force_accept_WM_WINDOWPOSCHANGING = 0;
+ }
+
+ /*
+ HWND hDesktopBkgWnd = FindWindow("SHELLDLL_DefView", "");
+ if (hDesktopBkgWnd)
+ {
+ HWND hWndInFrontOfIcons = GetWindow(h, GW_HWNDPREV);
+ if (hWndInFrontOfIcons != h)
+ {
+ m_force_accept_WM_WINDOWPOSCHANGING = 1;
+ SetWindowPos(hDesktopBkgWnd, h, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE);
+ m_force_accept_WM_WINDOWPOSCHANGING = 0;
+ }
+ }
+ */
+
+
+}
+
+void CPluginShell::DrawAndDisplay(int redraw)
+{
+ int cx = m_vjd3d9_device ? m_nTextWndWidth : m_lpDX->m_client_width;
+ int cy = m_vjd3d9_device ? m_nTextWndHeight : m_lpDX->m_client_height;
+ if (m_lpDDSText)
+ {
+ D3DSURFACE_DESC desc;
+ if (D3D_OK == m_lpDDSText->GetLevelDesc(0, &desc))
+ {
+ cx = min(cx, (int)desc.Width);
+ cy = min(cy, (int)desc.Height);
+ }
+ }
+ m_upper_left_corner_y = TEXT_MARGIN + GetCanvasMarginY();
+ m_upper_right_corner_y = TEXT_MARGIN + GetCanvasMarginY();
+ m_lower_left_corner_y = cy - TEXT_MARGIN - GetCanvasMarginY();
+ m_lower_right_corner_y = cy - TEXT_MARGIN - GetCanvasMarginY();
+ m_left_edge = TEXT_MARGIN + GetCanvasMarginX();
+ m_right_edge = cx - TEXT_MARGIN - GetCanvasMarginX();
+
+ /*if (m_screenmode == DESKTOP || m_screenmode == FAKE_FULLSCREEN)
+ {
+ // check if taskbar is above plugin window;
+ // if so, scoot text & icons out of the way.
+ // [...should always be true for Desktop Mode,
+ // but it's like this for code simplicity.]
+ int taskbar_is_above_plugin_window = 1;
+ HWND h = FindWindow("Shell_TrayWnd", NULL);
+ while (h) //(..shouldn't be very many windows to iterate through here)
+ {
+ h = GetWindow(h, GW_HWNDPREV);
+ if (h == GetPluginWindow())
+ {
+ taskbar_is_above_plugin_window = 0;
+ break;
+ }
+ }
+
+ if (taskbar_is_above_plugin_window)
+ {
+ // respect the taskbar area; make sure the text, desktop icons, etc.
+ // don't appear underneath it.
+ //m_upper_left_corner_y += m_lpDX->m_monitor_work_rect.top - m_lpDX->m_monitor_rect.top;
+ //m_upper_right_corner_y += m_lpDX->m_monitor_work_rect.top - m_lpDX->m_monitor_rect.top;
+ //m_lower_left_corner_y -= m_lpDX->m_monitor_rect.bottom - m_lpDX->m_monitor_work_rect.bottom;
+ //m_lower_right_corner_y -= m_lpDX->m_monitor_rect.bottom - m_lpDX->m_monitor_work_rect.bottom;
+ //m_left_edge += m_lpDX->m_monitor_work_rect.left - m_lpDX->m_monitor_rect.left;
+ //m_right_edge -= m_lpDX->m_monitor_rect.right - m_lpDX->m_monitor_work_rect.right;
+ m_lpDX->UpdateMonitorWorkRect();
+ 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());
+ 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());
+ 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());
+ 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());
+ m_left_edge = max(m_left_edge , m_lpDX->m_monitor_work_rect.left - m_lpDX->m_monitor_rect.left + TEXT_MARGIN + GetCanvasMarginX() );
+ 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());
+ }
+ }*/
+
+ if (D3D_OK==m_lpDX->m_lpDevice->BeginScene())
+ {
+ MyRenderFn(redraw);
+
+ PrepareFor2DDrawing_B(GetDevice(), GetWidth(), GetHeight());
+
+ RenderDesktop();
+ if (!m_vjd3d9_device) // in VJ mode, this renders to different context, so do it after BeginScene() on 2nd device.
+ RenderBuiltInTextMsgs(); // to m_lpDDSText?
+ 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);
+ RenderPlaylist();
+
+ if (!m_vjd3d9_device)
+ m_text.DrawNow();
+
+ m_lpDX->m_lpDevice->EndScene();
+ }
+
+ // VJ Mode:
+ if (m_vj_mode && m_vjd3d9_device && !m_hidden_textwnd && D3D_OK==m_vjd3d9_device->BeginScene())
+ {
+ if (!m_lpDDSText || m_bClearVJWindow)
+ m_vjd3d9_device->Clear(0, 0, D3DCLEAR_TARGET, 0xFF000000, 1.0f, 0);
+ m_bClearVJWindow = false;
+ // note: when using debug DX runtime, textwnd will flash red/green after frame 4, if no text is drawn on a frame!
+
+ RenderBuiltInTextMsgs();
+
+ PrepareFor2DDrawing_B(m_vjd3d9_device, m_nTextWndWidth, m_nTextWndHeight);
+
+ m_text.DrawNow();
+
+ m_vjd3d9_device->EndScene();
+ }
+
+ if (m_screenmode == DESKTOP)
+ {
+ // window is hidden after creation, until 1st frame is ready to go;
+ // now that it's ready, we show it.
+ // see dxcontext::Internal_Init()'s call to SetWindowPos() for the DESKTOP case.
+ if (!IsWindowVisible(GetPluginWindow()))
+ ShowWindow(GetPluginWindow(), SW_SHOWNORMAL);
+ }
+
+ 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))
+ {
+ int real_w = m_lpDX->m_REAL_client_width; // real client size, in pixels
+ int real_h = m_lpDX->m_REAL_client_height;
+ int fat_w = m_lpDX->m_client_width; // oversized VS canvas size, in pixels
+ int fat_h = m_lpDX->m_client_height;
+ int extra_w = fat_w - real_w;
+ int extra_h = fat_h - real_h;
+ RECT src, dst;
+ SetRect(&src, extra_w/2, extra_h/2, extra_w/2 + real_w, extra_h/2 + real_h);
+ SetRect(&dst, 0, 0, real_w, real_h);
+ m_lpDX->m_lpDevice->Present(&src, &dst,NULL,NULL);
+ }
+ else
+ m_lpDX->m_lpDevice->Present(NULL,NULL,NULL,NULL);
+
+ if (m_vjd3d9_device && !m_hidden_textwnd)
+ m_vjd3d9_device->Present(NULL,NULL,NULL,NULL);
+}
+
+void CPluginShell::EnforceMaxFPS()
+{
+ int max_fps;
+ switch (m_screenmode)
+ {
+ case WINDOWED: max_fps = m_max_fps_w; break;
+ case FULLSCREEN: max_fps = m_max_fps_fs; break;
+ case FAKE_FULLSCREEN: max_fps = m_max_fps_fs; break;
+ case DESKTOP: max_fps = m_max_fps_dm; break;
+ }
+
+ if (max_fps <= 0)
+ return;
+
+ float fps_lo = (float)max_fps;
+ float fps_hi = (float)max_fps;
+
+ if (m_save_cpu)
+ {
+ // Find the optimal lo/hi bounds for the fps
+ // that will result in a maximum difference,
+ // in the time for a single frame, of 0.003 seconds -
+ // the assumed granularity for Sleep(1) -
+
+ // Using this range of acceptable fps
+ // will allow us to do (sloppy) fps limiting
+ // using only Sleep(1), and never the
+ // second half of it: Sleep(0) in a tight loop,
+ // which sucks up the CPU (whereas Sleep(1)
+ // leaves it idle).
+
+ // The original equation:
+ // 1/(max_fps*t1) = 1/(max*fps/t1) - 0.003
+ // where:
+ // t1 > 0
+ // max_fps*t1 is the upper range for fps
+ // max_fps/t1 is the lower range for fps
+
+ float a = 1;
+ float b = -0.003f * max_fps;
+ float c = -1.0f;
+ float det = b*b - 4*a*c;
+ if (det>0)
+ {
+ float t1 = (-b + sqrtf(det)) / (2*a);
+ //float t2 = (-b - sqrtf(det)) / (2*a);
+
+ if (t1 > 1.0f)
+ {
+ fps_lo = max_fps / t1;
+ fps_hi = max_fps * t1;
+ // verify: now [1.0f/fps_lo - 1.0f/fps_hi] should equal 0.003 seconds.
+ // note: allowing tolerance to go beyond these values for
+ // fps_lo and fps_hi would gain nothing.
+ }
+ }
+ }
+
+ if (m_high_perf_timer_freq.QuadPart > 0)
+ {
+ LARGE_INTEGER t;
+ QueryPerformanceCounter(&t);
+
+ if (m_prev_end_of_frame.QuadPart != 0)
+ {
+ int ticks_to_wait_lo = (int)((float)m_high_perf_timer_freq.QuadPart / (float)fps_hi);
+ int ticks_to_wait_hi = (int)((float)m_high_perf_timer_freq.QuadPart / (float)fps_lo);
+ int done = 0;
+ int loops = 0;
+ do
+ {
+ QueryPerformanceCounter(&t);
+
+ __int64 t2 = t.QuadPart - m_prev_end_of_frame.QuadPart;
+ if (t2 > 2147483000)
+ done = 1;
+ if (t.QuadPart < m_prev_end_of_frame.QuadPart) // time wrap
+ done = 1;
+
+ // this is sloppy - if your freq. is high, this can overflow (to a (-) int) in just a few minutes
+ // but it's ok, we have protection for that above.
+ int ticks_passed = (int)(t.QuadPart - m_prev_end_of_frame.QuadPart);
+ if (ticks_passed >= ticks_to_wait_lo)
+ done = 1;
+
+ if (!done)
+ {
+ // if > 0.01s left, do Sleep(1), which will actually sleep some
+ // steady amount of up to 3 ms (depending on the OS),
+ // and do so in a nice way (cpu meter drops; laptop battery spared).
+ // otherwise, do a few Sleep(0)'s, which just give up the timeslice,
+ // but don't really save cpu or battery, but do pass a tiny
+ // amount of time.
+
+ //if (ticks_left > (int)m_high_perf_timer_freq.QuadPart/500)
+ if (ticks_to_wait_hi - ticks_passed > (int)m_high_perf_timer_freq.QuadPart/100)
+ Sleep(5);
+ else if (ticks_to_wait_hi - ticks_passed > (int)m_high_perf_timer_freq.QuadPart/1000)
+ Sleep(1);
+ else
+ for (int i=0; i<10; i++)
+ Sleep(0); // causes thread to give up its timeslice
+ }
+ }
+ while (!done);
+ }
+
+ m_prev_end_of_frame = t;
+ }
+ else
+ {
+ Sleep(1000/max_fps);
+ }
+}
+
+void CPluginShell::DoTime()
+{
+ if (m_frame==0)
+ {
+ m_fps = 30;
+ m_time = 0;
+ m_time_hist_pos = 0;
+ }
+
+ double new_raw_time;
+ float elapsed;
+
+ if (m_high_perf_timer_freq.QuadPart != 0)
+ {
+ // get high-precision time
+ // precision: usually from 1..6 us (MICROseconds), depending on the cpu speed.
+ // (higher cpu speeds tend to have better precision here)
+ LARGE_INTEGER t;
+ if (!QueryPerformanceCounter(&t))
+ {
+ m_high_perf_timer_freq.QuadPart = 0; // something went wrong (exception thrown) -> revert to crappy timer
+ }
+ else
+ {
+ new_raw_time = (double)t.QuadPart;
+ elapsed = (float)((new_raw_time - m_last_raw_time)/(double)m_high_perf_timer_freq.QuadPart);
+ }
+ }
+
+ if (m_high_perf_timer_freq.QuadPart == 0)
+ {
+ // get low-precision time
+ // precision: usually 1 ms (MILLIsecond) for win98, and 10 ms for win2k.
+ new_raw_time = (double)(timeGetTime()*0.001);
+ elapsed = (float)(new_raw_time - m_last_raw_time);
+ }
+
+ m_last_raw_time = new_raw_time;
+ int slots_to_look_back = (m_high_perf_timer_freq.QuadPart==0) ? TIME_HIST_SLOTS : TIME_HIST_SLOTS/2;
+
+ m_time += 1.0f/m_fps;
+
+ // timekeeping goals:
+ // 1. keep 'm_time' increasing SMOOTHLY: (smooth animation depends on it)
+ // m_time += 1.0f/m_fps; // where m_fps is a bit damped
+ // 2. keep m_time_hist[] 100% accurate (except for filtering out pauses),
+ // so that when we look take the difference between two entries,
+ // we get the real amount of time that passed between those 2 frames.
+ // m_time_hist[i] = m_last_raw_time + elapsed_corrected;
+
+ if (m_frame > TIME_HIST_SLOTS)
+ {
+ if (m_fps < 60.0f)
+ slots_to_look_back = (int)(slots_to_look_back*(0.1f + 0.9f*(m_fps/60.0f)));
+
+ if (elapsed > 5.0f/m_fps || elapsed > 1.0f || elapsed < 0)
+ elapsed = 1.0f / 30.0f;
+
+ float old_hist_time = m_time_hist[(m_time_hist_pos - slots_to_look_back + TIME_HIST_SLOTS) % TIME_HIST_SLOTS];
+ float new_hist_time = m_time_hist[(m_time_hist_pos - 1 + TIME_HIST_SLOTS) % TIME_HIST_SLOTS]
+ + elapsed;
+
+ m_time_hist[m_time_hist_pos] = new_hist_time;
+ m_time_hist_pos = (m_time_hist_pos+1) % TIME_HIST_SLOTS;
+
+ float new_fps = slots_to_look_back / (float)(new_hist_time - old_hist_time);
+ float damping = (m_high_perf_timer_freq.QuadPart==0) ? 0.93f : 0.87f;
+
+ // damp heavily, so that crappy timer precision doesn't make animation jerky
+ if (fabsf(m_fps - new_fps) > 3.0f)
+ m_fps = new_fps;
+ else
+ m_fps = damping*m_fps + (1-damping)*new_fps;
+ }
+ else
+ {
+ float damping = (m_high_perf_timer_freq.QuadPart==0) ? 0.8f : 0.6f;
+
+ if (m_frame < 2)
+ elapsed = 1.0f / 30.0f;
+ else if (elapsed > 1.0f || elapsed < 0)
+ elapsed = 1.0f / m_fps;
+
+ float old_hist_time = m_time_hist[0];
+ float new_hist_time = m_time_hist[(m_time_hist_pos - 1 + TIME_HIST_SLOTS) % TIME_HIST_SLOTS]
+ + elapsed;
+
+ m_time_hist[m_time_hist_pos] = new_hist_time;
+ m_time_hist_pos = (m_time_hist_pos+1) % TIME_HIST_SLOTS;
+
+ if (m_frame > 0)
+ {
+ float new_fps = (m_frame) / (new_hist_time - old_hist_time);
+ m_fps = damping*m_fps + (1-damping)*new_fps;
+ }
+ }
+
+ // Synchronize the audio and video by telling Winamp how many milliseconds we want the audio data,
+ // before it's actually audible. If we set this to the amount of time it takes to display 1 frame
+ // (1/fps), the video and audio should be perfectly synchronized.
+ if (m_fps < 2.0f)
+ mod1.latencyMs = 500;
+ else if (m_fps > 125.0f)
+ mod1.latencyMs = 8;
+ else
+ mod1.latencyMs = (int)(1000.0f/m_fps*m_lpDX->m_frame_delay + 0.5f);
+}
+
+void CPluginShell::AnalyzeNewSound(unsigned char *pWaveL, unsigned char *pWaveR)
+{
+ // we get 576 samples in from winamp.
+ // the output of the fft has 'num_frequencies' samples,
+ // and represents the frequency range 0 hz - 22,050 hz.
+ // usually, plugins only use half of this output (the range 0 hz - 11,025 hz),
+ // since >10 khz doesn't usually contribute much.
+
+ int i;
+
+ float temp_wave[2][576];
+
+ int old_i = 0;
+ for (i=0; i<576; i++)
+ {
+ m_sound.fWaveform[0][i] = (float)((pWaveL[i] ^ 128) - 128);
+ m_sound.fWaveform[1][i] = (float)((pWaveR[i] ^ 128) - 128);
+
+ // simulating single frequencies from 200 to 11,025 Hz:
+ //float freq = 1.0f + 11050*(GetFrame() % 100)*0.01f;
+ //m_sound.fWaveform[0][i] = 10*sinf(i*freq*6.28f/44100.0f);
+
+ // damp the input into the FFT a bit, to reduce high-frequency noise:
+ temp_wave[0][i] = 0.5f*(m_sound.fWaveform[0][i] + m_sound.fWaveform[0][old_i]);
+ temp_wave[1][i] = 0.5f*(m_sound.fWaveform[1][i] + m_sound.fWaveform[1][old_i]);
+ old_i = i;
+ }
+
+ m_fftobj.time_to_frequency_domain(temp_wave[0], m_sound.fSpectrum[0]);
+ m_fftobj.time_to_frequency_domain(temp_wave[1], m_sound.fSpectrum[1]);
+
+ // sum (left channel) spectrum up into 3 bands
+ // [note: the new ranges do it so that the 3 bands are equally spaced, pitch-wise]
+ float min_freq = 200.0f;
+ float max_freq = 11025.0f;
+ float net_octaves = (logf(max_freq/min_freq) / logf(2.0f)); // 5.7846348455575205777914165223593
+ float octaves_per_band = net_octaves / 3.0f; // 1.9282116151858401925971388407864
+ float mult = powf(2.0f, octaves_per_band); // each band's highest freq. divided by its lowest freq.; 3.805831305510122517035102576162
+ // [to verify: min_freq * mult * mult * mult should equal max_freq.]
+ for (int ch=0; ch<2; ch++)
+ {
+ for (i=0; i<3; i++)
+ {
+ // old guesswork code for this:
+ // float exp = 2.1f;
+ // int start = (int)(NUM_FREQUENCIES*0.5f*powf(i/3.0f, exp));
+ // int end = (int)(NUM_FREQUENCIES*0.5f*powf((i+1)/3.0f, exp));
+ // results:
+ // old range: new range (ideal):
+ // bass: 0-1097 200-761
+ // mids: 1097-4705 761-2897
+ // treb: 4705-11025 2897-11025
+ int start = (int)(NUM_FREQUENCIES * min_freq*powf(mult, (float)i)/11025.0f);
+ int end = (int)(NUM_FREQUENCIES * min_freq*powf(mult, (float)(i+1))/11025.0f);
+ if (start < 0) start = 0;
+ if (end > NUM_FREQUENCIES) end = NUM_FREQUENCIES;
+
+ m_sound.imm[ch][i] = 0;
+ for (int j=start; j<end; j++)
+ m_sound.imm[ch][i] += m_sound.fSpectrum[ch][j];
+ m_sound.imm[ch][i] /= (float)(end-start);
+ }
+ }
+
+ // some code to find empirical long-term averages for imm[0..2]:
+ /*{
+ static float sum[3];
+ static int count = 0;
+
+ #define FRAMES_PER_SONG 300 // should be at least 200!
+
+ if (m_frame < FRAMES_PER_SONG)
+ {
+ sum[0] = sum[1] = sum[2] = 0;
+ count = 0;
+ }
+ else
+ {
+ if (m_frame%FRAMES_PER_SONG == 0)
+ {
+ char buf[256];
+ sprintf(buf, "%.4f, %.4f, %.4f (%d samples / ~%d songs)\n",
+ sum[0]/(float)(count),
+ sum[1]/(float)(count),
+ sum[2]/(float)(count),
+ count,
+ count/(FRAMES_PER_SONG-10)
+ );
+ OutputDebugString(buf);
+
+ // skip to next song
+ PostMessage(m_hWndWinamp,WM_COMMAND,40048,0);
+ }
+ else if (m_frame%FRAMES_PER_SONG == 5)
+ {
+ // then advance to 0-2 minutes into the song:
+ PostMessage(m_hWndWinamp,WM_USER,(20 + (warand()%65) + (rand()%65))*1000,106);
+ }
+ else if (m_frame%FRAMES_PER_SONG >= 10)
+ {
+ sum[0] += m_sound.imm[0];
+ sum[1] += m_sound.imm[1];
+ sum[2] += m_sound.imm[2];
+ count++;
+ }
+ }
+ }*/
+
+ // multiply by long-term, empirically-determined inverse averages:
+ // (for a trial of 244 songs, 10 seconds each, somewhere in the 2nd or 3rd minute,
+ // the average levels were: 0.326781557 0.38087377 0.199888934
+ for (int ch=0; ch<2; ch++)
+ {
+ m_sound.imm[ch][0] /= 0.326781557f;//0.270f;
+ m_sound.imm[ch][1] /= 0.380873770f;//0.343f;
+ m_sound.imm[ch][2] /= 0.199888934f;//0.295f;
+ }
+
+ // do temporal blending to create attenuated and super-attenuated versions
+ for (int ch=0; ch<2; ch++)
+ {
+ for (i=0; i<3; i++)
+ {
+ // m_sound.avg[i]
+ {
+ float avg_mix;
+ if (m_sound.imm[ch][i] > m_sound.avg[ch][i])
+ avg_mix = AdjustRateToFPS(0.2f, 14.0f, m_fps);
+ else
+ avg_mix = AdjustRateToFPS(0.5f, 14.0f, m_fps);
+ m_sound.avg[ch][i] = m_sound.avg[ch][i]*avg_mix + m_sound.imm[ch][i]*(1-avg_mix);
+ }
+
+ // m_sound.med_avg[i]
+ // m_sound.long_avg[i]
+ {
+ float med_mix = 0.91f;//0.800f + 0.11f*powf(t, 0.4f); // primarily used for velocity_damping
+ float long_mix = 0.96f;//0.800f + 0.16f*powf(t, 0.2f); // primarily used for smoke plumes
+ med_mix = AdjustRateToFPS(med_mix, 14.0f, m_fps);
+ long_mix = AdjustRateToFPS(long_mix, 14.0f, m_fps);
+ m_sound.med_avg[ch][i] = m_sound.med_avg[ch][i]*(med_mix) + m_sound.imm[ch][i]*(1-med_mix);
+ m_sound.long_avg[ch][i] = m_sound.long_avg[ch][i]*(long_mix) + m_sound.imm[ch][i]*(1-long_mix);
+ }
+ }
+ }
+}
+
+void CPluginShell::PrepareFor2DDrawing_B(IDirect3DDevice9 *pDevice, int w, int h)
+{
+ // New 2D drawing area will have x,y coords in the range <-1,-1> .. <1,1>
+ // +--------+ Y=-1
+ // | |
+ // | screen | Z=0: front of scene
+ // | | Z=1: back of scene
+ // +--------+ Y=1
+ // X=-1 X=1
+ // NOTE: After calling this, be sure to then call (at least):
+ // 1. SetVertexShader()
+ // 2. SetTexture(), if you need it
+ // before rendering primitives!
+ // Also, be sure your sprites have a z coordinate of 0.
+
+ pDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
+ pDevice->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
+ pDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
+ pDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
+ pDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
+ pDevice->SetRenderState(D3DRS_FOGENABLE, FALSE);
+ pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
+ pDevice->SetRenderState(D3DRS_CLIPPING, TRUE);
+ pDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
+ pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
+ pDevice->SetRenderState(D3DRS_LOCALVIEWER, FALSE);
+ pDevice->SetRenderState(D3DRS_COLORVERTEX, TRUE);
+
+ pDevice->SetTexture(0, NULL);
+ pDevice->SetTexture(1, NULL);
+ pDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);//D3DTEXF_LINEAR);
+ pDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_POINT);//D3DTEXF_LINEAR);
+ pDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
+ pDevice->SetTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
+ pDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ pDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ pDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);
+ pDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+
+ pDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ pDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
+ pDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
+
+ pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
+
+ // set up for 2D drawing:
+ {
+ D3DXMATRIX Ortho2D;
+ D3DXMATRIX Identity;
+
+ pMatrixOrthoLH(&Ortho2D, (float)w, (float)h, 0.0f, 1.0f);
+ D3DXMatrixIdentity(&Identity);
+
+ pDevice->SetTransform(D3DTS_PROJECTION, &Ortho2D);
+ pDevice->SetTransform(D3DTS_WORLD, &Identity);
+ pDevice->SetTransform(D3DTS_VIEW, &Identity);
+ }
+}
+
+void CPluginShell::DrawDarkTranslucentBox(RECT* pr)
+{
+ // 'pr' is the rectangle that some text will occupy;
+ // a black box will be drawn around it, plus a bit of extra margin space.
+
+ if (m_vjd3d9_device)
+ return;
+
+ m_lpDX->m_lpDevice->SetVertexShader(NULL);
+ m_lpDX->m_lpDevice->SetPixelShader(NULL);
+ m_lpDX->m_lpDevice->SetFVF(SIMPLE_VERTEX_FORMAT);
+ m_lpDX->m_lpDevice->SetTexture(0, NULL);
+
+ m_lpDX->m_lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
+ m_lpDX->m_lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
+ m_lpDX->m_lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
+
+ m_lpDX->m_lpDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
+ m_lpDX->m_lpDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
+ m_lpDX->m_lpDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+ m_lpDX->m_lpDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ m_lpDX->m_lpDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
+
+ // set up a quad
+ SIMPLEVERTEX verts[4];
+ for (int i=0; i<4; i++)
+ {
+ verts[i].x = (i%2==0) ? (float)(-m_lpDX->m_client_width /2 + pr->left) :
+ (float)(-m_lpDX->m_client_width /2 + pr->right);
+ verts[i].y = (i/2==0) ? (float)-(-m_lpDX->m_client_height/2 + pr->bottom) :
+ (float)-(-m_lpDX->m_client_height/2 + pr->top);
+ verts[i].z = 0;
+ verts[i].Diffuse = (m_screenmode==DESKTOP) ? 0xE0000000 : 0xD0000000;
+ }
+
+ m_lpDX->m_lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, verts, sizeof(SIMPLEVERTEX));
+
+ // undo unusual state changes:
+ m_lpDX->m_lpDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
+ m_lpDX->m_lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
+}
+
+void CPluginShell::RenderBuiltInTextMsgs()
+{
+ int _show_press_f1_NOW = (m_show_press_f1_msg && m_time < PRESS_F1_DUR);
+
+ {
+ RECT r;
+
+ if (m_show_help)
+ {
+ int y = m_upper_left_corner_y;
+
+ SetRect(&r, 0, 0, GetWidth(), GetHeight());
+ if(!g_szHelp_W)
+ m_d3dx_font[HELPSCREEN_FONT]->DrawTextA(NULL, (char*)g_szHelp, -1, &r, DT_CALCRECT, 0xFFFFFFFF);
+ else
+ m_d3dx_font[HELPSCREEN_FONT]->DrawTextW(NULL, g_szHelp, -1, &r, DT_CALCRECT, 0xFFFFFFFF);
+
+ r.top += m_upper_left_corner_y;
+ r.left += m_left_edge;
+ r.right += m_left_edge + PLAYLIST_INNER_MARGIN*2;
+ r.bottom += m_upper_left_corner_y + PLAYLIST_INNER_MARGIN*2;
+ DrawDarkTranslucentBox(&r);
+
+ r.top += PLAYLIST_INNER_MARGIN;
+ r.left += PLAYLIST_INNER_MARGIN;
+ r.right -= PLAYLIST_INNER_MARGIN;
+ r.bottom -= PLAYLIST_INNER_MARGIN;
+ if(!g_szHelp_W)
+ m_d3dx_font[HELPSCREEN_FONT]->DrawTextA(NULL, (char*)g_szHelp, -1, &r, 0, 0xFFFFFFFF);
+ else
+ m_d3dx_font[HELPSCREEN_FONT]->DrawTextW(NULL, g_szHelp, -1, &r, 0, 0xFFFFFFFF);
+
+ m_upper_left_corner_y += r.bottom-r.top + PLAYLIST_INNER_MARGIN*3;
+ }
+
+ // render 'Press F1 for Help' message in lower-right corner:
+ if (_show_press_f1_NOW)
+ {
+ int dx = (int)(160.0f * powf(m_time/(float)(PRESS_F1_DUR), (float)(PRESS_F1_EXP)));
+ SetRect(&r, m_left_edge, m_lower_right_corner_y - GetFontHeight(DECORATIVE_FONT), m_right_edge + dx, m_lower_right_corner_y);
+ m_lower_right_corner_y -= m_d3dx_font[DECORATIVE_FONT]->DrawTextW(NULL, WASABI_API_LNGSTRINGW(IDS_PRESS_F1_MSG), -1, &r, DT_RIGHT, 0xFFFFFFFF);
+ }
+ }
+}
+
+void CPluginShell::RenderPlaylist()
+{
+ // draw playlist:
+ if (m_show_playlist)
+ {
+ RECT r;
+ int nSongs = SendMessage(m_hWndWinamp,WM_USER, 0, 124);
+ int now_playing = SendMessage(m_hWndWinamp,WM_USER, 0, 125);
+
+ if (nSongs <= 0)
+ {
+ m_show_playlist = 0;
+ }
+ else
+ {
+ int playlist_vert_pixels = m_lower_left_corner_y - m_upper_left_corner_y;
+ int disp_lines = min(MAX_SONGS_PER_PAGE, (playlist_vert_pixels - PLAYLIST_INNER_MARGIN*2) / GetFontHeight(PLAYLIST_FONT));
+ int total_pages = (nSongs) / disp_lines;
+
+ if (disp_lines<=0)
+ return;
+
+ // apply PgUp/PgDn keypresses since last time
+ m_playlist_pos -= m_playlist_pageups * disp_lines;
+ m_playlist_pageups = 0;
+
+ if (m_playlist_pos < 0)
+ m_playlist_pos = 0;
+ if (m_playlist_pos >= nSongs)
+ m_playlist_pos = nSongs-1;
+
+ // NOTE: 'dwFlags' is used for both DDRAW and DX9
+ DWORD dwFlags = DT_SINGLELINE;// | DT_NOPREFIX | DT_WORD_ELLIPSIS;
+ DWORD color;
+
+ int cur_page = (m_playlist_pos) / disp_lines;
+ int cur_line = (m_playlist_pos + disp_lines - 1) % disp_lines;
+ int new_top_idx = cur_page * disp_lines;
+ int new_btm_idx = new_top_idx + disp_lines;
+ wchar_t buf[1024] = {0};
+
+ // ask winamp for the song names, but DO IT BEFORE getting the DC,
+ // otherwise vaio will crash (~DDRAW port).
+ if (m_playlist_top_idx != new_top_idx ||
+ m_playlist_btm_idx != new_btm_idx)
+ {
+ for (int i=0; i<disp_lines; i++)
+ {
+ int j = new_top_idx + i;
+ if (j < nSongs)
+ {
+ // clip max len. of song name to 240 chars, to prevent overflows
+ lstrcpynW(buf, (wchar_t*)SendMessage(m_hWndWinamp, WM_USER, j, IPC_GETPLAYLISTTITLEW), 240);
+ wsprintfW(m_playlist[i], L"%d. %s ", j+1, buf); // leave an extra space @ end, so italicized fonts don't get clipped
+ }
+ }
+ }
+
+ // update playlist cache, if necessary:
+ if (m_playlist_top_idx != new_top_idx ||
+ m_playlist_btm_idx != new_btm_idx)
+ {
+ m_playlist_top_idx = new_top_idx;
+ m_playlist_btm_idx = new_btm_idx;
+ m_playlist_width_pixels = 0;
+
+ int max_w = min(m_right_edge - m_left_edge, m_lpDX->m_client_width - TEXT_MARGIN*2 - PLAYLIST_INNER_MARGIN*2);
+
+ for (int i=0; i<disp_lines; i++)
+ {
+ int j = new_top_idx + i;
+ if (j < nSongs)
+ {
+ // clip max len. of song name to 240 chars, to prevent overflows
+ //strcpy(buf, (char*)SendMessage(m_hWndWinamp, WM_USER, j, 212));
+ //buf[240] = 0;
+ //sprintf(m_playlist[i], "%d. %s ", j+1, buf); // leave an extra space @ end, so italicized fonts don't get clipped
+
+ SetRect(&r, 0, 0, max_w, 1024);
+ m_d3dx_font[PLAYLIST_FONT]->DrawTextW(NULL, m_playlist[i], -1, &r, dwFlags | DT_CALCRECT, 0xFFFFFFFF);
+ int w = r.right-r.left;
+ if (w>0)
+ m_playlist_width_pixels = max(m_playlist_width_pixels, w);
+ }
+ else
+ {
+ m_playlist[i][0] = 0;
+ }
+ }
+
+ if (m_playlist_width_pixels == 0 ||
+ m_playlist_width_pixels > max_w)
+ m_playlist_width_pixels = max_w;
+ }
+
+ int start = max(0, (cur_page)*disp_lines);
+ int end = min(nSongs, (cur_page+1)*disp_lines);
+
+ // draw dark box around where the playlist will go:
+
+ RECT r;
+ r.top = m_upper_left_corner_y;
+ r.left = m_left_edge;
+ r.right = m_left_edge + m_playlist_width_pixels + PLAYLIST_INNER_MARGIN*2;
+ r.bottom = m_upper_left_corner_y + (end-start)*GetFontHeight(PLAYLIST_FONT) + PLAYLIST_INNER_MARGIN*2;
+ DrawDarkTranslucentBox(&r);
+
+ //m_d3dx_font[PLAYLIST_FONT]->Begin();
+
+ // draw playlist text
+ int y = m_upper_left_corner_y + PLAYLIST_INNER_MARGIN;
+ for (int i=start; i<end; i++)
+ {
+ SetRect(&r, m_left_edge + PLAYLIST_INNER_MARGIN, y, m_left_edge + PLAYLIST_INNER_MARGIN + m_playlist_width_pixels, y + GetFontHeight(PLAYLIST_FONT));
+
+ if (m_lpDX->GetBitDepth() == 8)
+ color = (i==m_playlist_pos) ?
+ (i==now_playing ? 0xFFFFFFFF : 0xFFFFFFFF) :
+ (i==now_playing ? 0xFFFFFFFF : 0xFF707070);
+ else
+ color = (i==m_playlist_pos) ?
+ (i==now_playing ? PLAYLIST_COLOR_BOTH : PLAYLIST_COLOR_HILITE_TRACK) :
+ (i==now_playing ? PLAYLIST_COLOR_PLAYING_TRACK : PLAYLIST_COLOR_NORMAL);
+
+ y += m_d3dx_font[PLAYLIST_FONT]->DrawTextW(NULL, m_playlist[i-start], -1, &r, dwFlags, color);
+ }
+
+ //m_d3dx_font[PLAYLIST_FONT]->End();
+ }
+ }
+}
+
+void CPluginShell::SuggestHowToFreeSomeMem()
+{
+ // This function is called when the plugin runs out of video memory;
+ // it lets you show a messagebox to the user so you can (intelligently)
+ // suggest how to free up some video memory, based on what settings
+ // they've chosen.
+
+ wchar_t str[1024];
+
+ if (m_lpDX->m_current_mode.multisamp != D3DMULTISAMPLE_NONE)
+ {
+ if (m_lpDX->m_current_mode.screenmode == WINDOWED)
+ WASABI_API_LNGSTRINGW_BUF(IDS_TO_FREE_UP_SOME_MEMORY_RESTART_WINAMP_THEN_GO_TO_CONFIG, str, 2048);
+ else if (m_lpDX->m_current_mode.screenmode == FAKE_FULLSCREEN)
+ WASABI_API_LNGSTRINGW_BUF(IDS_TO_FREE_UP_SOME_MEMORY_RESTART_WINAMP_THEN_GO_TO_CONFIG_2, str, 2048);
+ else
+ WASABI_API_LNGSTRINGW_BUF(IDS_TO_FREE_UP_SOME_MEMORY_RESTART_WINAMP_THEN_GO_TO_CONFIG_3, str, 2048);
+ }
+ else
+ if (m_lpDX->m_current_mode.screenmode == FULLSCREEN) // true fullscreen
+ WASABI_API_LNGSTRINGW_BUF(IDS_TO_FREE_UP_VIDEO_MEMORY, str, 2048);
+ else // windowed, desktop mode, or fake fullscreen
+ WASABI_API_LNGSTRINGW_BUF(IDS_TO_FREE_UP_VIDEO_MEMORY, str, 2048);
+
+ MessageBoxW(m_lpDX->GetHwnd(), str, WASABI_API_LNGSTRINGW(IDS_MILKDROP_SUGGESTION), MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
+}
+
+LRESULT CALLBACK CPluginShell::WindowProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam)
+{
+ //if (uMsg==WM_GETDLGCODE)
+ // return DLGC_WANTALLKEYS|DLGC_WANTCHARS|DLGC_WANTMESSAGE; // this tells the embedwnd that we want keypresses to flow through to our client wnd.
+
+ if (uMsg == WM_CREATE)
+ {
+ CREATESTRUCT *create = (CREATESTRUCT *)lParam;
+ SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)create->lpCreateParams);
+ }
+
+ CPluginShell* p = (CPluginShell*)GetWindowLongPtr(hWnd,GWLP_USERDATA);
+ if (p)
+ return p->PluginShellWindowProc(hWnd, uMsg, wParam, lParam);
+ else
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+}
+
+LRESULT CPluginShell::PluginShellWindowProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam)
+{
+ USHORT mask = 1 << (sizeof(SHORT)*8 - 1);
+ //bool bShiftHeldDown = (GetKeyState(VK_SHIFT) & mask) != 0;
+ bool bCtrlHeldDown = (GetKeyState(VK_CONTROL) & mask) != 0;
+ //bool bAltHeldDown: most keys come in under WM_SYSKEYDOWN when ALT is depressed.
+
+ int i;
+#ifdef _DEBUG
+ char caption[256] = "WndProc: frame 0, ";
+ if (m_frame > 0)
+ {
+ float time = m_time;
+ int hours = (int)(time/3600);
+ time -= hours*3600;
+ int minutes = (int)(time/60);
+ time -= minutes*60;
+ int seconds = (int)time;
+ time -= seconds;
+ int dsec = (int)(time*100);
+ sprintf(caption, "WndProc: frame %d, t=%dh:%02dm:%02d.%02ds, ", m_frame, hours, minutes, seconds, dsec);
+ }
+
+ if (uMsg != WM_MOUSEMOVE &&
+ uMsg != WM_NCHITTEST &&
+ uMsg != WM_SETCURSOR &&
+ uMsg != WM_COPYDATA &&
+ uMsg != WM_USER)
+ OutputDebugMessage(caption, hWnd, uMsg, wParam, lParam);
+#endif
+
+ switch (uMsg)
+ {
+ case WM_USER:
+ if (m_screenmode == DESKTOP)
+ {
+ // this function resides in vms_desktop.dll;
+ // its response will come later, via the WM_COPYDATA
+ // message (See below).
+ //KIV: **THIS CALL CRASHES EXPLORER IN VISTA**
+ getItemData(wParam);
+ return 0;
+ }
+ break;
+
+ case WM_COPYDATA:
+ if (m_screenmode == DESKTOP)
+ {
+ // this message is vms_desktop.dll's response to
+ // our call to getItemData().
+ PCOPYDATASTRUCT c = (PCOPYDATASTRUCT)lParam;
+ if (c && (c->cbData % sizeof(icon_t) == 0))
+ {
+ icon_t *pNewIcons = (icon_t*)c->lpData;
+
+ EnterCriticalSection(&m_desktop_cs);
+
+ if (m_desktop_icon_state == 1 && (c->dwData & 0x80000000)) // if doing a total refresh...
+ {
+ // ...we build the list from zero
+ int len = c->dwData & 0xFFFF;
+ for (int i=0; i<len; i++)
+ m_icon_list.push_back(pNewIcons[i]);
+ }
+ else if (m_desktop_icon_state == 3 && !(c->dwData & 0x80000000))
+ {
+ // otherwise, we alter existing things in the list:
+ IconList::iterator p;
+ int start = c->dwData & 0xFFFF;
+ int len = c->dwData >> 16;
+
+ int i = 0;
+ for (p = m_icon_list.begin(); p != m_icon_list.end() && i<start; p++)
+ i++;
+ for (; p != m_icon_list.end() && i<start+len; p++)
+ {
+ p->x = pNewIcons[i-start].x;
+ p->y = pNewIcons[i-start].y;
+ memcpy(p->name, pNewIcons[i-start].name, sizeof(p->name));
+ memcpy(p->pidl, pNewIcons[i-start].pidl, sizeof(p->pidl));
+ i++;
+ }
+
+ m_desktop_icon_state = 2;
+ m_desktop_icon_update_frame = GetFrame();
+ }
+
+ LeaveCriticalSection(&m_desktop_cs);
+ }
+
+ return 0;
+ }
+ break;
+
+ case WM_ERASEBKGND:
+ // Repaint window when song is paused and image needs to be repainted:
+ 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
+ {
+ m_lpDX->m_lpDevice->Present(NULL,NULL,NULL,NULL);
+ return 0;
+ }
+ break;
+
+ case WM_WINDOWPOSCHANGING:
+ if (
+ m_screenmode == DESKTOP
+ && (!m_force_accept_WM_WINDOWPOSCHANGING)
+ && m_lpDX && m_lpDX->m_ready
+ )
+ {
+ // unless we requested it ourselves or it's init time,
+ // prevent the fake desktop window from moving around
+ // in the Z order! (i.e., keep it on the bottom)
+
+ // without this code, when you click on the 'real' desktop
+ // in a multimon setup, any windows that are overtop of the
+ // 'fake' desktop will flash, since they'll be covered
+ // up by the fake desktop window (but then shown again on
+ // the next frame, when we detect that the fake desktop
+ // window isn't on bottom & send it back to the bottom).
+
+ LPWINDOWPOS pwp = (LPWINDOWPOS)lParam;
+ if (pwp)
+ pwp->flags |= SWP_NOOWNERZORDER | SWP_NOZORDER;
+ }
+ if (m_screenmode==WINDOWED && m_lpDX && m_lpDX->m_ready && m_lpDX->m_current_mode.m_skin)
+ m_lpDX->SaveWindow();
+ break;
+ case WM_NCACTIVATE:
+ // *Very Important Handler!*
+ // -Without this code, the app would not work properly when running in true
+ // fullscreen mode on multiple monitors; it would auto-minimize whenever the
+ // user clicked on a window in another display.
+ if (wParam == 0 &&
+ m_screenmode == FULLSCREEN &&
+ m_frame > 0 &&
+ !m_exiting &&
+ m_lpDX &&
+ m_lpDX->m_ready
+ && m_lpDX->m_lpD3D &&
+ m_lpDX->m_lpD3D->GetAdapterCount() > 1
+ )
+ {
+ return 0;
+ }
+ break;
+
+ case WM_DESTROY:
+ // note: don't post quit message here if the window is being destroyed
+ // and re-created on a switch between windowed & FAKE fullscreen modes.
+ if (!m_lpDX->TempIgnoreDestroyMessages())
+ {
+ // this is a final exit, and not just destroy-then-recreate-the-window.
+ // so, flag DXContext so it knows that someone else
+ // will take care of destroying the window!
+ m_lpDX->OnTrulyExiting();
+ PostQuitMessage(0);
+ }
+ return FALSE;
+ break;
+ // benski> a little hack to get the window size correct. it seems to work
+ case WM_USER+555:
+ if (m_lpDX && m_lpDX->m_ready && m_screenmode==WINDOWED && !m_resizing)
+ {
+ OnUserResizeWindow();
+ m_lpDX->SaveWindow();
+ }
+ break;
+ case WM_MOVE:
+ m_lpDX->SaveWindow();
+ break;
+ case WM_SIZE:
+ // clear or set activity flag to reflect focus
+ if (m_lpDX && m_lpDX->m_ready && m_screenmode==WINDOWED && !m_resizing)
+ {
+ m_hidden = (SIZE_MAXHIDE==wParam || SIZE_MINIMIZED==wParam) ? TRUE : FALSE;
+
+ if (SIZE_MAXIMIZED==wParam || SIZE_RESTORED==wParam) // the window has been maximized or restored
+ OnUserResizeWindow();
+ }
+ break;
+
+ case WM_ENTERSIZEMOVE:
+ m_resizing = 1;
+ break;
+
+ case WM_EXITSIZEMOVE:
+ if (m_lpDX && m_lpDX->m_ready && m_screenmode==WINDOWED)
+ OnUserResizeWindow();
+ m_lpDX->SaveWindow();
+ m_resizing = 0;
+ break;
+
+ case WM_GETMINMAXINFO:
+ {
+ // don't let the window get too small
+ MINMAXINFO* p = (MINMAXINFO*)lParam;
+ if (p->ptMinTrackSize.x < 64)
+ p->ptMinTrackSize.x = 64;
+ p->ptMinTrackSize.y = p->ptMinTrackSize.x*3/4;
+ }
+ return 0;
+
+ case WM_MOUSEMOVE:
+ if (m_screenmode==DESKTOP && (m_desktop_dragging==1 || m_desktop_box==1))
+ {
+ m_desktop_drag_curpos.x = LOWORD(lParam);
+ m_desktop_drag_curpos.y = HIWORD(lParam);
+ if (m_desktop_box==1)
+ {
+ // update selection based on box coords
+ RECT box, temp;
+ box.left = min(m_desktop_drag_curpos.x, m_desktop_drag_startpos.x);
+ box.right = max(m_desktop_drag_curpos.x, m_desktop_drag_startpos.x);
+ box.top = min(m_desktop_drag_curpos.y, m_desktop_drag_startpos.y);
+ box.bottom = max(m_desktop_drag_curpos.y, m_desktop_drag_startpos.y);
+
+ IconList::iterator p;
+ for (p = m_icon_list.begin(); p != m_icon_list.end(); p++)
+ {
+ p->selected = 0;
+
+ if (IntersectRect(&temp, &box, &p->label_rect))
+ p->selected = 1;
+ else if (IntersectRect(&temp, &box, &p->icon_rect))
+ p->selected = 1;
+ }
+ }
+
+ // repaint window manually, if winamp is paused
+ if (SendMessage(m_hWndWinamp,WM_USER,0,104) != 1)
+ {
+ PushWindowToJustBeforeDesktop(GetPluginWindow());
+ DrawAndDisplay(1);
+ }
+
+ //return 0;
+ }
+ m_lpDX->SaveWindow();
+ break;
+
+ case WM_LBUTTONUP:
+ if (m_screenmode==DESKTOP)
+ {
+ if (m_desktop_dragging)
+ {
+ m_desktop_dragging = 0;
+
+ // move selected item(s) to new cursor position
+ int dx = LOWORD(lParam) - m_desktop_drag_startpos.x;
+ int dy = HIWORD(lParam) - m_desktop_drag_startpos.y;
+
+ if (dx!=0 || dy!=0)
+ {
+ int idx=0;
+ IconList::iterator p;
+ for (p = m_icon_list.begin(); p != m_icon_list.end(); p++)
+ {
+ if (p->selected)
+ {
+ SendMessage(m_hWndDesktopListView, LVM_SETITEMPOSITION, idx, MAKELPARAM(p->x + dx, p->y + dy));
+ p->x += dx;
+ p->y += dy;
+ }
+ idx++;
+ }
+ }
+
+ // repaint window manually, if winamp is paused
+ if (SendMessage(m_hWndWinamp,WM_USER,0,104) != 1)
+ {
+ PushWindowToJustBeforeDesktop(GetPluginWindow());
+ DrawAndDisplay(1);
+ }
+ }
+
+ if (m_desktop_box)
+ {
+ m_desktop_box = 0;
+
+ // repaint window manually, if winamp is paused
+ if (SendMessage(m_hWndWinamp,WM_USER,0,104) != 1)
+ {
+ PushWindowToJustBeforeDesktop(GetPluginWindow());
+ DrawAndDisplay(1);
+ }
+ }
+
+ //return 0;
+ }
+ break;
+
+ case WM_USER + 1666:
+ if (wParam == 1 && lParam == 15)
+ {
+ if (m_screenmode == FULLSCREEN || m_screenmode == FAKE_FULLSCREEN)
+ ToggleFullScreen();
+ }
+ return 0;
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONDBLCLK:
+ case WM_RBUTTONDOWN:
+ case WM_RBUTTONUP:
+ // Toggle between Fullscreen and Windowed modes on double-click
+ // note: this requires the 'CS_DBLCLKS' windowclass style!
+ if (m_screenmode != DESKTOP)
+ {
+ SetFocus(hWnd);
+ if (uMsg==WM_LBUTTONDBLCLK && m_frame>0)
+ {
+ ToggleFullScreen();
+ return 0;
+ }
+ }
+ else
+ {
+ POINT pt;
+ pt.x = LOWORD(lParam);
+ pt.y = HIWORD(lParam);
+
+ int done = 0;
+
+ for (int pass=0; pass<2 && !done; pass++)
+ {
+ IconList::iterator p;
+ for (p = m_icon_list.begin(); p != m_icon_list.end(); p++)
+ {
+ RECT *pr = (pass==0) ? &p->icon_rect : &p->label_rect;
+ int bottom_extend = (pass==0) ? 3 : 0; // accepts clicks in the 3-pixel gap between the icon and the text label.
+ if (pt.x >= pr->left &&
+ pt.x <= pr->right &&
+ pt.y >= pr->top &&
+ pt.y <= pr->bottom + bottom_extend)
+ {
+ switch (uMsg)
+ {
+ case WM_RBUTTONUP:
+ //pt.x += m_lpDX->m_monitor_rect.left;
+ //pt.y += m_lpDX->m_monitor_rect.top;
+ DoExplorerMenu(GetPluginWindow(), (LPITEMIDLIST)p->pidl, pt);
+ break;
+ case WM_LBUTTONDBLCLK:
+ {
+ char buf[MAX_PATH];
+ sprintf(buf, "%s\\%s", m_szDesktopFolder, p->name);
+ ExecutePidl((LPITEMIDLIST)p->pidl, buf, m_szDesktopFolder, GetPluginWindow());
+ }
+ break;
+ case WM_LBUTTONDOWN:
+ m_desktop_dragging = 1;
+ memcpy(m_desktop_drag_pidl, p->pidl, sizeof(m_desktop_drag_pidl));
+ m_desktop_drag_startpos.x = LOWORD(lParam);
+ m_desktop_drag_startpos.y = HIWORD(lParam);
+ m_desktop_drag_curpos.x = LOWORD(lParam);
+ m_desktop_drag_curpos.y = HIWORD(lParam);
+ if (!(wParam & MK_CONTROL)) // if CTRL not held down
+ {
+ if (!p->selected)
+ {
+ DeselectDesktop();
+ p->selected = 1;
+ }
+ }
+ else
+ {
+ p->selected = 1-p->selected;
+ }
+ break;
+ case WM_RBUTTONDOWN:
+ DeselectDesktop();
+ p->selected = 1;
+ break;
+ }
+
+ done = 1;
+ break;
+ }
+ }
+ }
+
+ if (!done)
+ {
+ // deselect all, unless they're CTRL+clicking and missed an icon.
+ if (uMsg!=WM_LBUTTONDOWN || !(wParam & MK_CONTROL))
+ DeselectDesktop();
+
+ if (uMsg==WM_RBUTTONUP)// || uMsg==WM_RBUTTONDOWN)
+ {
+ // note: can't use GetMenu and TrackPopupMenu here because the hwnd param to TrackPopupMenu must belong to current application.
+
+ // (before sending coords to desktop window, xform them into its client coords:)
+ POINT pt;
+ pt.x = LOWORD(lParam);
+ pt.y = HIWORD(lParam);
+ ScreenToClient(m_hWndDesktopListView, &pt);
+ lParam = MAKELPARAM(pt.x + m_lpDX->m_monitor_rect.left, pt.y + m_lpDX->m_monitor_rect.top);
+
+ PostMessage(m_hWndDesktopListView, uMsg, wParam, lParam);
+ //PostMessage(m_hWndDesktopListView, WM_CONTEXTMENU, (WPARAM)m_hWndDesktopListView, lParam);
+ }
+ else if (uMsg==WM_LBUTTONDOWN)
+ {
+ m_desktop_box = 1;
+ m_desktop_drag_startpos.x = LOWORD(lParam);
+ m_desktop_drag_startpos.y = HIWORD(lParam);
+ m_desktop_drag_curpos.x = LOWORD(lParam);
+ m_desktop_drag_curpos.y = HIWORD(lParam);
+ }
+ }
+
+ // repaint window manually, if winamp is paused
+ if (SendMessage(m_hWndWinamp,WM_USER,0,104) != 1)
+ {
+ PushWindowToJustBeforeDesktop(GetPluginWindow());
+ DrawAndDisplay(1);
+ }
+
+ //return 0;
+ }
+ break;
+
+ case WM_SETFOCUS:
+ // note: this msg never comes in when embedwnd is used, but that's ok, because that's only
+ // in Windowed mode, and m_lost_focus only makes us sleep when fullscreen.
+ m_lost_focus = 0;
+ break;
+
+ case WM_KILLFOCUS:
+ // note: this msg never comes in when embedwnd is used, but that's ok, because that's only
+ // in Windowed mode, and m_lost_focus only makes us sleep when fullscreen.
+ m_lost_focus = 1;
+ break;
+
+ case WM_SETCURSOR:
+ if (
+ (m_screenmode == FULLSCREEN) ||
+ (m_screenmode == FAKE_FULLSCREEN && m_lpDX->m_fake_fs_covers_all)
+ )
+ {
+ // hide the cursor
+ SetCursor(NULL);
+ return TRUE; // prevent Windows from setting cursor to window class cursor
+ }
+ break;
+
+ case WM_NCHITTEST:
+ // Prevent the user from selecting the menu in fullscreen mode
+ if (m_screenmode != WINDOWED)
+ return HTCLIENT;
+ break;
+
+ case WM_SYSCOMMAND:
+ // Prevent *moving/sizing* and *entering standby mode* when in fullscreen mode
+ switch (wParam)
+ {
+ case SC_MOVE:
+ case SC_SIZE:
+ case SC_MAXIMIZE:
+ case SC_KEYMENU:
+ if (m_screenmode != WINDOWED)
+ return 1;
+ break;
+ case SC_MONITORPOWER:
+ if (m_screenmode == FULLSCREEN || m_screenmode == FAKE_FULLSCREEN)
+ return 1;
+ break;
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ // launch popup context menu. see handler for WM_COMMAND also.
+ if (m_screenmode == DESKTOP)
+ {
+ // note: execution should never reach this point,
+ // because we don't pass WM_RBUTTONUP to DefWindowProc
+ // when in desktop mode!
+ return 0;
+ }
+ else if (m_screenmode == WINDOWED) // context menus only allowed in ~windowed modes
+ {
+ TrackPopupMenuEx(m_context_menu, TPM_VERTICAL, LOWORD(lParam), HIWORD(lParam), hWnd, NULL);
+ return 0;
+ }
+ break;
+
+ case WM_COMMAND:
+ // handle clicks on items on context menu.
+ if (m_screenmode == WINDOWED)
+ {
+ switch (LOWORD(wParam))
+ {
+ case ID_QUIT:
+ m_exiting = 1;
+ PostMessage(hWnd, WM_CLOSE, 0, 0);
+ return 0;
+ case ID_GO_FS:
+ if (m_frame > 0)
+ ToggleFullScreen();
+ return 0;
+ case ID_DESKTOP_MODE:
+ if (m_frame > 0)
+ ToggleDesktop();
+ return 0;
+ case ID_SHOWHELP:
+ ToggleHelp();
+ return 0;
+ case ID_SHOWPLAYLIST:
+ TogglePlaylist();
+ return 0;
+ }
+ // then allow the plugin to override any command:
+ if (MyWindowProc(hWnd, uMsg, wParam, lParam) == 0)
+ return 0;
+ }
+ break;
+
+ /*
+ KEY HANDLING: the basic idea:
+ -in all cases, handle or capture:
+ -ZXCVBRS, zxcvbrs
+ -also make sure it's case-insensitive! (lowercase come through only as WM_CHAR; uppercase come in as both)
+ -(ALT+ENTER)
+ -(F1, ESC, UP, DN, Left, Right, SHIFT+l/r)
+ -(P for playlist)
+ -when playlist showing: steal J, HOME, END, PGUP, PGDN, UP, DOWN, ESC
+ -(BLOCK J, L)
+ -when integrated with winamp (using embedwnd), also handle these keys:
+ -j, l, L, CTRL+L [windowed mode only!]
+ -CTRL+P, CTRL+D
+ -CTRL+TAB
+ -ALT-E
+ -ALT+F (main menu)
+ -ALT+3 (id3)
+ */
+
+ case WM_SYSKEYDOWN:
+ if (wParam==VK_RETURN && m_frame > 0)
+ {
+ ToggleFullScreen();
+ return 0;
+ }
+ // if in embedded mode (using winamp skin), pass ALT+ keys on to winamp
+ // ex: ALT+E, ALT+F, ALT+3...
+ if (m_screenmode==WINDOWED && m_lpDX->m_current_mode.m_skin)
+ return PostMessage(m_hWndWinamp, uMsg, wParam, lParam); // force-pass to winamp; required for embedwnd
+ break;
+
+ case WM_SYSKEYUP:
+ if (m_screenmode==WINDOWED && m_lpDX->m_current_mode.m_skin)
+ return PostMessage(m_hWndWinamp, uMsg, wParam, lParam); // force-pass to winamp; required for embedwnd
+ break;
+
+ case WM_SYSCHAR:
+ if ((wParam=='k' || wParam=='K'))
+ {
+ OnAltK();
+ return 0;
+ }
+ if ((wParam=='d' || wParam=='D') && m_frame > 0)
+ {
+ ToggleDesktop();
+ return 0;
+ }
+ break;
+
+ case WM_CHAR:
+ // if playlist is showing, steal p/j keys from the plugin:
+ if (m_show_playlist)
+ {
+ switch (wParam)
+ {
+ case 'j':
+ case 'J':
+ m_playlist_pos = SendMessage(m_hWndWinamp,WM_USER, 0, 125);
+ return 0;
+ default:
+ {
+ int nSongs = SendMessage(m_hWndWinamp,WM_USER, 0, 124);
+ int found = 0;
+ int orig_pos = m_playlist_pos;
+ int inc = (wParam>='A' && wParam<='Z') ? -1 : 1;
+ while (1)
+ {
+ if (inc==1 && m_playlist_pos >= nSongs-1)
+ break;
+ if (inc==-1 && m_playlist_pos <= 0)
+ break;
+
+ m_playlist_pos += inc;
+
+ char buf[32];
+ strncpy(buf, (char*)SendMessage(m_hWndWinamp, WM_USER, m_playlist_pos, 212), 31);
+ buf[31] = 0;
+
+ // remove song # and period from beginning
+ char *p = buf;
+ while (*p >= '0' && *p <= '9') p++;
+ if (*p == '.' && *(p+1) == ' ')
+ {
+ p += 2;
+ int pos = 0;
+ while (*p != 0)
+ {
+ buf[pos++] = *p;
+ p++;
+ }
+ buf[pos++] = 0;
+ }
+
+ int wParam2 = (wParam>='A' && wParam<='Z') ? (wParam + 'a'-'A') : (wParam + 'A'-'a');
+ if (buf[0]==wParam || buf[0]==wParam2)
+ {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ m_playlist_pos = orig_pos;
+ }
+ return 0;
+ }
+ }
+
+ // then allow the plugin to override any keys:
+ if (MyWindowProc(hWnd, uMsg, wParam, lParam) == 0)
+ return 0;
+
+ // finally, default key actions:
+ if (wParam == keyMappings[5] || wParam == keyMappings[6]) // 'z' or 'Z'
+ {
+ PostMessage(m_hWndWinamp,WM_COMMAND,40044,0);
+ return 0;
+ }
+ else
+ {
+ switch (wParam)
+ {
+ // WINAMP PLAYBACK CONTROL KEYS:
+ case 'x':
+ case 'X':
+ PostMessage(m_hWndWinamp,WM_COMMAND,40045,0);
+ return 0;
+ case 'c':
+ case 'C':
+ PostMessage(m_hWndWinamp,WM_COMMAND,40046,0);
+ return 0;
+ case 'v':
+ case 'V':
+ PostMessage(m_hWndWinamp,WM_COMMAND,40047,0);
+ return 0;
+ case 'b':
+ case 'B':
+ PostMessage(m_hWndWinamp,WM_COMMAND,40048,0);
+ return 0;
+ case 's':
+ case 'S':
+ //if (SendMessage(m_hWndWinamp,WM_USER,0,250))
+ // sprintf(m_szUserMessage, "shuffle is now OFF"); // shuffle was on
+ //else
+ // sprintf(m_szUserMessage, "shuffle is now ON"); // shuffle was off
+
+ // toggle shuffle
+ PostMessage(m_hWndWinamp,WM_COMMAND,40023,0);
+ return 0;
+ case 'r':
+ case 'R':
+ // toggle repeat
+ PostMessage(m_hWndWinamp,WM_COMMAND,40022,0);
+ return 0;
+ case 'p':
+ case 'P':
+ TogglePlaylist();
+ return 0;
+ case 'l':
+ // note that this is actually correct; when you hit 'l' from the
+ // MAIN winamp window, you get an "open files" dialog; when you hit
+ // 'l' from the playlist editor, you get an "add files to playlist" dialog.
+ // (that sends IDC_PLAYLIST_ADDMP3==1032 to the playlist, which we can't
+ // do from here.)
+ PostMessage(m_hWndWinamp,WM_COMMAND,40029,0);
+ return 0;
+ case 'L':
+ PostMessage(m_hWndWinamp,WM_COMMAND,40187,0);
+ return 0;
+ case 'j':
+ PostMessage(m_hWndWinamp,WM_COMMAND,40194,0);
+ return 0;
+ }
+
+ return 0;//DefWindowProc(hWnd,uMsg,wParam,lParam);
+ }
+ break; // end case WM_CHAR
+
+ case WM_KEYUP:
+
+ // allow the plugin to override any keys:
+ if (MyWindowProc(hWnd, uMsg, wParam, lParam) == 0)
+ return 0;
+
+ /*
+ switch(wParam)
+ {
+ case VK_SOMETHING:
+ ...
+ break;
+ }
+ */
+
+ return 0;
+ break;
+
+ case WM_KEYDOWN:
+ if (m_show_playlist)
+ {
+ switch (wParam)
+ {
+ case VK_ESCAPE:
+ if(m_show_playlist)
+ TogglePlaylist();
+ //m_show_playlist = 0;
+ return 0;
+
+ case VK_UP:
+ {
+ int nRepeat = lParam & 0xFFFF;
+ if (GetKeyState(VK_SHIFT) & mask)
+ m_playlist_pos -= 10*nRepeat;
+ else
+ m_playlist_pos -= nRepeat;
+ }
+ return 0;
+
+ case VK_DOWN:
+ {
+ int nRepeat = lParam & 0xFFFF;
+ if (GetKeyState(VK_SHIFT) & mask)
+ m_playlist_pos += 10*nRepeat;
+ else
+ m_playlist_pos += nRepeat;
+ }
+ return 0;
+
+ case VK_HOME:
+ m_playlist_pos = 0;
+ return 0;
+
+ case VK_END:
+ m_playlist_pos = SendMessage(m_hWndWinamp,WM_USER, 0, 124) - 1;
+ return 0;
+
+ case VK_PRIOR:
+ if (GetKeyState(VK_SHIFT) & mask)
+ m_playlist_pageups += 10;
+ else
+ m_playlist_pageups++;
+ return 0;
+
+ case VK_NEXT:
+ if (GetKeyState(VK_SHIFT) & mask)
+ m_playlist_pageups -= 10;
+ else
+ m_playlist_pageups--;
+ return 0;
+
+ case VK_RETURN:
+ SendMessage(m_hWndWinamp,WM_USER, m_playlist_pos, 121); // set sel
+ SendMessage(m_hWndWinamp,WM_COMMAND, 40045, 0); // play it
+ return 0;
+ }
+ }
+
+ // allow the plugin to override any keys:
+ if (MyWindowProc(hWnd, uMsg, wParam, lParam) == 0)
+ return 0;
+
+ switch (wParam)
+ {
+ case VK_F1:
+ m_show_press_f1_msg = 0;
+ ToggleHelp();
+ return 0;
+
+ case VK_ESCAPE:
+ if (m_show_help)
+ ToggleHelp();
+ else
+ {
+ if (m_screenmode == FAKE_FULLSCREEN || m_screenmode == FULLSCREEN)
+ {
+ ToggleFullScreen();
+ }
+ else if (m_screenmode == DESKTOP)
+ {
+ ToggleDesktop();
+ }
+ // exit the program on escape
+ //m_exiting = 1;
+ //PostMessage(hWnd, WM_CLOSE, 0, 0);
+ }
+ return 0;
+
+ case VK_UP:
+ // increase volume
+ {
+ int nRepeat = lParam & 0xFFFF;
+ for (i=0; i<nRepeat*2; i++) PostMessage(m_hWndWinamp,WM_COMMAND,40058,0);
+ }
+ return 0;
+
+ case VK_DOWN:
+ // decrease volume
+ {
+ int nRepeat = lParam & 0xFFFF;
+ for (i=0; i<nRepeat*2; i++) PostMessage(m_hWndWinamp,WM_COMMAND,40059,0);
+ }
+ return 0;
+
+ case VK_LEFT:
+ case VK_RIGHT:
+ {
+ bool bShiftHeldDown = (GetKeyState(VK_SHIFT) & mask) != 0;
+ int cmd = (wParam == VK_LEFT) ? 40144 : 40148;
+ int nRepeat = lParam & 0xFFFF;
+ int reps = (bShiftHeldDown) ? 6*nRepeat : 1*nRepeat;
+
+ for (int i=0; i<reps; i++)
+ PostMessage(m_hWndWinamp,WM_COMMAND,cmd,0);
+ }
+ return 0;
+ default:
+ // pass CTRL+A thru CTRL+Z, and also CTRL+TAB, to winamp, *if we're in windowed mode* and using an embedded window.
+ // be careful though; uppercase chars come both here AND to WM_CHAR handler,
+ // so we have to eat some of them here, to avoid them from acting twice.
+ if (m_screenmode==WINDOWED && m_lpDX && m_lpDX->m_current_mode.m_skin)
+ {
+ if (bCtrlHeldDown && ((wParam >= 'A' && wParam <= 'Z') || wParam==VK_TAB))
+ {
+ PostMessage(m_hWndWinamp, uMsg, wParam, lParam);
+ return 0;
+ }
+ }
+ return 0;
+ }
+
+ return 0;
+ break;
+ }
+
+ return MyWindowProc(hWnd, uMsg, wParam, lParam);//DefWindowProc(hWnd, uMsg, wParam, lParam);
+ //return 0L;
+}
+
+LRESULT CALLBACK CPluginShell::DesktopWndProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam)
+{
+ CPluginShell* p = (CPluginShell*)GetWindowLongPtr(hWnd,GWLP_USERDATA);
+ if (p)
+ return p->PluginShellDesktopWndProc(hWnd, uMsg, wParam, lParam);
+ else
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+LRESULT CPluginShell::PluginShellDesktopWndProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam)
+{
+ //#ifdef _DEBUG
+ // OutputDebugMessage("kbfocus", hWnd, uMsg, wParam, lParam);
+ //#endif
+
+ switch (uMsg)
+ {
+ case WM_KEYDOWN:
+ case WM_KEYUP:
+ case WM_CHAR:
+ case WM_SYSCHAR:
+ case WM_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ //PostMessage(GetPluginWindow(), uMsg, wParam, lParam);
+ PluginShellWindowProc(GetPluginWindow(), uMsg, wParam, lParam);
+ return 0;
+ break;
+ }
+
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+void CPluginShell::AlignWaves()
+{
+ // align waves, using recursive (mipmap-style) least-error matching
+ // note: NUM_WAVEFORM_SAMPLES must be between 32 and 576.
+
+ int align_offset[2] = { 0, 0 };
+
+#if (NUM_WAVEFORM_SAMPLES < 576) // [don't let this code bloat our DLL size if it's not going to be used]
+
+ int nSamples = NUM_WAVEFORM_SAMPLES;
+
+#define MAX_OCTAVES 10
+
+ int octaves = (int)floorf(logf((float)(576-nSamples))/logf(2.0f));
+ if (octaves < 4)
+ return;
+ if (octaves > MAX_OCTAVES)
+ octaves = MAX_OCTAVES;
+
+ for (int ch=0; ch<2; ch++)
+ {
+ // only worry about matching the lower 'nSamples' samples
+ float temp_new[MAX_OCTAVES][576];
+ float temp_old[MAX_OCTAVES][576];
+ static float temp_weight[MAX_OCTAVES][576];
+ static int first_nonzero_weight[MAX_OCTAVES];
+ static int last_nonzero_weight[MAX_OCTAVES];
+ int spls[MAX_OCTAVES];
+ int space[MAX_OCTAVES];
+
+ memcpy(temp_new[0], m_sound.fWaveform[ch], sizeof(float)*576);
+ memcpy(temp_old[0], &m_oldwave[ch][m_prev_align_offset[ch]], sizeof(float)*nSamples);
+ spls[0] = 576;
+ space[0] = 576 - nSamples;
+
+ // potential optimization: could reuse (instead of recompute) mip levels for m_oldwave[2][]?
+ int octave = 0;
+ for (octave=1; octave<octaves; octave++)
+ {
+ spls[octave] = spls[octave-1]/2;
+ space[octave] = space[octave-1]/2;
+ for (int n=0; n<spls[octave]; n++)
+ {
+ temp_new[octave][n] = 0.5f*(temp_new[octave-1][n*2] + temp_new[octave-1][n*2+1]);
+ temp_old[octave][n] = 0.5f*(temp_old[octave-1][n*2] + temp_old[octave-1][n*2+1]);
+ }
+ }
+
+ if (!m_align_weights_ready)
+ {
+ m_align_weights_ready = 1;
+ for (octave=0; octave<octaves; octave++)
+ {
+ int compare_samples = spls[octave] - space[octave];
+ int n = 0;
+ for (n=0; n<compare_samples; n++)
+ {
+ // start with pyramid-shaped pdf, from 0..1..0
+ if (n < compare_samples/2)
+ temp_weight[octave][n] = n*2/(float)compare_samples;
+ else
+ temp_weight[octave][n] = (compare_samples-1 - n)*2/(float)compare_samples;
+
+ // TWEAK how much the center matters, vs. the edges:
+ temp_weight[octave][n] = (temp_weight[octave][n] - 0.8f)*5.0f + 0.8f;
+
+ // clip:
+ if (temp_weight[octave][n]>1) temp_weight[octave][n] = 1;
+ if (temp_weight[octave][n]<0) temp_weight[octave][n] = 0;
+ }
+
+ n = 0;
+ while (temp_weight[octave][n] == 0 && n < compare_samples)
+ n++;
+ first_nonzero_weight[octave] = n;
+
+ n = compare_samples-1;
+ while (temp_weight[octave][n] == 0 && n >= 0)
+ n--;
+ last_nonzero_weight[octave] = n;
+ }
+ }
+
+ int n1 = 0;
+ int n2 = space[octaves-1];
+ for (octave = octaves-1; octave>=0; octave--)
+ {
+ // for example:
+ // space[octave] == 4
+ // spls[octave] == 36
+ // (so we test 32 samples, w/4 offsets)
+ int compare_samples = spls[octave]-space[octave];
+
+ int lowest_err_offset = -1;
+ float lowest_err_amount = 0;
+ for (int n=n1; n<n2; n++)
+ {
+ float err_sum = 0;
+ //for (int i=0; i<compare_samples; i++)
+ for (int i=first_nonzero_weight[octave]; i<=last_nonzero_weight[octave]; i++)
+ {
+ float x = (temp_new[octave][i+n] - temp_old[octave][i]) * temp_weight[octave][i];
+ if (x>0)
+ err_sum += x;
+ else
+ err_sum -= x;
+ }
+
+ if (lowest_err_offset == -1 || err_sum < lowest_err_amount)
+ {
+ lowest_err_offset = n;
+ lowest_err_amount = err_sum;
+ }
+ }
+
+ // now use 'lowest_err_offset' to guide bounds of search in next octave:
+ // space[octave] == 8
+ // spls[octave] == 72
+ // -say 'lowest_err_offset' was 2
+ // -that corresponds to samples 4 & 5 of the next octave
+ // -also, expand about this by 2 samples? YES.
+ // (so we'd test 64 samples, w/8->4 offsets)
+ if (octave > 0)
+ {
+ n1 = lowest_err_offset*2 -1;
+ n2 = lowest_err_offset*2+2+1;
+ if (n1 < 0) n1=0;
+ if (n2 > space[octave-1]) n2 = space[octave-1];
+ }
+ else
+ align_offset[ch] = lowest_err_offset;
+ }
+ }
+#endif
+ memcpy(m_oldwave[0], m_sound.fWaveform[0], sizeof(float)*576);
+ memcpy(m_oldwave[1], m_sound.fWaveform[1], sizeof(float)*576);
+ m_prev_align_offset[0] = align_offset[0];
+ m_prev_align_offset[1] = align_offset[1];
+
+ // finally, apply the results: modify m_sound.fWaveform[2][0..576]
+ // by scooting the aligned samples so that they start at m_sound.fWaveform[2][0].
+ for (int ch=0; ch<2; ch++)
+ if (align_offset[ch]>0)
+ {
+ for (int i=0; i<nSamples; i++)
+ m_sound.fWaveform[ch][i] = m_sound.fWaveform[ch][i+align_offset[ch]];
+ // zero the rest out, so it's visually evident that these samples are now bogus:
+ memset(&m_sound.fWaveform[ch][nSamples], 0, (576-nSamples)*sizeof(float));
+ }
+}
+
+LRESULT CALLBACK CPluginShell::VJModeWndProc(HWND hWnd, unsigned uMsg, WPARAM wParam, LPARAM lParam)
+{
+ CPluginShell* p = (CPluginShell*)GetWindowLongPtr(hWnd,GWLP_USERDATA);
+ if (p)
+ return p->PluginShellVJModeWndProc(hWnd, uMsg, wParam, lParam);
+ else
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+LRESULT CPluginShell::PluginShellVJModeWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+#ifdef _DEBUG
+ if (message != WM_MOUSEMOVE &&
+ message != WM_NCHITTEST &&
+ message != WM_SETCURSOR &&
+ message != WM_COPYDATA &&
+ message != WM_USER)
+ {
+ char caption[256] = "VJWndProc: frame 0, ";
+ if (m_frame > 0)
+ {
+ float time = m_time;
+ int hours = (int)(time/3600);
+ time -= hours*3600;
+ int minutes = (int)(time/60);
+ time -= minutes*60;
+ int seconds = (int)time;
+ time -= seconds;
+ int dsec = (int)(time*100);
+ sprintf(caption, "VJWndProc: frame %d, t=%dh:%02dm:%02d.%02ds, ", m_frame, hours, minutes, seconds, dsec);
+ }
+ OutputDebugMessage(caption, hwnd, message, wParam, lParam);
+ }
+#endif
+
+ switch (message)
+ {
+ case WM_KEYDOWN:
+ case WM_KEYUP:
+ case WM_CHAR:
+ case WM_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ case WM_SYSCHAR:
+ // pass keystrokes on to plugin!
+ return PluginShellWindowProc(GetPluginWindow(),message,wParam,lParam);
+
+ case WM_ERASEBKGND:
+ // Repaint window when song is paused and image needs to be repainted:
+ 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
+ {
+ m_vjd3d9_device->Present(NULL,NULL,NULL,NULL);
+ return 0;
+ }
+ break;
+
+ /*
+ case WM_WINDOWPOSCHANGING:
+ if (m_screenmode == DESKTOP)
+ {
+ LPWINDOWPOS pwp = (LPWINDOWPOS)lParam;
+ if (pwp)
+ pwp->flags |= SWP_NOOWNERZORDER | SWP_NOZORDER;
+ }
+ break;
+
+ case WM_ACTIVATEAPP:
+ // *Very Important Handler!*
+ // -Without this code, the app would not work properly when running in true
+ // fullscreen mode on multiple monitors; it would auto-minimize whenever the
+ // user clicked on a window in another display.
+ if (wParam == 1 &&
+ m_screenmode == DESKTOP &&
+ m_frame > 0 &&
+ !m_exiting
+ )
+ {
+ return 0;
+ }
+ break;
+
+ /*
+ case WM_NCACTIVATE:
+ // *Very Important Handler!*
+ // -Without this code, the app would not work properly when running in true
+ // fullscreen mode on multiple monitors; it would auto-minimize whenever the
+ // user clicked on a window in another display.
+ // (NOTE: main window also handles this message this way)
+ if (wParam == 0 &&
+ m_screenmode == FULLSCREEN &&
+ m_frame > 0 &&
+ !m_exiting &&
+ m_lpDX &&
+ m_lpDX->m_ready
+ && m_lpDX->m_lpD3D &&
+ m_lpDX->m_lpD3D->GetAdapterCount() > 1
+ )
+ {
+ return 0;
+ }
+ break;
+ */
+
+ /*
+ case WM_ACTIVATEAPP:
+ if (wParam == 1 &&
+ m_screenmode == DESKTOP &&
+ m_frame > 0 &&
+ !m_exiting &&
+ m_vjd3d9_device
+ )
+ {
+ return 0;
+ }
+ break;
+ */
+
+ /*
+ case WM_WINDOWPOSCHANGING:
+ if (
+ m_screenmode == DESKTOP
+ && (!m_force_accept_WM_WINDOWPOSCHANGING)
+ && m_lpDX && m_lpDX->m_ready
+ )
+ {
+ // unless we requested it ourselves or it's init time,
+ // prevent the fake desktop window from moving around
+ // in the Z order! (i.e., keep it on the bottom)
+
+ // without this code, when you click on the 'real' desktop
+ // in a multimon setup, any windows that are overtop of the
+ // 'fake' desktop will flash, since they'll be covered
+ // up by the fake desktop window (but then shown again on
+ // the next frame, when we detect that the fake desktop
+ // window isn't on bottom & send it back to the bottom).
+
+ LPWINDOWPOS pwp = (LPWINDOWPOS)lParam;
+ if (pwp)
+ pwp->flags |= SWP_NOOWNERZORDER | SWP_NOZORDER;
+ }
+ break;
+ */
+
+ case WM_CLOSE:
+ // if they close the VJ window (by some means other than ESC key),
+ // this will make the graphics window close, too.
+ m_exiting = 1;
+ if (GetPluginWindow())
+ PostMessage(GetPluginWindow(), WM_CLOSE, 0, 0);
+ break;
+
+ case WM_GETMINMAXINFO:
+ {
+ // don't let the window get too small
+ MINMAXINFO* p = (MINMAXINFO*)lParam;
+ if (p->ptMinTrackSize.x < 64)
+ p->ptMinTrackSize.x = 64;
+ p->ptMinTrackSize.y = p->ptMinTrackSize.x*3/4;
+ }
+ return 0;
+
+ case WM_SIZE:
+ // clear or set activity flag to reflect focus
+ if (m_vjd3d9_device && !m_resizing_textwnd)
+ {
+ m_hidden_textwnd = (SIZE_MAXHIDE==wParam || SIZE_MINIMIZED==wParam) ? TRUE : FALSE;
+
+ if (SIZE_MAXIMIZED==wParam || SIZE_RESTORED==wParam) // the window has been maximized or restored
+ OnUserResizeTextWindow();
+ }
+ break;
+
+ case WM_ENTERSIZEMOVE:
+ m_resizing_textwnd = 1;
+ break;
+
+ case WM_EXITSIZEMOVE:
+ if (m_vjd3d9_device)
+ OnUserResizeTextWindow();
+ m_resizing_textwnd = 0;
+ break;
+ }
+
+ return DefWindowProc(hwnd, message, wParam, lParam);
+} \ No newline at end of file