feat: wi-fi - expand support and fix issues, add GUI components
This commit is contained in:
@@ -182,6 +182,13 @@ namespace montauk::abi {
|
||||
// Paginated directory read (path, names, max, startIndex)
|
||||
static constexpr uint64_t SYS_READDIR_AT = 136;
|
||||
|
||||
// Set adapter BD_ADDR (6-byte buffer, addr[0] = LSB)
|
||||
static constexpr uint64_t SYS_BTSETADDR = 137;
|
||||
|
||||
// List bonded (paired) devices / forget a bond
|
||||
static constexpr uint64_t SYS_BTBONDS = 138;
|
||||
static constexpr uint64_t SYS_BTFORGET = 139;
|
||||
|
||||
/* Sdr.hpp -- software-defined radio receive API */
|
||||
static constexpr uint64_t SYS_SDR_COUNT = 140; // number of receivers
|
||||
static constexpr uint64_t SYS_SDR_INFO = 141; // (index, SdrDeviceInfo*)
|
||||
@@ -192,13 +199,36 @@ namespace montauk::abi {
|
||||
static constexpr uint64_t SYS_SDR_READ = 146; // (handle, buf, len) -> bytes
|
||||
static constexpr uint64_t SYS_SDR_SETPARAM = 147; // (handle, param, value)
|
||||
static constexpr uint64_t SYS_SDR_GETPARAM = 148; // (handle, param) -> value
|
||||
|
||||
// CPU power/thermal status
|
||||
static constexpr uint64_t SYS_POWERINFO = 149; // (PowerInfo*) -> 0, -1 unsupported
|
||||
|
||||
// Framebuffer page flip (double-buffered scanout)
|
||||
static constexpr uint64_t SYS_FBFLIP = 150;
|
||||
|
||||
// Absolute path of the running executable (for argv[0]).
|
||||
static constexpr uint64_t SYS_GETEXECPATH = 151; // (index, flags) -> new front index; index=-1 queries support (1/0); flags bit0 = wait vsync
|
||||
|
||||
// Path metadata (size, timestamps, mode). (const char* path, FileStat* out) -> 0, -1 on error/unsupported.
|
||||
static constexpr uint64_t SYS_STAT = 152;
|
||||
static constexpr uint64_t SYS_SETUNIXTIME = 153;
|
||||
|
||||
// Display/modesetting control
|
||||
static constexpr uint64_t SYS_DISPLAYINFO = 154;
|
||||
static constexpr uint64_t SYS_DISPLAYMODES = 155;
|
||||
static constexpr uint64_t SYS_DISPLAYSETMODE = 156;
|
||||
static constexpr uint64_t SYS_DISPLAYBRIGHTNESS = 157;
|
||||
|
||||
// Wi-Fi adapter control
|
||||
static constexpr uint64_t SYS_WIFI_SCAN = 158; // (WifiNetwork*, maxCount, timeoutMs) -> count
|
||||
static constexpr uint64_t SYS_WIFI_INFO = 159; // (WifiInfo*) -> 0, -1 if absent
|
||||
static constexpr uint64_t SYS_WIFI_CONNECT = 160; // (ssid, password) -> 0, <0 on error
|
||||
static constexpr uint64_t SYS_WIFI_DISCONNECT = 161; // () -> 0
|
||||
static constexpr uint64_t SYS_WIFI_SCAN_START = 162; // (timeoutMs) -> 0 started, 1 busy, -1 no adapter
|
||||
static constexpr uint64_t SYS_WIFI_RESULTS = 163; // (WifiNetwork*, maxCount) -> count, no radio work
|
||||
static constexpr uint64_t SYS_WIFI_CONNECT_ASYNC = 164; // (ssid, password) -> 0 accepted, <0 on error
|
||||
static constexpr uint64_t SYS_NETIFS = 165; // (NetIfInfo*, maxCount) -> count
|
||||
|
||||
// Tunable parameters (for SYS_SDR_SETPARAM / SYS_SDR_GETPARAM).
|
||||
static constexpr int SDR_PARAM_FREQ = 0; // center frequency, Hz
|
||||
static constexpr int SDR_PARAM_SAMPLE_RATE = 1; // sample rate, Hz
|
||||
@@ -238,7 +268,7 @@ namespace montauk::abi {
|
||||
static constexpr int AUDIO_CTL_PAUSE = 3;
|
||||
static constexpr int AUDIO_CTL_GET_OUTPUT = 4; // 0=HDA, 1=Bluetooth
|
||||
static constexpr int AUDIO_CTL_SET_OUTPUT = 5; // Switch audio output
|
||||
static constexpr int AUDIO_CTL_BT_STATUS = 6; // Get Bluetooth status
|
||||
static constexpr int AUDIO_CTL_BT_STATUS = 6; // 0=unavailable, 1=setup, 2=ready
|
||||
static constexpr int AUDIO_CTL_SET_MASTER_VOLUME = 7; // 0-100
|
||||
static constexpr int AUDIO_CTL_GET_MASTER_VOLUME = 8;
|
||||
static constexpr int AUDIO_CTL_SET_MUTE = 9; // 0/1, per-stream
|
||||
@@ -279,6 +309,17 @@ namespace montauk::abi {
|
||||
uint8_t Second;
|
||||
};
|
||||
|
||||
// Path metadata returned by SYS_STAT. Timestamps are UTC unix seconds;
|
||||
// a filesystem that does not record a given time reports it as 0.
|
||||
struct FileStat {
|
||||
uint64_t size; // file size in bytes
|
||||
int64_t mtime; // last data modification time
|
||||
int64_t ctime; // last inode (metadata) change time
|
||||
int64_t atime; // last access time
|
||||
uint32_t mode; // ext2/POSIX mode bits (type + permissions)
|
||||
uint32_t isDir; // 1 if the entry is a directory, else 0
|
||||
};
|
||||
|
||||
struct FbInfo {
|
||||
uint64_t width;
|
||||
uint64_t height;
|
||||
@@ -321,7 +362,7 @@ namespace montauk::abi {
|
||||
uint32_t capabilities;
|
||||
uint32_t modeCount;
|
||||
int32_t currentMode;
|
||||
int32_t brightness;
|
||||
int32_t brightness; // 0..100, or -1 when unavailable
|
||||
uint16_t deviceId;
|
||||
uint8_t generation;
|
||||
uint8_t connectorType;
|
||||
@@ -509,6 +550,12 @@ namespace montauk::abi {
|
||||
char name[64];
|
||||
};
|
||||
|
||||
// Bluetooth bonded (paired) device (returned by SYS_BTBONDS)
|
||||
struct BtBondInfo {
|
||||
uint8_t bdAddr[6];
|
||||
uint8_t _pad[2];
|
||||
};
|
||||
|
||||
// Software-defined radio receiver description (returned by SYS_SDR_INFO).
|
||||
struct SdrDeviceInfo {
|
||||
char name[64]; // e.g. "Realtek RTL2832U"
|
||||
@@ -527,12 +574,113 @@ namespace montauk::abi {
|
||||
uint32_t _pad2;
|
||||
};
|
||||
|
||||
// Wi-Fi security suites reported in WifiNetwork.security.
|
||||
static constexpr uint8_t WIFI_SEC_OPEN = 0;
|
||||
static constexpr uint8_t WIFI_SEC_WEP = 1;
|
||||
static constexpr uint8_t WIFI_SEC_WPA = 2;
|
||||
static constexpr uint8_t WIFI_SEC_WPA2 = 3;
|
||||
static constexpr uint8_t WIFI_SEC_WPA3 = 4;
|
||||
|
||||
// Adapter states reported in WifiInfo.state.
|
||||
static constexpr uint8_t WIFI_STATE_ABSENT = 0; // no device
|
||||
static constexpr uint8_t WIFI_STATE_DETECTED = 1; // waiting for firmware load
|
||||
static constexpr uint8_t WIFI_STATE_BOOTING = 2;
|
||||
static constexpr uint8_t WIFI_STATE_RUNNING = 3;
|
||||
static constexpr uint8_t WIFI_STATE_ERROR = 4;
|
||||
static constexpr uint8_t WIFI_STATE_RFKILL = 5; // radio disabled in hardware
|
||||
|
||||
// One scanned network (returned by SYS_WIFI_SCAN).
|
||||
struct WifiNetwork {
|
||||
char ssid[36]; // NUL-terminated; empty for hidden networks
|
||||
uint8_t bssid[6];
|
||||
uint8_t channel;
|
||||
int8_t rssi; // dBm
|
||||
uint8_t band; // 0 = 2.4 GHz, 1 = 5 GHz
|
||||
uint8_t security; // WIFI_SEC_*
|
||||
uint16_t beaconInterval; // TU
|
||||
};
|
||||
|
||||
// Association progress reported in WifiInfo.connState.
|
||||
static constexpr uint32_t WIFI_CONN_IDLE = 0;
|
||||
static constexpr uint32_t WIFI_CONN_CONTEXTS_UP = 1;
|
||||
static constexpr uint32_t WIFI_CONN_AUTHENTICATING = 2;
|
||||
static constexpr uint32_t WIFI_CONN_AUTHENTICATED = 3;
|
||||
static constexpr uint32_t WIFI_CONN_ASSOCIATING = 4;
|
||||
static constexpr uint32_t WIFI_CONN_ASSOCIATED = 5;
|
||||
static constexpr uint32_t WIFI_CONN_HANDSHAKING = 6;
|
||||
static constexpr uint32_t WIFI_CONN_CONNECTED = 7;
|
||||
static constexpr uint32_t WIFI_CONN_FAILED = 8;
|
||||
|
||||
// Negative results from SYS_WIFI_CONNECT.
|
||||
static constexpr int WIFI_ERR_NO_ADAPTER = -1; // no adapter, or not ready
|
||||
static constexpr int WIFI_ERR_NOT_FOUND = -2; // SSID absent from the scan
|
||||
static constexpr int WIFI_ERR_NEED_KEY = -3; // encrypted, no passphrase
|
||||
static constexpr int WIFI_ERR_UNSUPPORTED = -4; // WPA3-SAE, WEP, enterprise
|
||||
static constexpr int WIFI_ERR_AUTH = -5; // key exchange rejected
|
||||
static constexpr int WIFI_ERR_TIMEOUT = -6; // AP never answered
|
||||
static constexpr int WIFI_ERR_FAILED = -7; // anything else
|
||||
|
||||
// Adapter status (returned by SYS_WIFI_INFO).
|
||||
struct WifiInfo {
|
||||
uint8_t mac[6];
|
||||
uint8_t present; // 1 if a supported device was found
|
||||
uint8_t state; // WIFI_STATE_*
|
||||
uint8_t scanning;
|
||||
uint8_t bands; // bit0 = 2.4 GHz, bit1 = 5 GHz
|
||||
uint16_t channels; // usable channels after regulatory filtering
|
||||
char fwVersion[32];
|
||||
uint64_t rxPackets;
|
||||
uint32_t fwErrors;
|
||||
uint32_t connState; // WIFI_CONN_*
|
||||
uint64_t txPackets;
|
||||
char ssid[36]; // network joined, empty when disconnected
|
||||
uint8_t bssid[6];
|
||||
uint8_t connected; // 1 once the link can carry IP traffic
|
||||
uint8_t channel;
|
||||
int32_t lastError; // WIFI_ERR_* from the last async join, 0 = none
|
||||
uint32_t scanGeneration; // bumped every time a scan finishes
|
||||
uint8_t joining; // 1 while an async join is in flight
|
||||
uint8_t _pad[3];
|
||||
};
|
||||
|
||||
// Link-layer interface kinds reported in NetIfInfo.kind.
|
||||
static constexpr uint8_t NETIF_KIND_ETHERNET = 0;
|
||||
static constexpr uint8_t NETIF_KIND_WIRELESS = 1;
|
||||
|
||||
// One registered link-layer interface (returned by SYS_NETIFS). The IP
|
||||
// configuration is global to the stack, so it belongs to whichever
|
||||
// interface reports active = 1.
|
||||
struct NetIfInfo {
|
||||
char name[16]; // "eth0", "wlan0"
|
||||
uint8_t mac[6];
|
||||
uint8_t kind; // NETIF_KIND_*
|
||||
uint8_t linkUp;
|
||||
uint8_t active; // 1 if this is the interface carrying traffic
|
||||
uint8_t _pad[3];
|
||||
};
|
||||
|
||||
struct ThermalInfo {
|
||||
char name[32]; // short zone name (e.g. "THRM", "TZ00")
|
||||
int32_t temperature; // tenths of degrees Celsius, or -1 if unavailable
|
||||
uint32_t _pad;
|
||||
};
|
||||
|
||||
// CPU power/thermal snapshot (returned by SYS_POWERINFO)
|
||||
struct PowerInfo {
|
||||
uint8_t hwpActive; // hardware P-state scaling enabled
|
||||
uint8_t throttling; // thermal governor currently limiting frequency
|
||||
uint8_t tempC; // package temperature, degrees C (0 = unknown)
|
||||
uint8_t tjMaxC; // hardware throttle temperature
|
||||
uint8_t highestPerf; // HWP performance range (ratio units)
|
||||
uint8_t lowestPerf;
|
||||
uint8_t curMaxPerf; // thermal governor's current ceiling
|
||||
uint8_t epp; // energy/perf preference (0=perf, 255=power)
|
||||
uint32_t baseMHz; // nominal base frequency (0 = unknown)
|
||||
uint32_t maxMHz; // max turbo frequency
|
||||
uint32_t effMHz; // measured average active frequency
|
||||
uint32_t apIdleHint; // MWAIT hint used for AP deep idle
|
||||
};
|
||||
|
||||
struct ProcInfo {
|
||||
int32_t pid;
|
||||
int32_t parentPid;
|
||||
|
||||
@@ -161,24 +161,58 @@ struct Canvas {
|
||||
|
||||
// ---- Text ----
|
||||
|
||||
void text_bitmap(int x, int y, const char* str, Color c, int scale = 1) {
|
||||
if (!str || !font_data || scale <= 0) return;
|
||||
uint32_t pixel = c.to_pixel();
|
||||
int cx = x;
|
||||
for (int i = 0; str[i]; i++, cx += FONT_WIDTH * scale) {
|
||||
const uint8_t* glyph =
|
||||
&font_data[(unsigned char)str[i] * FONT_HEIGHT];
|
||||
for (int row = 0; row < FONT_HEIGHT; row++) {
|
||||
uint8_t bits = glyph[row];
|
||||
if (!bits) continue;
|
||||
for (int col = 0; col < FONT_WIDTH; col++) {
|
||||
if (!(bits & (0x80 >> col))) continue;
|
||||
int px = cx + col * scale;
|
||||
int py = y + row * scale;
|
||||
for (int sy = 0; sy < scale; sy++) {
|
||||
int dy = py + sy;
|
||||
if (dy < 0 || dy >= h) continue;
|
||||
for (int sx = 0; sx < scale; sx++) {
|
||||
int dx = px + sx;
|
||||
if (dx >= 0 && dx < w) pixels[dy * w + dx] = pixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void text(int x, int y, const char* str, Color c) {
|
||||
if (fonts::system_font && fonts::system_font->valid) {
|
||||
fonts::system_font->draw_to_buffer(pixels, w, h, x, y, str, c, fonts::UI_SIZE);
|
||||
if (fonts::system_font->draw_to_buffer(
|
||||
pixels, w, h, x, y, str, c, fonts::UI_SIZE))
|
||||
return;
|
||||
}
|
||||
text_bitmap(x, y, str, c);
|
||||
}
|
||||
|
||||
void text_2x(int x, int y, const char* str, Color c) {
|
||||
if (fonts::system_font && fonts::system_font->valid) {
|
||||
fonts::system_font->draw_to_buffer(pixels, w, h, x, y, str, c, fonts::LARGE_SIZE);
|
||||
if (fonts::system_font->draw_to_buffer(
|
||||
pixels, w, h, x, y, str, c, fonts::LARGE_SIZE))
|
||||
return;
|
||||
}
|
||||
text_bitmap(x, y, str, c, 2);
|
||||
}
|
||||
|
||||
void text_mono(int x, int y, const char* str, Color c) {
|
||||
if (fonts::mono && fonts::mono->valid) {
|
||||
fonts::mono->draw_to_buffer(pixels, w, h, x, y, str, c, fonts::TERM_SIZE);
|
||||
return;
|
||||
if (fonts::mono->draw_to_buffer(
|
||||
pixels, w, h, x, y, str, c, fonts::TERM_SIZE))
|
||||
return;
|
||||
}
|
||||
text(x, y, str, c);
|
||||
text_bitmap(x, y, str, c);
|
||||
}
|
||||
|
||||
// ---- Icons ----
|
||||
|
||||
@@ -15,9 +15,8 @@
|
||||
|
||||
namespace gui {
|
||||
|
||||
static constexpr int MAX_WINDOWS = 8;
|
||||
static constexpr int MAX_WINDOWS = 32;
|
||||
static constexpr int PANEL_HEIGHT = 32;
|
||||
static constexpr int MAX_LAUNCHER_ITEMS = 64;
|
||||
|
||||
enum DesktopItemSection : uint8_t {
|
||||
DESKTOP_ITEM_SECTION_HIDDEN = 0,
|
||||
@@ -111,9 +110,10 @@ struct DesktopState {
|
||||
bool launcher_open;
|
||||
char launcher_query[64];
|
||||
int launcher_query_len;
|
||||
LauncherItem launcher_items[MAX_LAUNCHER_ITEMS];
|
||||
LauncherItem* launcher_items; // dynamically grown
|
||||
int launcher_item_count;
|
||||
int launcher_results[MAX_LAUNCHER_ITEMS];
|
||||
int launcher_item_capacity;
|
||||
int* launcher_results; // indices into launcher_items, same capacity
|
||||
int launcher_result_count;
|
||||
int launcher_selected;
|
||||
int launcher_scroll;
|
||||
@@ -122,7 +122,6 @@ struct DesktopState {
|
||||
|
||||
SvgIcon icon_terminal;
|
||||
SvgIcon icon_filemanager;
|
||||
SvgIcon icon_sysinfo;
|
||||
SvgIcon icon_appmenu;
|
||||
SvgIcon icon_folder;
|
||||
SvgIcon icon_file;
|
||||
@@ -181,6 +180,48 @@ struct DesktopState {
|
||||
uint64_t net_cfg_last_poll;
|
||||
Rect net_icon_rect;
|
||||
|
||||
// Registered link-layer interfaces. The IP configuration is global to the
|
||||
// stack, so the wired and wireless popups use `active` to decide which of
|
||||
// them owns the address currently on screen.
|
||||
static constexpr int MAX_NETIFS = 4;
|
||||
montauk::abi::NetIfInfo netifs[MAX_NETIFS];
|
||||
int netif_count;
|
||||
bool eth_present;
|
||||
|
||||
// ---- Wi-Fi -------------------------------------------------------------
|
||||
// The panel entry exists only when a supported adapter is present.
|
||||
static constexpr int MAX_WIFI_NETWORKS = 32;
|
||||
static constexpr int WIFI_SSID_CAP = 36;
|
||||
static constexpr int WIFI_PSK_CAP = 72;
|
||||
|
||||
SvgIcon icon_wifi;
|
||||
bool wifi_present;
|
||||
bool wifi_popup_open;
|
||||
Rect wifi_icon_rect;
|
||||
montauk::abi::WifiInfo wifi_info;
|
||||
montauk::abi::WifiNetwork wifi_networks[MAX_WIFI_NETWORKS];
|
||||
int wifi_network_count;
|
||||
uint64_t wifi_last_poll;
|
||||
uint32_t wifi_scan_generation;
|
||||
bool wifi_scanning;
|
||||
int wifi_scroll; // first visible row of the list
|
||||
bool wifi_boot_scan_started; // the automatic scan at startup
|
||||
bool wifi_autoconnect_done; // saved-network join already tried
|
||||
bool wifi_joining; // a join we started is in flight
|
||||
bool wifi_dhcp_pending; // ask for a lease once the link is up
|
||||
char wifi_joining_ssid[WIFI_SSID_CAP];
|
||||
char wifi_status[96]; // last result line in the popup
|
||||
uint64_t wifi_status_time;
|
||||
|
||||
// Passphrase prompt, shown when a selected network needs a key.
|
||||
bool wifi_prompt_open;
|
||||
char wifi_prompt_ssid[WIFI_SSID_CAP];
|
||||
char wifi_prompt_password[WIFI_PSK_CAP];
|
||||
int wifi_prompt_len;
|
||||
bool wifi_prompt_reveal;
|
||||
bool wifi_prompt_remember;
|
||||
uint8_t wifi_prompt_security;
|
||||
|
||||
bool vol_popup_open;
|
||||
Rect vol_icon_rect;
|
||||
int vol_level; // 0-100
|
||||
@@ -207,7 +248,7 @@ struct DesktopState {
|
||||
// IDs of external windows we've sent a close event to but that haven't
|
||||
// been destroyed yet by their owning process. Prevents the poll loop
|
||||
// from re-creating them at the default position (visible flicker).
|
||||
static constexpr int MAX_CLOSING = 8;
|
||||
static constexpr int MAX_CLOSING = MAX_WINDOWS;
|
||||
int closing_ext_ids[MAX_CLOSING];
|
||||
int closing_ext_count;
|
||||
|
||||
|
||||
@@ -14,8 +14,10 @@ namespace gui {
|
||||
static constexpr int FONT_WIDTH = 8;
|
||||
static constexpr int FONT_HEIGHT = 16;
|
||||
|
||||
// Defined in font_data.cpp
|
||||
extern const uint8_t font_data[256 * 16];
|
||||
// Defined by programs that ship the built-in VGA fallback. Keep the symbol
|
||||
// weak so Canvas remains usable by small apps that intentionally rely only on
|
||||
// TrueType fonts, while login/desktop can recover when TrueType setup fails.
|
||||
extern const uint8_t font_data[256 * 16] __attribute__((weak));
|
||||
|
||||
// Dynamic font height: TTF line height or 16 (bitmap fallback)
|
||||
inline int system_font_height() {
|
||||
|
||||
@@ -678,6 +678,78 @@ inline bool same_color(Color a, Color b) {
|
||||
return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Anti-aliased rounded-rect fill
|
||||
// ============================================================================
|
||||
|
||||
// Coverage-based pixel blend; `cov` is 0..255 and the destination is treated
|
||||
// as opaque.
|
||||
inline void aa_blend_px(Canvas& c, int x, int y, Color color, int cov) {
|
||||
if (x < 0 || x >= c.w || y < 0 || y >= c.h || cov <= 0) return;
|
||||
if (cov > 255) cov = 255;
|
||||
uint32_t dst = c.pixels[y * c.w + x];
|
||||
uint32_t dr = (dst >> 16) & 0xFF, dg = (dst >> 8) & 0xFF, db = dst & 0xFF;
|
||||
uint32_t a = (uint32_t)cov, ia = 255 - a;
|
||||
uint32_t nr = (color.r * a + dr * ia + 127) / 255;
|
||||
uint32_t ng = (color.g * a + dg * ia + 127) / 255;
|
||||
uint32_t nb = (color.b * a + db * ia + 127) / 255;
|
||||
c.pixels[y * c.w + x] = 0xFF000000u | (nr << 16) | (ng << 8) | nb;
|
||||
}
|
||||
|
||||
// Rounded-rect fill with 4x4-supersampled corners. The straight interior is
|
||||
// filled solid (fast); only the four corner arcs pay for anti-aliasing, so
|
||||
// this stays cheap even for large frames.
|
||||
inline void fill_round_rect_aa(Canvas& c, const Rect& box, int radius, Color color) {
|
||||
if (box.w <= 0 || box.h <= 0) return;
|
||||
int r = radius;
|
||||
if (r < 0) r = 0;
|
||||
if (r > box.w / 2) r = box.w / 2;
|
||||
if (r > box.h / 2) r = box.h / 2;
|
||||
|
||||
if (r == 0) {
|
||||
c.fill_rect(box.x, box.y, box.w, box.h, color);
|
||||
return;
|
||||
}
|
||||
|
||||
// Solid interior: center band + top/bottom edge bands between the corners.
|
||||
c.fill_rect(box.x, box.y + r, box.w, box.h - 2 * r, color);
|
||||
c.fill_rect(box.x + r, box.y, box.w - 2 * r, r, color);
|
||||
c.fill_rect(box.x + r, box.y + box.h - r, box.w - 2 * r, r, color);
|
||||
|
||||
const int S = 4;
|
||||
double r2 = (double)r * r;
|
||||
// (cell origin x, cell origin y, arc center x, arc center y)
|
||||
int corners[4][4] = {
|
||||
{box.x, box.y, box.x + r, box.y + r},
|
||||
{box.x + box.w - r, box.y, box.x + box.w - r, box.y + r},
|
||||
{box.x, box.y + box.h - r, box.x + r, box.y + box.h - r},
|
||||
{box.x + box.w - r, box.y + box.h - r, box.x + box.w - r, box.y + box.h - r},
|
||||
};
|
||||
for (auto& cn : corners) {
|
||||
// Push the arc center a half pixel away from this corner (toward the
|
||||
// box interior). Pixel-center sampling otherwise sits half a pixel
|
||||
// closer to the center than the legacy pixel-corner test did, which
|
||||
// filled the arcs fuller and made small radii read as square. Left
|
||||
// corners share x == box.x, top corners share y == box.y.
|
||||
double cx = cn[2] + (cn[0] == box.x ? 0.5 : -0.5);
|
||||
double cy = cn[3] + (cn[1] == box.y ? 0.5 : -0.5);
|
||||
for (int yy = 0; yy < r; yy++) {
|
||||
int py = cn[1] + yy;
|
||||
for (int xx = 0; xx < r; xx++) {
|
||||
int px = cn[0] + xx;
|
||||
int hits = 0;
|
||||
for (int sy = 0; sy < S; sy++)
|
||||
for (int sx = 0; sx < S; sx++) {
|
||||
double dx = (px + (sx + 0.5) / S) - cx;
|
||||
double dy = (py + (sy + 0.5) / S) - cy;
|
||||
if (dx * dx + dy * dy <= r2) hits++;
|
||||
}
|
||||
if (hits) aa_blend_px(c, px, py, color, hits * 255 / (S * S));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void draw_rounded_frame(Canvas& c,
|
||||
const Rect& bounds,
|
||||
int radius,
|
||||
@@ -686,16 +758,16 @@ inline void draw_rounded_frame(Canvas& c,
|
||||
int border_w = 1) {
|
||||
if (bounds.empty()) return;
|
||||
if (border_w <= 0 || same_color(fill, border)) {
|
||||
c.fill_rounded_rect(bounds.x, bounds.y, bounds.w, bounds.h, gui_max(radius, 0), fill);
|
||||
fill_round_rect_aa(c, bounds, gui_max(radius, 0), fill);
|
||||
return;
|
||||
}
|
||||
|
||||
c.fill_rounded_rect(bounds.x, bounds.y, bounds.w, bounds.h, gui_max(radius, 0), border);
|
||||
fill_round_rect_aa(c, bounds, gui_max(radius, 0), border);
|
||||
int inner_w = bounds.w - border_w * 2;
|
||||
int inner_h = bounds.h - border_w * 2;
|
||||
if (inner_w <= 0 || inner_h <= 0) return;
|
||||
c.fill_rounded_rect(bounds.x + border_w, bounds.y + border_w,
|
||||
inner_w, inner_h, gui_max(radius - border_w, 0), fill);
|
||||
Rect inner = {bounds.x + border_w, bounds.y + border_w, inner_w, inner_h};
|
||||
fill_round_rect_aa(c, inner, gui_max(radius - border_w, 0), fill);
|
||||
}
|
||||
|
||||
inline ButtonColors resolve_button_colors(ButtonVariant variant,
|
||||
@@ -942,6 +1014,165 @@ inline void draw_radio(Canvas& c,
|
||||
label, theme.text);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Checkboxes and disclosure arrows
|
||||
// ============================================================================
|
||||
|
||||
enum CheckState : uint8_t {
|
||||
CHECK_OFF = 0,
|
||||
CHECK_ON,
|
||||
CHECK_MIXED,
|
||||
};
|
||||
|
||||
inline CheckState check_state(bool checked) {
|
||||
return checked ? CHECK_ON : CHECK_OFF;
|
||||
}
|
||||
|
||||
inline Rect checkbox_indicator_rect(const Rect& option, int size = 16) {
|
||||
return {option.x, option.y + (option.h - size) / 2, size, size};
|
||||
}
|
||||
|
||||
// Squared distance from a point to a line segment (no sqrt; used for the
|
||||
// thickness test of the checkmark strokes).
|
||||
inline double checkbox_seg_dist2(double px, double py,
|
||||
double ax, double ay, double bx, double by) {
|
||||
double dx = bx - ax, dy = by - ay;
|
||||
double len2 = dx * dx + dy * dy;
|
||||
double t = len2 > 0 ? ((px - ax) * dx + (py - ay) * dy) / len2 : 0.0;
|
||||
if (t < 0) t = 0; else if (t > 1) t = 1;
|
||||
double ex = px - (ax + t * dx), ey = py - (ay + t * dy);
|
||||
return ex * ex + ey * ey;
|
||||
}
|
||||
|
||||
// Anti-aliased two-stroke checkmark via 4x4 supersampling of a fixed-width
|
||||
// polyline.
|
||||
inline void draw_check_mark(Canvas& c, const Rect& box, Color color) {
|
||||
double ax = box.x + box.w * 0.24, ay = box.y + box.h * 0.50;
|
||||
double bx = box.x + box.w * 0.42, by = box.y + box.h * 0.68;
|
||||
double cx = box.x + box.w * 0.74, cy = box.y + box.h * 0.26;
|
||||
double hw = box.w * 0.085 + 0.35; // half stroke width
|
||||
double hw2 = hw * hw;
|
||||
const int S = 4;
|
||||
for (int py = box.y - 1; py <= box.y + box.h + 1; py++) {
|
||||
if (py < 0 || py >= c.h) continue;
|
||||
for (int px = box.x - 1; px <= box.x + box.w + 1; px++) {
|
||||
if (px < 0 || px >= c.w) continue;
|
||||
int hits = 0;
|
||||
for (int sy = 0; sy < S; sy++)
|
||||
for (int sx = 0; sx < S; sx++) {
|
||||
double fx = px + (sx + 0.5) / S;
|
||||
double fy = py + (sy + 0.5) / S;
|
||||
double d1 = checkbox_seg_dist2(fx, fy, ax, ay, bx, by);
|
||||
double d2 = checkbox_seg_dist2(fx, fy, bx, by, cx, cy);
|
||||
if ((d1 < d2 ? d1 : d2) <= hw2) hits++;
|
||||
}
|
||||
if (hits) aa_blend_px(c, px, py, color, hits * 255 / (S * S));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void draw_checkbox(Canvas& c,
|
||||
const Rect& option,
|
||||
const char* label,
|
||||
CheckState state,
|
||||
const Theme& theme,
|
||||
bool enabled = true,
|
||||
bool hovered = false) {
|
||||
Rect box = checkbox_indicator_rect(option);
|
||||
int radius = gui_max(box.w / 5, 2);
|
||||
|
||||
Color fill, border, mark;
|
||||
if (!enabled) {
|
||||
fill = theme.disabled_bg;
|
||||
border = theme.disabled_bg;
|
||||
mark = theme.disabled_fg;
|
||||
} else if (state == CHECK_OFF) {
|
||||
fill = colors::WHITE;
|
||||
border = hovered ? theme.accent : theme.border;
|
||||
mark = theme.accent_fg;
|
||||
} else {
|
||||
fill = hovered ? theme.accent_hover : theme.accent;
|
||||
border = fill;
|
||||
mark = theme.accent_fg;
|
||||
}
|
||||
|
||||
if (state == CHECK_OFF) {
|
||||
// Colored ring with a white interior.
|
||||
fill_round_rect_aa(c, box, radius, border);
|
||||
Rect inner = {box.x + 1, box.y + 1, box.w - 2, box.h - 2};
|
||||
fill_round_rect_aa(c, inner, gui_max(radius - 1, 1), fill);
|
||||
} else {
|
||||
fill_round_rect_aa(c, box, radius, fill);
|
||||
}
|
||||
|
||||
if (state == CHECK_ON) {
|
||||
draw_check_mark(c, box, mark);
|
||||
} else if (state == CHECK_MIXED) {
|
||||
Rect dash = {box.x + 4, box.y + box.h / 2 - 1, box.w - 8, 2};
|
||||
fill_round_rect_aa(c, dash, 1, mark);
|
||||
}
|
||||
|
||||
if (label && label[0]) {
|
||||
int fh = system_font_height();
|
||||
c.text(box.x + box.w + theme.gap_sm,
|
||||
option.y + (option.h - fh) / 2,
|
||||
label, enabled ? theme.text : theme.text_muted);
|
||||
}
|
||||
}
|
||||
|
||||
inline Rect disclosure_rect(const Rect& option, int size = 16) {
|
||||
return {option.x, option.y + (option.h - size) / 2, size, size};
|
||||
}
|
||||
|
||||
inline void draw_disclosure(Canvas& c,
|
||||
const Rect& box,
|
||||
bool expanded,
|
||||
const Theme& theme,
|
||||
bool hovered = false) {
|
||||
Color color = hovered ? theme.text : theme.text_subtle;
|
||||
double cx = box.x + box.w / 2.0;
|
||||
double cy = box.y + box.h / 2.0;
|
||||
double s = box.w * 0.26;
|
||||
if (s < 3) s = 3;
|
||||
|
||||
// Triangle vertices (equilateral-ish), rotated per state.
|
||||
double vx[3], vy[3];
|
||||
if (expanded) { // pointing down
|
||||
vx[0] = cx - s; vy[0] = cy - s * 0.6;
|
||||
vx[1] = cx + s; vy[1] = cy - s * 0.6;
|
||||
vx[2] = cx; vy[2] = cy + s * 0.75;
|
||||
} else { // pointing right
|
||||
vx[0] = cx - s * 0.6; vy[0] = cy - s;
|
||||
vx[1] = cx - s * 0.6; vy[1] = cy + s;
|
||||
vx[2] = cx + s * 0.75; vy[2] = cy;
|
||||
}
|
||||
|
||||
// Anti-aliased fill via 4x4 supersampling of the triangle's half-plane test.
|
||||
auto edge = [](double ax, double ay, double bx, double by, double px, double py) {
|
||||
return (px - ax) * (by - ay) - (py - ay) * (bx - ax);
|
||||
};
|
||||
const int S = 4;
|
||||
for (int py = box.y; py < box.y + box.h; py++) {
|
||||
if (py < 0 || py >= c.h) continue;
|
||||
for (int px = box.x; px < box.x + box.w; px++) {
|
||||
if (px < 0 || px >= c.w) continue;
|
||||
int hits = 0;
|
||||
for (int sy = 0; sy < S; sy++)
|
||||
for (int sx = 0; sx < S; sx++) {
|
||||
double fx = px + (sx + 0.5) / S;
|
||||
double fy = py + (sy + 0.5) / S;
|
||||
double e0 = edge(vx[0], vy[0], vx[1], vy[1], fx, fy);
|
||||
double e1 = edge(vx[1], vy[1], vx[2], vy[2], fx, fy);
|
||||
double e2 = edge(vx[2], vy[2], vx[0], vy[0], fx, fy);
|
||||
bool inside = (e0 >= 0 && e1 >= 0 && e2 >= 0) ||
|
||||
(e0 <= 0 && e1 <= 0 && e2 <= 0);
|
||||
if (inside) hits++;
|
||||
}
|
||||
if (hits) aa_blend_px(c, px, py, color, hits * 255 / (S * S));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline Rect swatch_rect(int row_x, int row_y, int index, int size = 24, int gap = 6) {
|
||||
return {row_x + index * (size + gap), row_y, size, size};
|
||||
}
|
||||
|
||||
@@ -795,11 +795,21 @@ static inline void terminal_resize(TerminalState* t, int new_cols, int new_rows)
|
||||
}
|
||||
}
|
||||
|
||||
int new_scrollback = keep - new_rows;
|
||||
// Anchor the new visible region to the cursor's line. Treating the bottom
|
||||
// new_rows of kept content as the screen (the naive keep - new_rows) only
|
||||
// works when the screen is full: with a partly filled screen -- e.g. a
|
||||
// shell prompt a few lines down with blank rows below it -- it would push
|
||||
// the real text up into scrollback and drop the view onto the blank region,
|
||||
// so a zoom appears to scroll down and strands the cursor in empty space.
|
||||
// Instead pin the cursor to the bottom row when there is enough history
|
||||
// above it, otherwise keep the content anchored at the top.
|
||||
int abs_cursor_y = t->scrollback_lines + t->cursor_y - discard;
|
||||
if (abs_cursor_y < 0) abs_cursor_y = 0;
|
||||
int new_scrollback = abs_cursor_y - (new_rows - 1);
|
||||
if (new_scrollback < 0) new_scrollback = 0;
|
||||
if (new_scrollback > t->max_scrollback) new_scrollback = t->max_scrollback;
|
||||
|
||||
// Adjust cursor
|
||||
int abs_cursor_y = t->scrollback_lines + t->cursor_y - discard;
|
||||
int new_cursor_y = abs_cursor_y - new_scrollback;
|
||||
if (new_cursor_y < 0) new_cursor_y = 0;
|
||||
if (new_cursor_y >= new_rows) new_cursor_y = new_rows - 1;
|
||||
|
||||
@@ -121,10 +121,18 @@ struct TrueTypeFont {
|
||||
return false;
|
||||
}
|
||||
|
||||
montauk::read(fd, data, 0, size);
|
||||
int read_result = montauk::read(fd, data, 0, size);
|
||||
montauk::close(fd);
|
||||
if (read_result < 0 || (uint64_t)read_result != size) {
|
||||
montauk::free(data);
|
||||
data = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stbtt_InitFont(&info, data, stbtt_GetFontOffsetForIndex(data, 0))) {
|
||||
// stbtt_GetFontOffsetForIndex returns -1 for non-font data; passing
|
||||
// that into stbtt_InitFont makes it dereference data + (uint32)-1.
|
||||
int off = stbtt_GetFontOffsetForIndex(data, 0);
|
||||
if (off < 0 || !stbtt_InitFont(&info, data, off)) {
|
||||
montauk::free(data);
|
||||
data = nullptr;
|
||||
return false;
|
||||
@@ -308,13 +316,14 @@ struct TrueTypeFont {
|
||||
draw(fb, x, y, text, fg, pixel_size);
|
||||
}
|
||||
|
||||
void draw_to_buffer(uint32_t* pixels, int buf_w, int buf_h,
|
||||
bool draw_to_buffer(uint32_t* pixels, int buf_w, int buf_h,
|
||||
int x, int y, const char* text,
|
||||
Color color, int pixel_size) {
|
||||
if (!valid) return;
|
||||
if (!valid) return false;
|
||||
GlyphCache* gc = get_cache(pixel_size);
|
||||
int cx = x;
|
||||
int baseline = y + gc->ascent;
|
||||
bool drew_pixel = false;
|
||||
|
||||
for (int i = 0; text[i]; i++) {
|
||||
CachedGlyph* g = get_glyph(gc, (unsigned char)text[i]);
|
||||
@@ -331,6 +340,7 @@ struct TrueTypeFont {
|
||||
if (dx < 0 || dx >= buf_w) continue;
|
||||
uint8_t alpha = g->bitmap[row * g->width + col];
|
||||
if (alpha == 0) continue;
|
||||
drew_pixel = true;
|
||||
|
||||
if (alpha == 255) {
|
||||
pixels[dy * buf_w + dx] =
|
||||
@@ -353,6 +363,7 @@ struct TrueTypeFont {
|
||||
}
|
||||
cx += g->advance;
|
||||
}
|
||||
return drew_pixel;
|
||||
}
|
||||
|
||||
// Draw text to buffer with clip rectangle (pixels outside clip_x..clip_x+clip_w are not drawn)
|
||||
|
||||
@@ -16,6 +16,9 @@ int islower(int c);
|
||||
int isprint(int c);
|
||||
int ispunct(int c);
|
||||
int isxdigit(int c);
|
||||
|
||||
#define isascii(c) (((c) & ~0x7F) == 0)
|
||||
#define toascii(c) ((c) & 0x7F)
|
||||
int iscntrl(int c);
|
||||
int isgraph(int c);
|
||||
int toupper(int c);
|
||||
|
||||
@@ -9,8 +9,13 @@ extern "C" {
|
||||
|
||||
#define NAME_MAX 255
|
||||
#define DT_UNKNOWN 0
|
||||
#define DT_FIFO 1
|
||||
#define DT_CHR 2
|
||||
#define DT_DIR 4
|
||||
#define DT_BLK 6
|
||||
#define DT_REG 8
|
||||
#define DT_LNK 10
|
||||
#define DT_SOCK 12
|
||||
|
||||
struct dirent {
|
||||
unsigned char d_type;
|
||||
|
||||
@@ -9,18 +9,88 @@ extern "C" {
|
||||
|
||||
extern int errno;
|
||||
|
||||
#define ENOENT 2
|
||||
#define EIO 5
|
||||
#define ENOMEM 12
|
||||
#define EACCES 13
|
||||
#define EINVAL 22
|
||||
#define ERANGE 34
|
||||
#define ENOSYS 38
|
||||
#define EISDIR 21
|
||||
#define ENOTDIR 20
|
||||
#define EEXIST 17
|
||||
#define EBADF 9
|
||||
#define EPERM 1
|
||||
/* Linux errno numbering. */
|
||||
#define EPERM 1
|
||||
#define ENOENT 2
|
||||
#define ESRCH 3
|
||||
#define EINTR 4
|
||||
#define EIO 5
|
||||
#define ENXIO 6
|
||||
#define E2BIG 7
|
||||
#define ENOEXEC 8
|
||||
#define EBADF 9
|
||||
#define ECHILD 10
|
||||
#define EAGAIN 11
|
||||
#define ENOMEM 12
|
||||
#define EACCES 13
|
||||
#define EFAULT 14
|
||||
#define EBUSY 16
|
||||
#define EEXIST 17
|
||||
#define EXDEV 18
|
||||
#define ENODEV 19
|
||||
#define ENOTDIR 20
|
||||
#define EISDIR 21
|
||||
#define EINVAL 22
|
||||
#define ENFILE 23
|
||||
#define EMFILE 24
|
||||
#define ENOTTY 25
|
||||
#define EFBIG 27
|
||||
#define ENOSPC 28
|
||||
#define ESPIPE 29
|
||||
#define EROFS 30
|
||||
#define EMLINK 31
|
||||
#define EPIPE 32
|
||||
#define EDOM 33
|
||||
#define ERANGE 34
|
||||
#define EDEADLK 35
|
||||
#define ENAMETOOLONG 36
|
||||
#define ENOLCK 37
|
||||
#define ENOSYS 38
|
||||
#define ENOTEMPTY 39
|
||||
#define ELOOP 40
|
||||
#define EWOULDBLOCK EAGAIN
|
||||
#define ETXTBSY 26
|
||||
#define ENOMSG 42
|
||||
#define EIDRM 43
|
||||
#define ENOSTR 60
|
||||
#define ENODATA 61
|
||||
#define ETIME 62
|
||||
#define ENOSR 63
|
||||
#define ENOLINK 67
|
||||
#define EPROTO 71
|
||||
#define EMULTIHOP 72
|
||||
#define EBADMSG 74
|
||||
#define EOVERFLOW 75
|
||||
#define EILSEQ 84
|
||||
#define ENOTSOCK 88
|
||||
#define EDESTADDRREQ 89
|
||||
#define EMSGSIZE 90
|
||||
#define EPROTOTYPE 91
|
||||
#define ENOPROTOOPT 92
|
||||
#define EPROTONOSUPPORT 93
|
||||
#define EOPNOTSUPP 95 /* == ENOTSUP, Linux numbering */
|
||||
#define EAFNOSUPPORT 97
|
||||
#define EADDRINUSE 98
|
||||
#define EADDRNOTAVAIL 99
|
||||
#define ENETDOWN 100
|
||||
#define ENETUNREACH 101
|
||||
#define ENETRESET 102
|
||||
#define ECONNABORTED 103
|
||||
#define ECONNRESET 104
|
||||
#define ENOBUFS 105
|
||||
#define EISCONN 106
|
||||
#define ENOTCONN 107
|
||||
#define ETIMEDOUT 110
|
||||
#define ECONNREFUSED 111
|
||||
#define EHOSTUNREACH 113
|
||||
#define EALREADY 114
|
||||
#define EINPROGRESS 115
|
||||
#define ESTALE 116
|
||||
#define EDQUOT 122
|
||||
#define ECANCELED 125
|
||||
#define EOWNERDEAD 130
|
||||
#define ENOTRECOVERABLE 131
|
||||
#define ENOTSUP 95
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -7,13 +7,27 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define O_RDONLY 0
|
||||
#define O_WRONLY 1
|
||||
#define O_RDWR 2
|
||||
#define O_CREAT 0x40
|
||||
#define O_TRUNC 0x200
|
||||
#define O_RDONLY 0
|
||||
#define O_WRONLY 1
|
||||
#define O_RDWR 2
|
||||
#define O_ACCMODE 3
|
||||
#define O_CREAT 0x40
|
||||
#define O_EXCL 0x80
|
||||
#define O_TRUNC 0x200
|
||||
#define O_APPEND 0x400
|
||||
#define O_NONBLOCK 0x800
|
||||
#define O_CLOEXEC 0x80000
|
||||
|
||||
#define F_DUPFD 0
|
||||
#define F_GETFD 1
|
||||
#define F_SETFD 2
|
||||
#define F_GETFL 3
|
||||
#define F_SETFL 4
|
||||
|
||||
#define FD_CLOEXEC 1
|
||||
|
||||
int open(const char *path, int flags, ...);
|
||||
int fcntl(int fd, int cmd, ...);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -23,5 +23,38 @@
|
||||
#define PRIX16 "X"
|
||||
#define PRIX32 "X"
|
||||
#define PRIX64 "lX"
|
||||
#define PRIo8 "o"
|
||||
#define PRIo16 "o"
|
||||
#define PRIo32 "o"
|
||||
#define PRIo64 "lo"
|
||||
|
||||
#define PRIdPTR "ld"
|
||||
#define PRIiPTR "li"
|
||||
#define PRIuPTR "lu"
|
||||
#define PRIxPTR "lx"
|
||||
#define PRIXPTR "lX"
|
||||
#define PRIdMAX "ld"
|
||||
#define PRIuMAX "lu"
|
||||
#define PRIxMAX "lx"
|
||||
|
||||
#define SCNd8 "d"
|
||||
#define SCNd16 "d"
|
||||
#define SCNd32 "d"
|
||||
#define SCNd64 "ld"
|
||||
#define SCNi8 "i"
|
||||
#define SCNi16 "i"
|
||||
#define SCNi32 "i"
|
||||
#define SCNi64 "li"
|
||||
#define SCNu8 "u"
|
||||
#define SCNu16 "u"
|
||||
#define SCNu32 "u"
|
||||
#define SCNu64 "lu"
|
||||
#define SCNx8 "x"
|
||||
#define SCNx16 "x"
|
||||
#define SCNx32 "x"
|
||||
#define SCNx64 "lx"
|
||||
#define SCNdPTR "ld"
|
||||
#define SCNuPTR "lu"
|
||||
#define SCNxPTR "lx"
|
||||
|
||||
#endif /* _LIBC_INTTYPES_H */
|
||||
|
||||
@@ -11,8 +11,47 @@ extern "C" {
|
||||
#define INFINITY __builtin_inff()
|
||||
#define NAN __builtin_nanf("")
|
||||
|
||||
/* C99 floating-point classification. */
|
||||
#define FP_NAN 0
|
||||
#define FP_INFINITE 1
|
||||
#define FP_ZERO 2
|
||||
#define FP_SUBNORMAL 3
|
||||
#define FP_NORMAL 4
|
||||
|
||||
#define fpclassify(x) \
|
||||
__builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, \
|
||||
FP_ZERO, x)
|
||||
#define isnan(x) __builtin_isnan(x)
|
||||
#define isinf(x) __builtin_isinf(x)
|
||||
#define isfinite(x) __builtin_isfinite(x)
|
||||
#define isnormal(x) __builtin_isnormal(x)
|
||||
#define signbit(x) __builtin_signbit(x)
|
||||
|
||||
double fabs(double x);
|
||||
double frexp(double x, int *exp);
|
||||
|
||||
/* C89 modf and the C99 float variants assumed by hosted libstdc++
|
||||
(--with-newlib crossconfig). The float forms wrap the double
|
||||
implementations. */
|
||||
double modf(double x, double *iptr);
|
||||
float modff(float x, float *iptr);
|
||||
double hypot(double x, double y);
|
||||
float hypotf(float x, float y);
|
||||
float acosf(float x);
|
||||
float asinf(float x);
|
||||
float atanf(float x);
|
||||
float atan2f(float y, float x);
|
||||
float coshf(float x);
|
||||
float expf(float x);
|
||||
float fmodf(float x, float y);
|
||||
float frexpf(float x, int *exp);
|
||||
float ldexpf(float x, int exp);
|
||||
float logf(float x);
|
||||
float log10f(float x);
|
||||
float powf(float x, float y);
|
||||
float sinhf(float x);
|
||||
float tanf(float x);
|
||||
float tanhf(float x);
|
||||
double ldexp(double x, int n);
|
||||
long double ldexpl(long double x, int n);
|
||||
double floor(double x);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef _LIBC_MEMORY_H
|
||||
#define _LIBC_MEMORY_H
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Traditional alias for string.h. */
|
||||
#include <string.h>
|
||||
|
||||
#endif /* _LIBC_MEMORY_H */
|
||||
@@ -19,106 +19,177 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ====================================================================
|
||||
Syscall numbers
|
||||
==================================================================== */
|
||||
|
||||
#define MTK_SYS_EXIT 0
|
||||
#define MTK_SYS_YIELD 1
|
||||
#define MTK_SYS_SLEEP_MS 2
|
||||
#define MTK_SYS_GETPID 3
|
||||
#define MTK_SYS_PRINT 4
|
||||
#define MTK_SYS_PUTCHAR 5
|
||||
#define MTK_SYS_OPEN 6
|
||||
#define MTK_SYS_READ 7
|
||||
#define MTK_SYS_GETSIZE 8
|
||||
#define MTK_SYS_CLOSE 9
|
||||
#define MTK_SYS_READDIR 10
|
||||
#define MTK_SYS_ALLOC 11
|
||||
#define MTK_SYS_FREE 12
|
||||
#define MTK_SYS_GETTICKS 13
|
||||
#define MTK_SYS_GETMILLISECONDS 14
|
||||
#define MTK_SYS_GETINFO 15
|
||||
#define MTK_SYS_ISKEYAVAILABLE 16
|
||||
#define MTK_SYS_GETKEY 17
|
||||
#define MTK_SYS_GETCHAR 18
|
||||
#define MTK_SYS_PING 19
|
||||
#define MTK_SYS_SPAWN 20
|
||||
#define MTK_SYS_WAITPID 23
|
||||
#define MTK_SYS_TERMSIZE 24
|
||||
#define MTK_SYS_GETARGS 25
|
||||
#define MTK_SYS_RESET 26
|
||||
#define MTK_SYS_SHUTDOWN 27
|
||||
#define MTK_SYS_SETUNIXTIME 153
|
||||
#define MTK_SYS_GETTIME 28
|
||||
#define MTK_SYS_SOCKET 29
|
||||
#define MTK_SYS_CONNECT 30
|
||||
#define MTK_SYS_BIND 31
|
||||
#define MTK_SYS_LISTEN 32
|
||||
#define MTK_SYS_ACCEPT 33
|
||||
#define MTK_SYS_SEND 34
|
||||
#define MTK_SYS_RECV 35
|
||||
#define MTK_SYS_CLOSESOCK 36
|
||||
#define MTK_SYS_GETNETCFG 37
|
||||
#define MTK_SYS_SETNETCFG 38
|
||||
#define MTK_SYS_SENDTO 39
|
||||
#define MTK_SYS_RECVFROM 40
|
||||
#define MTK_SYS_FWRITE 41
|
||||
#define MTK_SYS_FCREATE 42
|
||||
#define MTK_SYS_TERMSCALE 43
|
||||
#define MTK_SYS_RESOLVE 44
|
||||
#define MTK_SYS_GETRANDOM 45
|
||||
#define MTK_SYS_KLOG 46
|
||||
#define MTK_SYS_MOUSESTATE 47
|
||||
#define MTK_SYS_SETMOUSEBOUNDS 48
|
||||
#define MTK_SYS_SPAWN_REDIR 49
|
||||
#define MTK_SYS_CHILDIO_READ 50
|
||||
#define MTK_SYS_CHILDIO_WRITE 51
|
||||
#define MTK_SYS_WINCREATE 54
|
||||
#define MTK_SYS_WINDESTROY 55
|
||||
#define MTK_SYS_WINPRESENT 56
|
||||
#define MTK_SYS_WINPOLL 57
|
||||
#define MTK_SYS_WINENUM 58
|
||||
#define MTK_SYS_WINMAP 59
|
||||
#define MTK_SYS_WINUNMAP 97
|
||||
#define MTK_SYS_WINSENDEVENT 60
|
||||
#define MTK_SYS_PROCLIST 61
|
||||
#define MTK_SYS_KILL 62
|
||||
#define MTK_SYS_WINRESIZE 64
|
||||
#define MTK_SYS_WINSETSCALE 65
|
||||
#define MTK_SYS_WINGETSCALE 66
|
||||
#define MTK_SYS_MEMSTATS 67
|
||||
#define MTK_SYS_WINSETCURSOR 68
|
||||
#define MTK_SYS_WINSETFLAGS 126
|
||||
#define MTK_SYS_FDELETE 77
|
||||
#define MTK_SYS_FMKDIR 78
|
||||
#define MTK_SYS_FRENAME 94
|
||||
#define MTK_SYS_DRIVELIST 79
|
||||
#define MTK_SYS_DRIVELABEL 124
|
||||
#define MTK_SYS_AUDIOOPEN 80
|
||||
#define MTK_SYS_AUDIOCLOSE 81
|
||||
#define MTK_SYS_AUDIOWRITE 82
|
||||
#define MTK_SYS_AUDIOCTL 83
|
||||
#define MTK_SYS_SETTZ 90
|
||||
#define MTK_SYS_GETTZ 91
|
||||
#define MTK_SYS_GETCWD 95
|
||||
#define MTK_SYS_CHDIR 96
|
||||
#define MTK_SYS_DUPHANDLE 98
|
||||
#define MTK_SYS_WAIT_HANDLE 99
|
||||
#define MTK_SYS_STREAM_CREATE 100
|
||||
#define MTK_SYS_STREAM_READ 101
|
||||
#define MTK_SYS_STREAM_WRITE 102
|
||||
#define MTK_SYS_MAILBOX_CREATE 103
|
||||
#define MTK_SYS_MAILBOX_SEND 104
|
||||
#define MTK_SYS_MAILBOX_RECV 105
|
||||
#define MTK_SYS_WAITSET_CREATE 106
|
||||
#define MTK_SYS_WAITSET_ADD 107
|
||||
#define MTK_SYS_WAITSET_REMOVE 108
|
||||
#define MTK_SYS_WAITSET_WAIT 109
|
||||
#define MTK_SYS_PROC_OPEN 110
|
||||
#define MTK_SYS_SURFACE_CREATE 111
|
||||
#define MTK_SYS_SURFACE_MAP 112
|
||||
#define MTK_SYS_SURFACE_RESIZE 113
|
||||
/* @SYSCALLS-BEGIN
|
||||
Generated from Api/Syscall.hpp by scripts/montauk-syscalls.py.
|
||||
Do not edit by hand; run 'make gen-syscalls' to refresh.
|
||||
Wrapper functions below are hand-written. */
|
||||
#define MTK_SYS_EXIT 0
|
||||
#define MTK_SYS_YIELD 1
|
||||
#define MTK_SYS_SLEEP_MS 2
|
||||
#define MTK_SYS_GETPID 3
|
||||
#define MTK_SYS_PRINT 4
|
||||
#define MTK_SYS_PUTCHAR 5
|
||||
#define MTK_SYS_OPEN 6
|
||||
#define MTK_SYS_READ 7
|
||||
#define MTK_SYS_GETSIZE 8
|
||||
#define MTK_SYS_CLOSE 9
|
||||
#define MTK_SYS_READDIR 10
|
||||
#define MTK_SYS_ALLOC 11
|
||||
#define MTK_SYS_FREE 12
|
||||
#define MTK_SYS_GETTICKS 13
|
||||
#define MTK_SYS_GETMILLISECONDS 14
|
||||
#define MTK_SYS_GETINFO 15
|
||||
#define MTK_SYS_ISKEYAVAILABLE 16
|
||||
#define MTK_SYS_GETKEY 17
|
||||
#define MTK_SYS_GETCHAR 18
|
||||
#define MTK_SYS_PING 19
|
||||
#define MTK_SYS_SPAWN 20
|
||||
#define MTK_SYS_FBINFO 21
|
||||
#define MTK_SYS_FBMAP 22
|
||||
#define MTK_SYS_WAITPID 23
|
||||
#define MTK_SYS_TERMSIZE 24
|
||||
#define MTK_SYS_GETARGS 25
|
||||
#define MTK_SYS_RESET 26
|
||||
#define MTK_SYS_SHUTDOWN 27
|
||||
#define MTK_SYS_GETTIME 28
|
||||
#define MTK_SYS_SOCKET 29
|
||||
#define MTK_SYS_CONNECT 30
|
||||
#define MTK_SYS_BIND 31
|
||||
#define MTK_SYS_LISTEN 32
|
||||
#define MTK_SYS_ACCEPT 33
|
||||
#define MTK_SYS_SEND 34
|
||||
#define MTK_SYS_RECV 35
|
||||
#define MTK_SYS_CLOSESOCK 36
|
||||
#define MTK_SYS_GETNETCFG 37
|
||||
#define MTK_SYS_SETNETCFG 38
|
||||
#define MTK_SYS_SENDTO 39
|
||||
#define MTK_SYS_RECVFROM 40
|
||||
#define MTK_SYS_FWRITE 41
|
||||
#define MTK_SYS_FCREATE 42
|
||||
#define MTK_SYS_TERMSCALE 43
|
||||
#define MTK_SYS_RESOLVE 44
|
||||
#define MTK_SYS_GETRANDOM 45
|
||||
#define MTK_SYS_KLOG 46
|
||||
#define MTK_SYS_MOUSESTATE 47
|
||||
#define MTK_SYS_SETMOUSEBOUNDS 48
|
||||
#define MTK_SYS_SPAWN_REDIR 49
|
||||
#define MTK_SYS_CHILDIO_READ 50
|
||||
#define MTK_SYS_CHILDIO_WRITE 51
|
||||
#define MTK_SYS_CHILDIO_WRITEKEY 52
|
||||
#define MTK_SYS_CHILDIO_SETTERMSZ 53
|
||||
#define MTK_SYS_WINCREATE 54
|
||||
#define MTK_SYS_WINDESTROY 55
|
||||
#define MTK_SYS_WINPRESENT 56
|
||||
#define MTK_SYS_WINPOLL 57
|
||||
#define MTK_SYS_WINENUM 58
|
||||
#define MTK_SYS_WINMAP 59
|
||||
#define MTK_SYS_WINSENDEVENT 60
|
||||
#define MTK_SYS_PROCLIST 61
|
||||
#define MTK_SYS_KILL 62
|
||||
#define MTK_SYS_DEVLIST 63
|
||||
#define MTK_SYS_WINRESIZE 64
|
||||
#define MTK_SYS_WINSETSCALE 65
|
||||
#define MTK_SYS_WINGETSCALE 66
|
||||
#define MTK_SYS_MEMSTATS 67
|
||||
#define MTK_SYS_WINSETCURSOR 68
|
||||
#define MTK_SYS_DISKINFO 69
|
||||
#define MTK_SYS_PARTLIST 70
|
||||
#define MTK_SYS_DISKREAD 71
|
||||
#define MTK_SYS_DISKWRITE 72
|
||||
#define MTK_SYS_GPTINIT 73
|
||||
#define MTK_SYS_GPTADD 74
|
||||
#define MTK_SYS_FSMOUNT 75
|
||||
#define MTK_SYS_FSFORMAT 76
|
||||
#define MTK_SYS_FDELETE 77
|
||||
#define MTK_SYS_FMKDIR 78
|
||||
#define MTK_SYS_DRIVELIST 79
|
||||
#define MTK_SYS_AUDIOOPEN 80
|
||||
#define MTK_SYS_AUDIOCLOSE 81
|
||||
#define MTK_SYS_AUDIOWRITE 82
|
||||
#define MTK_SYS_AUDIOCTL 83
|
||||
#define MTK_SYS_BTSCAN 84
|
||||
#define MTK_SYS_BTCONNECT 85
|
||||
#define MTK_SYS_BTDISCONNECT 86
|
||||
#define MTK_SYS_BTLIST 87
|
||||
#define MTK_SYS_BTINFO 88
|
||||
#define MTK_SYS_SUSPEND 89
|
||||
#define MTK_SYS_SETTZ 90
|
||||
#define MTK_SYS_GETTZ 91
|
||||
#define MTK_SYS_SETUSER 92
|
||||
#define MTK_SYS_GETUSER 93
|
||||
#define MTK_SYS_FRENAME 94
|
||||
#define MTK_SYS_GETCWD 95
|
||||
#define MTK_SYS_CHDIR 96
|
||||
#define MTK_SYS_WINUNMAP 97
|
||||
#define MTK_SYS_DUPHANDLE 98
|
||||
#define MTK_SYS_WAIT_HANDLE 99
|
||||
#define MTK_SYS_STREAM_CREATE 100
|
||||
#define MTK_SYS_STREAM_READ 101
|
||||
#define MTK_SYS_STREAM_WRITE 102
|
||||
#define MTK_SYS_MAILBOX_CREATE 103
|
||||
#define MTK_SYS_MAILBOX_SEND 104
|
||||
#define MTK_SYS_MAILBOX_RECV 105
|
||||
#define MTK_SYS_WAITSET_CREATE 106
|
||||
#define MTK_SYS_WAITSET_ADD 107
|
||||
#define MTK_SYS_WAITSET_REMOVE 108
|
||||
#define MTK_SYS_WAITSET_WAIT 109
|
||||
#define MTK_SYS_PROC_OPEN 110
|
||||
#define MTK_SYS_SURFACE_CREATE 111
|
||||
#define MTK_SYS_SURFACE_MAP 112
|
||||
#define MTK_SYS_SURFACE_RESIZE 113
|
||||
#define MTK_SYS_LOAD_LIB 114
|
||||
#define MTK_SYS_UNLOAD_LIB 115
|
||||
#define MTK_SYS_DLSYM 116
|
||||
#define MTK_SYS_GETLIBBASE 117
|
||||
#define MTK_SYS_CRASH_REPORT 118
|
||||
#define MTK_SYS_CLIPBOARD_SET_TEXT 119
|
||||
#define MTK_SYS_CLIPBOARD_GET_INFO 120
|
||||
#define MTK_SYS_CLIPBOARD_GET_TEXT 121
|
||||
#define MTK_SYS_CLIPBOARD_CLEAR 122
|
||||
#define MTK_SYS_INPUT_WAIT 123
|
||||
#define MTK_SYS_DRIVELABEL 124
|
||||
#define MTK_SYS_NETSTATUS 125
|
||||
#define MTK_SYS_WINSETFLAGS 126
|
||||
#define MTK_SYS_DRIVEKIND 127
|
||||
#define MTK_SYS_AUDIOLIST 128
|
||||
#define MTK_SYS_AUDIOWAIT 129
|
||||
#define MTK_SYS_THREAD_SPAWN 130
|
||||
#define MTK_SYS_THREAD_EXIT 131
|
||||
#define MTK_SYS_THREAD_JOIN 132
|
||||
#define MTK_SYS_THREAD_SELF 133
|
||||
#define MTK_SYS_FS_SYNC 134
|
||||
#define MTK_SYS_POWER_REQUEST 135
|
||||
#define MTK_SYS_READDIR_AT 136
|
||||
#define MTK_SYS_BTSETADDR 137
|
||||
#define MTK_SYS_BTBONDS 138
|
||||
#define MTK_SYS_BTFORGET 139
|
||||
#define MTK_SYS_SDR_COUNT 140
|
||||
#define MTK_SYS_SDR_INFO 141
|
||||
#define MTK_SYS_SDR_OPEN 142
|
||||
#define MTK_SYS_SDR_CLOSE 143
|
||||
#define MTK_SYS_SDR_START 144
|
||||
#define MTK_SYS_SDR_STOP 145
|
||||
#define MTK_SYS_SDR_READ 146
|
||||
#define MTK_SYS_SDR_SETPARAM 147
|
||||
#define MTK_SYS_SDR_GETPARAM 148
|
||||
#define MTK_SYS_POWERINFO 149
|
||||
#define MTK_SYS_FBFLIP 150
|
||||
#define MTK_SYS_GETEXECPATH 151
|
||||
#define MTK_SYS_STAT 152
|
||||
#define MTK_SYS_SETUNIXTIME 153
|
||||
#define MTK_SYS_DISPLAYINFO 154
|
||||
#define MTK_SYS_DISPLAYMODES 155
|
||||
#define MTK_SYS_DISPLAYSETMODE 156
|
||||
#define MTK_SYS_DISPLAYBRIGHTNESS 157
|
||||
#define MTK_SYS_WIFI_SCAN 158
|
||||
#define MTK_SYS_WIFI_INFO 159
|
||||
#define MTK_SYS_WIFI_CONNECT 160
|
||||
#define MTK_SYS_WIFI_DISCONNECT 161
|
||||
#define MTK_SYS_WIFI_SCAN_START 162
|
||||
#define MTK_SYS_WIFI_RESULTS 163
|
||||
#define MTK_SYS_WIFI_CONNECT_ASYNC 164
|
||||
#define MTK_SYS_NETIFS 165
|
||||
/* @SYSCALLS-END */
|
||||
|
||||
#define MTK_SOCK_TCP 1
|
||||
#define MTK_SOCK_UDP 2
|
||||
@@ -354,6 +425,10 @@ static inline int mtk_getargs(char *buf, unsigned long max_len) {
|
||||
return (int)_mtk_syscall2(MTK_SYS_GETARGS, (long)buf, (long)max_len);
|
||||
}
|
||||
|
||||
static inline int mtk_getexecpath(char *buf, unsigned long maxLen) {
|
||||
return (int)mtk_syscall2(MTK_SYS_GETEXECPATH, (long)buf, (long)maxLen);
|
||||
}
|
||||
|
||||
static inline int mtk_chdir(const char *path) {
|
||||
return (int)_mtk_syscall1(MTK_SYS_CHDIR, (long)path);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -10,7 +12,26 @@ extern "C" {
|
||||
typedef int sig_atomic_t;
|
||||
typedef void (*sighandler_t)(int);
|
||||
|
||||
#define SIGINT 2
|
||||
/* Linux signal numbering. Only SIGINT is ever delivered today; the
|
||||
rest exist so hosted code can name them. */
|
||||
#define SIGHUP 1
|
||||
#define SIGINT 2
|
||||
#define SIGQUIT 3
|
||||
#define SIGILL 4
|
||||
#define SIGTRAP 5
|
||||
#define SIGABRT 6
|
||||
#define SIGBUS 7
|
||||
#define SIGFPE 8
|
||||
#define SIGKILL 9
|
||||
#define SIGUSR1 10
|
||||
#define SIGSEGV 11
|
||||
#define SIGUSR2 12
|
||||
#define SIGPIPE 13
|
||||
#define SIGALRM 14
|
||||
#define SIGTERM 15
|
||||
#define SIGCHLD 17
|
||||
#define SIGCONT 18
|
||||
#define SIGSTOP 19
|
||||
|
||||
#define SIG_DFL ((sighandler_t)0)
|
||||
#define SIG_IGN ((sighandler_t)1)
|
||||
@@ -18,6 +39,7 @@ typedef void (*sighandler_t)(int);
|
||||
|
||||
sighandler_t signal(int sig, sighandler_t handler);
|
||||
int raise(int sig);
|
||||
int kill(pid_t pid, int sig);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef _LIBC_SPAWN_H
|
||||
#define _LIBC_SPAWN_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* posix_spawn for MontaukOS, layered on SYS_SPAWN.
|
||||
*
|
||||
* Limitations (kernel spawn model):
|
||||
* - argv is joined into a single args string; arguments containing
|
||||
* spaces are rejected with EINVAL (no quoting in the kernel).
|
||||
* - envp is ignored (no environment transfer on spawn).
|
||||
* - file actions must be empty: stdio redirection needs kernel
|
||||
* support that does not exist yet, so any recorded action makes
|
||||
* posix_spawn fail with ENOTSUP rather than misbehave silently.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
short flags;
|
||||
} posix_spawnattr_t;
|
||||
|
||||
typedef struct {
|
||||
int action_count;
|
||||
} posix_spawn_file_actions_t;
|
||||
|
||||
int posix_spawnattr_init(posix_spawnattr_t *attr);
|
||||
int posix_spawnattr_destroy(posix_spawnattr_t *attr);
|
||||
int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags);
|
||||
int posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *flags);
|
||||
|
||||
int posix_spawn_file_actions_init(posix_spawn_file_actions_t *actions);
|
||||
int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *actions);
|
||||
int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *actions,
|
||||
int fd, int newfd);
|
||||
int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *actions,
|
||||
int fd);
|
||||
int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *actions,
|
||||
int fd, const char *path, int oflag,
|
||||
mode_t mode);
|
||||
|
||||
int posix_spawn(pid_t *pid, const char *path,
|
||||
const posix_spawn_file_actions_t *actions,
|
||||
const posix_spawnattr_t *attr,
|
||||
char *const argv[], char *const envp[]);
|
||||
int posix_spawnp(pid_t *pid, const char *file,
|
||||
const posix_spawn_file_actions_t *actions,
|
||||
const posix_spawnattr_t *attr,
|
||||
char *const argv[], char *const envp[]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBC_SPAWN_H */
|
||||
@@ -1,6 +1,12 @@
|
||||
#ifndef _LIBC_STDIO_H
|
||||
#define _LIBC_STDIO_H
|
||||
|
||||
/* Conventional guard marker: packages like GMP sniff the libc's stdio
|
||||
include-guard name to detect that FILE is available. */
|
||||
#ifndef _STDIO_H
|
||||
#define _STDIO_H 1
|
||||
#endif
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdarg.h>
|
||||
@@ -36,6 +42,21 @@ extern FILE *stdin;
|
||||
extern FILE *stdout;
|
||||
extern FILE *stderr;
|
||||
|
||||
int putc(int c, FILE *stream);
|
||||
void rewind(FILE *stream);
|
||||
int getchar(void);
|
||||
typedef long fpos_t;
|
||||
|
||||
int fgetpos(FILE *stream, fpos_t *pos);
|
||||
int fsetpos(FILE *stream, const fpos_t *pos);
|
||||
void setbuf(FILE *stream, char *buf);
|
||||
int fscanf(FILE *stream, const char *fmt, ...);
|
||||
int scanf(const char *fmt, ...);
|
||||
int vfscanf(FILE *stream, const char *fmt, va_list ap);
|
||||
|
||||
int fileno(FILE *stream);
|
||||
FILE *fdopen(int fd, const char *mode);
|
||||
|
||||
int printf(const char *fmt, ...);
|
||||
int fprintf(FILE *stream, const char *fmt, ...);
|
||||
int sprintf(char *str, const char *fmt, ...);
|
||||
|
||||
@@ -49,10 +49,24 @@ int unsetenv(const char *name);
|
||||
int putenv(char *string);
|
||||
|
||||
void qsort(void *base, size_t nmemb, size_t size,
|
||||
int (*compar)(const void *, const void *));
|
||||
int (*compar)(const void *, const void *));
|
||||
void *bsearch(const void *key, const void *base, size_t nmemb, size_t size,
|
||||
int (*compar)(const void *, const void *));
|
||||
|
||||
long strtol(const char *nptr, char **endptr, int base);
|
||||
unsigned long strtoul(const char *nptr, char **endptr, int base);
|
||||
long long strtoll(const char *nptr, char **endptr, int base);
|
||||
unsigned long long strtoull(const char *nptr, char **endptr, int base);
|
||||
long long atoll(const char *nptr);
|
||||
int mkstemp(char *template_);
|
||||
char *mktemp(char *template_);
|
||||
char *realpath(const char *path, char *resolved);
|
||||
size_t mbstowcs(wchar_t *dst, const char *src, size_t n);
|
||||
#define MB_CUR_MAX 1
|
||||
|
||||
int mblen(const char *s, size_t n);
|
||||
int mbtowc(wchar_t *pwc, const char *s, size_t n);
|
||||
int wctomb(char *s, wchar_t wc);
|
||||
double strtod(const char *nptr, char **endptr);
|
||||
float strtof(const char *nptr, char **endptr);
|
||||
long double strtold(const char *nptr, char **endptr);
|
||||
|
||||
@@ -31,6 +31,9 @@ char *strpbrk(const char *s, const char *accept);
|
||||
int strcasecmp(const char *s1, const char *s2);
|
||||
int strncasecmp(const char *s1, const char *s2, size_t n);
|
||||
int strcoll(const char *s1, const char *s2);
|
||||
size_t strxfrm(char *dest, const char *src, size_t n);
|
||||
char *strtok(char *str, const char *delim);
|
||||
char *strtok_r(char *str, const char *delim, char **saveptr);
|
||||
char *strstr(const char *haystack, const char *needle);
|
||||
const char *strerror(int errnum);
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef _LIBC_SYS_MMAN_H
|
||||
#define _LIBC_SYS_MMAN_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Anonymous-memory mmap over SYS_ALLOC. SYS_ALLOC returns
|
||||
* page-aligned process memory, which is exactly what callers like
|
||||
* GCC's page allocator need. File-backed mappings are not
|
||||
* supported and fail with ENODEV.
|
||||
*/
|
||||
|
||||
#define PROT_NONE 0
|
||||
#define PROT_READ 1
|
||||
#define PROT_WRITE 2
|
||||
#define PROT_EXEC 4
|
||||
|
||||
#define MAP_SHARED 0x01
|
||||
#define MAP_PRIVATE 0x02
|
||||
#define MAP_FIXED 0x10
|
||||
#define MAP_ANONYMOUS 0x20
|
||||
#define MAP_ANON MAP_ANONYMOUS
|
||||
|
||||
#define MAP_FAILED ((void *)-1)
|
||||
|
||||
void *mmap(void *addr, size_t length, int prot, int flags, int fd,
|
||||
long offset);
|
||||
int munmap(void *addr, size_t length);
|
||||
int mprotect(void *addr, size_t length, int prot);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBC_SYS_MMAN_H */
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef _LIBC_SYS_PARAM_H
|
||||
#define _LIBC_SYS_PARAM_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef MAXPATHLEN
|
||||
#define MAXPATHLEN 4096
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#define howmany(x, y) (((x) + ((y) - 1)) / (y))
|
||||
#define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
|
||||
|
||||
#endif /* _LIBC_SYS_PARAM_H */
|
||||
@@ -12,19 +12,55 @@ extern "C" {
|
||||
#define S_IFMT 0170000
|
||||
#define S_IFDIR 0040000
|
||||
#define S_IFREG 0100000
|
||||
#define S_IFCHR 0020000
|
||||
#define S_IFLNK 0120000
|
||||
|
||||
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
|
||||
#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
|
||||
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
|
||||
#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
|
||||
#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
|
||||
#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
|
||||
#define S_ISFIFO(mode) (0)
|
||||
#define S_ISSOCK(mode) (0)
|
||||
#define S_ISBLK(mode) (0)
|
||||
|
||||
/* stat() reports real permission bits: ext2 stores them natively, the
|
||||
ramdisk takes them from the USTAR header, and FAT32 synthesizes them from
|
||||
its read-only attribute. Changing them (chmod) is still not supported. */
|
||||
#define S_IRWXU 0700
|
||||
#define S_IRUSR 0400
|
||||
#define S_IWUSR 0200
|
||||
#define S_IXUSR 0100
|
||||
#define S_IRWXG 0070
|
||||
#define S_IRGRP 0040
|
||||
#define S_IWGRP 0020
|
||||
#define S_IXGRP 0010
|
||||
#define S_IRWXO 0007
|
||||
#define S_IROTH 0004
|
||||
#define S_IWOTH 0002
|
||||
#define S_IXOTH 0001
|
||||
|
||||
struct stat {
|
||||
mode_t st_mode;
|
||||
unsigned long st_size;
|
||||
time_t st_mtime;
|
||||
dev_t st_dev;
|
||||
ino_t st_ino;
|
||||
mode_t st_mode;
|
||||
nlink_t st_nlink;
|
||||
uid_t st_uid;
|
||||
gid_t st_gid;
|
||||
off_t st_size;
|
||||
blksize_t st_blksize;
|
||||
blkcnt_t st_blocks;
|
||||
time_t st_atime;
|
||||
time_t st_mtime;
|
||||
time_t st_ctime;
|
||||
};
|
||||
|
||||
int mkdir(const char *path, unsigned int mode);
|
||||
int stat(const char *path, struct stat *buf);
|
||||
int fstat(int fd, struct stat *buf);
|
||||
int mkdir(const char *path, unsigned int mode);
|
||||
int stat(const char *path, struct stat *buf);
|
||||
int fstat(int fd, struct stat *buf);
|
||||
int lstat(const char *path, struct stat *buf);
|
||||
int chmod(const char *path, mode_t mode);
|
||||
int fchmod(int fd, mode_t mode);
|
||||
mode_t umask(mode_t mask);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Fixed-width and pointer-sized integer types. Freestanding builds get
|
||||
this from the kernel freestanding headers; the cross toolchain from
|
||||
the GCC-provided stdint.h. Hosted code (e.g. libgcov) expects
|
||||
intptr_t to be visible via the stdio/stdlib include chain. */
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -15,6 +21,13 @@ typedef unsigned int mode_t;
|
||||
typedef unsigned int uid_t;
|
||||
typedef unsigned int gid_t;
|
||||
typedef long time_t;
|
||||
typedef unsigned long ino_t;
|
||||
typedef unsigned long dev_t;
|
||||
typedef unsigned long nlink_t;
|
||||
typedef long blkcnt_t;
|
||||
typedef long blksize_t;
|
||||
typedef long suseconds_t;
|
||||
typedef int clockid_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef _LIBC_SYS_WAIT_H
|
||||
#define _LIBC_SYS_WAIT_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* POSIX status decoding. waitpid() encodes a normal exit as code<<8
|
||||
and a killed/crashed child as the signal number in the low bits. */
|
||||
#define WIFEXITED(s) (((s) & 0x7F) == 0)
|
||||
#define WEXITSTATUS(s) (((s) >> 8) & 0xFF)
|
||||
#define WIFSIGNALED(s) (((s) & 0x7F) != 0)
|
||||
#define WTERMSIG(s) ((s) & 0x7F)
|
||||
#define WIFSTOPPED(s) (0)
|
||||
#define WSTOPSIG(s) (0)
|
||||
|
||||
#define WNOHANG 1
|
||||
#define WUNTRACED 2
|
||||
|
||||
pid_t wait(int *status);
|
||||
pid_t waitpid(pid_t pid, int *status, int options);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBC_SYS_WAIT_H */
|
||||
@@ -34,6 +34,9 @@ struct tm *localtime(const time_t *timer);
|
||||
time_t mktime(struct tm *tm);
|
||||
size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);
|
||||
|
||||
char *asctime(const struct tm *tm);
|
||||
char *ctime(const time_t *timep);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -4,11 +4,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define STDIN_FILENO 0
|
||||
#define STDOUT_FILENO 1
|
||||
#define STDERR_FILENO 2
|
||||
|
||||
#define F_OK 0
|
||||
#define X_OK 1
|
||||
#define W_OK 2
|
||||
@@ -22,6 +27,36 @@ int chdir(const char *path);
|
||||
char *getcwd(char *buf, size_t size);
|
||||
int access(const char *path, int mode);
|
||||
int isatty(int fd);
|
||||
int unlink(const char *path);
|
||||
int rmdir(const char *path);
|
||||
int dup(int fd);
|
||||
int dup2(int oldfd, int newfd);
|
||||
pid_t getpid(void);
|
||||
void _exit(int status) __attribute__((noreturn));
|
||||
pid_t fork(void);
|
||||
int execv(const char *path, char *const argv[]);
|
||||
int execve(const char *path, char *const argv[], char *const envp[]);
|
||||
int execvp(const char *file, char *const argv[]);
|
||||
int pipe(int fds[2]);
|
||||
long pathconf(const char *path, int name);
|
||||
int getpagesize(void);
|
||||
long sysconf(int name);
|
||||
|
||||
/* sysconf() names (Linux numbering). */
|
||||
#define _SC_OPEN_MAX 4
|
||||
#define _SC_PAGESIZE 30
|
||||
#define _SC_PAGE_SIZE 30
|
||||
#define _SC_NPROCESSORS_ONLN 84
|
||||
|
||||
/* pathconf() names (Linux numbering). */
|
||||
#define _PC_LINK_MAX 0
|
||||
#define _PC_MAX_CANON 1
|
||||
#define _PC_MAX_INPUT 2
|
||||
#define _PC_NAME_MAX 3
|
||||
#define _PC_PATH_MAX 4
|
||||
#define _PC_PIPE_BUF 5
|
||||
|
||||
extern char **environ;
|
||||
|
||||
unsigned int sleep(unsigned int seconds);
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef _LIBC_UTIME_H
|
||||
#define _LIBC_UTIME_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct utimbuf {
|
||||
time_t actime;
|
||||
time_t modtime;
|
||||
};
|
||||
|
||||
/* Accepted for POSIX compatibility; the Montauk VFS does not support
|
||||
setting file times. */
|
||||
int utime(const char *path, const struct utimbuf *times);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBC_UTIME_H */
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef _LIBC_WCHAR_H
|
||||
#define _LIBC_WCHAR_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef unsigned int wint_t;
|
||||
|
||||
/* Opaque shift state for the (byte-oriented) C locale. */
|
||||
typedef struct {
|
||||
unsigned long __opaque;
|
||||
} mbstate_t;
|
||||
|
||||
#define WEOF ((wint_t)-1)
|
||||
|
||||
/* Byte-oriented C locale only: one byte, one character. */
|
||||
size_t mbstowcs(wchar_t *dst, const char *src, size_t n);
|
||||
int mblen(const char *s, size_t n);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LIBC_WCHAR_H */
|
||||
@@ -123,13 +123,28 @@ namespace heap_detail {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static inline bool grow(uint64_t bytes) {
|
||||
uint64_t pages = (bytes + 0xFFF) / 0x1000;
|
||||
if (pages < 4) pages = 4;
|
||||
// Next slab size for heap growth. The kernel tracks a finite number
|
||||
// of SYS_ALLOC records per process (MaxHeapAllocs), so growing once
|
||||
// per large allocation exhausts them under allocation-heavy loads
|
||||
// (the native ld ran out mid-link). Doubling slabs keep the syscall
|
||||
// count logarithmic in total heap size.
|
||||
inline uint64_t g_grow_slab = 16 * 0x1000;
|
||||
|
||||
void* mem = montauk::alloc(pages * 0x1000);
|
||||
static inline bool grow(uint64_t bytes) {
|
||||
uint64_t want = (bytes + 0xFFF) & ~0xFFFULL;
|
||||
if (want < 0x4000) want = 0x4000;
|
||||
|
||||
uint64_t slab = (want > g_grow_slab) ? want : g_grow_slab;
|
||||
if (g_grow_slab < 4 * 1024 * 1024) g_grow_slab *= 2;
|
||||
|
||||
void* mem = montauk::alloc(slab);
|
||||
if (mem == nullptr && slab > want) {
|
||||
// Big slab refused (low memory): retry with the exact need.
|
||||
slab = want;
|
||||
mem = montauk::alloc(slab);
|
||||
}
|
||||
if (mem == nullptr) return false;
|
||||
insert_overflow(mem, pages * 0x1000);
|
||||
insert_overflow(mem, slab);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -165,6 +165,11 @@ namespace montauk {
|
||||
inline int frename(const char* oldPath, const char* newPath) {
|
||||
return (int)syscall2(montauk::abi::SYS_FRENAME, (uint64_t)oldPath, (uint64_t)newPath);
|
||||
}
|
||||
// Fill *out with metadata (size, timestamps, mode) for the path.
|
||||
// Returns 0 on success, -1 on error or if the filesystem lacks stat support.
|
||||
inline int stat(const char* path, montauk::abi::FileStat* out) {
|
||||
return (int)syscall2(montauk::abi::SYS_STAT, (uint64_t)path, (uint64_t)out);
|
||||
}
|
||||
inline int drivelist(int* outDrives, int max) {
|
||||
return (int)syscall2(montauk::abi::SYS_DRIVELIST, (uint64_t)outDrives, (uint64_t)max);
|
||||
}
|
||||
@@ -321,16 +326,24 @@ namespace montauk {
|
||||
}
|
||||
|
||||
// Process management
|
||||
inline void waitpid(int pid) { syscall1(montauk::abi::SYS_WAITPID, (uint64_t)pid); }
|
||||
// Blocks until pid exits. Returns 0..255 for a normal exit,
|
||||
// 256+signal when the process was killed or crashed.
|
||||
inline int waitpid(int pid) { return (int)syscall1(montauk::abi::SYS_WAITPID, (uint64_t)pid); }
|
||||
|
||||
// Framebuffer
|
||||
inline void fb_info(montauk::abi::FbInfo* info) { syscall1(montauk::abi::SYS_FBINFO, (uint64_t)info); }
|
||||
inline void* fb_map() { return (void*)syscall0(montauk::abi::SYS_FBMAP); }
|
||||
// Page flip between two scanout buffers. index -1 queries support;
|
||||
// index -2 acquires ownership and returns the live front buffer.
|
||||
|
||||
// Page flip between the two scanout buffers. fb_map() maps buffer 1
|
||||
// directly after buffer 0 (at +page_align(height*pitch)). index selects
|
||||
// the buffer to show; the hardware latches it at vblank (tear-free).
|
||||
// flags bit0 = block until the flip has been latched (vsync).
|
||||
// fb_flip(-1, 0) returns 1 when page flipping is available, 0 when not.
|
||||
// fb_flip(-2, 0) acquires ownership and returns the live buffer (0 or 1).
|
||||
inline int64_t fb_flip(int64_t index, uint64_t flags) {
|
||||
return syscall2(montauk::abi::SYS_FBFLIP, (uint64_t)index, flags);
|
||||
}
|
||||
|
||||
inline int display_info(montauk::abi::DisplayInfo* out) {
|
||||
return (int)syscall1(montauk::abi::SYS_DISPLAYINFO, (uint64_t)out);
|
||||
}
|
||||
@@ -562,6 +575,20 @@ namespace montauk {
|
||||
inline int bt_disconnect(const uint8_t* bdAddr) {
|
||||
return (int)syscall1(montauk::abi::SYS_BTDISCONNECT, (uint64_t)bdAddr);
|
||||
}
|
||||
// Change the adapter's BD_ADDR. bdAddr is a 6-byte buffer with bdAddr[0] as
|
||||
// the least-significant octet (same order as BtAdapterInfo::bdAddr). Returns
|
||||
// 0 on success, negative on failure. Persist separately to bluetooth.toml.
|
||||
inline int bt_set_addr(const uint8_t* bdAddr) {
|
||||
return (int)syscall1(montauk::abi::SYS_BTSETADDR, (uint64_t)bdAddr);
|
||||
}
|
||||
// List bonded (paired) devices. Returns count written to buf (may be 0).
|
||||
inline int bt_bonds(montauk::abi::BtBondInfo* buf, int maxCount) {
|
||||
return (int)syscall2(montauk::abi::SYS_BTBONDS, (uint64_t)buf, (uint64_t)maxCount);
|
||||
}
|
||||
// Forget a paired device (removes the bond; it must re-pair next time).
|
||||
inline int bt_forget(const uint8_t* bdAddr) {
|
||||
return (int)syscall1(montauk::abi::SYS_BTFORGET, (uint64_t)bdAddr);
|
||||
}
|
||||
inline int bt_list(montauk::abi::BtDevInfo* buf, int maxCount) {
|
||||
return (int)syscall2(montauk::abi::SYS_BTLIST, (uint64_t)buf, (uint64_t)maxCount);
|
||||
}
|
||||
@@ -569,6 +596,111 @@ namespace montauk {
|
||||
return (int)syscall1(montauk::abi::SYS_BTINFO, (uint64_t)buf);
|
||||
}
|
||||
|
||||
// Wi-Fi. scan() runs a full channel sweep and blocks until it finishes or
|
||||
// timeoutMs elapses, then fills buf with the networks seen; it returns the
|
||||
// number of entries written, or -1 when no adapter is ready.
|
||||
inline int wifi_scan(montauk::abi::WifiNetwork* buf, int maxCount,
|
||||
uint32_t timeoutMs) {
|
||||
return (int)syscall3(montauk::abi::SYS_WIFI_SCAN, (uint64_t)buf,
|
||||
(uint64_t)maxCount, (uint64_t)timeoutMs);
|
||||
}
|
||||
inline int wifi_info(montauk::abi::WifiInfo* out) {
|
||||
return (int)syscall1(montauk::abi::SYS_WIFI_INFO, (uint64_t)out);
|
||||
}
|
||||
// Join a network. Blocks until the link is up or the attempt fails, and
|
||||
// returns 0 or a WIFI_ERR_* code.
|
||||
inline int wifi_connect(const char* ssid, const char* password) {
|
||||
return (int)syscall2(montauk::abi::SYS_WIFI_CONNECT, (uint64_t)ssid,
|
||||
(uint64_t)password);
|
||||
}
|
||||
inline int wifi_disconnect() {
|
||||
return (int)syscall0(montauk::abi::SYS_WIFI_DISCONNECT);
|
||||
}
|
||||
|
||||
// Non-blocking pair for GUI code, which cannot stall for the seconds a
|
||||
// sweep or a handshake takes. scan_start() kicks off a sweep (0 started,
|
||||
// 1 one was already running, -1 no adapter); wifi_info().scanning drops
|
||||
// back to 0 and .scanGeneration moves on when it finishes, and
|
||||
// wifi_results() copies out the table without touching the radio.
|
||||
inline int wifi_scan_start(uint32_t timeoutMs) {
|
||||
return (int)syscall1(montauk::abi::SYS_WIFI_SCAN_START, (uint64_t)timeoutMs);
|
||||
}
|
||||
inline int wifi_results(montauk::abi::WifiNetwork* buf, int maxCount) {
|
||||
return (int)syscall2(montauk::abi::SYS_WIFI_RESULTS, (uint64_t)buf,
|
||||
(uint64_t)maxCount);
|
||||
}
|
||||
// Returns 0 once the join is under way, or a WIFI_ERR_* it failed on
|
||||
// before any frame went out. Watch wifi_info().joining for progress and
|
||||
// .lastError for the outcome.
|
||||
inline int wifi_connect_async(const char* ssid, const char* password) {
|
||||
return (int)syscall2(montauk::abi::SYS_WIFI_CONNECT_ASYNC, (uint64_t)ssid,
|
||||
(uint64_t)password);
|
||||
}
|
||||
|
||||
// List the registered link-layer interfaces. The IP configuration is
|
||||
// global to the stack; it belongs to whichever entry has active = 1.
|
||||
inline int net_interfaces(montauk::abi::NetIfInfo* buf, int maxCount) {
|
||||
return (int)syscall2(montauk::abi::SYS_NETIFS, (uint64_t)buf,
|
||||
(uint64_t)maxCount);
|
||||
}
|
||||
|
||||
// Software-defined radio (Rx). Receivers are identified by index [0, count);
|
||||
// open() returns a handle used by the rest of the calls. Samples are read
|
||||
// as interleaved 8-bit unsigned I/Q (CU8) from the device's ring buffer.
|
||||
inline int sdr_count() {
|
||||
return (int)syscall0(montauk::abi::SYS_SDR_COUNT);
|
||||
}
|
||||
inline int sdr_info(int index, montauk::abi::SdrDeviceInfo* out) {
|
||||
return (int)syscall2(montauk::abi::SYS_SDR_INFO, (uint64_t)index, (uint64_t)out);
|
||||
}
|
||||
inline int sdr_open(int index) {
|
||||
return (int)syscall1(montauk::abi::SYS_SDR_OPEN, (uint64_t)index);
|
||||
}
|
||||
inline int sdr_close(int handle) {
|
||||
return (int)syscall1(montauk::abi::SYS_SDR_CLOSE, (uint64_t)handle);
|
||||
}
|
||||
inline int sdr_start(int handle) {
|
||||
return (int)syscall1(montauk::abi::SYS_SDR_START, (uint64_t)handle);
|
||||
}
|
||||
inline int sdr_stop(int handle) {
|
||||
return (int)syscall1(montauk::abi::SYS_SDR_STOP, (uint64_t)handle);
|
||||
}
|
||||
// Non-blocking: copies up to len bytes of queued I/Q, returns bytes copied.
|
||||
inline int sdr_read(int handle, void* buf, uint32_t len) {
|
||||
return (int)syscall3(montauk::abi::SYS_SDR_READ, (uint64_t)handle, (uint64_t)buf, (uint64_t)len);
|
||||
}
|
||||
inline int sdr_set_param(int handle, int param, uint64_t value) {
|
||||
return (int)syscall3(montauk::abi::SYS_SDR_SETPARAM, (uint64_t)handle, (uint64_t)param, value);
|
||||
}
|
||||
inline int64_t sdr_get_param(int handle, int param) {
|
||||
return syscall2(montauk::abi::SYS_SDR_GETPARAM, (uint64_t)handle, (uint64_t)param);
|
||||
}
|
||||
// Convenience wrappers over sdr_set_param / sdr_get_param.
|
||||
inline int sdr_set_freq(int handle, uint64_t hz) {
|
||||
return sdr_set_param(handle, montauk::abi::SDR_PARAM_FREQ, hz);
|
||||
}
|
||||
inline uint64_t sdr_get_freq(int handle) {
|
||||
return (uint64_t)sdr_get_param(handle, montauk::abi::SDR_PARAM_FREQ);
|
||||
}
|
||||
inline int sdr_set_sample_rate(int handle, uint32_t hz) {
|
||||
return sdr_set_param(handle, montauk::abi::SDR_PARAM_SAMPLE_RATE, hz);
|
||||
}
|
||||
inline uint32_t sdr_get_sample_rate(int handle) {
|
||||
return (uint32_t)sdr_get_param(handle, montauk::abi::SDR_PARAM_SAMPLE_RATE);
|
||||
}
|
||||
inline int sdr_set_gain_mode(int handle, int manual) {
|
||||
return sdr_set_param(handle, montauk::abi::SDR_PARAM_GAIN_MODE, (uint64_t)manual);
|
||||
}
|
||||
inline int sdr_set_gain(int handle, int tenthsDb) {
|
||||
return sdr_set_param(handle, montauk::abi::SDR_PARAM_GAIN, (uint64_t)(int64_t)tenthsDb);
|
||||
}
|
||||
inline int sdr_set_freq_correction(int handle, int ppm) {
|
||||
return sdr_set_param(handle, montauk::abi::SDR_PARAM_FREQ_CORR, (uint64_t)(int64_t)ppm);
|
||||
}
|
||||
inline int sdr_set_agc(int handle, int on) {
|
||||
return sdr_set_param(handle, montauk::abi::SDR_PARAM_AGC, (uint64_t)on);
|
||||
}
|
||||
|
||||
// Kernel introspection
|
||||
inline void memstats(montauk::abi::MemStats* out) { syscall1(montauk::abi::SYS_MEMSTATS, (uint64_t)out); }
|
||||
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* wifi.h
|
||||
* Saved Wi-Fi networks (0:/config/wifi.toml) and small formatting helpers
|
||||
* shared by the desktop panel, the Network app and the wifi command.
|
||||
*
|
||||
* The file looks like this:
|
||||
*
|
||||
* [wifi]
|
||||
* autoconnect = true
|
||||
*
|
||||
* [network.0]
|
||||
* ssid = "Home"
|
||||
* psk = "passphrase"
|
||||
*
|
||||
* The passphrase is stored as typed, because that is what the join needs:
|
||||
* the kernel derives the PMK from it (or takes a 64-character hex string as
|
||||
* a raw PSK). Anyone who can read 0:/config can read the keys.
|
||||
*
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <montauk/config.h>
|
||||
#include <montauk/string.h>
|
||||
#include <montauk/syscall.h>
|
||||
|
||||
namespace montauk {
|
||||
namespace wifi {
|
||||
|
||||
// The scan table the kernel keeps is 64 entries; saving that many networks
|
||||
// is already far more than a laptop accumulates.
|
||||
static constexpr int MAX_SAVED = 32;
|
||||
static constexpr int SSID_CAP = 36;
|
||||
static constexpr int PSK_CAP = 72; // 64-character hex PSK plus NUL
|
||||
|
||||
struct SavedNetwork {
|
||||
char ssid[SSID_CAP];
|
||||
char psk[PSK_CAP];
|
||||
};
|
||||
|
||||
struct SavedList {
|
||||
SavedNetwork items[MAX_SAVED];
|
||||
int count;
|
||||
bool autoconnect;
|
||||
};
|
||||
|
||||
// ---- helpers -----------------------------------------------------------
|
||||
|
||||
inline void copy_str(char* dst, int cap, const char* src) {
|
||||
int i = 0;
|
||||
for (; src && src[i] && i < cap - 1; i++) dst[i] = src[i];
|
||||
dst[i] = '\0';
|
||||
}
|
||||
|
||||
// "network.<index>.<field>"
|
||||
inline void network_key(char* out, int cap, int index, const char* field) {
|
||||
char idx[8];
|
||||
int n = 0;
|
||||
if (index == 0) {
|
||||
idx[n++] = '0';
|
||||
} else {
|
||||
char tmp[8];
|
||||
int t = 0;
|
||||
for (int v = index; v > 0 && t < (int)sizeof(tmp); v /= 10)
|
||||
tmp[t++] = (char)('0' + (v % 10));
|
||||
while (t > 0) idx[n++] = tmp[--t];
|
||||
}
|
||||
idx[n] = '\0';
|
||||
|
||||
int p = 0;
|
||||
const char* prefix = "network.";
|
||||
while (*prefix && p < cap - 1) out[p++] = *prefix++;
|
||||
for (int i = 0; i < n && p < cap - 1; i++) out[p++] = idx[i];
|
||||
if (p < cap - 1) out[p++] = '.';
|
||||
while (field && *field && p < cap - 1) out[p++] = *field++;
|
||||
out[p] = '\0';
|
||||
}
|
||||
|
||||
// ---- load / store ------------------------------------------------------
|
||||
|
||||
inline void saved_load(SavedList* out) {
|
||||
if (!out) return;
|
||||
out->count = 0;
|
||||
out->autoconnect = true;
|
||||
|
||||
auto doc = montauk::config::load("wifi");
|
||||
out->autoconnect = doc.get_bool("wifi.autoconnect", true);
|
||||
|
||||
for (int i = 0; i < MAX_SAVED; i++) {
|
||||
char key[64];
|
||||
network_key(key, sizeof(key), i, "ssid");
|
||||
const char* ssid = doc.get_string(key, nullptr);
|
||||
if (!ssid || !ssid[0]) break; // entries are written contiguously
|
||||
|
||||
network_key(key, sizeof(key), i, "psk");
|
||||
const char* psk = doc.get_string(key, "");
|
||||
|
||||
SavedNetwork& n = out->items[out->count++];
|
||||
copy_str(n.ssid, SSID_CAP, ssid);
|
||||
copy_str(n.psk, PSK_CAP, psk);
|
||||
}
|
||||
|
||||
doc.destroy();
|
||||
}
|
||||
|
||||
// Rewrite the file from the list. Returns 0 on success.
|
||||
inline int saved_store(const SavedList* list) {
|
||||
if (!list) return -1;
|
||||
|
||||
montauk::toml::Doc doc;
|
||||
doc.init();
|
||||
montauk::config::set_bool(&doc, "wifi.autoconnect", list->autoconnect);
|
||||
|
||||
for (int i = 0; i < list->count && i < MAX_SAVED; i++) {
|
||||
char key[64];
|
||||
network_key(key, sizeof(key), i, "ssid");
|
||||
montauk::config::set_string(&doc, key, list->items[i].ssid);
|
||||
network_key(key, sizeof(key), i, "psk");
|
||||
montauk::config::set_string(&doc, key, list->items[i].psk);
|
||||
}
|
||||
|
||||
int rc = montauk::config::save("wifi", &doc);
|
||||
doc.destroy();
|
||||
return rc;
|
||||
}
|
||||
|
||||
inline int saved_index_of(const SavedList* list, const char* ssid) {
|
||||
if (!list || !ssid) return -1;
|
||||
for (int i = 0; i < list->count; i++) {
|
||||
if (montauk::streq(list->items[i].ssid, ssid)) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
inline const SavedNetwork* saved_find(const SavedList* list, const char* ssid) {
|
||||
int idx = saved_index_of(list, ssid);
|
||||
return idx < 0 ? nullptr : &list->items[idx];
|
||||
}
|
||||
|
||||
// Add or update an entry in memory. Returns false when the list is full.
|
||||
inline bool saved_set(SavedList* list, const char* ssid, const char* psk) {
|
||||
if (!list || !ssid || !ssid[0]) return false;
|
||||
int idx = saved_index_of(list, ssid);
|
||||
if (idx < 0) {
|
||||
if (list->count >= MAX_SAVED) return false;
|
||||
idx = list->count++;
|
||||
copy_str(list->items[idx].ssid, SSID_CAP, ssid);
|
||||
}
|
||||
copy_str(list->items[idx].psk, PSK_CAP, psk ? psk : "");
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool saved_remove(SavedList* list, const char* ssid) {
|
||||
int idx = saved_index_of(list, ssid);
|
||||
if (idx < 0) return false;
|
||||
for (int i = idx; i < list->count - 1; i++) list->items[i] = list->items[i + 1];
|
||||
list->count--;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Convenience wrappers that touch the file directly.
|
||||
|
||||
inline bool remember(const char* ssid, const char* psk) {
|
||||
SavedList list;
|
||||
saved_load(&list);
|
||||
if (!saved_set(&list, ssid, psk)) return false;
|
||||
return saved_store(&list) == 0;
|
||||
}
|
||||
|
||||
inline bool forget(const char* ssid) {
|
||||
SavedList list;
|
||||
saved_load(&list);
|
||||
if (!saved_remove(&list, ssid)) return false;
|
||||
return saved_store(&list) == 0;
|
||||
}
|
||||
|
||||
// Copy the saved passphrase for `ssid` into out. False when not saved.
|
||||
inline bool lookup(const char* ssid, char* out, int cap) {
|
||||
SavedList list;
|
||||
saved_load(&list);
|
||||
const SavedNetwork* n = saved_find(&list, ssid);
|
||||
if (!n) return false;
|
||||
copy_str(out, cap, n->psk);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---- presentation ------------------------------------------------------
|
||||
|
||||
inline const char* security_name(uint8_t security) {
|
||||
switch (security) {
|
||||
case montauk::abi::WIFI_SEC_OPEN: return "Open";
|
||||
case montauk::abi::WIFI_SEC_WEP: return "WEP";
|
||||
case montauk::abi::WIFI_SEC_WPA: return "WPA";
|
||||
case montauk::abi::WIFI_SEC_WPA2: return "WPA2";
|
||||
case montauk::abi::WIFI_SEC_WPA3: return "WPA3";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
inline bool needs_key(uint8_t security) {
|
||||
return security != montauk::abi::WIFI_SEC_OPEN;
|
||||
}
|
||||
|
||||
// 0-4 bars from an RSSI in dBm.
|
||||
inline int signal_bars(int8_t rssi) {
|
||||
if (rssi >= -55) return 4;
|
||||
if (rssi >= -67) return 3;
|
||||
if (rssi >= -75) return 2;
|
||||
if (rssi >= -85) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline const char* state_name(uint8_t state) {
|
||||
switch (state) {
|
||||
case montauk::abi::WIFI_STATE_ABSENT: return "No adapter";
|
||||
case montauk::abi::WIFI_STATE_DETECTED: return "Loading firmware";
|
||||
case montauk::abi::WIFI_STATE_BOOTING: return "Starting";
|
||||
case montauk::abi::WIFI_STATE_RUNNING: return "Ready";
|
||||
case montauk::abi::WIFI_STATE_ERROR: return "Adapter error";
|
||||
case montauk::abi::WIFI_STATE_RFKILL: return "Radio off";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// What the join is doing right now, for a progress line.
|
||||
inline const char* conn_state_name(uint32_t connState) {
|
||||
switch (connState) {
|
||||
case montauk::abi::WIFI_CONN_IDLE: return "Not connected";
|
||||
case montauk::abi::WIFI_CONN_CONTEXTS_UP: return "Preparing radio...";
|
||||
case montauk::abi::WIFI_CONN_AUTHENTICATING: return "Authenticating...";
|
||||
case montauk::abi::WIFI_CONN_AUTHENTICATED: return "Authenticated";
|
||||
case montauk::abi::WIFI_CONN_ASSOCIATING: return "Associating...";
|
||||
case montauk::abi::WIFI_CONN_ASSOCIATED: return "Associated";
|
||||
case montauk::abi::WIFI_CONN_HANDSHAKING: return "Exchanging keys...";
|
||||
case montauk::abi::WIFI_CONN_CONNECTED: return "Connected";
|
||||
case montauk::abi::WIFI_CONN_FAILED: return "Connection failed";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
inline const char* error_message(int err) {
|
||||
switch (err) {
|
||||
case 0: return "";
|
||||
case montauk::abi::WIFI_ERR_NO_ADAPTER: return "No Wi-Fi adapter is ready";
|
||||
case montauk::abi::WIFI_ERR_NOT_FOUND: return "That network is out of range";
|
||||
case montauk::abi::WIFI_ERR_NEED_KEY: return "This network needs a password";
|
||||
case montauk::abi::WIFI_ERR_UNSUPPORTED: return "This security type is not supported";
|
||||
case montauk::abi::WIFI_ERR_AUTH: return "Wrong password";
|
||||
case montauk::abi::WIFI_ERR_TIMEOUT: return "The network did not respond";
|
||||
default: return "Could not join the network";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wifi
|
||||
} // namespace montauk
|
||||
Reference in New Issue
Block a user