xW: add missing date change handling

This commit is contained in:
Přemysl Eric Janouch 2023-07-26 01:46:59 +02:00
parent 100de5ac2d
commit 81bc578773
Signed by: p
GPG Key ID: A0420B94F92B9493
1 changed files with 103 additions and 38 deletions

125
xW/xW.cpp
View File

@ -100,6 +100,7 @@ struct {
HWND hwndInput; ///< edit: user input HWND hwndInput; ///< edit: user input
HWND hwndLastFocused; ///< For Alt+Tab, e.g. HWND hwndLastFocused; ///< For Alt+Tab, e.g.
HANDLE date_change_timer; ///< Waitable timer for day changes
HICON hicon; ///< Normal program icon HICON hicon; ///< Normal program icon
HICON hiconHighlighted; ///< Highlighted program icon HICON hiconHighlighted; ///< Highlighted program icon
@ -402,8 +403,7 @@ refresh_status()
status += L"🡇 "; status += L"🡇 ";
status += g.buffer_current; status += g.buffer_current;
auto b = buffer_by_name(g.buffer_current); if (auto b = buffer_by_name(g.buffer_current)) {
if (b) {
if (!b->modes.empty()) if (!b->modes.empty())
status += L"(+" + b->modes + L")"; status += L"(+" + b->modes + L")";
if (b->hide_unimportant) if (b->hide_unimportant)
@ -542,39 +542,81 @@ convert_buffer_line(Relay::EventData_BufferLine &line)
} }
static void static void
buffer_print_line(std::vector<BufferLine>::const_iterator begin, buffer_print_date_change(bool &sameline, const tm &last, const tm &current)
std::vector<BufferLine>::const_iterator line)
{ {
CHARRANGE cr = {}; if (last.tm_year == current.tm_year &&
cr.cpMin = cr.cpMax = GetWindowTextLength(g.hwndBuffer); last.tm_mon == current.tm_mon &&
SendMessage(g.hwndBuffer, EM_EXSETSEL, 0, (LPARAM) &cr); last.tm_mday == current.tm_mday)
return;
// The Rich Edit control makes the window cursor transparent
// each time you add an independent newline character. Avoid that.
// (Sadly, this also makes Windows 7 end lines with a bogus space that
// has the CHARFORMAT2 of what we flush that newline together with.)
bool sameline = !cr.cpMin;
time_t current_unix = line->when / 1000;
time_t last_unix = (line != begin)
? (line - 1)->when / 1000
: time(NULL);
tm current = {}, last = {};
(void) localtime_s(&current, &current_unix);
(void) localtime_s(&last, &last_unix);
if (last.tm_year != current.tm_year ||
last.tm_mon != current.tm_mon ||
last.tm_mday != current.tm_mday) {
wchar_t buffer[64] = {}; wchar_t buffer[64] = {};
wcsftime(buffer, sizeof buffer, &L"\n%x\n"[sameline], &current); wcsftime(buffer, sizeof buffer, &L"\n%x"[sameline], &current);
sameline = true; sameline = false;
CHARFORMAT2 cf = default_charformat(); CHARFORMAT2 cf = default_charformat();
cf.dwEffects |= CFE_BOLD; cf.dwEffects |= CFE_BOLD;
richedit_replacesel(g.hwndBuffer, &cf, buffer); richedit_replacesel(g.hwndBuffer, &cf, buffer);
} }
static LONG
buffer_reset_selection()
{ {
CHARRANGE cr = {};
cr.cpMin = cr.cpMax = GetWindowTextLength(g.hwndBuffer);
SendMessage(g.hwndBuffer, EM_EXSETSEL, 0, (LPARAM) &cr);
return cr.cpMin;
}
static struct tm
buffer_localtime(time_t time)
{
// This isn't critical, so let it fail quietly.
struct tm result = {};
(void) localtime_s(&result, &time);
return result;
}
static void
buffer_print_and_watch_trailing_date_changes()
{
time_t current_unix = time(NULL);
tm current = buffer_localtime(current_unix);
auto b = buffer_by_name(g.buffer_current);
if (b && !b->lines.empty()) {
tm last = buffer_localtime(b->lines.back().when / 1000);
bool sameline = !buffer_reset_selection();
buffer_print_date_change(sameline, last, current);
}
current.tm_sec = current.tm_min = current.tm_hour = 0;
current.tm_mday++;
current.tm_isdst = -1;
const time_t midnight = mktime(&current);
if (midnight == (time_t) -1 || midnight < current_unix)
return;
// Note that after printing the first trailing update,
// follow-up updates may be duplicated if timer events arrive too early.
LARGE_INTEGER li = {};
li.QuadPart = (midnight - current_unix + 1) * -10000000LL;
SetWaitableTimer(g.date_change_timer, &li, 0, NULL, NULL, FALSE);
}
static void
buffer_print_line(std::vector<BufferLine>::const_iterator begin,
std::vector<BufferLine>::const_iterator line)
{
tm current = buffer_localtime(line->when / 1000);
tm last = buffer_localtime(
line == begin ? time(NULL) : (line - 1)->when / 1000);
// The Rich Edit control makes the window cursor transparent
// each time you add an independent newline character. Avoid that.
// (Sadly, this also makes Windows 7 end lines with a bogus space that
// has the CHARFORMAT2 of what we flush that newline together with.)
bool sameline = !buffer_reset_selection();
buffer_print_date_change(sameline, last, current);
wchar_t buffer[64] = {}; wchar_t buffer[64] = {};
wcsftime(buffer, sizeof buffer, &L"\n%H:%M:%S"[sameline], &current); wcsftime(buffer, sizeof buffer, &L"\n%H:%M:%S"[sameline], &current);
@ -585,7 +627,6 @@ buffer_print_line(std::vector<BufferLine>::const_iterator begin,
richedit_replacesel(g.hwndBuffer, &cf, buffer); richedit_replacesel(g.hwndBuffer, &cf, buffer);
cf = default_charformat(); cf = default_charformat();
richedit_replacesel(g.hwndBuffer, &cf, L" "); richedit_replacesel(g.hwndBuffer, &cf, L" ");
}
// Tabstops won't quite help us here, since we need it centred. // Tabstops won't quite help us here, since we need it centred.
std::wstring prefix; std::wstring prefix;
@ -673,6 +714,7 @@ refresh_buffer(const Buffer &b)
i++; i++;
} }
buffer_print_and_watch_trailing_date_changes();
buffer_scroll_to_bottom(); buffer_scroll_to_bottom();
SendMessage(g.hwndBuffer, WM_SETREDRAW, (WPARAM) TRUE, 0); SendMessage(g.hwndBuffer, WM_SETREDRAW, (WPARAM) TRUE, 0);
@ -1580,6 +1622,11 @@ window_proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
case WM_SIZE: case WM_SIZE:
process_resize(LOWORD(lParam), HIWORD(lParam)); process_resize(LOWORD(lParam), HIWORD(lParam));
return 0; return 0;
case WM_TIMECHANGE:
_tzset();
if (auto b = buffer_by_name(g.buffer_current))
refresh_buffer(*b);
return 0;
case WM_ACTIVATE: case WM_ACTIVATE:
if (LOWORD(wParam) == WA_INACTIVE) if (LOWORD(wParam) == WA_INACTIVE)
g.hwndLastFocused = GetFocus(); g.hwndLastFocused = GetFocus();
@ -1847,20 +1894,38 @@ wWinMain(HINSTANCE hInstance, [[maybe_unused]] HINSTANCE hPrevInstance,
return 1; return 1;
} }
g.date_change_timer = CreateWaitableTimer(NULL, FALSE, NULL);
if (!g.date_change_timer) {
show_error_message(format_error_message(GetLastError()).c_str());
return 1;
}
while (process_messages(accelerators)) { while (process_messages(accelerators)) {
HANDLE handles[] = {g.date_change_timer, g.event};
DWORD count = 2 - !handles[1];
DWORD result = MsgWaitForMultipleObjects( DWORD result = MsgWaitForMultipleObjects(
g.event != NULL, &g.event, FALSE, INFINITE, QS_ALLINPUT); count, handles, FALSE, INFINITE, QS_ALLINPUT);
if (result == WAIT_FAILED) { if (result == WAIT_FAILED) {
show_error_message(format_error_message(GetLastError()).c_str()); show_error_message(format_error_message(GetLastError()).c_str());
return 1; return 1;
} }
if (g.event != NULL && result == WAIT_OBJECT_0 && if (result >= WAIT_OBJECT_0 + count)
!relay_process_socket_events(error)) { continue;
auto signalled = handles[result];
if (signalled == g.date_change_timer) {
bool to_bottom = buffer_at_bottom();
buffer_print_and_watch_trailing_date_changes();
if (to_bottom)
buffer_scroll_to_bottom();
}
if (signalled == g.event && !relay_process_socket_events(error)) {
show_error_message(error.c_str()); show_error_message(error.c_str());
return 1; return 1;
} }
} }
FreeAddrInfo(g.addresses); FreeAddrInfo(g.addresses);
WSACleanup(); WSACleanup();
CloseHandle(g.date_change_timer);
return 0; return 0;
} }