feat: add time offset configuration
This commit is contained in:
@@ -333,6 +333,10 @@ namespace Montauk {
|
|||||||
return Sys_BtInfo((BtAdapterInfo*)frame->arg1);
|
return Sys_BtInfo((BtAdapterInfo*)frame->arg1);
|
||||||
case SYS_SUSPEND:
|
case SYS_SUSPEND:
|
||||||
return Sys_Suspend();
|
return Sys_Suspend();
|
||||||
|
case SYS_SETTZ:
|
||||||
|
return Sys_SetTZ((int32_t)frame->arg1);
|
||||||
|
case SYS_GETTZ:
|
||||||
|
return Sys_GetTZ();
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,6 +174,10 @@ namespace Montauk {
|
|||||||
/* Power.hpp */
|
/* Power.hpp */
|
||||||
static constexpr uint64_t SYS_SUSPEND = 89;
|
static constexpr uint64_t SYS_SUSPEND = 89;
|
||||||
|
|
||||||
|
/* Time.hpp */
|
||||||
|
static constexpr uint64_t SYS_SETTZ = 90;
|
||||||
|
static constexpr uint64_t SYS_GETTZ = 91;
|
||||||
|
|
||||||
static constexpr int SOCK_TCP = 1;
|
static constexpr int SOCK_TCP = 1;
|
||||||
static constexpr int SOCK_UDP = 2;
|
static constexpr int SOCK_UDP = 2;
|
||||||
|
|
||||||
|
|||||||
@@ -29,4 +29,13 @@ namespace Montauk {
|
|||||||
out->Minute = dt.Minute;
|
out->Minute = dt.Minute;
|
||||||
out->Second = dt.Second;
|
out->Second = dt.Second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int64_t Sys_SetTZ(int32_t offsetMinutes) {
|
||||||
|
Timekeeping::SetTZOffset(offsetMinutes);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64_t Sys_GetTZ() {
|
||||||
|
return (int64_t)Timekeeping::GetTZOffset();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
using namespace Kt;
|
using namespace Kt;
|
||||||
|
|
||||||
static int64_t g_bootEpoch = 0;
|
static int64_t g_bootEpoch = 0;
|
||||||
|
static int g_tzOffsetMinutes = 60; /* Default: UTC+1 (CET) until userspace overrides */
|
||||||
|
|
||||||
static bool IsLeapYear(int year) {
|
static bool IsLeapYear(int year) {
|
||||||
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
|
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
|
||||||
@@ -68,34 +69,29 @@ static Timekeeping::DateTime EpochToDate(int64_t epoch) {
|
|||||||
|
|
||||||
void Timekeeping::Init(uint16_t Year, uint8_t Month, uint8_t Day, uint8_t Hour, uint8_t Minute, uint8_t Second) {
|
void Timekeeping::Init(uint16_t Year, uint8_t Month, uint8_t Day, uint8_t Hour, uint8_t Minute, uint8_t Second) {
|
||||||
g_bootEpoch = DateToEpoch(Year, Month, Day, Hour, Minute, Second);
|
g_bootEpoch = DateToEpoch(Year, Month, Day, Hour, Minute, Second);
|
||||||
/* Hardcode CET for now */
|
|
||||||
TimeZone CET = {
|
|
||||||
"Central European Time",
|
|
||||||
"CET",
|
|
||||||
1, /* UTC+1 */
|
|
||||||
0,
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
Kt::KernelLogStream(INFO, "Timekeeping Service") << "Setting time zone to " << CET.TZLongName << " (" << CET.TZShortName << ")";
|
/* Apply default timezone offset for boot log display */
|
||||||
|
int adjMin = Minute + (g_tzOffsetMinutes % 60);
|
||||||
|
int adjHour = Hour + (g_tzOffsetMinutes / 60);
|
||||||
|
if (adjMin < 0) { adjMin += 60; adjHour -= 1; }
|
||||||
|
if (adjMin >= 60) { adjMin -= 60; adjHour += 1; }
|
||||||
|
if (adjHour < 0) adjHour += 24;
|
||||||
|
if (adjHour >= 24) adjHour -= 24;
|
||||||
|
|
||||||
Minute = Minute + CET.MinuteOffset;
|
int offH = g_tzOffsetMinutes / 60;
|
||||||
Hour = Hour + CET.HourOffset;
|
int offM = g_tzOffsetMinutes % 60;
|
||||||
if (Minute >= 60) {
|
if (offM < 0) offM = -offM;
|
||||||
Minute -= 60;
|
|
||||||
Hour += 1;
|
Kt::KernelLogStream(INFO, "Timekeeping Service") << "Time zone: UTC"
|
||||||
}
|
<< (offH >= 0 ? "+" : "") << offH
|
||||||
if (Hour >= 24) {
|
<< (offM ? ":" : "") << (offM >= 10 ? "" : (offM ? "0" : ""))
|
||||||
Hour -= 24;
|
<< (offM ? offM : 0);
|
||||||
Day += 1;
|
|
||||||
/* Note: No month/day overflow handling yet */
|
|
||||||
}
|
|
||||||
|
|
||||||
kcp::cstringstream minuteStream;
|
kcp::cstringstream minuteStream;
|
||||||
if (Minute < 10) {
|
if (adjMin < 10) {
|
||||||
minuteStream << "0";
|
minuteStream << "0";
|
||||||
}
|
}
|
||||||
minuteStream << Minute;
|
minuteStream << adjMin;
|
||||||
CString minuteStr = minuteStream.c_str();
|
CString minuteStr = minuteStream.c_str();
|
||||||
|
|
||||||
kcp::cstringstream secondStream;
|
kcp::cstringstream secondStream;
|
||||||
@@ -111,12 +107,10 @@ void Timekeeping::Init(uint16_t Year, uint8_t Month, uint8_t Day, uint8_t Hour,
|
|||||||
<< Day << " "
|
<< Day << " "
|
||||||
<< Months[Month] << " "
|
<< Months[Month] << " "
|
||||||
<< Year << ", "
|
<< Year << ", "
|
||||||
<< Hour << ":"
|
<< adjHour << ":"
|
||||||
|
|
||||||
<< minuteStr << ":"
|
<< minuteStr << ":"
|
||||||
<< secondStr
|
<< secondStr
|
||||||
<< " (" << CET.TZLongName << ")";
|
<< " (UTC" << (offH >= 0 ? "+" : "") << offH << ")";
|
||||||
|
|
||||||
|
|
||||||
CString dateString = panelStr.c_str();
|
CString dateString = panelStr.c_str();
|
||||||
|
|
||||||
@@ -128,5 +122,13 @@ int64_t Timekeeping::GetUnixTimestamp() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Timekeeping::DateTime Timekeeping::GetDateTime() {
|
Timekeeping::DateTime Timekeeping::GetDateTime() {
|
||||||
return EpochToDate(GetUnixTimestamp());
|
return EpochToDate(GetUnixTimestamp() + (int64_t)g_tzOffsetMinutes * 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timekeeping::SetTZOffset(int totalMinutes) {
|
||||||
|
g_tzOffsetMinutes = totalMinutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Timekeeping::GetTZOffset() {
|
||||||
|
return g_tzOffsetMinutes;
|
||||||
}
|
}
|
||||||
@@ -53,4 +53,7 @@ namespace Timekeeping {
|
|||||||
void Init(uint16_t Year, uint8_t Month, uint8_t Day, uint8_t Hour, uint8_t Minute, uint8_t Second);
|
void Init(uint16_t Year, uint8_t Month, uint8_t Day, uint8_t Hour, uint8_t Minute, uint8_t Second);
|
||||||
int64_t GetUnixTimestamp();
|
int64_t GetUnixTimestamp();
|
||||||
DateTime GetDateTime();
|
DateTime GetDateTime();
|
||||||
|
|
||||||
|
void SetTZOffset(int totalMinutes);
|
||||||
|
int GetTZOffset();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -116,6 +116,10 @@ namespace Montauk {
|
|||||||
// Power management
|
// Power management
|
||||||
static constexpr uint64_t SYS_SUSPEND = 89;
|
static constexpr uint64_t SYS_SUSPEND = 89;
|
||||||
|
|
||||||
|
// Timezone
|
||||||
|
static constexpr uint64_t SYS_SETTZ = 90;
|
||||||
|
static constexpr uint64_t SYS_GETTZ = 91;
|
||||||
|
|
||||||
// Audio control commands (for SYS_AUDIOCTL)
|
// Audio control commands (for SYS_AUDIOCTL)
|
||||||
static constexpr int AUDIO_CTL_SET_VOLUME = 0;
|
static constexpr int AUDIO_CTL_SET_VOLUME = 0;
|
||||||
static constexpr int AUDIO_CTL_GET_VOLUME = 1;
|
static constexpr int AUDIO_CTL_GET_VOLUME = 1;
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ struct DesktopSettings {
|
|||||||
bool show_shadows; // window shadows on/off
|
bool show_shadows; // window shadows on/off
|
||||||
bool clock_24h; // 24-hour clock format
|
bool clock_24h; // 24-hour clock format
|
||||||
int ui_scale; // 0=Small, 1=Default, 2=Large
|
int ui_scale; // 0=Small, 1=Default, 2=Large
|
||||||
|
int tz_offset_minutes; // timezone offset from UTC in minutes
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DesktopState {
|
struct DesktopState {
|
||||||
|
|||||||
@@ -94,6 +94,8 @@ extern "C" {
|
|||||||
#define MTK_SYS_AUDIOCLOSE 81
|
#define MTK_SYS_AUDIOCLOSE 81
|
||||||
#define MTK_SYS_AUDIOWRITE 82
|
#define MTK_SYS_AUDIOWRITE 82
|
||||||
#define MTK_SYS_AUDIOCTL 83
|
#define MTK_SYS_AUDIOCTL 83
|
||||||
|
#define MTK_SYS_SETTZ 90
|
||||||
|
#define MTK_SYS_GETTZ 91
|
||||||
|
|
||||||
#define MTK_SOCK_TCP 1
|
#define MTK_SOCK_TCP 1
|
||||||
#define MTK_SOCK_UDP 2
|
#define MTK_SOCK_UDP 2
|
||||||
@@ -411,6 +413,14 @@ static inline void mtk_gettime(mtk_datetime *out) {
|
|||||||
_mtk_syscall1(MTK_SYS_GETTIME, (long)out);
|
_mtk_syscall1(MTK_SYS_GETTIME, (long)out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void mtk_settz(int offset_minutes) {
|
||||||
|
_mtk_syscall1(MTK_SYS_SETTZ, (long)offset_minutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int mtk_gettz(void) {
|
||||||
|
return (int)_mtk_syscall0(MTK_SYS_GETTZ);
|
||||||
|
}
|
||||||
|
|
||||||
/* ====================================================================
|
/* ====================================================================
|
||||||
System info
|
System info
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|||||||
@@ -249,6 +249,10 @@ namespace montauk {
|
|||||||
// Timekeeping (wall-clock)
|
// Timekeeping (wall-clock)
|
||||||
inline void gettime(Montauk::DateTime* out) { syscall1(Montauk::SYS_GETTIME, (uint64_t)out); }
|
inline void gettime(Montauk::DateTime* out) { syscall1(Montauk::SYS_GETTIME, (uint64_t)out); }
|
||||||
|
|
||||||
|
// Timezone offset (total minutes from UTC)
|
||||||
|
inline void settz(int offset_minutes) { syscall1(Montauk::SYS_SETTZ, (uint64_t)(int64_t)offset_minutes); }
|
||||||
|
inline int gettz() { return (int)syscall0(Montauk::SYS_GETTZ); }
|
||||||
|
|
||||||
// Random number generation
|
// Random number generation
|
||||||
inline int64_t getrandom(void* buf, uint32_t len) {
|
inline int64_t getrandom(void* buf, uint32_t len) {
|
||||||
return syscall2(Montauk::SYS_GETRANDOM, (uint64_t)buf, (uint64_t)len);
|
return syscall2(Montauk::SYS_GETRANDOM, (uint64_t)buf, (uint64_t)len);
|
||||||
|
|||||||
@@ -301,6 +301,35 @@ static void settings_draw_display(Canvas& c, SettingsState* st) {
|
|||||||
draw_toggle_btn(c, bx, y, sbw, btn_h, "Small", s.ui_scale == 0, accent);
|
draw_toggle_btn(c, bx, y, sbw, btn_h, "Small", s.ui_scale == 0, accent);
|
||||||
draw_toggle_btn(c, bx + sbw + 8, y, sbw, btn_h, "Default", s.ui_scale == 1, accent);
|
draw_toggle_btn(c, bx + sbw + 8, y, sbw, btn_h, "Default", s.ui_scale == 1, accent);
|
||||||
draw_toggle_btn(c, bx + (sbw + 8) * 2, y, sbw, btn_h, "Large", s.ui_scale == 2, accent);
|
draw_toggle_btn(c, bx + (sbw + 8) * 2, y, sbw, btn_h, "Large", s.ui_scale == 2, accent);
|
||||||
|
y += btn_h + 20;
|
||||||
|
|
||||||
|
// Separator
|
||||||
|
c.hline(x, y, c.w - 2 * x, colors::BORDER);
|
||||||
|
y += 16;
|
||||||
|
|
||||||
|
// UTC Offset
|
||||||
|
c.text(x, y + 6, "UTC Offset", colors::TEXT_COLOR);
|
||||||
|
bx = x + 180;
|
||||||
|
|
||||||
|
// [-] button
|
||||||
|
draw_toggle_btn(c, bx, y, 36, btn_h, "-", false, accent);
|
||||||
|
|
||||||
|
// Offset label
|
||||||
|
int off = s.tz_offset_minutes;
|
||||||
|
int offH = off / 60;
|
||||||
|
int offM = off % 60;
|
||||||
|
if (offM < 0) offM = -offM;
|
||||||
|
char tz_label[16];
|
||||||
|
if (offM)
|
||||||
|
snprintf(tz_label, sizeof(tz_label), "UTC%s%d:%02d", offH >= 0 ? "+" : "", offH, offM);
|
||||||
|
else
|
||||||
|
snprintf(tz_label, sizeof(tz_label), "UTC%s%d", offH >= 0 ? "+" : "", offH);
|
||||||
|
int tw = text_width(tz_label);
|
||||||
|
int label_w = 90;
|
||||||
|
c.text(bx + 36 + (label_w - tw) / 2, y + 6, tz_label, colors::TEXT_COLOR);
|
||||||
|
|
||||||
|
// [+] button
|
||||||
|
draw_toggle_btn(c, bx + 36 + label_w, y, 36, btn_h, "+", false, accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void settings_draw_about(Canvas& c, SettingsState* st) {
|
static void settings_draw_about(Canvas& c, SettingsState* st) {
|
||||||
@@ -392,6 +421,14 @@ static void settings_persist(SettingsState* st) {
|
|||||||
doc.destroy();
|
doc.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void settings_persist_tz(SettingsState* st) {
|
||||||
|
montauk::toml::Doc doc;
|
||||||
|
doc.init();
|
||||||
|
montauk::config::set_int(&doc, "timezone.offset_minutes", st->desktop->settings.tz_offset_minutes);
|
||||||
|
montauk::config::save("timezone", &doc);
|
||||||
|
doc.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Users tab
|
// Users tab
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -1295,6 +1332,27 @@ static void settings_on_mouse(Window* win, MouseEvent& ev) {
|
|||||||
settings_persist(st);
|
settings_persist(st);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
y += btn_h + 20 + 16;
|
||||||
|
|
||||||
|
// UTC Offset: [-] button
|
||||||
|
if (mx >= bx && mx < bx + 36 && cy >= y && cy < y + btn_h) {
|
||||||
|
if (s.tz_offset_minutes > -720) {
|
||||||
|
s.tz_offset_minutes -= 60;
|
||||||
|
montauk::settz(s.tz_offset_minutes);
|
||||||
|
settings_persist_tz(st);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// UTC Offset: [+] button
|
||||||
|
int label_w = 90;
|
||||||
|
if (mx >= bx + 36 + label_w && mx < bx + 36 + label_w + 36 && cy >= y && cy < y + btn_h) {
|
||||||
|
if (s.tz_offset_minutes < 840) {
|
||||||
|
s.tz_offset_minutes += 60;
|
||||||
|
montauk::settz(s.tz_offset_minutes);
|
||||||
|
settings_persist_tz(st);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else if (st->active_tab == 2) {
|
} else if (st->active_tab == 2) {
|
||||||
// Users tab
|
// Users tab
|
||||||
if (!st->desktop->is_admin) return;
|
if (!st->desktop->is_admin) return;
|
||||||
|
|||||||
@@ -256,6 +256,7 @@ void gui::desktop_init(DesktopState* ds) {
|
|||||||
ds->settings.show_shadows = true;
|
ds->settings.show_shadows = true;
|
||||||
ds->settings.clock_24h = true;
|
ds->settings.clock_24h = true;
|
||||||
ds->settings.ui_scale = 1;
|
ds->settings.ui_scale = 1;
|
||||||
|
ds->settings.tz_offset_minutes = 60; // default CET (UTC+1)
|
||||||
|
|
||||||
// Load per-user desktop settings
|
// Load per-user desktop settings
|
||||||
{
|
{
|
||||||
@@ -311,6 +312,15 @@ void gui::desktop_init(DesktopState* ds) {
|
|||||||
|
|
||||||
doc.destroy();
|
doc.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load timezone config
|
||||||
|
{
|
||||||
|
auto tz = montauk::config::load("timezone");
|
||||||
|
ds->settings.tz_offset_minutes = (int)tz.get_int("timezone.offset_minutes", 60);
|
||||||
|
tz.destroy();
|
||||||
|
}
|
||||||
|
montauk::settz(ds->settings.tz_offset_minutes);
|
||||||
|
|
||||||
montauk::win_setscale(ds->settings.ui_scale);
|
montauk::win_setscale(ds->settings.ui_scale);
|
||||||
|
|
||||||
ds->ctx_menu_open = false;
|
ds->ctx_menu_open = false;
|
||||||
|
|||||||
@@ -113,6 +113,13 @@ namespace Montauk {
|
|||||||
static constexpr uint64_t SYS_BTLIST = 87;
|
static constexpr uint64_t SYS_BTLIST = 87;
|
||||||
static constexpr uint64_t SYS_BTINFO = 88;
|
static constexpr uint64_t SYS_BTINFO = 88;
|
||||||
|
|
||||||
|
// Power management
|
||||||
|
static constexpr uint64_t SYS_SUSPEND = 89;
|
||||||
|
|
||||||
|
// Timezone
|
||||||
|
static constexpr uint64_t SYS_SETTZ = 90;
|
||||||
|
static constexpr uint64_t SYS_GETTZ = 91;
|
||||||
|
|
||||||
// Audio control commands (for SYS_AUDIOCTL)
|
// Audio control commands (for SYS_AUDIOCTL)
|
||||||
static constexpr int AUDIO_CTL_SET_VOLUME = 0;
|
static constexpr int AUDIO_CTL_SET_VOLUME = 0;
|
||||||
static constexpr int AUDIO_CTL_GET_VOLUME = 1;
|
static constexpr int AUDIO_CTL_GET_VOLUME = 1;
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ struct DesktopSettings {
|
|||||||
bool show_shadows; // window shadows on/off
|
bool show_shadows; // window shadows on/off
|
||||||
bool clock_24h; // 24-hour clock format
|
bool clock_24h; // 24-hour clock format
|
||||||
int ui_scale; // 0=Small, 1=Default, 2=Large
|
int ui_scale; // 0=Small, 1=Default, 2=Large
|
||||||
|
int tz_offset_minutes; // timezone offset from UTC in minutes
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DesktopState {
|
struct DesktopState {
|
||||||
|
|||||||
@@ -249,6 +249,10 @@ namespace montauk {
|
|||||||
// Timekeeping (wall-clock)
|
// Timekeeping (wall-clock)
|
||||||
inline void gettime(Montauk::DateTime* out) { syscall1(Montauk::SYS_GETTIME, (uint64_t)out); }
|
inline void gettime(Montauk::DateTime* out) { syscall1(Montauk::SYS_GETTIME, (uint64_t)out); }
|
||||||
|
|
||||||
|
// Timezone offset (total minutes from UTC)
|
||||||
|
inline void settz(int offset_minutes) { syscall1(Montauk::SYS_SETTZ, (uint64_t)(int64_t)offset_minutes); }
|
||||||
|
inline int gettz() { return (int)syscall0(Montauk::SYS_GETTZ); }
|
||||||
|
|
||||||
// Random number generation
|
// Random number generation
|
||||||
inline int64_t getrandom(void* buf, uint32_t len) {
|
inline int64_t getrandom(void* buf, uint32_t len) {
|
||||||
return syscall2(Montauk::SYS_GETRANDOM, (uint64_t)buf, (uint64_t)len);
|
return syscall2(Montauk::SYS_GETRANDOM, (uint64_t)buf, (uint64_t)len);
|
||||||
|
|||||||
Reference in New Issue
Block a user