aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--xW/xW.cpp145
1 files changed, 105 insertions, 40 deletions
diff --git a/xW/xW.cpp b/xW/xW.cpp
index c0b6a03..d3de8a6 100644
--- a/xW/xW.cpp
+++ b/xW/xW.cpp
@@ -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;
-
- 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] = {};
- 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" ");
- }
+ 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], &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" ");
// 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;
}