diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d928886d..833dce06 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,7 +25,7 @@ jobs: with: go-version: '1.22' - name: install tools - run: sudo apt update && sudo apt install xorriso mtools grub-pc-bin zip gcc-i686-linux-gnu qemu-system-x86 vncsnapshot curl nasm jq -y + run: sudo apt update && sudo apt install xorriso mtools grub-pc-bin zip gcc-i686-linux-gnu qemu-system-x86 vncsnapshot curl nasm jq libunicorn-dev -y - name: install fire run: | git clone https://github.com/Glowman554/FireStorm @@ -49,4 +49,5 @@ jobs: libs.zip mckrnl/mckrnl.elf mckrnl/mckrnl.syms - res/initrd.saf \ No newline at end of file + res/initrd.saf + res/initrd-install.saf \ No newline at end of file diff --git a/Makefile b/Makefile index 5141c83d..6ec459a9 100644 --- a/Makefile +++ b/Makefile @@ -122,7 +122,7 @@ libs.zip: all cp user/libraries/libsyntax/include/* res/libs/include/. -rf ifeq ($(GUI),1) - cp user/base/desktop/include/* res/libs/include/. -rf + cp user/desktop/libdesktop/include/* res/libs/include/. -rf endif zip -r libs.zip res/libs/ diff --git a/release.sh b/release.sh index 20381b67..4a32350a 100644 --- a/release.sh +++ b/release.sh @@ -8,8 +8,8 @@ WEBHOOK_URL="$MESSAGE_WEBHOOK" BUILD_TOKEN="$MICROOS_BUILD_TOKEN" function do_release { - if [[ "$#" -lt 9 ]]; then - echo "Usage: release " + if [[ "$#" -lt 10 ]]; then + echo "Usage: release " exit 1 fi @@ -22,11 +22,12 @@ function do_release { local kernel="$7" local symbols="$8" local initrd="$9" + local initrd_install="${10}" echo "Releasing: $name" send_webhook "$name" "$cdrom" "$cdromMinimal" "$libs" "$message" "$screenshot" "$kernel" "$symbols" "$initrd" - trigger_build "$name" "$kernel" "$symbols" "$initrd" + trigger_build "$name" "$kernel" "$symbols" "$initrd_install" } function send_webhook { @@ -116,8 +117,25 @@ function release { local kernel=$(upload_file "mckrnl/core/mckrnl.elf") local symbols=$(upload_file "mckrnl/core/mckrnl.syms") local initrd=$(upload_file "res/initrd.saf") + local initrd_install=$(upload_file "res/initrd-install.saf") - do_release "$1" "$cdrom" "$cdrom_minimal" "$libs" "$2" "$screenshot" "$kernel" "$symbols" "$initrd" + do_release "$1" "$cdrom" "$cdrom_minimal" "$libs" "$2" "$screenshot" "$kernel" "$symbols" "$initrd" "$initrd_install" +} + +function install_pkgs_initrd { + make -C tools/microemu -B + ( + cp res/initrd res/initrd-install -rv + cd res/initrd-install +../../tools/microemu/microemu root:/bin/init.mex bin/terminal.mex < +#include +#include +#include +#include +#include +#include +#include + +void hostfs_resolve(hostfs_t *hfs, const char *vpath, char *out, size_t out_size) { + snprintf(out, out_size, "%s%s", hfs->host_root, vpath); +} + +file_t *hostfs_open(vfs_mount_t *mount, char *path, int flags) { + hostfs_t *hfs = (hostfs_t *)mount; + char resolved[1024]; + hostfs_resolve(hfs, path, resolved, sizeof(resolved)); + + struct stat st; + if (stat(resolved, &st) == 0 && S_ISDIR(st.st_mode)) { + return NULL; + } + + int oflags = O_RDONLY; + if (flags == FILE_OPEN_MODE_WRITE) { + oflags = O_WRONLY | O_CREAT; + } else if (flags == FILE_OPEN_MODE_READ_WRITE) { + oflags = O_RDWR | O_CREAT; + } + + int fd = open(resolved, oflags, 0644); + if (fd < 0) { + return NULL; + } + + file_t *f = calloc(1, sizeof(file_t)); + f->mount = mount; + f->mode = flags; + f->driver_specific_data = (void *)(intptr_t)fd; + strncpy(f->path, path, sizeof(f->path) - 1); + + if (fstat(fd, &st) == 0) { + f->size = st.st_size; + } + + return f; +} + +void hostfs_close(vfs_mount_t *mount, file_t *file) { + int fd = (int)(intptr_t)file->driver_specific_data; + close(fd); + free(file); +} + +void hostfs_read(vfs_mount_t *mount, file_t *file, void *buf, size_t size, size_t offset) { + int fd = (int)(intptr_t)file->driver_specific_data; + pread(fd, buf, size, offset); +} + +void hostfs_write(vfs_mount_t *mount, file_t *file, void *buf, size_t size, size_t offset) { + int fd = (int)(intptr_t)file->driver_specific_data; + pwrite(fd, buf, size, offset); +} + +void hostfs_delete(vfs_mount_t *mount, file_t *file) { + hostfs_t *hfs = (hostfs_t *)mount; + char resolved[1024]; + hostfs_resolve(hfs, file->path, resolved, sizeof(resolved)); + int fd = (int)(intptr_t)file->driver_specific_data; + close(fd); + unlink(resolved); + free(file); +} + +void hostfs_mkdir(vfs_mount_t *mount, char *path) { + hostfs_t *hfs = (hostfs_t *)mount; + char resolved[1024]; + hostfs_resolve(hfs, path, resolved, sizeof(resolved)); + mkdir(resolved, 0755); +} + +void hostfs_touch(vfs_mount_t *mount, char *path) { + hostfs_t *hfs = (hostfs_t *)mount; + char resolved[1024]; + hostfs_resolve(hfs, path, resolved, sizeof(resolved)); + int fd = open(resolved, O_CREAT | O_WRONLY, 0644); + if (fd >= 0) { + close(fd); + } +} + +dir_t hostfs_dir_at(vfs_mount_t *mount, int idx, char *path) { + hostfs_t *hfs = (hostfs_t *)mount; + char resolved[1024]; + hostfs_resolve(hfs, path, resolved, sizeof(resolved)); + + dir_t d = { .is_none = true }; + + DIR *dp = opendir(resolved); + if (!dp) { + return d; + } + + struct dirent *ent; + int i = 0; + while ((ent = readdir(dp)) != NULL) { + if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) { + continue; + } + + if (i == idx) { + strncpy(d.name, ent->d_name, 256); + d.name[255] = '\0'; + d.idx = idx; + d.is_none = false; + d.type = (ent->d_type == DT_DIR) ? ENTRY_DIR : ENTRY_FILE; + break; + } + i++; + } + closedir(dp); + return d; +} + +void hostfs_delete_dir(vfs_mount_t *mount, char *path) { + hostfs_t *hfs = (hostfs_t *)mount; + char resolved[1024]; + hostfs_resolve(hfs, path, resolved, sizeof(resolved)); + rmdir(resolved); +} + +void hostfs_truncate(vfs_mount_t *mount, file_t *file, size_t new_size) { + int fd = (int)(intptr_t)file->driver_specific_data; + ftruncate(fd, new_size); + file->size = new_size; +} + +char *hostfs_name(vfs_mount_t *mount) { + hostfs_t *hfs = (hostfs_t *)mount; + return hfs->mount_name; +} + +hostfs_t *hostfs_create(const char *mount_name, const char *host_dir) { + hostfs_t *hfs = calloc(1, sizeof(hostfs_t)); + strncpy(hfs->mount_name, mount_name, sizeof(hfs->mount_name) - 1); + strncpy(hfs->host_root, host_dir, sizeof(hfs->host_root) - 1); + + hfs->mount.open = hostfs_open; + hfs->mount.close = hostfs_close; + hfs->mount.read = hostfs_read; + hfs->mount.write = hostfs_write; + hfs->mount._delete = hostfs_delete; + hfs->mount.mkdir = hostfs_mkdir; + hfs->mount.touch = hostfs_touch; + hfs->mount.dir_at = hostfs_dir_at; + hfs->mount.delete_dir = hostfs_delete_dir; + hfs->mount.truncate = hostfs_truncate; + hfs->mount.name = hostfs_name; + + return hfs; +} diff --git a/tools/microemu/fs/ramfs.c b/tools/microemu/fs/ramfs.c new file mode 100644 index 00000000..bb4a0c4f --- /dev/null +++ b/tools/microemu/fs/ramfs.c @@ -0,0 +1,240 @@ +#include +#include + +#include +#include +#include +#include + +char* copy_until(char until, char* src, char* dest) { + int i = 0; + while (src[i] != '\0' && src[i] != until) { + dest[i] = src[i]; + i++; + } + dest[i] = 0; + + return &src[i + (src[i] == '\0' ? 0 : 1)]; +} + +char* ramfs_name(vfs_mount_t* mount) { + return (char*) (&mount[1]); +} + +ramfs_node_t* ramfs_find(char* path, ramfs_node_t* current) { + // debugf("ramfs_find(%s, %x, %s)", path, current, current->name); + + while (*path == '/') { + path++; + } + if (*path == 0) { + return current; + } + + char buffer[128] = { 0 }; + char* next = copy_until('/', path, buffer); + + for (int i = 0; i < sizeof(current->childs) / sizeof(current->childs[0]); i++) { + if (current->childs[i] && strcasecmp(buffer, current->childs[i]->name) == 0) { + return ramfs_find(next, current->childs[i]); + } + } + + return NULL; +} + +char* ramfs_split_path(char* path, char* out) { + char buffer[128] = { 0 }; + char* next = path; + char* to_create = NULL; + while (*(next = copy_until('/', next, buffer))) { + to_create = next; + } + + memcpy(out, path, to_create - path - 1); + + return to_create; +} + +file_t* ramfs_open(vfs_mount_t* mount, char* path, int flags) { + ramfs_node_t* node = ramfs_find(path, mount->driver_specific_data); + if (node == NULL || node->type != NODE_FILE) { + return NULL; + } + + file_t* f = (file_t*) malloc(sizeof(file_t)); + f->mount = mount; + f->size = node->fsize; + f->driver_specific_data = node; + + return f; +} + + +void ramfs_close(vfs_mount_t* mount, file_t* file) { + free(file); +} + +void ramfs_read(vfs_mount_t* mount, file_t* file, void* buf, size_t size, size_t offset) { + ramfs_node_t* node = (ramfs_node_t*) file->driver_specific_data; + + assert(node->fsize >= offset + size); + + memcpy(buf, node->fdata + offset, size); +} + +void ramfs_write(vfs_mount_t* mount, file_t* file, void* buf, size_t size, size_t offset) { + ramfs_node_t* node = (ramfs_node_t*) file->driver_specific_data; + + int new_file_size = offset + size; + if (new_file_size > node->fsize) { + node->fdata = realloc(node->fdata, new_file_size); + node->fsize = new_file_size; + } + + file->size = node->fsize; + + memcpy(node->fdata + offset, buf, size); +} + +void ramfs_truncate(vfs_mount_t* mount, file_t* file, size_t new_size) { + ramfs_node_t* node = (ramfs_node_t*) file->driver_specific_data; + node->fdata = realloc(node->fdata, new_size); + node->fsize = new_size; + file->size = node->fsize; +} + +void ramfs_delete(vfs_mount_t* mount, file_t* file) { + fatalf("ramfs_delete not implemented"); +} + +void ramfs_mkdir(vfs_mount_t* mount, char* path) { + if (ramfs_find(path, mount->driver_specific_data)) { + return; + } + + char path_buf[512] = { 0 }; + char* to_create = ramfs_split_path(path, path_buf); + + ramfs_node_t* node = ramfs_find(path_buf, mount->driver_specific_data); + if (node == NULL || node->type != NODE_DIR) { + return; + } + + for (int i = 0; i < sizeof(node->childs) / sizeof(node->childs[0]); i++) { + if (!node->childs[i]) { + ramfs_node_t* child = (ramfs_node_t*) malloc(sizeof(ramfs_node_t)); + memset(child, 0, sizeof(ramfs_node_t)); + child->type = NODE_DIR; + strcpy(child->name, to_create); + + node->childs[i] = child; + return; + } + } +} + +void ramfs_touch(vfs_mount_t* mount, char* path) { + if (ramfs_find(path, mount->driver_specific_data)) { + return; + } + + char path_buf[512] = { 0 }; + char* to_create = ramfs_split_path(path, path_buf); + + ramfs_node_t* node = ramfs_find(path_buf, mount->driver_specific_data); + if (node == NULL || node->type != NODE_DIR) { + return; + } + + for (int i = 0; i < sizeof(node->childs) / sizeof(node->childs[0]); i++) { + if (!node->childs[i]) { + ramfs_node_t* child = (ramfs_node_t*) malloc(sizeof(ramfs_node_t)); + memset(child, 0, sizeof(ramfs_node_t)); + child->type = NODE_FILE; + strcpy(child->name, to_create); + + node->childs[i] = child; + return; + } + } +} + +dir_t ramfs_dir_at(vfs_mount_t* mount, int idx, char* path) { + ramfs_node_t* node = ramfs_find(path, mount->driver_specific_data); + if (node == NULL || node->type != NODE_DIR) { + return (dir_t) { .is_none = true }; + } + + int actual_idx = 0; + int current_idx = idx + 1; + while (true) { + if (node->childs[actual_idx]) { + current_idx--; + } + if (!current_idx) { + break; + } else { + actual_idx++; + if (actual_idx >= sizeof(node->childs) / sizeof(node->childs[0])) { + return (dir_t) { .is_none = true }; + } + } + } + + dir_t dir = { 0 }; + dir.idx = idx; + dir.is_none = false; + + switch (node->childs[actual_idx]->type) { + case NODE_DIR: + dir.type = ENTRY_DIR; + break; + case NODE_FILE: + dir.type = ENTRY_FILE; + break; + default: + return (dir_t) { .is_none = true }; + } + + strcpy(dir.name, node->childs[actual_idx]->name); + + return dir; +} + + +void ramfs_delete_dir(vfs_mount_t* mount, char* path) { + fatalf("ramfs_delete_dir not implemented"); +} + + +vfs_mount_t* get_ramfs(char* name) { + int name_len = strlen(name); + vfs_mount_t* mount = (vfs_mount_t*) malloc(sizeof(vfs_mount_t) + name_len + 1); + memset(mount, 0, sizeof(vfs_mount_t) + name_len + 1); + + mount->open = ramfs_open; + mount->close = ramfs_close; + mount->read = ramfs_read; + mount->write = ramfs_write; + mount->_delete = ramfs_delete; + mount->mkdir = ramfs_mkdir; + mount->touch = ramfs_touch; + + mount->dir_at = ramfs_dir_at; + mount->delete_dir = ramfs_delete_dir; + + mount->truncate = ramfs_truncate; + + mount->name = ramfs_name; + + strcpy((char*) (&mount[1]), name); + + ramfs_node_t* root = (ramfs_node_t*) malloc(sizeof(ramfs_node_t)); + memset(root, 0, sizeof(ramfs_node_t)); + root->type = NODE_DIR; + + mount->driver_specific_data = root; + + return mount; +} \ No newline at end of file diff --git a/tools/microemu/fs/vfs.c b/tools/microemu/fs/vfs.c new file mode 100644 index 00000000..7086e99c --- /dev/null +++ b/tools/microemu/fs/vfs.c @@ -0,0 +1,154 @@ +#include +#include +#include +#include + +vfs_mount_t *mounts[VFS_MAX_MOUNTS]; +int num_mounts = 0; + +file_t *fd_table[VFS_MAX_FDS]; + +void vfs_init(void) { + num_mounts = 0; + memset(fd_table, 0, sizeof(fd_table)); +} + +void vfs_mount(vfs_mount_t *mount) { + if (num_mounts < VFS_MAX_MOUNTS) { + mounts[num_mounts++] = mount; + } +} + +vfs_mount_t *vfs_find_mount(char *path, char *file_path_out) { + char buf[512]; + strncpy(buf, path, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + + for (int i = 0; buf[i]; i++) { + if (buf[i] == ':') { + buf[i] = '\0'; + strcpy(file_path_out, &buf[i + 1]); + + for (int j = 0; j < num_mounts; j++) { + if (strcmp(mounts[j]->name(mounts[j]), buf) == 0) { + return mounts[j]; + } + } + return NULL; + } + } + return NULL; +} + +file_t *vfs_open(char *path, int flags) { + char file_path[512]; + vfs_mount_t *m = vfs_find_mount(path, file_path); + if (!m || !m->open) { + return NULL; + } + + file_t *f = m->open(m, file_path, flags); + if (f) { + f->mount = m; + } + + return f; +} + +void vfs_close(file_t *file) { + if (file && file->mount && file->mount->close) { + file->mount->close(file->mount, file); + } +} + +void vfs_read(file_t *file, void *buf, size_t size, size_t offset) { + if (file && file->mount && file->mount->read) { + file->mount->read(file->mount, file, buf, size, offset); + } +} + +void vfs_write(file_t *file, void *buf, size_t size, size_t offset) { + if (file && file->mount && file->mount->write) { + file->mount->write(file->mount, file, buf, size, offset); + } +} + +void vfs_delete(file_t *file) { + if (file && file->mount && file->mount->_delete) { + file->mount->_delete(file->mount, file); + } +} + +void vfs_truncate(file_t *file, size_t new_size) { + if (file && file->mount && file->mount->truncate) { + file->mount->truncate(file->mount, file, new_size); + } +} + +void vfs_mkdir(char *path) { + char file_path[512]; + vfs_mount_t *m = vfs_find_mount(path, file_path); + if (m && m->mkdir) { + m->mkdir(m, file_path); + } +} + +void vfs_touch(char *path) { + char file_path[512]; + vfs_mount_t *m = vfs_find_mount(path, file_path); + if (m && m->touch) { + m->touch(m, file_path); + } +} + +dir_t vfs_dir_at(int idx, char *path) { + char file_path[512]; + dir_t d = { .is_none = true }; + vfs_mount_t *m = vfs_find_mount(path, file_path); + if (m && m->dir_at) { + d = m->dir_at(m, idx, file_path); + } + return d; +} + +void vfs_delete_dir(char *path) { + char file_path[512]; + vfs_mount_t *m = vfs_find_mount(path, file_path); + if (m && m->delete_dir) { + m->delete_dir(m, file_path); + } +} + +bool vfs_fs_at(int idx, char *out) { + if (idx < 0 || idx >= num_mounts) { + return false; + } + + strcpy(out, mounts[idx]->name(mounts[idx])); + return true; +} + +int file_to_fd(file_t *file) { + for (int i = 0; i < VFS_MAX_FDS; i++) { + if (!fd_table[i]) { + fd_table[i] = file; + return i + VFS_FD_OFFSET; + } + } + return -1; +} + +file_t *fd_to_file(int fd) { + int idx = fd - VFS_FD_OFFSET; + if (idx < 0 || idx >= VFS_MAX_FDS) { + return NULL; + } + return fd_table[idx]; +} + +void fd_free(int fd) { + int idx = fd - VFS_FD_OFFSET; + if (idx >= 0 && idx < VFS_MAX_FDS) { + fd_table[idx] = NULL; + } +} diff --git a/tools/microemu/include/emu.h b/tools/microemu/include/emu.h new file mode 100644 index 00000000..65e76670 --- /dev/null +++ b/tools/microemu/include/emu.h @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ARGS_ADDR 0x20000000 +#define ARGS_SIZE 0x00100000 + +#define STACK_ADDR 0x0ff00000 +#define STACK_SIZE 0x00100000 + + + +#define fatalf(...) do { \ + fprintf(stderr, __VA_ARGS__); \ +} while (0) + +extern const char *testcmd; +extern int testcmd_pos; +extern uint32_t argv_addr; +extern uint32_t envp_addr; +extern char emu_pwd[512]; + +void read_emu_string(uc_engine *uc, uint32_t addr, char *buf, size_t max); + +void sys_open(uc_engine *uc, uint32_t ebx, uint32_t ecx); +void sys_close(uint32_t ebx); +void sys_read(uc_engine *uc, uint32_t ebx, uint32_t ecx, uint32_t edx, uint32_t esi); +void sys_write(uc_engine *uc, uint32_t ebx, uint32_t ecx, uint32_t edx, uint32_t esi); +void sys_filesize(uc_engine *uc, uint32_t ebx); +void sys_delete(uint32_t ebx); +void sys_mkdir(uc_engine *uc, uint32_t ebx); +void sys_dir_at(uc_engine *uc, uint32_t ebx, uint32_t ecx, uint32_t edx); +void sys_touch(uc_engine *uc, uint32_t ebx); +void sys_delete_dir(uc_engine *uc, uint32_t ebx); +void sys_fs_at(uc_engine *uc, uint32_t ebx, uint32_t ecx); +void sys_truncate(uint32_t ebx, uint32_t ecx); + +void sys_env(uc_engine *uc, uint32_t ebx, uint32_t ecx); + +void sys_mmap(uc_engine *uc, uint32_t ebx); +void sys_mmap_mapped(uc_engine *uc, uint32_t ebx); + +void sys_async_getc(uc_engine *uc); +void sys_async_getarrw(uc_engine *uc); +void sys_vmode(uc_engine *uc); +void sys_set_color(uc_engine *uc, uint32_t ebx); +void sys_rgb_color(uint32_t ebx); +void sys_vcursor_get(uc_engine *uc, uint32_t ebx, uint32_t ecx); +void sys_time(uc_engine *uc); +void sys_time_ms(uc_engine *uc); +void sys_raminfo(uc_engine *uc); +void sys_yield(uc_engine *uc); +void sys_exit(uc_engine *uc, uint32_t ebx); + +void sys_spawn(uc_engine *uc, uint32_t ebx, uint32_t ecx, uint32_t edx); +void sys_get_proc_info(uc_engine *uc, uint32_t ebx); +void sys_kill(uc_engine *uc, uint32_t ebx); +void sys_get_exit_code(uc_engine *uc, uint32_t ebx); + +void sys_set_term(uint32_t ebx, uint32_t ecx); + +void sys_message_send(uc_engine *uc, uint32_t ebx, uint32_t ecx, uint32_t edx); +void sys_message_recv(uc_engine *uc, uint32_t ebx, uint32_t ecx, uint32_t edx); diff --git a/tools/microemu/include/fs/hostfs.h b/tools/microemu/include/fs/hostfs.h new file mode 100644 index 00000000..47d44ab2 --- /dev/null +++ b/tools/microemu/include/fs/hostfs.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +typedef struct hostfs { + vfs_mount_t mount; + char mount_name[64]; + char host_root[512]; +} hostfs_t; + +hostfs_t *hostfs_create(const char *mount_name, const char *host_dir); diff --git a/tools/microemu/include/fs/ramfs.h b/tools/microemu/include/fs/ramfs.h new file mode 100644 index 00000000..ad80e13e --- /dev/null +++ b/tools/microemu/include/fs/ramfs.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +typedef enum ramfs_node_type { + NODE_FILE = 1, + NODE_DIR +} ramfs_node_type; + +typedef struct ramfs_node { + char name[92]; + ramfs_node_type type; + union { + struct ramfs_node* childs[1000]; + struct { + int fsize; + void* fdata; + }; + }; +} ramfs_node_t; + +vfs_mount_t* get_ramfs(char* name); \ No newline at end of file diff --git a/tools/microemu/include/fs/vfs.h b/tools/microemu/include/fs/vfs.h new file mode 100644 index 00000000..83d37d64 --- /dev/null +++ b/tools/microemu/include/fs/vfs.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include + +#define VFS_MAX_MOUNTS 16 +#define VFS_MAX_FDS 256 +#define VFS_FD_OFFSET 5 + +enum file_open_mode { + FILE_OPEN_MODE_READ, + FILE_OPEN_MODE_WRITE, + FILE_OPEN_MODE_READ_WRITE +}; + +enum dir_entry_type { + ENTRY_FILE, + ENTRY_DIR +}; + +typedef struct vfs_mount vfs_mount_t; + +typedef struct file { + vfs_mount_t *mount; + int mode; + void *driver_specific_data; + char path[512]; + size_t size; +} file_t; + +typedef struct dir_entry { + char name[256]; + int idx; + bool is_none; + int type; +} dir_t; + +struct vfs_mount { + file_t *(*open)(vfs_mount_t *mount, char *path, int flags); + void (*close)(vfs_mount_t *mount, file_t *file); + void (*read)(vfs_mount_t *mount, file_t *file, void *buf, size_t size, size_t offset); + void (*write)(vfs_mount_t *mount, file_t *file, void *buf, size_t size, size_t offset); + void (*_delete)(vfs_mount_t *mount, file_t *file); + void (*mkdir)(vfs_mount_t *mount, char *path); + void (*touch)(vfs_mount_t *mount, char *path); + dir_t (*dir_at)(vfs_mount_t *mount, int idx, char *path); + void (*delete_dir)(vfs_mount_t *mount, char *path); + void (*truncate)(vfs_mount_t *mount, file_t *file, size_t new_size); + char *(*name)(vfs_mount_t *mount); + void *driver_specific_data; +}; + +void vfs_init(void); +void vfs_mount(vfs_mount_t *mount); +vfs_mount_t *vfs_find_mount(char *path, char *file_path_out); + +file_t *vfs_open(char *path, int flags); +void vfs_close(file_t *file); +void vfs_read(file_t *file, void *buf, size_t size, size_t offset); +void vfs_write(file_t *file, void *buf, size_t size, size_t offset); +void vfs_delete(file_t *file); +void vfs_truncate(file_t *file, size_t new_size); + +// Directory operations +void vfs_mkdir(char *path); +void vfs_touch(char *path); +dir_t vfs_dir_at(int idx, char *path); +void vfs_delete_dir(char *path); +bool vfs_fs_at(int idx, char *out); + +// File descriptor layer +int file_to_fd(file_t *file); +file_t *fd_to_file(int fd); +void fd_free(int fd); diff --git a/tools/microemu/include/mex.h b/tools/microemu/include/mex.h new file mode 100644 index 00000000..0038b8a7 --- /dev/null +++ b/tools/microemu/include/mex.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +typedef struct mex_header { + char header[4]; // "MEX\0" + char programAuthor[64]; + unsigned int elfSizeCompressed; +} __attribute__((packed)) mex_header_t; + +typedef struct mex_header_v2 { + char header[4]; // "M2X\0" + char programAuthor[64]; + unsigned int flags; + unsigned int abiVersion; + unsigned int elfSizeCompressed; +} __attribute__((packed)) mex_header_v2_t; + +#define M_COMPRESSED_FLAG (1 << 0) + +uint8_t *mex_load(uint8_t *file, size_t file_size, size_t *elf_size); diff --git a/tools/microemu/include/scheduler.h b/tools/microemu/include/scheduler.h new file mode 100644 index 00000000..313f8d47 --- /dev/null +++ b/tools/microemu/include/scheduler.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include +#include + +#define MAX_TASKS 64 +#define SCHED_SLICE 200000 +#define MAX_EXIT_CODES 256 + +typedef struct { + uc_engine *uc; + uc_hook hook; + int pid; + bool active; + bool stopped; + int exit_code; + uint32_t entry; + + uint32_t argv_addr; + uint32_t envp_addr; + char pwd[512]; +} emu_proc_t; + +extern emu_proc_t procs[MAX_TASKS]; +extern int current_pid; + +void sched_init(void); + +int sched_spawn(uc_engine *parent_uc, uint32_t path_addr, uint32_t argv_addr, uint32_t envp_addr); + +int sched_load(const char *vfs_path, int host_argc, char **host_argv); + +int sched_run(void); + +emu_proc_t *sched_get_proc(int pid); + +emu_proc_t *sched_current(void); + +void sched_exit_current(int code); + +int sched_get_exit_code(int pid); diff --git a/tools/microemu/include/syscalls.h b/tools/microemu/include/syscalls.h new file mode 100644 index 00000000..d9a522f1 --- /dev/null +++ b/tools/microemu/include/syscalls.h @@ -0,0 +1,65 @@ +#pragma once + +#define SYS_OPEN_ID 1 +#define SYS_CLOSE_ID 2 +#define SYS_READ_ID 3 +#define SYS_WRITE_ID 4 +#define SYS_FILESIZE_ID 5 +#define SYS_DELETE_ID 6 +#define SYS_MKDIR_ID 7 +#define SYS_DIR_AT_ID 8 +#define SYS_TOUCH_ID 9 +#define SYS_DELETE_DIR_ID 10 +#define SYS_FS_AT_ID 11 +#define SYS_ASYNC_GETC_ID 12 +#define SYS_EXIT_ID 13 +#define SYS_MMAP_ID 14 +#define SYS_SPAWN_ID 15 +#define SYS_GET_PROC_INFO_ID 16 +#define SYS_YIELD_ID 17 +#define SYS_ENV_ID 18 +#define SYS_MMMAP_ID 19 +#define SYS_VMODE_ID 20 +#define SYS_VPOKE_ID 21 +#define SYS_VCURSOR_ID 22 +#define SYS_ICMP_ID 23 +#define SYS_SOCK_CONNECT_ID 26 +#define SYS_SOCK_DISCONNECT_ID 27 +#define SYS_SOCK_SEND_ID 28 +#define SYS_SOCK_RECV_ID 29 +#define SYS_TIME_ID 31 +#define SYS_SET_COLOR_ID 32 +#define SYS_ASYNC_GETARRW_ID 33 +#define SYS_VCURSOR_GET_ID 34 +#define SYS_TASK_LIST_GET_ID 35 +#define SYS_KILL_ID 36 +#define SYS_VPEEK_ID 37 +#define SYS_RAMINFO_ID 38 +#define SYS_MOUSE_INFO_ID 39 +#define SYS_TRUNCATE_ID 40 +#define SYS_TIME_MS_ID 41 +#define SYS_THREAD_ID 42 +#define SYS_SET_TERM_ID 43 +#define SYS_IPV4_RESOLVE_ROUTE_ID 44 +#define SYS_SOCK_SET_LOCAL_PORT_ID 45 +#define SYS_MMAP_MAPPED_ID 46 +#define SYS_SOUND_WRITE_PCM_ID 47 +#define SYS_SOUND_GET_SAMPLE_RATE_ID 48 +#define SYS_SET_PIPE_ID 49 +#define SYS_RGB_COLOR_ID 50 +#define SYS_GET_EXIT_CODE_ID 51 +#define SYS_MESSAGE_SEND_ID 52 +#define SYS_MESSAGE_RECV_ID 53 +#define SYS_SOCK_LISTEN_ID 54 +#define SYS_SOCK_ACCEPT_ID 55 + + +#define SYS_GET_ARGV_ID 0x00 +#define SYS_GET_ENVP_ID 0x01 +#define SYS_GET_PWD_ID 0x02 +#define SYS_SET_PWD_ID 0x03 +#define SYS_PWR_RESET_ID 0x04 +#define SYS_PWR_SHUTDOWN_ID 0x05 +#define SYS_ENV_PIN 0x06 +#define SYS_ENV_SET_LAYOUT 0x07 +#define SYS_ENV_TASK_SET_WAIT_TIME 0x08 \ No newline at end of file diff --git a/tools/microemu/include/tinf.h b/tools/microemu/include/tinf.h new file mode 100644 index 00000000..6fc65192 --- /dev/null +++ b/tools/microemu/include/tinf.h @@ -0,0 +1,140 @@ +/* + * tinf - tiny inflate library (inflate, gzip, zlib) + * + * Copyright (c) 2003-2019 Joergen Ibsen + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + */ + +#ifndef TINF_H_INCLUDED +#define TINF_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define TINF_VER_MAJOR 1 /**< Major version number */ +#define TINF_VER_MINOR 2 /**< Minor version number */ +#define TINF_VER_PATCH 1 /**< Patch version number */ +#define TINF_VER_STRING "1.2.1" /**< Version number as a string */ + +#ifndef TINFCC +# ifdef __WATCOMC__ +# define TINFCC __cdecl +# else +# define TINFCC +# endif +#endif + +/** + * Status codes returned. + * + * @see tinf_uncompress, tinf_gzip_uncompress, tinf_zlib_uncompress + */ +typedef enum { + TINF_OK = 0, /**< Success */ + TINF_DATA_ERROR = -3, /**< Input error */ + TINF_BUF_ERROR = -5 /**< Not enough room for output */ +} tinf_error_code; + +/** + * Initialize global data used by tinf. + * + * @deprecated No longer required, may be removed in a future version. + */ +void TINFCC tinf_init(void); + +/** + * Decompress `sourceLen` bytes of deflate data from `source` to `dest`. + * + * The variable `destLen` points to must contain the size of `dest` on entry, + * and will be set to the size of the decompressed data on success. + * + * Reads at most `sourceLen` bytes from `source`. + * Writes at most `*destLen` bytes to `dest`. + * + * @param dest pointer to where to place decompressed data + * @param destLen pointer to variable containing size of `dest` + * @param source pointer to compressed data + * @param sourceLen size of compressed data + * @return `TINF_OK` on success, error code on error + */ +int TINFCC tinf_uncompress(void *dest, unsigned int *destLen, + const void *source, unsigned int sourceLen); + +/** + * Decompress `sourceLen` bytes of gzip data from `source` to `dest`. + * + * The variable `destLen` points to must contain the size of `dest` on entry, + * and will be set to the size of the decompressed data on success. + * + * Reads at most `sourceLen` bytes from `source`. + * Writes at most `*destLen` bytes to `dest`. + * + * @param dest pointer to where to place decompressed data + * @param destLen pointer to variable containing size of `dest` + * @param source pointer to compressed data + * @param sourceLen size of compressed data + * @return `TINF_OK` on success, error code on error + */ +int TINFCC tinf_gzip_uncompress(void *dest, unsigned int *destLen, + const void *source, unsigned int sourceLen); + +/** + * Decompress `sourceLen` bytes of zlib data from `source` to `dest`. + * + * The variable `destLen` points to must contain the size of `dest` on entry, + * and will be set to the size of the decompressed data on success. + * + * Reads at most `sourceLen` bytes from `source`. + * Writes at most `*destLen` bytes to `dest`. + * + * @param dest pointer to where to place decompressed data + * @param destLen pointer to variable containing size of `dest` + * @param source pointer to compressed data + * @param sourceLen size of compressed data + * @return `TINF_OK` on success, error code on error + */ +int TINFCC tinf_zlib_uncompress(void *dest, unsigned int *destLen, + const void *source, unsigned int sourceLen); + +/** + * Compute Adler-32 checksum of `length` bytes starting at `data`. + * + * @param data pointer to data + * @param length size of data + * @return Adler-32 checksum + */ +unsigned int TINFCC tinf_adler32(const void *data, unsigned int length); + +/** + * Compute CRC32 checksum of `length` bytes starting at `data`. + * + * @param data pointer to data + * @param length size of data + * @return CRC32 checksum + */ +unsigned int TINFCC tinf_crc32(const void *data, unsigned int length); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* TINF_H_INCLUDED */ \ No newline at end of file diff --git a/tools/microemu/main.c b/tools/microemu/main.c new file mode 100644 index 00000000..7de05042 --- /dev/null +++ b/tools/microemu/main.c @@ -0,0 +1,187 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#define align_down(x, a) ((x) & ~((a) - 1)) +#define align_up(x, a) (((x) + (a) - 1) & ~((a) - 1)) + +char emu_pwd[512] = "root:/"; + +struct termios orig_termios; +void enable_raw_mode(void) { + tcgetattr(STDIN_FILENO, &orig_termios); + struct termios raw = orig_termios; + raw.c_lflag &= ~(ICANON | ECHO); + raw.c_cc[VMIN] = 0; + raw.c_cc[VTIME] = 0; + tcsetattr(STDIN_FILENO, TCSANOW, &raw); +} + +void restore_terminal(void) { + tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); +} + +void read_emu_string(uc_engine *uc, uint32_t addr, char *buf, size_t max) { + for (size_t i = 0; i < max - 1; i++) { + uc_mem_read(uc, addr + i, &buf[i], 1); + if (buf[i] == '\0') return; + } + buf[max - 1] = '\0'; +} + +void hook_intr(uc_engine *uc, uint32_t intno, void *user_data) { + if (intno != 0x30) { + return; + } + + uint32_t eax, ebx, ecx, edx, esi, edi; + uc_reg_read(uc, UC_X86_REG_EAX, &eax); + uc_reg_read(uc, UC_X86_REG_EBX, &ebx); + uc_reg_read(uc, UC_X86_REG_ECX, &ecx); + uc_reg_read(uc, UC_X86_REG_EDX, &edx); + uc_reg_read(uc, UC_X86_REG_ESI, &esi); + uc_reg_read(uc, UC_X86_REG_EDI, &edi); + + switch (eax) { + case SYS_OPEN_ID: + sys_open(uc, ebx, ecx); + break; + case SYS_CLOSE_ID: + sys_close(ebx); + break; + case SYS_READ_ID: + sys_read(uc, ebx, ecx, edx, esi); + break; + case SYS_WRITE_ID: + sys_write(uc, ebx, ecx, edx, esi); + break; + case SYS_FILESIZE_ID: + sys_filesize(uc, ebx); + break; + case SYS_DELETE_ID: + sys_delete(ebx); + break; + case SYS_MKDIR_ID: + sys_mkdir(uc, ebx); + break; + case SYS_DIR_AT_ID: + sys_dir_at(uc, ebx, ecx, edx); + break; + case SYS_TOUCH_ID: + sys_touch(uc, ebx); + break; + case SYS_DELETE_DIR_ID: + sys_delete_dir(uc, ebx); + break; + case SYS_FS_AT_ID: + sys_fs_at(uc, ebx, ecx); + break; + case SYS_TRUNCATE_ID: + sys_truncate(ebx, ecx); + break; + case SYS_ENV_ID: + sys_env(uc, ebx, ecx); + break; + case SYS_MMAP_ID: + sys_mmap(uc, ebx); + break; + case SYS_MMAP_MAPPED_ID: + sys_mmap_mapped(uc, ebx); + break; + case SYS_ASYNC_GETC_ID: + sys_async_getc(uc); + break; + case SYS_ASYNC_GETARRW_ID: + sys_async_getarrw(uc); + break; + case SYS_VMODE_ID: + sys_vmode(uc); + break; + case SYS_SET_COLOR_ID: + sys_set_color(uc, ebx); + break; + case SYS_RGB_COLOR_ID: + sys_rgb_color(ebx); + break; + case SYS_VCURSOR_GET_ID: + sys_vcursor_get(uc, ebx, ecx); + break; + case SYS_TIME_ID: + sys_time(uc); + break; + case SYS_TIME_MS_ID: + sys_time_ms(uc); + break; + case SYS_YIELD_ID: + sys_yield(uc); + break; + case SYS_EXIT_ID: + sys_exit(uc, ebx); + break; + case SYS_SPAWN_ID: + sys_spawn(uc, ebx, ecx, edx); + break; + case SYS_GET_PROC_INFO_ID: + sys_get_proc_info(uc, ebx); + break; + case SYS_KILL_ID: + sys_kill(uc, ebx); + break; + case SYS_GET_EXIT_CODE_ID: + sys_get_exit_code(uc, ebx); + break; + case SYS_RAMINFO_ID: + sys_raminfo(uc); + break; + case SYS_SET_TERM_ID: + sys_set_term(ebx, ecx); + break; + case SYS_MESSAGE_SEND_ID: + sys_message_send(uc, ebx, ecx, edx); + break; + case SYS_MESSAGE_RECV_ID: + sys_message_recv(uc, ebx, ecx, edx); + break; + + default: + printf("[emu] unknown syscall %d\n", eax); + sched_exit_current(-1); + break; + } +} + +int main(int argc, char **argv) { + if (argc < 2) { + printf("usage: %s [args...]\n", argv[0]); + return 1; + } + + vfs_init(); + vfs_mount((vfs_mount_t*) hostfs_create("root", ".")); + vfs_mount(get_ramfs("tmp")); + + enable_raw_mode(); + atexit(restore_terminal); + + sched_init(); + + int pid = sched_load(argv[1], argc - 1, argv + 1); + if (pid < 0) { + fprintf(stderr, "Failed to load %s\n", argv[1]); + return 1; + } + + int exit_code = sched_run(); + + restore_terminal(); + + return exit_code < 0 ? 1 : 0; +} \ No newline at end of file diff --git a/tools/microemu/mex.c b/tools/microemu/mex.c new file mode 100644 index 00000000..df771a4f --- /dev/null +++ b/tools/microemu/mex.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include + +uint8_t *mex_decompress(uint8_t *compressed, unsigned int comp_size, size_t *out_size) { + unsigned int decompressed_size = compressed[comp_size - 4] | (compressed[comp_size - 3] << 8) | (compressed[comp_size - 2] << 16) | (compressed[comp_size - 1] << 24); + + uint8_t *out = malloc(decompressed_size); + if (!out) { + return NULL; + } + + unsigned int destLen = decompressed_size; + int res = tinf_gzip_uncompress(out, &destLen, compressed, comp_size); + if (res != TINF_OK) { + fprintf(stderr, "[mex] decompression failed: %d\n", res); + free(out); + return NULL; + } + + *out_size = destLen; + return out; +} + +uint8_t *mex_load(uint8_t *file, size_t file_size, size_t *elf_size) { + if (file_size < 4) { + return NULL; + } + + if (file[0] == 'M' && file[1] == '2' && file[2] == 'X' && file[3] == '\0') { + if (file_size < sizeof(mex_header_v2_t)) { + fprintf(stderr, "[mex] file too small for v2 header\n"); + return NULL; + } + mex_header_v2_t *hdr = (mex_header_v2_t *)file; + uint8_t *payload = file + sizeof(mex_header_v2_t); + unsigned int comp_size = hdr->elfSizeCompressed; + + // printf("[mex] v2 by '%.64s', abi=%d, flags=0x%x, compressed=%u\n", hdr->programAuthor, hdr->abiVersion, hdr->flags, comp_size); + + if (hdr->flags & M_COMPRESSED_FLAG) { + return mex_decompress(payload, comp_size, elf_size); + } else { + uint8_t *out = malloc(comp_size); + if (!out) { + return NULL; + } + memcpy(out, payload, comp_size); + *elf_size = comp_size; + return out; + } + } + + if (file[0] == 'M' && file[1] == 'E' && file[2] == 'X' && file[3] == '\0') { + if (file_size < sizeof(mex_header_t)) { + fprintf(stderr, "[mex] file too small for v1 header\n"); + return NULL; + } + mex_header_t *hdr = (mex_header_t *)file; + uint8_t *payload = file + sizeof(mex_header_t); + unsigned int comp_size = hdr->elfSizeCompressed; + + // printf("[mex] v1 by '%.64s', compressed=%u\n", hdr->programAuthor, comp_size); + + return mex_decompress(payload, comp_size, elf_size); + } + + uint8_t *out = malloc(file_size); + if (!out) { + return NULL; + } + + memcpy(out, file, file_size); + *elf_size = file_size; + + return out; +} diff --git a/tools/microemu/scheduler.c b/tools/microemu/scheduler.c new file mode 100644 index 00000000..58c9a05e --- /dev/null +++ b/tools/microemu/scheduler.c @@ -0,0 +1,358 @@ +#include +#include +#include +#include +#include + +#define align_down(x, a) ((x) & ~((a) - 1)) +#define align_up(x, a) (((x) + (a) - 1) & ~((a) - 1)) + +emu_proc_t procs[MAX_TASKS]; +int current_pid = -1; +int next_pid = 1; + +typedef struct { + int pid; + int code; +} exit_code_t; + +exit_code_t exit_codes[MAX_EXIT_CODES]; +int exit_code_idx = 0; + +extern void hook_intr(uc_engine *uc, uint32_t intno, void *user_data); + +void sched_init(void) { + memset(procs, 0, sizeof(procs)); + memset(exit_codes, 0, sizeof(exit_codes)); + for (int i = 0; i < MAX_TASKS; i++) { + procs[i].pid = -1; + } +} + +emu_proc_t *sched_get_proc(int pid) { + for (int i = 0; i < MAX_TASKS; i++) { + if (procs[i].pid == pid && procs[i].active) { + return &procs[i]; + } + } + return NULL; +} + +emu_proc_t *sched_current(void) { + return sched_get_proc(current_pid); +} + +emu_proc_t *alloc_proc(void) { + for (int i = 0; i < MAX_TASKS; i++) { + if (!procs[i].active && procs[i].pid == -1) { + return &procs[i]; + } + } + return NULL; +} + +emu_proc_t *load_elf_into_proc(uint8_t *file, size_t elf_size) { + Elf32_Ehdr *eh = (Elf32_Ehdr *)file; + + if (memcmp(eh->e_ident, ELFMAG, SELFMAG) != 0) { + fprintf(stderr, "[sched] not an ELF file\n"); + return NULL; + } + if (eh->e_machine != EM_386) { + fprintf(stderr, "[sched] only 32-bit x86 supported\n"); + return NULL; + } + + emu_proc_t *proc = alloc_proc(); + if (!proc) { + fprintf(stderr, "[sched] no free process slots\n"); + exit(1); + return NULL; + } + + proc->pid = next_pid++; + proc->active = true; + proc->stopped = false; + proc->exit_code = 0; + + uc_open(UC_ARCH_X86, UC_MODE_32, &proc->uc); + + Elf32_Phdr *ph = (Elf32_Phdr *)(file + eh->e_phoff); + for (int i = 0; i < eh->e_phnum; i++) { + if (ph[i].p_type != PT_LOAD) continue; + + uint32_t vaddr = ph[i].p_vaddr; + uint32_t memsz = ph[i].p_memsz; + uint32_t filesz = ph[i].p_filesz; + uint32_t offset = ph[i].p_offset; + + uint32_t start = align_down(vaddr, 0x1000); + uint32_t end = align_up(vaddr + memsz, 0x1000); + uint32_t num_pages = (end - start) / 0x1000; + + for (uint32_t j = 0; j < num_pages; j++) { + uc_mem_map(proc->uc, start + j * 0x1000, 0x1000, UC_PROT_ALL); + } + + if (memsz > 0) { + uint8_t *zeros = calloc(1, memsz); + uc_mem_write(proc->uc, vaddr, zeros, memsz); + free(zeros); + } + if (filesz > 0) { + uc_mem_write(proc->uc, vaddr, file + offset, filesz); + } + } + + uc_mem_map(proc->uc, STACK_ADDR, STACK_SIZE, UC_PROT_ALL); + uint32_t esp = STACK_ADDR + STACK_SIZE - 4; + uc_reg_write(proc->uc, UC_X86_REG_ESP, &esp); + + uc_mem_map(proc->uc, ARGS_ADDR, ARGS_SIZE, UC_PROT_ALL); + + proc->entry = eh->e_entry; + + uc_hook_add(proc->uc, &proc->hook, UC_HOOK_INTR, hook_intr, proc, 1, 0); + + return proc; +} + +void setup_proc_args(emu_proc_t *proc, const char **argv_data, const char **envp_data) { + uint32_t str_ptr = ARGS_ADDR; + uint32_t argv_ptrs[64]; + uint32_t envp_ptrs[64]; + + int i; + for (i = 0; argv_data[i]; i++) { + size_t len = strlen(argv_data[i]) + 1; + uc_mem_write(proc->uc, str_ptr, argv_data[i], len); + argv_ptrs[i] = str_ptr; + str_ptr += len; + } + argv_ptrs[i] = 0; + + for (i = 0; envp_data[i]; i++) { + size_t len = strlen(envp_data[i]) + 1; + uc_mem_write(proc->uc, str_ptr, envp_data[i], len); + envp_ptrs[i] = str_ptr; + str_ptr += len; + } + envp_ptrs[i] = 0; + + str_ptr = (str_ptr + 3) & ~3; + + proc->argv_addr = str_ptr; + uc_mem_write(proc->uc, proc->argv_addr, argv_ptrs, sizeof(argv_ptrs)); + + proc->envp_addr = proc->argv_addr + sizeof(argv_ptrs); + uc_mem_write(proc->uc, proc->envp_addr, envp_ptrs, sizeof(envp_ptrs)); +} + +int sched_load(const char *vfs_path, int host_argc, char **host_argv) { + file_t *f = vfs_open(vfs_path, FILE_OPEN_MODE_READ); + if (!f) { + fprintf(stderr, "[sched] failed to open %s\n", vfs_path); + return -1; + } + + uint32_t size = f->size; + void *raw = malloc(size); + vfs_read(f, raw, size, 0); + vfs_close(f); + + size_t elf_size; + uint8_t *file = mex_load(raw, size, &elf_size); + free(raw); + if (!file) { + fprintf(stderr, "[sched] failed to load executable %s\n", vfs_path); + return -1; + } + + emu_proc_t *proc = load_elf_into_proc(file, elf_size); + free(file); + if (!proc) return -1; + + const char *argv_data[64]; + for (int i = 0; i < host_argc && i < 63; i++) { + argv_data[i] = host_argv[i]; + } + argv_data[host_argc < 63 ? host_argc : 63] = NULL; + + const char *envp_data[] = { NULL }; + setup_proc_args(proc, argv_data, envp_data); + + strncpy(proc->pwd, emu_pwd, sizeof(proc->pwd) - 1); + proc->pwd[sizeof(proc->pwd) - 1] = '\0'; + + return proc->pid; +} + +int sched_spawn(uc_engine *parent_uc, uint32_t path_addr, uint32_t g_argv_addr, uint32_t g_envp_addr) { + char path[512]; + read_emu_string(parent_uc, path_addr, path, sizeof(path)); + + const char *argv_data[64]; + char argv_bufs[64][512]; + int argc = 0; + for (int i = 0; i < 63; i++) { + uint32_t ptr; + uc_mem_read(parent_uc, g_argv_addr + i * 4, &ptr, 4); + if (ptr == 0) { + break; + } + + read_emu_string(parent_uc, ptr, argv_bufs[i], sizeof(argv_bufs[i])); + argv_data[i] = argv_bufs[i]; + argc++; + } + argv_data[argc] = NULL; + + const char *envp_data[64]; + char envp_bufs[64][512]; + int envc = 0; + for (int i = 0; i < 63; i++) { + uint32_t ptr; + uc_mem_read(parent_uc, g_envp_addr + i * 4, &ptr, 4); + if (ptr == 0) { + break; + } + + read_emu_string(parent_uc, ptr, envp_bufs[i], sizeof(envp_bufs[i])); + envp_data[i] = envp_bufs[i]; + envc++; + } + envp_data[envc] = NULL; + + file_t *f = vfs_open(path, FILE_OPEN_MODE_READ); + if (!f) { + fprintf(stderr, "[sched] spawn: failed to open %s\n", path); + return -1; + } + + uint32_t size = f->size; + void *raw = malloc(size); + vfs_read(f, raw, size, 0); + vfs_close(f); + + size_t elf_size; + uint8_t *file = mex_load(raw, size, &elf_size); + free(raw); + if (!file) { + fprintf(stderr, "[sched] spawn: failed to load %s\n", path); + return -1; + } + + emu_proc_t *proc = load_elf_into_proc(file, elf_size); + free(file); + if (!proc) { + return -1; + } + + setup_proc_args(proc, argv_data, envp_data); + + emu_proc_t *parent = sched_current(); + if (parent) { + strncpy(proc->pwd, parent->pwd, sizeof(proc->pwd) - 1); + proc->pwd[sizeof(proc->pwd) - 1] = '\0'; + } else { + strncpy(proc->pwd, emu_pwd, sizeof(proc->pwd) - 1); + proc->pwd[sizeof(proc->pwd) - 1] = '\0'; + } + + return proc->pid; +} + +void sched_exit_current(int code) { + emu_proc_t *proc = sched_current(); + if (!proc) { + return; + } + + exit_codes[exit_code_idx].pid = proc->pid; + exit_codes[exit_code_idx].code = code; + exit_code_idx = (exit_code_idx + 1) % MAX_EXIT_CODES; + + proc->active = false; + proc->stopped = true; + proc->exit_code = code; + uc_emu_stop(proc->uc); +} + +int sched_get_exit_code(int pid) { + for (int i = 0; i < MAX_EXIT_CODES; i++) { + if (exit_codes[i].pid == pid) { + return exit_codes[i].code; + } + } + return -1; +} + +int sched_run(void) { + int first_pid = -1; + + for (int i = 0; i < MAX_TASKS; i++) { + if (procs[i].active) { + first_pid = procs[i].pid; + break; + } + } + + if (first_pid == -1) return 1; + + while (1) { + bool any_active = false; + + for (int i = 0; i < MAX_TASKS; i++) { + if (!procs[i].active) continue; + any_active = true; + + emu_proc_t *proc = &procs[i]; + current_pid = proc->pid; + + // argv_addr = proc->argv_addr; + // envp_addr = proc->envp_addr; + // strncpy(emu_pwd, proc->pwd, sizeof(emu_pwd) - 1); + + uint32_t eip; + uc_reg_read(proc->uc, UC_X86_REG_EIP, &eip); + + if (eip == 0) { + eip = proc->entry; + } + + proc->stopped = false; + uc_err err = uc_emu_start(proc->uc, eip, 0, 0, SCHED_SLICE); + + // proc->argv_addr = argv_addr; + // proc->envp_addr = envp_addr; + // strncpy(proc->pwd, emu_pwd, sizeof(proc->pwd) - 1); + + if (proc->stopped) { + continue; + } + + if (err && err != UC_ERR_OK) { + fprintf(stderr, "[sched] pid %d emulation error: %s\n", proc->pid, uc_strerror(err)); + if (err == UC_ERR_READ_UNMAPPED) { + uint32_t fault_addr; + uc_reg_read(proc->uc, UC_X86_REG_EIP, &fault_addr); + fprintf(stderr, "[sched] pid %d unmapped memory access at %08x\n", proc->pid, fault_addr); + } + sched_exit_current(-1); + } + } + + if (!any_active) { + break; + } + } + + for (int i = 0; i < MAX_TASKS; i++) { + if (procs[i].uc) { + uc_close(procs[i].uc); + procs[i].uc = NULL; + } + } + + return sched_get_exit_code(first_pid); +} diff --git a/tools/microemu/syscalls/sys_env.c b/tools/microemu/syscalls/sys_env.c new file mode 100644 index 00000000..cebe86e7 --- /dev/null +++ b/tools/microemu/syscalls/sys_env.c @@ -0,0 +1,37 @@ +#include +#include +#include + +void sys_env(uc_engine *uc, uint32_t ebx, uint32_t ecx) { + emu_proc_t* current = sched_current(); + + int id = ebx; + switch (id) { + case SYS_GET_ARGV_ID: + uc_reg_write(uc, UC_X86_REG_ECX, ¤t->argv_addr); + break; + case SYS_GET_ENVP_ID: + uc_reg_write(uc, UC_X86_REG_ECX, ¤t->envp_addr); + break; + case SYS_GET_PWD_ID: + uc_mem_write(uc, ecx, current->pwd, strlen(current->pwd) + 1); + break; + case SYS_SET_PWD_ID: + read_emu_string(uc, ecx, current->pwd, sizeof(current->pwd)); + break; + case SYS_PWR_RESET_ID: + break; + case SYS_PWR_SHUTDOWN_ID: + exit(0); + break; + case SYS_ENV_PIN: + break; + case SYS_ENV_SET_LAYOUT: + break; + case SYS_ENV_TASK_SET_WAIT_TIME: + break; + default: + fatalf("[emu] unknown env syscall %d\n", id); + break; + } +} diff --git a/tools/microemu/syscalls/sys_file.c b/tools/microemu/syscalls/sys_file.c new file mode 100644 index 00000000..1afffbed --- /dev/null +++ b/tools/microemu/syscalls/sys_file.c @@ -0,0 +1,141 @@ +#include +#include + +void sys_open(uc_engine *uc, uint32_t ebx, uint32_t ecx) { + char path[512]; + read_emu_string(uc, ebx, path, sizeof(path)); + + file_t* f = vfs_open(path, ecx); + uint32_t result = (uint32_t)-1; + if (f) { + int efd = file_to_fd(f); + if (efd >= 0) { + result = efd; + } else { + vfs_close(f); + } + } + uc_reg_write(uc, UC_X86_REG_EDX, &result); +} + +void sys_close(uint32_t ebx) { + file_t* f = fd_to_file(ebx); + if (f) { + vfs_close(f); + fd_free(ebx); + } +} + +void sys_read(uc_engine *uc, uint32_t ebx, uint32_t ecx, uint32_t edx, uint32_t esi) { + uint32_t buffer = ecx; + uint32_t count = edx; + uint32_t offset = esi; + + file_t* f = fd_to_file(ebx); + if (f) { + char *data = malloc(count); + vfs_read(f, data, count, offset); + uc_mem_write(uc, buffer, data, count); + free(data); + } +} + +void sys_write(uc_engine *uc, uint32_t ebx, uint32_t ecx, uint32_t edx, uint32_t esi) { + uint32_t buffer = ecx; + uint32_t count = edx; + uint32_t offset = esi; + + char *data = malloc(count); + uc_mem_read(uc, buffer, data, count); + + if (ebx == 1) { + fwrite(data, 1, count, stdout); + fflush(stdout); + } else if (ebx == 2) { + fwrite(data, 1, count, stderr); + fflush(stderr); + } else { + file_t *f = fd_to_file(ebx); + if (f) { + vfs_write(f, data, count, offset); + } else { + // fatalf("[emu] write to unknown fd %d\n", ebx); + // TODO: should be an error but init expects dev:disk to open correctly without checking if it exists + } + } + + free(data); +} + +void sys_filesize(uc_engine *uc, uint32_t ebx) { + file_t *f = fd_to_file(ebx); + uint32_t size = 0; + if (f) { + size = f->size; + } + + uc_reg_write(uc, UC_X86_REG_ECX, &size); +} + +void sys_delete(uint32_t ebx) { + file_t* f = fd_to_file(ebx); + if (f) { + vfs_delete(f); + fd_free(ebx); + } +} + +void sys_mkdir(uc_engine *uc, uint32_t ebx) { + char path[512]; + read_emu_string(uc, ebx, path, sizeof(path)); + vfs_mkdir(path); +} + +void sys_dir_at(uc_engine *uc, uint32_t ebx, uint32_t ecx, uint32_t edx) { + char path[512]; + read_emu_string(uc, ebx, path, sizeof(path)); + uint32_t idx = ecx; + uint32_t out_addr = edx; + + dir_t d = vfs_dir_at(idx, path); + + uc_mem_write(uc, out_addr, d.name, 256); + uc_mem_write(uc, out_addr + 256, &d.idx, 4); + uint32_t is_none = d.is_none ? 1 : 0; + uc_mem_write(uc, out_addr + 260, &is_none, 4); + uint32_t type = d.type; + uc_mem_write(uc, out_addr + 264, &type, 4); +} + +void sys_touch(uc_engine *uc, uint32_t ebx) { + char path[512]; + read_emu_string(uc, ebx, path, sizeof(path)); + vfs_touch(path); +} + +void sys_delete_dir(uc_engine *uc, uint32_t ebx) { + char path[512]; + read_emu_string(uc, ebx, path, sizeof(path)); + vfs_delete_dir(path); +} + +void sys_fs_at(uc_engine *uc, uint32_t ebx, uint32_t ecx) { + uint32_t idx = ebx; + uint32_t out_buf = ecx; + + char name[256] = {0}; + uint32_t result = vfs_fs_at(idx, name) ? 1 : 0; + if (result) { + uc_mem_write(uc, out_buf, name, strlen(name) + 1); + } + uc_reg_write(uc, UC_X86_REG_EDX, &result); +} + +void sys_truncate(uint32_t ebx, uint32_t ecx) { + file_t *f = fd_to_file(ebx); + if (f) { + vfs_truncate(f, ecx); + } +} + + diff --git a/tools/microemu/syscalls/sys_io.c b/tools/microemu/syscalls/sys_io.c new file mode 100644 index 00000000..6641cb11 --- /dev/null +++ b/tools/microemu/syscalls/sys_io.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +void sys_async_getc(uc_engine *uc) { + uint32_t c = 0; + struct pollfd pfd = { .fd = STDIN_FILENO, .events = POLLIN }; + if (poll(&pfd, 1, 0) > 0) { + unsigned char ch; + if (read(STDIN_FILENO, &ch, 1) == 1) { + c = ch; + } + } + + if (c == 127) { + c = '\b'; + } + + uc_reg_write(uc, UC_X86_REG_EBX, &c); +} + +void sys_async_getarrw(uc_engine *uc) { + uint32_t zero = 0; + uc_reg_write(uc, UC_X86_REG_EBX, &zero); +} + +void sys_vmode(uc_engine *uc) { + uint32_t mode = 0; // TEXT_80x25 + uc_reg_write(uc, UC_X86_REG_EBX, &mode); +} + +void sys_set_color(uc_engine *uc, uint32_t ebx) { + char color[32]; + read_emu_string(uc, ebx, color, sizeof(color)); +} + +void sys_rgb_color(uint32_t ebx) { +} + +void sys_vcursor_get(uc_engine *uc, uint32_t ebx, uint32_t ecx) { + uint32_t zero = 0; + uc_mem_write(uc, ebx, &zero, 4); + uc_mem_write(uc, ecx, &zero, 4); +} + +void sys_time(uc_engine *uc) { + uint32_t t = (uint32_t)time(NULL); + uc_reg_write(uc, UC_X86_REG_EBX, &t); +} + +void sys_time_ms(uc_engine *uc) { + struct timeval tv; + gettimeofday(&tv, NULL); + uint32_t ms = (uint32_t)(tv.tv_sec * 1000 + tv.tv_usec / 1000); + uc_reg_write(uc, UC_X86_REG_EBX, &ms); +} + + + +void sys_raminfo(uc_engine *uc) { + uint32_t free_mem = 256 * 1024 * 1024; // 256 MB + uint32_t used_mem = 64 * 1024 * 1024; // 64 MB + uc_reg_write(uc, UC_X86_REG_EBX, &free_mem); + uc_reg_write(uc, UC_X86_REG_ECX, &used_mem); +} + +void sys_yield(uc_engine *uc) { + uc_emu_stop(uc); +} + +void sys_exit(uc_engine *uc, uint32_t ebx) { + sched_exit_current((int)ebx); +} + + diff --git a/tools/microemu/syscalls/sys_mem.c b/tools/microemu/syscalls/sys_mem.c new file mode 100644 index 00000000..84ae569f --- /dev/null +++ b/tools/microemu/syscalls/sys_mem.c @@ -0,0 +1,34 @@ +#include + +#define MMAP_CHUNK_PAGES 64 // 256KB per chunk +void emu_mmap_page(uc_engine *uc, uint32_t addr) { + uint8_t tmp; + if (uc_mem_read(uc, addr, &tmp, 1) == UC_ERR_OK) { + return; + } + + uint32_t chunk_size = MMAP_CHUNK_PAGES * 0x1000; + uint32_t chunk_start = addr & ~(chunk_size - 1); + + uc_err err = uc_mem_map(uc, chunk_start, chunk_size, UC_PROT_ALL); + if (err == UC_ERR_OK) { + return; + } + + uc_mem_map(uc, addr, 0x1000, UC_PROT_ALL); +} + +void sys_mmap(uc_engine *uc, uint32_t ebx) { + emu_mmap_page(uc, ebx); +} + +void sys_mmap_mapped(uc_engine *uc, uint32_t ebx) { + uint32_t mapped = 0; + uint8_t tmp; + if (uc_mem_read(uc, ebx, &tmp, 1) == UC_ERR_OK) { + mapped = 1; + } + uc_reg_write(uc, UC_X86_REG_ECX, &mapped); +} + + diff --git a/tools/microemu/syscalls/sys_message.c b/tools/microemu/syscalls/sys_message.c new file mode 100644 index 00000000..5352bac4 --- /dev/null +++ b/tools/microemu/syscalls/sys_message.c @@ -0,0 +1,90 @@ +#include +#include +#include + +void sys_set_term(uint32_t ebx, uint32_t ecx) { +} + +typedef struct msg_node { + void* data; + size_t size; + struct msg_node* next; +} msg_node_t; + +typedef struct msg_topic { + uint32_t id; + msg_node_t* head; + msg_node_t* tail; + struct msg_topic* next; +} msg_topic_t; + +static msg_topic_t* topics = NULL; + +static msg_topic_t* get_or_create_topic(uint32_t id) { + for (msg_topic_t *t = topics; t; t = t->next) { + if (t->id == id) { + return t; + } + } + + msg_topic_t* t = calloc(1, sizeof(*t)); + t->id = id; + t->next = topics; + topics = t; + return t; +} + +void sys_message_send(uc_engine *uc, uint32_t ebx, uint32_t ecx, uint32_t edx) { + uint32_t topic_id = ebx; + uint32_t buf_addr = ecx; + uint32_t size = edx; + + if (size == 0) { + return; + } + + void* buf = malloc(size); + if (uc_mem_read(uc, buf_addr, buf, size) != UC_ERR_OK) { + free(buf); + return; + } + + msg_topic_t* topic = get_or_create_topic(topic_id); + msg_node_t* node = calloc(1, sizeof(*node)); + node->data = buf; + node->size = size; + + if (topic->tail) { + topic->tail->next = node; + topic->tail = node; + } else { + topic->head = topic->tail = node; + } +} + +void sys_message_recv(uc_engine *uc, uint32_t ebx, uint32_t ecx, uint32_t edx) { + uint32_t topic_id = ebx; + uint32_t buf_addr = ecx; + uint32_t buf_size = edx; + + msg_topic_t* topic = get_or_create_topic(topic_id); + msg_node_t* node = topic->head; + + if (!node) { + uint32_t zero = 0; + uc_reg_write(uc, UC_X86_REG_EAX, &zero); + return; + } + + topic->head = node->next; + if (!topic->head) { + topic->tail = NULL; + } + + uint32_t copy = node->size < buf_size ? (uint32_t)node->size : buf_size; + uc_mem_write(uc, buf_addr, node->data, copy); + uc_reg_write(uc, UC_X86_REG_EAX, ©); + + free(node->data); + free(node); +} diff --git a/tools/microemu/syscalls/sys_proc.c b/tools/microemu/syscalls/sys_proc.c new file mode 100644 index 00000000..420ecb0f --- /dev/null +++ b/tools/microemu/syscalls/sys_proc.c @@ -0,0 +1,31 @@ +#include +#include + +void sys_spawn(uc_engine *uc, uint32_t ebx, uint32_t ecx, uint32_t edx) { + int pid = sched_spawn(uc, ebx, ecx, edx); + uint32_t result = (uint32_t)pid; + uc_reg_write(uc, UC_X86_REG_ESI, &result); +} + +void sys_get_proc_info(uc_engine *uc, uint32_t ebx) { + emu_proc_t* proc = sched_get_proc(ebx); + uint32_t exists = proc ? 1 : 0; + uc_reg_write(uc, UC_X86_REG_ECX, &exists); +} + +void sys_kill(uc_engine *uc, uint32_t ebx) { + emu_proc_t *proc = sched_get_proc(ebx); + if (proc) { + proc->active = false; + proc->stopped = true; + proc->exit_code = -2; + uc_emu_stop(proc->uc); + } + (void)uc; +} + +void sys_get_exit_code(uc_engine *uc, uint32_t ebx) { + int code = sched_get_exit_code(ebx); + uint32_t result = (uint32_t)code; + uc_reg_write(uc, UC_X86_REG_ECX, &result); +} diff --git a/tools/microemu/test.sh b/tools/microemu/test.sh new file mode 100644 index 00000000..ddbe5001 --- /dev/null +++ b/tools/microemu/test.sh @@ -0,0 +1,21 @@ +make -B + +MICROEMU=$(pwd)/microemu + +cd ../../res/initrd + + +$MICROEMU root:/bin/init.mex tmpfs < 0) { + int k = length < A32_NMAX ? length : A32_NMAX; + int i; + + for (i = k / 16; i; --i, buf += 16) { + s1 += buf[0]; + s2 += s1; + s1 += buf[1]; + s2 += s1; + s1 += buf[2]; + s2 += s1; + s1 += buf[3]; + s2 += s1; + s1 += buf[4]; + s2 += s1; + s1 += buf[5]; + s2 += s1; + s1 += buf[6]; + s2 += s1; + s1 += buf[7]; + s2 += s1; + + s1 += buf[8]; + s2 += s1; + s1 += buf[9]; + s2 += s1; + s1 += buf[10]; + s2 += s1; + s1 += buf[11]; + s2 += s1; + s1 += buf[12]; + s2 += s1; + s1 += buf[13]; + s2 += s1; + s1 += buf[14]; + s2 += s1; + s1 += buf[15]; + s2 += s1; + } + + for (i = k % 16; i; --i) { + s1 += *buf++; + s2 += s1; + } + + s1 %= A32_BASE; + s2 %= A32_BASE; + + length -= k; + } + + return (s2 << 16) | s1; +} \ No newline at end of file diff --git a/tools/microemu/tinf/crc32.c b/tools/microemu/tinf/crc32.c new file mode 100644 index 00000000..4c5043db --- /dev/null +++ b/tools/microemu/tinf/crc32.c @@ -0,0 +1,57 @@ +/* + * CRC32 checksum + * + * Copyright (c) 1998-2019 Joergen Ibsen + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + */ + +/* + * CRC32 algorithm taken from the zlib source, which is + * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler + */ + +#include "tinf.h" + +static const unsigned int tinf_crc32tab[16] = { + 0x00000000, 0x1DB71064, 0x3B6E20C8, 0x26D930AC, 0x76DC4190, + 0x6B6B51F4, 0x4DB26158, 0x5005713C, 0xEDB88320, 0xF00F9344, + 0xD6D6A3E8, 0xCB61B38C, 0x9B64C2B0, 0x86D3D2D4, 0xA00AE278, + 0xBDBDF21C +}; + +unsigned int tinf_crc32(const void *data, unsigned int length) +{ + const unsigned char *buf = (const unsigned char *) data; + unsigned int crc = 0xFFFFFFFF; + unsigned int i; + + if (length == 0) { + return 0; + } + + for (i = 0; i < length; ++i) { + crc ^= buf[i]; + crc = tinf_crc32tab[crc & 0x0F] ^ (crc >> 4); + crc = tinf_crc32tab[crc & 0x0F] ^ (crc >> 4); + } + + return crc ^ 0xFFFFFFFF; +} \ No newline at end of file diff --git a/tools/microemu/tinf/tinfgzip.c b/tools/microemu/tinf/tinfgzip.c new file mode 100644 index 00000000..9b7bcfc5 --- /dev/null +++ b/tools/microemu/tinf/tinfgzip.c @@ -0,0 +1,172 @@ +/* + * tinfgzip - tiny gzip decompressor + * + * Copyright (c) 2003-2019 Joergen Ibsen + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + */ + +#include "tinf.h" + +typedef enum { + FTEXT = 1, + FHCRC = 2, + FEXTRA = 4, + FNAME = 8, + FCOMMENT = 16 +} tinf_gzip_flag; + +static unsigned int read_le16(const unsigned char *p) +{ + return ((unsigned int) p[0]) + | ((unsigned int) p[1] << 8); +} + +static unsigned int read_le32(const unsigned char *p) +{ + return ((unsigned int) p[0]) + | ((unsigned int) p[1] << 8) + | ((unsigned int) p[2] << 16) + | ((unsigned int) p[3] << 24); +} + +int tinf_gzip_uncompress(void *dest, unsigned int *destLen, + const void *source, unsigned int sourceLen) +{ + const unsigned char *src = (const unsigned char *) source; + unsigned char *dst = (unsigned char *) dest; + const unsigned char *start; + unsigned int dlen, crc32; + int res; + unsigned char flg; + + /* -- Check header -- */ + + /* Check room for at least 10 byte header and 8 byte trailer */ + if (sourceLen < 18) { + return TINF_DATA_ERROR; + } + + /* Check id bytes */ + if (src[0] != 0x1F || src[1] != 0x8B) { + return TINF_DATA_ERROR; + } + + /* Check method is deflate */ + if (src[2] != 8) { + return TINF_DATA_ERROR; + } + + /* Get flag byte */ + flg = src[3]; + + /* Check that reserved bits are zero */ + if (flg & 0xE0) { + return TINF_DATA_ERROR; + } + + /* -- Find start of compressed data -- */ + + /* Skip base header of 10 bytes */ + start = src + 10; + + /* Skip extra data if present */ + if (flg & FEXTRA) { + unsigned int xlen = read_le16(start); + + if (xlen > sourceLen - 12) { + return TINF_DATA_ERROR; + } + + start += xlen + 2; + } + + /* Skip file name if present */ + if (flg & FNAME) { + do { + if (start - src >= sourceLen) { + return TINF_DATA_ERROR; + } + } while (*start++); + } + + /* Skip file comment if present */ + if (flg & FCOMMENT) { + do { + if (start - src >= sourceLen) { + return TINF_DATA_ERROR; + } + } while (*start++); + } + + /* Check header crc if present */ + if (flg & FHCRC) { + unsigned int hcrc; + + if (start - src > sourceLen - 2) { + return TINF_DATA_ERROR; + } + + hcrc = read_le16(start); + + if (hcrc != (tinf_crc32(src, start - src) & 0x0000FFFF)) { + return TINF_DATA_ERROR; + } + + start += 2; + } + + /* -- Get decompressed length -- */ + + dlen = read_le32(&src[sourceLen - 4]); + + if (dlen > *destLen) { + return TINF_BUF_ERROR; + } + + /* -- Get CRC32 checksum of original data -- */ + + crc32 = read_le32(&src[sourceLen - 8]); + + /* -- Decompress data -- */ + + if ((src + sourceLen) - start < 8) { + return TINF_DATA_ERROR; + } + + res = tinf_uncompress(dst, destLen, start, + (src + sourceLen) - start - 8); + + if (res != TINF_OK) { + return TINF_DATA_ERROR; + } + + if (*destLen != dlen) { + return TINF_DATA_ERROR; + } + + /* -- Check CRC32 checksum -- */ + + if (crc32 != tinf_crc32(dst, dlen)) { + return TINF_DATA_ERROR; + } + + return TINF_OK; +} \ No newline at end of file diff --git a/tools/microemu/tinf/tinflate.c b/tools/microemu/tinf/tinflate.c new file mode 100644 index 00000000..2af0ea18 --- /dev/null +++ b/tools/microemu/tinf/tinflate.c @@ -0,0 +1,635 @@ +/* + * tinflate - tiny inflate + * + * Copyright (c) 2003-2019 Joergen Ibsen + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + */ + +#include "tinf.h" + +#include + +#if defined(UINT_MAX) && (UINT_MAX) < 0xFFFFFFFFUL +# error "tinf requires unsigned int to be at least 32-bit" +#endif + +/* -- Internal data structures -- */ + +struct tinf_tree { + unsigned short counts[16]; /* Number of codes with a given length */ + unsigned short symbols[288]; /* Symbols sorted by code */ + int max_sym; +}; + +struct tinf_data { + const unsigned char *source; + const unsigned char *source_end; + unsigned int tag; + int bitcount; + int overflow; + + unsigned char *dest_start; + unsigned char *dest; + unsigned char *dest_end; + + struct tinf_tree ltree; /* Literal/length tree */ + struct tinf_tree dtree; /* Distance tree */ +}; + +/* -- Utility functions -- */ + +static unsigned int read_le16(const unsigned char *p) +{ + return ((unsigned int) p[0]) + | ((unsigned int) p[1] << 8); +} + +/* Build fixed Huffman trees */ +static void tinf_build_fixed_trees(struct tinf_tree *lt, struct tinf_tree *dt) +{ + int i; + + /* Build fixed literal/length tree */ + for (i = 0; i < 16; ++i) { + lt->counts[i] = 0; + } + + lt->counts[7] = 24; + lt->counts[8] = 152; + lt->counts[9] = 112; + + for (i = 0; i < 24; ++i) { + lt->symbols[i] = 256 + i; + } + for (i = 0; i < 144; ++i) { + lt->symbols[24 + i] = i; + } + for (i = 0; i < 8; ++i) { + lt->symbols[24 + 144 + i] = 280 + i; + } + for (i = 0; i < 112; ++i) { + lt->symbols[24 + 144 + 8 + i] = 144 + i; + } + + lt->max_sym = 285; + + /* Build fixed distance tree */ + for (i = 0; i < 16; ++i) { + dt->counts[i] = 0; + } + + dt->counts[5] = 32; + + for (i = 0; i < 32; ++i) { + dt->symbols[i] = i; + } + + dt->max_sym = 29; +} + +/* Given an array of code lengths, build a tree */ +static int tinf_build_tree(struct tinf_tree *t, const unsigned char *lengths, + unsigned int num) +{ + unsigned short offs[16]; + unsigned int i, num_codes, available; + + assert(num <= 288); + + for (i = 0; i < 16; ++i) { + t->counts[i] = 0; + } + + t->max_sym = -1; + + /* Count number of codes for each non-zero length */ + for (i = 0; i < num; ++i) { + assert(lengths[i] <= 15); + + if (lengths[i]) { + t->max_sym = i; + t->counts[lengths[i]]++; + } + } + + /* Compute offset table for distribution sort */ + for (available = 1, num_codes = 0, i = 0; i < 16; ++i) { + unsigned int used = t->counts[i]; + + /* Check length contains no more codes than available */ + if (used > available) { + return TINF_DATA_ERROR; + } + available = 2 * (available - used); + + offs[i] = num_codes; + num_codes += used; + } + + /* + * Check all codes were used, or for the special case of only one + * code that it has length 1 + */ + if ((num_codes > 1 && available > 0) + || (num_codes == 1 && t->counts[1] != 1)) { + return TINF_DATA_ERROR; + } + + /* Fill in symbols sorted by code */ + for (i = 0; i < num; ++i) { + if (lengths[i]) { + t->symbols[offs[lengths[i]]++] = i; + } + } + + /* + * For the special case of only one code (which will be 0) add a + * code 1 which results in a symbol that is too large + */ + if (num_codes == 1) { + t->counts[1] = 2; + t->symbols[1] = t->max_sym + 1; + } + + return TINF_OK; +} + +/* -- Decode functions -- */ + +static void tinf_refill(struct tinf_data *d, int num) +{ + assert(num >= 0 && num <= 32); + + /* Read bytes until at least num bits available */ + while (d->bitcount < num) { + if (d->source != d->source_end) { + d->tag |= (unsigned int) *d->source++ << d->bitcount; + } + else { + d->overflow = 1; + } + d->bitcount += 8; + } + + assert(d->bitcount <= 32); +} + +static unsigned int tinf_getbits_no_refill(struct tinf_data *d, int num) +{ + unsigned int bits; + + assert(num >= 0 && num <= d->bitcount); + + /* Get bits from tag */ + bits = d->tag & ((1UL << num) - 1); + + /* Remove bits from tag */ + d->tag >>= num; + d->bitcount -= num; + + return bits; +} + +/* Get num bits from source stream */ +static unsigned int tinf_getbits(struct tinf_data *d, int num) +{ + tinf_refill(d, num); + return tinf_getbits_no_refill(d, num); +} + +/* Read a num bit value from stream and add base */ +static unsigned int tinf_getbits_base(struct tinf_data *d, int num, int base) +{ + return base + (num ? tinf_getbits(d, num) : 0); +} + +/* Given a data stream and a tree, decode a symbol */ +static int tinf_decode_symbol(struct tinf_data *d, const struct tinf_tree *t) +{ + int base = 0, offs = 0; + int len; + + /* + * Get more bits while code index is above number of codes + * + * Rather than the actual code, we are computing the position of the + * code in the sorted order of codes, which is the index of the + * corresponding symbol. + * + * Conceptually, for each code length (level in the tree), there are + * counts[len] leaves on the left and internal nodes on the right. + * The index we have decoded so far is base + offs, and if that + * falls within the leaves we are done. Otherwise we adjust the range + * of offs and add one more bit to it. + */ + for (len = 1; ; ++len) { + offs = 2 * offs + tinf_getbits(d, 1); + + assert(len <= 15); + + if (offs < t->counts[len]) { + break; + } + + base += t->counts[len]; + offs -= t->counts[len]; + } + + assert(base + offs >= 0 && base + offs < 288); + + return t->symbols[base + offs]; +} + +/* Given a data stream, decode dynamic trees from it */ +static int tinf_decode_trees(struct tinf_data *d, struct tinf_tree *lt, + struct tinf_tree *dt) +{ + unsigned char lengths[288 + 32]; + + /* Special ordering of code length codes */ + static const unsigned char clcidx[19] = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, + 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + + unsigned int hlit, hdist, hclen; + unsigned int i, num, length; + int res; + + /* Get 5 bits HLIT (257-286) */ + hlit = tinf_getbits_base(d, 5, 257); + + /* Get 5 bits HDIST (1-32) */ + hdist = tinf_getbits_base(d, 5, 1); + + /* Get 4 bits HCLEN (4-19) */ + hclen = tinf_getbits_base(d, 4, 4); + + /* + * The RFC limits the range of HLIT to 286, but lists HDIST as range + * 1-32, even though distance codes 30 and 31 have no meaning. While + * we could allow the full range of HLIT and HDIST to make it possible + * to decode the fixed trees with this function, we consider it an + * error here. + * + * See also: https://github.com/madler/zlib/issues/82 + */ + if (hlit > 286 || hdist > 30) { + return TINF_DATA_ERROR; + } + + for (i = 0; i < 19; ++i) { + lengths[i] = 0; + } + + /* Read code lengths for code length alphabet */ + for (i = 0; i < hclen; ++i) { + /* Get 3 bits code length (0-7) */ + unsigned int clen = tinf_getbits(d, 3); + + lengths[clcidx[i]] = clen; + } + + /* Build code length tree (in literal/length tree to save space) */ + res = tinf_build_tree(lt, lengths, 19); + + if (res != TINF_OK) { + return res; + } + + /* Check code length tree is not empty */ + if (lt->max_sym == -1) { + return TINF_DATA_ERROR; + } + + /* Decode code lengths for the dynamic trees */ + for (num = 0; num < hlit + hdist; ) { + int sym = tinf_decode_symbol(d, lt); + + if (sym > lt->max_sym) { + return TINF_DATA_ERROR; + } + + switch (sym) { + case 16: + /* Copy previous code length 3-6 times (read 2 bits) */ + if (num == 0) { + return TINF_DATA_ERROR; + } + sym = lengths[num - 1]; + length = tinf_getbits_base(d, 2, 3); + break; + case 17: + /* Repeat code length 0 for 3-10 times (read 3 bits) */ + sym = 0; + length = tinf_getbits_base(d, 3, 3); + break; + case 18: + /* Repeat code length 0 for 11-138 times (read 7 bits) */ + sym = 0; + length = tinf_getbits_base(d, 7, 11); + break; + default: + /* Values 0-15 represent the actual code lengths */ + length = 1; + break; + } + + if (length > hlit + hdist - num) { + return TINF_DATA_ERROR; + } + + while (length--) { + lengths[num++] = sym; + } + } + + /* Check EOB symbol is present */ + if (lengths[256] == 0) { + return TINF_DATA_ERROR; + } + + /* Build dynamic trees */ + res = tinf_build_tree(lt, lengths, hlit); + + if (res != TINF_OK) { + return res; + } + + res = tinf_build_tree(dt, lengths + hlit, hdist); + + if (res != TINF_OK) { + return res; + } + + return TINF_OK; +} + +/* -- Block inflate functions -- */ + +/* Given a stream and two trees, inflate a block of data */ +static int tinf_inflate_block_data(struct tinf_data *d, struct tinf_tree *lt, + struct tinf_tree *dt) +{ + /* Extra bits and base tables for length codes */ + static const unsigned char length_bits[30] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 5, 5, 5, 5, 0, 127 + }; + + static const unsigned short length_base[30] = { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, + 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, + 67, 83, 99, 115, 131, 163, 195, 227, 258, 0 + }; + + /* Extra bits and base tables for distance codes */ + static const unsigned char dist_bits[30] = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, + 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 + }; + + static const unsigned short dist_base[30] = { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, + 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, + 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 + }; + + for (;;) { + int sym = tinf_decode_symbol(d, lt); + + /* Check for overflow in bit reader */ + if (d->overflow) { + return TINF_DATA_ERROR; + } + + if (sym < 256) { + if (d->dest == d->dest_end) { + return TINF_BUF_ERROR; + } + *d->dest++ = sym; + } + else { + int length, dist, offs; + int i; + + /* Check for end of block */ + if (sym == 256) { + return TINF_OK; + } + + /* Check sym is within range and distance tree is not empty */ + if (sym > lt->max_sym || sym - 257 > 28 || dt->max_sym == -1) { + return TINF_DATA_ERROR; + } + + sym -= 257; + + /* Possibly get more bits from length code */ + length = tinf_getbits_base(d, length_bits[sym], + length_base[sym]); + + dist = tinf_decode_symbol(d, dt); + + /* Check dist is within range */ + if (dist > dt->max_sym || dist > 29) { + return TINF_DATA_ERROR; + } + + /* Possibly get more bits from distance code */ + offs = tinf_getbits_base(d, dist_bits[dist], + dist_base[dist]); + + if (offs > d->dest - d->dest_start) { + return TINF_DATA_ERROR; + } + + if (d->dest_end - d->dest < length) { + return TINF_BUF_ERROR; + } + + /* Copy match */ + for (i = 0; i < length; ++i) { + d->dest[i] = d->dest[i - offs]; + } + + d->dest += length; + } + } +} + +/* Inflate an uncompressed block of data */ +static int tinf_inflate_uncompressed_block(struct tinf_data *d) +{ + unsigned int length, invlength; + + if (d->source_end - d->source < 4) { + return TINF_DATA_ERROR; + } + + /* Get length */ + length = read_le16(d->source); + + /* Get one's complement of length */ + invlength = read_le16(d->source + 2); + + /* Check length */ + if (length != (~invlength & 0x0000FFFF)) { + return TINF_DATA_ERROR; + } + + d->source += 4; + + if (d->source_end - d->source < length) { + return TINF_DATA_ERROR; + } + + if (d->dest_end - d->dest < length) { + return TINF_BUF_ERROR; + } + + /* Copy block */ + while (length--) { + *d->dest++ = *d->source++; + } + + /* Make sure we start next block on a byte boundary */ + d->tag = 0; + d->bitcount = 0; + + return TINF_OK; +} + +/* Inflate a block of data compressed with fixed Huffman trees */ +static int tinf_inflate_fixed_block(struct tinf_data *d) +{ + /* Build fixed Huffman trees */ + tinf_build_fixed_trees(&d->ltree, &d->dtree); + + /* Decode block using fixed trees */ + return tinf_inflate_block_data(d, &d->ltree, &d->dtree); +} + +/* Inflate a block of data compressed with dynamic Huffman trees */ +static int tinf_inflate_dynamic_block(struct tinf_data *d) +{ + /* Decode trees from stream */ + int res = tinf_decode_trees(d, &d->ltree, &d->dtree); + + if (res != TINF_OK) { + return res; + } + + /* Decode block using decoded trees */ + return tinf_inflate_block_data(d, &d->ltree, &d->dtree); +} + +/* -- Public functions -- */ + +/* Initialize global (static) data */ +void tinf_init(void) +{ + return; +} + +/* Inflate stream from source to dest */ +int tinf_uncompress(void *dest, unsigned int *destLen, + const void *source, unsigned int sourceLen) +{ + struct tinf_data d; + int bfinal; + + /* Initialise data */ + d.source = (const unsigned char *) source; + d.source_end = d.source + sourceLen; + d.tag = 0; + d.bitcount = 0; + d.overflow = 0; + + d.dest = (unsigned char *) dest; + d.dest_start = d.dest; + d.dest_end = d.dest + *destLen; + + do { + unsigned int btype; + int res; + + /* Read final block flag */ + bfinal = tinf_getbits(&d, 1); + + /* Read block type (2 bits) */ + btype = tinf_getbits(&d, 2); + + /* Decompress block */ + switch (btype) { + case 0: + /* Decompress uncompressed block */ + res = tinf_inflate_uncompressed_block(&d); + break; + case 1: + /* Decompress block with fixed Huffman trees */ + res = tinf_inflate_fixed_block(&d); + break; + case 2: + /* Decompress block with dynamic Huffman trees */ + res = tinf_inflate_dynamic_block(&d); + break; + default: + res = TINF_DATA_ERROR; + break; + } + + if (res != TINF_OK) { + return res; + } + } while (!bfinal); + + /* Check for overflow in bit reader */ + if (d.overflow) { + return TINF_DATA_ERROR; + } + + *destLen = d.dest - d.dest_start; + + return TINF_OK; +} + +/* clang -g -O1 -fsanitize=fuzzer,address -DTINF_FUZZING tinflate.c */ +#if defined(TINF_FUZZING) +#include +#include +#include +#include +#include + +unsigned char depacked[64 * 1024]; + +extern int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + if (size > UINT_MAX / 2) { return 0; } + unsigned int destLen = sizeof(depacked); + tinf_uncompress(depacked, &destLen, data, size); + return 0; +} +#endif \ No newline at end of file diff --git a/tools/microemu/tinf/tinfzlib.c b/tools/microemu/tinf/tinfzlib.c new file mode 100644 index 00000000..e9d6291a --- /dev/null +++ b/tools/microemu/tinf/tinfzlib.c @@ -0,0 +1,95 @@ +/* + * tinfzlib - tiny zlib decompressor + * + * Copyright (c) 2003-2019 Joergen Ibsen + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must + * not claim that you wrote the original software. If you use this + * software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must + * not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + */ + +#include "tinf.h" + +static unsigned int read_be32(const unsigned char *p) +{ + return ((unsigned int) p[0] << 24) + | ((unsigned int) p[1] << 16) + | ((unsigned int) p[2] << 8) + | ((unsigned int) p[3]); +} + +int tinf_zlib_uncompress(void *dest, unsigned int *destLen, + const void *source, unsigned int sourceLen) +{ + const unsigned char *src = (const unsigned char *) source; + unsigned char *dst = (unsigned char *) dest; + unsigned int a32; + int res; + unsigned char cmf, flg; + + /* -- Check header -- */ + + /* Check room for at least 2 byte header and 4 byte trailer */ + if (sourceLen < 6) { + return TINF_DATA_ERROR; + } + + /* Get header bytes */ + cmf = src[0]; + flg = src[1]; + + /* Check checksum */ + if ((256 * cmf + flg) % 31) { + return TINF_DATA_ERROR; + } + + /* Check method is deflate */ + if ((cmf & 0x0F) != 8) { + return TINF_DATA_ERROR; + } + + /* Check window size is valid */ + if ((cmf >> 4) > 7) { + return TINF_DATA_ERROR; + } + + /* Check there is no preset dictionary */ + if (flg & 0x20) { + return TINF_DATA_ERROR; + } + + /* -- Get Adler-32 checksum of original data -- */ + + a32 = read_be32(&src[sourceLen - 4]); + + /* -- Decompress data -- */ + + res = tinf_uncompress(dst, destLen, src + 2, sourceLen - 6); + + if (res != TINF_OK) { + return TINF_DATA_ERROR; + } + + /* -- Check Adler-32 checksum -- */ + + if (a32 != tinf_adler32(dst, *destLen)) { + return TINF_DATA_ERROR; + } + + return TINF_OK; +} \ No newline at end of file diff --git a/user/Makefile b/user/Makefile index 9a98297b..161e92b3 100644 --- a/user/Makefile +++ b/user/Makefile @@ -18,7 +18,11 @@ PROGRAMS += network/dhcp network/httpget network/ip network/listen network/nsloo PROGRAMS += services/resolved services/scheduler services/service services/shortcutd ifeq ($(GUI),1) - PROGRAMS += base/desktop + LIBS += desktop/libdesktop + + PROGRAMS += desktop/desktop desktop/counter desktop/sysctl desktop/taskmgr \ + desktop/netinfo desktop/explorer desktop/imgview desktop/service \ + desktop/terminal desktop/edit endif all: diff --git a/user/base/desktop/Makefile b/user/base/desktop/Makefile deleted file mode 100644 index cd8e0677..00000000 --- a/user/base/desktop/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -PROGRAM = desktop.elf - -include ../../program.mk - -CFLAGS += -I../../libraries/libload/include -I../../libraries/libsyntax/include - -LINK = -lsyntax -lload \ No newline at end of file diff --git a/user/base/desktop/button.c b/user/base/desktop/button.c deleted file mode 100644 index 9e737a36..00000000 --- a/user/base/desktop/button.c +++ /dev/null @@ -1,64 +0,0 @@ -#include -#include - -void button_init(button_t* btn, int x, int y, int width, int height, const char* label, void (*on_click)(window_instance_t* w, void* userdata), void* userdata) { - btn->x = x; - btn->y = y; - btn->width = width; - btn->height = height; - btn->label = label; - btn->bg_color = 0x445566; - btn->hover_color = 0x5577aa; - btn->text_color = 0xffffff; - btn->border_color = 0x666688; - btn->is_hovered = false; - btn->on_click = on_click; - btn->userdata = userdata; -} - -void button_draw(button_t* btn, window_instance_t* w) { - uint32_t color = btn->is_hovered ? btn->hover_color : btn->text_color; - uint32_t border = btn->is_hovered ? btn->hover_color : btn->border_color; - - window_draw_rect(w, btn->x, btn->y, btn->width, btn->height, btn->bg_color); - - int text_y = btn->y + (btn->height - 16) / 2; - window_draw_string(w, btn->x + 8, text_y, btn->label, color); - - window_draw_line(w, btn->x, btn->y, btn->x + btn->width, btn->y, border); - window_draw_line(w, btn->x, btn->y + btn->height - 1, btn->x + btn->width, btn->y + btn->height - 1, border); - window_draw_line(w, btn->x, btn->y, btn->x, btn->y + btn->height, border); - window_draw_line(w, btn->x + btn->width - 1, btn->y, btn->x + btn->width - 1, btn->y + btn->height, border); -} - -bool button_hit_test(button_t* btn, int x, int y) { - return x >= btn->x && x < btn->x + btn->width && - y >= btn->y && y < btn->y + btn->height; -} - -bool button_handle_event(button_t* btn, window_instance_t* w, event_t* event) { - if (event->type == EVENT_MOUSE_MOVE) { - int rel_x = event->x; - int rel_y = event->y - TITLE_BAR_HEIGHT; - bool now_hovered = button_hit_test(btn, rel_x, rel_y); - if (now_hovered != btn->is_hovered) { - btn->is_hovered = now_hovered; - w->is_dirty = true; - } - return now_hovered; - } - - if (event->type == EVENT_MOUSE_CLICK && event->button == MOUSE_BUTTON_LEFT) { - int rel_x = event->x; - int rel_y = event->y - TITLE_BAR_HEIGHT; - if (button_hit_test(btn, rel_x, rel_y)) { - if (btn->on_click) { - btn->on_click(w, btn->userdata); - } - w->is_dirty = true; - return true; - } - } - - return false; -} diff --git a/user/base/desktop/include/button.h b/user/base/desktop/include/button.h deleted file mode 100644 index 48163465..00000000 --- a/user/base/desktop/include/button.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include -#include -#include - -typedef struct { - int x; - int y; - int width; - int height; - const char* label; - uint32_t bg_color; - uint32_t hover_color; - uint32_t text_color; - uint32_t border_color; - bool is_hovered; - void (*on_click)(window_instance_t* w, void* userdata); - void* userdata; -} button_t; - -void button_init(button_t* btn, int x, int y, int width, int height, const char* label, void (*on_click)(window_instance_t* w, void* userdata), void* userdata); -void button_draw(button_t* btn, window_instance_t* w); -bool button_hit_test(button_t* btn, int x, int y); -bool button_handle_event(button_t* btn, window_instance_t* w, event_t* event); diff --git a/user/base/desktop/include/filepicker.h b/user/base/desktop/include/filepicker.h deleted file mode 100644 index 30838901..00000000 --- a/user/base/desktop/include/filepicker.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include - -typedef void (*filepicker_callback_t)(const char* path); - -void filepicker_open(filepicker_callback_t callback); diff --git a/user/base/desktop/include/window.h b/user/base/desktop/include/window.h deleted file mode 100644 index 75b63727..00000000 --- a/user/base/desktop/include/window.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include - -void window_add(int x, int y, int width, int height, const char* title, uint32_t bg_color, - void (*init)(window_instance_t*), - void (*update)(window_instance_t*, event_t*), - void (*draw)(window_instance_t*), - void (*cleanup)(window_instance_t*)); - -void window_close(int idx); -int window_at_point(int x, int y); -drag_type_t detect_drag_type(window_instance_t* w, int x, int y); -void handle_window_drag(window_instance_t* w, int mouse_x, int mouse_y); - -int window_get_count(void); -int window_get_focused(void); -void window_set_focused(int idx); -window_instance_t* window_get(int idx); diff --git a/user/base/desktop/include/window_helpers.h b/user/base/desktop/include/window_helpers.h deleted file mode 100644 index 981c2380..00000000 --- a/user/base/desktop/include/window_helpers.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include - -extern psf1_font_t font; - -static inline void window_draw_string(window_instance_t* w, int x, int y, const char* str, uint32_t fg_color) { - desktop_draw_string(&font, w->x + x, w->y + TITLE_BAR_HEIGHT + y, str, fg_color, w->bg_color); -} - -static inline void window_draw_char(window_instance_t* w, int x, int y, char c, uint32_t color, uint32_t bgcolor) { - desktop_draw_char(&font, w->x + x, w->y + y, c, color, bgcolor); -} - - -static inline void window_draw_line(window_instance_t* w, int x1, int y1, int x2, int y2, uint32_t color) { - desktop_draw_line(w->x + x1, w->y + TITLE_BAR_HEIGHT + y1, w->x + x2, w->y + TITLE_BAR_HEIGHT + y2, color); -} - -static inline void window_draw_pixel(window_instance_t* w, int x, int y, uint32_t color) { - desktop_set_pixel(w->x + x, w->y + TITLE_BAR_HEIGHT + y, color); -} - -static inline void window_draw_rect(window_instance_t* w, int x, int y, int width, int height, uint32_t color) { - desktop_draw_rect(w->x + x, w->y + TITLE_BAR_HEIGHT + y, width, height, color); -} - -static inline void window_set_pixel(window_instance_t* w, int x, int y, uint32_t color) { - desktop_set_pixel(w->x + x, w->y + y, color); -} - -static inline void window_draw_fpic(window_instance_t* w, fpic_image_t* pic, int x, int y) { - desktop_draw_fpic(pic, w->x + x, w->y + y); -} -static inline void window_draw_fpic_scaled(window_instance_t* w, fpic_image_t* pic, int x, int y, int scale) { - desktop_draw_fpic_scaled(pic, w->x + x, w->y + y, scale); -} diff --git a/user/base/desktop/lookup.c b/user/base/desktop/lookup.c deleted file mode 100644 index 8e1e7f71..00000000 --- a/user/base/desktop/lookup.c +++ /dev/null @@ -1,44 +0,0 @@ -#include - -#include -#include -#include -#include - -typedef struct symbols { - char name[64]; - void* address; -} symbols_t; - -symbols_t* symbols = NULL; -int num_symbols = 0; - - -void load_symbols() { - FILE* syms = fopen("dev:symbols", "r"); - if (!syms) { - printf("Could not open symbols file\n"); - abort(); - } - - fsize(syms, symbols_size); - symbols = malloc(symbols_size); - fread(symbols, 1, symbols_size, syms); - fclose(syms); - - num_symbols = symbols_size / sizeof(symbols_t); -} - -void* lookup(char* name) { - if (!symbols) { - load_symbols(); - } - - for (int i = 0; i < num_symbols; i++) { - if (strcmp(symbols[i].name, name) == 0) { - return symbols[i].address; - } - } - - return NULL; -} diff --git a/user/base/desktop/windows.c b/user/base/desktop/windows.c deleted file mode 100644 index 168909fe..00000000 --- a/user/base/desktop/windows.c +++ /dev/null @@ -1,69 +0,0 @@ -#include -#include -#include - -#include -#include -#include - -#include "windows/launcher/launcher.h" -#include "windows/counter/counter.h" -#include "windows/explorer/explorer.h" -#include "windows/taskmgr/taskmgr.h" -#include "windows/sysctl/sysctl.h" -#include "windows/netinfo/netinfo.h" -#include "windows/imgview/imgview.h" -#include "windows/edit/edit.h" -#include "windows/terminal/terminal.h" -#include "windows/service/service.h" - -window_definition_t** window_definitions = NULL; - -void load_extension(const char* s) { - FILE* file = fopen(s, "r"); - if (!file) { - printf("Could not open %s\n", s); - abort(); - } - - fsize(file, size); - void* buf = malloc(size); - fread(buf, 1, size, file); - fclose(file); - - - loaded_object_t obj = load(buf, size); - - void (*init)() = symbol(&obj, "init"); - if (!init) { - printf("Could not find init symbol\n"); - abort(); - } - - init(); -} - -void register_window(window_definition_t* def) { - window_definitions = array_push(window_definitions, &def); -} - -void register_windows(void) { - window_definitions = array_create(sizeof(window_definition_t*), 8); - - register_window(&launcher_definition); - register_window(&counter_definition); - register_window(&explorer_definition); - register_window(&taskmgr_definition); - register_window(&sysctl_definition); - register_window(&netinfo_definition); - register_window(&edit_definition); - register_window(&imgview_definition); - register_window(&terminal_definition); - register_window(&service_window_definition); - - desktop_register_file_assoc("fpic", imgview_open); - desktop_register_file_assoc("bmp", imgview_open); - desktop_register_file_assoc("mbif", imgview_open); - desktop_register_file_assoc("o", load_extension); -} - diff --git a/user/base/desktop/windows/counter/counter.c b/user/base/desktop/windows/counter/counter.c deleted file mode 100644 index 65139d9d..00000000 --- a/user/base/desktop/windows/counter/counter.c +++ /dev/null @@ -1,70 +0,0 @@ -#include "counter.h" -#include -#include -#include -#include -#include - -static void on_decrement(window_instance_t* w, void* userdata) { - counter_state_t* state = (counter_state_t*)w->state; - state->count--; -} - -static void on_increment(window_instance_t* w, void* userdata) { - counter_state_t* state = (counter_state_t*)w->state; - state->count++; -} - -static void on_reset(window_instance_t* w, void* userdata) { - counter_state_t* state = (counter_state_t*)w->state; - (void)userdata; - state->count = 0; -} - -void counter_init(window_instance_t* w) { - counter_state_t* state = malloc(sizeof(counter_state_t)); - state->count = 0; - - button_init(&state->decrement_btn, 10, 40, 100, 28, "- Minus", on_decrement, NULL); - button_init(&state->increment_btn, 120, 40, 100, 28, "+ Plus", on_increment, NULL); - button_init(&state->reset_btn, 10, 76, 210, 28, "Reset", on_reset, NULL); - - w->state = state; - w->title_bar_color = 0x4444ff; -} - -void counter_update(window_instance_t* w, event_t* event) { - counter_state_t* state = (counter_state_t*)w->state; - - button_handle_event(&state->decrement_btn, w, event); - button_handle_event(&state->increment_btn, w, event); - button_handle_event(&state->reset_btn, w, event); -} - -void counter_draw(window_instance_t* w) { - counter_state_t* state = (counter_state_t*)w->state; - - char buf[32]; - sprintf(buf, "Count: %d", state->count); - window_draw_string(w, 10, 10, buf, 0xffffff); - - button_draw(&state->decrement_btn, w); - button_draw(&state->increment_btn, w); - button_draw(&state->reset_btn, w); -} - -void counter_cleanup(window_instance_t* w) { - if (w->state) { - free(w->state); - w->state = NULL; - } -} - -window_definition_t counter_definition = { - .name = "Counter", - .register_window = register_counter_window, -}; - -void register_counter_window(void) { - window_add(50, 50, 300, 250, "Counter", 0x334455, counter_init, counter_update, counter_draw, counter_cleanup); -} diff --git a/user/base/desktop/windows/counter/counter.h b/user/base/desktop/windows/counter/counter.h deleted file mode 100644 index 5b24a57d..00000000 --- a/user/base/desktop/windows/counter/counter.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include -#include - -typedef struct { - int count; - button_t increment_btn; - button_t decrement_btn; - button_t reset_btn; -} counter_state_t; - -void counter_init(window_instance_t* w); -void counter_update(window_instance_t* w, event_t* event); -void counter_draw(window_instance_t* w); -void counter_cleanup(window_instance_t* w); -void register_counter_window(void); - -extern window_definition_t counter_definition; - diff --git a/user/base/desktop/windows/edit/edit.c b/user/base/desktop/windows/edit/edit.c deleted file mode 100644 index e36fa913..00000000 --- a/user/base/desktop/windows/edit/edit.c +++ /dev/null @@ -1,440 +0,0 @@ -#include "edit.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern psf1_font_t font; - -uint32_t color_lut[] = { - [black] = 0x888888, - [red] = 0xFF6B6B, - [green] = 0x88FFAA, - [yellow] = 0xFFFF88, - [blue] = 0x6B9FFF, - [magenta] = 0xFF88FF, - [cyan] = 0x88FFFF, - [white] = 0xFFFFFF, -}; - - - -#define CHAR_W 8 -#define CHAR_H 16 -#define STATUS_BAR_H 16 - -void recompute_color(edit_state_t* st) { - if (!st->syntax) { - return; - } - - if (st->color) { - free(st->color); - } - st->color = highlight(st->input_buffer, st->current_size, st->syntax); -} - - -void move_up(edit_state_t* st) { - if (st->buffer_ln_idx <= 0 || st->buffer_idx <= 0) { - return; - } - - int prev = st->buffer_idx; - for (int i = st->buffer_idx; i > 0; i--) { - st->buffer_idx--; - if (st->input_buffer[i - 1] == '\n' || st->buffer_idx < 0) { - break; - } - } - - if (st->buffer_idx < 0) { - st->buffer_idx = prev; - } else { - st->buffer_ln_idx--; - } -} - -void move_down(edit_state_t* st) { - if (st->buffer_ln_idx >= (int)st->ln_cnt - 1 || st->buffer_idx >= st->current_size) { - return; - } - - int prev = st->buffer_idx; - for (unsigned int i = st->buffer_idx; i < st->current_size; i++) { - st->buffer_idx++; - if (st->input_buffer[i] == '\n' || st->buffer_idx > st->current_size) { - break; - } - } - - if (st->buffer_idx > st->current_size) { - st->buffer_idx = prev; - } else { - st->buffer_ln_idx++; - } -} - -void move_left(edit_state_t* st) { - if (st->buffer_idx <= 0) { - return; - } - - if (st->input_buffer[st->buffer_idx - 1] == '\n') { - st->buffer_ln_idx--; - } - - st->buffer_idx--; -} - -void move_right(edit_state_t* st) { - if (st->buffer_idx < st->current_size) { - if (st->input_buffer[st->buffer_idx] == '\n') { - st->buffer_ln_idx++; - } - - st->buffer_idx++; - } -} - - -void handle_arrow(edit_state_t* st, int arrow) { - switch (arrow) { - case ARROW_UP: - move_up(st); - break; - case ARROW_DOWN: - move_down(st); - break; - case ARROW_LEFT: - move_left(st); - break; - case ARROW_RIGHT: - move_right(st); - break; - } -} - -void handle_key(edit_state_t* st, char input) { - if (!st->is_in_insert_mode) { - switch (input) { - case '\e': - st->is_in_insert_mode = true; - break; - case 'a': - move_left(st); - break; - case 'd': - move_right(st); - break; - case 'w': - move_up(st); - break; - case 's': - move_down(st); - break; - case '+': - st->file = freopen(st->file_name, "w", st->file); - fseek(st->file, 0, SEEK_SET); - fwrite(st->input_buffer, st->current_size, 1, st->file); - fseek(st->file, st->current_size, SEEK_SET); - ftruncate(st->file); - st->is_edited = false; - break; - } - } else { - switch (input) { - case '\b': - if (st->buffer_idx <= 0 || st->current_size <= 0) { - break; - } - - if (st->input_buffer[st->buffer_idx - 1] == '\n') { - st->buffer_ln_idx--; - } - - if (st->buffer_idx < st->current_size) { - memmove(&st->input_buffer[st->buffer_idx - 1], &st->input_buffer[st->buffer_idx], (st->current_size - st->buffer_idx)); - } - - if (st->input_buffer[st->buffer_idx] == '\n') { - st->ln_cnt--; - } else { - st->char_cnt--; - } - st->input_buffer = realloc(st->input_buffer, --st->current_size); - st->buffer_idx--; - recompute_color(st); - break; - - case '\e': - st->is_in_insert_mode = false; - break; - - default: - if (input == '\n') { - st->ln_cnt++; st->buffer_ln_idx++; - } else { - st->char_cnt++; - } - - st->is_edited = true; - st->current_size++; - st->input_buffer = realloc(st->input_buffer, st->current_size); - memmove(&st->input_buffer[st->buffer_idx + 1], &st->input_buffer[st->buffer_idx], (st->current_size - st->buffer_idx - 1)); - st->input_buffer[st->buffer_idx] = input; - st->buffer_idx++; - recompute_color(st); - break; - } - } -} - - -static char s_pending_path[128]; - -void edit_load_file(edit_state_t* st, const char* path); - -void edit_init(window_instance_t* w) { - edit_state_t* st = malloc(sizeof(edit_state_t)); - memset(st, 0, sizeof(edit_state_t)); - st->is_in_insert_mode = true; - w->state = st; - w->title_bar_color = 0x445566; - - if (s_pending_path[0]) { - edit_load_file(st, s_pending_path); - s_pending_path[0] = '\0'; - } -} - -char* get_file_extension(const char* filename) { - char* chr_ptr = strchr(filename, '.'); - if (chr_ptr == NULL) { - return ""; - } - return ++chr_ptr; -} - -void edit_load_file(edit_state_t* st, const char* path) { - int plen = strlen(path); - if (plen >= (int)sizeof(st->file_name)) { - plen = sizeof(st->file_name) - 1; - } - - memcpy(st->file_name, path, plen); - st->file_name[plen] = '\0'; - - st->file = fopen(path, "r"); - if (!st->file) { - return; - } - - fsize(st->file, file_size); - st->input_buffer = malloc(file_size ? file_size : 1); - memset(st->input_buffer, 0, file_size ? file_size : 1); - st->current_size = file_size; - st->buffer_idx = file_size; - - if (file_size) { - fread(st->input_buffer, file_size, 1, st->file); - } - - st->ln_cnt = 0; - st->char_cnt = 0; - for (unsigned int i = 0; i < st->current_size; i++) { - st->char_cnt++; - if (st->input_buffer[i] == '\n') { - st->ln_cnt++; st->char_cnt--; - } - } - st->buffer_ln_idx = st->ln_cnt; - - if (!getenv("NOSYX")) { - char syx[128] = { 0 }; - strcat(syx, getenv("ROOT_FS")); - strcat(syx, "syntax/"); - strcat(syx, get_file_extension(st->file_name)); - strcat(syx, ".syx"); - st->syntax = load_syntax(syx); - } - - recompute_color(st); -} - -static void edit_picker_callback(const char* path) { - edit_open(path); -} - -void edit_update(window_instance_t* w, event_t* event) { - edit_state_t* st = (edit_state_t*)w->state; - if (!st->input_buffer) { - return; - } - - if (event->type == EVENT_KEY_PRESS && event->key) { - handle_key(st, event->key); - w->is_dirty = true; - } else if (event->type == EVENT_ARROW_KEY && event->arrow) { - handle_arrow(st, event->arrow); - w->is_dirty = true; - } -} - -void edit_draw(window_instance_t* w) { - edit_state_t* st = (edit_state_t*)w->state; - - if (!st->input_buffer) { - window_draw_string(w, 4, 4, "No file loaded", 0xFF6B6B); - return; - } - - int content_h = w->height - TITLE_BAR_HEIGHT - STATUS_BAR_H; - int viewport_rows = content_h / CHAR_H; - if (viewport_rows < 1) viewport_rows = 1; - - char gutter_buf[16] = { 0 }; - sprintf(gutter_buf, "%d.", st->ln_cnt > 0 ? st->ln_cnt : 1); - int gutter_chars = strlen(gutter_buf) + 1; - int gutter_px = gutter_chars * CHAR_W; - - if ((int)st->buffer_ln_idx < st->scroll_offset) { - st->scroll_offset = st->buffer_ln_idx; - } else if ((int)st->buffer_ln_idx >= st->scroll_offset + viewport_rows) { - st->scroll_offset = st->buffer_ln_idx - viewport_rows + 1; - } - if (st->scroll_offset < 0) { - st->scroll_offset = 0; - } - - int line = 0; - int draw_y = 0; - int draw_x = gutter_px; - bool cursor_drawn = false; - - int vis_start_drawn = 0; - - for (unsigned int i = 0; i < st->current_size; i++) { - bool in_view = (line >= st->scroll_offset && line < st->scroll_offset + viewport_rows); - - if (in_view) { - if (draw_y + CHAR_H > content_h) { - break; - } - - if (!vis_start_drawn || (draw_x == gutter_px && i > 0 && st->input_buffer[i - 1] == '\n')) { - int display_line = line + 1; - char ln_buf[16] = { 0 }; - sprintf(ln_buf, "%d.", display_line); - window_draw_string(w, 0, draw_y, ln_buf, 0x888888); - vis_start_drawn = 1; - } - - if (i == st->buffer_idx) { - desktop_draw_char(&font, w->x + draw_x, w->y + TITLE_BAR_HEIGHT + draw_y, '|', 0x88FFFF, 0x1a1a2e); - draw_x += CHAR_W; - cursor_drawn = true; - } - - if (st->input_buffer[i] >= 0x20 && st->input_buffer[i] <= 0x7E) { - uint32_t fg = 0xFFFFFF; - if (st->color && i < st->current_size) { - fg = color_lut[st->color[i]]; - } - desktop_draw_char(&font, w->x + draw_x, w->y + TITLE_BAR_HEIGHT + draw_y, st->input_buffer[i], fg, 0x1a1a2e); - } - draw_x += CHAR_W; - - if (st->input_buffer[i] == '\n') { - line++; - draw_x = gutter_px; - draw_y += CHAR_H; - } else if (draw_x + CHAR_W >= w->width) { - draw_x = gutter_px; - draw_y += CHAR_H; - } - } else { - if (st->input_buffer[i] == '\n') line++; - } - } - - if (!cursor_drawn) { - if (line >= st->scroll_offset && line < st->scroll_offset + viewport_rows && draw_y + CHAR_H <= content_h) { - if (!vis_start_drawn) { - char ln_buf[16] = { 0 }; - sprintf(ln_buf, "%d.", line + 1); - window_draw_string(w, 0, draw_y, ln_buf, 0x888888); - } - desktop_draw_char(&font, w->x + draw_x, w->y + TITLE_BAR_HEIGHT + draw_y, '|', 0x88FFFF, 0x1a1a2e); - } - } - - int status_y = w->height - STATUS_BAR_H; - for (int x = 0; x < w->width; x++) { - for (int y = status_y; y < w->height; y++) { - window_set_pixel(w, x, y, 0x333344); - } - } - - char status[256] = { 0 }; - sprintf(status, " %s [%c] %s Ln %d/%d", st->file_name, st->is_edited ? '*' : '-', st->is_in_insert_mode ? "INSERT" : "EDIT", st->buffer_ln_idx + 1, st->ln_cnt > 0 ? st->ln_cnt : 1); - - desktop_draw_string(&font, w->x, w->y + status_y, status, 0xFFFFFF, 0x333344); -} - -void edit_cleanup(window_instance_t* w) { - edit_state_t* st = (edit_state_t*)w->state; - if (st) { - if (st->input_buffer) { - free(st->input_buffer); - } - - if (st->color) { - free(st->color); - } - - if (st->syntax) { - free(st->syntax); - } - - if (st->file) { - fclose(st->file); - } - - free(st); - w->state = NULL; - } -} - -window_definition_t edit_definition = { - .name = "Editor", - .register_window = edit_open_picker, -}; - -void edit_open_picker() { - filepicker_open(edit_picker_callback); -} - -void edit_open(const char* path) { - int plen = strnlen(path, sizeof(s_pending_path) - 1); - memcpy(s_pending_path, path, plen); - s_pending_path[plen] = '\0'; - - const char* base = path; - for (const char* p = path; *p; p++) { - if (*p == '/') { - base = p + 1; - } - } - char title[64] = { 0 }; - sprintf(title, "Edit: %s", base); - - window_add(80, 60, 500, 380, title, 0x1a1a2e, edit_init, edit_update, edit_draw, edit_cleanup); -} diff --git a/user/base/desktop/windows/edit/edit.h b/user/base/desktop/windows/edit/edit.h deleted file mode 100644 index b8cef8db..00000000 --- a/user/base/desktop/windows/edit/edit.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -typedef struct { - char file_name[128]; - char* input_buffer; - bool is_edited; - bool is_in_insert_mode; - - unsigned int ln_cnt; - unsigned int char_cnt; - - unsigned int buffer_ln_idx; - unsigned int buffer_idx; - - unsigned int current_size; - - int scroll_offset; - - FILE* file; - - uint8_t* color; - - syntax_header_t* syntax; -} edit_state_t; - -void edit_init(window_instance_t* w); -void edit_update(window_instance_t* w, event_t* event); -void edit_draw(window_instance_t* w); -void edit_cleanup(window_instance_t* w); -void edit_open_picker(void); -void edit_open(const char* path); - -extern window_definition_t edit_definition; diff --git a/user/base/desktop/windows/explorer/explorer.c b/user/base/desktop/windows/explorer/explorer.c deleted file mode 100644 index 6abb7518..00000000 --- a/user/base/desktop/windows/explorer/explorer.c +++ /dev/null @@ -1,298 +0,0 @@ -#include "explorer.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern psf1_font_t font; - -void format_size(char* out, size_t size) { - if (size < 1024) { - sprintf(out, "%d b", size); - } else if (size < 1024 * 1024) { - sprintf(out, "%d Kb", size / 1024); - } else if (size < 1024 * 1024 * 1024) { - sprintf(out, "%d Mb", size / (1024 * 1024)); - } else { - sprintf(out, "%d Gb", size / (1024 * 1024 * 1024)); - } -} - -void explorer_invalidate_cache(explorer_state_t* state) { - if (state->size_cached) { - memset(state->size_cached, 0, sizeof(bool) * state->max_rows); - } -} - -void explorer_realloc_rows(explorer_state_t* state, int new_max) { - if (new_max < 1) { - new_max = 1; - } - - state->max_rows = new_max; - state->files = realloc(state->files, sizeof(click_area_t) * new_max); - memset(state->files, 0, sizeof(click_area_t) * new_max); - state->sizes = realloc(state->sizes, sizeof(int) * new_max); - memset(state->sizes, 0, sizeof(int) * new_max); - state->size_cached = realloc(state->size_cached, sizeof(bool) * new_max); - memset(state->size_cached, 0, sizeof(bool) * new_max); -} - -void on_explorer_back(window_instance_t* w, void* userdata) { - explorer_state_t* state = (explorer_state_t*)w->state; - char full_path[128] = { 0 }; - strcpy(full_path, state->cwd); - int fp_len = strlen(full_path); - if (fp_len > 0 && full_path[fp_len - 1] != '/') { - strcat(full_path, "/"); - } - strcat(full_path, ".."); - char path_buf[256] = { 0 }; - if (!resolve(full_path, path_buf)) { - state->fs_mode = true; - memset(state->cwd, 0, 64); - } else { - memset(state->cwd, 0, 64); - strcpy(state->cwd, path_buf); - } - state->offset = 0; - explorer_invalidate_cache(state); -} - -void on_explorer_up(window_instance_t* w, void* userdata) { - explorer_state_t* state = (explorer_state_t*)w->state; - if (state->offset > 0) { - state->offset--; - explorer_invalidate_cache(state); - } -} - -void on_explorer_down(window_instance_t* w, void* userdata) { - (void)userdata; - explorer_state_t* state = (explorer_state_t*)w->state; - state->offset++; - explorer_invalidate_cache(state); -} - -void explorer_init(window_instance_t* w) { - explorer_state_t* state = malloc(sizeof(explorer_state_t)); - memset(state, 0, sizeof(explorer_state_t)); - state->offset = 0; - state->fs_mode = true; - state->cached_w = w->width; - state->cached_h = w->height; - int max = (w->height - TITLE_BAR_HEIGHT - 50) / 16; - if (max < 1) { - max = 1; - } - - state->max_rows = max; - state->files = malloc(sizeof(click_area_t) * max); - memset(state->files, 0, sizeof(click_area_t) * max); - state->sizes = malloc(sizeof(int) * max); - memset(state->sizes, 0, sizeof(int) * max); - state->size_cached = malloc(sizeof(bool) * max); - memset(state->size_cached, 0, sizeof(bool) * max); - memset(state->cwd, 0, 64); - - button_init(&state->back_btn, w->width - 54, 2, 50, 18, "Back", on_explorer_back, NULL); - button_init(&state->up_btn, w->width - 28, 22, 24, 20, "^", on_explorer_up, NULL); - button_init(&state->down_btn, w->width - 28, w->height - TITLE_BAR_HEIGHT - 24, 24, 20, "v", on_explorer_down, NULL); - - w->state = state; - w->title_bar_color = 0x4488ff; -} - -void explorer_update(window_instance_t* w, event_t* event) { - explorer_state_t* state = (explorer_state_t*)w->state; - - button_handle_event(&state->back_btn, w, event); - button_handle_event(&state->up_btn, w, event); - button_handle_event(&state->down_btn, w, event); - - if (event->type == EVENT_MOUSE_CLICK && event->button == MOUSE_BUTTON_LEFT) { - int rel_x = event->x; - int rel_y = event->y; - - for (int i = 0; i < state->max_rows; i++) { - click_area_t* btn = &state->files[i]; - if (btn->width && btn->height && - rel_x >= btn->x && rel_x < btn->x + btn->width && - rel_y >= btn->y && rel_y < btn->y + btn->height) { - - if (state->fs_mode) { - char out[512] = { 0 }; - fs_at(out, state->offset + i); - if (out[0]) { - strcat(out, ":/"); - memset(state->cwd, 0, 64); - strcpy(state->cwd, out); - state->fs_mode = false; - state->offset = 0; - explorer_invalidate_cache(state); - w->is_dirty = true; - } - } else { - dir_t dir; - dir_at(state->cwd, state->offset + i, &dir); - - if (!dir.is_none) { - if (dir.type == ENTRY_DIR) { - char full_path[128] = { 0 }; - strcpy(full_path, state->cwd); - int fp_len = strlen(full_path); - if (fp_len > 0 && full_path[fp_len - 1] != '/') { - strcat(full_path, "/"); - } - strcat(full_path, dir.name); - char path_buf[256] = { 0 }; - if (!resolve(full_path, path_buf)) { - state->fs_mode = true; - memset(state->cwd, 0, 64); - state->offset = 0; - } else { - memset(state->cwd, 0, 64); - strcpy(state->cwd, path_buf); - state->offset = 0; - } - explorer_invalidate_cache(state); - w->is_dirty = true; - } else if (dir.type == ENTRY_FILE) { - char full_path[128] = { 0 }; - strcpy(full_path, state->cwd); - int fp_len = strlen(full_path); - if (fp_len > 0 && full_path[fp_len - 1] != '/') { - strcat(full_path, "/"); - } - strcat(full_path, dir.name); - desktop_open_file(full_path); - } - } - } - break; - } - } - } -} - -void explorer_draw(window_instance_t* w) { - explorer_state_t* state = (explorer_state_t*)w->state; - - if (w->width != state->cached_w || w->height != state->cached_h) { - state->cached_w = w->width; - state->cached_h = w->height; - int new_max = (w->height - TITLE_BAR_HEIGHT - 50) / 16; - explorer_realloc_rows(state, new_max); - state->down_btn.y = w->height - TITLE_BAR_HEIGHT - 24; - } - - window_draw_string(w, 2, 2, state->cwd, 0xffffff); - - window_draw_line(w, 0, 20, w->width, 20, 0x444444); - - button_draw(&state->back_btn, w); - button_draw(&state->up_btn, w); - button_draw(&state->down_btn, w); - - int start_y = 32; - - if (state->fs_mode) { - for (int i = 0; i < state->max_rows; i++) { - char out[512] = { 0 }; - if (!fs_at(out, state->offset + i)) { - break; - } - - int y = start_y + i * 16; - window_draw_string(w, 2, y, out, 0x90EE90); - - state->files[i].x = 0; - state->files[i].y = TITLE_BAR_HEIGHT + y; - state->files[i].width = w->width - 32; - state->files[i].height = 16; - - } - } else { - dir_t dir; - int count = 0; - for (int i = state->offset; i < state->offset + state->max_rows && count < state->max_rows; i++) { - dir_at(state->cwd, i, &dir); - if (dir.is_none) { - break; - } - - int y = start_y + count * 16; - uint32_t color = (dir.type == ENTRY_DIR) ? 0x90EE90 : 0xFF6B6B; - window_draw_string(w, 2, y, dir.name, color); - window_draw_char(w, w->width - 32, TITLE_BAR_HEIGHT + y, dir.type == ENTRY_FILE ? 'F' : 'D', 0xffffff, 0x1a1a2e); - - if (dir.type == ENTRY_FILE) { - if (!state->size_cached[count]) { - char full_path[128] = { 0 }; - strcpy(full_path, state->cwd); - int fp_len = strlen(full_path); - if (fp_len > 0 && full_path[fp_len - 1] != '/') { - strcat(full_path, "/"); - } - strcat(full_path, dir.name); - int fd = open(full_path, 0); - if (fd >= 0) { - state->sizes[count] = filesize(fd); - close(fd); - } else { - state->sizes[count] = -1; - } - state->size_cached[count] = true; - } - if (state->sizes[count] >= 0) { - char size_str[16] = { 0 }; - format_size(size_str, state->sizes[count]); - int size_x = w->width - 40 - strlen(size_str) * 8; - window_draw_string(w, size_x, y, size_str, 0x888888); - } - } - - state->files[count].x = 0; - state->files[count].y = TITLE_BAR_HEIGHT + y; - state->files[count].width = w->width - 32; - state->files[count].height = 16; - - count++; - } - } -} - -void explorer_cleanup(window_instance_t* w) { - explorer_state_t* state = (explorer_state_t*)w->state; - if (state) { - if (state->files) { - free(state->files); - } - - if (state->sizes) { - free(state->sizes); - } - - if (state->size_cached) { - free(state->size_cached); - } - - free(state); - w->state = NULL; - } -} - -window_definition_t explorer_definition = { - .name = "Explorer", - .register_window = register_explorer_window, -}; - -void register_explorer_window(void) { - window_add(100, 100, 400, 300, "Explorer", 0x1a1a2e, explorer_init, explorer_update, explorer_draw, explorer_cleanup); -} diff --git a/user/base/desktop/windows/explorer/explorer.h b/user/base/desktop/windows/explorer/explorer.h deleted file mode 100644 index 70402d77..00000000 --- a/user/base/desktop/windows/explorer/explorer.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include - -typedef struct { - click_area_t* files; - int offset; - int max_rows; - bool fs_mode; - char cwd[64]; - int* sizes; - bool* size_cached; - int cached_w; - int cached_h; - button_t back_btn; - button_t up_btn; - button_t down_btn; -} explorer_state_t; - -void explorer_init(window_instance_t* w); -void explorer_update(window_instance_t* w, event_t* event); -void explorer_draw(window_instance_t* w); -void explorer_cleanup(window_instance_t* w); -void register_explorer_window(void); - -extern window_definition_t explorer_definition; - diff --git a/user/base/desktop/windows/filepicker/filepicker.c b/user/base/desktop/windows/filepicker/filepicker.c deleted file mode 100644 index 0321346a..00000000 --- a/user/base/desktop/windows/filepicker/filepicker.c +++ /dev/null @@ -1,250 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern psf1_font_t font; - -#define FP_ROW_H 16 -#define FP_HDR_H 32 - -typedef struct { - click_area_t* rows; - int offset; - int max_rows; - bool fs_mode; - char cwd[128]; - filepicker_callback_t callback; - button_t back_btn; - button_t up_btn; - button_t down_btn; -} filepicker_state_t; - -static void on_fp_back(window_instance_t* w, void* userdata) { - filepicker_state_t* st = (filepicker_state_t*)w->state; - if (st->fs_mode) { - return; - } - char full[128] = { 0 }; - strcpy(full, st->cwd); - int fl = strlen(full); - if (fl > 0 && full[fl - 1] != '/') { - strcat(full, "/"); - } - strcat(full, ".."); - char buf[256] = { 0 }; - if (!resolve(full, buf)) { - st->fs_mode = true; - memset(st->cwd, 0, sizeof(st->cwd)); - } else { - memset(st->cwd, 0, sizeof(st->cwd)); - strcpy(st->cwd, buf); - } - st->offset = 0; -} - -static void on_fp_up(window_instance_t* w, void* userdata) { - (void)userdata; - filepicker_state_t* st = (filepicker_state_t*)w->state; - if (st->offset > 0) st->offset--; -} - -static void on_fp_down(window_instance_t* w, void* userdata) { - (void)userdata; - filepicker_state_t* st = (filepicker_state_t*)w->state; - st->offset++; -} - -void fp_init(window_instance_t* w) { - filepicker_state_t* st = malloc(sizeof(filepicker_state_t)); - memset(st, 0, sizeof(filepicker_state_t)); - st->max_rows = (w->height - TITLE_BAR_HEIGHT - FP_HDR_H - 16) / FP_ROW_H; - if (st->max_rows < 1) { - st->max_rows = 1; - } - st->fs_mode = true; - st->rows = malloc(sizeof(click_area_t) * st->max_rows); - memset(st->rows, 0, sizeof(click_area_t) * st->max_rows); - - button_init(&st->back_btn, w->width - 54, 2, 50, 18, "Back", on_fp_back, NULL); - button_init(&st->up_btn, w->width - 28, FP_HDR_H, 24, 20, "^", on_fp_up, NULL); - button_init(&st->down_btn, w->width - 28, w->height - TITLE_BAR_HEIGHT - 24, 24, 20, "v", on_fp_down, NULL); - - w->state = st; - w->title_bar_color = 0x446688; - w->is_dirty = true; -} - -static filepicker_callback_t s_pending_callback = NULL; - -void fp_update(window_instance_t* w, event_t* event) { - filepicker_state_t* st = (filepicker_state_t*)w->state; - if (!st) { - return; - } - - if (!st->callback && s_pending_callback) { - st->callback = s_pending_callback; - s_pending_callback = NULL; - } - - button_handle_event(&st->back_btn, w, event); - button_handle_event(&st->up_btn, w, event); - button_handle_event(&st->down_btn, w, event); - - if (event->type != EVENT_MOUSE_CLICK || event->button != MOUSE_BUTTON_LEFT) { - return; - } - - int rx = event->x; - int ry = event->y; - - for (int i = 0; i < st->max_rows; i++) { - click_area_t* btn = &st->rows[i]; - if (!btn->width || !btn->height) { - continue; - } - - if (rx < btn->x || rx >= btn->x + btn->width) { - continue; - } - - if (ry < btn->y || ry >= btn->y + btn->height) { - continue; - } - - if (st->fs_mode) { - char out[512] = { 0 }; - if (fs_at(out, st->offset + i) && out[0]) { - strcat(out, ":/"); - memset(st->cwd, 0, sizeof(st->cwd)); - strcpy(st->cwd, out); - st->fs_mode = false; - st->offset = 0; - w->is_dirty = true; - } - } else { - dir_t dir; - dir_at(st->cwd, st->offset + i, &dir); - if (dir.is_none) { - break; - } - - char full[256] = { 0 }; - strcpy(full, st->cwd); - int fl = strlen(full); - if (fl > 0 && full[fl - 1] != '/') { - strcat(full, "/"); - } - - strcat(full, dir.name); - - if (dir.type == ENTRY_DIR) { - char buf[128] = { 0 }; - if (!resolve(full, buf)) { - st->fs_mode = true; - memset(st->cwd, 0, sizeof(st->cwd)); - } else { - memset(st->cwd, 0, sizeof(st->cwd)); - strcpy(st->cwd, buf); - } - st->offset = 0; - w->is_dirty = true; - } else if (dir.type == ENTRY_FILE) { - filepicker_callback_t cb = st->callback; - int count = window_get_count(); - for (int j = 0; j < count; j++) { - if (window_get(j) == w) { - window_close(j); - break; - } - } - if (cb) { - cb(full); - } - return; - } - } - break; - } -} - -static void fp_draw(window_instance_t* w) { - filepicker_state_t* st = (filepicker_state_t*)w->state; - - window_draw_string(w, 2, 2, st->cwd[0] ? st->cwd : "(select a filesystem)", 0xffffff); - window_draw_line(w, 0, 20, w->width, 20, 0x444444); - - button_draw(&st->back_btn, w); - button_draw(&st->up_btn, w); - button_draw(&st->down_btn, w); - - int start_y = FP_HDR_H; - - if (st->fs_mode) { - for (int i = 0; i < st->max_rows; i++) { - char out[512] = { 0 }; - if (!fs_at(out, st->offset + i)) { - break; - } - - int y = start_y + i * FP_ROW_H; - window_draw_string(w, 2, y, out, 0x90EE90); - - st->rows[i].x = 0; - st->rows[i].y = TITLE_BAR_HEIGHT + y; - st->rows[i].width = w->width - 32; - st->rows[i].height = FP_ROW_H; - } - } else { - dir_t dir; - int cnt = 0; - for (int i = st->offset; i < st->offset + st->max_rows && cnt < st->max_rows; i++) { - dir_at(st->cwd, i, &dir); - if (dir.is_none) { - break; - } - - int y = start_y + cnt * FP_ROW_H; - uint32_t color = (dir.type == ENTRY_DIR) ? 0x90EE90 : 0xFF6B6B; - window_draw_string(w, 2, y, dir.name, color); - window_draw_char(w, w->width - 32, TITLE_BAR_HEIGHT + y, dir.type == ENTRY_FILE ? 'F' : 'D', 0xffffff, 0x1a1a2e); - - st->rows[cnt].x = 0; - st->rows[cnt].y = TITLE_BAR_HEIGHT + y; - st->rows[cnt].width = w->width - 32; - st->rows[cnt].height = FP_ROW_H; - cnt++; - } - } -} - -static void fp_cleanup(window_instance_t* w) { - filepicker_state_t* st = (filepicker_state_t*)w->state; - if (st) { - if (st->rows) free(st->rows); - free(st); - w->state = NULL; - } -} - - -void filepicker_open(filepicker_callback_t callback) { - s_pending_callback = callback; - window_add(120, 80, 380, 320, "Open File", 0x445533, fp_init, fp_update, fp_draw, fp_cleanup); - - window_instance_t* w = window_get(window_get_count() - 1); - if (w && w->state) { - filepicker_state_t* st = (filepicker_state_t*)w->state; - if (!st->callback) { - st->callback = s_pending_callback; - s_pending_callback = NULL; - } - } -} diff --git a/user/base/desktop/windows/imgview/bmp_fmt.c b/user/base/desktop/windows/imgview/bmp_fmt.c deleted file mode 100644 index 4b09617b..00000000 --- a/user/base/desktop/windows/imgview/bmp_fmt.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "scanner.h" -#include - -bool test_bmp(void* buf, int size) { - uint8_t* data = (uint8_t*)buf; - return data[0] == 'B' && data[1] == 'M'; -} - -void get_size_bmp(void* buf, int size, int* w, int* h) { - uint8_t* data = (uint8_t*)buf; - *w = *(int32_t*)(data + 18); - int raw_h = *(int32_t*)(data + 22); - *h = raw_h < 0 ? -raw_h : raw_h; -} - -uint32_t get_pixel_bmp(void* buf, int size, int x, int y) { - uint8_t* data = (uint8_t*)buf; - int w, h; - get_size_bmp(buf, size, &w, &h); - int raw_h = *(int32_t*)(data + 22); - int bpp = *(int16_t*)(data + 28); - int row_sz = w * bpp / 8; - int pad = (4 - (row_sz % 4)) % 4; - // bottom-up when raw_h > 0, top-down when raw_h < 0 - int src_y = (raw_h > 0) ? (h - y - 1) : y; - int offset = 54 + src_y * (row_sz + pad); - uint8_t* px = data + offset + x * bpp / 8; - if (bpp == 32) { - return *(uint32_t*)px; - } else if (bpp == 24) { - return 0xff000000 | (uint32_t)px[2] << 16 | (uint32_t)px[1] << 8 | px[0]; - } else if (bpp == 8) { - uint8_t v = *px; - return 0xff000000 | ((uint32_t)v << 16) | ((uint32_t)v << 8) | v; - } - return 0; -} - -format_scanner_t bmp_scanner = { - .test_format = test_bmp, - .get_pixel = get_pixel_bmp, - .get_size = get_size_bmp, -}; diff --git a/user/base/desktop/windows/imgview/fpic_fmt.c b/user/base/desktop/windows/imgview/fpic_fmt.c deleted file mode 100644 index 6719a852..00000000 --- a/user/base/desktop/windows/imgview/fpic_fmt.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "scanner.h" -#include - -typedef struct { - uint64_t magic; - uint64_t width; - uint64_t height; - uint32_t pixels[]; -} __attribute__((packed)) imgview_fpic_t; - -bool test_fpic(void* buf, int size) { - imgview_fpic_t* img = (imgview_fpic_t*)buf; - return img->magic == 0xc0ffebabe; -} - -uint32_t get_pixel_fpic(void* buf, int size, int x, int y) { - imgview_fpic_t* img = (imgview_fpic_t*)buf; - return img->pixels[(uint64_t)y * img->width + (uint64_t)x]; -} - -void get_size_fpic(void* buf, int size, int* w, int* h) { - imgview_fpic_t* img = (imgview_fpic_t*)buf; - *w = (int)img->width; - *h = (int)img->height; -} - -format_scanner_t fpic_scanner = { - .test_format = test_fpic, - .get_pixel = get_pixel_fpic, - .get_size = get_size_fpic, -}; diff --git a/user/base/desktop/windows/imgview/imgview.c b/user/base/desktop/windows/imgview/imgview.c deleted file mode 100644 index 27515093..00000000 --- a/user/base/desktop/windows/imgview/imgview.c +++ /dev/null @@ -1,131 +0,0 @@ -#include "imgview.h" -#include "scanner.h" -#include -#include -#include -#include -#include -#include -#include - -extern psf1_font_t font; - -typedef struct { - void* file_buf; - int file_size; - format_scanner_t* scanner; -} imgview_state_t; - -char s_pending_path[128]; - -void imgview_init(window_instance_t* w); -void imgview_update(window_instance_t* w, event_t* event); -void imgview_draw(window_instance_t* w); -void imgview_cleanup(window_instance_t* w); - -void imgview_open(const char* path) { - int path_len = strnlen(path, sizeof(s_pending_path) - 1); - memcpy(s_pending_path, path, path_len); - s_pending_path[path_len] = '\0'; - - const char* base = path; - for (const char* p = path; *p; p++) { - if (*p == '/') { - base = p + 1; - } - } - char title[64] = { 0 }; - memcpy(title, "Image: ", 7); - int blen = strnlen(base, sizeof(title) - 8); - memcpy(title + 7, base, blen); - - window_add(80, 80, 400, 300, title, 0x000000, imgview_init, imgview_update, imgview_draw, imgview_cleanup); -} - -void imgview_init(window_instance_t* w) { - imgview_state_t* state = malloc(sizeof(imgview_state_t)); - memset(state, 0, sizeof(imgview_state_t)); - w->state = state; - w->title_bar_color = 0x222244; - w->is_dirty = true; - - FILE* f = fopen(s_pending_path, "rb"); - if (!f) { - return; - } - fsize(f, img_file_size); - void* buf = malloc(img_file_size); - fread(buf, img_file_size, 1, f); - fclose(f); - - state->file_buf = buf; - state->file_size = (int)img_file_size; - state->scanner = get_scanner(buf, (int)img_file_size); -} - -void imgview_update(window_instance_t* w, event_t* event) { - w->is_dirty = true; -} - -void imgview_draw(window_instance_t* w) { - imgview_state_t* state = (imgview_state_t*)w->state; - - if (!state || !state->scanner) { - window_draw_string(w, 4, 4, "Unsupported format", 0xff4444); - return; - } - - int img_w, img_h; - state->scanner->get_size(state->file_buf, state->file_size, &img_w, &img_h); - if (img_w <= 0 || img_h <= 0) { - return; - } - - int content_w = w->width; - int content_h = w->height - TITLE_BAR_HEIGHT; - - int fit_x = content_w / img_w; - int fit_y = content_h / img_h; - int scale = (fit_x < fit_y) ? fit_x : fit_y; - if (scale < 1) { - scale = 1; - } - - int draw_w = img_w * scale; - int draw_h = img_h * scale; - - int off_x = (content_w - draw_w) / 2; - int off_y = (content_h - draw_h) / 2; - - for (int px = 0; px < img_w; px++) { - for (int py = 0; py < img_h; py++) { - uint32_t color = state->scanner->get_pixel(state->file_buf, state->file_size, px, py); - uint32_t rgb = color & 0x00FFFFFF; - for (int sx = 0; sx < scale; sx++) { - for (int sy = 0; sy < scale; sy++) { - window_set_pixel(w, off_x + px * scale + sx, TITLE_BAR_HEIGHT + off_y + py * scale + sy, rgb); - } - } - } - } -} - -void imgview_cleanup(window_instance_t* w) { - imgview_state_t* state = (imgview_state_t*)w->state; - if (state) { - if (state->file_buf) { - free(state->file_buf); - } - free(state); - w->state = NULL; - } -} - -window_definition_t imgview_definition = { - .name = "Image Viewer", - .register_window = imgview_open_picker, -}; - -void imgview_open_picker(void) { - filepicker_open(imgview_open); -} diff --git a/user/base/desktop/windows/imgview/imgview.h b/user/base/desktop/windows/imgview/imgview.h deleted file mode 100644 index fbf00953..00000000 --- a/user/base/desktop/windows/imgview/imgview.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include - -void imgview_open(const char* path); - -// Open a file picker, then open the selected image. -void imgview_open_picker(void); - -extern window_definition_t imgview_definition; diff --git a/user/base/desktop/windows/imgview/mbif_fmt.c b/user/base/desktop/windows/imgview/mbif_fmt.c deleted file mode 100644 index 8b19c5cb..00000000 --- a/user/base/desktop/windows/imgview/mbif_fmt.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "scanner.h" -#include - -typedef struct { - int32_t width, height, xOff, yOff; - int64_t size; - uint32_t imageBuffer[]; -} __attribute__((packed)) mbif_image_t; - -bool test_mbif(void* buf, int file_size) { - mbif_image_t* img = (mbif_image_t*)buf; - return img->size == (int64_t)(img->width * img->height * 4); -} - -uint32_t get_pixel_mbif(void* buf, int file_size, int x, int y) { - mbif_image_t* img = (mbif_image_t*)buf; - return img->imageBuffer[y * img->width + x]; -} - -void get_size_mbif(void* buf, int file_size, int* w, int* h) { - mbif_image_t* img = (mbif_image_t*)buf; - *w = img->width; - *h = img->height; -} - -format_scanner_t mbif_scanner = { - .test_format = test_mbif, - .get_pixel = get_pixel_mbif, - .get_size = get_size_mbif, -}; diff --git a/user/base/desktop/windows/imgview/scanner.c b/user/base/desktop/windows/imgview/scanner.c deleted file mode 100644 index 2397e158..00000000 --- a/user/base/desktop/windows/imgview/scanner.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "scanner.h" -#include - -format_scanner_t* scanners[] = { - &fpic_scanner, - &bmp_scanner, - &mbif_scanner, - NULL -}; - -format_scanner_t* get_scanner(void* buf, int size) { - for (int i = 0; scanners[i]; i++) { - if (scanners[i]->test_format(buf, size)) { - return scanners[i]; - } - } - return NULL; -} diff --git a/user/base/desktop/windows/imgview/scanner.h b/user/base/desktop/windows/imgview/scanner.h deleted file mode 100644 index 73b4ff2f..00000000 --- a/user/base/desktop/windows/imgview/scanner.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include -#include - -typedef struct { - bool (*test_format)(void* buf, int size); - uint32_t (*get_pixel)(void* buf, int size, int x, int y); - void (*get_size)(void* buf, int size, int* width, int* height); -} format_scanner_t; - -extern format_scanner_t fpic_scanner; -extern format_scanner_t bmp_scanner; -extern format_scanner_t mbif_scanner; - -format_scanner_t* get_scanner(void* buf, int size); diff --git a/user/base/desktop/windows/launcher/launcher.c b/user/base/desktop/windows/launcher/launcher.c deleted file mode 100644 index ccd7d952..00000000 --- a/user/base/desktop/windows/launcher/launcher.c +++ /dev/null @@ -1,68 +0,0 @@ -#include "launcher.h" -#include -#include -#include -#include -#include -#include - -#define LAUNCHER_BUTTON_HEIGHT 24 -#define LAUNCHER_BUTTON_SPACING 6 -#define LAUNCHER_PADDING 10 - -void on_launcher_button(window_instance_t* w, void* userdata) { - window_definition_t* def = (window_definition_t*)userdata; - def->register_window(); -} - -void launcher_init(window_instance_t* w) { - launcher_state_t* state = malloc(sizeof(launcher_state_t)); - state->button_count = (int)array_length(window_definitions); - state->buttons = malloc(sizeof(button_t) * state->button_count); - - for (int i = 0; i < state->button_count; i++) { - button_init(&state->buttons[i], LAUNCHER_PADDING, 30 + i * (LAUNCHER_BUTTON_HEIGHT + LAUNCHER_BUTTON_SPACING), w->width - (LAUNCHER_PADDING * 2), LAUNCHER_BUTTON_HEIGHT, window_definitions[i]->name, on_launcher_button, window_definitions[i]); - state->buttons[i].text_color = 0xaaffaa; - } - - w->state = state; - w->title_bar_color = 0xff6600; -} - -void launcher_update(window_instance_t* w, event_t* event) { - launcher_state_t* state = (launcher_state_t*)w->state; - - for (int i = 0; i < state->button_count; i++) { - button_handle_event(&state->buttons[i], w, event); - } -} - -void launcher_draw(window_instance_t* w) { - launcher_state_t* state = (launcher_state_t*)w->state; - - window_draw_string(w, LAUNCHER_PADDING, 5, "Available Windows:", 0xffffff); - - for (int i = 0; i < state->button_count; i++) { - button_draw(&state->buttons[i], w); - } -} - -void launcher_cleanup(window_instance_t* w) { - launcher_state_t* state = (launcher_state_t*)w->state; - if (state) { - if (state->buttons) { - free(state->buttons); - } - free(state); - w->state = NULL; - } -} - -void register_launcher_window(void) { - window_add(10, 10, 300, 400, "Launcher", 0x333333, launcher_init, launcher_update, launcher_draw, launcher_cleanup); -} - -window_definition_t launcher_definition = { - .name = "Launcher", - .register_window = register_launcher_window, -}; \ No newline at end of file diff --git a/user/base/desktop/windows/launcher/launcher.h b/user/base/desktop/windows/launcher/launcher.h deleted file mode 100644 index 6a775a87..00000000 --- a/user/base/desktop/windows/launcher/launcher.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include - -typedef struct { - button_t* buttons; - int button_count; -} launcher_state_t; - -void launcher_init(window_instance_t* w); -void launcher_update(window_instance_t* w, event_t* event); -void launcher_draw(window_instance_t* w); -void launcher_cleanup(window_instance_t* w); - -void register_launcher_window(); - -extern window_definition_t launcher_definition; - diff --git a/user/base/desktop/windows/netinfo/netinfo.c b/user/base/desktop/windows/netinfo/netinfo.c deleted file mode 100644 index d0e3fe37..00000000 --- a/user/base/desktop/windows/netinfo/netinfo.c +++ /dev/null @@ -1,133 +0,0 @@ -#include "netinfo.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern psf1_font_t font; - -static void netinfo_refresh(netinfo_state_t* state) { - char buffer[16] = { 0 }; - sprintf(buffer, "dev:nic%d", state->interface); - int fd = open(buffer, FILE_OPEN_MODE_READ); - if (fd < 0) { - state->nic_valid = 0; - return; - } - close(fd); - state->nic_data = nic_read(state->interface); - state->nic_valid = 1; -} - -void on_netinfo_up(window_instance_t* w, void* userdata) { - netinfo_state_t* state = (netinfo_state_t*)w->state; - state->interface--; - netinfo_refresh(state); -} - -void on_netinfo_down(window_instance_t* w, void* userdata) { - netinfo_state_t* state = (netinfo_state_t*)w->state; - state->interface++; - netinfo_refresh(state); -} - -void on_netinfo_dhcp(window_instance_t* w, void* userdata) { - netinfo_state_t* state = (netinfo_state_t*)w->state; - char interface_str[8] = { 0 }; - sprintf(interface_str, "%d", state->interface); - char* exec = search_executable("dhcp"); - if (exec != NULL) { - const char* dhcp_argv[] = { "dhcp", "-i", interface_str, NULL }; - spawn(exec, dhcp_argv, NULL); - free(exec); - } - netinfo_refresh(state); -} - -void netinfo_init(window_instance_t* w) { - netinfo_state_t* state = malloc(sizeof(netinfo_state_t)); - state->interface = 0; - netinfo_refresh(state); - - button_init(&state->up_btn, w->width - 28, 2, 24, 20, "^", on_netinfo_up, NULL); - button_init(&state->down_btn, w->width - 28, w->height - TITLE_BAR_HEIGHT - 24, 24, 20, "v", on_netinfo_down, NULL); - button_init(&state->dhcp_btn, w->width - 54, 26, 50, 20, "DHCP", on_netinfo_dhcp, NULL); - state->dhcp_btn.bg_color = 0x336644; - state->dhcp_btn.hover_color = 0x44aa66; - - w->state = state; - w->title_bar_color = 0x1a3a2e; - w->is_dirty = true; -} - -void netinfo_update(window_instance_t* w, event_t* event) { - netinfo_state_t* state = (netinfo_state_t*)w->state; - - w->is_dirty = true; - - button_handle_event(&state->up_btn, w, event); - button_handle_event(&state->down_btn, w, event); - button_handle_event(&state->dhcp_btn, w, event); -} - -void netinfo_draw(window_instance_t* w) { - netinfo_state_t* state = (netinfo_state_t*)w->state; - - button_draw(&state->up_btn, w); - button_draw(&state->down_btn, w); - button_draw(&state->dhcp_btn, w); - - char buffer[128] = { 0 }; - - if (!state->nic_valid) { - sprintf(buffer, "no nic %d", state->interface); - window_draw_string(w, 4, 4, buffer, 0xff4444); - return; - } - - nic_content_t* nic = &state->nic_data; - - int line = 0; - sprintf(buffer, "Interface: %d", state->interface); - window_draw_string(w, 4, line * 16 + 4, buffer, 0xaaffcc); line += 2; - - sprintf(buffer, "ip: %d.%d.%d.%d", nic->ip_config.ip.ip_p[0], nic->ip_config.ip.ip_p[1], nic->ip_config.ip.ip_p[2], nic->ip_config.ip.ip_p[3]); - window_draw_string(w, 4, line * 16 + 4, buffer, 0xffffff); line++; - - sprintf(buffer, "mask: %d.%d.%d.%d", nic->ip_config.subnet_mask.ip_p[0], nic->ip_config.subnet_mask.ip_p[1], nic->ip_config.subnet_mask.ip_p[2], nic->ip_config.subnet_mask.ip_p[3]); - window_draw_string(w, 4, line * 16 + 4, buffer, 0xffffff); line++; - - sprintf(buffer, "gw: %d.%d.%d.%d", nic->ip_config.gateway_ip.ip_p[0], nic->ip_config.gateway_ip.ip_p[1], nic->ip_config.gateway_ip.ip_p[2], nic->ip_config.gateway_ip.ip_p[3]); - window_draw_string(w, 4, line * 16 + 4, buffer, 0xffffff); line++; - - sprintf(buffer, "dns: %d.%d.%d.%d", nic->ip_config.dns_ip.ip_p[0], nic->ip_config.dns_ip.ip_p[1], nic->ip_config.dns_ip.ip_p[2], nic->ip_config.dns_ip.ip_p[3]); - window_draw_string(w, 4, line * 16 + 4, buffer, 0xffffff); line++; - - sprintf(buffer, "mac: %x:%x:%x:%x:%x:%x", nic->mac.mac_p[0], nic->mac.mac_p[1], nic->mac.mac_p[2], nic->mac.mac_p[3], nic->mac.mac_p[4], nic->mac.mac_p[5]); - window_draw_string(w, 4, line * 16 + 4, buffer, 0xffffff); line++; - - sprintf(buffer, "dev: %s", nic->name); - window_draw_string(w, 4, line * 16 + 4, buffer, 0xffffff); -} - -void netinfo_cleanup(window_instance_t* w) { - if (w->state) { - free(w->state); - w->state = NULL; - } -} - -window_definition_t netinfo_definition = { - .name = "Network Info", - .register_window = register_netinfo_window, -}; - -void register_netinfo_window(void) { - window_add(80, 80, 280, 250, "Network Info", 0x051015, netinfo_init, netinfo_update, netinfo_draw, netinfo_cleanup); -} diff --git a/user/base/desktop/windows/netinfo/netinfo.h b/user/base/desktop/windows/netinfo/netinfo.h deleted file mode 100644 index 43bcf487..00000000 --- a/user/base/desktop/windows/netinfo/netinfo.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include -#include -#include - -typedef struct { - int interface; - int nic_valid; - nic_content_t nic_data; - button_t up_btn; - button_t down_btn; - button_t dhcp_btn; -} netinfo_state_t; - -void netinfo_init(window_instance_t* w); -void netinfo_update(window_instance_t* w, event_t* event); -void netinfo_draw(window_instance_t* w); -void netinfo_cleanup(window_instance_t* w); -void register_netinfo_window(void); - -extern window_definition_t netinfo_definition; diff --git a/user/base/desktop/windows/service/service.c b/user/base/desktop/windows/service/service.c deleted file mode 100644 index b7bc6194..00000000 --- a/user/base/desktop/windows/service/service.c +++ /dev/null @@ -1,305 +0,0 @@ -#include "service.h" - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#define ROW_HEIGHT 22 -#define TABLE_START_Y 46 -#define BUTTON_W 56 -#define BUTTON_H 18 -#define POLL_INTERVAL_MS 80 - -#define OP_START 1 -#define OP_STOP 2 -#define OP_RESTART 3 - -static int service_safe_count(const service_window_state_t* state) { - int count = state->list.count; - if (count < 0) { - return 0; - } - if (count > MAX_SERVICES) { - return MAX_SERVICES; - } - return count; -} - -static void service_request_list(service_window_state_t* state, window_instance_t* w) { - int dummy = 0; - message_send(TOPIC_SERVICE_LIST, &dummy, sizeof(dummy)); - state->waiting_for_list = 1; - state->next_poll_ms = time_ms() + POLL_INTERVAL_MS; - strcpy(state->status_text, "Requesting service list..."); - w->is_realtime = true; -} - -static void service_request_op(service_window_state_t* state, window_instance_t* w, int service_idx, int op) { - int service_count = service_safe_count(state); - if (!state->has_list || service_idx < 0 || service_idx >= service_count) { - return; - } - - service_op_request_t req = { 0 }; - memset(&req, 0, sizeof(req)); - size_t slen = strnlen(state->list.services[service_idx].name, SERVICE_NAME_LEN - 1); - memcpy(req.name, state->list.services[service_idx].name, slen); - req.name[slen] = 0; - - uint32_t topic = TOPIC_SERVICE_START; - const char* op_name = "start"; - if (op == OP_STOP) { - topic = TOPIC_SERVICE_STOP; - op_name = "stop"; - } else if (op == OP_RESTART) { - topic = TOPIC_SERVICE_RESTART; - op_name = "restart"; - } - - message_send(topic, &req, sizeof(req)); - state->waiting_for_op = 1; - state->next_poll_ms = time_ms() + POLL_INTERVAL_MS; - - sprintf(state->status_text, "Requesting %s for '%s'...", op_name, req.name); - w->is_realtime = true; -} - -static void on_service_refresh(window_instance_t* w, void* userdata) { - service_window_state_t* state = (service_window_state_t*)w->state; - if (state->waiting_for_list || state->waiting_for_op) { - return; - } - service_request_list(state, w); -} - -static void on_service_start(window_instance_t* w, void* userdata) { - service_window_state_t* state = (service_window_state_t*)w->state; - if (state->waiting_for_list || state->waiting_for_op) { - return; - } - int idx = (int)(intptr_t)userdata; - service_request_op(state, w, idx, OP_START); -} - -static void on_service_stop(window_instance_t* w, void* userdata) { - service_window_state_t* state = (service_window_state_t*)w->state; - if (state->waiting_for_list || state->waiting_for_op) { - return; - } - int idx = (int)(intptr_t)userdata; - service_request_op(state, w, idx, OP_STOP); -} - -static void on_service_restart(window_instance_t* w, void* userdata) { - service_window_state_t* state = (service_window_state_t*)w->state; - if (state->waiting_for_list || state->waiting_for_op) { - return; - } - int idx = (int)(intptr_t)userdata; - service_request_op(state, w, idx, OP_RESTART); -} - -static void service_rebuild_buttons(service_window_state_t* state, window_instance_t* w) { - int visible_rows = (w->height - TITLE_BAR_HEIGHT - TABLE_START_Y - 8) / ROW_HEIGHT; - if (visible_rows < 0) { - visible_rows = 0; - } - - int count = service_safe_count(state); - if (count > visible_rows) { - count = visible_rows; - } - - int action_x = w->width - (BUTTON_W * 3 + 10); - for (int i = 0; i < count; i++) { - int row_y = TABLE_START_Y + i * ROW_HEIGHT; - - button_init(&state->start_buttons[i], action_x, row_y, BUTTON_W, BUTTON_H, "Start", on_service_start, (void*)(intptr_t)i); - state->start_buttons[i].bg_color = 0x244426; - state->start_buttons[i].hover_color = 0x2f6633; - - button_init(&state->stop_buttons[i], action_x + BUTTON_W + 3, row_y, BUTTON_W, BUTTON_H, "Stop", on_service_stop, (void*)(intptr_t)i); - state->stop_buttons[i].bg_color = 0x553322; - state->stop_buttons[i].hover_color = 0x885533; - - button_init(&state->restart_buttons[i], action_x + (BUTTON_W + 3) * 2, row_y, BUTTON_W, BUTTON_H, "Again", on_service_restart, (void*)(intptr_t)i); - state->restart_buttons[i].bg_color = 0x334466; - state->restart_buttons[i].hover_color = 0x4477aa; - } -} - -static const char* service_status_to_string(int status) { - switch (status) { - case SERVICE_STATUS_RUNNING: - return "running"; - case SERVICE_STATUS_STOPPED: - return "stopped"; - case SERVICE_STATUS_FAILED: - return "failed"; - default: - return "unknown"; - } -} - -static void service_poll_replies(service_window_state_t* state, window_instance_t* w) { - long now = time_ms(); - if (now < state->next_poll_ms) { - return; - } - state->next_poll_ms = now + POLL_INTERVAL_MS; - - if (state->waiting_for_list) { - service_list_reply_t reply = { 0 }; - memset(&reply, 0, sizeof(reply)); - if (message_recv(TOPIC_SERVICE_LIST_REPLY, &reply, sizeof(reply)) > 0) { - state->list = reply; - state->has_list = 1; - state->waiting_for_list = 0; - service_rebuild_buttons(state, w); - sprintf(state->status_text, "Loaded %d service(s)", service_safe_count(state)); - w->is_dirty = true; - } - } - - if (state->waiting_for_op) { - service_op_reply_t op_reply = { 0 }; - memset(&op_reply, 0, sizeof(op_reply)); - if (message_recv(TOPIC_SERVICE_OP_REPLY, &op_reply, sizeof(op_reply)) > 0) { - state->waiting_for_op = 0; - if (op_reply.success) { - sprintf(state->status_text, "OK: %s", op_reply.message); - } else { - sprintf(state->status_text, "Error: %s", op_reply.message); - } - w->is_dirty = true; - - service_request_list(state, w); - } - } - - if (!state->waiting_for_list && !state->waiting_for_op) { - w->is_realtime = false; - } -} - -void service_window_init(window_instance_t* w) { - service_window_state_t* state = malloc(sizeof(service_window_state_t)); - memset(state, 0, sizeof(service_window_state_t)); - - button_init(&state->refresh_btn, w->width - 72, 4, 68, 18, "Refresh", on_service_refresh, NULL); - state->refresh_btn.bg_color = 0x224455; - state->refresh_btn.hover_color = 0x3377aa; - - w->state = state; - w->title_bar_color = 0x20354a; - w->is_dirty = true; - - service_request_list(state, w); -} - -void service_window_update(window_instance_t* w, event_t* event) { - service_window_state_t* state = (service_window_state_t*)w->state; - - button_handle_event(&state->refresh_btn, w, event); - - if (state->has_list) { - service_rebuild_buttons(state, w); - - int visible_rows = (w->height - TITLE_BAR_HEIGHT - TABLE_START_Y - 8) / ROW_HEIGHT; - if (visible_rows < 0) { - visible_rows = 0; - } - - int count = service_safe_count(state); - if (count > visible_rows) { - count = visible_rows; - } - - for (int i = 0; i < count; i++) { - button_handle_event(&state->start_buttons[i], w, event); - button_handle_event(&state->stop_buttons[i], w, event); - button_handle_event(&state->restart_buttons[i], w, event); - } - } - - if (event->type == EVENT_NONE && (state->waiting_for_list || state->waiting_for_op)) { - service_poll_replies(state, w); - } -} - -void service_window_draw(window_instance_t* w) { - service_window_state_t* state = (service_window_state_t*)w->state; - - button_draw(&state->refresh_btn, w); - - window_draw_line(w, 0, 24, w->width, 24, 0x335577); - window_draw_string(w, 4, 28, "Service", 0x99ccff); - window_draw_string(w, 110, 28, "Status", 0x99ccff); - window_draw_string(w, 190, 28, "PID", 0x99ccff); - window_draw_string(w, 228, 28, "Retry", 0x99ccff); - - int visible_rows = (w->height - TITLE_BAR_HEIGHT - TABLE_START_Y - 8) / ROW_HEIGHT; - if (visible_rows < 0) { - visible_rows = 0; - } - - if (!state->has_list) { - window_draw_string(w, 4, TABLE_START_Y, "No data loaded", 0xffcc88); - } else { - int count = service_safe_count(state); - if (count > visible_rows) { - count = visible_rows; - } - - for (int i = 0; i < count; i++) { - service_info_t* svc = &state->list.services[i]; - int row_y = TABLE_START_Y + i * ROW_HEIGHT; - - window_draw_string(w, 4, row_y, svc->name, 0xffffff); - window_draw_string(w, 110, row_y, service_status_to_string(svc->status), 0xffffff); - - char num_buf[16] = { 0 }; - sprintf(num_buf, "%d", svc->pid); - window_draw_string(w, 190, row_y, num_buf, 0xffffff); - - sprintf(num_buf, "%d", svc->retry); - window_draw_string(w, 228, row_y, num_buf, 0xffffff); - - button_draw(&state->start_buttons[i], w); - button_draw(&state->stop_buttons[i], w); - button_draw(&state->restart_buttons[i], w); - } - - if (service_safe_count(state) > count) { - char more_buf[48] = { 0 }; - sprintf(more_buf, "%d more service(s) not shown", service_safe_count(state) - count); - window_draw_string(w, 4, w->height - TITLE_BAR_HEIGHT - 34, more_buf, 0xcccc88); - } - } - - window_draw_line(w, 0, w->height - TITLE_BAR_HEIGHT - 18, w->width, w->height - TITLE_BAR_HEIGHT - 18, 0x335577); - window_draw_string(w, 4, w->height - TITLE_BAR_HEIGHT - 16, state->status_text, 0xaaddff); -} - -void service_window_cleanup(window_instance_t* w) { - if (w->state) { - free(w->state); - w->state = NULL; - } -} - -window_definition_t service_window_definition = { - .name = "Services", - .register_window = register_service_window, -}; - -void register_service_window(void) { - window_add(90, 90, 500, 360, "Services", 0x0c1118, service_window_init, service_window_update, service_window_draw, service_window_cleanup); -} \ No newline at end of file diff --git a/user/base/desktop/windows/service/service.h b/user/base/desktop/windows/service/service.h deleted file mode 100644 index 255c5e9a..00000000 --- a/user/base/desktop/windows/service/service.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include -#include - -typedef struct { - service_list_reply_t list; - button_t refresh_btn; - button_t start_buttons[MAX_SERVICES]; - button_t stop_buttons[MAX_SERVICES]; - button_t restart_buttons[MAX_SERVICES]; - int has_list; - int waiting_for_list; - int waiting_for_op; - long next_poll_ms; - char status_text[96]; -} service_window_state_t; - -void service_window_init(window_instance_t* w); -void service_window_update(window_instance_t* w, event_t* event); -void service_window_draw(window_instance_t* w); -void service_window_cleanup(window_instance_t* w); -void register_service_window(void); - -extern window_definition_t service_window_definition; \ No newline at end of file diff --git a/user/base/desktop/windows/sysctl/sysctl.c b/user/base/desktop/windows/sysctl/sysctl.c deleted file mode 100644 index 4a400a8b..00000000 --- a/user/base/desktop/windows/sysctl/sysctl.c +++ /dev/null @@ -1,127 +0,0 @@ -#include "sysctl.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern psf1_font_t font; - -#define is_kb(x) ((x) >= 1024) -#define is_mb(x) ((x) >= 1024 * 1024) -#define is_gb(x) ((x) >= 1024 * 1024 * 1024) -#define to_kb(x) ((x) / 1024) -#define to_mb(x) ((x) / 1024 / 1024) -#define to_gb(x) ((x) / 1024 / 1024 / 1024) - -static void format_memory_usage(char* out_buf, uint32_t usage) { - if (is_gb(usage)) { - sprintf(out_buf, "%d,%d GB", to_gb(usage), to_mb(usage) % 1024); - } else if (is_mb(usage)) { - sprintf(out_buf, "%d,%d MB", to_mb(usage), to_kb(usage) % 1024); - } else if (is_kb(usage)) { - sprintf(out_buf, "%d,%d KB", to_kb(usage), usage % 1024); - } else { - sprintf(out_buf, "%d B", usage); - } -} - -void on_reboot(window_instance_t* w, void* userdata) { - env(SYS_PWR_RESET_ID); -} - -void on_shutdown(window_instance_t* w, void* userdata) { - env(SYS_PWR_SHUTDOWN_ID); -} - -void sysctl_init(window_instance_t* w) { - sysctl_state_t* state = malloc(sizeof(sysctl_state_t)); - - button_init(&state->reboot_btn, w->width - 80, 2, 76, 22, "Reboot", on_reboot, NULL); - state->reboot_btn.bg_color = 0x554400; - state->reboot_btn.hover_color = 0x887700; - - button_init(&state->shutdown_btn, w->width - 80, 28, 76, 22, "Shutdown", on_shutdown, NULL); - state->shutdown_btn.bg_color = 0x660000; - state->shutdown_btn.hover_color = 0xaa2222; - - w->state = state; - w->title_bar_color = 0x553366; - w->is_dirty = true; -} - -void sysctl_update(window_instance_t* w, event_t* event) { - sysctl_state_t* state = (sysctl_state_t*)w->state; - - w->is_dirty = true; - - button_handle_event(&state->reboot_btn, w, event); - button_handle_event(&state->shutdown_btn, w, event); -} - -void sysctl_draw(window_instance_t* w) { - sysctl_state_t* state = (sysctl_state_t*)w->state; - - button_draw(&state->reboot_btn, w); - button_draw(&state->shutdown_btn, w); - - char timebuf[128] = { 0 }; - unix_time_to_string(time(NULL), timebuf); - - char timemsbuf[32] = { 0 }; - sprintf(timemsbuf, "%d ms", time_ms()); - - uint32_t mem_free, mem_used; - raminfo(&mem_free, &mem_used); - - char total_str[32] = { 0 }; - char free_str[32] = { 0 }; - char used_str[32] = { 0 }; - format_memory_usage(total_str, mem_free + mem_used); - format_memory_usage(free_str, mem_free); - format_memory_usage(used_str, mem_used); - - int line = 0; - window_draw_string(w, 4, line * 16 + 4, "TIME:", 0xaaaaff); - line++; - window_draw_string(w, 4, line * 16 + 4, timebuf, 0xffffff); - line++; - line++; - window_draw_string(w, 4, line * 16 + 4, "TIMEMS:", 0xaaaaff); - line++; - window_draw_string(w, 4, line * 16 + 4, timemsbuf, 0xffffff); - line++; - line++; - window_draw_string(w, 4, line * 16 + 4, "TOTAL:", 0xaaaaff); - line++; - window_draw_string(w, 4, line * 16 + 4, total_str, 0xffffff); - line++; - window_draw_string(w, 4, line * 16 + 4, "FREE:", 0xaaaaff); - line++; - window_draw_string(w, 4, line * 16 + 4, free_str, 0xffffff); - line++; - window_draw_string(w, 4, line * 16 + 4, "USED:", 0xaaaaff); - line++; - window_draw_string(w, 4, line * 16 + 4, used_str, 0xffffff); -} - -void sysctl_cleanup(window_instance_t* w) { - if (w->state) { - free(w->state); - w->state = NULL; - } -} - -window_definition_t sysctl_definition = { - .name = "System Control", - .register_window = register_sysctl_window, -}; - -void register_sysctl_window(void) { - window_add(60, 60, 280, 260, "System Control", 0x1a0a2e, sysctl_init, sysctl_update, sysctl_draw, sysctl_cleanup); -} diff --git a/user/base/desktop/windows/sysctl/sysctl.h b/user/base/desktop/windows/sysctl/sysctl.h deleted file mode 100644 index 7d3b88ab..00000000 --- a/user/base/desktop/windows/sysctl/sysctl.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include - -typedef struct { - button_t reboot_btn; - button_t shutdown_btn; -} sysctl_state_t; - -void sysctl_init(window_instance_t* w); -void sysctl_update(window_instance_t* w, event_t* event); -void sysctl_draw(window_instance_t* w); -void sysctl_cleanup(window_instance_t* w); -void register_sysctl_window(void); - -extern window_definition_t sysctl_definition; diff --git a/user/base/desktop/windows/taskmgr/taskmgr.c b/user/base/desktop/windows/taskmgr/taskmgr.c deleted file mode 100644 index cc7d10c9..00000000 --- a/user/base/desktop/windows/taskmgr/taskmgr.c +++ /dev/null @@ -1,118 +0,0 @@ -#include "taskmgr.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern psf1_font_t font; - -#define is_kb(x) ((x) >= 1024) -#define is_mb(x) ((x) >= 1024 * 1024) -#define is_gb(x) ((x) >= 1024 * 1024 * 1024) -#define to_kb(x) ((x) / 1024) -#define to_mb(x) ((x) / 1024 / 1024) -#define to_gb(x) ((x) / 1024 / 1024 / 1024) - -static int format_memory_usage(char* out_buf, uint32_t usage) { - if (is_gb(usage)) { - return sprintf(out_buf, "%d,%d GB", to_gb(usage), to_mb(usage) % 1024); - } else if (is_mb(usage)) { - return sprintf(out_buf, "%d,%d MB", to_mb(usage), to_kb(usage) % 1024); - } else if (is_kb(usage)) { - return sprintf(out_buf, "%d,%d KB", to_kb(usage), usage % 1024); - } else { - return sprintf(out_buf, "%d B", usage); - } -} - -static void on_kill_task(window_instance_t* w, void* userdata) { - int pid = (int)(long)userdata; - kill(pid); -} - -void taskmgr_init(window_instance_t* w) { - taskmgr_state_t* state = malloc(sizeof(taskmgr_state_t)); - memset(state, 0, sizeof(taskmgr_state_t)); - state->task_count = 0; - w->state = state; - w->title_bar_color = 0x226644; - w->is_dirty = true; -} - -void taskmgr_update(window_instance_t* w, event_t* event) { - taskmgr_state_t* state = (taskmgr_state_t*)w->state; - - state->task_count = get_task_list(state->tasks, TASKMGR_MAX_TASKS); - w->is_dirty = true; - - for (int i = 0; i < state->task_count; i++) { - int row_y = 20 + i * 16; - int btn_x = w->width - 7 * 8; - button_init(&state->kill_buttons[i], btn_x, row_y, 6 * 8, 16, "Kill", on_kill_task, (void*)(long)state->tasks[i].pid); - state->kill_buttons[i].bg_color = 0x662222; - state->kill_buttons[i].hover_color = 0xaa3333; - state->kill_buttons[i].text_color = 0xff8888; - button_handle_event(&state->kill_buttons[i], w, event); - } -} - -void taskmgr_draw(window_instance_t* w) { - taskmgr_state_t* state = (taskmgr_state_t*)w->state; - - window_draw_string(w, 4, 4, "PID Term Name", 0x88ffaa); - window_draw_string(w, w->width - 6 * 8, 4, "Kill", 0x88ffaa); - - window_draw_line(w, 0, 18, w->width, 18, 0x336644); - - // Task rows - for (int i = 0; i < state->task_count; i++) { - int row_y = 20 + i * 16; - - char pid_buf[16] = { 0 }; - sprintf(pid_buf, "%d", state->tasks[i].pid); - window_draw_string(w, 4, row_y + 1, pid_buf, 0xffffff); - char term_buf[16] = { 0 }; - sprintf(term_buf, "%d", state->tasks[i].term); - window_draw_string(w, 5 * 8, row_y + 1, term_buf, 0xffffff); - - window_draw_string(w, 10 * 8, row_y + 1, state->tasks[i].name, 0xffffff); - - button_draw(&state->kill_buttons[i], w); - } - - uint32_t mem_free; - uint32_t mem_used; - raminfo(&mem_free, &mem_used); - - char mem_buf[80] = { 0 }; - char* ptr = mem_buf; - ptr += sprintf(ptr, "Mem: "); - ptr += format_memory_usage(ptr, mem_used); - ptr += sprintf(ptr, " / "); - ptr += format_memory_usage(ptr, mem_free + mem_used); - - int mem_y = w->height - TITLE_BAR_HEIGHT - 16; - window_draw_line(w, 0, mem_y - 2, w->width, mem_y - 2, 0x336644); - window_draw_string(w, 4, mem_y, mem_buf, 0x88ffaa); -} - -void taskmgr_cleanup(window_instance_t* w) { - if (w->state) { - free(w->state); - w->state = NULL; - } -} - -window_definition_t taskmgr_definition = { - .name = "Task Manager", - .register_window = register_taskmgr_window, -}; - -void register_taskmgr_window(void) { - window_add(50, 50, 320, 300, "Task Manager", 0x111111, taskmgr_init, taskmgr_update, taskmgr_draw, taskmgr_cleanup); -} diff --git a/user/base/desktop/windows/taskmgr/taskmgr.h b/user/base/desktop/windows/taskmgr/taskmgr.h deleted file mode 100644 index 16efdbe4..00000000 --- a/user/base/desktop/windows/taskmgr/taskmgr.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include -#include - -#define TASKMGR_MAX_TASKS 32 - -typedef struct { - task_list_t tasks[TASKMGR_MAX_TASKS]; - button_t kill_buttons[TASKMGR_MAX_TASKS]; - int task_count; -} taskmgr_state_t; - -void taskmgr_init(window_instance_t* w); -void taskmgr_update(window_instance_t* w, event_t* event); -void taskmgr_draw(window_instance_t* w); -void taskmgr_cleanup(window_instance_t* w); -void register_taskmgr_window(void); - -extern window_definition_t taskmgr_definition; diff --git a/user/base/desktop/windows/terminal/terminal.c b/user/base/desktop/windows/terminal/terminal.c deleted file mode 100644 index 3ebb2297..00000000 --- a/user/base/desktop/windows/terminal/terminal.c +++ /dev/null @@ -1,290 +0,0 @@ -#include "terminal.h" -#include "output.h" -#include "commands.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -extern psf1_font_t font; - -#define CHAR_W 8 -#define CHAR_H 16 -#define BG_COLOR 0x1a1a2e - -#define TEXT_COLOR 0xCCCCCC -#define INPUT_COLOR 0xFFFFFF - - -void terminal_init(window_instance_t* w) { - terminal_state_t* st = malloc(sizeof(terminal_state_t)); - memset(st, 0, sizeof(terminal_state_t)); - - st->output_buf = malloc(TERM_OUTPUT_SIZE); - memset(st->output_buf, 0, TERM_OUTPUT_SIZE); - st->output_len = 0; - - st->input_len = 0; - memset(st->input_buf, 0, TERM_INPUT_SIZE + 1); - - st->history = NULL; - st->history_size = 0; - st->history_index = 0; - st->scroll_offset = 0; - - st->envp = (char**)env(SYS_GET_ENVP_ID); - - w->state = st; - w->bg_color = BG_COLOR; - w->title_bar_color = 0x16213e; - - term_print_prompt(st); -} - - -void history_add(terminal_state_t* st, const char* cmd) { - if (st->history_size == 0) { - st->history = malloc(sizeof(char*)); - } else if (st->history_size < TERM_HISTORY_MAX) { - st->history = realloc(st->history, sizeof(char*) * (st->history_size + 1)); - } else { - free(st->history[0]); - memmove(st->history, st->history + 1, sizeof(char*) * (TERM_HISTORY_MAX - 1)); - st->history_size--; - } - st->history[st->history_size] = strdup(cmd); - st->history_size++; - st->history_index = st->history_size; -} - -void history_recall(terminal_state_t* st) { - if (st->history_index < 0 || st->history_index >= st->history_size) { - return; - } - memset(st->input_buf, 0, TERM_INPUT_SIZE + 1); - strcpy(st->input_buf, st->history[st->history_index]); - st->input_len = strlen(st->input_buf); -} - - -void terminal_update(window_instance_t* w, event_t* event) { - terminal_state_t* st = (terminal_state_t*)w->state; - - if (event->type == EVENT_KEY_PRESS) { - char key = event->key; - - if (key == '\n') { - term_append(st, st->input_buf, st->input_len); - term_puts(st, "\n"); - - if (st->input_len > 0) { - char cmd_copy[TERM_INPUT_SIZE + 1]; - memcpy(cmd_copy, st->input_buf, st->input_len + 1); - - history_add(st, st->input_buf); - - bool keep_going = execute_command(st, cmd_copy); - if (!keep_going) { - int count = window_get_count(); - for (int i = 0; i < count; i++) { - if (window_get(i) == w) { - window_close(i); - return; - } - } - return; - } - } - - memset(st->input_buf, 0, TERM_INPUT_SIZE + 1); - st->input_len = 0; - st->scroll_offset = 0; - term_print_prompt(st); - - } else if (key == '\b') { - if (st->input_len > 0) { - st->input_len--; - st->input_buf[st->input_len] = '\0'; - } - } else if (key == 27) { - memset(st->input_buf, 0, TERM_INPUT_SIZE + 1); - st->input_len = 0; - } else if (key >= 0x20 && key <= 0x7E) { - if (st->input_len < TERM_INPUT_SIZE) { - st->input_buf[st->input_len++] = key; - st->input_buf[st->input_len] = '\0'; - } - } - w->is_dirty = true; - } else if (event->type == EVENT_ARROW_KEY) { - if (event->arrow == ARROW_UP) { - if (st->history_index > 0) { - st->history_index--; - history_recall(st); - } - w->is_dirty = true; - } else if (event->arrow == ARROW_DOWN) { - if (st->history_index < st->history_size - 1) { - st->history_index++; - history_recall(st); - } else if (st->history_index == st->history_size - 1) { - st->history_index = st->history_size; - memset(st->input_buf, 0, TERM_INPUT_SIZE + 1); - st->input_len = 0; - } - w->is_dirty = true; - } else if (event->arrow == ARROW_UP + 10) { - st->scroll_offset += 3; - w->is_dirty = true; - } else if (event->arrow == ARROW_DOWN + 10) { - if (st->scroll_offset > 0) st->scroll_offset -= 3; - w->is_dirty = true; - } - } -} - - #define EMIT_CHAR(ch, color_val) { \ - if (cur_line >= start_line && cur_line < start_line + rows) { \ - draw_x = (col_pos) * CHAR_W; \ - draw_y = (cur_line - start_line) * CHAR_H; \ - window_draw_char(w, draw_x, TITLE_BAR_HEIGHT + draw_y, ch, color_val, BG_COLOR); \ - } \ - } - -void terminal_draw(window_instance_t* w) { - terminal_state_t* st = (terminal_state_t*)w->state; - - int content_w = w->width; - int content_h = w->height - TITLE_BAR_HEIGHT; - int cols = content_w / CHAR_W; - int rows = content_h / CHAR_H; - - if (cols <= 0 || rows <= 0) { - return; - } - - int total_lines = 1; - int col = 0; - - for (int i = 0; i < st->output_len; i++) { - if (st->output_buf[i] == '\n') { - total_lines++; - col = 0; - } else { - col++; - if (col >= cols) { - total_lines++; - col = 0; - } - } - } - - for (int i = 0; i < st->input_len; i++) { - if (st->input_buf[i] == '\n') { - total_lines++; - col = 0; - } else { - col++; - if (col >= cols) { - total_lines++; - col = 0; - } - } - } - - int start_line = total_lines - rows + st->scroll_offset; - if (start_line < 0) { - start_line = 0; - } - - int draw_x = 0; - int draw_y = 0; - - - - int col_pos = 0; - int cur_line = 0; - - for (int i = 0; i < st->output_len; i++) { - char c = st->output_buf[i]; - if (c == '\n') { - while (col_pos < cols) { - EMIT_CHAR(' ', BG_COLOR); - col_pos++; - } - cur_line++; - col_pos = 0; - } else { - EMIT_CHAR(c, TEXT_COLOR); - col_pos++; - if (col_pos >= cols) { - cur_line++; - col_pos = 0; - } - } - } - - for (int i = 0; i < st->input_len; i++) { - char c = st->input_buf[i]; - if (c == '\n') { - while (col_pos < cols) { - EMIT_CHAR(' ', BG_COLOR); - col_pos++; - } - cur_line++; - col_pos = 0; - } else { - EMIT_CHAR(c, INPUT_COLOR); - col_pos++; - if (col_pos >= cols) { - cur_line++; - col_pos = 0; - } - } - } - - if (cur_line >= start_line && cur_line < start_line + rows) { - draw_x = col_pos * CHAR_W; - draw_y = (cur_line - start_line) * CHAR_H; - window_draw_rect(w, draw_x, draw_y, CHAR_W - 4, CHAR_H - 4, 0xCCCCCC); - } - - int next_row = cur_line - start_line + 1; - if (next_row < rows) { - window_draw_rect(w, 0, next_row * CHAR_H, content_w, (rows - next_row) * CHAR_H, BG_COLOR); - } -} - - -void terminal_cleanup(window_instance_t* w) { - terminal_state_t* st = (terminal_state_t*)w->state; - - if (st->output_buf) { - free(st->output_buf); - } - - if (st->history) { - for (int i = 0; i < st->history_size; i++) { - free(st->history[i]); - } - free(st->history); - } - free(st); - w->state = NULL; -} - - -window_definition_t terminal_definition = { - .name = "Terminal", - .register_window = register_terminal_window, -}; - -void register_terminal_window(void) { - window_add(60, 60, 640, 400, "Terminal", BG_COLOR, terminal_init, terminal_update, terminal_draw, terminal_cleanup); -} diff --git a/user/base/desktop/windows/terminal/terminal.h b/user/base/desktop/windows/terminal/terminal.h deleted file mode 100644 index e03d1bb2..00000000 --- a/user/base/desktop/windows/terminal/terminal.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include -#include - -#define TERM_OUTPUT_SIZE (64 * 1024) -#define TERM_INPUT_SIZE 2048 -#define TERM_HISTORY_MAX 64 - -typedef struct { - char* output_buf; - int output_len; - - char input_buf[TERM_INPUT_SIZE + 1]; - int input_len; - - char** history; - int history_size; - int history_index; - - int scroll_offset; - - char** envp; -} terminal_state_t; - -void terminal_init(window_instance_t* w); -void terminal_update(window_instance_t* w, event_t* event); -void terminal_draw(window_instance_t* w); -void terminal_cleanup(window_instance_t* w); -void register_terminal_window(void); - -extern window_definition_t terminal_definition; diff --git a/user/base/init/main.c b/user/base/init/main.c index 096502ff..23b2626b 100644 --- a/user/base/init/main.c +++ b/user/base/init/main.c @@ -57,8 +57,14 @@ void envp_append(char* key, char* val) { int main(int argc, char* argv[]) { bool copy = false; - if (argc == 2 && strcmp(argv[1], "tmpfs") == 0) { - copy = true; + char* init_process = INIT_PROCESS; + + if (argc == 2) { + if (strcmp(argv[1], "tmpfs") == 0) { + copy = true; + } else { + init_process = argv[1]; + } } char cwd[64] = { 0 }; @@ -133,7 +139,7 @@ int main(int argc, char* argv[]) { char process[128] = { 0 }; strcat(process, cwd); - strcat(process, INIT_PROCESS); + strcat(process, init_process); #ifdef INIT_DEBUG printf("got process %s\n", process); #endif diff --git a/user/desktop/counter/Makefile b/user/desktop/counter/Makefile new file mode 100644 index 00000000..14d479c8 --- /dev/null +++ b/user/desktop/counter/Makefile @@ -0,0 +1,7 @@ +PROGRAM = desktop-counter.elf + +include ../../program.mk + +CFLAGS += -I../libdesktop/include + +LINK = -ldesktop diff --git a/user/desktop/counter/main.c b/user/desktop/counter/main.c new file mode 100644 index 00000000..ac0786bc --- /dev/null +++ b/user/desktop/counter/main.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include +#include + +#define BG_COLOR 0x334455 + +int main(int argc, char** argv) { + wm_client_t client; + wm_client_init(&client); + + wm_client_set_title(&client, "Counter"); + wm_client_set_title_bar_color(&client, 0x4444ff); + wm_client_set_bg_color(&client, BG_COLOR); + + int count = 0; + + ui_button_t minus_btn, plus_btn, reset_btn; + ui_button_init(&minus_btn, 10, 40, 100, 28, "- Minus"); + ui_button_init(&plus_btn, 120, 40, 100, 28, "+ Plus"); + ui_button_init(&reset_btn, 10, 76, 210, 28, "Reset"); + + while (!wm_client_should_close(&client)) { + wm_event_t evt; + int dirty = 0; + + while (wm_client_poll_event(&client, &evt)) { + dirty |= ui_button_update(&minus_btn, &evt); + dirty |= ui_button_update(&plus_btn, &evt); + dirty |= ui_button_update(&reset_btn, &evt); + } + + if (ui_button_clicked(&minus_btn)) { + count--; dirty = 1; + } + if (ui_button_clicked(&plus_btn)) { + count++; dirty = 1; + } + if (ui_button_clicked(&reset_btn)) { + count = 0; dirty = 1; + } + + if (dirty) { + int w = wm_client_width(&client); + int h = wm_client_height(&client); + wm_client_fill_rect(&client, 0, 0, w, h, BG_COLOR); + + char buf[32]; + sprintf(buf, "Count: %d", count); + wm_client_draw_string(&client, 10, 10, buf, 0xffffff, BG_COLOR); + + ui_button_draw(&minus_btn, &client); + ui_button_draw(&plus_btn, &client); + ui_button_draw(&reset_btn, &client); + + wm_client_flush(&client); + } + + yield(); + } + + return 0; +} diff --git a/user/desktop/desktop/Makefile b/user/desktop/desktop/Makefile new file mode 100644 index 00000000..45ed36b6 --- /dev/null +++ b/user/desktop/desktop/Makefile @@ -0,0 +1,5 @@ +PROGRAM = desktop.elf + +include ../../program.mk + +CFLAGS += -I../libdesktop/include diff --git a/user/base/desktop/desktop.c b/user/desktop/desktop/desktop.c similarity index 94% rename from user/base/desktop/desktop.c rename to user/desktop/desktop/desktop.c index f6409b1c..13840faf 100644 --- a/user/base/desktop/desktop.c +++ b/user/desktop/desktop/desktop.c @@ -2,33 +2,42 @@ #include #include #include +#include #include #include #include +#include #include +#include #include +#include +#include +#include #include #include -#include "windows/edit/edit.h" #define MAX_ASSOCS 16 typedef struct { char ext[16]; - void (*open_fn)(const char* path); + window_definition_t* def; + void (*open_fn)(window_definition_t* def, const char* path); } file_assoc_t; file_assoc_t s_assocs[MAX_ASSOCS]; int s_assoc_count = 0; -void desktop_register_file_assoc(const char* ext, void (*open_fn)(const char* path)) { +void edit_open_ext(const char* path); + +void desktop_register_file_assoc(const char* ext, window_definition_t* def, void (*open_fn)(window_definition_t* def, const char* path)) { if (s_assoc_count >= MAX_ASSOCS) { return; } int ext_len = strnlen(ext, sizeof(s_assocs[0].ext) - 1); memcpy(s_assocs[s_assoc_count].ext, ext, ext_len); s_assocs[s_assoc_count].ext[ext_len] = '\0'; + s_assocs[s_assoc_count].def = def; s_assocs[s_assoc_count].open_fn = open_fn; s_assoc_count++; } @@ -46,12 +55,10 @@ void desktop_open_file(const char* path) { const char* ext = dot + 1; for (int i = 0; i < s_assoc_count; i++) { if (strcmp(s_assocs[i].ext, ext) == 0) { - s_assocs[i].open_fn(path); + s_assocs[i].open_fn(s_assocs[i].def, path); return; } } - - edit_open(path); } void desktop_draw_background(void) { @@ -146,7 +153,7 @@ bool desktop_start_menu_handle_click(int x, int y) { click_area_t* item = &s_menu_items[i]; if (x >= item->x && x < item->x + item->width && y >= item->y && y < item->y + item->height) { window_definition_t* def = window_definitions[i]; - def->register_window(); + def->register_window(def); s_start_menu_open = false; return true; } @@ -358,3 +365,10 @@ void desktop_draw_all(void) { desktop_draw_start_menu(); } } + +void desktop_poll_messages(void) { + char path[256] = { 0 }; + if (message_recv(TOPIC_DESKTOP_OPEN_FILE, path, sizeof(path)) > 0) { + desktop_open_file(path); + } +} diff --git a/user/desktop/desktop/external_window.c b/user/desktop/desktop/external_window.c new file mode 100644 index 00000000..cc4b388e --- /dev/null +++ b/user/desktop/desktop/external_window.c @@ -0,0 +1,293 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +bool slot_used[WM_MAX_SLOTS] = { false }; + +int external_window_alloc_slot(void) { + for (int i = 0; i < WM_MAX_SLOTS; i++) { + if (!slot_used[i]) { + slot_used[i] = true; + return i; + } + } + return -1; +} + +void external_window_free_slot(int slot) { + if (slot >= 0 && slot < WM_MAX_SLOTS) { + slot_used[slot] = false; + } +} + +void map_shm_pages(int slot, int pid, int content_w, int content_h) { + uintptr_t base = (uintptr_t)WM_SHM_ADDR(slot); + + mmmap((void*)base, (void*)base, pid); + + int pixel_bytes = content_w * content_h * 4; + int pixel_pages = (pixel_bytes + WM_PAGE_SIZE - 1) / WM_PAGE_SIZE; + + for (int i = 0; i < pixel_pages; i++) { + uintptr_t addr = base + WM_PIXELS_OFFSET + i * WM_PAGE_SIZE; + mmmap((void*)addr, (void*)addr, pid); + } +} + +void setup_shm_control(external_state_t* est, window_instance_t* w, int slot, int content_w, int content_h) { + wm_shared_t* ctl = WM_SHM_CONTROL(slot); + memset(ctl, 0, sizeof(wm_shared_t)); + ctl->width = content_w; + ctl->height = content_h; + ctl->event_write = 0; + ctl->event_read = 0; + ctl->dirty = 0; + ctl->alive = 0; + ctl->close_requested = 0; + memcpy(ctl->title, w->title, 64); + ctl->bg_color = w->bg_color; + ctl->title_bar_color = w->title_bar_color; + + est->control = ctl; + est->pixels = WM_SHM_PIXELS(slot); +} + +void wait_for_client(wm_shared_t* ctl) { + ctl->state = WM_STATE_READY; + + int timeout = 500; + while (ctl->state != WM_STATE_ACK && timeout > 0) { + yield(); + timeout--; + } + + if (ctl->state == WM_STATE_ACK) { + ctl->state = WM_STATE_CONNECTED; + } +} + +void external_window_init(window_instance_t* w) { + external_state_t* est = (external_state_t*)w->state; + + int slot = est->slot; + int content_w = w->width; + int content_h = w->height - TITLE_BAR_HEIGHT; + + if (content_w > WM_MAX_PIXEL_WIDTH) { + content_w = WM_MAX_PIXEL_WIDTH; + } + if (content_h > WM_MAX_PIXEL_HEIGHT) { + content_h = WM_MAX_PIXEL_HEIGHT; + } + if (content_w < 1) { + content_w = 1; + } + if (content_h < 1) { + content_h = 1; + } + + char* exec = search_executable((char*) est->executable); + if (!exec) { + printf("desktop: failed to resolve %s\n", est->executable); + return; + } + + const char* argv[] = { est->executable, est->launch_file, NULL }; + + char slot_str[16]; + sprintf(slot_str, "WMS=%d", slot); + + int envc = 0; + for (char** e = global_envp; *e; e++) { + envc++; + } + const char* envp[envc + 2]; + for (int i = 0; i < envc; i++) { + envp[i] = global_envp[i]; + } + envp[envc] = slot_str; + envp[envc + 1] = NULL; + + int pid = spawn(exec, argv, envp); + if (pid < 0) { + free(exec); + printf("desktop: failed to spawn %s\n", est->executable); + return; + } + + est->pid = pid; + free(exec); + w->child_pid = pid; + + map_shm_pages(slot, pid, content_w, content_h); + setup_shm_control(est, w, slot, content_w, content_h); + wait_for_client(est->control); + + w->is_external = true; + w->shm_slot = slot; + w->shm_base = (void*)WM_SHM_ADDR(slot); +} + +void external_window_init_spawned(window_instance_t* w) { + external_state_t* est = (external_state_t*)w->state; + + int slot = est->slot; + int pid = est->pid; + int content_w = w->width; + int content_h = w->height - TITLE_BAR_HEIGHT; + + if (content_w > WM_MAX_PIXEL_WIDTH) { + content_w = WM_MAX_PIXEL_WIDTH; + } + if (content_h > WM_MAX_PIXEL_HEIGHT) { + content_h = WM_MAX_PIXEL_HEIGHT; + } + if (content_w < 1) { + content_w = 1; + } + if (content_h < 1) { + content_h = 1; + } + + w->child_pid = pid; + + map_shm_pages(slot, pid, content_w, content_h); + setup_shm_control(est, w, slot, content_w, content_h); + wait_for_client(est->control); + + w->is_external = true; + w->shm_slot = slot; + w->shm_base = (void*)WM_SHM_ADDR(slot); +} + +void external_window_send_event(window_instance_t* w, event_t* event) { + external_state_t* est = (external_state_t*)w->state; + if (!est || !est->control) { + return; + } + + if (event->type == EVENT_NONE) { // event is only to tick the local update + return; + } + + wm_shared_t* ctl = est->control; + + if (ctl->event_write - ctl->event_read >= WM_MAX_EVENTS) { + return; + } + + int idx = ctl->event_write % WM_MAX_EVENTS; + ctl->events[idx].type = event->type; + ctl->events[idx].x = event->x; + ctl->events[idx].y = event->y - TITLE_BAR_HEIGHT; + ctl->events[idx].button = event->button; + ctl->events[idx].key = event->key; + ctl->events[idx].arrow = event->arrow; + ctl->event_write++; +} + +void external_window_update(window_instance_t* w, event_t* event) { + external_state_t* est = (external_state_t*)w->state; + if (!est || !est->control) { + return; + } + + wm_shared_t* ctl = est->control; + + if (!get_proc_info(est->pid)) { + w->is_dirty = true; + return; + } + + if (ctl->title[0] != '\0') { + memcpy(w->title, ctl->title, 64); + } + w->bg_color = ctl->bg_color; + w->title_bar_color = ctl->title_bar_color; + external_window_send_event(w, event); + + if (ctl->dirty) { + w->is_dirty = true; + ctl->dirty = 0; + } +} + +void external_window_draw(window_instance_t* w) { + external_state_t* est = (external_state_t*)w->state; + if (!est || !est->pixels) { + return; + } + + wm_shared_t* ctl = est->control; + int content_w = ctl->width; + int content_h = ctl->height; + int win_x = w->x; + int win_y = w->y + TITLE_BAR_HEIGHT; + + for (int j = 0; j < content_h; j++) { + for (int i = 0; i < content_w; i++) { + uint32_t color = est->pixels[j * content_w + i]; + uint32_t fb_x = win_x + i; + uint32_t fb_y = win_y + j; + if (fb_x < framebuffer.fb_width && fb_y < framebuffer.fb_height) { + *(uint32_t*)(framebuffer.fb_addr + (fb_x * 4) + (fb_y * framebuffer.fb_pitch)) = color; + } + } + } +} + +void external_window_cleanup(window_instance_t* w) { + external_state_t* est = (external_state_t*)w->state; + if (!est) { + return; + } + + if (est->control) { + est->control->close_requested = 1; + } + + for (int i = 0; i < 50; i++) { + if (!get_proc_info(est->pid)) { + break; + } + yield(); + } + + if (get_proc_info(est->pid)) { + kill(est->pid); + } + + external_window_free_slot(est->slot); + free(est); + w->state = NULL; + w->is_external = false; +} + +void window_add_external(const char* executable, const char* launch_file, int x, int y, int width, int height, const char* title, uint32_t bg_color) { + int slot = external_window_alloc_slot(); + if (slot < 0) { + printf("desktop: no free SHM slots\n"); + return; + } + + external_state_t* est = malloc(sizeof(external_state_t)); + memset(est, 0, sizeof(external_state_t)); + est->executable = executable; + est->launch_file = launch_file; + est->slot = slot; + + window_add_with_state(x, y, width, height, title, bg_color, + est, + external_window_init, + external_window_update, + external_window_draw, + external_window_cleanup); +} diff --git a/user/base/desktop/font.c b/user/desktop/desktop/font.c similarity index 100% rename from user/base/desktop/font.c rename to user/desktop/desktop/font.c diff --git a/user/base/desktop/framebuffer.c b/user/desktop/desktop/framebuffer.c similarity index 100% rename from user/base/desktop/framebuffer.c rename to user/desktop/desktop/framebuffer.c diff --git a/user/base/desktop/graphics.c b/user/desktop/desktop/graphics.c similarity index 100% rename from user/base/desktop/graphics.c rename to user/desktop/desktop/graphics.c diff --git a/user/base/desktop/include/desktop.h b/user/desktop/desktop/include/desktop.h similarity index 67% rename from user/base/desktop/include/desktop.h rename to user/desktop/desktop/include/desktop.h index 925168ed..ca179a3a 100644 --- a/user/base/desktop/include/desktop.h +++ b/user/desktop/desktop/include/desktop.h @@ -5,7 +5,7 @@ void desktop_draw_background(void); void desktop_draw_all(void); -void desktop_register_file_assoc(const char* ext, void (*open_fn)(const char* path)); +void desktop_register_file_assoc(const char* ext, window_definition_t* def, void (*open_fn)(window_definition_t* def, const char* path)); void desktop_open_file(const char* path); void desktop_start_menu_init(void); @@ -13,3 +13,4 @@ bool desktop_is_start_menu_open(void); void desktop_toggle_start_menu(void); bool desktop_start_menu_handle_click(int x, int y); bool desktop_taskbar_handle_click(int x, int y); +void desktop_poll_messages(void); diff --git a/user/desktop/desktop/include/external_window.h b/user/desktop/desktop/include/external_window.h new file mode 100644 index 00000000..795e18d9 --- /dev/null +++ b/user/desktop/desktop/include/external_window.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include + +// State struct stored in window_instance_t.state for external windows +typedef struct { + const char* executable; + int slot; + int pid; + wm_shared_t* control; + uint32_t* pixels; + const char* launch_file; // for file associations +} external_state_t; + +// Open an external window by spawning a child process. +// executable: path to the .mex file +// x, y, width, height: initial window geometry +// title: initial window title +// bg_color: background color +void window_add_external(const char* executable, const char* launch_file, int x, int y, int width, int height, + const char* title, uint32_t bg_color); + +// Send an event to an external window's shared memory event queue. +void external_window_send_event(window_instance_t* w, event_t* event); + +// Draw callback for external windows: copies pixels from SHM into the framebuffer. +void external_window_draw(window_instance_t* w); + +// Update callback for external windows: checks client state, sends events. +void external_window_update(window_instance_t* w, event_t* event); + +// Cleanup callback for external windows: kills child, unmaps SHM. +void external_window_cleanup(window_instance_t* w); + +// Init callback for external windows (called after window_add sets up the struct). +void external_window_init(window_instance_t* w); + +// Init callback for external windows where the child is already spawned. +// Used when opening files via file associations. +void external_window_init_spawned(window_instance_t* w); + +// Allocate the next free SHM slot. Returns -1 if none available. +int external_window_alloc_slot(void); + +// Free a previously allocated SHM slot. +void external_window_free_slot(int slot); diff --git a/user/base/desktop/include/font.h b/user/desktop/desktop/include/font.h similarity index 100% rename from user/base/desktop/include/font.h rename to user/desktop/desktop/include/font.h diff --git a/user/base/desktop/include/framebuffer.h b/user/desktop/desktop/include/framebuffer.h similarity index 100% rename from user/base/desktop/include/framebuffer.h rename to user/desktop/desktop/include/framebuffer.h diff --git a/user/base/desktop/include/graphics.h b/user/desktop/desktop/include/graphics.h similarity index 100% rename from user/base/desktop/include/graphics.h rename to user/desktop/desktop/include/graphics.h diff --git a/user/base/desktop/include/types.h b/user/desktop/desktop/include/types.h similarity index 83% rename from user/base/desktop/include/types.h rename to user/desktop/desktop/include/types.h index 0c452bb3..bdb02a64 100644 --- a/user/base/desktop/include/types.h +++ b/user/desktop/desktop/include/types.h @@ -79,9 +79,17 @@ typedef enum { CURSOR_RESIZE_NWSE, } cursor_type_t; -typedef struct { +typedef struct window_definition { const char* name; - void (*register_window)(void); + void (*register_window)(struct window_definition* def); + const char* executable; + + // for external windows: + int x; + int y; + int width; + int height; + uint32_t bg_color; } window_definition_t; typedef struct window_instance { @@ -104,7 +112,6 @@ typedef struct window_instance { click_area_t minimize_button; bool is_focused; bool is_dirty; - bool is_realtime; bool is_minimized; drag_type_t drag_state; @@ -114,4 +121,13 @@ typedef struct window_instance { int drag_start_win_y; int drag_start_win_width; int drag_start_win_height; + + // External window fields (for windows running as separate processes) + bool is_external; + int child_pid; + int shm_slot; + void* shm_base; } window_instance_t; + + +extern char** global_envp; \ No newline at end of file diff --git a/user/desktop/desktop/include/window.h b/user/desktop/desktop/include/window.h new file mode 100644 index 00000000..da4202a8 --- /dev/null +++ b/user/desktop/desktop/include/window.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +void window_add_with_state(int x, int y, int width, int height, const char* title, uint32_t bg_color, + void* state, + void (*init)(window_instance_t*), + void (*update)(window_instance_t*, event_t*), + void (*draw)(window_instance_t*), + void (*cleanup)(window_instance_t*)); + +void window_close(int idx); +int window_at_point(int x, int y); +drag_type_t detect_drag_type(window_instance_t* w, int x, int y); +void handle_window_drag(window_instance_t* w, int mouse_x, int mouse_y); + +int window_get_count(void); +int window_get_focused(void); +void window_set_focused(int idx); +window_instance_t* window_get(int idx); diff --git a/user/base/desktop/include/windows.h b/user/desktop/desktop/include/windows.h similarity index 100% rename from user/base/desktop/include/windows.h rename to user/desktop/desktop/include/windows.h diff --git a/user/base/desktop/main.c b/user/desktop/desktop/main.c similarity index 93% rename from user/base/desktop/main.c rename to user/desktop/desktop/main.c index 87f60ff8..468c2394 100644 --- a/user/base/desktop/main.c +++ b/user/desktop/desktop/main.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -11,23 +12,24 @@ #include #include -#include "windows/launcher/launcher.h" - psf1_font_t font; +char** global_envp; + static inline bool check_click_area(click_area_t* area, int x, int y) { return x >= area->x && x < area->x + area->width && y >= area->y && y < area->y + area->height; } -int main() { +int main(int argc, char** argv, char** envp) { + global_envp = envp; + fb_init(); font = load_psf1_font("dev:font"); register_windows(); - register_launcher_window(); desktop_start_menu_init(); desktop_draw_all(); @@ -40,12 +42,15 @@ int main() { bool last_button_right = false; bool last_button_middle = false; int dragging_window = -1; + unsigned long last_clock_time = 0; + + mouse_info_t prev_info = {0}; while (true) { mouse_info_t info; mouse_info(&info); - bool should_redraw = true; + bool should_redraw = false; if ((info.button_left && !last_button_left) || (info.button_right && !last_button_right) || @@ -118,6 +123,11 @@ int main() { } } + if (prev_info.x != info.x || prev_info.y != info.y ) { + should_redraw = true; + } + prev_info = info; + if (info.button_left && dragging_window >= 0) { window_instance_t* w = window_get(dragging_window); if (w) { @@ -198,7 +208,7 @@ int main() { for (int i = 0; i < window_get_count(); i++) { window_instance_t* w = window_get(i); - if (w && w->is_realtime && w->update && !w->is_minimized) { + if (w && w->is_external && w->update && !w->is_minimized) { event_t none_evt; none_evt.type = EVENT_NONE; none_evt.key = 0; @@ -212,6 +222,14 @@ int main() { } } } + + desktop_poll_messages(); + + unsigned long now_clock = (unsigned long)time(NULL); + if (now_clock != last_clock_time) { + last_clock_time = now_clock; + should_redraw = true; + } if (should_redraw) { cursor_type_t cursor = CURSOR_DEFAULT; diff --git a/user/base/desktop/resources/back.fpic b/user/desktop/desktop/resources/back.fpic similarity index 100% rename from user/base/desktop/resources/back.fpic rename to user/desktop/desktop/resources/back.fpic diff --git a/user/base/desktop/resources/dhcp.fpic b/user/desktop/desktop/resources/dhcp.fpic similarity index 100% rename from user/base/desktop/resources/dhcp.fpic rename to user/desktop/desktop/resources/dhcp.fpic diff --git a/user/base/desktop/resources/down_arrow.fpic b/user/desktop/desktop/resources/down_arrow.fpic similarity index 100% rename from user/base/desktop/resources/down_arrow.fpic rename to user/desktop/desktop/resources/down_arrow.fpic diff --git a/user/base/desktop/resources/exit.fpic b/user/desktop/desktop/resources/exit.fpic similarity index 100% rename from user/base/desktop/resources/exit.fpic rename to user/desktop/desktop/resources/exit.fpic diff --git a/user/base/desktop/resources/reboot.fpic b/user/desktop/desktop/resources/reboot.fpic similarity index 100% rename from user/base/desktop/resources/reboot.fpic rename to user/desktop/desktop/resources/reboot.fpic diff --git a/user/base/desktop/resources/resources.S b/user/desktop/desktop/resources/resources.S similarity index 100% rename from user/base/desktop/resources/resources.S rename to user/desktop/desktop/resources/resources.S diff --git a/user/base/desktop/resources/shutdown.fpic b/user/desktop/desktop/resources/shutdown.fpic similarity index 100% rename from user/base/desktop/resources/shutdown.fpic rename to user/desktop/desktop/resources/shutdown.fpic diff --git a/user/base/desktop/resources/up_arrow.fpic b/user/desktop/desktop/resources/up_arrow.fpic similarity index 100% rename from user/base/desktop/resources/up_arrow.fpic rename to user/desktop/desktop/resources/up_arrow.fpic diff --git a/user/base/desktop/window.c b/user/desktop/desktop/window.c similarity index 93% rename from user/base/desktop/window.c rename to user/desktop/desktop/window.c index 92d31ce6..065dee44 100644 --- a/user/base/desktop/window.c +++ b/user/desktop/desktop/window.c @@ -7,11 +7,12 @@ window_instance_t windows[MAX_WINDOWS]; int window_count = 0; int focused_window = -1; -void window_add(int x, int y, int width, int height, const char* title, uint32_t bg_color, - void (*init)(window_instance_t*), - void (*update)(window_instance_t*, event_t*), - void (*draw)(window_instance_t*), - void (*cleanup)(window_instance_t*)) { +void window_add_with_state(int x, int y, int width, int height, const char* title, uint32_t bg_color, + void* state, + void (*init)(window_instance_t*), + void (*update)(window_instance_t*, event_t*), + void (*draw)(window_instance_t*), + void (*cleanup)(window_instance_t*)) { if (window_count >= MAX_WINDOWS) { return; } @@ -26,6 +27,7 @@ void window_add(int x, int y, int width, int height, const char* title, uint32_t strcpy(w->title, title); w->bg_color = bg_color; w->title_bar_color = 0x444477; + w->state = state; w->init = init; w->update = update; w->draw = draw; diff --git a/user/desktop/desktop/windows.c b/user/desktop/desktop/windows.c new file mode 100644 index 00000000..71923d03 --- /dev/null +++ b/user/desktop/desktop/windows.c @@ -0,0 +1,114 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +window_definition_t** window_definitions = NULL; + +void register_window(window_definition_t* def) { + window_definitions = array_push(window_definitions, &def); +} + +void launch_external(struct window_definition* def) { + window_add_external(def->executable, NULL, def->x, def->y, def->width, def->height, def->name, def->bg_color); +} + +void launch_external_ext(struct window_definition* def, const char* path) { + window_add_external(def->executable, path, def->x, def->y, def->width, def->height, def->name, def->bg_color); +} + +window_definition_t counter_def = { + .name = "Counter", + .register_window = launch_external, + .executable = "desktop-counter", + .x = 50, .y = 50, .width = 300, .height = 250, .bg_color = 0x334455 +}; + +window_definition_t explorer_def = { + .name = "Explorer", + .register_window = launch_external, + .executable = "desktop-explorer", + .x = 100, .y = 100, .width = 400, .height = 300, .bg_color = 0x1a1a2e +}; + +window_definition_t taskmgr_def = { + .name = "Task Manager", + .register_window = launch_external, + .executable = "desktop-taskmgr", + .x = 100, .y = 100, .width = 480, .height = 360, .bg_color = 0x1a1a2e +}; + +window_definition_t sysctl_def = { + .name = "System Control", + .register_window = launch_external, + .executable = "desktop-sysctl", + .x = 60, .y = 60, .width = 280, .height = 260, .bg_color = 0x1a0a2e +}; + +window_definition_t netinfo_def = { + .name = "Network Info", + .register_window = launch_external, + .executable = "desktop-netinfo", + .x = 60, .y = 60, .width = 400, .height = 320, .bg_color = 0x1a1a2e +}; + +window_definition_t edit_def = { + .name = "Editor", + .register_window = launch_external, + .executable = "desktop-edit", + .x = 80, .y = 40, .width = 640, .height = 480, .bg_color = 0x1e1e2e +}; + +window_definition_t imgview_def = { + .name = "Image Viewer", + .register_window = launch_external, + .executable = "desktop-imgview", + .x = 100, .y = 80, .width = 500, .height = 400, .bg_color = 0x000000 +}; + +window_definition_t terminal_def = { + .name = "Terminal", + .register_window = launch_external, + .executable = "desktop-terminal", + .x = 60, .y = 60, .width = 640, .height = 400, .bg_color = 0x1a1a2e +}; + +window_definition_t service_def = { + .name = "Services", + .register_window = launch_external, + .executable = "desktop-service", + .x = 60, .y = 60, .width = 400, .height = 360, .bg_color = 0x1a1a2e +}; + +void register_windows(void) { + window_definitions = array_create(sizeof(window_definition_t*), 12); + + // TODO: load from config file? + register_window(&counter_def); + register_window(&explorer_def); + register_window(&taskmgr_def); + register_window(&sysctl_def); + register_window(&netinfo_def); + register_window(&edit_def); + register_window(&imgview_def); + register_window(&terminal_def); + register_window(&service_def); + + desktop_register_file_assoc("fpic", &imgview_def, launch_external_ext); + desktop_register_file_assoc("bmp", &imgview_def, launch_external_ext); + desktop_register_file_assoc("mbif", &imgview_def, launch_external_ext); + + desktop_register_file_assoc("txt", &edit_def, launch_external_ext); + desktop_register_file_assoc("conf", &edit_def, launch_external_ext); + desktop_register_file_assoc("md", &edit_def, launch_external_ext); + desktop_register_file_assoc("c", &edit_def, launch_external_ext); + desktop_register_file_assoc("asm", &edit_def, launch_external_ext); + desktop_register_file_assoc("msh", &edit_def, launch_external_ext); +} + diff --git a/user/desktop/edit/Makefile b/user/desktop/edit/Makefile new file mode 100644 index 00000000..d3be0d5b --- /dev/null +++ b/user/desktop/edit/Makefile @@ -0,0 +1,6 @@ +PROGRAM = desktop-edit.elf + +include ../../program.mk + +CFLAGS += -I../libdesktop/include -I../../libraries/libsyntax/include +LINK = -ldesktop -lsyntax diff --git a/user/desktop/edit/include/edit.h b/user/desktop/edit/include/edit.h new file mode 100644 index 00000000..b7a93fe9 --- /dev/null +++ b/user/desktop/edit/include/edit.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +typedef struct edit_state { + char* file_name; + char* input_buffer; + bool is_edited; + bool is_in_insert_mode; + bool show_tab_char; + + unsigned int ln_cnt; + unsigned int char_cnt; + + unsigned int buffer_ln_idx; + unsigned int buffer_idx; + + unsigned int current_size; + + bool read_only; + + FILE* file; +} edit_state_t; diff --git a/user/desktop/edit/include/input.h b/user/desktop/edit/include/input.h new file mode 100644 index 00000000..956527d4 --- /dev/null +++ b/user/desktop/edit/include/input.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +bool handle_event(edit_state_t* state, wm_event_t* evt); diff --git a/user/desktop/edit/include/render.h b/user/desktop/edit/include/render.h new file mode 100644 index 00000000..e7cf8b16 --- /dev/null +++ b/user/desktop/edit/include/render.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include +#include + +void render_ui(wm_client_t* client, edit_state_t* state); + +void rerender_color(edit_state_t* state); + +extern syntax_header_t* syntax; diff --git a/user/desktop/edit/input.c b/user/desktop/edit/input.c new file mode 100644 index 00000000..8929d4c3 --- /dev/null +++ b/user/desktop/edit/input.c @@ -0,0 +1,202 @@ +#include + +#include +#include +#include + +#include + +void move_up(edit_state_t* state) { + if (state->buffer_ln_idx <= 0 || state->buffer_idx <= 0) { + } else { + // move one line up + int prev_buff = state->buffer_idx; + + for (int i = state->buffer_idx; i > 0; i--) { + state->buffer_idx--; + if (state->input_buffer[i - 1] == '\n' || state->buffer_idx < 0) { + break; + } + } + if (state->buffer_idx < 0) { + state->buffer_idx = prev_buff; + } else { + state->buffer_ln_idx--; + } + } +} + +void move_down(edit_state_t* state) { + if (state->buffer_ln_idx >= state->ln_cnt - 1 || state->buffer_idx >= state->current_size) { + } else { + // move one line up + int prev_buff = state->buffer_idx; + + for (int i = state->buffer_idx; i < state->current_size; i++) { + state->buffer_idx++; + if (state->input_buffer[i] == '\n' || state->buffer_idx > state->current_size) { + break; + } + } + if (state->buffer_idx > state->current_size) { + state->buffer_idx = prev_buff; + } else { + state->buffer_ln_idx++; + } + } +} + +void move_left(edit_state_t* state) { + if (!(state->buffer_idx <= 0)) { + if (state->input_buffer[state->buffer_idx - 1] == '\n') { + state->buffer_ln_idx--; + } + state->buffer_idx -= 1; + } +} + +void move_right(edit_state_t* state) { + if (state->buffer_idx < state->current_size) { + if (state->input_buffer[state->buffer_idx] == '\n') { + state->buffer_ln_idx++; + } + state->buffer_idx += 1; + } +} + + +bool handle_event(edit_state_t* state, wm_event_t* evt) { + if (evt->type == WM_EVENT_ARROW_KEY) { + switch (evt->arrow) { + case WM_ARROW_UP: + move_up(state); + break; + case WM_ARROW_DOWN: + move_down(state); + break; + case WM_ARROW_LEFT: + move_left(state); + break; + case WM_ARROW_RIGHT: + move_right(state); + break; + } + return false; + } + + if (evt->type != WM_EVENT_KEY_PRESS) { + return false; + } + + char input = evt->key; + + if (state->read_only) { + switch (input) { + case 'q': + return true; + case '\e': + return true; + case 'a': + move_left(state); + break; + case 'd': + move_right(state); + break; + case 'w': + move_up(state); + break; + case 's': + move_down(state); + break; + } + } else { + if (!state->is_in_insert_mode) { + switch (input) { + case 'q': + return true; + case '\e': + state->is_in_insert_mode = !state->is_in_insert_mode; + break; + + case 'a': + move_left(state); + break; + case 'd': + move_right(state); + break; + case 'w': + move_up(state); + break; + case 's': + move_down(state); + break; + + case 't': + state->show_tab_char = !state->show_tab_char; + break; + + case '+': + state->file = freopen(state->file_name, "w", state->file); + fseek(state->file, 0, SEEK_SET); + fwrite(state->input_buffer, state->current_size, 1, state->file); + fseek(state->file, state->current_size, SEEK_SET); + ftruncate(state->file); + state->is_edited = false; + break; + } + } else { + switch (input) { + case '\b': { + if (state->buffer_idx < 1 || state->current_size < 1) { + } else { + if (state->input_buffer[state->buffer_idx - 1] == '\n') { + state->buffer_ln_idx--; + } + + if (state->buffer_idx == state->current_size) { + } else { + memmove((void*) &state->input_buffer[state->buffer_idx - 1], (void*) &state->input_buffer[state->buffer_idx], (state->current_size - state->buffer_idx) * sizeof(char)); + } + if (state->input_buffer[state->buffer_idx] == '\n') { + state->ln_cnt--; + } else { + state->char_cnt--; + } + state->input_buffer = (char*) realloc((void*) state->input_buffer, --state->current_size); + state->buffer_idx--; + + rerender_color(state); + } + } + break; + + case '\e': { + state->is_in_insert_mode = !state->is_in_insert_mode; + } + break; + + + default: { + if (input == '\n') { + state->ln_cnt++; + state->buffer_ln_idx++; + } else { + state->char_cnt++; + } + + state->is_edited = true; + state->current_size++; + state->input_buffer = (char*) realloc((void*) state->input_buffer, state->current_size); + memmove((void*) &state->input_buffer[state->buffer_idx + 1], (void*) &state->input_buffer[state->buffer_idx], (state->current_size - state->buffer_idx - 1) * sizeof(char)); + state->input_buffer[state->buffer_idx] = input; + state->buffer_idx++; + + rerender_color(state); + } + break; + } + } + } + + return false; +} diff --git a/user/desktop/edit/main.c b/user/desktop/edit/main.c new file mode 100644 index 00000000..c5dfdbad --- /dev/null +++ b/user/desktop/edit/main.c @@ -0,0 +1,151 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void print_usage(char* prog) { + printf("Usage: %s [-r] \n\n", prog); + + printf("Cheat sheet\n"); + printf("w -> move up a line\n"); + printf("a -> move left a character\n"); + printf("s -> move down a line\n"); + printf("d -> move right a char\n"); + printf("+ -> write changes to file\n"); + printf("q -> quit\n"); + + printf("\n"); + printf("Those commands work in the EDIT mode witch can be activated by pressing 'esc'.\n"); + printf("To exit EDIT mode press 'esc' again.\n"); + printf("To disable syntax highlighting set the 'NOSYX' env variable to something.\n"); +} + +char* get_file_extension(const char* filename) { + char* chr_ptr = strchr(filename, '.'); + if (chr_ptr == NULL) { + return ""; + } + return ++chr_ptr; +} + +int main(int argc, char* argv[], char* envp[]) { + bool read_only = false; + char* file_name = NULL; + + int idx = 1; + while (idx < argc) { + if (strcmp(argv[idx], "-r") == 0) { + read_only = true; + } else if (strcmp(argv[idx], "-h") == 0) { + print_usage(argv[0]); + return 0; + } else { + if (file_name == NULL) { + file_name = argv[idx]; + } else { + printf("Error: Too many arguments\n"); + print_usage(argv[0]); + return 1; + } + } + + idx++; + } + + if (file_name == NULL) { + printf("Error: No file name provided\n"); + print_usage(argv[0]); + return 1; + } + + + if (!getenv("NOSYX")) { + char syx[128] = { 0 }; + strcat(syx, getenv("ROOT_FS")); + strcat(syx, "syntax/"); + strcat(syx, get_file_extension(file_name)); + strcat(syx, ".syx"); + syntax = load_syntax(syx); + } + + + edit_state_t state = { 0 }; + + state.file_name = file_name; + state.is_edited = false; + state.is_in_insert_mode = true; + state.read_only = read_only; + state.show_tab_char = false; + + state.file = fopen(file_name, "r"); + if (state.file == NULL) { + printf("Could not open file %s\n", file_name); + return 1; + } + + fsize(state.file, file_size); + state.input_buffer = (char*) malloc(sizeof(char) * file_size); + memset(state.input_buffer, 0, sizeof(char) * file_size); + state.current_size = file_size; + state.buffer_idx = file_size; + + if (file_size) { + fread(state.input_buffer, file_size, 1, state.file); + } + + state.ln_cnt = 1; + for (int i = 0; i < state.current_size; i++) { + state.char_cnt++; + if (state.input_buffer[i] == '\n') { + state.ln_cnt++; + state.buffer_ln_idx++; + state.char_cnt--; + } + } + + if (read_only) { + state.buffer_idx = 0; + state.buffer_ln_idx = 0; + } + + wm_client_t client; + wm_client_init(&client); + + char title[128] = { 0 }; + sprintf(title, "Edit - %s", file_name); + wm_client_set_title(&client, title); + wm_client_set_title_bar_color(&client, 0x16213e); + wm_client_set_bg_color(&client, 0x1a1a2e); + + render_ui(&client, &state); + wm_client_flush(&client); + + while (!wm_client_should_close(&client)) { + wm_event_t evt; + int dirty = 0; + + while (wm_client_poll_event(&client, &evt)) { + if (handle_event(&state, &evt)) { + goto done; + } + dirty = 1; + } + + if (dirty) { + render_ui(&client, &state); + wm_client_flush(&client); + } + + yield(); + } + +done: + free(state.input_buffer); + fclose(state.file); + return 0; +} diff --git a/user/desktop/edit/render.c b/user/desktop/edit/render.c new file mode 100644 index 00000000..d4963ce3 --- /dev/null +++ b/user/desktop/edit/render.c @@ -0,0 +1,163 @@ +#include + +#include +#include + +#define TAB_WIDTH 4 +#define CHAR_W 8 +#define CHAR_H 16 + +#define BG_COLOR 0x1a1a2e +#define TEXT_COLOR 0xCCCCCC +#define LINENO_COLOR 0x888888 +#define CURSOR_COLOR 0x00CCCC +#define STATUS_COLOR 0xFFFFFF + +syntax_header_t* syntax = NULL; + +uint32_t color_translation[] = { + 0x000000, + 0xCC0000, + 0x00CC00, + 0xCCCC00, + 0x0000CC, + 0xCC00CC, + 0x00CCCC, + 0xCCCCCC +}; + +uint8_t* color = NULL; + +void rerender_color(edit_state_t* state) { + if (syntax) { + if (color) { + free(color); + } + color = highlight(state->input_buffer, state->current_size, syntax); + } +} + +void draw_line_number(wm_client_t* client, int cur_y, int current_line) { + char buff[16] = { 0 }; + memset(buff, 0, sizeof(buff)); + sprintf(buff, "%d.", current_line); + wm_client_draw_string(client, 0, cur_y * CHAR_H, buff, LINENO_COLOR, BG_COLOR); +} + + +void render_ui(wm_client_t* client, edit_state_t* state) { + if (!color) { + rerender_color(state); + } + + int width = wm_client_width(client) / CHAR_W; + int height = wm_client_height(client) / CHAR_H; + + wm_client_fill_rect(client, 0, 0, wm_client_width(client), wm_client_height(client), BG_COLOR); + + char buff[512] = { 0 }; + if (state->read_only) { + sprintf(buff, "File: %s Current Line: %d Line: %d", state->file_name, state->buffer_ln_idx + 1, state->ln_cnt); + } else { + sprintf(buff, "File: %s [%c] -- %s -- Current Line: %d Line: %d", state->file_name, state->is_edited ? '*' : '-', state->is_in_insert_mode ? "INSERT" : "EDIT", state->buffer_ln_idx + 1, state->ln_cnt); + } + + wm_client_draw_string(client, 0, (height - 1) * CHAR_H, buff, STATUS_COLOR, BG_COLOR); + + int j = 0; + int cur_x = 0; + int cur_y = 0; + int already_drawn = 0; + bool initial_line_drawn = false; + bool cursor_drawn = false; + + memset(buff, 0, 512); + sprintf(buff, "%d .", state->ln_cnt); + int space_to_draw = 1 + strlen(buff); + cur_x = space_to_draw; + + int viewport_height = height - 3; + static int start_line = 0; + + if (state->buffer_ln_idx < start_line) { + start_line = state->buffer_ln_idx; + } else if (state->buffer_ln_idx >= start_line + viewport_height) { + start_line = state->buffer_ln_idx - viewport_height + 1; + } + + if (start_line < 0) { + start_line = 0; + } + if (start_line + viewport_height > state->ln_cnt) { + start_line = state->ln_cnt - viewport_height; + if (start_line < 0) { + start_line = 0; + } + } + + int current_line = start_line + 1; + + for (int i = 0; i < state->current_size; i++) { + if ((j >= start_line && j < start_line + viewport_height) && already_drawn <= viewport_height) { + if (!initial_line_drawn) { + initial_line_drawn = true; + draw_line_number(client, cur_y, current_line); + } + + if (i == state->buffer_idx) { + wm_client_draw_char(client, cur_x * CHAR_W, cur_y * CHAR_H, '|', CURSOR_COLOR, BG_COLOR); + cur_x++; + cursor_drawn = true; + } + + if (state->input_buffer[i] == '\t') { + int col = cur_x - space_to_draw; + int next_tab_stop = ((col / TAB_WIDTH) + 1) * TAB_WIDTH; + int spaces = next_tab_stop - col; + + for (int s = 0; s < spaces; s++) { + if (cur_x >= width) { + cur_y++; + cur_x = space_to_draw; + already_drawn++; + draw_line_number(client, cur_y, current_line); + } + wm_client_draw_char(client, cur_x * CHAR_W, cur_y * CHAR_H, state->show_tab_char ? '.' : ' ', LINENO_COLOR, BG_COLOR); + cur_x++; + } + cur_x--; + } + + if (state->input_buffer[i] >= 0x20 && state->input_buffer[i] <= 0x7E) { + wm_client_draw_char(client, cur_x * CHAR_W, cur_y * CHAR_H, state->input_buffer[i], color ? color_translation[color[i]] : TEXT_COLOR, BG_COLOR); + } + + cur_x++; + + if (state->input_buffer[i] == '\n') { + already_drawn++; + current_line++; + j++; + cur_x = space_to_draw; + cur_y++; + draw_line_number(client, cur_y, current_line); + } else if (cur_x == width - 1) { + cur_y++; + cur_x = space_to_draw; + already_drawn++; + } + } else { + if (state->input_buffer[i] == '\n') { + j++; + } + } + } + + if (!cursor_drawn) { + if (!initial_line_drawn) { + sprintf(buff, "%d.", current_line); + wm_client_draw_string(client, 0, cur_y * CHAR_H, buff, LINENO_COLOR, BG_COLOR); + } + wm_client_draw_char(client, cur_x * CHAR_W, cur_y * CHAR_H, '|', CURSOR_COLOR, BG_COLOR); + } +} diff --git a/user/desktop/explorer/Makefile b/user/desktop/explorer/Makefile new file mode 100644 index 00000000..0b1dcf19 --- /dev/null +++ b/user/desktop/explorer/Makefile @@ -0,0 +1,7 @@ +PROGRAM = desktop-explorer.elf + +include ../../program.mk + +CFLAGS += -I../libdesktop/include + +LINK = -ldesktop diff --git a/user/desktop/explorer/main.c b/user/desktop/explorer/main.c new file mode 100644 index 00000000..b94bc044 --- /dev/null +++ b/user/desktop/explorer/main.c @@ -0,0 +1,232 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BG_COLOR 0x1a1a2e + +void format_size(char* out, int size) { + if (size < 1024) { + sprintf(out, "%d b", size); + } else if (size < 1024 * 1024) { + sprintf(out, "%d Kb", size / 1024); + } else { + sprintf(out, "%d Mb", size / (1024 * 1024)); + } +} + +typedef struct { + int x, y, w, h; +} row_area_t; + +int main(int argc, char** argv) { + wm_client_t client; + wm_client_init(&client); + + wm_client_set_title(&client, "Explorer"); + wm_client_set_title_bar_color(&client, 0x4488ff); + wm_client_set_bg_color(&client, BG_COLOR); + + int cw = wm_client_width(&client); + int ch = wm_client_height(&client); + + char cwd[128] = { 0 }; + int offset = 0; + bool fs_mode = true; + + int max_rows = (ch - 50) / 16; + if (max_rows < 1) { + max_rows = 1; + } + + row_area_t* rows = malloc(sizeof(row_area_t) * max_rows); + memset(rows, 0, sizeof(row_area_t) * max_rows); + + ui_button_t back_btn, up_btn, down_btn; + ui_button_init(&back_btn, cw - 54, 2, 50, 18, "Back"); + ui_button_init(&up_btn, cw - 28, 22, 24, 20, "^"); + ui_button_init(&down_btn, cw - 28, ch - 24, 24, 20, "v"); + + int need_redraw = 1; + + while (!wm_client_should_close(&client)) { + wm_event_t evt; + while (wm_client_poll_event(&client, &evt)) { + need_redraw |= ui_button_update(&back_btn, &evt); + need_redraw |= ui_button_update(&up_btn, &evt); + need_redraw |= ui_button_update(&down_btn, &evt); + + if (evt.type == WM_EVENT_MOUSE_CLICK && evt.button == WM_MOUSE_BUTTON_LEFT) { + for (int i = 0; i < max_rows; i++) { + if (rows[i].w && rows[i].h && + evt.x >= rows[i].x && evt.x < rows[i].x + rows[i].w && + evt.y >= rows[i].y && evt.y < rows[i].y + rows[i].h) { + + if (fs_mode) { + char out[512] = { 0 }; + if (fs_at(out, offset + i) && out[0]) { + strcat(out, ":/"); + memset(cwd, 0, sizeof(cwd)); + strcpy(cwd, out); + fs_mode = false; + offset = 0; + need_redraw = 1; + } + } else { + dir_t dir; + dir_at(cwd, offset + i, &dir); + if (!dir.is_none) { + char full_path[128] = { 0 }; + strcpy(full_path, cwd); + int fp_len = strlen(full_path); + if (fp_len > 0 && full_path[fp_len - 1] != '/') { + strcat(full_path, "/"); + } + strcat(full_path, dir.name); + + if (dir.type == ENTRY_DIR) { + char path_buf[256] = { 0 }; + if (!resolve(full_path, path_buf)) { + fs_mode = true; + memset(cwd, 0, sizeof(cwd)); + offset = 0; + } else { + memset(cwd, 0, sizeof(cwd)); + strcpy(cwd, path_buf); + offset = 0; + } + need_redraw = 1; + } else if (dir.type == ENTRY_FILE) { + message_send(TOPIC_DESKTOP_OPEN_FILE, full_path, strlen(full_path) + 1); + } + } + } + break; + } + } + } + } + + if (ui_button_clicked(&back_btn)) { + if (!fs_mode) { + char full_path[128] = { 0 }; + strcpy(full_path, cwd); + int fp_len = strlen(full_path); + if (fp_len > 0 && full_path[fp_len - 1] != '/') { + strcat(full_path, "/"); + } + + strcat(full_path, ".."); + + char path_buf[256] = { 0 }; + if (!resolve(full_path, path_buf)) { + fs_mode = true; + memset(cwd, 0, sizeof(cwd)); + } else { + memset(cwd, 0, sizeof(cwd)); + strcpy(cwd, path_buf); + } + offset = 0; + } + need_redraw = 1; + } + if (ui_button_clicked(&up_btn)) { + if (offset > 0) { + offset--; + } + need_redraw = 1; + } + if (ui_button_clicked(&down_btn)) { + offset++; + need_redraw = 1; + } + + if (need_redraw) { + int w = wm_client_width(&client); + int h = wm_client_height(&client); + wm_client_fill_rect(&client, 0, 0, w, h, BG_COLOR); + + wm_client_draw_string(&client, 2, 2, cwd[0] ? cwd : "(select filesystem)", 0xffffff, BG_COLOR); + wm_client_draw_line(&client, 0, 20, w, 20, 0x444444); + + ui_button_draw(&back_btn, &client); + ui_button_draw(&up_btn, &client); + ui_button_draw(&down_btn, &client); + + int start_y = 32; + memset(rows, 0, sizeof(row_area_t) * max_rows); + + if (fs_mode) { + for (int i = 0; i < max_rows; i++) { + char out[512] = { 0 }; + if (!fs_at(out, offset + i)) { + break; + } + + int y = start_y + i * 16; + wm_client_draw_string(&client, 2, y, out, 0x90EE90, BG_COLOR); + rows[i].x = 0; + rows[i].y = y; + rows[i].w = w - 32; + rows[i].h = 16; + } + } else { + dir_t dir; + int count = 0; + for (int i = offset; i < offset + max_rows && count < max_rows; i++) { + dir_at(cwd, i, &dir); + if (dir.is_none) { + break; + } + + int y = start_y + count * 16; + uint32_t color = (dir.type == ENTRY_DIR) ? 0x90EE90 : 0xFF6B6B; + wm_client_draw_string(&client, 2, y, dir.name, color, BG_COLOR); + + char type_ch = dir.type == ENTRY_FILE ? 'F' : 'D'; + wm_client_draw_char(&client, w - 32, y, type_ch, 0xffffff, BG_COLOR); + + if (dir.type == ENTRY_FILE) { + char full_path[128] = { 0 }; + strcpy(full_path, cwd); + int fp_len = strlen(full_path); + if (fp_len > 0 && full_path[fp_len - 1] != '/') { + strcat(full_path, "/"); + } + + strcat(full_path, dir.name); + + int fd = open(full_path, 0); + if (fd >= 0) { + char size_str[16] = { 0 }; + format_size(size_str, filesize(fd)); + close(fd); + int size_x = w - 40 - strlen(size_str) * 8; + wm_client_draw_string(&client, size_x, y, size_str, 0x888888, BG_COLOR); + } + } + + rows[count].x = 0; + rows[count].y = y; + rows[count].w = w - 32; + rows[count].h = 16; + count++; + } + } + + wm_client_flush(&client); + need_redraw = 0; + } + + yield(); + } + + free(rows); + return 0; +} diff --git a/user/desktop/imgview/Makefile b/user/desktop/imgview/Makefile new file mode 100644 index 00000000..725caf6e --- /dev/null +++ b/user/desktop/imgview/Makefile @@ -0,0 +1,7 @@ +PROGRAM = desktop-imgview.elf + +include ../../program.mk + +CFLAGS += -I../libdesktop/include + +LINK = -ldesktop diff --git a/user/desktop/imgview/main.c b/user/desktop/imgview/main.c new file mode 100644 index 00000000..73b25d6f --- /dev/null +++ b/user/desktop/imgview/main.c @@ -0,0 +1,134 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define BG_COLOR 0x000000 + +typedef struct { + uint64_t magic; // 0xc0ffebabe + uint64_t width; + uint64_t height; + uint32_t pixels[]; +} __attribute__((packed)) fpic_image_t; + + +void draw_image_scaled(wm_client_t* client, fpic_image_t* pic, int max_w, int max_h) { + if (!pic || pic->magic != 0xc0ffebabe) { + return; + } + + int scale_w = 1; + int scale_h = 1; + + if ((int)pic->width > max_w) { + scale_w = (pic->width + max_w - 1) / max_w; + } + + if ((int)pic->height > max_h) { + scale_h = (pic->height + max_h - 1) / max_h; + } + + int scale = scale_w > scale_h ? scale_w : scale_h; + if (scale < 1) { + scale = 1; + } + + int out_w = pic->width / scale; + int out_h = pic->height / scale; + + int ox = (max_w - out_w) / 2; + int oy = (max_h - out_h) / 2; + if (ox < 0) { + ox = 0; + } + + if (oy < 0) { + oy = 0; + } + + for (int j = 0; j < out_h; j++) { + for (int i = 0; i < out_w; i++) { + int src_x = i * scale; + int src_y = j * scale; + if (src_x < (int)pic->width && src_y < (int)pic->height) { + uint32_t color = pic->pixels[src_y * pic->width + src_x]; + uint8_t alpha = (color >> 24) & 0xFF; + if (alpha > 0) { + wm_client_set_pixel(client, ox + i, oy + j, color & 0x00FFFFFF); + } + } + } + } +} + +int main(int argc, char** argv) { + wm_client_t client; + wm_client_init(&client); + + wm_client_set_title(&client, "Image Viewer"); + wm_client_set_title_bar_color(&client, 0x226644); + wm_client_set_bg_color(&client, BG_COLOR); + + // Load image if path is provided as argv[2] + void* img_data = NULL; + int img_size = 0; + + if (argc >= 2) { + const char* path = argv[1]; + FILE* f = fopen(path, "r"); + if (f) { + fsize(f, size); + img_data = malloc(size); + fread(img_data, 1, size, f); + fclose(f); + img_size = size; + + char title[64] = { 0 }; + const char* name = path; + for (const char* p = path; *p; p++) { + if (*p == '/') { + name = p + 1; + } + } + + sprintf(title, "Image: %s", name); + wm_client_set_title(&client, title); + } + } + + int w = wm_client_width(&client); + int h = wm_client_height(&client); + wm_client_fill_rect(&client, 0, 0, w, h, BG_COLOR); + + if (img_data && img_size > 0) { + fpic_image_t* fpic = (fpic_image_t*)img_data; + if (fpic->magic == 0xc0ffebabe) { + draw_image_scaled(&client, fpic, w, h); + } else { + wm_client_draw_string(&client, 4, 4, "Unsupported format", 0xff4444, BG_COLOR); + } + } else { + wm_client_draw_string(&client, 4, 4, "No image loaded", 0x888888, BG_COLOR); + } + + wm_client_flush(&client); + + while (!wm_client_should_close(&client)) { + wm_event_t evt; + while (wm_client_poll_event(&client, &evt)) { + } + + yield(); + } + + if (img_data) { + free(img_data); + } + + return 0; +} diff --git a/user/desktop/libdesktop/Makefile b/user/desktop/libdesktop/Makefile new file mode 100644 index 00000000..ff1a8f64 --- /dev/null +++ b/user/desktop/libdesktop/Makefile @@ -0,0 +1,5 @@ +LIBRARY = libdesktop.o + +include ../../library.mk + +CFLAGS += -I../../libraries/libc/include diff --git a/user/desktop/libdesktop/include/ui/button.h b/user/desktop/libdesktop/include/ui/button.h new file mode 100644 index 00000000..6e8f9569 --- /dev/null +++ b/user/desktop/libdesktop/include/ui/button.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include + +typedef struct { + int x, y, w, h; + const char* label; + uint32_t bg_color; + uint32_t hover_color; + uint32_t text_color; + uint32_t border_color; + int is_hovered; + int was_clicked; +} ui_button_t; + +void ui_button_init(ui_button_t* b, int x, int y, int w, int h, const char* label); +int ui_button_update(ui_button_t* b, wm_event_t* evt); +int ui_button_clicked(ui_button_t* b); +void ui_button_draw(ui_button_t* b, wm_client_t* c); diff --git a/user/desktop/libdesktop/include/wm_client.h b/user/desktop/libdesktop/include/wm_client.h new file mode 100644 index 00000000..e7fc3aa5 --- /dev/null +++ b/user/desktop/libdesktop/include/wm_client.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include + +#define WM_PSF1_MAGIC0 0x36 +#define WM_PSF1_MAGIC1 0x04 + +typedef struct { + uint8_t magic[2]; + uint8_t mode; + uint8_t charsize; +} wm_psf1_header_t; + +typedef struct { + wm_psf1_header_t* header; + void* glyph_buffer; +} wm_psf1_font_t; + +// Client context +typedef struct { + wm_shared_t* control; + uint32_t* pixels; + wm_psf1_font_t font; + int width; + int height; + int slot; +} wm_client_t; + +void wm_client_init(wm_client_t* client); + +bool wm_client_poll_event(wm_client_t* client, wm_event_t* out); + +bool wm_client_should_close(wm_client_t* client); + +int wm_client_width(wm_client_t* client); +int wm_client_height(wm_client_t* client); + +void wm_client_set_pixel(wm_client_t* client, int x, int y, uint32_t color); +void wm_client_fill_rect(wm_client_t* client, int x, int y, int w, int h, uint32_t color); +void wm_client_draw_string(wm_client_t* client, int x, int y, const char* str, uint32_t fg, uint32_t bg); +void wm_client_draw_char(wm_client_t* client, int x, int y, char c, uint32_t fg, uint32_t bg); +void wm_client_draw_line(wm_client_t* client, int x1, int y1, int x2, int y2, uint32_t color); +void wm_client_draw_rect(wm_client_t* client, int x, int y, int w, int h, uint32_t color); + +void wm_client_flush(wm_client_t* client); + +void wm_client_set_title(wm_client_t* client, const char* title); +void wm_client_set_bg_color(wm_client_t* client, uint32_t color); +void wm_client_set_title_bar_color(wm_client_t* client, uint32_t color); + diff --git a/user/desktop/libdesktop/include/wm_protocol.h b/user/desktop/libdesktop/include/wm_protocol.h new file mode 100644 index 00000000..c4f7d799 --- /dev/null +++ b/user/desktop/libdesktop/include/wm_protocol.h @@ -0,0 +1,71 @@ +#pragma once + +#include +#include + +#define WM_SHM_BASE 0xA0000000 +#define WM_SHM_SLOT_SIZE 0x200000 +#define WM_CONTROL_OFFSET 0x0000 +#define WM_PIXELS_OFFSET 0x1000 +#define WM_PAGE_SIZE 0x1000 + +#define WM_MAX_EVENTS 32 +#define WM_MAX_PIXEL_WIDTH 800 +#define WM_MAX_PIXEL_HEIGHT 600 +#define WM_MAX_PIXEL_BYTES (WM_MAX_PIXEL_WIDTH * WM_MAX_PIXEL_HEIGHT * 4) +#define WM_MAX_PIXEL_PAGES ((WM_MAX_PIXEL_BYTES + WM_PAGE_SIZE - 1) / WM_PAGE_SIZE) +#define WM_MAX_SLOTS 16 + +#define WM_SHM_ADDR(slot) ((void*)(WM_SHM_BASE + (slot) * WM_SHM_SLOT_SIZE)) +#define WM_SHM_CONTROL(slot) ((wm_shared_t*)((uintptr_t)WM_SHM_ADDR(slot) + WM_CONTROL_OFFSET)) +#define WM_SHM_PIXELS(slot) ((uint32_t*)((uintptr_t)WM_SHM_ADDR(slot) + WM_PIXELS_OFFSET)) + +enum { + WM_STATE_INIT = 0, + WM_STATE_READY = 1, + WM_STATE_ACK = 2, + WM_STATE_CONNECTED = 3 +}; + +#define WM_EVENT_NONE 0 +#define WM_EVENT_MOUSE_CLICK 1 +#define WM_EVENT_MOUSE_MOVE 2 +#define WM_EVENT_CLOSE 3 +#define WM_EVENT_KEY_PRESS 4 +#define WM_EVENT_ARROW_KEY 5 + +#define WM_MOUSE_BUTTON_LEFT 1 +#define WM_MOUSE_BUTTON_RIGHT 2 +#define WM_MOUSE_BUTTON_MIDDLE 4 + +#define WM_ARROW_UP 1 +#define WM_ARROW_DOWN 2 +#define WM_ARROW_LEFT 3 +#define WM_ARROW_RIGHT 4 + +typedef struct { + int type; + int x; + int y; + int button; + char key; + int arrow; +} wm_event_t; + +typedef struct { + volatile int state; + + int width; + int height; + + volatile int event_write; + volatile int event_read; + wm_event_t events[WM_MAX_EVENTS]; + + volatile int dirty; + volatile int alive; + volatile int close_requested; + char title[64]; + uint32_t bg_color; + uint32_t title_bar_color; +} wm_shared_t; diff --git a/user/desktop/libdesktop/ui/button.c b/user/desktop/libdesktop/ui/button.c new file mode 100644 index 00000000..ed0643bc --- /dev/null +++ b/user/desktop/libdesktop/ui/button.c @@ -0,0 +1,62 @@ +#include + +void ui_button_init(ui_button_t* b, int x, int y, int w, int h, const char* label) { + b->x = x; + b->y = y; + b->w = w; + b->h = h; + b->label = label; + b->bg_color = 0x445566; + b->hover_color = 0x5577aa; + b->text_color = 0xffffff; + b->border_color = 0x666688; + b->is_hovered = 0; + b->was_clicked = 0; +} + +int hit_test(ui_button_t* b, int x, int y) { + return x >= b->x && x < b->x + b->w && y >= b->y && y < b->y + b->h; +} + +int ui_button_update(ui_button_t* b, wm_event_t* evt) { + int dirty = 0; + + if (evt->type == WM_EVENT_MOUSE_MOVE) { + int now = hit_test(b, evt->x, evt->y); + if (now != b->is_hovered) { + b->is_hovered = now; + dirty = 1; + } + } + + if (evt->type == WM_EVENT_MOUSE_CLICK && evt->button == WM_MOUSE_BUTTON_LEFT) { + if (hit_test(b, evt->x, evt->y)) { + b->was_clicked = 1; + dirty = 1; + } + } + + return dirty; +} + +int ui_button_clicked(ui_button_t* b) { + if (b->was_clicked) { + b->was_clicked = 0; + return 1; + } + return 0; +} + +void ui_button_draw(ui_button_t* b, wm_client_t* c) { + uint32_t bg = b->is_hovered ? b->hover_color : b->bg_color; + + wm_client_fill_rect(c, b->x, b->y, b->w, b->h, bg); + + int text_y = b->y + (b->h - 16) / 2; + wm_client_draw_string(c, b->x + 8, text_y, b->label, b->text_color, bg); + + wm_client_draw_line(c, b->x, b->y, b->x + b->w, b->y, b->border_color); + wm_client_draw_line(c, b->x, b->y + b->h - 1, b->x + b->w, b->y + b->h - 1, b->border_color); + wm_client_draw_line(c, b->x, b->y, b->x, b->y + b->h, b->border_color); + wm_client_draw_line(c, b->x + b->w - 1, b->y, b->x + b->w - 1, b->y + b->h, b->border_color); +} diff --git a/user/desktop/libdesktop/wm_client.c b/user/desktop/libdesktop/wm_client.c new file mode 100644 index 00000000..8ec31bec --- /dev/null +++ b/user/desktop/libdesktop/wm_client.c @@ -0,0 +1,193 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +#define RABS(x) ((x) < 0 ? -(x) : (x)) + +wm_psf1_font_t load_wm_font(void) { + wm_psf1_font_t font; + font.header = NULL; + font.glyph_buffer = NULL; + + FILE* f = fopen("dev:font", "r"); + if (!f) { + return font; + } + + fsize(f, size); + void* buf = malloc(size); + fread(buf, 1, size, f); + fclose(f); + + font.header = (wm_psf1_header_t*)buf; + if (font.header->magic[0] != WM_PSF1_MAGIC0 || font.header->magic[1] != WM_PSF1_MAGIC1) { + free(buf); + font.header = NULL; + return font; + } + + font.glyph_buffer = (void*)((uint8_t*)buf + sizeof(wm_psf1_header_t)); + return font; +} + +void wm_client_init(wm_client_t* client) { + char* slot_env = getenv("WMS"); + if (!slot_env) { + printf("wm_client_init: WMS env variable not set\n"); + exit(1); + } + + client->slot = atoi(slot_env); + + void* base = WM_SHM_ADDR(client->slot); + + while (!mmap_mapped(base)) { + yield(); + } + + client->control = (wm_shared_t*)((uintptr_t)base + WM_CONTROL_OFFSET); + client->pixels = (uint32_t*)((uintptr_t)base + WM_PIXELS_OFFSET); + + while (client->control->state != WM_STATE_READY) { + yield(); + } + + client->width = client->control->width; + client->height = client->control->height; + + client->control->alive = 1; + client->control->state = WM_STATE_ACK; + + while (client->control->state != WM_STATE_CONNECTED) { + yield(); + } + + client->font = load_wm_font(); + + memset(client->pixels, 0, client->width * client->height * 4); +} + +bool wm_client_poll_event(wm_client_t* client, wm_event_t* out) { + wm_shared_t* ctl = client->control; + + if (ctl->event_read == ctl->event_write) { + return false; + } + + *out = ctl->events[ctl->event_read % WM_MAX_EVENTS]; + ctl->event_read++; + return true; +} + +bool wm_client_should_close(wm_client_t* client) { + return client->control->close_requested != 0; +} + +int wm_client_width(wm_client_t* client) { + return client->control->width; +} + +int wm_client_height(wm_client_t* client) { + return client->control->height; +} + +void wm_client_set_pixel(wm_client_t* client, int x, int y, uint32_t color) { + int w = client->control->width; + int h = client->control->height; + if (x < 0 || x >= w || y < 0 || y >= h) { + return; + } + client->pixels[y * w + x] = color; +} + +void wm_client_fill_rect(wm_client_t* client, int x, int y, int w, int h, uint32_t color) { + for (int j = y; j < y + h; j++) { + for (int i = x; i < x + w; i++) { + wm_client_set_pixel(client, i, j, color); + } + } +} + +void wm_client_draw_char(wm_client_t* client, int x, int y, char c, uint32_t fg, uint32_t bg) { + if (!client->font.header || !client->font.glyph_buffer) { + return; + } + char* font_ptr = (char*)client->font.glyph_buffer + (c * client->font.header->charsize); + + for (int j = 0; j < 16; j++) { + for (int i = 0; i < 8; i++) { + if ((*font_ptr & (0b10000000 >> i)) > 0) { + wm_client_set_pixel(client, x + i, y + j, fg); + } else { + wm_client_set_pixel(client, x + i, y + j, bg); + } + } + font_ptr++; + } +} + +void wm_client_draw_string(wm_client_t* client, int x, int y, const char* str, uint32_t fg, uint32_t bg) { + int i = 0; + while (str[i]) { + wm_client_draw_char(client, x + 8 * i, y, str[i], fg, bg); + i++; + } +} + +void wm_client_draw_line(wm_client_t* client, int x1, int y1, int x2, int y2, uint32_t color) { + int dx = RABS(x2 - x1); + int dy = RABS(y2 - y1); + int sx = x1 < x2 ? 1 : -1; + int sy = y1 < y2 ? 1 : -1; + int err = dx - dy; + + while (1) { + wm_client_set_pixel(client, x1, y1, color); + if (x1 == x2 && y1 == y2) { + break; + } + int e2 = 2 * err; + if (e2 > -dy) { + err -= dy; + x1 += sx; + } + if (e2 < dx) { + err += dx; + y1 += sy; + } + } +} + +void wm_client_draw_rect(wm_client_t* client, int x, int y, int w, int h, uint32_t color) { + wm_client_draw_line(client, x, y, x + w, y, color); + wm_client_draw_line(client, x, y + h, x + w, y + h, color); + wm_client_draw_line(client, x, y, x, y + h, color); + wm_client_draw_line(client, x + w, y, x + w, y + h, color); +} + +void wm_client_flush(wm_client_t* client) { + client->control->dirty = 1; + while (client->control->dirty) { + yield(); + } +} + +void wm_client_set_title(wm_client_t* client, const char* title) { + memset(client->control->title, 0, 64); + size_t len = strnlen(title, 63); + memcpy(client->control->title, title, len); +} + +void wm_client_set_bg_color(wm_client_t* client, uint32_t color) { + client->control->bg_color = color; +} + +void wm_client_set_title_bar_color(wm_client_t* client, uint32_t color) { + client->control->title_bar_color = color; +} diff --git a/user/desktop/netinfo/Makefile b/user/desktop/netinfo/Makefile new file mode 100644 index 00000000..10ec579d --- /dev/null +++ b/user/desktop/netinfo/Makefile @@ -0,0 +1,7 @@ +PROGRAM = desktop-netinfo.elf + +include ../../program.mk + +CFLAGS += -I../libdesktop/include + +LINK = -ldesktop diff --git a/user/desktop/netinfo/main.c b/user/desktop/netinfo/main.c new file mode 100644 index 00000000..ec20d65c --- /dev/null +++ b/user/desktop/netinfo/main.c @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BG_COLOR 0x051015 + +int nic_valid(int interface) { + char buffer[16] = { 0 }; + sprintf(buffer, "dev:nic%d", interface); + + int fd = open(buffer, FILE_OPEN_MODE_READ); + if (fd < 0) { + return 0; + } + + close(fd); + return 1; +} + +int main(int argc, char** argv) { + wm_client_t client; + wm_client_init(&client); + + wm_client_set_title(&client, "Network Info"); + wm_client_set_title_bar_color(&client, 0x1a3a2e); + wm_client_set_bg_color(&client, BG_COLOR); + + int cw = wm_client_width(&client); + int ch = wm_client_height(&client); + int interface = 0; + + ui_button_t up_btn, down_btn, dhcp_btn; + ui_button_init(&up_btn, cw - 28, 2, 24, 20, "^"); + ui_button_init(&down_btn, cw - 28, ch - 24, 24, 20, "v"); + ui_button_init(&dhcp_btn, cw - 54, 26, 50, 20, "DHCP"); + dhcp_btn.bg_color = 0x336644; + dhcp_btn.hover_color = 0x44aa66; + + int need_redraw = 1; + + while (!wm_client_should_close(&client)) { + wm_event_t evt; + while (wm_client_poll_event(&client, &evt)) { + need_redraw |= ui_button_update(&up_btn, &evt); + need_redraw |= ui_button_update(&down_btn, &evt); + need_redraw |= ui_button_update(&dhcp_btn, &evt); + } + + if (ui_button_clicked(&up_btn)) { + interface--; + need_redraw = 1; + } + if (ui_button_clicked(&down_btn)) { + interface++; + need_redraw = 1; + } + if (ui_button_clicked(&dhcp_btn)) { + char cmd_str[32] = { 0 }; + sprintf(cmd_str, "dhcp -i %d", interface); + system(cmd_str); + need_redraw = 1; + } + + + if (need_redraw) { + int w = wm_client_width(&client); + int h = wm_client_height(&client); + wm_client_fill_rect(&client, 0, 0, w, h, BG_COLOR); + + ui_button_draw(&up_btn, &client); + ui_button_draw(&down_btn, &client); + ui_button_draw(&dhcp_btn, &client); + + char buf[128] = { 0 }; + int line = 0; + sprintf(buf, "Interface: %d", interface); + wm_client_draw_string(&client, 4, line * 16 + 4, buf, 0xaaffcc, BG_COLOR); + line += 2; + + if (!nic_valid(interface)) { + sprintf(buf, "no nic %d", interface); + wm_client_draw_string(&client, 4, line * 16 + 4, buf, 0xff4444, BG_COLOR); + } else { + nic_content_t nic = nic_read(interface); + + sprintf(buf, "ip: %d.%d.%d.%d", nic.ip_config.ip.ip_p[0], nic.ip_config.ip.ip_p[1], nic.ip_config.ip.ip_p[2], nic.ip_config.ip.ip_p[3]); + wm_client_draw_string(&client, 4, line * 16 + 4, buf, 0xffffff, BG_COLOR); line++; + + sprintf(buf, "mask: %d.%d.%d.%d", nic.ip_config.subnet_mask.ip_p[0], nic.ip_config.subnet_mask.ip_p[1], nic.ip_config.subnet_mask.ip_p[2], nic.ip_config.subnet_mask.ip_p[3]); + wm_client_draw_string(&client, 4, line * 16 + 4, buf, 0xffffff, BG_COLOR); line++; + + sprintf(buf, "gw: %d.%d.%d.%d", nic.ip_config.gateway_ip.ip_p[0], nic.ip_config.gateway_ip.ip_p[1], nic.ip_config.gateway_ip.ip_p[2], nic.ip_config.gateway_ip.ip_p[3]); + wm_client_draw_string(&client, 4, line * 16 + 4, buf, 0xffffff, BG_COLOR); line++; + + sprintf(buf, "dns: %d.%d.%d.%d", nic.ip_config.dns_ip.ip_p[0], nic.ip_config.dns_ip.ip_p[1], nic.ip_config.dns_ip.ip_p[2], nic.ip_config.dns_ip.ip_p[3]); + wm_client_draw_string(&client, 4, line * 16 + 4, buf, 0xffffff, BG_COLOR); line++; + + sprintf(buf, "mac: %x:%x:%x:%x:%x:%x", nic.mac.mac_p[0], nic.mac.mac_p[1], nic.mac.mac_p[2], nic.mac.mac_p[3], nic.mac.mac_p[4], nic.mac.mac_p[5]); + wm_client_draw_string(&client, 4, line * 16 + 4, buf, 0xffffff, BG_COLOR); line++; + + sprintf(buf, "dev: %s", nic.name); + wm_client_draw_string(&client, 4, line * 16 + 4, buf, 0xffffff, BG_COLOR); + } + + wm_client_flush(&client); + need_redraw = 0; + } + + yield(); + } + + return 0; +} diff --git a/user/desktop/service/Makefile b/user/desktop/service/Makefile new file mode 100644 index 00000000..960b2399 --- /dev/null +++ b/user/desktop/service/Makefile @@ -0,0 +1,7 @@ +PROGRAM = desktop-service.elf + +include ../../program.mk + +CFLAGS += -I../libdesktop/include + +LINK = -ldesktop diff --git a/user/desktop/service/main.c b/user/desktop/service/main.c new file mode 100644 index 00000000..6e8016ec --- /dev/null +++ b/user/desktop/service/main.c @@ -0,0 +1,251 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BG_COLOR 0x0c1118 +#define ROW_HEIGHT 22 +#define TABLE_START_Y 46 +#define BUTTON_W 56 +#define BUTTON_H 18 +#define POLL_INTERVAL_MS 80 + +#define OP_START 1 +#define OP_STOP 2 +#define OP_RESTART 3 + +const char* status_to_string(int status) { + switch (status) { + case SERVICE_STATUS_RUNNING: return "running"; + case SERVICE_STATUS_STOPPED: return "stopped"; + case SERVICE_STATUS_FAILED: return "failed"; + default: return "unknown"; + } +} + +int main(int argc, char** argv) { + wm_client_t client; + wm_client_init(&client); + + wm_client_set_title(&client, "Services"); + wm_client_set_title_bar_color(&client, 0x20354a); + wm_client_set_bg_color(&client, BG_COLOR); + + int cw = wm_client_width(&client); + int ch = wm_client_height(&client); + + service_list_reply_t list; + memset(&list, 0, sizeof(list)); + int has_list = 0; + int waiting_for_list = 0; + int waiting_for_op = 0; + long next_poll_ms = 0; + char status_text[96] = { 0 }; + + ui_button_t refresh_btn; + ui_button_init(&refresh_btn, cw - 72, 4, 68, 18, "Refresh"); + refresh_btn.bg_color = 0x224455; + refresh_btn.hover_color = 0x3377aa; + + int max_visible = (ch - TABLE_START_Y - 8) / ROW_HEIGHT; + if (max_visible < 0) { + max_visible = 0; + } + + if (max_visible > MAX_SERVICES) { + max_visible = MAX_SERVICES; + } + + ui_button_t* start_btns = malloc(sizeof(ui_button_t) * max_visible); + ui_button_t* stop_btns = malloc(sizeof(ui_button_t) * max_visible); + ui_button_t* restart_btns = malloc(sizeof(ui_button_t) * max_visible); + memset(start_btns, 0, sizeof(ui_button_t) * max_visible); + memset(stop_btns, 0, sizeof(ui_button_t) * max_visible); + memset(restart_btns, 0, sizeof(ui_button_t) * max_visible); + + int dummy = 0; + message_send(TOPIC_SERVICE_LIST, &dummy, sizeof(dummy)); + waiting_for_list = 1; + next_poll_ms = time_ms() + POLL_INTERVAL_MS; + strcpy(status_text, "Requesting service list..."); + + int need_redraw = 1; + + while (!wm_client_should_close(&client)) { + wm_event_t evt; + while (wm_client_poll_event(&client, &evt)) { + need_redraw |= ui_button_update(&refresh_btn, &evt); + if (has_list) { + int count = list.count; + if (count > max_visible) { + count = max_visible; + } + for (int i = 0; i < count; i++) { + need_redraw |= ui_button_update(&start_btns[i], &evt); + need_redraw |= ui_button_update(&stop_btns[i], &evt); + need_redraw |= ui_button_update(&restart_btns[i], &evt); + } + } + } + + if (ui_button_clicked(&refresh_btn) && !waiting_for_list && !waiting_for_op) { + message_send(TOPIC_SERVICE_LIST, &dummy, sizeof(dummy)); + waiting_for_list = 1; + next_poll_ms = time_ms() + POLL_INTERVAL_MS; + strcpy(status_text, "Requesting service list..."); + need_redraw = 1; + } + + if (has_list && !waiting_for_list && !waiting_for_op) { + int count = list.count; + if (count > max_visible) { + count = max_visible; + } + + for (int i = 0; i < count; i++) { + if (ui_button_clicked(&start_btns[i])) { + service_op_request_t req = { 0 }; + size_t slen = strnlen(list.services[i].name, SERVICE_NAME_LEN - 1); + memcpy(req.name, list.services[i].name, slen); + req.name[slen] = 0; + message_send(TOPIC_SERVICE_START, &req, sizeof(req)); + waiting_for_op = 1; + next_poll_ms = time_ms() + POLL_INTERVAL_MS; + sprintf(status_text, "Starting '%s'...", req.name); + need_redraw = 1; + } + + if (ui_button_clicked(&stop_btns[i])) { + service_op_request_t req = { 0 }; + size_t slen = strnlen(list.services[i].name, SERVICE_NAME_LEN - 1); + memcpy(req.name, list.services[i].name, slen); + req.name[slen] = 0; + message_send(TOPIC_SERVICE_STOP, &req, sizeof(req)); + waiting_for_op = 1; + next_poll_ms = time_ms() + POLL_INTERVAL_MS; + sprintf(status_text, "Stopping '%s'...", req.name); + need_redraw = 1; + } + + if (ui_button_clicked(&restart_btns[i])) { + service_op_request_t req = { 0 }; + size_t slen = strnlen(list.services[i].name, SERVICE_NAME_LEN - 1); + memcpy(req.name, list.services[i].name, slen); + req.name[slen] = 0; + message_send(TOPIC_SERVICE_RESTART, &req, sizeof(req)); + waiting_for_op = 1; + next_poll_ms = time_ms() + POLL_INTERVAL_MS; + sprintf(status_text, "Restarting '%s'...", req.name); + need_redraw = 1; + } + } + } + + long now = time_ms(); + if (now >= next_poll_ms) { + next_poll_ms = now + POLL_INTERVAL_MS; + if (waiting_for_list) { + service_list_reply_t reply = { 0 }; + if (message_recv(TOPIC_SERVICE_LIST_REPLY, &reply, sizeof(reply)) > 0) { + list = reply; + has_list = 1; + waiting_for_list = 0; + sprintf(status_text, "Loaded %d service(s)", list.count); + need_redraw = 1; + + int count = list.count; + if (count > max_visible) { + count = max_visible; + } + + int action_x = cw - (BUTTON_W * 3 + 10); + for (int i = 0; i < count; i++) { + int row_y = TABLE_START_Y + i * ROW_HEIGHT; + ui_button_init(&start_btns[i], action_x, row_y, BUTTON_W, BUTTON_H, "Start"); + start_btns[i].bg_color = 0x244426; + start_btns[i].hover_color = 0x2f6633; + ui_button_init(&stop_btns[i], action_x + BUTTON_W + 3, row_y, BUTTON_W, BUTTON_H, "Stop"); + stop_btns[i].bg_color = 0x553322; + stop_btns[i].hover_color = 0x885533; + ui_button_init(&restart_btns[i], action_x + (BUTTON_W + 3) * 2, row_y, BUTTON_W, BUTTON_H, "Again"); + restart_btns[i].bg_color = 0x334466; + restart_btns[i].hover_color = 0x4477aa; + } + } + } + + if (waiting_for_op) { + service_op_reply_t op_reply = { 0 }; + if (message_recv(TOPIC_SERVICE_OP_REPLY, &op_reply, sizeof(op_reply)) > 0) { + waiting_for_op = 0; + if (op_reply.success) { + sprintf(status_text, "OK: %s", op_reply.message); + } else { + sprintf(status_text, "Error: %s", op_reply.message); + } + + message_send(TOPIC_SERVICE_LIST, &dummy, sizeof(dummy)); + waiting_for_list = 1; + next_poll_ms = now + POLL_INTERVAL_MS; + need_redraw = 1; + } + } + } + + if (need_redraw) { + int w = wm_client_width(&client); + int h = wm_client_height(&client); + wm_client_fill_rect(&client, 0, 0, w, h, BG_COLOR); + + ui_button_draw(&refresh_btn, &client); + + wm_client_draw_line(&client, 0, 24, w, 24, 0x335577); + wm_client_draw_string(&client, 4, 28, "Service", 0x99ccff, BG_COLOR); + wm_client_draw_string(&client, 110, 28, "Status", 0x99ccff, BG_COLOR); + wm_client_draw_string(&client, 190, 28, "PID", 0x99ccff, BG_COLOR); + wm_client_draw_string(&client, 228, 28, "Retry", 0x99ccff, BG_COLOR); + + if (!has_list) { + wm_client_draw_string(&client, 4, TABLE_START_Y, "No data loaded", 0xffcc88, BG_COLOR); + } else { + int count = list.count; + if (count > max_visible) count = max_visible; + for (int i = 0; i < count; i++) { + service_info_t* svc = &list.services[i]; + int row_y = TABLE_START_Y + i * ROW_HEIGHT; + wm_client_draw_string(&client, 4, row_y, svc->name, 0xffffff, BG_COLOR); + wm_client_draw_string(&client, 110, row_y, status_to_string(svc->status), 0xffffff, BG_COLOR); + + char num_buf[16] = { 0 }; + sprintf(num_buf, "%d", svc->pid); + wm_client_draw_string(&client, 190, row_y, num_buf, 0xffffff, BG_COLOR); + sprintf(num_buf, "%d", svc->retry); + wm_client_draw_string(&client, 228, row_y, num_buf, 0xffffff, BG_COLOR); + + ui_button_draw(&start_btns[i], &client); + ui_button_draw(&stop_btns[i], &client); + ui_button_draw(&restart_btns[i], &client); + } + } + + wm_client_draw_line(&client, 0, h - 18, w, h - 18, 0x335577); + wm_client_draw_string(&client, 4, h - 16, status_text, 0xaaddff, BG_COLOR); + + wm_client_flush(&client); + need_redraw = 0; + } + + yield(); + } + + free(start_btns); + free(stop_btns); + free(restart_btns); + return 0; +} diff --git a/user/desktop/sysctl/Makefile b/user/desktop/sysctl/Makefile new file mode 100644 index 00000000..953c3fd8 --- /dev/null +++ b/user/desktop/sysctl/Makefile @@ -0,0 +1,7 @@ +PROGRAM = desktop-sysctl.elf + +include ../../program.mk + +CFLAGS += -I../libdesktop/include + +LINK = -ldesktop diff --git a/user/desktop/sysctl/main.c b/user/desktop/sysctl/main.c new file mode 100644 index 00000000..28f37b25 --- /dev/null +++ b/user/desktop/sysctl/main.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BG_COLOR 0x1a0a2e + +#define is_kb(x) ((x) >= 1024) +#define is_mb(x) ((x) >= 1024 * 1024) +#define is_gb(x) ((unsigned)(x) >= 1024u * 1024u * 1024u) +#define to_kb(x) ((x) / 1024) +#define to_mb(x) ((x) / 1024 / 1024) +#define to_gb(x) ((x) / 1024 / 1024 / 1024) + +void format_memory_usage(char* out_buf, uint32_t usage) { + if (is_gb(usage)) { + sprintf(out_buf, "%d,%d GB", to_gb(usage), to_mb(usage) % 1024); + } else if (is_mb(usage)) { + sprintf(out_buf, "%d,%d MB", to_mb(usage), to_kb(usage) % 1024); + } else if (is_kb(usage)) { + sprintf(out_buf, "%d,%d KB", to_kb(usage), usage % 1024); + } else { + sprintf(out_buf, "%d B", usage); + } +} + +int main(int argc, char** argv) { + wm_client_t client; + wm_client_init(&client); + + wm_client_set_title(&client, "System Control"); + wm_client_set_title_bar_color(&client, 0x553366); + wm_client_set_bg_color(&client, BG_COLOR); + + int cw = wm_client_width(&client); + + ui_button_t reboot_btn, shutdown_btn; + ui_button_init(&reboot_btn, cw - 80, 2, 76, 22, "Reboot"); + reboot_btn.bg_color = 0x554400; + reboot_btn.hover_color = 0x887700; + ui_button_init(&shutdown_btn, cw - 80, 28, 76, 22, "Shutdown"); + shutdown_btn.bg_color = 0x660000; + shutdown_btn.hover_color = 0xaa2222; + + while (!wm_client_should_close(&client)) { + wm_event_t evt; + while (wm_client_poll_event(&client, &evt)) { + ui_button_update(&reboot_btn, &evt); + ui_button_update(&shutdown_btn, &evt); + } + + if (ui_button_clicked(&reboot_btn)) { + env(SYS_PWR_RESET_ID); + } + if (ui_button_clicked(&shutdown_btn)) { + env(SYS_PWR_SHUTDOWN_ID); + } + + int w = wm_client_width(&client); + int h = wm_client_height(&client); + wm_client_fill_rect(&client, 0, 0, w, h, BG_COLOR); + + ui_button_draw(&reboot_btn, &client); + ui_button_draw(&shutdown_btn, &client); + + char timebuf[128] = { 0 }; + unix_time_to_string(time(NULL), timebuf); + + char timemsbuf[32] = { 0 }; + sprintf(timemsbuf, "%d ms", time_ms()); + + uint32_t mem_free, mem_used; + raminfo(&mem_free, &mem_used); + + char total_str[32] = { 0 }; + char free_str[32] = { 0 }; + char used_str[32] = { 0 }; + format_memory_usage(total_str, mem_free + mem_used); + format_memory_usage(free_str, mem_free); + format_memory_usage(used_str, mem_used); + + int line = 0; + wm_client_draw_string(&client, 4, line * 16 + 4, "TIME:", 0xaaaaff, BG_COLOR); line++; + wm_client_draw_string(&client, 4, line * 16 + 4, timebuf, 0xffffff, BG_COLOR); line++; + line++; + wm_client_draw_string(&client, 4, line * 16 + 4, "TIMEMS:", 0xaaaaff, BG_COLOR); line++; + wm_client_draw_string(&client, 4, line * 16 + 4, timemsbuf, 0xffffff, BG_COLOR); line++; + line++; + wm_client_draw_string(&client, 4, line * 16 + 4, "TOTAL:", 0xaaaaff, BG_COLOR); line++; + wm_client_draw_string(&client, 4, line * 16 + 4, total_str, 0xffffff, BG_COLOR); line++; + wm_client_draw_string(&client, 4, line * 16 + 4, "FREE:", 0xaaaaff, BG_COLOR); line++; + wm_client_draw_string(&client, 4, line * 16 + 4, free_str, 0xffffff, BG_COLOR); line++; + wm_client_draw_string(&client, 4, line * 16 + 4, "USED:", 0xaaaaff, BG_COLOR); line++; + wm_client_draw_string(&client, 4, line * 16 + 4, used_str, 0xffffff, BG_COLOR); + + wm_client_flush(&client); + yield(); + } + + return 0; +} diff --git a/user/desktop/taskmgr/Makefile b/user/desktop/taskmgr/Makefile new file mode 100644 index 00000000..44e7c7e2 --- /dev/null +++ b/user/desktop/taskmgr/Makefile @@ -0,0 +1,7 @@ +PROGRAM = desktop-taskmgr.elf + +include ../../program.mk + +CFLAGS += -I../libdesktop/include + +LINK = -ldesktop diff --git a/user/desktop/taskmgr/main.c b/user/desktop/taskmgr/main.c new file mode 100644 index 00000000..4d615d35 --- /dev/null +++ b/user/desktop/taskmgr/main.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include + +#define BG_COLOR 0x1a1a2e + +int main(int argc, char** argv) { + wm_client_t client; + wm_client_init(&client); + + wm_client_set_title(&client, "Task Manager"); + wm_client_set_title_bar_color(&client, 0x884422); + wm_client_set_bg_color(&client, BG_COLOR); + + task_list_t tasks[64] = { 0 }; + int count = 0; + + while (!wm_client_should_close(&client)) { + wm_event_t evt; + while (wm_client_poll_event(&client, &evt)) { + } + + int w = wm_client_width(&client); + int h = wm_client_height(&client); + wm_client_fill_rect(&client, 0, 0, w, h, BG_COLOR); + + wm_client_draw_string(&client, 4, 2, "PID", 0xaaaaff, BG_COLOR); + wm_client_draw_string(&client, 60, 2, "TERM", 0xaaaaff, BG_COLOR); + wm_client_draw_string(&client, 120, 2, "NAME", 0xaaaaff, BG_COLOR); + wm_client_draw_line(&client, 0, 18, w, 18, 0x444444); + + count = get_task_list(tasks, 64); + + int y = 20; + for (int i = 0; i < count && y + 16 < h - 30; i++) { + char pid_str[16]; + sprintf(pid_str, "%d", tasks[i].pid); + char term_str[16]; + sprintf(term_str, "%d", tasks[i].term); + + wm_client_draw_string(&client, 4, y, pid_str, 0xffffff, BG_COLOR); + wm_client_draw_string(&client, 60, y, term_str, 0xffffff, BG_COLOR); + wm_client_draw_string(&client, 120, y, tasks[i].name, 0xffffff, BG_COLOR); + + + y += 18; + } + + uint32_t mem_free, mem_used; + raminfo(&mem_free, &mem_used); + uint32_t total = mem_free + mem_used; + + int bar_y = h - 24; + int bar_w = w - 8; + wm_client_fill_rect(&client, 4, bar_y, bar_w, 16, 0x333333); + + if (total > 0) { + int used_w = (int)((uint64_t)mem_used * bar_w / total); + wm_client_fill_rect(&client, 4, bar_y, used_w, 16, 0x44aa44); + } + + char mem_str[64]; + sprintf(mem_str, "Memory: %d / %d KB", mem_used / 1024, total / 1024); + wm_client_draw_string(&client, 8, bar_y, mem_str, 0xffffff, 0x333333); + + wm_client_flush(&client); + yield(); + } + + return 0; +} diff --git a/user/desktop/terminal/Makefile b/user/desktop/terminal/Makefile new file mode 100644 index 00000000..02b78796 --- /dev/null +++ b/user/desktop/terminal/Makefile @@ -0,0 +1,7 @@ +PROGRAM = desktop-terminal.elf + +include ../../program.mk + +CFLAGS += -I../libdesktop/include + +LINK = -ldesktop diff --git a/user/base/desktop/windows/terminal/argv.c b/user/desktop/terminal/argv.c similarity index 99% rename from user/base/desktop/windows/terminal/argv.c rename to user/desktop/terminal/argv.c index b35feaf6..50baad2c 100644 --- a/user/base/desktop/windows/terminal/argv.c +++ b/user/desktop/terminal/argv.c @@ -1,4 +1,4 @@ -#include "argv.h" +#include #include #include @@ -102,7 +102,6 @@ char* process_line_simple(char* command) { if (start > 0) { memmove(command, command + start, new_len); } - command[new_len] = '\0'; return command; } diff --git a/user/base/desktop/windows/terminal/commands.c b/user/desktop/terminal/commands.c similarity index 89% rename from user/base/desktop/windows/terminal/commands.c rename to user/desktop/terminal/commands.c index 69e0f160..55345065 100644 --- a/user/base/desktop/windows/terminal/commands.c +++ b/user/desktop/terminal/commands.c @@ -1,19 +1,16 @@ -#include "commands.h" -#include "desktop.h" -#include "output.h" -#include "argv.h" +#include +#include +#include #include #include -#include #include #include #include #include #include - -#define PIPE_BUFFER_SIZE 65536 +#include void builtin_cd(terminal_state_t* st, char** argv) { int argc = 0; @@ -60,16 +57,6 @@ void builtin_pwd(terminal_state_t* st) { term_printf(st, "%s\n", cwd); } -void builtin_open(terminal_state_t* st, char* path) { - char path_buf[256] = { 0 }; - if (!resolve(path, path_buf)) { - term_printf(st, "Could not resolve path: %s\n", path); - return; - } - - desktop_open_file(path_buf); -} - void builtin_export(terminal_state_t* st, char* cmd) { char* env_str = read_env_vars(cmd); if (strlen(env_str) <= 7) { @@ -120,8 +107,7 @@ void builtin_layout(terminal_state_t* st, char* cmd) { } } - -void set_wait_and_yield_term() { +void set_wait_and_yield_term(void) { set_env(SYS_ENV_TASK_SET_WAIT_TIME, (void*)100); yield(); } @@ -167,7 +153,6 @@ int spawn_and_capture(terminal_state_t* st, char** argv) { return pid; } - bool execute_command(terminal_state_t* st, char* command) { char* cmd = process_line_simple(command); if (cmd[0] == '\0') { @@ -192,10 +177,6 @@ bool execute_command(terminal_state_t* st, char* command) { st->output_len = 0; st->output_buf[0] = '\0'; st->scroll_offset = 0; - } else if (strncmp(cmd, "open ", 5) == 0) { - char* env_cmd = read_env_vars(cmd); - builtin_open(st, env_cmd + 5); - free(env_cmd); } else { char* env_cmd = read_env_vars(cmd); char** argv = argv_split_simple(env_cmd); diff --git a/user/base/desktop/windows/terminal/argv.h b/user/desktop/terminal/include/argv.h similarity index 90% rename from user/base/desktop/windows/terminal/argv.h rename to user/desktop/terminal/include/argv.h index 4265bac4..47e2a573 100644 --- a/user/base/desktop/windows/terminal/argv.h +++ b/user/desktop/terminal/include/argv.h @@ -1,7 +1,5 @@ #pragma once -#include - char* read_env_vars(char* in); char** argv_split_simple(char* str); char** argv_env_process_simple(char** in); diff --git a/user/base/desktop/windows/terminal/commands.h b/user/desktop/terminal/include/commands.h similarity index 81% rename from user/base/desktop/windows/terminal/commands.h rename to user/desktop/terminal/include/commands.h index 8553888a..5b77a945 100644 --- a/user/base/desktop/windows/terminal/commands.h +++ b/user/desktop/terminal/include/commands.h @@ -1,6 +1,6 @@ #pragma once -#include "terminal.h" +#include #include bool execute_command(terminal_state_t* st, char* command); diff --git a/user/base/desktop/windows/terminal/output.h b/user/desktop/terminal/include/output.h similarity index 91% rename from user/base/desktop/windows/terminal/output.h rename to user/desktop/terminal/include/output.h index 95605c09..0a4296a0 100644 --- a/user/base/desktop/windows/terminal/output.h +++ b/user/desktop/terminal/include/output.h @@ -1,6 +1,6 @@ #pragma once -#include "terminal.h" +#include void term_append(terminal_state_t* st, const char* text, int len); void term_puts(terminal_state_t* st, const char* str); diff --git a/user/desktop/terminal/include/terminal.h b/user/desktop/terminal/include/terminal.h new file mode 100644 index 00000000..5147ddb2 --- /dev/null +++ b/user/desktop/terminal/include/terminal.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +#define TERM_OUTPUT_SIZE (64 * 1024) +#define TERM_INPUT_SIZE 2048 +#define TERM_HISTORY_MAX 64 +#define PIPE_BUFFER_SIZE 65536 + +typedef struct { + char* output_buf; + int output_len; + + char input_buf[TERM_INPUT_SIZE + 1]; + int input_len; + + char** history; + int history_size; + int history_index; + + int scroll_offset; + + char** envp; +} terminal_state_t; diff --git a/user/desktop/terminal/main.c b/user/desktop/terminal/main.c new file mode 100644 index 00000000..cd5c5cca --- /dev/null +++ b/user/desktop/terminal/main.c @@ -0,0 +1,255 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define BG_COLOR 0x1a1a2e +#define TEXT_COLOR 0xCCCCCC +#define INPUT_COLOR 0xFFFFFF +#define CURSOR_COLOR 0xCCCCCC + +#define CHAR_W 8 +#define CHAR_H 16 + +void history_add(terminal_state_t* st, const char* cmd) { + if (st->history_size == 0) { + st->history = malloc(sizeof(char*)); + } else if (st->history_size < TERM_HISTORY_MAX) { + st->history = realloc(st->history, sizeof(char*) * (st->history_size + 1)); + } else { + free(st->history[0]); + memmove(st->history, st->history + 1, sizeof(char*) * (TERM_HISTORY_MAX - 1)); + st->history_size--; + } + st->history[st->history_size] = strdup(cmd); + st->history_size++; + st->history_index = st->history_size; +} + +void history_recall(terminal_state_t* st) { + if (st->history_index < 0 || st->history_index >= st->history_size) { + return; + } + + memset(st->input_buf, 0, TERM_INPUT_SIZE + 1); + strcpy(st->input_buf, st->history[st->history_index]); + st->input_len = strlen(st->input_buf); +} + +void terminal_draw(wm_client_t* client, terminal_state_t* st) { + int content_w = wm_client_width(client); + int content_h = wm_client_height(client); + int cols = content_w / CHAR_W; + int rows = content_h / CHAR_H; + + if (cols <= 0 || rows <= 0) { + return; + } + + int total_lines = 1; + int col = 0; + + for (int i = 0; i < st->output_len; i++) { + if (st->output_buf[i] == '\n') { + total_lines++; + col = 0; + } else { + col++; + if (col >= cols) { + total_lines++; + col = 0; + } + } + } + + for (int i = 0; i < st->input_len; i++) { + if (st->input_buf[i] == '\n') { + total_lines++; + col = 0; + } else { + col++; + if (col >= cols) { + total_lines++; + col = 0; + } + } + } + + int start_line = total_lines - rows + st->scroll_offset; + if (start_line < 0) { + start_line = 0; + } + + + wm_client_fill_rect(client, 0, 0, content_w, content_h, BG_COLOR); + + int col_pos = 0; + int cur_line = 0; + + #define EMIT_CHAR(ch, color_val) do { \ + if (cur_line >= start_line && cur_line < start_line + rows) { \ + int dx = col_pos * CHAR_W; \ + int dy = (cur_line - start_line) * CHAR_H; \ + wm_client_draw_char(client, dx, dy, ch, color_val, BG_COLOR); \ + } \ + } while (0) + + for (int i = 0; i < st->output_len; i++) { + char c = st->output_buf[i]; + if (c == '\n') { + cur_line++; + col_pos = 0; + } else { + EMIT_CHAR(c, TEXT_COLOR); + col_pos++; + if (col_pos >= cols) { + cur_line++; + col_pos = 0; + } + } + } + + for (int i = 0; i < st->input_len; i++) { + char c = st->input_buf[i]; + if (c == '\n') { + cur_line++; + col_pos = 0; + } else { + EMIT_CHAR(c, INPUT_COLOR); + col_pos++; + if (col_pos >= cols) { + cur_line++; + col_pos = 0; + } + } + } + + #undef EMIT_CHAR + + if (cur_line >= start_line && cur_line < start_line + rows) { + int cx = col_pos * CHAR_W; + int cy = (cur_line - start_line) * CHAR_H; + wm_client_fill_rect(client, cx, cy, CHAR_W - 4, CHAR_H - 4, CURSOR_COLOR); + } +} + +int main(int argc, char** argv, char** envp) { + wm_client_t client; + wm_client_init(&client); + + wm_client_set_title(&client, "Terminal"); + wm_client_set_title_bar_color(&client, 0x16213e); + wm_client_set_bg_color(&client, BG_COLOR); + + terminal_state_t st; + memset(&st, 0, sizeof(st)); + st.output_buf = malloc(TERM_OUTPUT_SIZE); + memset(st.output_buf, 0, TERM_OUTPUT_SIZE); + st.output_len = 0; + st.input_len = 0; + st.history = NULL; + st.history_size = 0; + st.history_index = 0; + st.scroll_offset = 0; + st.envp = envp; + + term_print_prompt(&st); + + terminal_draw(&client, &st); + wm_client_flush(&client); + + while (!wm_client_should_close(&client)) { + wm_event_t evt; + int dirty = 0; + + while (wm_client_poll_event(&client, &evt)) { + if (evt.type == WM_EVENT_KEY_PRESS) { + char key = evt.key; + + if (key == '\n') { + term_append(&st, st.input_buf, st.input_len); + term_puts(&st, "\n"); + + if (st.input_len > 0) { + char cmd_copy[TERM_INPUT_SIZE + 1]; + memcpy(cmd_copy, st.input_buf, st.input_len + 1); + history_add(&st, st.input_buf); + + bool keep_going = execute_command(&st, cmd_copy); + if (!keep_going) { + goto done; + } + } + + memset(st.input_buf, 0, TERM_INPUT_SIZE + 1); + st.input_len = 0; + st.scroll_offset = 0; + term_print_prompt(&st); + dirty = 1; + + } else if (key == '\b') { + if (st.input_len > 0) { + st.input_len--; + st.input_buf[st.input_len] = '\0'; + dirty = 1; + } + } else if (key == 27) { + memset(st.input_buf, 0, TERM_INPUT_SIZE + 1); + st.input_len = 0; + dirty = 1; + } else if (key >= 0x20 && key <= 0x7E) { + if (st.input_len < TERM_INPUT_SIZE) { + st.input_buf[st.input_len++] = key; + st.input_buf[st.input_len] = '\0'; + dirty = 1; + } + } + } else if (evt.type == WM_EVENT_ARROW_KEY) { + if (evt.arrow == WM_ARROW_UP) { + if (st.history_index > 0) { + st.history_index--; + history_recall(&st); + dirty = 1; + } + } else if (evt.arrow == WM_ARROW_DOWN) { + if (st.history_index < st.history_size - 1) { + st.history_index++; + history_recall(&st); + } else if (st.history_index == st.history_size - 1) { + st.history_index = st.history_size; + memset(st.input_buf, 0, TERM_INPUT_SIZE + 1); + st.input_len = 0; + } + dirty = 1; + } + } + } + + if (dirty) { + terminal_draw(&client, &st); + wm_client_flush(&client); + } + + yield(); + } + +done: + if (st.output_buf) { + free(st.output_buf); + } + + if (st.history) { + for (int i = 0; i < st.history_size; i++) { + free(st.history[i]); + } + + free(st.history); + } + return 0; +} diff --git a/user/base/desktop/windows/terminal/output.c b/user/desktop/terminal/output.c similarity index 90% rename from user/base/desktop/windows/terminal/output.c rename to user/desktop/terminal/output.c index ff9d5dd7..a76b1f14 100644 --- a/user/base/desktop/windows/terminal/output.c +++ b/user/desktop/terminal/output.c @@ -1,12 +1,10 @@ -#include "output.h" +#include #include #include #include -#define GET_CWD(cwd) char cwd[64] = { 0 }; set_env(SYS_GET_PWD_ID, cwd) - void term_append(terminal_state_t* st, const char* text, int len) { if (len <= 0) { return; @@ -42,6 +40,7 @@ void term_printf(terminal_state_t* st, const char* fmt, ...) { } void term_print_prompt(terminal_state_t* st) { - GET_CWD(cwd); + char cwd[64] = { 0 }; + set_env(SYS_GET_PWD_ID, cwd); term_printf(st, "\nshell %s > ", cwd); } diff --git a/user/libraries/libc/include/non-standard/sys/message.h b/user/libraries/libc/include/non-standard/sys/message.h index 3c397df8..f211a554 100644 --- a/user/libraries/libc/include/non-standard/sys/message.h +++ b/user/libraries/libc/include/non-standard/sys/message.h @@ -11,6 +11,7 @@ #define TOPIC_SERVICE_STOP 7 #define TOPIC_SERVICE_RESTART 8 #define TOPIC_SERVICE_OP_REPLY 9 +#define TOPIC_DESKTOP_OPEN_FILE 10 #define SERVICE_STATUS_RUNNING 0 #define SERVICE_STATUS_STOPPED 1