LibCore: Create a system time zone watcher

This creates platform-dependent monitors to detect when the system time
zone changes. On Linux, we use a file watcher to monitor files such as
/etc/localtime for changes. On macOS, this uses CFNotificationCenter to
be notified by the OS when the time zone changes.

Note: the macOS implementation requires running in a process which is
running the CoreFoundation event loop. Both the AppKit and Qt chromes
are doing so in the UI process, but this means we cannot run this
monitor in the WebContent process.
This commit is contained in:
Timothy Flynn 2024-08-24 12:15:13 -04:00 committed by Andreas Kling
parent b31c11bca5
commit 577efcdc32
Notes: github-actions[bot] 2024-08-25 07:48:42 +00:00
6 changed files with 172 additions and 1 deletions

View file

@ -63,21 +63,24 @@ if (NOT WIN32 AND NOT EMSCRIPTEN)
list(APPEND SOURCES LocalServer.cpp)
endif()
# FIXME: Implement Core::FileWatcher for *BSD and Windows.
# FIXME: Implement these for other systems.
if (LINUX AND NOT EMSCRIPTEN)
list(APPEND SOURCES
FileWatcherLinux.cpp
Platform/ProcessStatisticsLinux.cpp
TimeZoneWatcherLinux.cpp
)
elseif (APPLE AND NOT IOS)
list(APPEND SOURCES
FileWatcherMacOS.mm
Platform/ProcessStatisticsMach.cpp
TimeZoneWatcherMacOS.mm
)
else()
list(APPEND SOURCES
FileWatcherUnimplemented.cpp
Platform/ProcessStatisticsUnimplemented.cpp
TimeZoneWatcherUnimplemented.cpp
)
endif()

View file

@ -45,6 +45,7 @@ class TCPServer;
class TCPSocket;
class Timer;
class TimerEvent;
class TimeZoneWatcher;
class UDPServer;
class UDPSocket;

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Error.h>
#include <AK/Function.h>
#include <AK/Noncopyable.h>
#include <AK/NonnullOwnPtr.h>
namespace Core {
class TimeZoneWatcher {
AK_MAKE_NONCOPYABLE(TimeZoneWatcher);
public:
static ErrorOr<NonnullOwnPtr<TimeZoneWatcher>> create();
virtual ~TimeZoneWatcher() = default;
Function<void()> on_time_zone_changed;
protected:
TimeZoneWatcher() = default;
};
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Platform.h>
#include <LibCore/FileWatcher.h>
#include <LibCore/TimeZoneWatcher.h>
#if !defined(AK_OS_LINUX)
static_assert(false, "This file must only be used for Linux");
#endif
namespace Core {
static constexpr auto time_zone_files = Array {
"/etc/localtime"sv,
"/etc/timezone"sv,
"/etc/TZ"sv,
};
static constexpr auto time_zone_mask = FileWatcherEvent::Type::ContentModified
| FileWatcherEvent::Type::Deleted
| FileWatcherEvent::Type::DoNotFollowLink;
class TimeZoneWatcherImpl final : public TimeZoneWatcher {
public:
static ErrorOr<NonnullOwnPtr<TimeZoneWatcherImpl>> create()
{
auto file_watcher = TRY(FileWatcher::create());
for (auto time_zone_file : time_zone_files) {
if (auto result = file_watcher->add_watch(time_zone_file, time_zone_mask); !result.is_error())
break;
}
return adopt_own(*new TimeZoneWatcherImpl(move(file_watcher)));
}
private:
explicit TimeZoneWatcherImpl(NonnullRefPtr<FileWatcher> file_watcher)
: m_file_watcher(move(file_watcher))
{
m_file_watcher->on_change = [this](Core::FileWatcherEvent const& event) {
if (on_time_zone_changed)
on_time_zone_changed();
if (has_flag(event.type, FileWatcherEvent::Type::Deleted))
(void)m_file_watcher->add_watch(event.event_path, time_zone_mask);
};
}
NonnullRefPtr<FileWatcher> m_file_watcher;
};
ErrorOr<NonnullOwnPtr<TimeZoneWatcher>> TimeZoneWatcher::create()
{
return TimeZoneWatcherImpl::create();
}
}

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Platform.h>
#include <LibCore/TimeZoneWatcher.h>
#if !defined(AK_OS_MACOS)
static_assert(false, "This file must only be used for macOS");
#endif
#include <CoreFoundation/CoreFoundation.h>
namespace Core {
class TimeZoneWatcherImpl final : public TimeZoneWatcher {
public:
static ErrorOr<NonnullOwnPtr<TimeZoneWatcherImpl>> create()
{
return adopt_own(*new TimeZoneWatcherImpl());
}
virtual ~TimeZoneWatcherImpl() override
{
CFNotificationCenterRemoveObserver(
CFNotificationCenterGetLocalCenter(),
this,
kCFTimeZoneSystemTimeZoneDidChangeNotification,
nullptr);
}
private:
explicit TimeZoneWatcherImpl()
{
CFNotificationCenterAddObserver(
CFNotificationCenterGetLocalCenter(),
this,
time_zone_changed,
kCFTimeZoneSystemTimeZoneDidChangeNotification,
nullptr,
CFNotificationSuspensionBehaviorDeliverImmediately);
}
static void time_zone_changed(CFNotificationCenterRef, void* observer, CFStringRef, void const*, CFDictionaryRef)
{
auto const& time_zone_watcher = *reinterpret_cast<TimeZoneWatcherImpl*>(observer);
if (time_zone_watcher.on_time_zone_changed)
time_zone_watcher.on_time_zone_changed();
}
};
ErrorOr<NonnullOwnPtr<TimeZoneWatcher>> TimeZoneWatcher::create()
{
return TimeZoneWatcherImpl::create();
}
}

View file

@ -0,0 +1,16 @@
/*
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibCore/TimeZoneWatcher.h>
namespace Core {
ErrorOr<NonnullOwnPtr<TimeZoneWatcher>> TimeZoneWatcher::create()
{
return Error::from_errno(ENOTSUP);
}
}