From ceb8715d26f2fb2c717a6d6039b4b4fc33005cad Mon Sep 17 00:00:00 2001 From: Maximus5 Date: Sun, 10 Mar 2019 16:49:07 +0300 Subject: [PATCH] =?UTF-8?q?gh-1323,=20gh-1385,=20gh-1841:=20Let=20?= =?UTF-8?q?=E2=80=98Alternative=20mode=E2=80=99=20toolbar=20button=20works?= =?UTF-8?q?=20with=20console=20buffers.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ConEmu/RealBuffer.cpp | 54 +++++---------------- src/ConEmuCD/ConEmuSrv.cpp | 89 +++++++++++++++++++++-------------- src/ConEmuCD/ConEmuSrv.h | 4 +- src/ConEmuCD/ConsoleMain.cpp | 1 + src/ConEmuCD/SrvCommands.cpp | 91 ++++++++++++++++++++++++++++-------- src/ConEmuHk/Ansi.cpp | 5 ++ 6 files changed, 147 insertions(+), 97 deletions(-) diff --git a/src/ConEmu/RealBuffer.cpp b/src/ConEmu/RealBuffer.cpp index 43d723b154..2d80f4f3a0 100644 --- a/src/ConEmu/RealBuffer.cpp +++ b/src/ConEmu/RealBuffer.cpp @@ -588,52 +588,22 @@ bool CRealBuffer::LoadAlternativeConsole(LoadAltMode iMode /*= lam_Default*/) } } - if (iMode == lam_LastOutput) + if ((iMode == lam_LastOutput) || (iMode == lam_FullBuffer)) { - MFileMapping StoredOutputHdr; - MFileMapping StoredOutputItem; - - CESERVER_CONSAVE_MAPHDR* pHdr = NULL; - CESERVER_CONSAVE_MAP* pData = NULL; - CONSOLE_SCREEN_BUFFER_INFO storedSbi = {}; - DWORD cchMaxBufferSize = 0; - size_t nMaxSize = 0; - - StoredOutputHdr.InitName(CECONOUTPUTNAME, LODWORD(mp_RCon->hConWnd)); - if (!(pHdr = StoredOutputHdr.Open()) || !pHdr->sCurrentMap[0]) - { - // #AltBuffer Load alt buffer from Console Server (CECMD_GETOUTPUT / cmd_GetOutput) - DisplayLastError(L"Stored output mapping was not created!"); - goto wrap; - } - - cchMaxBufferSize = std::min(pHdr->MaxCellCount, (DWORD)(pHdr->info.dwSize.X * pHdr->info.dwSize.Y)); - - StoredOutputItem.InitName(pHdr->sCurrentMap); //-V205 - nMaxSize = sizeof(*pData) + cchMaxBufferSize * sizeof(pData->Data[0]); - if (!(pData = StoredOutputItem.Open(FALSE,nMaxSize))) - { - DisplayLastError(L"Stored output data mapping was not created!"); - goto wrap; - } - - if ((pData->hdr.nVersion != CESERVER_REQ_VER) || (pData->hdr.cbSize <= sizeof(CESERVER_CONSAVE_MAP))) - { - DisplayLastError(L"Invalid data in mapping header", -1); - goto wrap; - } - - storedSbi = pData->info; - - lbRc = LoadDataFromDump(storedSbi, pData->Data, cchMaxBufferSize); - } - else if (iMode == lam_FullBuffer) - { - CESERVER_REQ *pIn = ExecuteNewCmd(CECMD_CONSOLEFULL, sizeof(CESERVER_REQ_HDR)+sizeof(DWORD)); + CESERVER_REQ *pIn = ExecuteNewCmd(CECMD_CONSOLEFULL, sizeof(CESERVER_REQ_HDR) + 2 * sizeof(DWORD)); if (pIn) { int dynHeight = mp_RCon->mp_RBuf->GetDynamicHeight(); - pIn->dwData[0] = (dynHeight > 0) ? dynHeight : 0; + if (iMode == lam_FullBuffer) + { + pIn->dwData[0] = (dynHeight > 0) ? dynHeight : 0; + pIn->dwData[1] = FALSE; + } + else + { + pIn->dwData[0] = 0; + pIn->dwData[1] = TRUE; + } CESERVER_REQ *pOut = ExecuteSrvCmd(mp_RCon->GetServerPID(), pIn, ghWnd); if (pOut && (pOut->hdr.cbSize > sizeof(CESERVER_CONSAVE_MAP))) { diff --git a/src/ConEmuCD/ConEmuSrv.cpp b/src/ConEmuCD/ConEmuSrv.cpp index 42b929abba..43dd34c115 100644 --- a/src/ConEmuCD/ConEmuSrv.cpp +++ b/src/ConEmuCD/ConEmuSrv.cpp @@ -1097,13 +1097,6 @@ int ServerInit() // Межпроцессный семафор не помогает, оставил пока только в качестве заглушки //InitializeConsoleInputSemaphore(); - // Windows 7 has a bug which makes impossible to utilize ScreenBuffers - // https://conemu.github.io/en/MicrosoftBugs.html#CorruptedScreenBuffer - if (gpSrv->osv.dwMajorVersion == 6 && gpSrv->osv.dwMinorVersion == 1) - gpSrv->bReopenHandleAllowed = FALSE; - else - gpSrv->bReopenHandleAllowed = TRUE; - if (gnRunMode == RM_SERVER) { if (!gnConfirmExitParm) @@ -1841,7 +1834,7 @@ BOOL MyWriteConsoleOutput(HANDLE hOut, CHAR_INFO *pData, COORD& bufSize, COORD& void ConOutCloseHandle() { - if (gpSrv->bReopenHandleAllowed) + if (isReopenHandleAllowed()) { // Need to block all requests to output buffer in other threads MSectionLockSimple csRead; @@ -1852,31 +1845,27 @@ void ConOutCloseHandle() } } +// В Win7 закрытие дескриптора в ДРУГОМ процессе - закрывает консольный буфер ПОЛНОСТЬЮ!!! +// В итоге, буфер вывода telnet'а схлопывается! +bool isReopenHandleAllowed() +{ + // Windows 7 has a bug which makes impossible to utilize ScreenBuffers + // https://conemu.github.io/en/MicrosoftBugs.html#CorruptedScreenBuffer + if (IsWin7Eql()) + return false; + return true; +} + bool CmdOutputOpenMap(CONSOLE_SCREEN_BUFFER_INFO& lsbi, CESERVER_CONSAVE_MAPHDR*& pHdr, CESERVER_CONSAVE_MAP*& pData) { LogFunction(L"CmdOutputOpenMap"); - pHdr = NULL; - pData = NULL; - - // В Win7 закрытие дескриптора в ДРУГОМ процессе - закрывает консольный буфер ПОЛНОСТЬЮ!!! - // В итоге, буфер вывода telnet'а схлопывается! - if (gpSrv->bReopenHandleAllowed) - { - ConOutCloseHandle(); - } + pHdr = nullptr; + pData = nullptr; // Need to block all requests to output buffer in other threads MSectionLockSimple csRead; csRead.Lock(&gpSrv->csReadConsoleInfo, LOCK_READOUTPUT_TIMEOUT); - // !!! Нас интересует реальное положение дел в консоли, - // а не скорректированное функцией MyGetConsoleScreenBufferInfo - if (!GetConsoleScreenBufferInfo(ghConOut, &lsbi)) - { - return false; // Не смогли получить информацию о консоли... - } - - if (!gpSrv->pStoredOutputHdr) { gpSrv->pStoredOutputHdr = new MFileMapping; @@ -1900,14 +1889,12 @@ bool CmdOutputOpenMap(CONSOLE_SCREEN_BUFFER_INFO& lsbi, CESERVER_CONSAVE_MAPHDR* } } + if (!lsbi.dwSize.Y) + lsbi = pHdr->info; + WARNING("А вот это нужно бы делать в RefreshThread!!!"); DEBUGSTR(L"--- CmdOutputStore begin\n"); - //MSectionLock CS; CS.Lock(gpcsStoredOutput, FALSE); - - - - COORD crMaxSize = MyGetLargestConsoleWindowSize(ghConOut); DWORD cchOneBufferSize = lsbi.dwSize.X * lsbi.dwSize.Y; // Читаем всю консоль целиком! DWORD cchMaxBufferSize = std::max(pHdr->MaxCellCount, (lsbi.dwSize.Y * lsbi.dwSize.X)); @@ -1994,15 +1981,36 @@ bool CmdOutputOpenMap(CONSOLE_SCREEN_BUFFER_INFO& lsbi, CESERVER_CONSAVE_MAPHDR* // Сохранить данные ВСЕЙ консоли в mapping void CmdOutputStore(bool abCreateOnly /*= false*/) { + const bool reopen_allowed = isReopenHandleAllowed(); + if (reopen_allowed) + { + // Nothing to do, in this mode we utilize conhost screen buffers + return; + } + LogFunction(L"CmdOutputStore"); - CONSOLE_SCREEN_BUFFER_INFO lsbi = {{0,0}}; + CONSOLE_SCREEN_BUFFER_INFO lsbi = {}; CESERVER_CONSAVE_MAPHDR* pHdr = NULL; CESERVER_CONSAVE_MAP* pData = NULL; // Need to block all requests to output buffer in other threads MSectionLockSimple csRead; csRead.Lock(&gpSrv->csReadConsoleInfo, LOCK_READOUTPUT_TIMEOUT); + // Just in case we change the logic somehow + if (reopen_allowed) + ConOutCloseHandle(); + + // !!! Нас интересует реальное положение дел в консоли, + // а не скорректированное функцией MyGetConsoleScreenBufferInfo + if (!GetConsoleScreenBufferInfo(ghConOut, &lsbi) || !lsbi.dwSize.Y) + { + LogString("GetConsoleScreenBufferInfo failed"); + return; // Не смогли получить информацию о консоли... + } + // just for information + COORD crMaxSize = MyGetLargestConsoleWindowSize(ghConOut); + if (!CmdOutputOpenMap(lsbi, pHdr, pData)) return; @@ -2033,6 +2041,7 @@ void CmdOutputStore(bool abCreateOnly /*= false*/) LogString("CmdOutputStore finished"); DEBUGSTR(L"--- CmdOutputStore end\n"); + UNREFERENCED_PARAMETER(crMaxSize); } // abSimpleMode==true - просто восстановить экран на момент вызова CmdOutputStore @@ -2040,6 +2049,13 @@ void CmdOutputStore(bool abCreateOnly /*= false*/) // задел на будущее для выполнения команд из Far (без /w), mc, или еще кого. void CmdOutputRestore(bool abSimpleMode) { + const bool reopen_allowed = isReopenHandleAllowed(); + if (reopen_allowed) + { + // Nothing to do, in this mode we utilize conhost screen buffers + return; + } + LogFunction(L"CmdOutputRestore"); if (!abSimpleMode) @@ -2050,11 +2066,14 @@ void CmdOutputRestore(bool abSimpleMode) return; } - // Need to block all requests to output buffer in other threads MSectionLockSimple csRead; csRead.Lock(&gpSrv->csReadConsoleInfo, LOCK_READOUTPUT_TIMEOUT); - CONSOLE_SCREEN_BUFFER_INFO lsbi = {{0,0}}; + // Just in case we change the logic somehow + if (reopen_allowed) + ConOutCloseHandle(); + + CONSOLE_SCREEN_BUFFER_INFO lsbi = {}; CESERVER_CONSAVE_MAPHDR* pHdr = NULL; CESERVER_CONSAVE_MAP* pData = NULL; if (!CmdOutputOpenMap(lsbi, pHdr, pData)) @@ -4501,7 +4520,7 @@ bool FreezeRefreshThread() _ASSERTE(GetCurrentThreadId() != gpSrv->dwRefreshThread); return false; } - + gpSrv->nRefreshFreezeRequests++; ResetEvent(gpSrv->hFreezeRefreshThread); @@ -4738,7 +4757,7 @@ DWORD WINAPI RefreshThread(LPVOID lpvParam) // Always update con handle, мягкий вариант // !!! В Win7 закрытие дескриптора в ДРУГОМ процессе - закрывает консольный буфер ПОЛНОСТЬЮ. В итоге, буфер вывода telnet'а схлопывается! !!! // 120507 - Если крутится альт.сервер - то игнорировать - if (gpSrv->bReopenHandleAllowed + if (isReopenHandleAllowed() && !nAltWait && ((GetTickCount() - nLastConHandleTick) > UPDATECONHANDLE_TIMEOUT)) { diff --git a/src/ConEmuCD/ConEmuSrv.h b/src/ConEmuCD/ConEmuSrv.h index f4968021de..41d996fffc 100644 --- a/src/ConEmuCD/ConEmuSrv.h +++ b/src/ConEmuCD/ConEmuSrv.h @@ -407,7 +407,10 @@ bool IsAutoAttachAllowed(); //extern MConHandle ghConIn; extern MConHandle ghConOut; extern MConHandle gPrimaryBuffer, gAltBuffer; +extern USHORT gnPrimaryBufferLastRow; void ConOutCloseHandle(); +bool CmdOutputOpenMap(CONSOLE_SCREEN_BUFFER_INFO& lsbi, CESERVER_CONSAVE_MAPHDR*& pHdr, CESERVER_CONSAVE_MAP*& pData); +bool isReopenHandleAllowed(); typedef enum tag_RunMode @@ -526,7 +529,6 @@ struct SrvInfo bool bServerForcedTermination; // OSVERSIONINFO osv; - BOOL bReopenHandleAllowed; UINT nMaxFPS; // MSection *csAltSrv; diff --git a/src/ConEmuCD/ConsoleMain.cpp b/src/ConEmuCD/ConsoleMain.cpp index a28e872af7..74e023fe1e 100644 --- a/src/ConEmuCD/ConsoleMain.cpp +++ b/src/ConEmuCD/ConsoleMain.cpp @@ -156,6 +156,7 @@ FGetConsoleDisplayMode pfnGetConsoleDisplayMode = NULL; MConHandle ghConOut(L"CONOUT$"); //Used to store and restore console screen buffers in cmd_AltBuffer MConHandle gPrimaryBuffer(NULL), gAltBuffer(NULL); +USHORT gnPrimaryBufferLastRow = 0; // last detected written row in gPrimaryBuffer // Время ожидания завершения консольных процессов, когда юзер нажал крестик в КОНСОЛЬНОМ окне // The system also displays this dialog box if the process does not respond within a certain time-out period diff --git a/src/ConEmuCD/SrvCommands.cpp b/src/ConEmuCD/SrvCommands.cpp index 6369a75bed..8ad8c4a2fa 100644 --- a/src/ConEmuCD/SrvCommands.cpp +++ b/src/ConEmuCD/SrvCommands.cpp @@ -524,6 +524,7 @@ BOOL cmd_SetSizeXXX_CmdStartedFinished(CESERVER_REQ& in, CESERVER_REQ** out) if (in.hdr.nCmd == CECMD_CMDSTARTED) { // Восстановить текст скрытой (прокрученной вверх) части консоли + // #LongConsoleOutput This does not work CmdOutputRestore(false); } } @@ -1749,33 +1750,24 @@ BOOL cmd_FreezeAltServer(CESERVER_REQ& in, CESERVER_REQ** out) return TRUE; } -BOOL cmd_LoadFullConsoleData(CESERVER_REQ& in, CESERVER_REQ** out) +namespace { +// hOutput - our console output handle +// max_height - 0 for unlimited, >0 for detected dynamic height +BOOL LoadFullConsoleData(HANDLE hOutput, WORD max_height, CESERVER_REQ** out) { BOOL lbRc = FALSE; - //DWORD nPrevAltServer = 0; - - // В Win7 закрытие дескриптора в ДРУГОМ процессе - закрывает консольный буфер ПОЛНОСТЬЮ!!! - // В итоге, буфер вывода telnet'а схлопывается! - if (gpSrv->bReopenHandleAllowed) - { - ConOutCloseHandle(); - } - - // Need to block all requests to output buffer in other threads - MSectionLockSimple csRead; csRead.Lock(&gpSrv->csReadConsoleInfo, LOCK_READOUTPUT_TIMEOUT); CONSOLE_SCREEN_BUFFER_INFO lsbi = {{0,0}}; // !!! Нас интересует реальное положение дел в консоли, // а не скорректированное функцией MyGetConsoleScreenBufferInfo - if (!GetConsoleScreenBufferInfo(ghConOut, &lsbi)) + if (!GetConsoleScreenBufferInfo(hOutput, &lsbi)) { return FALSE; // Не смогли получить информацию о консоли... } // Support dynamic height - do not load all 32K lines - if (in.DataSize() >= sizeof(DWORD)) + if (max_height && (static_cast(lsbi.dwSize.Y) > max_height)) { - if (static_cast(lsbi.dwSize.Y) > in.dwData[0]) - lsbi.dwSize.Y = LOWORD(in.dwData[0]); + lsbi.dwSize.Y = max_height; } CESERVER_CONSAVE_MAP* pData = NULL; @@ -1788,7 +1780,7 @@ BOOL cmd_LoadFullConsoleData(CESERVER_REQ& in, CESERVER_REQ** out) COORD BufSize = {lsbi.dwSize.X, lsbi.dwSize.Y}; SMALL_RECT ReadRect = {0, 0, lsbi.dwSize.X-1, lsbi.dwSize.Y-1}; - lbRc = MyReadConsoleOutput(ghConOut, pData->Data, BufSize, ReadRect); + lbRc = MyReadConsoleOutput(hOutput, pData->Data, BufSize, ReadRect); if (lbRc) { @@ -1800,7 +1792,7 @@ BOOL cmd_LoadFullConsoleData(CESERVER_REQ& in, CESERVER_REQ** out) // Еще раз считать информацию по консоли (курсор положение и прочее...) // За время чтения данных - они могли прокрутиться вверх CONSOLE_SCREEN_BUFFER_INFO lsbi2 = {{0,0}}; - if (GetConsoleScreenBufferInfo(ghConOut, &lsbi2)) + if (GetConsoleScreenBufferInfo(hOutput, &lsbi2)) { // Обновим только курсор, а то юзер может получить черный экран, вместо ожидаемого текста // Если во время "dir c:\ /s" запросить AltConsole - получаем черный экран. @@ -1815,6 +1807,65 @@ BOOL cmd_LoadFullConsoleData(CESERVER_REQ& in, CESERVER_REQ** out) return lbRc; } +BOOL LoadConsoleMapData(CESERVER_REQ** out) +{ + BOOL lbRc = FALSE; + CONSOLE_SCREEN_BUFFER_INFO lsbi = {{0,0}}; + CESERVER_CONSAVE_MAPHDR* pMapHdr = NULL; + CESERVER_CONSAVE_MAP* pMapData = NULL; + if (!CmdOutputOpenMap(lsbi, pMapHdr, pMapData)) + return FALSE; + // #AltBuffer try to detect max used row + const size_t max_cells = std::min(lsbi.dwSize.X * lsbi.dwSize.Y, pMapData->MaxCellCount); + const size_t max_cells_cb = (max_cells * sizeof(pMapData->Data[0])); + const size_t cbReplySize = sizeof(CESERVER_CONSAVE_MAP) + max_cells_cb; + *out = ExecuteNewCmd(CECMD_CONSOLEFULL, cbReplySize); + + if ((*out) != NULL) + { + auto pData = (CESERVER_CONSAVE_MAP*)*out; + pData->info = lsbi; + pData->Succeeded = lbRc; + pData->MaxCellCount = lsbi.dwSize.X * lsbi.dwSize.Y; + pData->CurrentIndex = pMapData->CurrentIndex; + memmove_s(pData->Data, max_cells_cb, pMapData->Data, max_cells_cb); + lbRc = TRUE; + } + return lbRc; +} + +BOOL LoadFullConsoleDataReal(CESERVER_REQ& in, CESERVER_REQ** out) +{ + BOOL lbRc = FALSE; + + // В Win7 закрытие дескриптора в ДРУГОМ процессе - закрывает консольный буфер ПОЛНОСТЬЮ!!! + // В итоге, буфер вывода telnet'а схлопывается! + if (isReopenHandleAllowed()) + { + ConOutCloseHandle(); + } + + return LoadFullConsoleData(ghConOut, (in.DataSize() >= sizeof(DWORD)) ? LOWORD(in.dwData[0]) : 0, out); +} +} // namespace + +BOOL cmd_LoadFullConsoleData(CESERVER_REQ& in, CESERVER_REQ** out) +{ + // Need to block all requests to output buffer in other threads + MSectionLockSimple csRead; csRead.Lock(&gpSrv->csReadConsoleInfo, LOCK_READOUTPUT_TIMEOUT); + + BOOL alt_screen = (in.DataSize() >= 2 * sizeof(DWORD)) ? in.dwData[1] : FALSE; + if (!alt_screen) + return LoadFullConsoleDataReal(in, out); + else if (gPrimaryBuffer.HasHandle()) + return LoadFullConsoleData(gPrimaryBuffer, gnPrimaryBufferLastRow, out); + else if (!isReopenHandleAllowed()) + return LoadConsoleMapData(out); + + _ASSERTE(FALSE && "Unsupported retrieve mode!"); + return FALSE; +} + BOOL cmd_SetFullScreen(CESERVER_REQ& in, CESERVER_REQ** out) { BOOL lbRc = FALSE; @@ -2095,7 +2146,7 @@ BOOL cmd_AltBuffer(CESERVER_REQ& in, CESERVER_REQ** out) }; // In Windows 7 we have to use legacy mode - if (!gpSrv->bReopenHandleAllowed) + if (!isReopenHandleAllowed()) { if (in.AltBuf.AbFlags & abf_SaveContents) CmdOutputStore(); @@ -2125,6 +2176,7 @@ BOOL cmd_AltBuffer(CESERVER_REQ& in, CESERVER_REQ** out) ghConOut.SetHandlePtr(gAltBuffer); if ((lbRc = do_resize_buffer())) { + gnPrimaryBufferLastRow = in.AltBuf.BufferHeight; alt_buffer_set = true; } else @@ -2148,6 +2200,7 @@ BOOL cmd_AltBuffer(CESERVER_REQ& in, CESERVER_REQ** out) gPrimaryBuffer.Close(); gAltBuffer.Close(); alt_buffer_set = false; + gnPrimaryBufferLastRow = 0; lbRc = do_resize_buffer(); } } diff --git a/src/ConEmuHk/Ansi.cpp b/src/ConEmuHk/Ansi.cpp index 27bded06f9..3b73e0dbd2 100644 --- a/src/ConEmuHk/Ansi.cpp +++ b/src/ConEmuHk/Ansi.cpp @@ -4055,6 +4055,11 @@ HANDLE CEAnsi::XTermAltBuffer(bool bSetAltBuffer) if (pIn) { pIn->AltBuf.AbFlags = abf_BufferOff|abf_SaveContents; + // support "virtual" dynamic console buffer height + if (CESERVER_CONSOLE_APP_MAPPING* pAppMap = gpAppMap ? gpAppMap->Ptr() : NULL) + pIn->AltBuf.BufferHeight = std::max(pAppMap->nLastConsoleRow, csbi1.srWindow.Bottom); + else + pIn->AltBuf.BufferHeight = csbi1.srWindow.Bottom; pOut = ExecuteSrvCmd(gnServerPID, pIn, ghConWnd); if (pOut) {