diff options
-rw-r--r-- | xW/xW.cpp | 145 |
1 files changed, 105 insertions, 40 deletions
@@ -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 ¤t) +{ + 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], ¤t); + 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(¤t); + 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; - - 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(¤t, ¤t_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], ¤t); - 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], ¤t); - - 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" "); - } + bool sameline = !buffer_reset_selection(); + buffer_print_date_change(sameline, last, current); + + wchar_t buffer[64] = {}; + wcsftime(buffer, sizeof buffer, &L"\n%H:%M:%S"[sameline], ¤t); + + 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; } |