1 // TiMidity++ Win32 GUI New Console
2 // Copyright (c) 2018 Starg <https://osdn.net/projects/timidity41>
10 #endif /* HAVE_CONFIG_H */
21 #include "w32g_new_console.h"
36 #include <string_view>
50 namespace TimW32gNewConsole
53 LPCTSTR pClassName = _T("TimW32gNewConsole");
55 // Campbell color theme
56 // https://github.com/Microsoft/console/blob/master/tools/ColorTool/schemes/campbell.ini
57 const COLORREF BackgroundColor = RGB(12, 12, 12);
58 const COLORREF NormalColor = RGB(204, 204, 204);
59 const COLORREF ErrorColor = RGB(231, 72, 86);
60 const COLORREF WarningColor = RGB(249, 241, 165);
61 const COLORREF InfoColor = RGB(58, 150, 221);
63 using TString = std::basic_string<TCHAR>;
64 using TStringView = std::basic_string_view<TCHAR>;
66 bool CopyTextToClipboard(TStringView text)
70 if (::OpenClipboard(nullptr))
72 HGLOBAL hGlobal = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (text.size() + 1) * sizeof(TCHAR));
76 auto p = reinterpret_cast<LPTSTR>(::GlobalLock(hGlobal));
77 text.copy(p, text.size());
78 p[text.size()] = _T('\0');
79 ::GlobalUnlock(hGlobal);
84 UINT format = CF_UNICODETEXT;
86 UINT format = CF_TEXT;
89 if (::SetClipboardData(format, hGlobal))
95 ::GlobalFree(hGlobal);
109 UniqueLock() : m_pLock(nullptr)
113 explicit UniqueLock(T& lock) : m_pLock(&lock)
115 m_pLock->DoLockUnique();
118 UniqueLock(const UniqueLock&) = delete;
119 UniqueLock& operator=(const UniqueLock&) = delete;
121 UniqueLock(UniqueLock&& rhs) noexcept : m_pLock()
126 UniqueLock& operator=(UniqueLock&& rhs) noexcept
128 UniqueLock(std::move(rhs)).swap(*this);
137 void swap(UniqueLock& rhs) noexcept
140 swap(m_pLock, rhs.m_pLock);
147 m_pLock->DoUnlockUnique();
160 SharedLock() : m_pLock(nullptr)
164 explicit SharedLock(T& lock) : m_pLock(&lock)
166 m_pLock->DoLockShared();
169 SharedLock(const SharedLock&) = delete;
170 SharedLock& operator=(const SharedLock&) = delete;
172 SharedLock(SharedLock&& rhs) noexcept : m_pLock()
177 SharedLock& operator=(SharedLock&& rhs) noexcept
179 SharedLock(std::move(rhs)).swap(*this);
188 void swap(SharedLock& rhs) noexcept
191 swap(m_pLock, rhs.m_pLock);
198 m_pLock->DoUnlockShared();
209 friend class UniqueLock<SRWLock>;
210 friend class SharedLock<SRWLock>;
215 ::InitializeSRWLock(&m_Lock);
218 SRWLock(const SRWLock&) = delete;
219 SRWLock& operator=(const SRWLock&) = delete;
220 SRWLock(SRWLock&&) = delete;
221 SRWLock& operator=(SRWLock&&) = delete;
223 ~SRWLock() = default;
225 UniqueLock<SRWLock> LockUnique()
227 return UniqueLock<SRWLock>(*this);
230 SharedLock<SRWLock> LockShared()
232 return SharedLock<SRWLock>(*this);
243 ::AcquireSRWLockExclusive(&m_Lock);
246 void DoUnlockUnique()
248 ::ReleaseSRWLockExclusive(&m_Lock);
253 ::AcquireSRWLockShared(&m_Lock);
256 void DoUnlockShared()
258 ::ReleaseSRWLockShared(&m_Lock);
264 struct TextLocationInfo
270 struct StyledLineFragment
272 std::size_t Offset; // offset in std::string
279 std::size_t Offset; // offset in std::vector<StyledLineFragment>
283 class StyledTextBuffer
295 m_MaxColumnLength = 0;
298 void Append(COLORREF color, LPCTSTR pText)
300 Append(color, TStringView(pText));
303 void Append(COLORREF color, TStringView text)
305 std::size_t offset = 0;
307 while (offset < text.size())
309 // split input into lines
310 std::size_t nlOffset = text.find_first_of(_T("\r\n"), offset);
312 if (nlOffset == text.npos)
314 AppendNoNewline(color, text.substr(offset));
319 if (offset < nlOffset)
321 AppendNoNewline(color, text.substr(offset, offset - nlOffset));
326 if (text[nlOffset] == _T('\r') && nlOffset + 1 < text.size() && text[nlOffset + 1] == _T('\n'))
328 offset = nlOffset + 2;
332 offset = nlOffset + 1;
340 m_Lines.push_back({m_Fragments.size(), 0});
343 std::size_t GetLineCount() const
345 return m_Lines.size();
348 std::size_t GetLastLineNumber() const
350 std::size_t n = GetLineCount();
351 return n > 0 ? n - 1 : 0;
354 std::size_t GetMaxColumnLength() const
356 return m_MaxColumnLength;
359 std::size_t GetMaxLastColumnNumber() const
361 std::size_t n = GetMaxColumnLength();
362 return n > 0 ? n - 1 : 0;
365 std::size_t GetColumnLength(std::size_t line) const
367 return std::accumulate(
368 m_Fragments.begin() + m_Lines[line].Offset,
369 m_Fragments.begin() + m_Lines[line].Offset + m_Lines[line].Length,
371 [] (auto&& a, auto&& b)
378 std::size_t GetLastColumnNumber(std::size_t line) const
380 std::size_t n = GetColumnLength(line);
381 return n > 0 ? n - 1 : 0;
384 TStringView GetString() const
389 const std::vector<StyledLine>& GetLines() const
394 const std::vector<StyledLineFragment>& GetFragments() const
399 TStringView GetLineString(std::size_t line) const
401 const auto& lineInfo = m_Lines[line];
403 if (lineInfo.Length == 0)
408 std::size_t first = m_Fragments[lineInfo.Offset].Offset;
409 std::size_t last = lineInfo.Offset + lineInfo.Length == m_Fragments.size()
411 : m_Fragments[lineInfo.Offset + lineInfo.Length].Offset;
413 return TStringView(m_String.data() + first, last - first);
416 TString CopySubstring(TextLocationInfo start, TextLocationInfo end) const
418 TString ret(GetLineString(start.Line).substr(start.Column, start.Line < end.Line ? TStringView::npos : end.Column + 1 - start.Column));
420 for (std::size_t line = start.Line + 1; line <= end.Line; line++)
422 ret.append(_T("\r\n"));
423 ret.append(GetLineString(line).substr(0, line < end.Line ? TStringView::npos : end.Column + 1));
430 void AppendNoNewline(COLORREF color, TStringView text)
432 std::size_t stringOffset = m_String.size();
433 m_String.append(text);
435 std::size_t fragmentOffset = m_Fragments.size();
436 m_Fragments.push_back({stringOffset, text.size(), color});
440 m_Lines.push_back({fragmentOffset, 1});
444 m_Lines.back().Length++;
447 // update m_MaxColumnLength
448 m_MaxColumnLength = std::max(GetColumnLength(GetLastLineNumber()), m_MaxColumnLength);
452 std::vector<StyledLine> m_Lines;
453 std::vector<StyledLineFragment> m_Fragments;
454 std::size_t m_MaxColumnLength = 0; // max number of characters in line
457 StyledTextBuffer GlobalNewConsoleBuffer;
459 class NewConsoleWindow
468 explicit NewConsoleWindow(StyledTextBuffer& buffer) : m_Buffer(buffer)
472 NewConsoleWindow(const NewConsoleWindow&) = delete;
473 NewConsoleWindow& operator=(const NewConsoleWindow&) = delete;
474 ~NewConsoleWindow() = default;
478 auto lock = m_Lock.LockUnique();
482 m_CurrentTopLineNumber = 0;
483 m_CurrentLeftColumnNumber = 0;
486 void Write(LPCTSTR pText)
488 Write(NormalColor, pText, false);
491 void WriteV(LPCTSTR pFormat, va_list args)
493 std::array<TCHAR, BUFSIZ> buf;
494 std::vsnprintf(buf.data(), buf.size(), pFormat, args);
495 Write(NormalColor, buf.data(), false);
498 void Write(COLORREF color, LPCTSTR pText, bool newline)
500 auto lock = m_Lock.LockUnique();
501 bool shouldAutoScroll = ShouldAutoScroll();
503 m_Buffer.Append(color, pText);
507 m_Buffer.AppendNewline();
510 if (shouldAutoScroll)
516 static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
518 auto pConsoleWindow = reinterpret_cast<NewConsoleWindow*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));
523 pConsoleWindow = new NewConsoleWindow(GlobalNewConsoleBuffer);
524 ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pConsoleWindow));
525 pConsoleWindow->m_hWnd = hWnd;
526 pConsoleWindow->OnCreate();
532 pConsoleWindow->OnDestroy();
533 pConsoleWindow->m_hWnd = nullptr;
534 ::SetWindowLongPtr(hWnd, GWLP_USERDATA, 0);
535 delete pConsoleWindow;
545 pConsoleWindow->OnSize();
549 pConsoleWindow->OnPaint();
553 pConsoleWindow->OnVScroll(wParam, lParam);
557 pConsoleWindow->OnHScroll(wParam, lParam);
561 pConsoleWindow->OnMouseWheel(wParam, lParam);
565 pConsoleWindow->OnMouseHWheel(wParam, lParam);
569 pConsoleWindow->OnLButtonDown(wParam, lParam);
573 pConsoleWindow->OnLButtonUp(wParam, lParam);
577 pConsoleWindow->OnMouseMove(wParam, lParam);
581 pConsoleWindow->OnKeyDown(wParam, lParam);
585 pConsoleWindow->OnTimer(wParam, lParam);
595 return ::DefWindowProc(hWnd, msg, wParam, lParam);
601 ::SetTimer(m_hWnd, RedrawTimer, 200, nullptr);
603 ::ShowScrollBar(m_hWnd, SB_BOTH, true);
604 InitializeGDIResource();
605 InvalidateRect(m_hWnd, nullptr, true);
610 ::KillTimer(m_hWnd, RedrawTimer);
611 UninitializeGDIResource();
616 // Recreate everything
617 InitializeGDIResource();
618 InvalidateRect(m_hWnd, nullptr, true);
624 HDC hDC = ::BeginPaint(m_hWnd, &ps);
627 ::GetClientRect(m_hWnd, &rc);
628 ::FillRect(m_hBackDC, &rc, m_hBgBrush);
631 auto lock = m_Lock.LockShared();
632 UpdateScrollBarsNoLock();
634 int lineCount = std::min(static_cast<int>(m_Buffer.GetLineCount() - m_CurrentTopLineNumber), GetVisibleLinesInWindow());
636 for (int i = 0; i < lineCount; i++)
638 auto lineInfo = m_Buffer.GetLines()[m_CurrentTopLineNumber + i];
639 auto first = m_Buffer.GetFragments().begin() + lineInfo.Offset;
640 auto last = first + lineInfo.Length;
642 int x = -m_CurrentLeftColumnNumber * m_FontWidth;
643 int y = i * m_FontHeight;
648 [str = m_Buffer.GetString(), hWnd = m_hWnd, hDC = m_hBackDC, &x, y, fontWidth = m_FontWidth] (const StyledLineFragment& lf)
650 ::SetTextColor(hDC, lf.Color);
651 ::TextOut(hDC, x, y, str.data() + lf.Offset, lf.Length);
652 x += lf.Length * fontWidth;
657 if (m_SelStart.has_value() && m_SelEnd.has_value())
662 if (std::make_pair(s->Line, s->Column) > std::make_pair(e->Line, e->Column))
667 auto [x, y] = PositionFromTextLocation(*s);
668 auto [xe, ye] = PositionFromTextLocation(*e);
670 if (s->Line == e->Line)
672 ::BitBlt(m_hBackDC, x, y, xe - x + m_FontWidth, m_FontHeight, nullptr, 0, 0, DSTINVERT);
676 ::BitBlt(m_hBackDC, x, y, (rc.right - rc.left) - x, m_FontHeight, nullptr, 0, 0, DSTINVERT);
677 ::BitBlt(m_hBackDC, 0, y + m_FontHeight, rc.right - rc.left, ye - (y + m_FontHeight), nullptr, 0, 0, DSTINVERT);
678 ::BitBlt(m_hBackDC, 0, ye, xe + m_FontWidth, m_FontHeight, nullptr, 0, 0, DSTINVERT);
683 ::BitBlt(hDC, 0, 0, rc.right - rc.left, rc.bottom - rc.top, m_hBackDC, 0, 0, SRCCOPY);
684 ::EndPaint(m_hWnd, &ps);
687 void OnVScroll(WPARAM wParam, LPARAM)
689 auto lock = m_Lock.LockUnique();
691 switch (LOWORD(wParam))
694 m_CurrentTopLineNumber = 0;
698 m_CurrentTopLineNumber = GetMaxTopLineNumber();
702 m_CurrentTopLineNumber = std::max(0, m_CurrentTopLineNumber - 1);
706 m_CurrentTopLineNumber = std::min(m_CurrentTopLineNumber + 1, GetMaxTopLineNumber());
710 m_CurrentTopLineNumber = std::max(0, m_CurrentTopLineNumber - GetVisibleLinesInWindow());
714 m_CurrentTopLineNumber = std::min(m_CurrentTopLineNumber + GetVisibleLinesInWindow(), GetMaxTopLineNumber());
717 case SB_THUMBPOSITION:
721 si.cbSize = sizeof(SCROLLINFO);
722 si.fMask = SIF_TRACKPOS;
723 ::GetScrollInfo(m_hWnd, SB_VERT, &si);
724 m_CurrentTopLineNumber = si.nTrackPos;
732 InvalidateRect(m_hWnd, nullptr, true);
735 void OnHScroll(WPARAM wParam, LPARAM)
737 auto lock = m_Lock.LockUnique();
739 switch (LOWORD(wParam))
742 m_CurrentLeftColumnNumber = 0;
746 m_CurrentLeftColumnNumber = GetMaxLeftColumnNumber();
750 m_CurrentLeftColumnNumber = std::max(0, m_CurrentLeftColumnNumber - 1);
754 m_CurrentLeftColumnNumber = std::min(m_CurrentLeftColumnNumber + 1, GetMaxLeftColumnNumber());
758 m_CurrentLeftColumnNumber = std::max(0, m_CurrentLeftColumnNumber - GetVisileColumnsInWindow());
762 m_CurrentLeftColumnNumber = std::min(m_CurrentLeftColumnNumber + GetVisileColumnsInWindow(), GetMaxLeftColumnNumber());
765 case SB_THUMBPOSITION:
769 si.cbSize = sizeof(SCROLLINFO);
770 si.fMask = SIF_TRACKPOS;
771 ::GetScrollInfo(m_hWnd, SB_HORZ, &si);
772 m_CurrentLeftColumnNumber = si.nTrackPos;
780 InvalidateRect(m_hWnd, nullptr, true);
783 void OnMouseWheel(WPARAM wParam, LPARAM)
785 auto lock = m_Lock.LockUnique();
787 m_CurrentTopLineNumber = std::clamp(
788 m_CurrentTopLineNumber - GET_WHEEL_DELTA_WPARAM(wParam) * 3 / WHEEL_DELTA,
790 GetMaxTopLineNumber()
793 InvalidateRect(m_hWnd, nullptr, true);
796 void OnMouseHWheel(WPARAM wParam, LPARAM)
798 auto lock = m_Lock.LockUnique();
800 m_CurrentLeftColumnNumber = std::clamp(
801 m_CurrentLeftColumnNumber + GET_WHEEL_DELTA_WPARAM(wParam) * 4 / WHEEL_DELTA,
803 GetMaxLeftColumnNumber()
806 InvalidateRect(m_hWnd, nullptr, true);
809 void OnLButtonDown(WPARAM, LPARAM lParam)
811 auto lock = m_Lock.LockShared();
812 m_SelStart = TextLocationFromPosition(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), true);
813 m_SelEnd = m_SelStart;
815 if (m_SelStart.has_value())
818 ::SetTimer(m_hWnd, DragScrollTimer, 100, nullptr);
821 InvalidateRect(m_hWnd, nullptr, true);
824 void OnLButtonUp(WPARAM, LPARAM lParam)
826 if (m_SelStart.has_value())
828 ::KillTimer(m_hWnd, DragScrollTimer);
831 auto lock = m_Lock.LockShared();
832 m_SelEnd = TextLocationFromPosition(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), false);
834 if (m_SelStart.has_value() && m_SelEnd.has_value())
836 if (std::make_pair(m_SelStart->Line, m_SelStart->Column) > std::make_pair(m_SelEnd->Line, m_SelEnd->Column))
838 m_SelStart.swap(m_SelEnd);
841 CopyTextToClipboard(m_Buffer.CopySubstring(*m_SelStart, *m_SelEnd));
849 InvalidateRect(m_hWnd, nullptr, true);
853 void OnMouseMove(WPARAM wParam, LPARAM lParam)
855 if (m_SelStart.has_value() && (wParam & MK_LBUTTON))
857 auto lock = m_Lock.LockShared();
858 m_SelEnd = TextLocationFromPosition(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), false);
859 InvalidateRect(m_hWnd, nullptr, true);
863 void OnKeyDown(WPARAM wParam, LPARAM)
865 auto lock = m_Lock.LockUnique();
870 if (::GetKeyState(VK_CONTROL) < 0)
873 std::size_t lastLine = m_Buffer.GetLastLineNumber();
874 m_SelEnd = {lastLine, m_Buffer.GetLastColumnNumber(lastLine)};
875 CopyTextToClipboard(m_Buffer.CopySubstring(*m_SelStart, *m_SelEnd));
882 m_CurrentTopLineNumber = std::max(0, m_CurrentTopLineNumber - 1);
888 m_CurrentTopLineNumber = std::min(m_CurrentTopLineNumber + 1, GetMaxTopLineNumber());
894 m_CurrentLeftColumnNumber = std::max(0, m_CurrentLeftColumnNumber - 1);
900 m_CurrentLeftColumnNumber = std::min(m_CurrentLeftColumnNumber + 1, GetMaxLeftColumnNumber());
904 m_CurrentTopLineNumber = std::max(0, m_CurrentTopLineNumber - GetVisibleLinesInWindow());
908 m_CurrentTopLineNumber = std::min(m_CurrentTopLineNumber + GetVisibleLinesInWindow(), GetMaxTopLineNumber());
912 m_CurrentTopLineNumber = 0;
916 m_CurrentTopLineNumber = GetMaxTopLineNumber();
923 InvalidateRect(m_hWnd, nullptr, true);
926 void OnTimer(WPARAM wParam, LPARAM)
931 InvalidateRect(m_hWnd, nullptr, true);
934 case DragScrollTimer:
938 ::ScreenToClient(m_hWnd, &pt);
940 auto lock = m_Lock.LockUnique();
941 DoDragScrollNoLock(pt.x, pt.y);
943 if (m_SelStart.has_value())
945 m_SelEnd = TextLocationFromPosition(pt.x, pt.y, false);
946 InvalidateRect(m_hWnd, nullptr, true);
956 void InitializeGDIResource()
958 UninitializeGDIResource();
960 HDC hDC = ::GetDC(m_hWnd);
961 m_hBackDC = ::CreateCompatibleDC(hDC);
962 m_SavedDCState = ::SaveDC(m_hBackDC);
965 ::GetClientRect(m_hWnd, &rc);
966 m_hBackBitmap = ::CreateCompatibleBitmap(hDC, rc.right - rc.left, rc.bottom - rc.top);
967 ::SelectObject(m_hBackDC, m_hBackBitmap);
969 ::ReleaseDC(m_hWnd, hDC);
972 m_hFont = ::CreateFont(
985 FIXED_PITCH | FF_DONTCARE,
986 _T("
\82l
\82r
\83S
\83V
\83b
\83N")
989 m_hFont = ::CreateFont(
1000 CLIP_DEFAULT_PRECIS,
1002 FIXED_PITCH | FF_DONTCARE,
1006 ::SelectObject(m_hBackDC, m_hFont);
1007 ::SetBkColor(m_hBackDC, BackgroundColor);
1010 ::GetTextMetrics(m_hBackDC, &tm);
1011 m_FontHeight = tm.tmHeight;
1012 m_FontWidth = tm.tmAveCharWidth;
1014 m_hBgBrush = ::CreateSolidBrush(BackgroundColor);
1017 void UninitializeGDIResource()
1021 ::DeleteObject(m_hBgBrush);
1022 m_hBgBrush = nullptr;
1027 ::RestoreDC(m_hBackDC, m_SavedDCState);
1029 ::DeleteObject(m_hFont);
1031 ::DeleteObject(m_hBackBitmap);
1032 m_hBackBitmap = nullptr;
1033 ::DeleteDC(m_hBackDC);
1034 m_hBackDC = nullptr;
1038 int GetMaxTopLineNumber() const
1040 return std::max(0, static_cast<int>(m_Buffer.GetLineCount() - GetVisibleLinesInWindow()));
1043 int GetMaxLeftColumnNumber() const
1045 return std::max(0, static_cast<int>(m_Buffer.GetMaxColumnLength() - GetVisileColumnsInWindow()));
1048 int GetVisibleLinesInWindow() const
1051 ::GetClientRect(m_hWnd, &rc);
1052 return (rc.bottom - rc.top) / m_FontHeight;
1055 int GetVisileColumnsInWindow() const
1058 ::GetClientRect(m_hWnd, &rc);
1059 return (rc.right - rc.left) / m_FontWidth;
1062 void DoDragScrollNoLock(int x, int y)
1065 ::GetClientRect(m_hWnd, &rc);
1069 m_CurrentLeftColumnNumber = std::max(0, m_CurrentLeftColumnNumber - 1);
1071 else if (rc.right <= x)
1073 m_CurrentLeftColumnNumber = std::min(m_CurrentLeftColumnNumber + 1, GetMaxLeftColumnNumber());
1078 m_CurrentTopLineNumber = std::max(0, m_CurrentTopLineNumber - 1);
1080 else if (rc.bottom <= y)
1082 m_CurrentTopLineNumber = std::min(m_CurrentTopLineNumber + 1, GetMaxTopLineNumber());
1086 bool ShouldAutoScroll() const
1088 return !m_SelStart.has_value() && !m_SelEnd.has_value() && GetMaxTopLineNumber() <= m_CurrentTopLineNumber;
1093 m_CurrentTopLineNumber = GetMaxTopLineNumber();
1096 void UpdateScrollBarsNoLock()
1098 SCROLLINFO siv = {};
1099 SCROLLINFO sih = {};
1101 siv.cbSize = sizeof(SCROLLINFO);
1102 siv.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
1104 siv.nMax = m_Buffer.GetLastLineNumber();
1105 siv.nPage = static_cast<UINT>(GetVisibleLinesInWindow());
1106 siv.nPos = m_CurrentTopLineNumber;
1108 sih.cbSize = sizeof(SCROLLINFO);
1109 sih.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
1111 sih.nMax = m_Buffer.GetMaxLastColumnNumber();
1112 sih.nPage = static_cast<UINT>(GetVisileColumnsInWindow());
1113 sih.nPos = m_CurrentLeftColumnNumber;
1115 ::SetScrollInfo(m_hWnd, SB_VERT, &siv, true);
1116 ::SetScrollInfo(m_hWnd, SB_HORZ, &sih, true);
1119 std::optional<TextLocationInfo> TextLocationFromPosition(int x, int y, bool exact) const
1121 int line = m_CurrentTopLineNumber + y / m_FontHeight;
1125 if (line < 0 || m_Buffer.GetLineCount() == 0)
1127 return TextLocationInfo{static_cast<std::size_t>(0), static_cast<std::size_t>(0)};
1129 else if (m_Buffer.GetLineCount() <= line)
1131 std::size_t lastLine = m_Buffer.GetLastLineNumber();
1132 return TextLocationInfo{lastLine, m_Buffer.GetLastColumnNumber(lastLine)};
1136 if (0 <= line && line < m_Buffer.GetLineCount())
1138 int col = m_CurrentLeftColumnNumber + x / m_FontWidth;
1142 return TextLocationInfo{
1143 static_cast<std::size_t>(line),
1144 static_cast<std::size_t>(std::clamp(col, 0, static_cast<int>(m_Buffer.GetLastColumnNumber(line))))
1148 if (0 <= col && col < m_Buffer.GetColumnLength(line))
1150 return TextLocationInfo{static_cast<std::size_t>(line), static_cast<std::size_t>(col)};
1154 return std::nullopt;
1157 std::pair<int, int> PositionFromTextLocation(TextLocationInfo loc) const
1160 static_cast<int>((loc.Column - m_CurrentLeftColumnNumber) * m_FontWidth),
1161 static_cast<int>((loc.Line - m_CurrentTopLineNumber) * m_FontHeight)
1165 StyledTextBuffer& m_Buffer;
1166 std::optional<TextLocationInfo> m_SelStart;
1167 std::optional<TextLocationInfo> m_SelEnd; // inclusive
1170 HWND m_hWnd = nullptr;
1173 int m_CurrentTopLineNumber = 0; // line # of the top line of the window
1174 int m_CurrentLeftColumnNumber = 0;
1176 HDC m_hBackDC = nullptr;
1177 int m_SavedDCState = 0;
1178 HBITMAP m_hBackBitmap = nullptr;
1179 HFONT m_hFont = nullptr;
1180 int m_FontHeight = 0;
1181 int m_FontWidth = 0; // monospace font only
1182 HBRUSH m_hBgBrush = nullptr;
1185 } // namespace TimW32gNewConsole
1187 extern "C" void ClearNewConsoleBuffer(void)
1189 if (::IsWindow(hConsoleWnd))
1191 auto pConsoleWindow = reinterpret_cast<TimW32gNewConsole::NewConsoleWindow*>(
1192 ::GetWindowLongPtr(::GetDlgItem(hConsoleWnd, IDC_EDIT), GWLP_USERDATA)
1197 pConsoleWindow->Clear();
1202 TimW32gNewConsole::GlobalNewConsoleBuffer.Clear();
1205 extern "C" void NewConsoleBufferWriteCMsg(int type, int verbosity_level, LPCTSTR str)
1207 COLORREF color = TimW32gNewConsole::NormalColor;
1209 if (type == CMSG_FATAL || type == CMSG_ERROR)
1211 color = TimW32gNewConsole::ErrorColor;
1213 else if (type == CMSG_WARNING)
1215 color = TimW32gNewConsole::WarningColor;
1217 else if (type == CMSG_INFO && verbosity_level <= VERB_NORMAL)
1219 color = TimW32gNewConsole::InfoColor;
1222 if (::IsWindow(hConsoleWnd))
1224 auto pConsoleWindow = reinterpret_cast<TimW32gNewConsole::NewConsoleWindow*>(
1225 ::GetWindowLongPtr(::GetDlgItem(hConsoleWnd, IDC_EDIT), GWLP_USERDATA)
1230 pConsoleWindow->Write(color, str, true);
1235 TimW32gNewConsole::GlobalNewConsoleBuffer.Append(color, str);
1236 TimW32gNewConsole::GlobalNewConsoleBuffer.AppendNewline();
1239 extern "C" void InitializeNewConsole(void)
1242 wc.cbSize = sizeof(wc);
1244 if (!::GetClassInfoEx(::GetModuleHandle(nullptr), TimW32gNewConsole::pClassName, &wc))
1246 wc.style = CS_HREDRAW | CS_VREDRAW;
1247 wc.lpfnWndProc = &TimW32gNewConsole::NewConsoleWindow::WindowProc;
1250 wc.hInstance = ::GetModuleHandle(nullptr);
1252 wc.hCursor = ::LoadCursor(nullptr, IDC_ARROW);
1253 wc.hbrBackground = nullptr;
1254 wc.lpszMenuName = nullptr;
1255 wc.lpszClassName = TimW32gNewConsole::pClassName;
1256 wc.hIconSm = nullptr;
1258 ::RegisterClassEx(&wc);
1262 extern "C" void NewConsoleClear(HWND hwnd)
1264 auto pConsoleWindow = reinterpret_cast<TimW32gNewConsole::NewConsoleWindow*>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));
1268 pConsoleWindow->Clear();
1272 extern "C" void NewConsoleWrite(HWND hwnd, LPCTSTR str)
1274 auto pConsoleWindow = reinterpret_cast<TimW32gNewConsole::NewConsoleWindow*>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));
1278 pConsoleWindow->Write(str);
1282 extern "C" void NewConsoleWriteV(HWND hwnd, LPCTSTR format, va_list args)
1284 auto pConsoleWindow = reinterpret_cast<TimW32gNewConsole::NewConsoleWindow*>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));
1288 pConsoleWindow->WriteV(format, args);