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

141
xW/xW.cpp
View File

@ -100,6 +100,7 @@ struct {
HWND hwndInput; ///< edit: user input
HWND hwndLastFocused; ///< For Alt+Tab, e.g.
HANDLE date_change_timer; ///< Waitable timer for day changes
HICON hicon; ///< Normal program icon
HICON hiconHighlighted; ///< Highlighted program icon
@ -402,8 +403,7 @@ refresh_status()
status += L"🡇 ";
status += g.buffer_current;
auto b = buffer_by_name(g.buffer_current);
if (b) {
if (auto b = buffer_by_name(g.buffer_current)) {
if (!b->modes.empty())
status += L"(+" + b->modes + L")";
if (b->hide_unimportant)
@ -542,50 +542,91 @@ convert_buffer_line(Relay::EventData_BufferLine &line)
}
static void
buffer_print_line(std::vector<BufferLine>::const_iterator begin,
std::vector<BufferLine>::const_iterator line)
buffer_print_date_change(bool &sameline, const tm &last, const tm &current)
{
if (last.tm_year == current.tm_year &&
last.tm_mon == current.tm_mon &&
last.tm_mday == current.tm_mday)
return;
wchar_t buffer[64] = {};
wcsftime(buffer, sizeof buffer, &L"\n%x"[sameline], &current);
sameline = false;
CHARFORMAT2 cf = default_charformat();
cf.dwEffects |= CFE_BOLD;
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 = !cr.cpMin;
bool sameline = !buffer_reset_selection();
buffer_print_date_change(sameline, last, current);
time_t current_unix = line->when / 1000;
time_t last_unix = (line != begin)
? (line - 1)->when / 1000
: time(NULL);
wchar_t buffer[64] = {};
wcsftime(buffer, sizeof buffer, &L"\n%H:%M:%S"[sameline], &current);
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] = {};
wcsftime(buffer, sizeof buffer, &L"\n%x\n"[sameline], &current);
sameline = true;
CHARFORMAT2 cf = default_charformat();
cf.dwEffects |= CFE_BOLD;
richedit_replacesel(g.hwndBuffer, &cf, buffer);
}
{
wchar_t buffer[64] = {};
wcsftime(buffer, sizeof buffer, &L"\n%H:%M:%S"[sameline], &current);
CHARFORMAT2 cf = default_charformat();
cf.dwEffects &= ~(CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR);
cf.crTextColor = RGB(0xbb, 0xbb, 0xbb);
cf.crBackColor = RGB(0xf8, 0xf8, 0xf8);
richedit_replacesel(g.hwndBuffer, &cf, buffer);
cf = default_charformat();
richedit_replacesel(g.hwndBuffer, &cf, L" ");
}
CHARFORMAT2 cf = default_charformat();
cf.dwEffects &= ~(CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR);
cf.crTextColor = RGB(0xbb, 0xbb, 0xbb);
cf.crBackColor = RGB(0xf8, 0xf8, 0xf8);
richedit_replacesel(g.hwndBuffer, &cf, buffer);
cf = default_charformat();
richedit_replacesel(g.hwndBuffer, &cf, L" ");
// Tabstops won't quite help us here, since we need it centred.
std::wstring prefix;
@ -673,6 +714,7 @@ refresh_buffer(const Buffer &b)
i++;
}
buffer_print_and_watch_trailing_date_changes();
buffer_scroll_to_bottom();
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:
process_resize(LOWORD(lParam), HIWORD(lParam));
return 0;
case WM_TIMECHANGE:
_tzset();
if (auto b = buffer_by_name(g.buffer_current))
refresh_buffer(*b);
return 0;
case WM_ACTIVATE:
if (LOWORD(wParam) == WA_INACTIVE)
g.hwndLastFocused = GetFocus();
@ -1847,20 +1894,38 @@ wWinMain(HINSTANCE hInstance, [[maybe_unused]] HINSTANCE hPrevInstance,
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)) {
HANDLE handles[] = {g.date_change_timer, g.event};
DWORD count = 2 - !handles[1];
DWORD result = MsgWaitForMultipleObjects(
g.event != NULL, &g.event, FALSE, INFINITE, QS_ALLINPUT);
count, handles, FALSE, INFINITE, QS_ALLINPUT);
if (result == WAIT_FAILED) {
show_error_message(format_error_message(GetLastError()).c_str());
return 1;
}
if (g.event != NULL && result == WAIT_OBJECT_0 &&
!relay_process_socket_events(error)) {
if (result >= WAIT_OBJECT_0 + count)
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());
return 1;
}
}
FreeAddrInfo(g.addresses);
WSACleanup();
CloseHandle(g.date_change_timer);
return 0;
}