//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //===========================================================================// #define WIN32_LEAN_AND_MEAN #define OEMRESOURCE // SRC only #define PROTECTED_THINGS_DISABLE #include #include #include #include #pragma warning( disable : 4201 ) #include #pragma warning( default : 4201 ) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vgui_internal.h" #include "bitmap.h" #include "VPanel.h" #include "utlvector.h" #include "utlsymbol.h" #include "tier1/utldict.h" #include "filesystem.h" #include "SteamBootStrapper.h" #include "vgui_surfacelib/Win32Font.h" #include "vgui_surfacelib/FontManager.h" #include "vgui_key_translation.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #ifdef PlaySound #undef PlaySound #endif #ifdef CreateFont #undef CreateFont #endif using namespace vgui; #define PLAT(vpanel) (((VPanel *)vpanel)->Plat()) namespace vgui { class SurfacePlat { public: HWND hwnd; HDC hdc; HDC hwndDC; HDC textureDC; HGLRC hglrc; HRGN clipRgn; HBITMAP bitmap; int bitmapSize[2]; int restoreInfo[4]; bool isFullscreen; bool disabled; // whether the window can take user input int fullscreenInfo[3]; VPanel *embeddedPanel; bool isToolbar; // whether it has an icon in the tool tray or not HICON notifyIcon; VPANEL notifyPanel; HPanel lastKeyFocusIndex; // index to the last panel to have the focus }; class Texture { public: int _id; HBITMAP _bitmap; HBITMAP _maskBitmap; bool _bMask; HICON _icon; int _wide; int _tall; void *_dib; void *_maskDib; const char *_filename; }; } //----------------------------------------------------------------------------- // Purpose: Implementation of ISurface for use running under windows, renders using GDI // This is not used in the game, EngineSurface is used instead //----------------------------------------------------------------------------- class CWin32Surface : public ISurface { public: friend class CIconImage; CWin32Surface(); ~CWin32Surface(); virtual void Shutdown(); virtual void RunFrame(); virtual VPANEL GetEmbeddedPanel(); virtual void SetEmbeddedPanel( VPANEL panel); // initializes the surface with the current window state virtual void SetCurrentContextPanel(VPANEL panel); virtual void PushMakeCurrent(VPANEL panel,bool useInsets); virtual void PopMakeCurrent(VPANEL panel); virtual void DrawSetColor(int r, int g, int b, int a); virtual void DrawSetColor(Color col); virtual void DrawFilledRect(int x0, int y0, int x1, int y1); virtual void DrawFilledRectFastFade( int x0, int y0, int x1, int y1, int fadeStartPt, int fadeEndPt, unsigned int alpha0, unsigned int alpha1, bool bHorizontal ); virtual void DrawFilledRectFade( int x0, int y0, int x1, int y1, unsigned int alpha0, unsigned int alpha1, bool bHorizontal ); virtual void DrawFilledRectArray( IntRect *pRects, int numRects ); virtual void DrawOutlinedRect(int x0, int y0, int x1, int y1); virtual void DrawLine(int x0, int y0, int x1, int y1); virtual void DrawPolyLine(int *px, int *py, int numPoints); virtual void DrawSetTextFont(HFont font); virtual void DrawSetTextColor(int r, int g, int b, int a); virtual void DrawSetTextColor(Color col); virtual void DrawSetTextPos(int x, int y); virtual void DrawSetTextScale(float sx, float sy); virtual void DrawPrintText(const wchar_t *, int textLen, FontDrawType_t drawType = FONT_DRAW_DEFAULT); virtual void DrawUnicodeChar(wchar_t wch, FontDrawType_t drawType = FONT_DRAW_DEFAULT ); virtual void DrawUnicodeString( const wchar_t *pwString, FontDrawType_t drawType = FONT_DRAW_DEFAULT ); virtual void DrawGetTextPos(int& x,int& y); virtual bool DrawGetTextureFile(int id, char *filename, int maxlen ); virtual int DrawGetTextureId( char const *filename ); virtual void DrawSetTextureFile(int id, const char *filename, int hardwareFilter, bool forceReload = false); virtual void DrawSetTexture(int id); virtual void DrawSetTextureRGBA(int id, const unsigned char *rgba, int wide, int tall, int hardwareFilter, bool forceReload = false); virtual void DrawSetTextureRGBAEx(int id, const unsigned char *rgba, int wide, int tall, ImageFormat imageFormat ); virtual void DrawGetTextureSize(int id, int &wide, int &tall); virtual IVguiMatInfo *DrawGetTextureMatInfoFactory( int id ) { return NULL; } virtual void DrawTexturedRect(int x0, int y0, int x1, int y1); virtual int CreateNewTextureID( bool procedural ); virtual bool IsTextureIDValid(int id); virtual void FreeTextureData( Texture *pTexture ); virtual bool DeleteTextureByID(int id); virtual void DrawFlushText(); virtual IHTML *CreateHTMLWindow(vgui::IHTMLEvents *events, VPANEL context); virtual void PaintHTMLWindow(IHTML *htmlwin); virtual void DeleteHTMLWindow(IHTML *htmlwin); virtual VPANEL GetNotifyPanel(); virtual void SetNotifyIcon(VPANEL context, HTexture icon, VPANEL panelToReceiveMessages, const char *text); virtual void SetPanelForInput( VPANEL vpanel ); virtual void GetScreenSize(int &wide, int &tall); virtual void SetAsTopMost(VPANEL panel, bool state); virtual void BringToFront(VPANEL panel); virtual void SetForegroundWindow(VPANEL panel); virtual void SetPanelVisible(VPANEL panel, bool visible); virtual void SetMinimized(VPANEL panel, bool state); virtual bool IsMinimized(VPANEL panel); virtual void FlashWindow(VPANEL panel, bool state); virtual void SetTitle(VPANEL panel, const wchar_t *title); virtual void SetAsToolBar(VPANEL panel, bool state); virtual bool SupportsFeature(SurfaceFeature_e feature); virtual void SetTopLevelFocus(VPANEL panel); virtual int GetPopupCount(); virtual VPANEL GetPopup(int index); virtual void AddPanel(VPANEL panel); virtual void ReleasePanel(VPANEL panel); virtual void CreatePopup(VPANEL panel, bool minimised, bool showTaskbarIcon, bool disabled, bool mouseInput, bool kbInput ); virtual bool RecreateContext(VPANEL panel); virtual void EnableMouseCapture(VPANEL panel, bool state); virtual bool ShouldPaintChildPanel(VPANEL childPanel); virtual void MovePopupToFront(VPANEL panel); virtual void MovePopupToBack(VPANEL panel); virtual void SwapBuffers(VPANEL panel); virtual void Invalidate(VPANEL panel); virtual void SetCursor(HCursor cursor); virtual void SetCursorAlwaysVisible( bool visible ) {} virtual void ApplyChanges(); virtual bool IsWithin(int x, int y); virtual bool HasFocus(); virtual void GetWorkspaceBounds(int &x, int &y, int &wide, int &tall); virtual void SolveTraverse(VPANEL panel, bool forceApplySchemeSettings); virtual void PaintTraverse(VPANEL panel); virtual void RestrictPaintToSinglePanel(VPANEL panel); virtual void SetModalPanel(VPANEL ); virtual VPANEL GetModalPanel(); virtual void UnlockCursor(); virtual void LockCursor(); virtual void SetTranslateExtendedKeys(bool state); virtual VPANEL GetTopmostPopup(); // sound virtual void PlaySound(const char *fileName); // fonts virtual HFont CreateFont(); virtual bool SetFontGlyphSet(HFont font, const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags, int nRangeMin = 0, int nRangeMax = 0); virtual int GetFontTall(HFont font); virtual int GetFontTallRequested(HFont font); virtual int GetFontAscent(HFont font, wchar_t wch); virtual void GetCharABCwide(HFont font, int ch, int &a, int &b, int &c); virtual int GetCharacterWidth(HFont font, int ch); virtual void GetTextSize(HFont font, const wchar_t *text, int &wide, int &tall); virtual bool AddCustomFontFile(const char *fontName, const char *fontFileName); virtual bool AddBitmapFontFile(const char *fontFileName); virtual void SetBitmapFontName( const char *pName, const char *pFontFilename ); virtual const char *GetBitmapFontName( const char *pName ); virtual bool SetBitmapFontGlyphSet(HFont font, const char *windowsFontName, float scalex, float scaley, int flags); virtual bool IsFontAdditive(HFont font); virtual void PrecacheFontCharacters(HFont font, const wchar_t *pCharacters); virtual void ClearTemporaryFontCache( void ); virtual const char *GetFontName( HFont font ); virtual const char *GetFontFamilyName( HFont font ); virtual bool IsCursorVisible() { return true; } // gets the absolute coordinates of the screen (in screen space) void GetAbsoluteWindowBounds(int &x, int &y, int &wide, int &tall); // methods void setFocus(VPANEL panel); void GetProportionalBase( int &width, int &height ) { width = BASE_WIDTH; height = BASE_HEIGHT; } virtual void CalculateMouseVisible(); virtual bool NeedKBInput(); // we use the default IInput cursor functions virtual bool HasCursorPosFunctions() { return false; } virtual void SurfaceGetCursorPos(int &x, int &y) {} virtual void SurfaceSetCursorPos(int x, int y){} virtual void SetAllowHTMLJavaScript( bool state ); // SRC specific interfaces virtual void DrawTexturedLine( const Vertex_t &a, const Vertex_t &b ); virtual void DrawOutlinedCircle(int x, int y, int radius, int segments) ; virtual void DrawTexturedPolyLine( const Vertex_t *p,int n ) ; // (Note: this connects the first and last points). virtual void DrawTexturedSubRect( int x0, int y0, int x1, int y1, float texs0, float text0, float texs1, float text1 ); virtual void DrawTexturedPolygon(int n, Vertex_t *pVertices, bool bClipVertices = true); virtual const wchar_t *GetTitle(VPANEL panel); virtual void LockCursor( bool state ); virtual bool IsCursorLocked( void ) const; virtual void SetWorkspaceInsets( int left, int top, int right, int bottom ); // Lower level char drawing code, call DrawGet then pass in info to DrawRender (NOT SUPPORTED BY DEFAULT SURFACE )!!! virtual bool DrawGetUnicodeCharRenderInfo( wchar_t ch, CharRenderInfo& info ); virtual void DrawRenderCharFromInfo( const CharRenderInfo& info ); // alpha multipliers not yet implemented virtual void DrawSetAlphaMultiplier( float alpha /* [0..1] */ ) {} virtual float DrawGetAlphaMultiplier() { return 1.0f; } // Here's where the app systems get to learn about each other virtual bool Connect( CreateInterfaceFn factory ); virtual void Disconnect(); // Here's where systems can access other interfaces implemented by this object // Returns NULL if it doesn't implement the requested interface virtual void *QueryInterface( const char *pInterfaceName ); // Init, shutdown virtual InitReturnVal_t Init(); //virtual void Shutdown(); // screen size changing void OnScreenSizeChanged( int nOldWidth, int nOldHeight ) { } // We don't support this for non material system surfaces (we could) virtual vgui::HCursor CreateCursorFromFile( char const *curOrAniFile, char const *pPathID ) { Assert( 0 ); return dc_arrow; } virtual void PaintTraverseEx(VPANEL panel, bool paintPopups = false ) { PaintTraverse( panel ); } virtual float GetZPos() const { return 0.0f; } virtual IImage *GetIconImageForFullPath( char const *pFullPath ); virtual const char *GetResolutionKey( void ) const { Assert( 0 ); return NULL; } virtual bool ForceScreenSizeOverride( bool bState, int wide, int tall ); // LocalToScreen, ParentLocalToScreen fixups for explicit PaintTraverse calls on Panels not at 0, 0 position virtual bool ForceScreenPosOffset( bool bState, int x, int y ); virtual void OffsetAbsPos( int &x, int &y ); virtual bool IsScreenSizeOverrideActive( void ); virtual bool IsScreenPosOverrideActive( void ); void GetKernedCharWidth( HFont font, wchar_t ch, wchar_t chBefore, wchar_t chAfter, float &wide, float &flabcA ) { Assert( 0 ); wide = 0.0f; flabcA = 0.0f; } // split screen state changed, etc. void ResetFontCaches() { } virtual void DestroyTextureID( int id ); virtual int GetTextureNumFrames( int id ); virtual void DrawSetTextureFrame( int id, int nFrame, unsigned int *pFrameCache ); virtual void DrawUpdateRegionTextureRGBA( int nTextureID, int x, int y, const unsigned char *pchData, int wide, int tall, ImageFormat imageFormat ) { } virtual bool BHTMLWindowNeedsPaint(IHTML *htmlwin) { return false; } virtual const char *GetWebkitHTMLUserAgentString(); virtual void *Deprecated_AccessChromeHTMLController() { return NULL; } virtual void SetFullscreenViewport( int x, int y, int w, int h ) OVERRIDE { m_nFullscreenViewportX = x; m_nFullscreenViewportY = y; m_nFullscreenViewportWidth = w; m_nFullscreenViewportHeight = h; m_pFullscreenRenderTarget = NULL; } virtual void GetFullscreenViewport( int & x, int & y, int & w, int & h ) OVERRIDE { x = m_nFullscreenViewportX; y = m_nFullscreenViewportY; w = m_nFullscreenViewportWidth; h = m_nFullscreenViewportHeight; } virtual void PushFullscreenViewport(); virtual void PopFullscreenViewport(); // software cursors aren't available in tools virtual void SetSoftwareCursor( bool bUseSoftwareCursor ) OVERRIDE {} virtual void PaintSoftwareCursor() OVERRIDE {} private: CUtlDict< IImage *, unsigned short > m_FileTypeImages; enum { BASE_HEIGHT = 480, BASE_WIDTH = 640 }; bool LoadTGA(Texture *texture, const char *filename); bool LoadBMP(Texture *texture, const char *filename); void InternalThinkTraverse(VPANEL panel); void InternalSolveTraverse(VPANEL panel); void InternalSchemeSettingsTraverse(VPANEL panel, bool forceApplySchemeSettings); VPANEL GetContextPanelForChildPanel(VPANEL childPanel); void initStaticData(); // sets the current line drawing color, called by DrawSetTextColor void SetLineColor(Color col); VPANEL _embeddedPanel; // main panel VPANEL _notifyPanel; VPANEL _currentContextPanel; HTexture _notifyIcon; Dar _popupList; // list of panels that have their own win32 window HICON _currentCursor; OSVERSIONINFO m_WindowsVersion; bool m_bSupportsUnicode; CUtlVector m_CustomFontFileNames; // current font info HFont m_hCurrentFont; CWin32Font *m_pActiveFont; HPEN pen; // the pen used to draw lines bool _needKB; bool _needMouse; int m_TextPos[2]; Texture *m_pCurrentTexture; static bool TextureLessFunc(const Texture &lhs, const Texture &rhs); Texture *GetTextureById(int id); Texture *AllocTextureForId(int id); int GetNumTextures(); CUtlRBTree m_VGuiSurfaceTextures; struct ScreenOverride_t { ScreenOverride_t() : m_bActive( false ) { m_nValue[ 0 ] = m_nValue[ 1 ] = 0; } bool m_bActive; int m_nValue[ 2 ]; }; ScreenOverride_t m_ScreenSizeOverride; ScreenOverride_t m_ScreenPosOverride; bool m_bAllowJavaScript; int m_nFullscreenViewportX; int m_nFullscreenViewportY; int m_nFullscreenViewportWidth; int m_nFullscreenViewportHeight; ITexture *m_pFullscreenRenderTarget; }; CWin32Surface g_Surface; EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CWin32Surface, ISurface, VGUI_SURFACE_INTERFACE_VERSION, g_Surface); //!! these defines duplicated in Surface_Win32.cpp #define WM_MY_TRAY_NOTIFICATION (WM_USER+1) static UINT staticShutdownMsg = 0; static HICON staticDefaultCursor[20]; static WNDCLASS staticWndclass = { NULL }; static ATOM staticWndclassAtom = 0; static bool staticStaticDataInitialized = false; static bool staticSurfaceAvailable; // these functions defined below static LRESULT CALLBACK staticProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam); static void staticNotifyIconProc(HWND hwnd, WPARAM wparam, LPARAM lparam); #ifdef DEBUG_TIMING #define START_TIMER() \ float paintTime;\ static LARGE_INTEGER ticksPerSecond = { 0 };\ if (!ticksPerSecond.QuadPart)\ {\ QueryPerformanceFrequency(&ticksPerSecond);\ }\ LARGE_INTEGER Start, end;\ QueryPerformanceCounter(&Start); #define END_TIMER(x) \ QueryPerformanceCounter(&end);\ paintTime = (float)(((end.QuadPart - Start.QuadPart) * 1000000) / ticksPerSecond.QuadPart) / 1000;\ if (paintTime > 1.0f) Msg(x, paintTime); #else #define START_TIMER() #define END_TIMER(x) #endif // DEBUG_TIMING //----------------------------------------------------------------------------- // Purpose: Handles drag and drop //----------------------------------------------------------------------------- class CSurfaceDragDropTarget : public IDropTarget { public: CSurfaceDragDropTarget() { _refCount = 0; _dragData = NULL; OleInitialize(NULL); } private: unsigned int _refCount; KeyValues *_dragData; virtual HRESULT STDMETHODCALLTYPE QueryInterface( /* [in] */ REFIID riid, /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject) { if (riid == IID_IDropTarget) { *ppvObject = (IDropTarget *)this; return S_OK; } return E_NOINTERFACE; } virtual ULONG STDMETHODCALLTYPE AddRef( void) { return ++_refCount; } virtual ULONG STDMETHODCALLTYPE Release( void) { return --_refCount; } virtual HRESULT STDMETHODCALLTYPE DragEnter(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { if (_dragData) { _dragData->deleteThis(); } _dragData = calculateData(pDataObject); return DragOver(grfKeyState, pt, pdwEffect); } virtual HRESULT STDMETHODCALLTYPE DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { *pdwEffect = DROPEFFECT_NONE; if (!_dragData || !g_pIVgui->IsRunning()) return S_OK; // get the panel the mouse is over VPANEL mouseOver = g_pInput->GetMouseOver(); if (mouseOver) { // check to see if the panel will accept this message if (((VPanel *)mouseOver)->Client()->RequestInfo(_dragData)) { *pdwEffect = DROPEFFECT_COPY; } } return S_OK; } virtual HRESULT STDMETHODCALLTYPE DragLeave() { return S_OK; } virtual HRESULT STDMETHODCALLTYPE Drop(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect) { *pdwEffect = DROPEFFECT_NONE; if (!_dragData || !g_pIVgui->IsRunning()) return S_OK; // get the panel the mouse is over VPANEL target = (VPANEL)_dragData->GetPtr("AcceptPanel"); if (target && _dragData) { // check to see if the panel will accept this message g_pIVgui->PostMessage(target, _dragData, NULL); _dragData = NULL; } if (_dragData) { _dragData->deleteThis(); } _dragData = NULL; return S_OK; } // internal methods virtual KeyValues *calculateData(IDataObject *pDataObject) { KeyValues *dragData = NULL; // check on the type of data FORMATETC format = { CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM storage; if (pDataObject->GetData(&format, &storage) == S_OK) { // we got some data if (storage.tymed == TYMED_HGLOBAL) { const char *buf = (const char *)GlobalLock(storage.hGlobal); dragData = new KeyValues("DragDrop", "type", "text", "text", buf); GlobalUnlock(storage.hGlobal); } ReleaseStgMedium(&storage); } else { // try getting a file format.cfFormat = CF_HDROP; if (pDataObject->GetData(&format, &storage) == S_OK && storage.tymed == TYMED_HGLOBAL) { dragData = new KeyValues("DragDrop", "type", "files"); KeyValues *fileList = dragData->FindKey("list", true); // parse out the file list HDROP hdrop = (HDROP)GlobalLock(storage.hGlobal); char namebuf[32], buf[512]; int count = DragQueryFile(hdrop, 0xFFFFFFFF, buf, 511); for (int i = 0; i < count; i++) { Q_snprintf(namebuf, sizeof( namebuf ), "%d", i); DragQueryFile(hdrop, i, buf, 511); fileList->SetString(namebuf, buf); } GlobalUnlock(storage.hGlobal); } } return dragData; } }; static CSurfaceDragDropTarget staticDragDropTarget; bool CWin32Surface::TextureLessFunc(const Texture &lhs, const Texture &rhs) { return lhs._id < rhs._id; } Texture *CWin32Surface::GetTextureById(int id) { Texture findTex = { id }; int index = m_VGuiSurfaceTextures.Find(findTex); if (m_VGuiSurfaceTextures.IsValidIndex(index)) { return &m_VGuiSurfaceTextures[index]; } return NULL; } Texture *CWin32Surface::AllocTextureForId(int id) { Texture newTex = { id }; int index = m_VGuiSurfaceTextures.Insert(newTex); return &m_VGuiSurfaceTextures[index]; } int CWin32Surface::GetNumTextures() { return m_VGuiSurfaceTextures.Count(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- static void staticGenerateIconForTexture(Texture *texture, HDC hdc) { // see if there is an iconic version of the texture file first char buf[256]; Q_snprintf(buf, sizeof( buf ), "%s.ico", texture->_filename); texture->_icon = (HICON)::LoadImage(NULL, buf, IMAGE_ICON, 16, 16, LR_LOADFROMFILE | LR_DEFAULTSIZE); if (texture->_icon) return; if (texture->_wide > 32 || texture->_tall > 32) return; // generate an icon from the tga // generate the AND plane based on the alpha uchar planeAND[32 * 32 / 8]; /* !! disabled until verified // // from the alpha of the bitmap, generate a bitmask // uchar *ptr = (uchar *)texture->_dib + 3; // jump to alpha portion uchar *ptrEnd = (uchar *)texture->_dib + (texture->_wide * texture->_tall * 4); uchar *andPtr = planeAND; while (ptr < ptrEnd) { // Start all empty *andPtr = 0; // assume the number of pixel is a multiple of 8 // if the alpha is high enough, then mask out the pixel // it's two bits per pixel, strangely enough if (*ptr > 127) { *andPtr |= 0x03; } ptr += 4; if (*ptr > 127) { *andPtr |= 0x0C; } ptr += 4; if (*ptr > 127) { *andPtr |= 0x30; } ptr += 4; if (*ptr > 127) { *andPtr |= 0xC0; } ptr += 4; andPtr++; } */ memset(planeAND, 0x00, sizeof(planeAND)); HBITMAP mask = ::CreateBitmap(texture->_wide, texture->_tall, 1, 1, planeAND); // create the icon ICONINFO iconInfo; iconInfo.fIcon = TRUE; iconInfo.xHotspot = 8; iconInfo.yHotspot = 8; iconInfo.hbmMask = mask; iconInfo.hbmColor = texture->_bitmap; texture->_icon = ::CreateIconIndirect(&iconInfo); ::DeleteObject(mask); } //----------------------------------------------------------------------------- // Purpose: Constructor, basic variable initialization //----------------------------------------------------------------------------- CWin32Surface::CWin32Surface() : m_VGuiSurfaceTextures(0, 128, TextureLessFunc) { _currentCursor = NULL; m_pCurrentTexture = NULL; initStaticData(); staticSurfaceAvailable = false; m_bAllowJavaScript = false; m_hCurrentFont = 0; pen = NULL; _needKB = true; _needMouse = true; HRESULT hr; // this step is IMPORTANT , it turns "on" COM if (FAILED(hr = CoInitialize(NULL))) { // failed } // get our version info m_WindowsVersion.dwOSVersionInfoSize = sizeof(m_WindowsVersion); GetVersionEx(&m_WindowsVersion); if (m_WindowsVersion.dwMajorVersion >= 5) { m_bSupportsUnicode = true; } else { m_bSupportsUnicode = false; } m_TextPos[0] = m_TextPos[1] = 0; m_nFullscreenViewportX = m_nFullscreenViewportY = 0; m_nFullscreenViewportWidth = m_nFullscreenViewportHeight = 0; m_pFullscreenRenderTarget = NULL; } //----------------------------------------------------------------------------- // Purpose: Destructor //----------------------------------------------------------------------------- CWin32Surface::~CWin32Surface() { // ensure we don't try and process any more windows messages staticSurfaceAvailable = false; // free all the textures for (int i = 0; i < m_VGuiSurfaceTextures.MaxElement(); i++) { if (!m_VGuiSurfaceTextures.IsValidIndex(i)) continue; Texture *texture = &m_VGuiSurfaceTextures[i]; if (texture->_bitmap) { ::DeleteObject(texture->_bitmap); } if (texture->_maskBitmap) { ::DeleteObject(texture->_maskBitmap); } if (texture->_icon) { ::DestroyIcon(texture->_icon); } } CoUninitialize(); // turn com off } //----------------------------------------------------------------------------- // Purpose: Shuts down app //----------------------------------------------------------------------------- void CWin32Surface::Shutdown() { for ( int i = m_FileTypeImages.First(); i != m_FileTypeImages.InvalidIndex(); i = m_FileTypeImages.Next( i ) ) { delete m_FileTypeImages[ i ]; } m_FileTypeImages.RemoveAll(); // free the fonts FontManager().ClearAllFonts(); // release any custom font files {for (int i = 0; i < m_CustomFontFileNames.Count(); i++) { ::RemoveFontResource(m_CustomFontFileNames[i].String()); }} m_CustomFontFileNames.RemoveAll(); // ensure we don't try and process windows messages during Shutdown staticSurfaceAvailable = false; // release any panels still with surfaces while (GetPopupCount()) { ReleasePanel(GetPopup(0)); } // kill our windows instance ::UnregisterClass("Surface", ::GetModuleHandle(NULL)); staticWndclassAtom = NULL; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- VPANEL CWin32Surface::GetEmbeddedPanel() { return _embeddedPanel; } // SRC specific interfaces void CWin32Surface::DrawTexturedLine( const Vertex_t &a, const Vertex_t &b ) { } void CWin32Surface::DrawOutlinedCircle(int x, int y, int radius, int segments) { } void CWin32Surface::DrawTexturedPolyLine( const Vertex_t *p,int n ) { } void CWin32Surface::DrawTexturedSubRect( int x0, int y0, int x1, int y1, float texs0, float text0, float texs1, float text1 ) { } void CWin32Surface::DrawTexturedPolygon(int n, Vertex_t *pVertices, bool bClipVertices /*= true*/) { NOTE_UNUSED( bClipVertices ); POINT *pt; HDC hdc = PLAT(_currentContextPanel)->hdc; pt = (POINT *)malloc(sizeof(POINT) * n); if(pt) { for(int i=0;ihwnd, 0, 50, (TIMERPROC) NULL); // fonts initialization char language[64]; if (g_pSystem->GetRegistryString("HKEY_CURRENT_USER\\Software\\Valve\\Source\\Language", language, sizeof(language)-1)) { FontManager().SetLanguage(language); } else { FontManager().SetLanguage("english"); } } // Lower level char drawing code, call DrawGet then pass in info to DrawRender bool CWin32Surface::DrawGetUnicodeCharRenderInfo( wchar_t ch, CharRenderInfo& info ) { // Only supported in engine renderer! Assert( 0 ); info.valid = false; return false; } void CWin32Surface::DrawRenderCharFromInfo( const CharRenderInfo& info ) { Assert( 0 ); } //----------------------------------------------------------------------------- // Purpose: Sets the current drawing context //----------------------------------------------------------------------------- void CWin32Surface::SetCurrentContextPanel(VPANEL panel) { _currentContextPanel = panel; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- VPANEL CWin32Surface::GetContextPanelForChildPanel(VPANEL childPanel) { VPANEL contextPanel = childPanel; while (contextPanel && !PLAT(contextPanel)) { contextPanel = (VPANEL)((VPanel *)contextPanel)->GetParent(); } return contextPanel; } // static currently used win32 Paint info static ::PAINTSTRUCT s_CurrentPaintStruct; void CWin32Surface::PushMakeCurrent(VPANEL panel, bool useInsets) { // find the win32 window context from the hierarchy VPANEL currentContextPanel = GetContextPanelForChildPanel(panel); SetCurrentContextPanel(currentContextPanel); // clear the current active font so that it will be reset m_pActiveFont = NULL; if (!currentContextPanel) { // no drawing context found, something is seriously wrong Msg( "Warning: VPanel with no drawing context\n" ); } int inset[4]; //!! need to make the inset part of VPanel ((VPanel *)panel)->Client()->GetInset(inset[0],inset[1],inset[2],inset[3]); if(!useInsets) { inset[0]=0; inset[1]=0; inset[2]=0; inset[3]=0; } int absThis[4]; ((VPanel *)_currentContextPanel)->GetAbsPos(absThis[0], absThis[1]); ((VPanel *)_currentContextPanel)->GetSize(absThis[2], absThis[3]); absThis[2] += absThis[0]; absThis[3] += absThis[1]; int absPanel[4]; ((VPanel *)panel)->GetAbsPos(absPanel[0], absPanel[1]); ((VPanel *)panel)->GetSize(absPanel[2], absPanel[3]); absPanel[2] += absPanel[0]; absPanel[3] += absPanel[1]; int clipRect[4]; ((VPanel *)panel)->Client()->GetClipRect(clipRect[0],clipRect[1],clipRect[2],clipRect[3]); if ( _currentContextPanel == panel ) { // this panel has it's own window, so use screen space ::SetViewportOrgEx(PLAT(_currentContextPanel)->hdc,0+inset[0],0+inset[1],null); } else { // child window, so set win32 up so all subsequent drawing calls are done in local space ::SetViewportOrgEx(PLAT(_currentContextPanel)->hdc,(absPanel[0]+inset[0])-absThis[0],(absPanel[1]+inset[1])-absThis[1],null); } // setup clipping // get and translate clipRect into surface space, then factor in inset int x0 = clipRect[0] - absThis[0]; int y0 = clipRect[1] - absThis[1]; int x1 = (clipRect[2] - absThis[0]) - inset[2]; int y1 = (clipRect[3] - absThis[1]) - inset[3]; //set the rect and select to make it current ::SetRectRgn(PLAT(_currentContextPanel)->clipRgn,x0,y0,x1,y1); ::SelectObject(PLAT(_currentContextPanel)->hdc, PLAT(_currentContextPanel)->clipRgn); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWin32Surface::PopMakeCurrent(VPANEL panel) { if (panel == _currentContextPanel) { // reset the current panel to be the main panel SetCurrentContextPanel(_embeddedPanel); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWin32Surface::GetScreenSize(int &wide, int &tall) { if ( m_ScreenSizeOverride.m_bActive ) { wide = m_ScreenSizeOverride.m_nValue[ 0 ]; tall = m_ScreenSizeOverride.m_nValue[ 1 ]; return; } VPANEL context = _embeddedPanel; if (context) { wide = ::GetDeviceCaps(PLAT(context)->hdc, HORZRES); tall = ::GetDeviceCaps(PLAT(context)->hdc, VERTRES); } } bool CWin32Surface::ForceScreenSizeOverride( bool bState, int wide, int tall ) { bool bWasSet = m_ScreenSizeOverride.m_bActive; m_ScreenSizeOverride.m_bActive = bState; m_ScreenSizeOverride.m_nValue[ 0 ] = wide; m_ScreenSizeOverride.m_nValue[ 1 ] = tall; return bWasSet; } // LocalToScreen, ParentLocalToScreen fixups for explicit PaintTraverse calls on Panels not at 0, 0 position bool CWin32Surface::ForceScreenPosOffset( bool bState, int x, int y ) { bool bWasSet = m_ScreenPosOverride.m_bActive; m_ScreenPosOverride.m_bActive = bState; m_ScreenPosOverride.m_nValue[ 0 ] = x; m_ScreenPosOverride.m_nValue[ 1 ] = y; return bWasSet; } void CWin32Surface::OffsetAbsPos( int &x, int &y ) { if ( !m_ScreenPosOverride.m_bActive ) return; x += m_ScreenPosOverride.m_nValue[ 0 ]; y += m_ScreenPosOverride.m_nValue[ 1 ]; } bool CWin32Surface::IsScreenSizeOverrideActive( void ) { return ( m_ScreenSizeOverride.m_bActive ); } bool CWin32Surface::IsScreenPosOverrideActive( void ) { return ( m_ScreenPosOverride.m_bActive ); } void CWin32Surface::DestroyTextureID( int id ) { // not implemented } int CWin32Surface::GetTextureNumFrames( int id ) { // not implemented return 0; } void CWin32Surface::DrawSetTextureFrame( int id, int nFrame, unsigned int *pFrameCache ) { // not implemented } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- VPANEL CWin32Surface::GetNotifyPanel() { return _notifyPanel; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWin32Surface::SetNotifyIcon(VPANEL context, HTexture iconID, VPANEL panelToReceiveMessages, const char *text) { context = GetContextPanelForChildPanel(context); if (!context) return; if (!text) { text = ""; } // things haven't changed so just update the tooltip if (_notifyIcon == iconID && _notifyPanel == panelToReceiveMessages && context && PLAT(context)->notifyIcon) { ::NOTIFYICONDATA iconData = { sizeof(iconData), PLAT(context)->hwnd, 1, // icon ID NIF_TIP, // modify tooltip only WM_MY_TRAY_NOTIFICATION, // callback message PLAT(context)->notifyIcon, // icon handle "", // tooltip text }; strncpy(iconData.szTip, text, 63); iconData.szTip[63] = '\0'; ::Shell_NotifyIcon(NIM_MODIFY, &iconData); return; } _notifyIcon = iconID; _notifyPanel = panelToReceiveMessages; DWORD dwMessage = NIM_MODIFY; if (!iconID) { dwMessage = NIM_DELETE; PLAT(context)->notifyIcon = NULL; } else if (!PLAT(context)->notifyIcon) { dwMessage = NIM_ADD; } // make sure the icon has been loaded Texture *texture = NULL; if (iconID) { texture = GetTextureById(iconID); if (!texture->_icon) { // generate an icon for the texture staticGenerateIconForTexture(texture, PLAT(_currentContextPanel)->hdc); } } // get the icon if (texture) { PLAT(context)->notifyIcon = texture->_icon; } ::NOTIFYICONDATA iconData = { sizeof(iconData), PLAT(context)->hwnd, 1, // icon ID NIF_ICON | NIF_TIP | NIF_MESSAGE, // items used WM_MY_TRAY_NOTIFICATION, // callback message PLAT(context)->notifyIcon, // icon handle "", // tooltip text }; strncpy(iconData.szTip, text, 63); iconData.szTip[63] = '\0'; BOOL success = ::Shell_NotifyIcon(dwMessage, &iconData); if (iconID && !success) { DWORD err = GetLastError(); Msg("error: SetNotifyIcon(%d) failed\n", err); } } void CWin32Surface::DrawSetColor(int r,int g,int b,int a) { SetBkColor(PLAT(_currentContextPanel)->hdc,RGB(r,g,b)); } void CWin32Surface::DrawSetColor(Color col) { DrawSetColor(col[0], col[1], col[2], col[3]); } void CWin32Surface::DrawSetTextPos(int x, int y) { MoveToEx(PLAT(_currentContextPanel)->hdc,x,y,null); m_TextPos[0] = x; m_TextPos[1] = y; } void CWin32Surface::DrawGetTextPos(int& x,int& y) { x = m_TextPos[0]; y = m_TextPos[1]; } void CWin32Surface::DrawSetTextFont(HFont font) { Assert(font); // make the font current m_hCurrentFont = font; // m_FontAmalgams[m_hCurrentFont].GetFontForChar('a')->SetAsActiveFont(_currentContextPanel->Plat()->hdc); } void CWin32Surface::DrawFilledRect(int x0,int y0,int x1,int y1) { // trick to draw filled rectangles using current background color RECT rect = { x0, y0, x1, y1}; ExtTextOut(PLAT(_currentContextPanel)->hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); } void CWin32Surface::DrawFilledRectArray( IntRect *pRects, int numRects ) { int i; for( i = 0; i < numRects; i++ ) { DrawFilledRect( pRects[i].x0, pRects[i].y0, pRects[i].x1, pRects[i].y1 ); } } void CWin32Surface::DrawOutlinedRect(int x0,int y0,int x1,int y1) { // draw an outline of a rectangle using 4 filledRect DrawFilledRect(x0,y0,x1,y0+1); // top DrawFilledRect(x0,y1-1,x1,y1); // bottom DrawFilledRect(x0,y0+1,x0+1,y1-1); // left DrawFilledRect(x1-1,y0+1,x1,y1-1); // right } void CWin32Surface::DrawLine(int x0,int y0,int x1,int y1) { POINT pt[2]; CONST POINT *p=pt; pt[0].x=x0; pt[0].y=y0; pt[1].x=x1; pt[1].y=y1; // MoveToEx(_currentContextPanel->Plat()->hdc,x0,y0,NULL); // LineTo(_currentContextPanel->Plat()->hdc,x1,y1); Polyline(PLAT(_currentContextPanel)->hdc,p , 2); } void CWin32Surface::DrawPolyLine(int *px, int *py, int numPoints) { POINT *pt; pt = (POINT *)malloc(sizeof(POINT) * numPoints); if(pt) { for(int i=0;ihdc, pt , numPoints); free(pt); } } void CWin32Surface::DrawSetTextColor(int r,int g,int b,int a) { // set the draw color for lines SetLineColor(Color(r,g,b,a)); SetTextColor(PLAT(_currentContextPanel)->hdc,RGB(r,g,b)); } void CWin32Surface::DrawSetTextColor(Color col) { // set the draw color for lines SetLineColor(col); // end then for text DrawSetTextColor(col[0], col[1], col[2], col[3]); } void CWin32Surface::SetLineColor(Color col) { HPEN tmp=pen; pen = CreatePen(PS_SOLID,0,RGB(col[0], col[1], col[2])); if(pen) { SelectObject(PLAT(_currentContextPanel)->hdc, pen ); } // you must delete the pen AFTER a new one is selected if(tmp) { DeleteObject(tmp); } } void CWin32Surface::DrawPrintText(const wchar_t *text, int textLen, FontDrawType_t drawType /*= FONT_DRAW_DEFAULT*/) { Assert(text); if (!text) return; if (textLen < 1) return; for (int i = 0; i < textLen; i++) { DrawUnicodeChar(text[i]); } // ExtTextOut(_currentContextPanel->Plat()->hdc, 0, 0, 0, NULL, text, textLen, NULL); } void CWin32Surface::DrawUnicodeString( const wchar_t *pwString, FontDrawType_t drawType /*= FONT_DRAW_DEFAULT*/) { Assert( pwString ); if ( !pwString ) return; while ( wchar_t ch = *pwString++ ) { DrawUnicodeChar( ch ); } } //----------------------------------------------------------------------------- // Purpose: draws single unicode character at the current position with the // current font & color //----------------------------------------------------------------------------- void CWin32Surface::DrawUnicodeChar(wchar_t wch, FontDrawType_t drawType /*= FONT_DRAW_DEFAULT*/) { // set the current font CWin32Font *winFont = FontManager().GetFontForChar(m_hCurrentFont, wch); if (!winFont) return; if (m_pActiveFont != winFont) { winFont->SetAsActiveFont(PLAT(_currentContextPanel)->hdc); m_pActiveFont = winFont; } if (m_bSupportsUnicode) { ExtTextOutW(PLAT(_currentContextPanel)->hdc, 0, 0, 0, NULL, &wch, 1, NULL); } else { char mbcs[6] = { 0 }; ::WideCharToMultiByte(CP_ACP, 0, &wch, 1, mbcs, sizeof(mbcs), NULL, NULL); ExtTextOutA(PLAT(_currentContextPanel)->hdc, 0, 0, 0, NULL, mbcs, strlen(mbcs), NULL); } } //----------------------------------------------------------------------------- // Purpose: allocates a new texture id //----------------------------------------------------------------------------- int CWin32Surface::CreateNewTextureID( bool procedural ) { //!! hack, arbitrary base static int staticBindIndex = 2700; return staticBindIndex++; } //----------------------------------------------------------------------------- // Purpose: returns true if the texture id has a valid texture bound to it //----------------------------------------------------------------------------- bool CWin32Surface::IsTextureIDValid(int id) { return (GetTextureById(id) != NULL); } //----------------------------------------------------------------------------- // Purpose: clear up alloced resources on a texture //----------------------------------------------------------------------------- void CWin32Surface::FreeTextureData( vgui::Texture *pTexture ) { if ( pTexture->_bitmap ) { ::DeleteObject( pTexture->_bitmap ); } // if ( pTexture->m_bitmapScaled ) // { // ::DeleteObject( pTexture->m_bitmapScaled ); // } if ( pTexture->_maskBitmap ) { ::DeleteObject( pTexture->_maskBitmap ); } if ( pTexture->_icon ) { ::DestroyIcon( pTexture->_icon ); } // if ( pTexture->rgba ) // delete[] pTexture->rgba; } //----------------------------------------------------------------------------- // Purpose: returns true if the texture id has a valid texture bound to it //----------------------------------------------------------------------------- bool CWin32Surface::DeleteTextureByID(int id) { #if defined( PS3OVERLAYUI_EXPORTS ) AUTO_LOCK( m_MutexTextureData ); #endif Texture findTex = { id }; int index = m_VGuiSurfaceTextures.Find(findTex); if (m_VGuiSurfaceTextures.IsValidIndex(index)) { Texture *texture = &m_VGuiSurfaceTextures[index]; FreeTextureData( texture ); m_VGuiSurfaceTextures.RemoveAt(index); return true; } return false; } //----------------------------------------------------------------------------- // Purpose: does nothing, since we don't need this optimization in win32 //----------------------------------------------------------------------------- void CWin32Surface::DrawFlushText() { } //----------------------------------------------------------------------------- // Purpose: create a html helper object //----------------------------------------------------------------------------- IHTML *CWin32Surface::CreateHTMLWindow(vgui::IHTMLEvents *events, VPANEL context ) { return NULL; } //----------------------------------------------------------------------------- // Purpose: delete an html helper object //----------------------------------------------------------------------------- void CWin32Surface::DeleteHTMLWindow(IHTML *htmlwin) { } //----------------------------------------------------------------------------- // Purpose: tell a html window to update its backing texture //----------------------------------------------------------------------------- void CWin32Surface::PaintHTMLWindow(IHTML *htmlwin) { } //----------------------------------------------------------------------------- void CWin32Surface::SetAllowHTMLJavaScript( bool state ) { } //----------------------------------------------------------------------------- // Purpose: sets the current active texture //----------------------------------------------------------------------------- void CWin32Surface::DrawSetTexture(int id) { Texture *texture = GetTextureById(id); m_pCurrentTexture = texture; } HBITMAP staticCreateBitmapHandle(int wide, int tall, HDC hdc, int bpp, void **dib); //----------------------------------------------------------------------------- // Purpose: maps a texture from memory to an id, and uploads it into the engine //----------------------------------------------------------------------------- void CWin32Surface::DrawSetTextureRGBA(int id,const unsigned char* rgba,int wide,int tall, int hardwareFilter, bool forceReload) { DrawSetTextureRGBAEx( id, rgba, wide, tall, IMAGE_FORMAT_RGBA8888 ); } //----------------------------------------------------------------------------- // Purpose: maps a texture from memory to an id, and uploads it into the engine //----------------------------------------------------------------------------- void CWin32Surface::DrawSetTextureRGBAEx(int id,const unsigned char* rgba,int wide,int tall, ImageFormat imageFormat ) { Texture *texture = GetTextureById(id); if (texture) { if (texture->_bitmap) { ::DeleteObject(texture->_bitmap); } if (texture->_maskBitmap) { ::DeleteObject(texture->_maskBitmap); } } if (!texture) { // allocate a new texture texture = AllocTextureForId(id); memset(texture, 0, sizeof(Texture)); } { // no texture or forced load the new texture texture->_id = id; texture->_filename =NULL; texture->_wide = wide; texture->_tall = tall; texture->_icon = NULL; texture->_dib = NULL; texture->_bitmap = staticCreateBitmapHandle(texture->_wide, texture->_tall, PLAT(_currentContextPanel)->hdc, 32, &texture->_dib ); // copy over the texture data memcpy(texture->_dib,rgba,wide*tall*4); } m_pCurrentTexture = texture; } //----------------------------------------------------------------------------- // Purpose: // Input : id - // *filename - // maxlen - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CWin32Surface::DrawGetTextureFile(int id, char *filename, int maxlen ) { Texture *texture = GetTextureById(id); if ( !texture ) return false; Q_strncpy( filename, texture->_filename, maxlen ); return true; } int CWin32Surface::DrawGetTextureId( char const *filename ) { int i = m_VGuiSurfaceTextures.FirstInorder(); while ( i != m_VGuiSurfaceTextures.InvalidIndex() ) { Texture *texture = &m_VGuiSurfaceTextures[i]; if ( !Q_stricmp( filename, texture->_filename ) ) return texture->_id; i = m_VGuiSurfaceTextures.NextInorder( i ); } return -1; } //----------------------------------------------------------------------------- // Purpose: Maps a texture file to an id, and makes it the current drawing texture // tries to load as a .tga first, and if not found as a .bmp //----------------------------------------------------------------------------- void CWin32Surface::DrawSetTextureFile(int id, const char *filename, int hardwareFilter, bool forceReload /*= false*/) { Texture *texture = GetTextureById(id); if (!texture || stricmp(filename, texture->_filename) || forceReload ) { // no texture, or the filename is different; load the new texture if (!texture) { // allocate a new texture texture = AllocTextureForId(id); memset(texture, 0, sizeof(Texture)); } if (texture) { if (texture->_bitmap) { ::DeleteObject(texture->_bitmap); } if (texture->_maskBitmap) { ::DeleteObject(texture->_maskBitmap); } } texture->_id = id; texture->_filename = filename; // try and find the file bool success = LoadTGA(texture, filename); if (!success) { // strip off the vgui/ and try again const char *psz = Q_stristr(filename, "vgui/"); if (psz) { success = LoadTGA(texture, filename + strlen("vgui/")); } } if (!success) { Msg("Error: texture file '%s' does not exist or is invalid\n", filename); return; } } m_pCurrentTexture = texture; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWin32Surface::DrawTexturedRect(int x0,int y0,int x1,int y1) { if (m_pCurrentTexture == null) { return; } if (PLAT(_currentContextPanel)->textureDC == null) { return; } HBITMAP bitmap = m_pCurrentTexture->_bitmap; int wide = m_pCurrentTexture->_wide; int tall = m_pCurrentTexture->_tall; HGDIOBJ oldObject; if (m_pCurrentTexture->_bMask) { HBITMAP bitmap_mask = m_pCurrentTexture->_maskBitmap; // draw the mask first to clear out the background to black oldObject = ::SelectObject(PLAT(_currentContextPanel)->textureDC, bitmap_mask); ::StretchBlt(PLAT(_currentContextPanel)->hdc,x0,y0,x1-x0,y1-y0,PLAT(_currentContextPanel)->textureDC,0,0,wide,tall,SRCAND); // draw over the 'black areas' with the bitmap ::SelectObject(PLAT(_currentContextPanel)->textureDC, bitmap); ::StretchBlt(PLAT(_currentContextPanel)->hdc,x0,y0,x1-x0,y1-y0,PLAT(_currentContextPanel)->textureDC,0,0,wide,tall,SRCPAINT); } else { oldObject = ::SelectObject(PLAT(_currentContextPanel)->textureDC, bitmap); ::StretchBlt(PLAT(_currentContextPanel)->hdc,x0,y0,x1-x0,y1-y0,PLAT(_currentContextPanel)->textureDC,0,0,wide,tall,SRCCOPY); } ::SelectObject(PLAT(_currentContextPanel)->textureDC, oldObject); // test code, should be used in win98/win2k for true alpha // BLENDFUNCTION blendFunction = { AC_SRC_OVER, 0, 255, 0 }; // ::AlphaBlend(PLAT(_currentContextPanel)->hdc,x0,y0,x1-x0,y1-y0,PLAT(_currentContextPanel)->textureDC,0,0,wide,tall,blendFunction); } //----------------------------------------------------------------------------- // Purpose: Called by vgui to get texture dimensions //----------------------------------------------------------------------------- void CWin32Surface::DrawGetTextureSize(int id, int &wide, int &tall) { Texture *texture = GetTextureById(id); if (!texture) return; wide = texture->_wide; tall = texture->_tall; } #pragma pack(1) typedef struct { unsigned char id_length, colormap_type, image_type; unsigned short colormap_index, colormap_length; unsigned char colormap_size; unsigned short x_origin, y_origin, width, height; unsigned char pixel_size, attributes; } tga_header_t; #pragma pack() HBITMAP staticCreateBitmapHandle(int wide, int tall, HDC hdc, int bpp, void **dib) { BITMAPINFOHEADER bitmapInfoHeader; memset(&bitmapInfoHeader, 0, sizeof(bitmapInfoHeader)); bitmapInfoHeader.biSize = sizeof(bitmapInfoHeader); bitmapInfoHeader.biWidth = wide; bitmapInfoHeader.biHeight = -tall; bitmapInfoHeader.biPlanes = 1; bitmapInfoHeader.biBitCount = bpp; bitmapInfoHeader.biCompression = BI_RGB; HBITMAP hRet; hRet = CreateDIBSection(hdc, (BITMAPINFO*)&bitmapInfoHeader, DIB_RGB_COLORS, dib, 0, 0); if ( !hRet ) Error( "staticCreateBitmapHandle: can't create DIB" ); return hRet; } #define DIB_HEADER_MARKER ((WORD) ('M' << 8) | 'B') //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CWin32Surface::LoadBMP(Texture *texture, const char *filename) { // try load the tga char buf[1024]; _snprintf(buf, sizeof(buf), "%s.bmp", filename); FileHandle_t file = g_pFullFileSystem->Open(buf, "rb", NULL); if (!file) return false; bool success = false; // Parse bitmap BITMAPFILEHEADER bmfHeader; DWORD dwBitsSize, dwFileSize; LPBITMAPINFO lpbmi; dwFileSize = g_pFullFileSystem->Size( file ); g_pFullFileSystem->Read( &bmfHeader, sizeof(bmfHeader), file ); if (bmfHeader.bfType == DIB_HEADER_MARKER) { dwBitsSize = dwFileSize - sizeof(bmfHeader); HGLOBAL hDIB = ::GlobalAlloc( GMEM_MOVEABLE | GMEM_ZEROINIT, dwBitsSize ); char *pDIB = (LPSTR)::GlobalLock((HGLOBAL)hDIB); { int i, j; g_pFullFileSystem->Read(pDIB, dwBitsSize, file ); lpbmi = (LPBITMAPINFO)pDIB; // we now have a block of memory, rgba // throw a bitmap header on it and register it in windows texture->_wide = lpbmi->bmiHeader.biWidth; texture->_tall = lpbmi->bmiHeader.biHeight; texture->_icon = NULL; texture->_bMask = false; texture->_bitmap = staticCreateBitmapHandle( texture->_wide, texture->_tall, PLAT(_currentContextPanel)->hdc, 32, &texture->_dib ); unsigned char *rgba = (unsigned char *)( pDIB + sizeof( BITMAPINFOHEADER ) + 256 * sizeof( RGBQUAD ) ); // Copy raw data for (j = 0; j < texture->_tall; j++) { for (i = 0; i _wide; i++) { int y = (texture->_tall - j - 1); int offs = ( y * texture->_wide + i); int offsdest = (j * texture->_wide + i) * 4; unsigned char *src = ((unsigned char *)rgba) + offs; char *dst = ((char*)texture->_dib) + offsdest; dst[0] = lpbmi->bmiColors[ *src ].rgbRed; dst[1] = lpbmi->bmiColors[ *src ].rgbGreen; dst[2] = lpbmi->bmiColors[ *src ].rgbBlue; dst[3] = (unsigned char)255; } } success = true; } ::GlobalUnlock( hDIB); ::GlobalFree((HGLOBAL) hDIB); } g_pFullFileSystem->Close(file); return success; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CWin32Surface::LoadTGA(Texture *texture, const char *filename) { bool invertAlpha = false; // try load the tga char buf[1024]; _snprintf(buf, sizeof(buf), "%s.tga", filename); FileHandle_t file = g_pFullFileSystem->Open(buf, "rb", NULL); if (!file) { return LoadBMP( texture, filename ); } // read the header tga_header_t tgaHeader; g_pFullFileSystem->Read(&tgaHeader, sizeof(tgaHeader), file); if (tgaHeader.image_type != 2 && tgaHeader.image_type != 10) { g_pIVgui->DPrintf2("Error: texture file '%s' has invalid image_type %d\n", filename, tgaHeader.image_type); return false; } if (tgaHeader.colormap_type != 0) { return false; } if (tgaHeader.pixel_size != 24 && tgaHeader.pixel_size !=32) { return false; } if (tgaHeader.id_length != 0) { g_pFullFileSystem->Seek( file, tgaHeader.id_length, FILESYSTEM_SEEK_CURRENT ); } // allocate memory for the destination uchar *rgba = (unsigned char *)malloc(tgaHeader.width * tgaHeader.height * 4); int column, row; uchar *ptr; bool bMask = false; if (tgaHeader.image_type == 2) { for (row = tgaHeader.height - 1; row >= 0; row--) { ptr = ((uchar *)rgba) + (row * tgaHeader.width * 4); for (column = 0; column < tgaHeader.width; column++) { switch (tgaHeader.pixel_size) { case 24: { g_pFullFileSystem->Read(ptr + 2, 1, file); g_pFullFileSystem->Read(ptr + 1, 1, file); g_pFullFileSystem->Read(ptr + 0, 1, file); ptr[3] = 255; if (invertAlpha) { ptr[3] = 0; } ptr += 4; break; } case 32: { g_pFullFileSystem->Read(ptr + 2, 1, file); g_pFullFileSystem->Read(ptr + 1, 1, file); g_pFullFileSystem->Read(ptr + 0, 1, file); g_pFullFileSystem->Read(ptr + 3, 1, file); if (!invertAlpha) { // if it's 0, then it's going to be zeroed out if (ptr[3] == 0) { ptr[3] = 255; } else { // anything besides 0 will be shown ptr[3] = 0; } //ptr[3] = 255 - ptr[3]; } bMask = true; ptr += 4; break; } } } } } else { uchar packetHeader, packetSize, j, color[4]; for(row = tgaHeader.height - 1; row >= 0; row--) { ptr = ((uchar*)rgba) + row * tgaHeader.width * 4; for(column=0;columnRead(&packetHeader, 1, file); packetSize = 1 + (packetHeader & 0x7f); if (packetHeader & 0x80) { switch (tgaHeader.pixel_size) { case 24: { g_pFullFileSystem->Read(color + 2, 1, file); g_pFullFileSystem->Read(color + 1, 1, file); g_pFullFileSystem->Read(color + 0, 1, file); if (invertAlpha) { color[3] = 0; } else { color[3] = 255; } break; } case 32: { g_pFullFileSystem->Read(color + 2, 1, file); g_pFullFileSystem->Read(color + 1, 1, file); g_pFullFileSystem->Read(color + 0, 1, file); g_pFullFileSystem->Read(color + 3, 1, file); bMask = true; if (invertAlpha) { color[3] = 255 - color[3]; } break; } } for (j = 0; j < packetSize; j++) { ptr[0] = color[0]; ptr[1] = color[1]; ptr[2] = color[2]; ptr[3] = color[3]; ptr += 4; column++; if (column == tgaHeader.width) { column = 0; if (row > 0) { row--; } else { goto breakOut; } ptr = ((uchar*)rgba) + (row * tgaHeader.width * 4); } } } else { for (j = 0; j < packetSize; j++) { switch (tgaHeader.pixel_size) { case 24: { g_pFullFileSystem->Read(ptr + 2, 1, file); g_pFullFileSystem->Read(ptr + 1, 1, file); g_pFullFileSystem->Read(ptr + 0, 1, file); ptr[3] = 255; if (invertAlpha) { ptr[3] = 0; } else { ptr[3] = 255; } ptr += 4; break; } case 32: { g_pFullFileSystem->Read(ptr + 2, 1, file); g_pFullFileSystem->Read(ptr + 1, 1, file); g_pFullFileSystem->Read(ptr + 0, 1, file); g_pFullFileSystem->Read(ptr + 3, 1, file); if (invertAlpha) { ptr[3]=255-ptr[3]; } ptr += 4; break; } } column++; if (column == tgaHeader.width) { column = 0; if(row > 0) { row--; } else { goto breakOut; } ptr = ((uchar*)rgba) + row * tgaHeader.width * 4; } } } } } breakOut:; } g_pFullFileSystem->Close(file); // we now have a block of memory, rgba // throw a bitmap header on it and register it in windows texture->_wide = tgaHeader.width; texture->_tall = tgaHeader.height; texture->_icon = NULL; texture->_bitmap = staticCreateBitmapHandle(tgaHeader.width, tgaHeader.height, PLAT(_currentContextPanel)->hdc, 32, &texture->_dib); texture->_bMask = bMask; if (bMask) texture->_maskBitmap = staticCreateBitmapHandle(tgaHeader.width, tgaHeader.height, PLAT(_currentContextPanel)->hdc, 32, &texture->_maskDib); else texture->_maskBitmap = NULL; for (int j = 0; j < texture->_tall; j++) { for (int i = 0; i < texture->_wide; i++) { int offs = (j * texture->_wide + i) * 4; char *src = ((char*)rgba) + offs; char *dst = ((char*)texture->_dib) + offs; if (bMask) { char *maskDst = ((char*) texture->_maskDib) + offs; // if there's value here, then it needs to be cleared if (src[3]) { // mask will be & with background, so it needs to be 0xFF maskDst[0] = maskDst[1] = maskDst[2] = maskDst[3] = -1;; // clear the art in the bitmap because it's not going to display and will be | with background dst[0] = dst[1] = dst[2] = dst[3] = 0; } else { // this will clear the background before drawing this bitmap maskDst[0] = maskDst[1] = maskDst[2] = maskDst[3] = 0x00; // copy over the art dst[0] = src[2]; dst[1] = src[1]; dst[2] = src[0]; dst[3] = src[3]; } } else { dst[0] = src[2]; dst[1] = src[1]; dst[2] = src[0]; dst[3] = src[3]; } } } free(rgba); return true; } //----------------------------------------------------------------------------- // Purpose: brings the current surface to the foreground //----------------------------------------------------------------------------- void CWin32Surface::BringToFront(VPANEL panel) { // force the panel to the top of the windows z-order panel = GetContextPanelForChildPanel(panel); if (panel && PLAT(panel)) { // this should trigger a WM_SETFOCUS message which will move the window to the top ::SetActiveWindow(PLAT(panel)->hwnd); } } //----------------------------------------------------------------------------- // Purpose: puts the thread that created the specified window into the foreground // and activates the window. //----------------------------------------------------------------------------- void CWin32Surface::SetForegroundWindow(VPANEL panel) { if (panel && PLAT(panel)) { ::SetForegroundWindow(PLAT(panel)->hwnd); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWin32Surface::MovePopupToFront(VPANEL panel) { _popupList.MoveElementToEnd(panel); g_pIVgui->PostMessage(panel, new KeyValues("OnMovedPopupToFront"), NULL); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWin32Surface::MovePopupToBack(VPANEL panel) { } //----------------------------------------------------------------------------- // Purpose: // HWND_TOPMOST - Places the window above all non-topmost windows. // The window maintains its topmost position even when it is deactivated. // HWND_NOTOPMOST - Places the window above all non-topmost windows (that is, behind // all topmost windows). This flag has no effect if the window is already a non-topmost window. //----------------------------------------------------------------------------- void CWin32Surface::SetAsTopMost(VPANEL panel, bool state) { panel = GetContextPanelForChildPanel(panel); DWORD style=SWP_NOMOVE|SWP_NOSIZE; if (PLAT(panel)->disabled) { style |= SWP_NOACTIVATE; } if (state) { SetFocus(PLAT(panel)->hwnd); SetWindowPos(PLAT(panel)->hwnd,HWND_TOPMOST,0,0,0,0,style ); } else { SetWindowPos(PLAT(panel)->hwnd,HWND_NOTOPMOST,0,0,0,0,style ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWin32Surface::SetPanelVisible(VPANEL panel, bool visible) { // if the panel has an attached window we need to set it's style if (PLAT(panel)) { if (visible) { // show the window ::ShowWindow(PLAT(panel)->hwnd, SW_SHOWNA); } else { // hide the window ::ShowWindow(PLAT(panel)->hwnd, SW_HIDE); } } } //----------------------------------------------------------------------------- // Purpose: Flashes the window icon in the taskbar, to get the users attention // Input : flashCount - number of times to flash the window //----------------------------------------------------------------------------- void CWin32Surface::FlashWindow(VPANEL panel, bool state) { if (!PLAT(panel)) return; ::FlashWindow(PLAT(panel)->hwnd, state); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWin32Surface::SetTopLevelFocus(VPANEL panel) { // this is handled by WM_FOCUS messages instead of directly } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWin32Surface::SetMinimized(VPANEL panel, bool state) { if (PLAT(panel)) { if (state) { ::ShowWindow(PLAT(panel)->hwnd, SW_MINIMIZE); } else { ::ShowWindow(PLAT(panel)->hwnd, SW_SHOWNORMAL); } } } //----------------------------------------------------------------------------- // Purpose: returns true if the window is minimized //----------------------------------------------------------------------------- bool CWin32Surface::IsMinimized(VPANEL panel) { if (PLAT(panel)->hwnd) { return ::IsIconic(PLAT(panel)->hwnd); } return false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWin32Surface::SetTitle(VPANEL panel, const wchar_t *title) { panel = GetContextPanelForChildPanel(panel); if (panel) { if (m_bSupportsUnicode) { SetWindowTextW(PLAT(panel)->hwnd, title); } else { char mbcs[512]; ::WideCharToMultiByte(CP_ACP, 0, title, -1, mbcs, sizeof(mbcs), NULL, NULL); SetWindowTextA(PLAT(panel)->hwnd, mbcs); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWin32Surface::SetAsToolBar(VPANEL panel, bool state) { panel = GetContextPanelForChildPanel(panel); if (panel && PLAT(panel)) { if (state) { ::SetWindowLong(PLAT(panel)->hwnd, GWL_EXSTYLE, ::GetWindowLong(PLAT(panel)->hwnd, GWL_EXSTYLE) | WS_EX_TOOLWINDOW); } else { ::SetWindowLong(PLAT(panel)->hwnd, GWL_EXSTYLE, ::GetWindowLong(PLAT(panel)->hwnd, GWL_EXSTYLE) & ~WS_EX_TOOLWINDOW); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CWin32Surface::GetPopupCount() { return _popupList.GetCount(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- VPANEL CWin32Surface::GetPopup(int index) { return _popupList[index]; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWin32Surface::CreatePopup(VPANEL panel, bool minimised, bool showTaskbarIcon, bool disabled, bool mouseInput, bool kbInput ) { if (((VPanel *)panel)->IsPopup() && PLAT(panel)) { // it's already a popup so bail return; } // make sure it's in the hierarchy if (!((VPanel *)panel)->GetParent() && panel != _embeddedPanel) { ((VPanel *)panel)->SetParent((VPanel *)_embeddedPanel); } int x,y,wide,tall; ((VPanel *)panel)->GetPos(x, y); ((VPanel *)panel)->GetSize(wide, tall); ((VPanel *)panel)->SetPopup(true); ((VPanel *)panel)->SetKeyBoardInputEnabled(kbInput); ((VPanel *)panel)->SetMouseInputEnabled(mouseInput); // find our parent window if we have one HWND hwndParent = NULL; VPANEL pParent = GetContextPanelForChildPanel(panel); if (pParent && pParent != _embeddedPanel) { hwndParent = PLAT(pParent)->hwnd; } //create the window and initialize platform specific data //window is initial a popup and not visible //when ApplyChanges is called the window will be shown unless //it SetVisible(false) is called SurfacePlat *plat = new SurfacePlat; ((VPanel *)panel)->SetPlat(plat); // WS_SYSMENU and WS_MINIMIZEBOX flags are there simply to make icon appear in taskbar // WS_EX_TOOLWINDOW hides the taskbar/ALT-TAB button DWORD style=0,style_ex=0; if (!showTaskbarIcon) { style = WS_POPUP | WS_CLIPCHILDREN; style_ex= WS_EX_TOOLWINDOW; if (!hwndParent) { hwndParent = PLAT(_embeddedPanel)->hwnd; } } else { style = WS_POPUP | WS_SYSMENU | WS_MINIMIZEBOX | WS_CLIPCHILDREN; } if (panel != _embeddedPanel && ((VPanel *)panel)->IsVisible()) { style |= WS_VISIBLE; } if(disabled) { style |= WS_DISABLED; } if ( minimised ) { style |= WS_MINIMIZE; } plat->hwnd = CreateWindowEx(style_ex, "Surface", "", style, x, y, wide, tall, hwndParent, NULL, GetModuleHandle(NULL), NULL); plat->clipRgn = CreateRectRgn(0,0,64,64); plat->hdc = CreateCompatibleDC(NULL); plat->hwndDC = NULL; plat->bitmap = null; plat->bitmapSize[0] = 0; plat->bitmapSize[1] = 0; plat->isFullscreen = false; plat->embeddedPanel = (VPanel *)panel; plat->disabled=disabled; plat->notifyIcon = NULL; plat->textureDC = NULL; ::SetBkMode(plat->hdc, TRANSPARENT); ::SetWindowLong(plat->hwnd, GWL_USERDATA, (LONG)g_pIVgui->PanelToHandle(panel)); ::SetTextAlign(plat->hdc, TA_LEFT | TA_TOP | TA_UPDATECP); if (!((VPanel *)panel)->IsVisible() || panel == _embeddedPanel) { SetPanelVisible(panel, false); } // create the context RecreateContext(panel); ::RegisterDragDrop(plat->hwnd, &staticDragDropTarget); // add the panel to the popup list if (!_popupList.HasElement(panel)) { _popupList.AddElement(panel); } else { // somehow getting added twice, fundamental problem _asm int 3; } // hack, force a windows sound to be played // the first time PlaySound is called there is 300ms hang, so this forces it to happen at startup // instead of while the user is trying to do something // we can't do this before a window created else it'll hang static bool first = true; if (first) { // double startTime = system()->GetCurrentTime(); ::PlaySoundA("", NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT | SND_NOSTOP | SND_NOWAIT); // double endTime = system()->GetCurrentTime(); // ivgui()->DPrintf2("PlaySound() : %dms\n", (int)((endTime - startTime) * 1000)); first = false; } //!! hack to set a valid current context panel SetCurrentContextPanel(_embeddedPanel); } //----------------------------------------------------------------------------- // Purpose: Called when a panel is created //----------------------------------------------------------------------------- void CWin32Surface::AddPanel(VPANEL panel) { } //----------------------------------------------------------------------------- // Purpose: Called when a panel gets deleted //----------------------------------------------------------------------------- void CWin32Surface::ReleasePanel(VPANEL panel) { _popupList.RemoveElement(panel); if (panel == _currentContextPanel) { _currentContextPanel = _embeddedPanel; } if (PLAT(panel)) { SurfacePlat *plat = PLAT(panel); // release drag/drop ::RevokeDragDrop(plat->hwnd); // remove notify icons if (plat->notifyIcon) { SetNotifyIcon(panel, NULL, NULL, NULL); } // hide the panel SetPanelVisible(panel, false); // free all the windows/bitmap/DC handles we are using ::SetWindowLong(plat->hwnd, GWL_USERDATA, (LONG)-1); ::SetWindowPos(plat->hwnd, HWND_BOTTOM, 0, 0, 1, 1, SWP_NOREDRAW|SWP_HIDEWINDOW); // free the window context if ( plat->bitmap ) { ::DeleteObject( plat->bitmap ); } if ( plat->textureDC ) { ::DeleteDC( plat->textureDC ); } if (plat->hwndDC) { ::ReleaseDC( plat->hwnd, plat->hwndDC ); } ::DeleteDC( plat->hdc ); ::DestroyWindow( plat->hwnd ); ::DeleteObject( plat->clipRgn ); // don't free staticWndclassAtom, because it is shared amongst surfaces, // and is automatically freed when the application terminates delete plat; ((VPanel *)panel)->SetPlat(NULL); } } //----------------------------------------------------------------------------- // Purpose: Applies any changes to the panel into the underline wnidow //----------------------------------------------------------------------------- bool CWin32Surface::RecreateContext(VPANEL panel) { if (panel && PLAT(panel)) { SurfacePlat *plat = PLAT(panel); int wide,tall; ((VPanel *)panel)->GetSize(wide,tall); // rebuild bitmap only if necessary // simple scheme to prevent excessive allocations by allocating only when // bigger. It also adds in 100 extra for subsequent sizings // it will also realloc if the size is 200 smaller to shrink memory usage if ((wide > plat->bitmapSize[0]) || (tall > plat->bitmapSize[1]) || (wide < (plat->bitmapSize[0] - 200)) || (tall < (plat->bitmapSize[1] - 200))) { if (plat->bitmap != null) { ::DeleteObject(plat->bitmap); } plat->hwndDC = GetDC(plat->hwnd); plat->bitmap = ::CreateCompatibleBitmap(plat->hwndDC, wide + 100, tall + 100); plat->bitmapSize[0] = wide + 100; plat->bitmapSize[1] = tall + 100; if (plat->textureDC) { ::DeleteDC(plat->textureDC); } ::SelectObject(plat->hdc, plat->bitmap); plat->textureDC = ::CreateCompatibleDC(plat->hdc); ::ReleaseDC(plat->hwnd, plat->hwndDC); plat->hwndDC = NULL; } } return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWin32Surface::EnableMouseCapture(VPANEL panel, bool state) { VPANEL contextPanel = GetContextPanelForChildPanel(panel); if (state) { ::SetCapture(PLAT(contextPanel)->hwnd); } else { ::ReleaseCapture(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CWin32Surface::ShouldPaintChildPanel(VPANEL childPanel) { // don't Paint children as part of the normal process, handle them in with WM_PAINT messages instead return !((VPanel *)childPanel)->IsPopup(); } //----------------------------------------------------------------------------- // Purpose: Called after a Paint to display the new buffer //----------------------------------------------------------------------------- void CWin32Surface::SwapBuffers(VPANEL panel) { if (PLAT(panel)) { START_TIMER(); SurfacePlat *plat = PLAT(panel); int wide,tall; ((VPanel *)panel)->GetSize(wide,tall); plat->hwndDC = ::GetDC(plat->hwnd); // reset origin and clipping then blit ::SetRectRgn(plat->clipRgn, 0, 0, wide, tall); ::SelectObject(plat->hdc, plat->clipRgn); ::SetViewportOrgEx(plat->hdc, 0, 0, NULL); ::BitBlt(plat->hwndDC, 0, 0, wide, tall, plat->hdc, 0, 0, SRCCOPY); ::ReleaseDC(plat->hwnd, plat->hwndDC); plat->hwndDC = NULL; END_TIMER("SwapBuffers time: %.2fms\n"); } } //----------------------------------------------------------------------------- // Purpose: Called every frame to change to window state if necessary //----------------------------------------------------------------------------- void CWin32Surface::ApplyChanges() { for (int i = 0; i < GetPopupCount(); i++) { VPANEL panel = GetPopup(i); if (!PLAT(panel)) continue; SurfacePlat *Plat = PLAT(panel); // force the main panel to be invisible if (panel == GetEmbeddedPanel()) { ::ShowWindow(Plat->hwnd, SW_HIDE); continue; } // hide and skip by invisible panels if (!((VPanel *)panel)->IsVisible()) { ::ShowWindow(Plat->hwnd, SW_HIDE); continue; } RECT rect; int x, y, wide, tall, sx, sy, swide, stall; // get how big the win32 window ::GetWindowRect(Plat->hwnd, &rect); sx = rect.left; sy = rect.top; swide = rect.right-rect.left; stall = rect.bottom-rect.top; // how big is the embedded VPanel ((VPanel *)panel)->GetPos(x, y); ((VPanel *)panel)->GetSize(wide, tall); // if they are not the same, then adjust the win32 window so it is if ((x != sx) || (y != sy) || (wide != swide) || (tall != stall)) { ::SetWindowPos(Plat->hwnd, null, x, y, wide, tall, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS); if ( sx > 0 || sy > 0 ) // only message for moves that are on the screen { g_pIVgui->PostMessage(panel, new KeyValues("Move"), NULL ); } } // check to see if the win32 window is visible if (::GetWindowLong(Plat->hwnd, GWL_STYLE) & WS_VISIBLE) { //check to see if embedded VPanel is not visible, if so then hide the win32 window if (!((VPanel *)panel)->IsVisible()) { ::ShowWindow(Plat->hwnd, SW_HIDE); } } else // win32 window is hidden { //check to see if embedded VPanel is visible, if so then show the win32 window if (((VPanel *)panel)->IsVisible()) { ::ShowWindow(Plat->hwnd, SW_SHOWNA); } } //if the win32 window changed size and is visible, then the context needs to be recreated if (((wide != swide) || (tall != stall)) && ((VPanel *)panel)->IsVisible()) { RecreateContext(panel); } } } //----------------------------------------------------------------------------- // Purpose: recurses the panels calculating absolute positions // parents must be solved before children //----------------------------------------------------------------------------- void CWin32Surface::InternalSolveTraverse(VPANEL panel) { // solve the parent ((VPanel *)panel)->Solve(); // now we can solve the children for (int i = 0; i < ((VPanel *)panel)->GetChildCount(); i++) { VPanel *child = ((VPanel *)panel)->GetChild(i); if (child->IsVisible()) { InternalSolveTraverse((VPANEL)child); } } } //----------------------------------------------------------------------------- // Purpose: recurses the panels giving them a chance to do a user-defined think, // PerformLayout and ApplySchemeSettings // must be done child before parent //----------------------------------------------------------------------------- void CWin32Surface::InternalThinkTraverse(VPANEL panel) { // parent ((VPanel *)panel)->Client()->Think(); // then the children... for (int i = 0; i < ((VPanel *)panel)->GetChildCount(); i++) { VPanel *child = ((VPanel *)panel)->GetChild(i); if (child->IsVisible()) { InternalThinkTraverse((VPANEL)child); } } } //----------------------------------------------------------------------------- // Purpose: recurses the panels giving them a chance to do a ApplySchemeSettings // must be done child before parent //----------------------------------------------------------------------------- void CWin32Surface::InternalSchemeSettingsTraverse(VPANEL panel, bool forceApplySchemeSettings) { // think the children... for (int i = 0; i < ((VPanel *)panel)->GetChildCount(); i++) { VPanel *child = ((VPanel *)panel)->GetChild(i); if ( forceApplySchemeSettings || child->IsVisible() ) { InternalSchemeSettingsTraverse((VPANEL)child, forceApplySchemeSettings); } } // ... then the parent ((VPanel *)panel)->Client()->PerformApplySchemeSettings(); } //----------------------------------------------------------------------------- // Purpose: Walks through the panel tree calling Solve() on them all, in order //----------------------------------------------------------------------------- void CWin32Surface::SolveTraverse(VPANEL panel, bool forceApplySchemeSettings) { // ignore visibility for this InternalSchemeSettingsTraverse(panel,forceApplySchemeSettings); // do apply scheme settings, child to parent if (!((VPanel *)panel)->IsVisible()) return; InternalThinkTraverse(panel); InternalSolveTraverse(panel); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWin32Surface::PaintTraverse(VPANEL panel) { START_TIMER(); ((VPanel *)panel)->Client()->PaintTraverse(false,true); END_TIMER("Paint time: %.2fms\n"); } // FIXME: write these functions! void CWin32Surface::RestrictPaintToSinglePanel(VPANEL panel) { } void CWin32Surface::SetModalPanel(VPANEL ) { } VPANEL CWin32Surface::GetModalPanel() { return 0; } void CWin32Surface::UnlockCursor() { } void CWin32Surface::LockCursor() { } void CWin32Surface::SetTranslateExtendedKeys(bool state) { } VPANEL CWin32Surface::GetTopmostPopup() { return 0; } //----------------------------------------------------------------------------- // Purpose: Checks to see if the mouse should be visible or not //----------------------------------------------------------------------------- void CWin32Surface::CalculateMouseVisible() { int i; _needMouse = false; _needKB = false; for(i = 0 ; i < g_pSurface->GetPopupCount() ; i++ ) { VPanel *pop = (VPanel *)g_pSurface->GetPopup( i ) ; bool isVisible=pop->IsVisible(); VPanel *p= pop->GetParent(); while(p && isVisible) { if( p->IsVisible()==false) { isVisible=false; break; } p=p->GetParent(); } if(isVisible) { _needMouse |= pop->IsMouseInputEnabled(); _needKB |= pop->IsKeyBoardInputEnabled(); } } if (_needMouse) { SetCursor(vgui::dc_arrow); UnlockCursor(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CWin32Surface::NeedKBInput() { return _needKB; } //----------------------------------------------------------------------------- // Purpose: Sets the current cursor //----------------------------------------------------------------------------- void CWin32Surface::SetCursor(HCursor cursor) { switch (cursor) { case dc_none: case dc_arrow: case dc_ibeam: case dc_hourglass: case dc_waitarrow: case dc_crosshair: case dc_up: case dc_sizenwse: case dc_sizenesw: case dc_sizewe: case dc_sizens: case dc_sizeall: case dc_no: case dc_hand: { _currentCursor = staticDefaultCursor[cursor]; break; } case dc_user: { break; } case dc_blank: // don't touch the cursor, just stick with what windows is currently using { return; break; } } ::SetCursor(_currentCursor); } //----------------------------------------------------------------------------- // Purpose: Forces the window to be redrawn //----------------------------------------------------------------------------- void CWin32Surface::Invalidate(VPANEL panel) { panel = GetContextPanelForChildPanel(panel); if (panel && PLAT(panel) && ((VPanel *)panel)->IsVisible()) { // last parm must be false so WM_ERASEBKGND is not generated, that should // only be generated by windows InvalidateRect(PLAT(panel)->hwnd, NULL, false); } } //----------------------------------------------------------------------------- // Purpose: Checks to see if any of the windows have focus // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CWin32Surface::HasFocus() { HWND focus = ::GetFocus(); if (!focus) return false; // see if any of the windows on the surface have the focus for (int i = 0; i < GetPopupCount(); i++) { VPANEL panel = GetPopup(i); if (focus == PLAT(panel)->hwnd) return true; } return false; } //----------------------------------------------------------------------------- // Purpose: Returns true if the cursor is over this surface // Uses the windows call to do this, instead of doing it procedurally //----------------------------------------------------------------------------- bool CWin32Surface::IsWithin(int x,int y) { POINT pnt={x,y}; HWND hwnd = WindowFromPoint(pnt); for (int i = 0; i < GetPopupCount(); i++) { if (PLAT(GetPopup(i))->hwnd == hwnd) { return true; } } return false; } //----------------------------------------------------------------------------- // Purpose: handles a set focus message //----------------------------------------------------------------------------- void CWin32Surface::setFocus(VPANEL panel) { // reset the focus to the last focused panel panel = GetContextPanelForChildPanel(panel); if (panel && PLAT(panel)) { // make sure we have a valid panel VPANEL focus = panel; if (focus && ((VPanel *)focus)->HasParent((VPanel *)panel)) { // Must be a child of the modal surface, if set if ( g_pInput->GetAppModalSurface() && !((VPanel *)focus)->HasParent((VPanel *)g_pInput->GetAppModalSurface()) ) { // trying to set focus to something that's not the modal panel, set it back SetForegroundWindow(g_pInput->GetAppModalSurface()); } else { // make sure focus is at top of list // the subfocus will be automatically calculated in IInput::RunFrame() ((VPanel *)focus)->MoveToFront(); return; } } } // let the main panel get focus as the default if (panel) { ((VPanel *)panel)->Client()->RequestFocus(0); } } struct FontRangeItem_t { const char *name; int lowerRange; int upperRange; }; FontRangeItem_t g_FontRanges[] = { { "Basic Latin", 0x0000, 0x007F }, { "Latin-1 Supplement", 0x0080, 0x00FF }, { "Latin Extended-A", 0x0100, 0x017F }, { "Latin Extended-B", 0x0180, 0x024F }, { "IPA Extensions", 0x0250, 0x02AF }, { "Spacing Modifier Letters", 0x02B0, 0x02FF }, { "Combining Diacritical Marks", 0x0300, 0x036F }, { "Greek", 0x0370, 0x03FF }, { "Cyrillic", 0x0400, 0x04FF }, { "Armenian", 0x0530, 0x058F }, { "Hebrew", 0x0590, 0x05FF }, { "Arabic", 0x0600, 0x06FF }, { "Syriac", 0x0700, 0x074F }, { "Thaana", 0x0780, 0x07BF }, { "Devanagari", 0x0900, 0x097F }, { "Bengali", 0x0980, 0x09FF }, { "Gurmukhi", 0x0A00, 0x0A7F }, { "Gujarati", 0x0A80, 0x0AFF }, { "Oriya", 0x0B00, 0x0B7F }, { "Tamil", 0x0B80, 0x0BFF }, { "Telugu", 0x0C00, 0x0C7F }, { "Kannada", 0x0C80, 0x0CFF }, { "Malayalam", 0x0D00, 0x0D7F }, { "Sinhala", 0x0D80, 0x0DFF }, { "Thai", 0x0E00, 0x0E7F }, { "Lao", 0x0E80, 0x0EFF }, { "Tibetan", 0x0F00, 0x0FBF }, { "Myanmar", 0x1000, 0x109F }, { "Georgian", 0x10A0, 0x10FF }, { "Hangul Jamo", 0x1100, 0x11FF }, { "Ethiopic", 0x1200, 0x137F }, { "Cherokee", 0x13A0, 0x13FF }, { "Unified Canadian Aboriginal Syllabics", 0x1400, 0x167F }, { "Ogham", 0x1680, 0x169F }, { "Runic", 0x16A0, 0x16FF }, { "Khmer", 0x1780, 0x17FF }, { "Mongolian", 0x1800, 0x18AF }, { "Latin Extended Additional", 0x1E00, 0x1EFF }, { "Greek Extended", 0x1F00, 0x1FFF }, { "General Punctuation", 0x2000, 0x206F }, { "Superscripts and Subscripts", 0x2070, 0x209F }, { "Currency Symbols", 0x20A0, 0x20CF }, { "Combining Diacritical Marks for Symbols",0x20D0, 0x20FF }, { "Letterlike Symbols", 0x2100, 0x214F }, { "Number Forms", 0x2150, 0x218F }, { "Arrows", 0x2190, 0x21FF }, { "Mathematical Operators", 0x2200, 0x22FF }, { "Miscellaneous Technical", 0x2300, 0x23FF }, { "Control Pictures", 0x2400, 0x243F }, { "Optical Character Recognition", 0x2440, 0x245F }, { "Enclosed Alphanumerics", 0x2460, 0x24FF }, { "Box Drawing", 0x2500, 0x257F }, { "Block Elements", 0x2580, 0x259F }, { "Geometric Shapes", 0x25A0, 0x25FF }, { "Miscellaneous Symbols", 0x2600, 0x26FF }, { "Dingbats", 0x2700, 0x27BF }, { "Braille Patterns", 0x2800, 0x28FF }, { "CJK Radicals Supplement", 0x2E80, 0x2EFF }, { "KangXi Radicals", 0x2F00, 0x2FDF }, { "Ideographic Description Characters", 0x2FF0, 0x2FFF }, { "CJK Symbols and Punctuation", 0x3000, 0x303F }, { "Hiragana", 0x3040, 0x309F }, { "Katakana", 0x30A0, 0x30FF }, { "Bopomofo", 0x3100, 0x312F }, { "Hangul Compatibility Jamo", 0x3130, 0x318F }, { "Kanbun", 0x3190, 0x319F }, { "Bopomofo Extended", 0x31A0, 0x31BF }, { "Enclosed CJK Letters and Months", 0x3200, 0x32FF }, { "CJK Compatibility", 0x3300, 0x33FF }, { "CJK Unified Ideographs Extension A", 0x3400, 0x4DB5 }, { "CJK Unified Ideographs", 0x4E00, 0x9FFF }, { "Yi Syllables", 0xA000, 0xA48F }, { "Yi Radicals", 0xA490, 0xA4CF }, { "Hangul Syllables", 0xAC00, 0xD7A3 }, { "-- surrogates --", 0xD800, 0xDBFF }, { "CJK Compatibility Ideographs", 0xF900, 0xFAFF }, { "Alphabetic Presentation Forms", 0xFB00, 0xFB4F }, { "Arabic Presentation Forms-A", 0xFB50, 0xFDFF }, { "Combining Half Marks", 0xFE20, 0xFE2F }, { "CJK Compatibility Forms", 0xFE30, 0xFE4F }, { "Small Form Variants", 0xFE50, 0xFE6F }, { "Arabic Presentation Forms-B", 0xFE70, 0xFEFF }, { "Halfwidth and Fullwidth Forms", 0xFF00, 0xFFEF }, { "Specials", 0xFFF0, 0xFFFF }, }; //----------------------------------------------------------------------------- // Purpose: creates a new empty font //----------------------------------------------------------------------------- HFont CWin32Surface::CreateFont() { return FontManager().CreateFont(); } //----------------------------------------------------------------------------- // Purpose: adds glyphs to a font created by CreateFont() //----------------------------------------------------------------------------- bool CWin32Surface::SetFontGlyphSet(HFont font, const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags, int nRangeMin, int nRangeMax) { return FontManager().SetFontGlyphSet(font, windowsFontName, tall, weight, blur, scanlines, flags, nRangeMin, nRangeMax); } //----------------------------------------------------------------------------- // Purpose: returns the max height of a font //----------------------------------------------------------------------------- int CWin32Surface::GetFontTall(HFont font) { return FontManager().GetFontTall(font); } //----------------------------------------------------------------------------- // Purpose: returns the requested height of a font //----------------------------------------------------------------------------- int CWin32Surface::GetFontTallRequested(HFont font) { return FontManager().GetFontTallRequested(font); } //----------------------------------------------------------------------------- // Purpose: returns the max height of a font //----------------------------------------------------------------------------- int CWin32Surface::GetFontAscent(HFont font, wchar_t wch) { return FontManager().GetFontAscent(font,wch); } //----------------------------------------------------------------------------- // Purpose: // Input : font - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CWin32Surface::IsFontAdditive(HFont font) { return FontManager().IsFontAdditive(font); } //----------------------------------------------------------------------------- // Purpose: returns the abc widths of a single character //----------------------------------------------------------------------------- void CWin32Surface::GetCharABCwide(HFont font, int ch, int &a, int &b, int &c) { FontManager().GetCharABCwide(font, ch, a, b, c); } //----------------------------------------------------------------------------- // Purpose: returns the pixel width of a single character //----------------------------------------------------------------------------- int CWin32Surface::GetCharacterWidth(HFont font, int ch) { return FontManager().GetCharacterWidth(font, ch); } //----------------------------------------------------------------------------- // Purpose: returns the area of a text string, including newlines //----------------------------------------------------------------------------- void CWin32Surface::GetTextSize(HFont font, const wchar_t *text, int &wide, int &tall) { FontManager().GetTextSize(font, text, wide, tall); } //----------------------------------------------------------------------------- // Purpose: adds a custom font file (only supports true type font files (.ttf) for now) //----------------------------------------------------------------------------- bool CWin32Surface::AddCustomFontFile(const char *fontName, const char *fontFileName) { char fullPath[ MAX_PATH ]; g_pFullFileSystem->GetLocalPath(fontFileName, fullPath, sizeof( fullPath )); m_CustomFontFileNames.AddToTail(fontFileName); return (::AddFontResource(fullPath) > 0); } //----------------------------------------------------------------------------- // Purpose: Pre-compiled bitmap font support for game engine - not implemented for GDI //----------------------------------------------------------------------------- bool CWin32Surface::AddBitmapFontFile(const char *fontFileName) { Assert( 0 ); return false; } void CWin32Surface::SetBitmapFontName( const char *pName, const char *pFontFilename ) { Assert( 0 ); } const char *CWin32Surface::GetBitmapFontName( const char *pName ) { Assert( 0 ); return NULL; } bool CWin32Surface::SetBitmapFontGlyphSet(HFont font, const char *windowsFontName, float scalex, float scaley, int flags) { Assert( 0 ); return false; } void CWin32Surface::PrecacheFontCharacters(HFont font, const wchar_t *pCharacters) { Assert( 0 ); } void CWin32Surface::ClearTemporaryFontCache( void ) { Assert( 0 ); } const char *CWin32Surface::GetFontName( HFont font ) { return FontManager().GetFontName( font ); } const char *CWin32Surface::GetFontFamilyName( HFont font ) { return FontManager().GetFontFamilyName( font ); } void CWin32Surface::DrawSetTextScale(float sx, float sy) { Assert( 0 ); } void CWin32Surface::SetPanelForInput( VPANEL vpanel ) { Assert( 0 ); } void CWin32Surface::DrawFilledRectFastFade( int x0, int y0, int x1, int y1, int fadeStartPt, int fadeEndPt, unsigned int alpha0, unsigned int alpha1, bool bHorizontal ) { Assert( 0 ); } void CWin32Surface::DrawFilledRectFade( int x0, int y0, int x1, int y1, unsigned int alpha0, unsigned int alpha1, bool bHorizontal ) { Assert( 0 ); } //----------------------------------------------------------------------------- // Purpose: Returns the bounds of the usable workspace area //----------------------------------------------------------------------------- void CWin32Surface::GetWorkspaceBounds(int &x, int &y, int &wide, int &tall) { RECT rcScreen; ::SystemParametersInfo(SPI_GETWORKAREA, 0, &rcScreen, 0); x = rcScreen.left; y = rcScreen.top; wide = rcScreen.right - x; tall = rcScreen.bottom - y; } //----------------------------------------------------------------------------- // Purpose: gets the absolute coordinates of the screen (in screen space) //----------------------------------------------------------------------------- void CWin32Surface::GetAbsoluteWindowBounds(int &x, int &y, int &wide, int &tall) { // always work in full window screen space x = 0; y = 0; GetScreenSize(wide, tall); } //----------------------------------------------------------------------------- // Purpose: Plays a sound // Input : *fileName - name of the wav file //----------------------------------------------------------------------------- void CWin32Surface::PlaySound(const char *fileName) { char localPath[MAX_PATH]; if (!g_pFullFileSystem->GetLocalPath(fileName, localPath, sizeof(localPath))) return; g_pFullFileSystem->GetLocalCopy(localPath); ::PlaySoundA(localPath, NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT | SND_NOSTOP | SND_NOWAIT); } bool GetIconSize( ICONINFO& iconInfo, int& w, int& h ) { w = h = 0; HBITMAP bitmap = iconInfo.hbmColor; BITMAP bm; if ( 0 == GetObject((HGDIOBJ)bitmap, sizeof(BITMAP), (LPVOID)&bm) ) { return false; } w = bm.bmWidth; h = bm.bmHeight; return true; } class CIconImage : public IImage { public: CIconImage( HICON hIcon ) : m_hIcon( CopyIcon( hIcon ) ) { m_Pos.x = m_Pos.y = 0; ICONINFO iconInfo; if ( 0 != GetIconInfo( m_hIcon, &iconInfo ) ) { int w, h; GetIconSize( iconInfo, w, h ); m_Size.cx = w; m_Size.cy = h; } else { m_Size.cx = 0; m_Size.cy = 0; } } // virtual destructor virtual ~CIconImage() { DestroyIcon( m_hIcon ); } // Call to Paint the image // Image will draw within the current panel context at the specified position virtual void Paint() { if ( !m_hIcon ) return; if ( !m_Size.cx || !m_Size.cy ) return; HDC hdc = PLAT(g_Surface._currentContextPanel)->hdc; // Translate position to screen space based on surface state... int x, y; x = m_Pos.x; y = m_Pos.y; DrawIconEx ( hdc, x, y, m_hIcon, m_Size.cx, m_Size.cy, 0, NULL, DI_NORMAL ); } // Set the position of the image virtual void SetPos(int x, int y) { m_Pos.x = x; m_Pos.y = y; } // Gets the size of the content virtual void GetContentSize(int &wide, int &tall) { wide = m_Size.cx; tall = m_Size.cy; } // Get the size the image will actually draw in (usually defaults to the content size) virtual void GetSize(int &wide, int &tall) { GetContentSize( wide, tall ); } // Sets the size of the image virtual void SetSize(int wide, int tall) { // Nothing } // Set the draw color virtual void SetColor(Color col) { // Nothing } virtual bool Evict() { return false; } virtual int GetNumFrames() { return 0; } virtual void SetFrame( int nFrame ) {} virtual HTexture GetID() { return 0; } virtual void SetRotation( int iRotation ) { return; }; private: HICON m_hIcon; POINT m_Pos; SIZE m_Size; }; static bool ShouldMakeUnique( char const *extension ) { if ( !Q_stricmp( extension, "cur" ) ) return true; if ( !Q_stricmp( extension, "ani" ) ) return true; return false; } IImage *CWin32Surface::GetIconImageForFullPath( char const *pFullPath ) { IImage *newIcon = NULL; SHFILEINFO info = { 0 }; DWORD_PTR dwResult = SHGetFileInfo( pFullPath, 0, &info, sizeof( info ), SHGFI_TYPENAME | SHGFI_ICON | SHGFI_SMALLICON | SHGFI_SHELLICONSIZE ); if ( dwResult ) { if ( info.szTypeName[ 0 ] != 0 ) { char ext[ 32 ]; Q_ExtractFileExtension( pFullPath, ext, sizeof( ext ) ); char lookup[ 512 ]; Q_snprintf( lookup, sizeof( lookup ), "%s", ShouldMakeUnique( ext ) ? pFullPath : info.szTypeName ); // Now check the dictionary unsigned short idx = m_FileTypeImages.Find( lookup ); if ( idx == m_FileTypeImages.InvalidIndex() ) { newIcon = new CIconImage( info.hIcon ); idx = m_FileTypeImages.Insert( lookup, newIcon ); } newIcon = m_FileTypeImages[ idx ]; } DestroyIcon( info.hIcon ); } return newIcon; } //----------------------------------------------------------------------------- // Purpose: Handles windows message pump //----------------------------------------------------------------------------- void CWin32Surface::RunFrame() { ::MSG msg; if (!g_pIVgui->GetShouldVGuiControlSleep()) { // if vgui doesn't control sleeping, then make sure we don't block in this loop if (!::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) return; } // always get at least one message // this means it will block until input // there is a timer set to force this loop to run at least 20Hz, which should be fine for the desktop do { BOOL ret = ::GetMessage(&msg, NULL, 0, 0); if (ret == 0) break; if (ret == -1) { g_pIVgui->Stop(); break; } ::TranslateMessage(&msg); ::DispatchMessage(&msg); } while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)); } //----------------------------------------------------------------------------- // Purpose: cap bits //----------------------------------------------------------------------------- bool CWin32Surface::SupportsFeature(SurfaceFeature_e feature) { switch (feature) { case ISurface::ESCAPE_KEY: case ISurface::ANTIALIASED_FONTS: case ISurface::OPENING_NEW_HTML_WINDOWS: case ISurface::FRAME_MINIMIZE_MAXIMIZE: case ISurface::DIRECT_HWND_RENDER: return true; case ISurface::DROPSHADOW_FONTS: case ISurface::OUTLINE_FONTS: default: return false; }; } //----------------------------------------------------------------------------- // Purpose: Initializes all the static data for the app // Should be only called during load time //----------------------------------------------------------------------------- void CWin32Surface::initStaticData() { //load up all default cursors, this gets called everytime a Surface is created, but //who cares staticDefaultCursor[dc_none] =null; staticDefaultCursor[dc_arrow] =(HICON)LoadCursor(null,(LPCTSTR)OCR_NORMAL); staticDefaultCursor[dc_ibeam] =(HICON)LoadCursor(null,(LPCTSTR)OCR_IBEAM); staticDefaultCursor[dc_hourglass]=(HICON)LoadCursor(null,(LPCTSTR)OCR_WAIT); staticDefaultCursor[dc_waitarrow]=(HICON)LoadCursor(null,(LPCTSTR)OCR_APPSTARTING); staticDefaultCursor[dc_crosshair]=(HICON)LoadCursor(null,(LPCTSTR)OCR_CROSS); staticDefaultCursor[dc_up] =(HICON)LoadCursor(null,(LPCTSTR)OCR_UP); staticDefaultCursor[dc_sizenwse] =(HICON)LoadCursor(null,(LPCTSTR)OCR_SIZENWSE); staticDefaultCursor[dc_sizenesw] =(HICON)LoadCursor(null,(LPCTSTR)OCR_SIZENESW); staticDefaultCursor[dc_sizewe] =(HICON)LoadCursor(null,(LPCTSTR)OCR_SIZEWE); staticDefaultCursor[dc_sizens] =(HICON)LoadCursor(null,(LPCTSTR)OCR_SIZENS); staticDefaultCursor[dc_sizeall] =(HICON)LoadCursor(null,(LPCTSTR)OCR_SIZEALL); staticDefaultCursor[dc_no] =(HICON)LoadCursor(null,(LPCTSTR)OCR_NO); staticDefaultCursor[dc_hand] =(HICON)LoadCursor(null,(LPCTSTR)32649); // make and register a very simple Window Class memset( &staticWndclass,0,sizeof(staticWndclass) ); staticWndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; staticWndclass.lpfnWndProc = staticProc; staticWndclass.hInstance = GetModuleHandle(NULL); // Get the resource ID of the icon group from the environment...default to 101. DWORD wIconID = 101; const char *sIconResourceID = getenv(szSteamBootStrapperIconIdEnvVar); if ( sIconResourceID ) { DWORD wTmpIconID = 0; if ( sscanf(sIconResourceID, "%u", &wTmpIconID) == 1 && wTmpIconID > 101 ) { wIconID = wTmpIconID; } } staticWndclass.hIcon = ::LoadIcon(staticWndclass.hInstance, MAKEINTRESOURCE(wIconID)); staticWndclass.lpszClassName = "Surface"; staticWndclassAtom = ::RegisterClass( &staticWndclass ); // register our global Shutdown command staticShutdownMsg = ::RegisterWindowMessage("ShutdownValvePlatform"); } //----------------------------------------------------------------------------- // Purpose: our basic user agent string when doing http requests from the client for webkit //----------------------------------------------------------------------------- const char *CWin32Surface::GetWebkitHTMLUserAgentString() { return "Valve Client"; } //----------------------------------------------------------------------------- // Purpose: Handle switching in and out of "render to fullscreen" mode. We don't // actually support this mode in tools. //----------------------------------------------------------------------------- void CWin32Surface::PushFullscreenViewport() { AssertMsg( false, "Fullscreen viewport mode is unimplemented in CWin32Surface" ); } void CWin32Surface::PopFullscreenViewport() { } //----------------------------------------------------------------------------- // Purpose: Handles windows messages sent to the notify tray icon //----------------------------------------------------------------------------- static void staticNotifyIconProc(HWND hwnd, WPARAM wparam, LPARAM lparam) { switch (lparam) { case WM_LBUTTONDOWN: { // notify the window of the double click if (g_Surface.GetNotifyPanel()) { g_pIVgui->PostMessage(g_pSurface->GetNotifyPanel(), new KeyValues("NotifyIconMsg", "msg", "WM_LBUTTONDOWN"), NULL); } break; } case WM_LBUTTONDBLCLK: { //HACK: always bring us to front if the user double clicks the icon ::SetForegroundWindow(hwnd); // notify the window of the double click if (g_pSurface->GetNotifyPanel()) { g_pIVgui->PostMessage(g_pSurface->GetNotifyPanel(), new KeyValues("NotifyIconMsg", "msg", "LBUTTONDBLCLK"), NULL); } break; } case WM_RBUTTONUP: // win95/98/NT case WM_CONTEXTMENU: // win2k/ME { // display context menu if (g_pSurface->GetNotifyPanel()) { g_pIVgui->PostMessage(g_pSurface->GetNotifyPanel(), new KeyValues("NotifyIconMsg", "msg", "CONTEXTMENU"), NULL); } break; } default: break; } } static KeyCode g_iPreviousKeyCode = KEY_NONE; static void PostCursorMoved( HWND hwnd, LPARAM lparam ) { POINT pt; pt.x = (short)LOWORD(lparam); pt.y = (short)HIWORD(lparam); ::ClientToScreen( hwnd, &pt); g_pInput->InternalCursorMoved(pt.x, pt.y); } static LRESULT CALLBACK staticProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam) { static UINT s_uTaskbarRestart; VPANEL panel = NULL; IClientPanel *client = NULL; if (staticSurfaceAvailable) { panel = g_pIVgui->HandleToPanel(::GetWindowLong(hwnd, GWL_USERDATA)); if (panel) { client = ((VPanel *)panel)->Client(); } } // special case msg handle if (msg == staticShutdownMsg) { // we're being notified that we have to Shutdown g_pIVgui->ShutdownMessage(lparam); return ::DefWindowProc(hwnd,msg,wparam,lparam); } if (msg == WM_ENDSESSION && wparam == TRUE) { // system is being shutdown // after this message is processed, the app will be terminated // so all necessary shutdown functions need to occur now if (g_pSurface->GetEmbeddedPanel()) { g_pIPanel->SendMessage(g_pSurface->GetEmbeddedPanel(), new KeyValues("WindowsEndSession"), NULL); } return 0; } if (!panel) { return ::DefWindowProc(hwnd,msg,wparam,lparam); } bool sendToDefWindowProc = true; // md: temporarily disabled until the infinite recursion gets fixed. if ( ImmIsUIMessage( NULL, msg, wparam, lparam ) ) { sendToDefWindowProc = false; } switch (msg) { case MS_WM_XBUTTONDOWN: { PostCursorMoved( hwnd, lparam ); MouseCode code = ( HIWORD( wparam ) == 1 ) ? MOUSE_4 : MOUSE_5; g_pInput->SetMouseCodeState( code, BUTTON_PRESSED ); g_pInput->InternalMousePressed( code ); break; } case MS_WM_XBUTTONUP: { PostCursorMoved( hwnd, lparam ); MouseCode code = ( HIWORD( wparam ) == 1 ) ? MOUSE_4 : MOUSE_5; g_pInput->SetMouseCodeState( code, BUTTON_RELEASED ); g_pInput->InternalMouseReleased( code ); break; } case MS_WM_XBUTTONDBLCLK: { PostCursorMoved( hwnd, lparam ); MouseCode code = ( HIWORD( wparam ) == 1 ) ? MOUSE_4 : MOUSE_5; g_pInput->SetMouseCodeState( code, BUTTON_DOUBLECLICKED ); g_pInput->InternalMouseDoublePressed( code ); break; } case WM_CREATE: { s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); break; } case WM_CLOSE: { // tell the panel to close g_pIVgui->PostMessage(panel, new KeyValues("Close"), NULL); // don't Run default message pump, as that destroys the window return 0; } case WM_MY_TRAY_NOTIFICATION: { staticNotifyIconProc(hwnd, wparam, lparam); break; } case WM_SETFOCUS: { g_Surface.setFocus(panel); //g_pIVgui->DPrintf("Set Focus %s %p\n", client->GetName(), panel); break; } case WM_KILLFOCUS: { g_Surface.setFocus(NULL); break; } case WM_APP: { g_Surface.setFocus(NULL); break; } case WM_SETCURSOR: { //!! SetCursor(staticCurrentCursor); break; } case WM_MOUSEMOVE: { POINT pt; pt.x = (short)LOWORD(lparam); pt.y = (short)HIWORD(lparam); // This code catches the case when a WM_LBUTTONUP is lost bool bLMButtonDown = wparam & MK_LBUTTON; bool bRMButtonDown = wparam & MK_RBUTTON; bool bMMButtonDown = wparam & MK_MBUTTON; if ( !bLMButtonDown && g_pInput->IsMouseDown( MOUSE_LEFT ) ) { g_pInput->SetMouseCodeState( MOUSE_LEFT, BUTTON_RELEASED ); g_pInput->InternalMouseReleased(MOUSE_LEFT); } if ( !bRMButtonDown && g_pInput->IsMouseDown( MOUSE_RIGHT ) ) { g_pInput->SetMouseCodeState( MOUSE_LEFT, BUTTON_RELEASED ); g_pInput->InternalMouseReleased(MOUSE_LEFT); } if ( !bMMButtonDown && g_pInput->IsMouseDown( MOUSE_MIDDLE ) ) { g_pInput->SetMouseCodeState( MOUSE_MIDDLE, BUTTON_RELEASED ); g_pInput->InternalMouseReleased(MOUSE_MIDDLE); } ::ClientToScreen((HWND)hwnd, &pt); g_pInput->InternalCursorMoved(pt.x, pt.y); break; } case WM_LBUTTONDOWN: { PostCursorMoved( hwnd, lparam ); g_pInput->SetMouseCodeState( MOUSE_LEFT, BUTTON_PRESSED ); g_pInput->InternalMousePressed(MOUSE_LEFT); break; } case WM_RBUTTONDOWN: { PostCursorMoved( hwnd, lparam ); g_pInput->SetMouseCodeState( MOUSE_RIGHT, BUTTON_PRESSED ); g_pInput->InternalMousePressed(MOUSE_RIGHT); break; } case WM_MBUTTONDOWN: { PostCursorMoved( hwnd, lparam ); g_pInput->SetMouseCodeState( MOUSE_MIDDLE, BUTTON_PRESSED ); g_pInput->InternalMousePressed(MOUSE_MIDDLE); break; } case WM_LBUTTONDBLCLK: { PostCursorMoved( hwnd, lparam ); g_pInput->SetMouseCodeState( MOUSE_LEFT, BUTTON_DOUBLECLICKED ); g_pInput->InternalMouseDoublePressed( MOUSE_LEFT ); break; } case WM_RBUTTONDBLCLK: { PostCursorMoved( hwnd, lparam ); g_pInput->SetMouseCodeState( MOUSE_RIGHT, BUTTON_DOUBLECLICKED ); g_pInput->InternalMouseDoublePressed( MOUSE_RIGHT ); break; } case WM_MBUTTONDBLCLK: { PostCursorMoved( hwnd, lparam ); g_pInput->SetMouseCodeState( MOUSE_MIDDLE, BUTTON_DOUBLECLICKED ); g_pInput->InternalMouseDoublePressed( MOUSE_MIDDLE ); break; } case WM_LBUTTONUP: { PostCursorMoved( hwnd, lparam ); g_pInput->SetMouseCodeState( MOUSE_LEFT, BUTTON_RELEASED ); g_pInput->InternalMouseReleased( MOUSE_LEFT ); break; } case WM_RBUTTONUP: { PostCursorMoved( hwnd, lparam ); g_pInput->SetMouseCodeState( MOUSE_RIGHT, BUTTON_RELEASED ); g_pInput->InternalMouseReleased( MOUSE_RIGHT ); break; } case WM_MBUTTONUP: { PostCursorMoved( hwnd, lparam ); g_pInput->SetMouseCodeState( MOUSE_MIDDLE, BUTTON_RELEASED ); g_pInput->InternalMouseReleased( MOUSE_MIDDLE ); break; } case WM_MOUSEWHEEL: { g_pInput->InternalMouseWheeled(((short)HIWORD(wparam))/WHEEL_DELTA); break; } case WM_KEYDOWN: case WM_SYSKEYDOWN: { int code = wparam; g_iPreviousKeyCode = KeyCode_VirtualKeyToVGUI( code ); bool bRepeating = ( lparam & ( 1<<30 ) ) != 0; if ( !bRepeating ) { g_pInput->SetKeyCodeState( g_iPreviousKeyCode, BUTTON_PRESSED ); g_pInput->InternalKeyCodePressed( g_iPreviousKeyCode ); } g_pInput->InternalKeyCodeTyped( g_iPreviousKeyCode ); // Deal with toggles if ( !bRepeating ) { if ( code == VK_CAPITAL || code == VK_SCROLL || code == VK_NUMLOCK ) { ButtonCode_t toggleCode; switch( code ) { default: case VK_CAPITAL: toggleCode = KEY_CAPSLOCKTOGGLE; break; case VK_SCROLL: toggleCode = KEY_SCROLLLOCKTOGGLE; break; case VK_NUMLOCK: toggleCode = KEY_NUMLOCKTOGGLE; break; }; SHORT wState = GetKeyState( code ); bool bToggleState = ( wState & 0x1 ) != 0; if ( bToggleState ) { g_pInput->SetKeyCodeState( toggleCode, BUTTON_PRESSED ); g_pInput->InternalKeyCodePressed( toggleCode ); } else { g_pInput->SetKeyCodeState( toggleCode, BUTTON_RELEASED ); g_pInput->InternalKeyCodeReleased( toggleCode ); } } } break; } case WM_SYSCHAR: case WM_CHAR: { int unichar = wparam; g_pInput->InternalKeyTyped(unichar); break; } case WM_KEYUP: case WM_SYSKEYUP: { KeyCode code = KeyCode_VirtualKeyToVGUI( wparam ); g_pInput->SetKeyCodeState( code, BUTTON_RELEASED ); g_pInput->InternalKeyCodeReleased( code ); break; } case WM_ERASEBKGND: { //since the vgui Invalidate call does not erase the background //this will only be called when windows itselfs wants a Repaint. //this is the desired behavior because this call will for the //surface and all its children to end up being repainted, which //is what you want when windows wants you to Repaint the surface, //but not what you want when say a control wants to be painted //VPanel::Repaint will always Invalidate the surface so it will //get a WM_PAINT, but that does not necessarily mean you want //the whole surface painted //simply this means.. this call only happens when windows wants the Repaint //and WM_PAINT gets called just after this to do the real painting client->Repaint(); // vgui::g_pIVgui->DPrintf( "WM_ERASEBKGND(%X)\n", panel ); break; } case WM_PAINT: { //surface was by repainted vgui or by windows itself, do the repainting all repainting //will goes through here and nowhere else // post Paint messages to the que // panel->SolveTraverse(); // post a message to Paint the window // vgui::g_pIVgui->DPrintf( "WM_PAINT(%X)\n", panel ); // g_pInput->PostMessage( panel, new KeyValues("Paint"), NULL ); // this relies on platTick() being called BEFORE dispatchMessages(), so that painting occurs // on the same frame that the WM_PAINT message is received // double startTime, solveTime, paintTime; // OLD CODE // check that the panel is visible all the way to the root bool IsVisible=g_pIPanel->IsVisible(panel); VPANEL p= g_pIPanel->GetParent(panel); // drill down the heirachy checking that everything is visible while(p && IsVisible) { if( g_pIPanel->IsVisible(p)==false) { IsVisible=false; break; } p=g_pIPanel->GetParent(p); } if( IsVisible ) { PAINTSTRUCT ps; ::BeginPaint(hwnd,&ps); // startTime = system()->GetCurrentTime(); g_Surface.SolveTraverse(panel, false); // solveTime = system()->GetCurrentTime(); g_Surface.PaintTraverse(panel); // paintTime = system()->GetCurrentTime(); ::EndPaint(hwnd,&ps); } // debug timing code // ivgui()->DPrintf2("Paint timings (%.3f %.3f)\n", (float)(solveTime - startTime), (float)(paintTime - solveTime)); /* char dbtxt[200]; Q_snprintf(dbtxt,200,"paint:%p -- %p\n",hwnd,g_Surface.GetHTMLWindow(0)->GetIEHWND()); OutputDebugString( dbtxt ); */ //clear the update rectangle so it does not get another Repaint ::ValidateRect(hwnd, NULL); break; } default: { if (msg == s_uTaskbarRestart) { // notify window to re-add taskbar icons g_pIVgui->PostMessage(panel, new KeyValues("TaskbarRestart"), NULL); } break; } } if ( sendToDefWindowProc ) { return DefWindowProc(hwnd,msg,wparam,lparam); } return TRUE; }