// Helper function: support Tab-completion in cmd.exe and others COORD FixupReadStartCursorPos(DWORD nNumberOfCharsToRead, CONSOLE_SCREEN_BUFFER_INFO& csbi, const MY_CONSOLE_READCONSOLE_CONTROL* pInputControl) { COORD crStartCursorPos = csbi.dwCursorPosition; if (pInputControl && (pInputControl->nLength >= sizeof(*pInputControl))) { if (pInputControl->nInitialChars < nNumberOfCharsToRead) { // Part of input is already "printed" to the buffer, take it into account int nNewX = (int)crStartCursorPos.X - pInputControl->nInitialChars; int nNewY = (int)crStartCursorPos.Y; if (nNewX < 0) { int nRows = klMin(nNewY, (int)((-nNewX) / csbi.dwSize.X)); if ((nRows < nNewY) && ((nNewX + (nRows * csbi.dwSize.X)) < 0)) nRows++; _ASSERTE(((nNewY - nRows) >= 0) && ((nNewX + (nRows * csbi.dwSize.X)) >= 0)); nNewX += (nRows * csbi.dwSize.X); nNewY -= nRows; } if (nNewX >= 0 && nNewY >= 0) { crStartCursorPos = MakeCoord(nNewX, nNewY); csbi.dwCursorPosition = crStartCursorPos; } else { _ASSERTE(nNewX >= 0 && nNewY >= 0); } } } return crStartCursorPos; }
bool CBackground::CreateField(int anWidth, int anHeight) { if (!hBgDc || !hBgBitmap || anWidth != bgSize.X || anHeight != bgSize.Y) { Destroy(); } else { return true; // уже создано } bool lbRc = false; // Создать MemoryDC const HDC hScreenDC = GetDC(ghWnd); hBgDc = CreateCompatibleDC(hScreenDC); if (hBgDc) { bgSize.X = klMin(32767,anWidth); bgSize.Y = klMin(32767,anHeight); hBgBitmap = CreateCompatibleBitmap(hScreenDC, bgSize.X, bgSize.Y); if (hBgBitmap) { // Выбрать в MemoryDC созданный Bitmap для установки его размера hOldBitmap = (HBITMAP)SelectObject(hBgDc, hBgBitmap); // И залить черным фоном RECT rcFull = MakeRect(bgSize.X, bgSize.Y); FillRect(hBgDc, &rcFull, (HBRUSH)GetStockObject(BLACK_BRUSH)); // теперь - OK lbRc = true; } } ReleaseDC(ghWnd, hScreenDC); return lbRc; }
bool CBackground::FillBackground( const BITMAPFILEHEADER* apBkImgData, // Содержимое *.bmp файла LONG X, LONG Y, LONG Width, LONG Height, // Куда нужно положить картинку BackgroundOp Operation, // {eUpLeft = 0, eStretch = 1, eTile = 2, eUpRight = 4, ...} bool abFade) // затемнение картинки, когда ConEmu НЕ в фокусе { if (!hBgDc) return false; // Залить черным фоном RECT rcFull = MakeRect(X,Y,Width,Height); FillRect(hBgDc, &rcFull, (HBRUSH)GetStockObject(BLACK_BRUSH)); if (apBkImgData == NULL || apBkImgData->bfType != 0x4D42/*BM*/ || IsBadReadPtr(apBkImgData, apBkImgData->bfSize)) { return false; } bool lbRc = false; HDC hLoadDC = NULL; HBITMAP hLoadBmp = NULL; BITMAPINFO* pBmp = (BITMAPINFO*)(apBkImgData+1); LPBYTE pBits = ((LPBYTE)apBkImgData) + apBkImgData->bfOffBits; LPVOID pDstBits = NULL; BITMAPINFOHEADER* pHdr = &pBmp->bmiHeader; if (pHdr->biPlanes != 1 || pHdr->biCompression != BI_RGB) // BI_JPEG|BI_PNG { return false; } DWORD nBitSize = apBkImgData->bfSize - apBkImgData->bfOffBits; TODO("Stride?"); DWORD nCalcSize = (pHdr->biWidth * pHdr->biHeight * pHdr->biBitCount) >> 3; if (nBitSize > nCalcSize) nBitSize = nCalcSize; if (!gpSet->isFadeInactive) abFade = false; // Создать MemoryDC const HDC hScreenDC = GetDC(ghWnd); if (hScreenDC) { hLoadDC = CreateCompatibleDC(hScreenDC); ReleaseDC(ghWnd, hScreenDC); if (hLoadDC) { hLoadBmp = CreateDIBSection(hLoadDC, pBmp, DIB_RGB_COLORS, &pDstBits, NULL, 0); if (hLoadBmp && pDstBits) { // Поместить биты из apBkImgData в hLoadDC HBITMAP hOldLoadBmp = (HBITMAP)SelectObject(hLoadDC, hLoadBmp); memmove(pDstBits, pBits, nBitSize); GdiFlush(); // Гарантировать commit битов // Теперь - скопировать биты из hLoadDC в hBgDc с учетом положения и Operation BLENDFUNCTION bf = {AC_SRC_OVER, 0, gpSet->bgImageDarker, 0}; if (abFade) { // GetFadeColor возвращает ColorRef, поэтому при вызове для (0..255) // он должен вернуть "коэффициент" затемнения или осветления DWORD nHigh = (gpSet->GetFadeColor(255) & 0xFF); if (nHigh < 255) { // Затемнение фона bf.SourceConstantAlpha = LOBYTE(nHigh * bf.SourceConstantAlpha / 255); } //// "коэффициент" вернется в виде RGB (R==G==B) //DWORD nLow = gpSet->GetFadeColor(0); //if (nLow > 0 && ((nLow & 0xFF) < nHigh)) //{ // // Осветление фона // RECT r = {X,Y,X+Width,Y+Height}; // HBRUSH h = CreateSolidBrush(nLow); // FillRect(hBgDc, &r, h); // DeleteObject(h); // // еще нужно убедиться, что сама картинка будет немного прозрачной, // // чтобы это осветление было заметно // if ((nLow & 0xFF) < 200) // bf.SourceConstantAlpha = klMin((int)bf.SourceConstantAlpha, (int)(255 - (nLow & 0xFF))); // else if (bf.SourceConstantAlpha >= 240) // bf.SourceConstantAlpha = 240; //} } if ((Operation == eUpLeft) || (Operation == eUpRight) || (Operation == eDownLeft) || (Operation == eDownRight) || (Operation == eCenter)) { int W = klMin(Width,pHdr->biWidth); int H = klMin(Height,pHdr->biHeight); if (GdiAlphaBlend(hBgDc, X, Y, W, H, hLoadDC, 0, 0, W, H, bf)) lbRc = true; } else if (Operation == eStretch || Operation == eFit) { if (GdiAlphaBlend(hBgDc, X, Y, Width, Height, hLoadDC, 0, 0, pHdr->biWidth, pHdr->biHeight, bf)) lbRc = true; } else if (Operation == eFill) { int srcX = 0, srcY = 0, srcW = pHdr->biWidth, srcH = pHdr->biHeight; if (Width && Width > Height) { srcH = klMin((srcW * Height / Width), _abs(pHdr->biHeight)); srcY = (pHdr->biHeight - srcH) / 2; } else if (Height) { srcW = klMin((srcH * Width / Height), pHdr->biWidth); srcX = (pHdr->biWidth - srcW) / 2; } if (GdiAlphaBlend(hBgDc, X, Y, Width, Height, hLoadDC, srcX, srcY, srcW, srcH, bf)) lbRc = true; } else if (Operation == eTile) { for (int DY = Y; DY < (Y+Height); DY += pHdr->biHeight) { for (int DX = X; DX < (X+Width); DX += pHdr->biWidth) { int W = klMin((Width-DX),pHdr->biWidth); int H = klMin((Height-DY),pHdr->biHeight); if (GdiAlphaBlend(hBgDc, DX, DY, W, H, hLoadDC, 0, 0, W, H, bf)) lbRc = true; } } } _ASSERTE(lbRc && "GdiAlphaBlend failed in background creation?"); TODO("Осветление картинки в Fade, когда gpSet->mn_FadeLow>0"); //if (abFade) //{ // // "коэффициент" вернется в виде RGB (R==G==B) // DWORD nLow = gpSet->GetFadeColor(0); // if (nLow) // { // // Осветление фона // RECT r = {X,Y,X+Width,Y+Height}; // HBRUSH h = CreateSolidBrush(nLow); // // Осветлить картинку // //FillRect(hBgDc, &r, h); // DeleteObject(h); // } //} SelectObject(hLoadDC, hOldLoadBmp); } if (hLoadBmp) { DeleteObject(hLoadBmp); hLoadBmp = NULL; } DeleteDC(hLoadDC); hLoadDC = NULL; } } return lbRc; }
// returns count of *lines* were copied to pChar/pAttr // nWidth & nHeight - dimension which VCon want to display UINT CRConData::GetConsoleData(wchar_t* rpChar, CharAttr* rpAttr, UINT anWidth, UINT anHeight, wchar_t wSetChar, CharAttr lcaDef, CharAttr *lcaTable, CharAttr *lcaTableExt, bool bFade, bool bExtendColors, BYTE nExtendColorIdx, bool bExtendFonts) { TODO("Во время ресайза консоль может подглючивать - отдает не то что нужно..."); //_ASSERTE(*con.pConChar!=ucBoxDblVert); UINT nYMax = klMin(anHeight,nHeight); MFileMapping<AnnotationHeader>& TrueMap = mp_RCon->m_TrueColorerMap; const AnnotationInfo *pTrueData = mp_RCon->mp_TrueColorerData; wchar_t *pszDst = rpChar; CharAttr *pcaDst = rpAttr; wchar_t *pszSrc = pConChar; WORD *pnSrc = pConAttr; bool lbIsFar = mp_RCon->isFar(); // Т.к. есть блокировка (csData), то con.pConChar и con.pConAttr // не должны меняться во время выполнения этой функции const wchar_t* const pszSrcStart = pConChar; WORD* const pnSrcStart = pConAttr; size_t nSrcCells = (nWidth * nHeight); Assert(pszSrcStart==pszSrc && pnSrcStart==pnSrc); const AnnotationInfo *pcolSrc = NULL; const AnnotationInfo *pcolEnd = NULL; BOOL bUseColorData = FALSE, bStartUseColorData = FALSE; if (gpSet->isTrueColorer && TrueMap.IsValid() && pTrueData) { pcolSrc = pTrueData; pcolEnd = pTrueData + TrueMap.Ptr()->bufferSize; bUseColorData = TRUE; WARNING("Если far/w - pcolSrc нужно поднять вверх, bStartUseColorData=TRUE, bUseColorData=FALSE"); if (m_sbi.dwSize.Y > (int)nHeight) // con.bBufferHeight { int lnShiftRows = (m_sbi.dwSize.Y - anHeight) - m_sbi.srWindow.Top; if (lnShiftRows > 0) { #ifdef _DEBUG if (nWidth != (m_sbi.srWindow.Right - m_sbi.srWindow.Left + 1)) { _ASSERTE(nWidth == (m_sbi.srWindow.Right - m_sbi.srWindow.Left + 1)); } #endif pcolSrc -= (lnShiftRows * nWidth); DEBUGSTRTRUEMOD(L"TrueMod skipped due to nShiftRows, bStartUseColorData was set"); bUseColorData = FALSE; bStartUseColorData = TRUE; } #ifdef _DEBUG else if (lnShiftRows < 0) { //_ASSERTE(nShiftRows>=0); wchar_t szLog[200]; _wsprintf(szLog, SKIPCOUNT(szLog) L"!!! CRealBuffer::GetConsoleData !!! " L"nShiftRows=%i nWidth=%i nHeight=%i Rect={%i,%i}-{%i,%i} Buf={%i,%i}", lnShiftRows, anWidth, anHeight, m_sbi.srWindow.Left, m_sbi.srWindow.Top, m_sbi.srWindow.Right, m_sbi.srWindow.Bottom, m_sbi.dwSize.X, m_sbi.dwSize.Y); mp_RCon->LogString(szLog); } #endif } } else { DEBUGSTRTRUEMOD(L"TrueMod is not allowed here"); } DWORD cbDstLineSize = anWidth * 2; DWORD cnSrcLineLen = nWidth; DWORD cbSrcLineSize = cnSrcLineLen * 2; #ifdef _DEBUG if (nWidth != m_sbi.dwSize.X) { _ASSERTE(nWidth == m_sbi.dwSize.X); // Scrolling? } #endif DWORD cbLineSize = min(cbDstLineSize,cbSrcLineSize); int nCharsLeft = (anWidth > nWidth) ? (anWidth - nWidth) : 0; //int nY, nX; //120331 - Нехорошо заменять на "черный" с самого начала BYTE attrBackLast = 0; UINT nExtendStartsY = 0; //int nPrevDlgBorder = -1; bool lbStoreBackLast = false; if (bExtendColors) { BYTE FirstBackAttr = lcaTable[(*pnSrc) & 0xFF].nBackIdx; if (FirstBackAttr != nExtendColorIdx) attrBackLast = FirstBackAttr; const CEFAR_INFO_MAPPING* pFarInfo = lbIsFar ? mp_RCon->GetFarInfo() : NULL; if (pFarInfo) { // Если в качестве цвета "расширения" выбран цвет панелей - значит // пользователь просто настроил "другую" палитру для панелей фара. // К сожалению, таким образом нельзя заменить только цвета для элемента под курсором. if (CONBACKCOLOR(pFarInfo->nFarColors[col_PanelText]) != nExtendColorIdx) lbStoreBackLast = true; else attrBackLast = FirstBackAttr; if (pFarInfo->FarInterfaceSettings.AlwaysShowMenuBar || mp_RCon->isEditor() || mp_RCon->isViewer()) nExtendStartsY = 1; // пропустить обработку строки меню } else { lbStoreBackLast = true; } } // Собственно данные for (UINT nY = 0; nY < nYMax; nY++) { if (nY == nExtendStartsY) lcaTable = lcaTableExt; // Текст memmove(pszDst, pszSrc, cbLineSize); if (nCharsLeft > 0) wmemset(pszDst+cnSrcLineLen, wSetChar, nCharsLeft); // Console text colors (Fg,Bg) BYTE PalIndex = MAKECONCOLOR(7,0); // While console is in recreate (shutdown console, startup new root) // it's shown using monochrome (gray on black) bool bForceMono = (mp_RCon->mn_InRecreate != 0); int iTail = cnSrcLineLen; wchar_t* pch = pszDst; for (UINT nX = 0; // Don't forget to advance same pointers at the and if bPair iTail-- > 0; nX++, pnSrc++, pcolSrc++, pch++) { CharAttr& lca = pcaDst[nX]; bool hasTrueColor = false; bool hasFont = false; // If not "mono" we need only lower byte with color indexes if (!bForceMono) { if (((*pnSrc) & COMMON_LVB_REVERSE_VIDEO) && (((*pnSrc) & CHANGED_CONATTR) == COMMON_LVB_REVERSE_VIDEO)) PalIndex = MAKECONCOLOR(CONBACKCOLOR(*pnSrc), CONFORECOLOR(*pnSrc)); // Inverse else PalIndex = ((*pnSrc) & 0xFF); } TODO("OPTIMIZE: lca = lcaTable[PalIndex];"); lca = lcaTable[PalIndex]; TODO("OPTIMIZE: вынести проверку bExtendColors за циклы"); bool bPair = (iTail > 0); ucs32 wwch = ucs32_from_wchar(pch, bPair); _ASSERTE(wwch >= 0); // Colorer & Far - TrueMod TODO("OPTIMIZE: вынести проверку bUseColorData за циклы"); if (bStartUseColorData && !bUseColorData) { // В случае "far /w" буфер цвета может начаться НИЖЕ верхней видимой границы, // если буфер немного прокручен вверх if (pcolSrc >= mp_RCon->mp_TrueColorerData) { DEBUGSTRTRUEMOD(L"TrueMod forced back due bStartUseColorData"); bUseColorData = TRUE; } } if (bUseColorData) { if (pcolSrc >= pcolEnd) { DEBUGSTRTRUEMOD(L"TrueMod stopped - out of buffer"); bUseColorData = FALSE; } else { TODO("OPTIMIZE: доступ к битовым полям тяжело идет..."); if (pcolSrc->fg_valid) { hasTrueColor = true; hasFont = true; lca.nFontIndex = fnt_Normal; //bold/italic/underline will be set below lca.crForeColor = bFade ? gpSet->GetFadeColor(pcolSrc->fg_color) : pcolSrc->fg_color; if (pcolSrc->bk_valid) lca.crBackColor = bFade ? gpSet->GetFadeColor(pcolSrc->bk_color) : pcolSrc->bk_color; } else if (pcolSrc->bk_valid) { hasTrueColor = true; hasFont = true; lca.nFontIndex = fnt_Normal; //bold/italic/underline will be set below lca.crBackColor = bFade ? gpSet->GetFadeColor(pcolSrc->bk_color) : pcolSrc->bk_color; } // nFontIndex: 0 - normal, 1 - bold, 2 - italic, 3 - bold&italic,..., 4 - underline, ... if (pcolSrc->style) { hasFont = true; lca.nFontIndex = pcolSrc->style & fnt_StdFontMask; } } } if (!hasFont && ((*pnSrc) & COMMON_LVB_UNDERSCORE) && ((nX >= ROWID_USED_CELLS) || !((*pnSrc) & (CHANGED_CONATTR & ~COMMON_LVB_UNDERSCORE))) ) { lca.nFontIndex = fnt_Underline; } if (!hasTrueColor && bExtendColors && (nY >= nExtendStartsY)) { if (lca.nBackIdx == nExtendColorIdx) { // Have to change the color to adjacent(?) cell lca.nBackIdx = attrBackLast; // For the background nExtendColorIdx we use upper // palette range for text: 16..31 instead of 0..15 lca.nForeIdx += 0x10; lca.crForeColor = lca.crOrigForeColor = mp_RCon->mp_Palette->m_Colors[lca.nForeIdx]; lca.crBackColor = lca.crOrigBackColor = mp_RCon->mp_Palette->m_Colors[lca.nBackIdx]; } else if (lbStoreBackLast) { // Remember last "normal" background attrBackLast = lca.nBackIdx; } } switch (get_wcwidth(wwch)) { case 0: lca.Flags2 |= CharAttr2_Combining; break; case 2: lca.Flags2 |= CharAttr2_DoubleSpaced; break; } if (bPair) { lca.Flags2 |= CharAttr2_Surrogate; CharAttr& lca2 = pcaDst[nX+1]; lca2 = lca; lca2.Flags2 = (lca.Flags2 & ~(CharAttr2_Combining)) | CharAttr2_NonSpacing; // advance +1 character nX++; pnSrc++; pcolSrc++; pch++; iTail--; } } // Залить остаток (если запрошен больший участок, чем есть консоль for (UINT nX = cnSrcLineLen; nX < anWidth; nX++) { pcaDst[nX] = lcaDef; } // Far2 показывает красный 'A' в правом нижнем углу консоли // Этот ярко красный цвет фона может попасть в Extend Font Colors if (bExtendFonts && ((nY+1) == nYMax) && lbIsFar && (pszDst[anWidth-1] == L'A') && (PalIndex == 0xCF)) { // Вернуть "родной" цвет и шрифт pcaDst[anWidth-1] = lcaTable[PalIndex]; } // Next line pszDst += anWidth; pcaDst += anWidth; pszSrc += cnSrcLineLen; } #ifndef __GNUC__ UNREFERENCED_PARAMETER(pszSrcStart); UNREFERENCED_PARAMETER(pnSrcStart); #endif UNREFERENCED_PARAMETER(nSrcCells); return nYMax; }