mirror of
https://github.com/JannisHeydemann/BoredOS.git
synced 2026-05-30 10:26:59 +00:00
src/kernel --> src/
This commit is contained in:
130
src/userland/sys/about.c
Normal file
130
src/userland/sys/about.c
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include "syscall.h"
|
||||
#include "libc/libui.h"
|
||||
#include "libc/stdlib.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
static uint32_t ansi_to_boredos_color(int code) {
|
||||
uint32_t default_color = 0xFFFFFFFF;
|
||||
|
||||
switch (code) {
|
||||
case 0: return default_color;
|
||||
case 30: return 0xFF000000; // Black
|
||||
case 31: return 0xFFFF4444; // Red
|
||||
case 32: return 0xFF6A9955; // Green
|
||||
case 33: return 0xFFFFFF00; // Yellow
|
||||
case 34: return 0xFF569CD6; // Blue
|
||||
case 35: return 0xFFB589D6; // Magenta
|
||||
case 36: return 0xFF4EC9B0; // Cyan
|
||||
case 37: return 0xFFCCCCCC; // White
|
||||
case 90: return 0xFF808080; // Bright Black (Gray)
|
||||
case 91: return 0xFFFF6B6B; // Bright Red
|
||||
case 92: return 0xFF78DE78; // Bright Green
|
||||
case 93: return 0xFFFFFF55; // Bright Yellow
|
||||
case 94: return 0xFF87CEEB; // Bright Blue
|
||||
case 95: return 0xFFFF77FF; // Bright Magenta
|
||||
case 96: return 0xFF66D9EF; // Bright Cyan
|
||||
case 97: return 0xFFFFFFFF; // Bright White
|
||||
default: return default_color;
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_ansi_string(ui_window_t win, int x, int y, const char *str) {
|
||||
uint32_t current_color = 0xFFFFFFFF;
|
||||
int current_x = x;
|
||||
char segment[256];
|
||||
int seg_idx = 0;
|
||||
|
||||
while (*str) {
|
||||
if (*str == '\033' && *(str + 1) == '[') {
|
||||
if (seg_idx > 0) {
|
||||
segment[seg_idx] = 0;
|
||||
ui_draw_string_bitmap(win, current_x, y, segment, current_color);
|
||||
current_x += seg_idx * 8; // Bitmap font is exactly 8px wide
|
||||
seg_idx = 0;
|
||||
}
|
||||
|
||||
str += 2;
|
||||
int code = 0;
|
||||
while (*str >= '0' && *str <= '9') {
|
||||
code = code * 10 + (*str - '0');
|
||||
str++;
|
||||
}
|
||||
if (*str == 'm') {
|
||||
current_color = ansi_to_boredos_color(code);
|
||||
str++;
|
||||
}
|
||||
} else {
|
||||
segment[seg_idx++] = *str++;
|
||||
}
|
||||
}
|
||||
|
||||
if (seg_idx > 0) {
|
||||
segment[seg_idx] = 0;
|
||||
ui_draw_string_bitmap(win, current_x, y, segment, current_color);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_ascii_logo(ui_window_t win, int x, int y) {
|
||||
const char *logo[] = {
|
||||
"\033[35m==================== \033[97m__ ____ ____ \033[0m",
|
||||
"\033[35m=================== \033[97m/ /_ / __ \\/ ___\\\033[0m",
|
||||
"\033[34m================== \033[97m/ __ \\/ / / /\\___ \\\033[0m",
|
||||
"\033[34m================= \033[97m/ /_/ / /_/ /____/ /\033[0m",
|
||||
"\033[36m================ \033[97m/_.___/\\____//_____/ \033[0m",
|
||||
"\033[36m=============== \033[0m",
|
||||
NULL
|
||||
};
|
||||
|
||||
for (int i = 0; logo[i] != NULL; i++) {
|
||||
draw_ansi_string(win, x, y + (i * 10), logo[i]); // Bitmap line height is 10px
|
||||
}
|
||||
}
|
||||
|
||||
static void about_paint(ui_window_t win) {
|
||||
int w = 340;
|
||||
int h = 240;
|
||||
|
||||
// Clear background to prevent alpha-blended text from accumulating on repaints
|
||||
ui_draw_rect(win, 0, 0, w, h, 0xFF1E1E1E);
|
||||
|
||||
int offset_x = 15;
|
||||
int offset_y = 35;
|
||||
|
||||
draw_ascii_logo(win, 14, offset_y);
|
||||
|
||||
int fh = ui_get_font_height();
|
||||
ui_draw_string(win, offset_x, offset_y + 105, "BoredOS 'Retrowave'", 0xFFFFFFFF);
|
||||
ui_draw_string(win, offset_x, offset_y + 105 + fh, "BoredOS Version 1.70", 0xFFFFFFFF);
|
||||
ui_draw_string(win, offset_x, offset_y + 105 + fh*2, "Kernel Version 3.1.0", 0xFFFFFFFF);
|
||||
|
||||
// Copyright
|
||||
ui_draw_string(win, offset_x, offset_y + 105 + fh*3, "(C) 2026 boreddevnl.", 0xFFFFFFFF);
|
||||
ui_draw_string(win, offset_x, offset_y + 105 + fh*4, "All rights reserved.", 0xFFFFFFFF);
|
||||
|
||||
ui_mark_dirty(win, 0, 0, w, h);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
ui_window_t win_about = ui_window_create("About BoredOS", 250, 180, 340, 240);
|
||||
|
||||
about_paint(win_about);
|
||||
|
||||
gui_event_t ev;
|
||||
while (1) {
|
||||
if (ui_get_event(win_about, &ev)) {
|
||||
if (ev.type == GUI_EVENT_PAINT) {
|
||||
about_paint(win_about);
|
||||
} else if (ev.type == GUI_EVENT_CLOSE) {
|
||||
sys_exit(0);
|
||||
}
|
||||
} else {
|
||||
// Avoid high CPU usage
|
||||
sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
12
src/userland/sys/beep.c
Normal file
12
src/userland/sys/beep.c
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
(void)argc; (void)argv;
|
||||
sys_system(14, 392, 400, 0, 0);
|
||||
printf("BEEP!\n");
|
||||
return 0;
|
||||
}
|
||||
263
src/userland/sys/clock.c
Normal file
263
src/userland/sys/clock.c
Normal file
@@ -0,0 +1,263 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include "syscall.h"
|
||||
#include "libui.h"
|
||||
#include "stdlib.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#define WIN_W 250
|
||||
#define WIN_H 250
|
||||
|
||||
#define COLOR_DARK_BG 0xFF1E1E1E
|
||||
#define COLOR_DARK_PANEL 0xFF2D2D2D
|
||||
#define COLOR_DARK_TEXT 0xFFF0F0F0
|
||||
#define COLOR_DARK_BORDER 0xFF3A3A3A
|
||||
#define COLOR_TAB_ACTIVE 0xFF4A90E2
|
||||
#define COLOR_ACCENT 0xFF4A90E2
|
||||
|
||||
typedef enum {
|
||||
TAB_CLOCK,
|
||||
TAB_TIMER,
|
||||
TAB_STOPWATCH
|
||||
} tab_t;
|
||||
|
||||
static ui_window_t win_clock;
|
||||
static tab_t current_tab = TAB_CLOCK;
|
||||
|
||||
// Clock State
|
||||
static int adj_h = -1, adj_m = 0, adj_s = 0;
|
||||
static int cur_h, cur_m, cur_s;
|
||||
static int cur_y, cur_mon, cur_d;
|
||||
|
||||
// Timer State
|
||||
static int timer_h = 0, timer_m = 0, timer_s = 0;
|
||||
static long long timer_end_ticks = 0;
|
||||
static bool timer_running = false;
|
||||
|
||||
// Stopwatch State
|
||||
static long long sw_start_ticks = 0;
|
||||
static long long sw_elapsed_ticks = 0;
|
||||
static bool sw_running = false;
|
||||
|
||||
static void format_time(char *buf, int h, int m, int s) {
|
||||
buf[0] = '0' + (h / 10);
|
||||
buf[1] = '0' + (h % 10);
|
||||
buf[2] = ':';
|
||||
buf[3] = '0' + (m / 10);
|
||||
buf[4] = '0' + (m % 10);
|
||||
buf[5] = ':';
|
||||
buf[6] = '0' + (s / 10);
|
||||
buf[7] = '0' + (s % 10);
|
||||
buf[8] = 0;
|
||||
}
|
||||
|
||||
static void update_rtc_state(void) {
|
||||
int dt[6];
|
||||
int old_s = cur_s;
|
||||
sys_system(11, (uint64_t)dt, 0, 0, 0);
|
||||
cur_y = dt[0]; cur_mon = dt[1]; cur_d = dt[2];
|
||||
cur_h = dt[3]; cur_m = dt[4]; cur_s = dt[5];
|
||||
|
||||
if (adj_h == -1) {
|
||||
adj_h = cur_h; adj_m = cur_m; adj_s = cur_s;
|
||||
} else if (cur_s != old_s) {
|
||||
adj_s++;
|
||||
if (adj_s >= 60) { adj_s = 0; adj_m++; }
|
||||
if (adj_m >= 60) { adj_m = 0; adj_h++; }
|
||||
if (adj_h >= 24) { adj_h = 0; }
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_btn(int x, int y, int w, int h, const char *label, bool highlight) {
|
||||
ui_draw_rounded_rect_filled(win_clock, x, y, w, h, 8, highlight ? COLOR_ACCENT : COLOR_DARK_BORDER);
|
||||
int tw = strlen(label) * 8;
|
||||
ui_draw_string(win_clock, x + (w - tw) / 2, y + (h - 8) / 2, label, COLOR_DARK_TEXT);
|
||||
}
|
||||
|
||||
static void clock_paint(void) {
|
||||
ui_draw_rect(win_clock, 0, 0, WIN_W -5, WIN_H -20, COLOR_DARK_BG);
|
||||
|
||||
const char *tabs[] = {"Clock", "Timer", "Stopwatch"};
|
||||
int tab_w = 70;
|
||||
int total_tabs_w = tab_w * 3 + 16;
|
||||
int start_x = (WIN_W - total_tabs_w) / 2;
|
||||
int tab_y = 8;
|
||||
int tab_h = 24;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
uint32_t bg = (current_tab == i) ? COLOR_TAB_ACTIVE : COLOR_DARK_PANEL;
|
||||
ui_draw_rounded_rect_filled(win_clock, start_x + i * (tab_w + 8), tab_y, tab_w, tab_h, 12, bg);
|
||||
int tw = strlen(tabs[i]) * 8;
|
||||
ui_draw_string(win_clock, start_x + i * (tab_w + 8) + (tab_w - tw) / 2, tab_y + (tab_h - 8) / 2, tabs[i], COLOR_DARK_TEXT);
|
||||
}
|
||||
|
||||
char buf[64];
|
||||
if (current_tab == TAB_CLOCK) {
|
||||
update_rtc_state();
|
||||
ui_draw_string(win_clock, (WIN_W - 12 * 8) / 2, 60, "Current Time", COLOR_DARK_TEXT);
|
||||
format_time(buf, cur_h, cur_m, cur_s);
|
||||
ui_draw_string(win_clock, (WIN_W - 8 * 8) / 2, 80, buf, COLOR_DARK_TEXT);
|
||||
|
||||
ui_draw_string(win_clock, (WIN_W - 12 * 8) / 2, 115, "Adjust Time:", COLOR_DARK_TEXT);
|
||||
|
||||
char adj_buf[32];
|
||||
format_time(buf, adj_h, adj_m, adj_s);
|
||||
adj_buf[0] = buf[0]; adj_buf[1] = buf[1]; adj_buf[2] = ' ';
|
||||
adj_buf[3] = ':'; adj_buf[4] = ' ';
|
||||
adj_buf[5] = buf[3]; adj_buf[6] = buf[4]; adj_buf[7] = ' ';
|
||||
adj_buf[8] = ':'; adj_buf[9] = ' ';
|
||||
adj_buf[10] = buf[6]; adj_buf[11] = buf[7]; adj_buf[12] = 0;
|
||||
int ax = (WIN_W - 12 * 8) / 2;
|
||||
ui_draw_string(win_clock, ax, 150, adj_buf, COLOR_DARK_TEXT);
|
||||
|
||||
// Arrows
|
||||
ui_draw_string(win_clock, ax + 4, 135, "^", COLOR_ACCENT);
|
||||
ui_draw_string(win_clock, ax + 44, 135, "^", COLOR_ACCENT);
|
||||
ui_draw_string(win_clock, ax + 84, 135, "^", COLOR_ACCENT);
|
||||
|
||||
ui_draw_string(win_clock, ax + 4, 165, "v", COLOR_ACCENT);
|
||||
ui_draw_string(win_clock, ax + 44, 165, "v", COLOR_ACCENT);
|
||||
ui_draw_string(win_clock, ax + 84, 165, "v", COLOR_ACCENT);
|
||||
|
||||
draw_btn((WIN_W - 120) / 2, 195, 120, 28, "Apply changes", false);
|
||||
|
||||
} else if (current_tab == TAB_TIMER) {
|
||||
long long now = sys_system(16, 0, 0, 0, 0);
|
||||
int display_h = timer_h, display_m = timer_m, display_s = timer_s;
|
||||
|
||||
if (timer_running) {
|
||||
long long rem = timer_end_ticks - now;
|
||||
if (rem <= 0) {
|
||||
timer_running = false;
|
||||
display_h = display_m = display_s = 0;
|
||||
for(int i=0; i<3; i++) sys_system(14, 440, 200, 0, 0);
|
||||
} else {
|
||||
int s = rem / 60;
|
||||
display_h = s / 3600;
|
||||
display_m = (s % 3600) / 60;
|
||||
display_s = s % 60;
|
||||
}
|
||||
}
|
||||
|
||||
format_time(buf, display_h, display_m, display_s);
|
||||
ui_draw_string(win_clock, (WIN_W - 8 * 8) / 2, 85, buf, COLOR_DARK_TEXT);
|
||||
|
||||
int ax = (WIN_W - 8 * 8) / 2;
|
||||
if (!timer_running) {
|
||||
ui_draw_string(win_clock, ax + 4, 65, "^", COLOR_ACCENT);
|
||||
ui_draw_string(win_clock, ax + 28, 65, "^", COLOR_ACCENT);
|
||||
ui_draw_string(win_clock, ax + 52, 65, "^", COLOR_ACCENT);
|
||||
ui_draw_string(win_clock, ax + 4, 105, "v", COLOR_ACCENT);
|
||||
ui_draw_string(win_clock, ax + 28, 105, "v", COLOR_ACCENT);
|
||||
ui_draw_string(win_clock, ax + 52, 105, "v", COLOR_ACCENT);
|
||||
}
|
||||
|
||||
draw_btn((WIN_W - 120) / 2, 150, 120, 30, timer_running ? "Stop Timer" : "Start Timer", false);
|
||||
|
||||
} else if (current_tab == TAB_STOPWATCH) {
|
||||
long long elapsed = sw_elapsed_ticks;
|
||||
if (sw_running) elapsed += (sys_system(16, 0, 0, 0, 0) - sw_start_ticks);
|
||||
|
||||
int ms_val = (elapsed % 60) * 1000 / 60;
|
||||
int s_val = (elapsed / 60) % 60;
|
||||
int m_val = (elapsed / 3600) % 60;
|
||||
|
||||
buf[0] = '0' + (m_val / 10); buf[1] = '0' + (m_val % 10); buf[2] = ':';
|
||||
buf[3] = '0' + (s_val / 10); buf[4] = '0' + (s_val % 10); buf[5] = '.';
|
||||
buf[6] = '0' + (ms_val / 100); buf[7] = '0' + ((ms_val / 10) % 10); buf[8] = '0' + (ms_val % 10); buf[9] = 0;
|
||||
ui_draw_string(win_clock, (WIN_W - 9 * 8) / 2, 85, buf, COLOR_DARK_TEXT);
|
||||
|
||||
draw_btn(20, 150, 90, 30, sw_running ? "Stop" : "Start", false);
|
||||
draw_btn(140, 150, 90, 30, "Reset", false);
|
||||
}
|
||||
}
|
||||
|
||||
static void clock_click(int x, int y) {
|
||||
int tab_w = 70;
|
||||
int tab_spacing = 8;
|
||||
int total_tabs_w = tab_w * 3 + tab_spacing * 2;
|
||||
int start_x = (WIN_W - total_tabs_w) / 2;
|
||||
int tab_y = 8;
|
||||
int tab_h = 24;
|
||||
|
||||
if (y >= tab_y && y <= tab_y + tab_h) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int tx = start_x + i * (tab_w + tab_spacing);
|
||||
if (x >= tx && x <= tx + tab_w) {
|
||||
current_tab = (tab_t)i;
|
||||
clock_paint();
|
||||
ui_mark_dirty(win_clock, 0, 0, WIN_W, WIN_H);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ax_clock = (WIN_W - 12 * 8) / 2;
|
||||
int ax_timer = (WIN_W - 8 * 8) / 2;
|
||||
|
||||
if (current_tab == TAB_CLOCK) {
|
||||
if (y >= 130 && y <= 150) {
|
||||
if (x >= ax_clock && x <= ax_clock + 32) adj_h = (adj_h + 1) % 24;
|
||||
else if (x >= ax_clock + 33 && x <= ax_clock + 72) adj_m = (adj_m + 1) % 60;
|
||||
else if (x >= ax_clock + 73 && x <= ax_clock + 110) adj_s = (adj_s + 1) % 60;
|
||||
} else if (y >= 160 && y <= 180) {
|
||||
if (x >= ax_clock && x <= ax_clock + 32) adj_h = (adj_h + 23) % 24;
|
||||
else if (x >= ax_clock + 33 && x <= ax_clock + 72) adj_m = (adj_m + 59) % 60;
|
||||
else if (x >= ax_clock + 73 && x <= ax_clock + 110) adj_s = (adj_s + 59) % 60;
|
||||
} else if (x >= (WIN_W - 120) / 2 && x <= (WIN_W + 120) / 2 && y >= 195 && y <= 223) {
|
||||
int dt[6] = {cur_y, cur_mon, cur_d, adj_h, adj_m, adj_s};
|
||||
sys_system(32, (uint64_t)dt, 0, 0, 0);
|
||||
}
|
||||
} else if (current_tab == TAB_TIMER) {
|
||||
if (!timer_running) {
|
||||
if (y >= 60 && y <= 85) {
|
||||
if (x >= ax_timer && x <= ax_timer + 20) timer_h = (timer_h + 1) % 24;
|
||||
else if (x >= ax_timer + 24 && x <= ax_timer + 44) timer_m = (timer_m + 1) % 60;
|
||||
else if (x >= ax_timer + 48 && x <= ax_timer + 68) timer_s = (timer_s + 1) % 60;
|
||||
} else if (y >= 100 && y <= 125) {
|
||||
if (x >= ax_timer && x <= ax_timer + 20) timer_h = (timer_h + 23) % 24;
|
||||
else if (x >= ax_timer + 24 && x <= ax_timer + 44) timer_m = (timer_m + 59) % 60;
|
||||
else if (x >= ax_timer + 48 && x <= ax_timer + 68) timer_s = (timer_s + 59) % 60;
|
||||
}
|
||||
}
|
||||
if (x >= (WIN_W - 120) / 2 && x <= (WIN_W + 120) / 2 && y >= 150 && y <= 180) {
|
||||
if (!timer_running) {
|
||||
if (timer_h > 0 || timer_m > 0 || timer_s > 0) {
|
||||
timer_running = true;
|
||||
timer_end_ticks = sys_system(16, 0, 0, 0, 0) + (long long)(timer_h * 3600 + timer_m * 60 + timer_s) * 60;
|
||||
}
|
||||
} else timer_running = false;
|
||||
}
|
||||
} else if (current_tab == TAB_STOPWATCH) {
|
||||
if (x >= 20 && x <= 110 && y >= 150 && y <= 180) {
|
||||
if (sw_running) { sw_elapsed_ticks += (sys_system(16, 0, 0, 0, 0) - sw_start_ticks); sw_running = false; }
|
||||
else { sw_start_ticks = sys_system(16, 0, 0, 0, 0); sw_running = true; }
|
||||
} else if (x >= 140 && x <= 230 && y >= 150 && y <= 180) {
|
||||
sw_elapsed_ticks = 0; sw_start_ticks = sys_system(16, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
clock_paint();
|
||||
ui_mark_dirty(win_clock, 0, 0, WIN_W, WIN_H);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
win_clock = ui_window_create("Clock", 200, 150, WIN_W, WIN_H);
|
||||
update_rtc_state();
|
||||
clock_paint();
|
||||
ui_mark_dirty(win_clock, 0, 0, WIN_W, WIN_H);
|
||||
gui_event_t ev;
|
||||
long long last_rep = 0;
|
||||
while (1) {
|
||||
if (ui_get_event(win_clock, &ev)) {
|
||||
if (ev.type == GUI_EVENT_PAINT) { clock_paint(); ui_mark_dirty(win_clock, 0, 0, WIN_W, WIN_H); }
|
||||
else if (ev.type == GUI_EVENT_CLICK) clock_click(ev.arg1, ev.arg2);
|
||||
else if (ev.type == GUI_EVENT_CLOSE) sys_exit(0);
|
||||
} else {
|
||||
long long now = sys_system(16, 0, 0, 0, 0);
|
||||
if (now - last_rep >= 6) { clock_paint(); ui_mark_dirty(win_clock, 0, 0, WIN_W, WIN_H); last_rep = now; }
|
||||
sleep(10);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
14
src/userland/sys/crash.c
Normal file
14
src/userland/sys/crash.c
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include "syscall.h"
|
||||
|
||||
int main() {
|
||||
const char* msg = "Attempting to crash via null dereference...\n";
|
||||
sys_write(1, msg, 45);
|
||||
|
||||
volatile int* p = (int*)0;
|
||||
*p = 123;
|
||||
|
||||
return 0;
|
||||
}
|
||||
19
src/userland/sys/meminfo.c
Normal file
19
src/userland/sys/meminfo.c
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
(void)argc; (void)argv;
|
||||
uint64_t mem[2];
|
||||
if (sys_system(15, (uint64_t)mem, 0, 0, 0) == 0) {
|
||||
printf("Memory Info:\n");
|
||||
printf("Total: %d MB\n", (int)(mem[0] / 1024 / 1024));
|
||||
printf("Used: %d MB\n", (int)(mem[1] / 1024 / 1024));
|
||||
printf("Free: %d MB\n", (int)((mem[0] - mem[1]) / 1024 / 1024));
|
||||
} else {
|
||||
printf("Error: Could not retrieve memory info.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
278
src/userland/sys/net.c
Normal file
278
src/userland/sys/net.c
Normal file
@@ -0,0 +1,278 @@
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
static void print_ip(const net_ipv4_address_t* ip) {
|
||||
if (!ip) return;
|
||||
printf("%d.%d.%d.%d", ip->bytes[0], ip->bytes[1], ip->bytes[2], ip->bytes[3]);
|
||||
}
|
||||
|
||||
static int parse_ip(const char* str, net_ipv4_address_t* ip) {
|
||||
int val = 0;
|
||||
int part = 0;
|
||||
const char* p = str;
|
||||
while (*p) {
|
||||
if (*p >= '0' && *p <= '9') {
|
||||
val = val * 10 + (*p - '0');
|
||||
if (val > 255) return -1;
|
||||
} else if (*p == '.') {
|
||||
if (part > 3) return -1;
|
||||
ip->bytes[part++] = (uint8_t)val;
|
||||
val = 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
if (part != 3) return -1;
|
||||
ip->bytes[3] = (uint8_t)val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int resolve_host(const char* host, net_ipv4_address_t* ip) {
|
||||
if (parse_ip(host, ip) == 0) return 0;
|
||||
// Try DNS
|
||||
return sys_dns_lookup(host, ip);
|
||||
}
|
||||
|
||||
static void cmd_dhcp(void) {
|
||||
printf("Acquiring DHCP lease...\n");
|
||||
if (sys_network_dhcp_acquire() == 0) {
|
||||
net_ipv4_address_t ip;
|
||||
sys_network_get_ip(&ip);
|
||||
printf("DHCP Success. IP: ");
|
||||
print_ip(&ip);
|
||||
printf("\n");
|
||||
} else {
|
||||
printf("DHCP Failed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_dnsset(const char* ip_str) {
|
||||
net_ipv4_address_t ip;
|
||||
if (parse_ip(ip_str, &ip) != 0) {
|
||||
printf("Invalid IP: %s\n", ip_str);
|
||||
return;
|
||||
}
|
||||
if (sys_set_dns_server(&ip) == 0) {
|
||||
printf("DNS server set to %s\n", ip_str);
|
||||
} else {
|
||||
printf("Failed to set DNS server.\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_dig(const char* name) {
|
||||
net_ipv4_address_t ip;
|
||||
printf("Resolving %s...\n", name);
|
||||
if (sys_dns_lookup(name, &ip) == 0) {
|
||||
printf("%s resolves to ", name);
|
||||
print_ip(&ip);
|
||||
printf("\n");
|
||||
} else {
|
||||
printf("Failed to resolve %s\n", name);
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_nc(const char* host, const char* port_str, const char* msg) {
|
||||
net_ipv4_address_t ip;
|
||||
if (resolve_host(host, &ip) != 0) {
|
||||
printf("Failed to resolve %s\n", host);
|
||||
return;
|
||||
}
|
||||
uint16_t port = (uint16_t)atoi(port_str);
|
||||
|
||||
printf("Connecting to "); print_ip(&ip); printf(":%d...\n", port);
|
||||
if (sys_tcp_connect(&ip, port) != 0) {
|
||||
printf("Connection failed.\n");
|
||||
return;
|
||||
}
|
||||
printf("Connected.\n");
|
||||
|
||||
if (msg == NULL) msg = "Hello world! (From BoredOS)\n";
|
||||
sys_tcp_send(msg, strlen(msg));
|
||||
|
||||
char buf[1024];
|
||||
int len = sys_tcp_recv(buf, 1023);
|
||||
if (len > 0) {
|
||||
buf[len] = 0;
|
||||
printf("Received: %s\n", buf);
|
||||
}
|
||||
sys_tcp_close();
|
||||
}
|
||||
|
||||
static void cmd_curl(const char* url) {
|
||||
const char* host_start = url;
|
||||
int is_https = 0;
|
||||
if (url[0] == 'h' && url[1] == 't' && url[2] == 't' && url[3] == 'p') {
|
||||
if (url[4] == 's' && url[5] == ':') {
|
||||
is_https = 1;
|
||||
host_start = url + 8;
|
||||
} else if (url[4] == ':') {
|
||||
host_start = url + 7;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_https) {
|
||||
printf("Error: HTTPS is not yet supported in BoredOS. Please use http://\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char hostname[256];
|
||||
int i = 0;
|
||||
while (host_start[i] && host_start[i] != '/' && i < 255) {
|
||||
hostname[i] = host_start[i];
|
||||
i++;
|
||||
}
|
||||
hostname[i] = 0;
|
||||
|
||||
net_ipv4_address_t ip;
|
||||
if (sys_dns_lookup(hostname, &ip) != 0) {
|
||||
printf("Failed to resolve %s\n", hostname);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("Connecting to %s (", hostname); print_ip(&ip); printf("):80...\n");
|
||||
if (sys_tcp_connect(&ip, 80) != 0) {
|
||||
printf("Failed to connect to %s:80\n", hostname);
|
||||
return;
|
||||
}
|
||||
|
||||
const char* path = host_start + i;
|
||||
if (*path == 0) path = "/";
|
||||
|
||||
char request[1024];
|
||||
int req_len = 0;
|
||||
|
||||
const char *r1 = "GET ";
|
||||
const char *r2 = " HTTP/1.1\r\nHost: ";
|
||||
const char *r3 = "\r\nUser-Agent: BoredOS/1.0\r\nAccept: */*\r\nConnection: close\r\n\r\n";
|
||||
|
||||
const char *p;
|
||||
p = r1; while(*p) request[req_len++] = *p++;
|
||||
p = path; while(*p) request[req_len++] = *p++;
|
||||
p = r2; while(*p) request[req_len++] = *p++;
|
||||
p = hostname; while(*p) request[req_len++] = *p++;
|
||||
p = r3; while(*p) request[req_len++] = *p++;
|
||||
request[req_len] = 0;
|
||||
|
||||
sys_tcp_send(request, req_len);
|
||||
|
||||
char buf[4096];
|
||||
int total = 0;
|
||||
while (1) {
|
||||
int len = sys_tcp_recv(buf, 4095);
|
||||
if (len < 0) {
|
||||
printf("\n[Error: Connection error]\n");
|
||||
break;
|
||||
}
|
||||
if (len == 0) break; // End of stream or timeout
|
||||
buf[len] = 0;
|
||||
printf("%s", buf);
|
||||
total += len;
|
||||
if (total > 1000000) {
|
||||
printf("\n[Error: Data limit exceeded]\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
sys_tcp_close();
|
||||
}
|
||||
|
||||
static void cmd_ping(const char* host) {
|
||||
net_ipv4_address_t ip;
|
||||
if (resolve_host(host, &ip) != 0) {
|
||||
printf("Failed to resolve %s\n", host);
|
||||
return;
|
||||
}
|
||||
printf("Pinging %s (", host); print_ip(&ip); printf(")...\n");
|
||||
int successful = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int rtt = sys_icmp_ping(&ip);
|
||||
if (rtt >= 0) {
|
||||
printf("64 bytes from "); print_ip(&ip);
|
||||
printf(": icmp_seq=%d time=%dms\n", i + 1, rtt);
|
||||
successful++;
|
||||
} else {
|
||||
printf("Request timeout for icmp_seq %d\n", i + 1);
|
||||
}
|
||||
for(volatile int d=0; d<1000000; d++);
|
||||
}
|
||||
printf("\n--- %s ping statistics ---\n", host);
|
||||
printf("4 packets transmitted, %d received, %d%% packet loss\n", successful, (4-successful)*25);
|
||||
}
|
||||
|
||||
static void cmd_netinfo(void) {
|
||||
if (!sys_network_is_initialized()) {
|
||||
printf("Network not initialized.\n");
|
||||
return;
|
||||
}
|
||||
net_mac_address_t mac;
|
||||
net_ipv4_address_t ip, gw, dns;
|
||||
char nic_name[64];
|
||||
|
||||
if (sys_network_get_nic_name(nic_name) == 0) {
|
||||
printf("NIC: %s\n", nic_name);
|
||||
} else {
|
||||
printf("NIC: Unknown\n");
|
||||
}
|
||||
|
||||
sys_network_get_mac(&mac);
|
||||
printf("MAC: %X:%X:%X:%X:%X:%X\n", mac.bytes[0], mac.bytes[1], mac.bytes[2], mac.bytes[3], mac.bytes[4], mac.bytes[5]);
|
||||
|
||||
if (sys_network_has_ip()) {
|
||||
sys_network_get_ip(&ip);
|
||||
sys_network_get_gateway(&gw);
|
||||
sys_network_get_dns(&dns);
|
||||
printf("IP: "); print_ip(&ip); printf("\n");
|
||||
printf("GW: "); print_ip(&gw); printf("\n");
|
||||
printf("DNS: "); print_ip(&dns); printf("\n");
|
||||
} else {
|
||||
printf("IP: Not assigned (DHCP in progress or failed)\n");
|
||||
}
|
||||
|
||||
printf("Stats: Link RX=%d, TX=%d, UDP RX=%d\n", sys_network_get_stat(0), sys_network_get_stat(2), sys_network_get_stat(1));
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
printf("Usage: net <command> [args]\n");
|
||||
printf("Commands: dhcp, dnsset <ip>, dig <host>, nc <host> <port>, curl <url>, ping <host>, init, info, unlock\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "init") == 0) {
|
||||
if (sys_network_init() == 0) printf("Network OK\n");
|
||||
else printf("Network Fail\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!sys_network_is_initialized()) {
|
||||
printf("Initializing network...\n");
|
||||
sys_network_init();
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "dhcp") == 0) cmd_dhcp();
|
||||
else if (strcmp(argv[1], "dnsset") == 0) {
|
||||
if (argc < 3) cmd_dnsset("1.1.1.1");
|
||||
else cmd_dnsset(argv[2]);
|
||||
} else if (strcmp(argv[1], "dig") == 0) {
|
||||
if (argc < 3) printf("Usage: net dig <host>\n");
|
||||
else cmd_dig(argv[2]);
|
||||
} else if (strcmp(argv[1], "nc") == 0) {
|
||||
if (argc < 4) printf("Usage: net nc <host> <port> [message]\n");
|
||||
else {
|
||||
const char* msg = (argc >= 5) ? argv[4] : NULL;
|
||||
cmd_nc(argv[2], argv[3], msg);
|
||||
}
|
||||
} else if (strcmp(argv[1], "curl") == 0) {
|
||||
if (argc < 3) printf("Usage: net curl <url>\n");
|
||||
else cmd_curl(argv[2]);
|
||||
} else if (strcmp(argv[1], "ping") == 0) {
|
||||
if (argc < 3) printf("Usage: net ping <host>\n");
|
||||
else cmd_ping(argv[2]);
|
||||
} else if (strcmp(argv[1], "info") == 0) cmd_netinfo();
|
||||
else if (strcmp(argv[1], "unlock") == 0) {
|
||||
sys_network_force_unlock();
|
||||
printf("Network processing lock cleared.\n");
|
||||
} else printf("Unknown command: %s\n", argv[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
33
src/userland/sys/pci_list.c
Normal file
33
src/userland/sys/pci_list.c
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
typedef struct {
|
||||
uint16_t vendor;
|
||||
uint16_t device;
|
||||
uint8_t class_code;
|
||||
uint8_t subclass;
|
||||
} pci_info_t;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
(void)argc; (void)argv;
|
||||
int count = sys_system(17, 0, 0, 0, 0);
|
||||
if (count < 0) {
|
||||
printf("Error: Could not retrieve PCI device count.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("PCI Devices (%d found):\n", count);
|
||||
printf("---------------------------\n");
|
||||
for (int i = 0; i < count; i++) {
|
||||
pci_info_t info;
|
||||
if (sys_system(17, (uint64_t)&info, i, 0, 0) == 0) {
|
||||
printf("[%d] Vendor:%04x Device:%04x Class:%02x Sub:%02x\n",
|
||||
i, info.vendor, info.device, info.class_code, info.subclass);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
70
src/userland/sys/ping.c
Normal file
70
src/userland/sys/ping.c
Normal file
@@ -0,0 +1,70 @@
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
static int parse_ip(const char* str, net_ipv4_address_t* ip) {
|
||||
int val = 0;
|
||||
int part = 0;
|
||||
const char* p = str;
|
||||
while (*p) {
|
||||
if (*p >= '0' && *p <= '9') {
|
||||
val = val * 10 + (*p - '0');
|
||||
if (val > 255) return -1;
|
||||
} else if (*p == '.') {
|
||||
if (part > 3) return -1;
|
||||
ip->bytes[part++] = (uint8_t)val;
|
||||
val = 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
if (part != 3) return -1;
|
||||
ip->bytes[3] = (uint8_t)val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int resolve_host(const char* host, net_ipv4_address_t* ip) {
|
||||
if (parse_ip(host, ip) == 0) return 0;
|
||||
return sys_dns_lookup(host, ip);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
printf("Usage: ping <host>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!sys_network_is_initialized()) {
|
||||
printf("Initializing network...\n");
|
||||
sys_network_init();
|
||||
}
|
||||
|
||||
const char *host = argv[1];
|
||||
net_ipv4_address_t ip;
|
||||
|
||||
if (resolve_host(host, &ip) != 0) {
|
||||
printf("Failed to resolve %s\n", host);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Pinging %s (%d.%d.%d.%d)...\n", host, ip.bytes[0], ip.bytes[1], ip.bytes[2], ip.bytes[3]);
|
||||
|
||||
int successful = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int rtt = sys_icmp_ping(&ip);
|
||||
if (rtt >= 0) {
|
||||
printf("64 bytes from %d.%d.%d.%d: icmp_seq=%d time=%dms\n",
|
||||
ip.bytes[0], ip.bytes[1], ip.bytes[2], ip.bytes[3], i + 1, rtt);
|
||||
successful++;
|
||||
} else {
|
||||
printf("Request timeout for icmp_seq %d\n", i + 1);
|
||||
}
|
||||
// Small delay between pings
|
||||
for(volatile int d=0; d<1000000; d++);
|
||||
}
|
||||
|
||||
printf("\n--- %s ping statistics ---\n", host);
|
||||
printf("4 packets transmitted, %d received, %d%% packet loss\n", successful, (4-successful)*25);
|
||||
|
||||
return successful > 0 ? 0 : 1;
|
||||
}
|
||||
12
src/userland/sys/reboot.c
Normal file
12
src/userland/sys/reboot.c
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
(void)argc; (void)argv;
|
||||
printf("Rebooting...\n");
|
||||
sys_system(12, 0, 0, 0, 0); // SYSTEM_CMD_REBOOT
|
||||
return 0;
|
||||
}
|
||||
12
src/userland/sys/shutdown.c
Normal file
12
src/userland/sys/shutdown.c
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
(void)argc; (void)argv;
|
||||
printf("Shutting down...\n");
|
||||
sys_system(13, 0, 0, 0, 0); // SYSTEM_CMD_SHUTDOWN
|
||||
return 0;
|
||||
}
|
||||
33
src/userland/sys/sweden.c
Normal file
33
src/userland/sys/sweden.c
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
static void beep(int freq, int ms) {
|
||||
sys_system(14, freq, ms, 0, 0);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
(void)argc; (void)argv;
|
||||
printf("Playing Sweden - C418 (Minecraft)...\n");
|
||||
|
||||
// Main melody (simplified pattern)
|
||||
beep(392, 400); // G4
|
||||
beep(440, 400); // A4
|
||||
beep(493, 800); // B4
|
||||
|
||||
beep(440, 400); // A4
|
||||
beep(392, 800); // G4
|
||||
beep(329, 800); // E4
|
||||
|
||||
beep(392, 400); // G4
|
||||
beep(440, 400); // A4
|
||||
beep(493, 800); // B4
|
||||
|
||||
beep(440, 400); // A4
|
||||
beep(392, 1200); // G4
|
||||
|
||||
printf("Done.\n");
|
||||
return 0;
|
||||
}
|
||||
300
src/userland/sys/sysfetch.c
Normal file
300
src/userland/sys/sysfetch.c
Normal file
@@ -0,0 +1,300 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define MAX_ASCII_LINES 32
|
||||
#define MAX_ASCII_WIDTH 80
|
||||
#define MAX_INFO_LINES 10
|
||||
|
||||
static char* strchr(const char *s, int c) {
|
||||
while (*s != (char)c) {
|
||||
if (!*s++) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return (char *)s;
|
||||
}
|
||||
|
||||
static char* strncpy(char *dest, const char *src, size_t n) {
|
||||
size_t i;
|
||||
for (i = 0; i < n && src[i] != '\0'; i++) dest[i] = src[i];
|
||||
for ( ; i < n; i++) dest[i] = '\0';
|
||||
return dest;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char ascii_art_file[256];
|
||||
char user_host_string[64];
|
||||
char separator[64];
|
||||
char os_label[32];
|
||||
char kernel_label[32];
|
||||
char uptime_label[32];
|
||||
char shell_label[32];
|
||||
char memory_label[32];
|
||||
} SysfetchConfig;
|
||||
|
||||
static SysfetchConfig config;
|
||||
static char ascii_lines[MAX_ASCII_LINES][MAX_ASCII_WIDTH];
|
||||
static int ascii_line_count = 0;
|
||||
|
||||
static uint32_t ansi_to_boredos_color(int code) {
|
||||
uint32_t default_color = sys_get_shell_config("default_text_color");
|
||||
if (default_color == 0) default_color = 0xFFCCCCCC;
|
||||
|
||||
switch (code) {
|
||||
case 0: return default_color;
|
||||
case 30: return 0xFF000000; // Black
|
||||
case 31: return 0xFFFF4444; // Red
|
||||
case 32: return 0xFF6A9955; // Green
|
||||
case 33: return 0xFFFFFF00; // Yellow
|
||||
case 34: return 0xFF569CD6; // Blue
|
||||
case 35: return 0xFFB589D6; // Magenta
|
||||
case 36: return 0xFF4EC9B0; // Cyan
|
||||
case 37: return 0xFFCCCCCC; // White
|
||||
case 90: return 0xFF808080; // Bright Black (Gray)
|
||||
case 91: return 0xFFFF6B6B; // Bright Red
|
||||
case 92: return 0xFF78DE78; // Bright Green
|
||||
case 93: return 0xFFFFFF55; // Bright Yellow
|
||||
case 94: return 0xFF87CEEB; // Bright Blue
|
||||
case 95: return 0xFFFF77FF; // Bright Magenta
|
||||
case 96: return 0xFF66D9EF; // Bright Cyan
|
||||
case 97: return 0xFFFFFFFF; // Bright White
|
||||
default: return default_color;
|
||||
}
|
||||
}
|
||||
|
||||
static void printf_ansi(const char *str) {
|
||||
uint32_t original_color = sys_get_shell_config("default_text_color");
|
||||
if (original_color == 0) original_color = 0xFFCCCCCC;
|
||||
|
||||
while (*str) {
|
||||
if (*str == '\033' && *(str + 1) == '[') {
|
||||
str += 2; // Skip escape and [
|
||||
int code = 0;
|
||||
while (*str >= '0' && *str <= '9') {
|
||||
code = code * 10 + (*str - '0');
|
||||
str++;
|
||||
}
|
||||
if (*str == 'm') {
|
||||
sys_set_text_color(ansi_to_boredos_color(code));
|
||||
str++;
|
||||
}
|
||||
} else {
|
||||
char c[2] = {*str, 0};
|
||||
sys_write(1, c, 1);
|
||||
str++;
|
||||
}
|
||||
}
|
||||
sys_set_text_color(original_color);
|
||||
}
|
||||
|
||||
static int strlen_ansi(const char *str) {
|
||||
int len = 0;
|
||||
while (*str) {
|
||||
if (*str == '\033' && *(str + 1) == '[') {
|
||||
str += 2;
|
||||
while (*str && *str != 'm') str++;
|
||||
if (*str == 'm') str++;
|
||||
} else {
|
||||
len++;
|
||||
str++;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static char* trim(char *str) {
|
||||
char *end;
|
||||
while (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r') str++;
|
||||
if (*str == 0) return str;
|
||||
end = str + strlen(str) - 1;
|
||||
while (end > str && (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r')) end--;
|
||||
*(end + 1) = 0;
|
||||
return str;
|
||||
}
|
||||
|
||||
static void set_config_defaults() {
|
||||
strcpy(config.ascii_art_file, "A:/Library/art/boredos.txt");
|
||||
strcpy(config.user_host_string, "root@boredos");
|
||||
strcpy(config.separator, "------------");
|
||||
strcpy(config.os_label, "OS");
|
||||
strcpy(config.kernel_label, "Kernel");
|
||||
strcpy(config.uptime_label, "Uptime");
|
||||
strcpy(config.shell_label, "Shell");
|
||||
strcpy(config.memory_label, "Memory");
|
||||
}
|
||||
|
||||
static void parse_config(char* buffer) {
|
||||
char *line = buffer;
|
||||
while (*line) {
|
||||
char *next_line = strchr(line, '\n');
|
||||
if (next_line) *next_line = 0;
|
||||
|
||||
if (line[0] != '/' && line[0] != '\0') {
|
||||
char *key = line;
|
||||
char *val = strchr(line, '=');
|
||||
if (val) {
|
||||
*val = 0;
|
||||
val++;
|
||||
key = trim(key);
|
||||
val = trim(val);
|
||||
|
||||
if (strcmp(key, "ascii_art_file") == 0) strcpy(config.ascii_art_file, val);
|
||||
else if (strcmp(key, "user_host_string") == 0) strcpy(config.user_host_string, val);
|
||||
else if (strcmp(key, "separator") == 0) strcpy(config.separator, val);
|
||||
else if (strcmp(key, "os_label") == 0) strcpy(config.os_label, val);
|
||||
else if (strcmp(key, "kernel_label") == 0) strcpy(config.kernel_label, val);
|
||||
else if (strcmp(key, "uptime_label") == 0) strcpy(config.uptime_label, val);
|
||||
else if (strcmp(key, "shell_label") == 0) strcpy(config.shell_label, val);
|
||||
else if (strcmp(key, "memory_label") == 0) strcpy(config.memory_label, val);
|
||||
}
|
||||
}
|
||||
|
||||
if (next_line) line = next_line + 1;
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
static void load_config() {
|
||||
set_config_defaults();
|
||||
int fd = sys_open("A:/Library/conf/sysfetch.cfg", "r");
|
||||
if (fd < 0) return;
|
||||
|
||||
char *buffer = malloc(4096);
|
||||
if (!buffer) {
|
||||
sys_close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
int bytes = sys_read(fd, buffer, 4095);
|
||||
sys_close(fd);
|
||||
|
||||
if (bytes > 0) {
|
||||
buffer[bytes] = 0;
|
||||
parse_config(buffer);
|
||||
}
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static void load_ascii_art() {
|
||||
int fd = sys_open(config.ascii_art_file, "r");
|
||||
if (fd < 0) {
|
||||
strcpy(ascii_lines[0], "\033[35m====================== \033[97m__ ____ ____ \033[0m");
|
||||
strcpy(ascii_lines[1], "\033[35m===================== \033[97m/ /_ / __ \\/ ___\\\033[0m");
|
||||
strcpy(ascii_lines[2], "\033[34m==================== \033[97m/ __ \\/ / / /\\___ \\\033[0m");
|
||||
strcpy(ascii_lines[3], "\033[34m=================== \033[97m/ /_/ / /_/ /____/ /\033[0m");
|
||||
strcpy(ascii_lines[4], "\033[36m================== \033[97m/_.___/\\____//_____/ \033[0m");
|
||||
strcpy(ascii_lines[5], "\033[36m================= \033[0m");
|
||||
|
||||
ascii_line_count = 6;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
char *buffer = malloc(4096);
|
||||
if (!buffer) {
|
||||
sys_close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
int bytes = sys_read(fd, buffer, 4095);
|
||||
sys_close(fd);
|
||||
|
||||
if (bytes > 0) {
|
||||
buffer[bytes] = 0;
|
||||
char *line = buffer;
|
||||
while (*line && ascii_line_count < MAX_ASCII_LINES) {
|
||||
char *next_line = strchr(line, '\n');
|
||||
if (next_line) *next_line = 0;
|
||||
|
||||
strncpy(ascii_lines[ascii_line_count], line, MAX_ASCII_WIDTH - 1);
|
||||
ascii_lines[ascii_line_count][MAX_ASCII_WIDTH - 1] = 0;
|
||||
ascii_line_count++;
|
||||
|
||||
if (next_line) line = next_line + 1;
|
||||
else break;
|
||||
}
|
||||
}
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
(void)argc; (void)argv;
|
||||
load_config();
|
||||
load_ascii_art();
|
||||
|
||||
char info_lines[MAX_INFO_LINES][128];
|
||||
char temp_buf[32];
|
||||
int info_line_count = 0;
|
||||
|
||||
if (config.user_host_string[0]) {
|
||||
strcpy(info_lines[info_line_count++], config.user_host_string);
|
||||
}
|
||||
if (config.separator[0]) {
|
||||
strcpy(info_lines[info_line_count++], config.separator);
|
||||
}
|
||||
if (config.os_label[0]) {
|
||||
strcpy(info_lines[info_line_count], config.os_label);
|
||||
strcat(info_lines[info_line_count++], ": BoredOS V1.70 'Retrowave'");
|
||||
}
|
||||
if (config.kernel_label[0]) {
|
||||
strcpy(info_lines[info_line_count], config.kernel_label);
|
||||
strcat(info_lines[info_line_count++], ": Boredkernel V3.1.0 x86_64");
|
||||
}
|
||||
if (config.uptime_label[0]) {
|
||||
uint64_t ticks = sys_system(16, 0, 0, 0, 0);
|
||||
int minutes = ticks / 3600; // 60Hz timer
|
||||
strcpy(info_lines[info_line_count], config.uptime_label);
|
||||
strcat(info_lines[info_line_count], ": ");
|
||||
itoa(minutes, temp_buf);
|
||||
strcat(info_lines[info_line_count], temp_buf);
|
||||
strcat(info_lines[info_line_count++], " mins");
|
||||
}
|
||||
if (config.shell_label[0]) {
|
||||
strcpy(info_lines[info_line_count], config.shell_label);
|
||||
strcat(info_lines[info_line_count++], ": bsh");
|
||||
}
|
||||
if (config.memory_label[0]) {
|
||||
uint64_t mem[2];
|
||||
if (sys_system(15, (uint64_t)mem, 0, 0, 0) == 0) {
|
||||
strcpy(info_lines[info_line_count], config.memory_label);
|
||||
strcat(info_lines[info_line_count], ": ");
|
||||
itoa((int)(mem[1] / 1024 / 1024), temp_buf);
|
||||
strcat(info_lines[info_line_count], temp_buf);
|
||||
strcat(info_lines[info_line_count], "MiB / ");
|
||||
itoa((int)(mem[0] / 1024 / 1024), temp_buf);
|
||||
strcat(info_lines[info_line_count], temp_buf);
|
||||
strcat(info_lines[info_line_count++], "MiB");
|
||||
}
|
||||
}
|
||||
|
||||
int max_lines = (ascii_line_count > info_line_count) ? ascii_line_count : info_line_count;
|
||||
int ascii_width = 0;
|
||||
for (int i = 0; i < ascii_line_count; i++) {
|
||||
int len = strlen_ansi(ascii_lines[i]);
|
||||
if (len > ascii_width) ascii_width = len;
|
||||
}
|
||||
|
||||
for (int i = 0; i < max_lines; i++) {
|
||||
if (i < ascii_line_count) {
|
||||
printf_ansi(ascii_lines[i]);
|
||||
int padding = ascii_width - strlen_ansi(ascii_lines[i]);
|
||||
for(int j=0; j<padding; j++) printf(" ");
|
||||
} else {
|
||||
for(int j=0; j<ascii_width; j++) printf(" ");
|
||||
}
|
||||
|
||||
printf(" ");
|
||||
|
||||
if (i < info_line_count) {
|
||||
printf_ansi(info_lines[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
405
src/userland/sys/telnet.c
Normal file
405
src/userland/sys/telnet.c
Normal file
@@ -0,0 +1,405 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
#include "libc/libui.h"
|
||||
|
||||
|
||||
static int term_cols = 116;
|
||||
static int term_rows = 41;
|
||||
|
||||
#define IAC 255
|
||||
#define DONT 254
|
||||
#define DO 253
|
||||
#define WONT 252
|
||||
#define WILL 251
|
||||
#define SB 250 // sub-negotiation begin
|
||||
#define SE 240 // sub-negotiation end
|
||||
#define GA 249
|
||||
#define EL 248
|
||||
#define EC 247
|
||||
#define AYT 246
|
||||
#define AO 245
|
||||
#define IP 244
|
||||
#define BRK 243
|
||||
#define DM 242
|
||||
#define NOP 241
|
||||
|
||||
// Telnet options
|
||||
#define OPT_ECHO 1
|
||||
#define OPT_SUPPRESS_GA 3
|
||||
#define OPT_STATUS 5
|
||||
#define OPT_TIMING_MARK 6
|
||||
#define OPT_NAWS 31 // Negotiate About Window Size
|
||||
#define OPT_NEW_ENVIRON 39
|
||||
#define OPT_TERMINAL_TYPE 24
|
||||
|
||||
// ─── IAC send helpers ────────────────────────────────────────────────────────
|
||||
|
||||
static void telnet_send(const uint8_t *data, int len) {
|
||||
sys_tcp_send(data, len);
|
||||
}
|
||||
|
||||
static void telnet_send_3(uint8_t a, uint8_t b, uint8_t c) {
|
||||
uint8_t buf[3] = { a, b, c };
|
||||
telnet_send(buf, 3);
|
||||
}
|
||||
|
||||
// Send NAWS subnegotiation with current terminal dimensions
|
||||
static void telnet_send_naws(void) {
|
||||
uint8_t buf[9];
|
||||
buf[0] = IAC;
|
||||
buf[1] = SB;
|
||||
buf[2] = OPT_NAWS;
|
||||
buf[3] = (uint8_t)((term_cols >> 8) & 0xFF);
|
||||
buf[4] = (uint8_t)(term_cols & 0xFF);
|
||||
buf[5] = (uint8_t)((term_rows >> 8) & 0xFF);
|
||||
buf[6] = (uint8_t)(term_rows & 0xFF);
|
||||
buf[7] = IAC;
|
||||
buf[8] = SE;
|
||||
telnet_send(buf, 9);
|
||||
}
|
||||
|
||||
static void telnet_handle_option(uint8_t cmd, uint8_t opt) {
|
||||
switch (cmd) {
|
||||
case DO:
|
||||
if (opt == OPT_NAWS) {
|
||||
telnet_send_3(IAC, WILL, OPT_NAWS);
|
||||
telnet_send_naws();
|
||||
} else if (opt == OPT_TERMINAL_TYPE) {
|
||||
telnet_send_3(IAC, WILL, OPT_TERMINAL_TYPE);
|
||||
} else {
|
||||
telnet_send_3(IAC, WONT, opt);
|
||||
}
|
||||
break;
|
||||
|
||||
case DONT:
|
||||
telnet_send_3(IAC, WONT, opt);
|
||||
break;
|
||||
|
||||
case WILL:
|
||||
if (opt == OPT_SUPPRESS_GA) {
|
||||
// Good — accept suppressed GA (most BBS systems do this)
|
||||
telnet_send_3(IAC, DO, OPT_SUPPRESS_GA);
|
||||
} else if (opt == OPT_ECHO) {
|
||||
// Server will echo chars (remote echo) — accept it
|
||||
telnet_send_3(IAC, DO, OPT_ECHO);
|
||||
} else {
|
||||
// Refuse other server offers
|
||||
telnet_send_3(IAC, DONT, opt);
|
||||
}
|
||||
break;
|
||||
|
||||
case WONT:
|
||||
telnet_send_3(IAC, DONT, opt);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void telnet_handle_sb_terminal_type(const uint8_t *sb_data, int sb_len) {
|
||||
if (sb_len < 1 || sb_data[0] != 1) return; // 1 = SEND
|
||||
uint8_t reply[12];
|
||||
int i = 0;
|
||||
reply[i++] = IAC;
|
||||
reply[i++] = SB;
|
||||
reply[i++] = OPT_TERMINAL_TYPE;
|
||||
reply[i++] = 0; // IS
|
||||
reply[i++] = 'A'; reply[i++] = 'N'; reply[i++] = 'S'; reply[i++] = 'I';
|
||||
reply[i++] = IAC;
|
||||
reply[i++] = SE;
|
||||
telnet_send(reply, i);
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef enum {
|
||||
TS_DATA = 0,
|
||||
TS_IAC,
|
||||
TS_CMD,
|
||||
TS_OPT,
|
||||
TS_SB,
|
||||
TS_SB_IAC
|
||||
} TelnetParseState;
|
||||
|
||||
static TelnetParseState ts_state = TS_DATA;
|
||||
static uint8_t ts_cmd = 0;
|
||||
static uint8_t ts_sb_opt = 0;
|
||||
static uint8_t ts_sb_buf[256];
|
||||
static int ts_sb_pos = 0;
|
||||
|
||||
// Output buffer — accumulate non-IAC bytes to write in bulk
|
||||
static char out_buf[4096];
|
||||
static int out_pos = 0;
|
||||
|
||||
static void flush_out(void) {
|
||||
if (out_pos > 0) {
|
||||
sys_write(1, out_buf, out_pos);
|
||||
out_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void out_char(char c) {
|
||||
if (out_pos >= (int)(sizeof(out_buf) - 1)) {
|
||||
flush_out();
|
||||
}
|
||||
out_buf[out_pos++] = c;
|
||||
}
|
||||
|
||||
// Process a chunk of raw TCP data from server. Returns false if connection lost.
|
||||
static int telnet_process(const uint8_t *data, int len) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
uint8_t b = data[i];
|
||||
|
||||
switch (ts_state) {
|
||||
case TS_DATA:
|
||||
if (b == IAC) {
|
||||
ts_state = TS_IAC;
|
||||
} else {
|
||||
// Pass directly to display
|
||||
out_char((char)b);
|
||||
}
|
||||
break;
|
||||
|
||||
case TS_IAC:
|
||||
switch (b) {
|
||||
case IAC:
|
||||
// Escaped IAC — literal 0xFF
|
||||
out_char((char)0xFF);
|
||||
ts_state = TS_DATA;
|
||||
break;
|
||||
case DO: case DONT: case WILL: case WONT:
|
||||
ts_cmd = b;
|
||||
ts_state = TS_OPT;
|
||||
break;
|
||||
case SB:
|
||||
ts_sb_pos = 0;
|
||||
ts_state = TS_CMD;
|
||||
break;
|
||||
case GA: case NOP: case DM: case BRK: case IP:
|
||||
case AO: case AYT: case EC: case EL:
|
||||
ts_state = TS_DATA;
|
||||
break;
|
||||
default:
|
||||
ts_state = TS_DATA;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case TS_CMD:
|
||||
ts_sb_opt = b;
|
||||
ts_sb_pos = 0;
|
||||
ts_state = TS_SB;
|
||||
break;
|
||||
|
||||
case TS_OPT:
|
||||
flush_out();
|
||||
telnet_handle_option(ts_cmd, b);
|
||||
ts_state = TS_DATA;
|
||||
break;
|
||||
|
||||
case TS_SB:
|
||||
if (b == IAC) {
|
||||
ts_state = TS_SB_IAC;
|
||||
} else {
|
||||
if (ts_sb_pos < (int)sizeof(ts_sb_buf) - 1) {
|
||||
ts_sb_buf[ts_sb_pos++] = b;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TS_SB_IAC:
|
||||
if (b == SE) {
|
||||
flush_out();
|
||||
if (ts_sb_opt == OPT_TERMINAL_TYPE) {
|
||||
telnet_handle_sb_terminal_type(ts_sb_buf, ts_sb_pos);
|
||||
}
|
||||
ts_state = TS_DATA;
|
||||
} else if (b == IAC) {
|
||||
if (ts_sb_pos < (int)sizeof(ts_sb_buf) - 1) {
|
||||
ts_sb_buf[ts_sb_pos++] = IAC;
|
||||
}
|
||||
ts_state = TS_SB;
|
||||
} else {
|
||||
ts_state = TS_DATA;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
flush_out();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int map_key(char c, uint8_t *key_out) {
|
||||
if (c == 29) {
|
||||
// Ctrl+]
|
||||
return -1;
|
||||
}
|
||||
if (c == 17) {
|
||||
// UP arrow
|
||||
key_out[0] = 0x1b; key_out[1] = '['; key_out[2] = 'A';
|
||||
return 3;
|
||||
}
|
||||
if (c == 18) {
|
||||
// DOWN arrow
|
||||
key_out[0] = 0x1b; key_out[1] = '['; key_out[2] = 'B';
|
||||
return 3;
|
||||
}
|
||||
if (c == 20) {
|
||||
// RIGHT arrow
|
||||
key_out[0] = 0x1b; key_out[1] = '['; key_out[2] = 'C';
|
||||
return 3;
|
||||
}
|
||||
if (c == 19) {
|
||||
// LEFT arrow
|
||||
key_out[0] = 0x1b; key_out[1] = '['; key_out[2] = 'D';
|
||||
return 3;
|
||||
}
|
||||
if (c == '\n') {
|
||||
// Enter
|
||||
key_out[0] = '\r'; key_out[1] = '\n';
|
||||
return 2;
|
||||
}
|
||||
if (c == '\b') {
|
||||
// Backspace
|
||||
key_out[0] = '\x7f';
|
||||
return 1;
|
||||
}
|
||||
// Normal printable character
|
||||
key_out[0] = (uint8_t)c;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int my_atoi(const char *s) {
|
||||
int v = 0;
|
||||
while (*s >= '0' && *s <= '9') { v = v*10 + (*s - '0'); s++; }
|
||||
return v;
|
||||
}
|
||||
|
||||
static int parse_ip(const char *s, net_ipv4_address_t *ip) {
|
||||
int part = 0, val = 0;
|
||||
while (*s) {
|
||||
if (*s >= '0' && *s <= '9') {
|
||||
val = val*10 + (*s - '0');
|
||||
if (val > 255) return -1;
|
||||
} else if (*s == '.') {
|
||||
if (part > 3) return -1;
|
||||
ip->bytes[part++] = (uint8_t)val;
|
||||
val = 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
if (part != 3) return -1;
|
||||
ip->bytes[3] = (uint8_t)val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
printf("Usage: telnet <host> [port]\n");
|
||||
printf(" Connect to a Telnet BBS or server.\n");
|
||||
printf(" Default port: 23\n");
|
||||
printf(" Press Ctrl+] to disconnect.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *host = argv[1];
|
||||
int port = (argc >= 3) ? my_atoi(argv[2]) : 23;
|
||||
if (port <= 0 || port > 65535) port = 23;
|
||||
|
||||
if (!sys_network_is_initialized()) {
|
||||
printf("Initializing network...\n");
|
||||
sys_network_init();
|
||||
}
|
||||
if (!sys_network_has_ip()) {
|
||||
printf("Acquiring DHCP...\n");
|
||||
if (sys_network_dhcp_acquire() != 0) {
|
||||
printf("DHCP failed.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
net_ipv4_address_t ip;
|
||||
if (parse_ip(host, &ip) != 0) {
|
||||
printf("Resolving %s...\n", host);
|
||||
if (sys_dns_lookup(host, &ip) != 0) {
|
||||
printf("Failed to resolve: %s\n", host);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Connecting to %s:%d...\n", host, port);
|
||||
if (sys_tcp_connect(&ip, (uint16_t)port) != 0) {
|
||||
printf("Connection failed.\n");
|
||||
return 1;
|
||||
}
|
||||
printf("Connected. Press Ctrl+] to disconnect.\n\n");
|
||||
|
||||
sys_system(41, 1, 0, 0, 0); // SYSTEM_CMD_SET_RAW_MODE = 1
|
||||
|
||||
uint8_t recv_buf[4096];
|
||||
int total = 0;
|
||||
int idle_count = 0;
|
||||
int connected = 1;
|
||||
|
||||
while (connected) {
|
||||
gui_event_t ev;
|
||||
while (ui_get_event(0, &ev)) { // win=0 for console proc
|
||||
if (ev.type == GUI_EVENT_KEY) {
|
||||
uint8_t key_data[16];
|
||||
int key_len = map_key((char)ev.arg1, key_data);
|
||||
if (key_len < 0) {
|
||||
connected = 0;
|
||||
break;
|
||||
}
|
||||
telnet_send(key_data, key_len);
|
||||
}
|
||||
}
|
||||
if (!connected) break;
|
||||
|
||||
int len = sys_tcp_recv_nb(recv_buf, sizeof(recv_buf) - 1);
|
||||
if (len < 0) {
|
||||
printf("\r\n[Connection error]\r\n");
|
||||
connected = 0;
|
||||
break;
|
||||
}
|
||||
if (len == 0) {
|
||||
idle_count++;
|
||||
// Don't timeout too fast if we are just waiting for user input
|
||||
if (idle_count > 10000000) {
|
||||
printf("\r\n[Connection timed out]\r\n");
|
||||
connected = 0;
|
||||
break;
|
||||
}
|
||||
sys_yield();
|
||||
continue;
|
||||
}
|
||||
|
||||
idle_count = 0;
|
||||
total += len;
|
||||
|
||||
if (total > 10000000) {
|
||||
printf("\r\n[Data limit reached]\r\n");
|
||||
connected = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!telnet_process(recv_buf, len)) {
|
||||
connected = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Disable raw mode before exiting
|
||||
sys_system(41, 0, 0, 0, 0);
|
||||
|
||||
sys_tcp_close();
|
||||
printf("\r\n[Telnet session ended]\r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
19
src/userland/sys/uptime.c
Normal file
19
src/userland/sys/uptime.c
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
(void)argc; (void)argv;
|
||||
uint64_t ticks = sys_system(16, 0, 0, 0, 0); // SYSTEM_CMD_UPTIME
|
||||
uint64_t seconds = ticks / 100; // 100Hz timer assumed
|
||||
uint64_t minutes = seconds / 60;
|
||||
uint64_t hours = minutes / 60;
|
||||
uint64_t days = hours / 24;
|
||||
|
||||
printf("Uptime: %d days, %d hours, %d minutes, %d seconds\n",
|
||||
(int)days, (int)(hours % 24), (int)(minutes % 60), (int)(seconds % 60));
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user