mirror of
https://github.com/JannisHeydemann/BoredOS.git
synced 2026-05-30 02:16:58 +00:00
V1.51/2.5.0
This update adds support for IDE and SATA drives (that are formatted to fat32) this allows for persistent storage over drives and gives the system some use other than just a showcase :D The user can switch between drives in the explorer by clicking on the dropdown and then clicking any drive letter ( A:, B: .. C:) this list dynamically expands on how many drives there are in the system. In the cmd the user just types A:, B:, C: etc to switch to that drive.
This commit is contained in:
@@ -14,8 +14,8 @@ static void about_paint(Window *win) {
|
||||
|
||||
// Version info
|
||||
draw_string(offset_x, offset_y + 105, "BoredOS 'Panda'", COLOR_BLACK);
|
||||
draw_string(offset_x, offset_y + 120, "BoredOS Version 1.50", COLOR_BLACK);
|
||||
draw_string(offset_x, offset_y + 135, "Kernel Version 2.4.0", COLOR_BLACK);
|
||||
draw_string(offset_x, offset_y + 120, "BoredOS Version 1.51", COLOR_BLACK);
|
||||
draw_string(offset_x, offset_y + 135, "Kernel Version 2.5.0", COLOR_BLACK);
|
||||
|
||||
// Copyright
|
||||
draw_string(offset_x, offset_y + 150, "(C) 2026 boreddevnl.", COLOR_BLACK);
|
||||
|
||||
546
src/kernel/cmd.c
546
src/kernel/cmd.c
@@ -6,6 +6,7 @@
|
||||
#include "notepad.h"
|
||||
#include "calculator.h"
|
||||
#include "fat32.h"
|
||||
#include "disk.h"
|
||||
#include "cli_apps/cli_apps.h"
|
||||
#include "licensewr.h"
|
||||
#include <stddef.h>
|
||||
@@ -21,7 +22,6 @@
|
||||
#define CMD_ROWS 41
|
||||
#define LINE_HEIGHT 10
|
||||
#define CHAR_WIDTH 8
|
||||
#define PROMPT "> "
|
||||
|
||||
#define COLOR_RED 0xFFFF0000
|
||||
|
||||
@@ -39,6 +39,12 @@ typedef enum {
|
||||
MODE_PAGER
|
||||
} CmdMode;
|
||||
|
||||
// CMD Window State (per-window context)
|
||||
typedef struct {
|
||||
char current_drive;
|
||||
char current_dir[256];
|
||||
} CmdState;
|
||||
|
||||
// --- State ---
|
||||
Window win_cmd;
|
||||
|
||||
@@ -47,6 +53,7 @@ static CharCell screen_buffer[CMD_ROWS][CMD_COLS];
|
||||
static int cursor_row = 0;
|
||||
static int cursor_col = 0;
|
||||
static uint32_t current_color = COLOR_LTGRAY;
|
||||
static CmdState *cmd_state = NULL; // Will be set in cmd_init
|
||||
|
||||
// Pager State
|
||||
static CmdMode current_mode = MODE_SHELL;
|
||||
@@ -139,8 +146,18 @@ static void cmd_history_add(const char *cmd) {
|
||||
if (history_len < HISTORY_MAX) history_len++;
|
||||
}
|
||||
|
||||
static void cmd_print_prompt(void) {
|
||||
char buf[5];
|
||||
buf[0] = cmd_state ? cmd_state->current_drive : 'A';
|
||||
buf[1] = ':';
|
||||
buf[2] = '>';
|
||||
buf[3] = ' ';
|
||||
buf[4] = 0;
|
||||
cmd_write(buf);
|
||||
}
|
||||
|
||||
static void cmd_clear_line_content(void) {
|
||||
int prompt_len = cmd_strlen(PROMPT);
|
||||
int prompt_len = 4; // "A:> "
|
||||
for (int i = prompt_len; i < CMD_COLS; i++) {
|
||||
screen_buffer[cursor_row][i].c = ' ';
|
||||
screen_buffer[cursor_row][i].color = current_color;
|
||||
@@ -327,6 +344,204 @@ void pager_set_mode(void) {
|
||||
}
|
||||
|
||||
// Internal LS command to avoid stack overflow in external module
|
||||
static void cmd_update_dir(const char *path); // Forward declaration
|
||||
|
||||
static void internal_cmd_pwd(char *args) {
|
||||
(void)args;
|
||||
if (cmd_state) {
|
||||
char drive_str[3];
|
||||
drive_str[0] = cmd_state->current_drive;
|
||||
drive_str[1] = ':';
|
||||
drive_str[2] = 0;
|
||||
cmd_write(drive_str);
|
||||
cmd_write(cmd_state->current_dir);
|
||||
} else {
|
||||
char cwd[256];
|
||||
fat32_get_current_dir(cwd, sizeof(cwd));
|
||||
cmd_write(cwd);
|
||||
}
|
||||
cmd_write("\n");
|
||||
}
|
||||
|
||||
static void internal_cmd_cd(char *args) {
|
||||
// Handle cd with proper cmd_state context
|
||||
if (!args || !args[0]) {
|
||||
// No argument - show current directory
|
||||
if (cmd_state) {
|
||||
char drive_str[3];
|
||||
drive_str[0] = cmd_state->current_drive;
|
||||
drive_str[1] = ':';
|
||||
drive_str[2] = 0;
|
||||
cmd_write(drive_str);
|
||||
cmd_write(cmd_state->current_dir);
|
||||
cmd_write("\n");
|
||||
} else {
|
||||
char cwd[256];
|
||||
fat32_get_current_dir(cwd, sizeof(cwd));
|
||||
cmd_write("Current directory: ");
|
||||
cmd_write(cwd);
|
||||
cmd_write("\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse argument (remove trailing spaces/tabs)
|
||||
char path[256];
|
||||
int i = 0;
|
||||
while (args[i] && args[i] != ' ' && args[i] != '\t') {
|
||||
path[i] = args[i];
|
||||
i++;
|
||||
}
|
||||
path[i] = 0;
|
||||
|
||||
// For cmd_state, we need to build and validate the full path
|
||||
if (cmd_state) {
|
||||
// Build full path for validation
|
||||
char full_path[512] = {0};
|
||||
if (path[1] == ':') {
|
||||
// Has drive letter
|
||||
cmd_strcpy(full_path, path);
|
||||
} else if (path[0] == '/') {
|
||||
// Absolute path
|
||||
full_path[0] = cmd_state->current_drive;
|
||||
full_path[1] = ':';
|
||||
int j = 2;
|
||||
int k = 0;
|
||||
while (path[k] && j < 509) {
|
||||
full_path[j++] = path[k++];
|
||||
}
|
||||
full_path[j] = 0;
|
||||
} else {
|
||||
// Relative path - resolve from current directory
|
||||
full_path[0] = cmd_state->current_drive;
|
||||
full_path[1] = ':';
|
||||
int j = 2;
|
||||
|
||||
// Copy current directory
|
||||
const char *dir = cmd_state->current_dir;
|
||||
while (*dir && j < 509) {
|
||||
full_path[j++] = *dir++;
|
||||
}
|
||||
|
||||
// Add separator if needed
|
||||
if (j > 2 && full_path[j-1] != '/') {
|
||||
full_path[j++] = '/';
|
||||
}
|
||||
|
||||
// Add path argument
|
||||
int k = 0;
|
||||
while (path[k] && j < 509) {
|
||||
full_path[j++] = path[k++];
|
||||
}
|
||||
full_path[j] = 0;
|
||||
}
|
||||
|
||||
// Validate directory exists
|
||||
if (fat32_is_directory(full_path)) {
|
||||
// Normalize the path to resolve .. and .
|
||||
char normalized_path[512];
|
||||
fat32_normalize_path(full_path, normalized_path);
|
||||
|
||||
cmd_update_dir(normalized_path);
|
||||
cmd_write("Changed to: ");
|
||||
char drive_str[3];
|
||||
drive_str[0] = cmd_state->current_drive;
|
||||
drive_str[1] = ':';
|
||||
drive_str[2] = 0;
|
||||
cmd_write(drive_str);
|
||||
cmd_write(cmd_state->current_dir);
|
||||
cmd_write("\n");
|
||||
} else {
|
||||
cmd_write("Error: Cannot change to directory: ");
|
||||
cmd_write(path);
|
||||
cmd_write("\n");
|
||||
}
|
||||
} else {
|
||||
// Fallback to global state if no cmd_state
|
||||
if (fat32_chdir(path)) {
|
||||
char cwd[256];
|
||||
fat32_get_current_dir(cwd, sizeof(cwd));
|
||||
cmd_write("Changed to: ");
|
||||
cmd_write(cwd);
|
||||
cmd_write("\n");
|
||||
} else {
|
||||
cmd_write("Error: Cannot change to directory: ");
|
||||
cmd_write(path);
|
||||
cmd_write("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void internal_cmd_txtedit(char *args) {
|
||||
// Parse the file path argument
|
||||
char filepath[256];
|
||||
int i = 0;
|
||||
|
||||
// Skip leading whitespace
|
||||
while (args && args[i] && (args[i] == ' ' || args[i] == '\t')) {
|
||||
i++;
|
||||
}
|
||||
|
||||
// Extract filepath (args now includes drive prefix from cmd_exec_single)
|
||||
int j = 0;
|
||||
while (args && args[i] && args[i] != ' ' && args[i] != '\t' && j < 255) {
|
||||
filepath[j++] = args[i++];
|
||||
}
|
||||
filepath[j] = 0;
|
||||
|
||||
// If no filepath provided, show usage
|
||||
if (j == 0) {
|
||||
cmd_write("Usage: txtedit <filename>\n");
|
||||
cmd_write("Example: txtedit myfile.txt\n");
|
||||
cmd_write(" txtedit /document.txt\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalize the path (filepath already includes drive from cmd_exec_single)
|
||||
char normalized_path[256];
|
||||
fat32_normalize_path(filepath, normalized_path);
|
||||
|
||||
// Extract drive from normalized path to set it as current temporarily
|
||||
char drive = 'A';
|
||||
if (normalized_path[1] == ':') {
|
||||
drive = normalized_path[0];
|
||||
if (drive >= 'a' && drive <= 'z') drive -= 32;
|
||||
}
|
||||
|
||||
// Set global drive temporarily to match file's drive
|
||||
char saved_drive = fat32_get_current_drive();
|
||||
fat32_change_drive(drive);
|
||||
|
||||
// Open the file in the GUI editor
|
||||
extern void editor_open_file(const char *filename);
|
||||
extern Window win_editor;
|
||||
editor_open_file(normalized_path);
|
||||
|
||||
// Restore the drive
|
||||
fat32_change_drive(saved_drive);
|
||||
|
||||
// Make editor window visible and focused, bring to front
|
||||
extern Window win_explorer;
|
||||
extern Window win_cmd;
|
||||
extern Window win_notepad;
|
||||
extern Window win_calculator;
|
||||
|
||||
win_editor.visible = true;
|
||||
win_editor.focused = true;
|
||||
|
||||
// Calculate max z_index to bring window to front
|
||||
int max_z = 0;
|
||||
if (win_explorer.z_index > max_z) max_z = win_explorer.z_index;
|
||||
if (win_cmd.z_index > max_z) max_z = win_cmd.z_index;
|
||||
if (win_notepad.z_index > max_z) max_z = win_notepad.z_index;
|
||||
if (win_calculator.z_index > max_z) max_z = win_calculator.z_index;
|
||||
win_editor.z_index = max_z + 1;
|
||||
|
||||
cmd_write("Opening: ");
|
||||
cmd_write(normalized_path);
|
||||
cmd_write("\n");
|
||||
}
|
||||
|
||||
static void internal_cmd_ls(char *args) {
|
||||
char path[256];
|
||||
if (args && *args) {
|
||||
@@ -386,8 +601,8 @@ static const CommandEntry commands[] = {
|
||||
{"math", cli_cmd_math},
|
||||
{"MAN", cli_cmd_man},
|
||||
{"man", cli_cmd_man},
|
||||
{"TXTEDIT", cli_cmd_txtedit},
|
||||
{"txtedit", cli_cmd_txtedit},
|
||||
{"TXTEDIT", internal_cmd_txtedit},
|
||||
{"txtedit", internal_cmd_txtedit},
|
||||
{"UPTIME", cli_cmd_uptime},
|
||||
{"uptime", cli_cmd_uptime},
|
||||
{"BEEP", cli_cmd_beep},
|
||||
@@ -405,10 +620,10 @@ static const CommandEntry commands[] = {
|
||||
{"EXIT", cli_cmd_exit},
|
||||
{"exit", cli_cmd_exit},
|
||||
// Filesystem Commands
|
||||
{"CD", cli_cmd_cd},
|
||||
{"cd", cli_cmd_cd},
|
||||
{"PWD", cli_cmd_pwd},
|
||||
{"pwd", cli_cmd_pwd},
|
||||
{"CD", internal_cmd_cd},
|
||||
{"cd", internal_cmd_cd},
|
||||
{"PWD", internal_cmd_pwd},
|
||||
{"pwd", internal_cmd_pwd},
|
||||
{"LS", internal_cmd_ls},
|
||||
{"ls", internal_cmd_ls},
|
||||
{"MKDIR", cli_cmd_mkdir},
|
||||
@@ -468,6 +683,88 @@ static const CommandEntry commands[] = {
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
// Helper to build full path with cmd window's drive context
|
||||
static void cmd_build_full_path(const char *relative_path, char *full_path) {
|
||||
if (!cmd_state) {
|
||||
if (relative_path[0]) {
|
||||
cmd_strcpy(full_path, relative_path);
|
||||
} else {
|
||||
full_path[0] = 'A';
|
||||
full_path[1] = ':';
|
||||
full_path[2] = '/';
|
||||
full_path[3] = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If path already has drive letter, use it as-is
|
||||
if (relative_path && relative_path[1] == ':') {
|
||||
cmd_strcpy(full_path, relative_path);
|
||||
return;
|
||||
}
|
||||
|
||||
// Build path with cmd_state's drive and directory
|
||||
int i = 0;
|
||||
full_path[i++] = cmd_state->current_drive;
|
||||
full_path[i++] = ':';
|
||||
|
||||
// Add current directory
|
||||
const char *dir = cmd_state->current_dir;
|
||||
while (*dir && i < 509) {
|
||||
full_path[i++] = *dir++;
|
||||
}
|
||||
|
||||
// Add path argument
|
||||
if (relative_path && relative_path[0]) {
|
||||
if (i > 2 && full_path[i-1] != '/') {
|
||||
full_path[i++] = '/';
|
||||
}
|
||||
const char *p = relative_path;
|
||||
while (*p && i < 509) {
|
||||
full_path[i++] = *p++;
|
||||
}
|
||||
}
|
||||
|
||||
full_path[i] = 0;
|
||||
}
|
||||
|
||||
// Helper to sync cmd window directory after cd
|
||||
static void cmd_update_dir(const char *path) {
|
||||
if (!cmd_state || !path) return;
|
||||
|
||||
// Extract drive if provided
|
||||
const char *p = path;
|
||||
char drive = cmd_state->current_drive;
|
||||
if (p[0] && p[1] == ':') {
|
||||
drive = p[0];
|
||||
if (drive >= 'a' && drive <= 'z') drive -= 32;
|
||||
p += 2;
|
||||
}
|
||||
|
||||
// Update drive
|
||||
cmd_state->current_drive = drive;
|
||||
|
||||
// Update directory
|
||||
if (*p) {
|
||||
// Remove trailing slashes and copy
|
||||
int len = 0;
|
||||
while (p[len]) len++;
|
||||
while (len > 0 && p[len-1] == '/') len--;
|
||||
|
||||
if (len == 0) {
|
||||
cmd_state->current_dir[0] = '/';
|
||||
cmd_state->current_dir[1] = 0;
|
||||
} else {
|
||||
for (int i = 0; i < len && i < 255; i++) {
|
||||
cmd_state->current_dir[i] = p[i];
|
||||
}
|
||||
cmd_state->current_dir[len] = 0;
|
||||
}
|
||||
} else {
|
||||
cmd_state->current_dir[0] = '/';
|
||||
cmd_state->current_dir[1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const char* find_pipe(const char* cmd) {
|
||||
while (*cmd) {
|
||||
@@ -483,8 +780,42 @@ static void cmd_exec_single(char *cmd) {
|
||||
while (*cmd == ' ') cmd++;
|
||||
if (!*cmd) return;
|
||||
|
||||
// Check for drive switch (e.g. "A:", "B:")
|
||||
if (cmd[0] && cmd[1] == ':' && cmd[2] == 0) {
|
||||
char letter = cmd[0];
|
||||
if (letter >= 'a' && letter <= 'z') letter -= 32;
|
||||
|
||||
// Check if drive exists (don't change global, just check)
|
||||
if (disk_get_by_letter(letter)) {
|
||||
// Update cmd window's drive, not global
|
||||
if (cmd_state) {
|
||||
cmd_state->current_drive = letter;
|
||||
cmd_state->current_dir[0] = '/';
|
||||
cmd_state->current_dir[1] = 0;
|
||||
}
|
||||
} else {
|
||||
cmd_write("Invalid drive.\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd[0] == '.' && cmd[1] == '/') {
|
||||
char *filename = cmd + 2;
|
||||
|
||||
// Build full path with drive context
|
||||
char full_exec_path[512];
|
||||
if (cmd_state && cmd_state->current_drive != 'A') {
|
||||
full_exec_path[0] = cmd_state->current_drive;
|
||||
full_exec_path[1] = ':';
|
||||
int i = 2;
|
||||
const char *p = filename;
|
||||
while (*p && i < 509) {
|
||||
full_exec_path[i++] = *p++;
|
||||
}
|
||||
full_exec_path[i] = 0;
|
||||
filename = full_exec_path;
|
||||
}
|
||||
|
||||
FAT32_FileHandle *fh = fat32_open(filename, "r");
|
||||
if (fh) {
|
||||
|
||||
@@ -521,6 +852,168 @@ static void cmd_exec_single(char *cmd) {
|
||||
args++; // Point to start of args
|
||||
}
|
||||
|
||||
// For file system commands, prepend drive context to path args if on different drive
|
||||
// Build full path with drive letter for commands that take paths
|
||||
char full_path_arg[512] = {0};
|
||||
bool is_cd_command = (cmd_strcmp(cmd, "cd") == 0 || cmd_strcmp(cmd, "CD") == 0);
|
||||
bool is_ls_command = (cmd_strcmp(cmd, "ls") == 0 || cmd_strcmp(cmd, "LS") == 0);
|
||||
bool is_echo_command = (cmd_strcmp(cmd, "echo") == 0 || cmd_strcmp(cmd, "ECHO") == 0);
|
||||
|
||||
if (cmd_state) {
|
||||
// Check if this is a command that takes a path argument
|
||||
bool needs_path = (is_ls_command ||
|
||||
is_cd_command ||
|
||||
cmd_strcmp(cmd, "mkdir") == 0 || cmd_strcmp(cmd, "MKDIR") == 0 ||
|
||||
cmd_strcmp(cmd, "rm") == 0 || cmd_strcmp(cmd, "RM") == 0 ||
|
||||
cmd_strcmp(cmd, "cat") == 0 || cmd_strcmp(cmd, "CAT") == 0 ||
|
||||
is_echo_command ||
|
||||
cmd_strcmp(cmd, "cc") == 0 || cmd_strcmp(cmd, "CC") == 0 ||
|
||||
cmd_strcmp(cmd, "compc") == 0 || cmd_strcmp(cmd, "COMPC") == 0 ||
|
||||
cmd_strcmp(cmd, "touch") == 0 || cmd_strcmp(cmd, "TOUCH") == 0 ||
|
||||
cmd_strcmp(cmd, "cp") == 0 || cmd_strcmp(cmd, "CP") == 0 ||
|
||||
cmd_strcmp(cmd, "mv") == 0 || cmd_strcmp(cmd, "MV") == 0 ||
|
||||
cmd_strcmp(cmd, "txtedit") == 0 || cmd_strcmp(cmd, "TXTEDIT") == 0 ||
|
||||
cmd_strcmp(cmd, "tx") == 0 || cmd_strcmp(cmd, "TX") == 0);
|
||||
|
||||
if (needs_path) {
|
||||
if (args && args[0]) {
|
||||
// For echo with redirection, we need to prepend drive to redirect target too
|
||||
if (is_echo_command) {
|
||||
// Find > or >> and prepend drive to the filename after it
|
||||
char temp_args[512] = {0};
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
bool in_redirect = false;
|
||||
|
||||
while (args[i] && j < 509) {
|
||||
if (args[i] == '>' && args[i+1] == '>') {
|
||||
// >> redirection
|
||||
temp_args[j++] = '>';
|
||||
temp_args[j++] = '>';
|
||||
i += 2;
|
||||
while (args[i] == ' ') { temp_args[j++] = ' '; i++; }
|
||||
|
||||
// Prepend drive to filename
|
||||
if (args[i] && args[i+1] != ':') {
|
||||
temp_args[j++] = cmd_state->current_drive;
|
||||
temp_args[j++] = ':';
|
||||
}
|
||||
in_redirect = true;
|
||||
} else if (args[i] == '>' && args[i+1] != '>') {
|
||||
// > redirection
|
||||
temp_args[j++] = '>';
|
||||
i++;
|
||||
while (args[i] == ' ') { temp_args[j++] = ' '; i++; }
|
||||
|
||||
// Prepend drive to filename
|
||||
if (args[i] && args[i+1] != ':') {
|
||||
temp_args[j++] = cmd_state->current_drive;
|
||||
temp_args[j++] = ':';
|
||||
}
|
||||
in_redirect = true;
|
||||
} else {
|
||||
temp_args[j++] = args[i++];
|
||||
}
|
||||
}
|
||||
temp_args[j] = 0;
|
||||
cmd_strcpy(full_path_arg, temp_args);
|
||||
args = full_path_arg;
|
||||
} else if (cmd_strcmp(cmd, "cat") == 0 || cmd_strcmp(cmd, "CAT") == 0 ||
|
||||
cmd_strcmp(cmd, "cc") == 0 || cmd_strcmp(cmd, "CC") == 0 ||
|
||||
cmd_strcmp(cmd, "compc") == 0 || cmd_strcmp(cmd, "COMPC") == 0 ||
|
||||
cmd_strcmp(cmd, "touch") == 0 || cmd_strcmp(cmd, "TOUCH") == 0 ||
|
||||
cmd_strcmp(cmd, "cp") == 0 || cmd_strcmp(cmd, "CP") == 0 ||
|
||||
cmd_strcmp(cmd, "mv") == 0 || cmd_strcmp(cmd, "MV") == 0 ||
|
||||
cmd_strcmp(cmd, "txtedit") == 0 || cmd_strcmp(cmd, "TXTEDIT") == 0 ||
|
||||
cmd_strcmp(cmd, "tx") == 0 || cmd_strcmp(cmd, "TX") == 0) {
|
||||
// For cat, cc, compc, touch, cp, mv, txtedit: prepend drive to file arguments if not already present
|
||||
if (args[1] == ':') {
|
||||
// Already has drive letter
|
||||
cmd_strcpy(full_path_arg, args);
|
||||
} else {
|
||||
// Add drive letter
|
||||
full_path_arg[0] = cmd_state->current_drive;
|
||||
full_path_arg[1] = ':';
|
||||
int i = 2;
|
||||
int j = 0;
|
||||
while (args[j] && i < 509) {
|
||||
full_path_arg[i++] = args[j++];
|
||||
}
|
||||
full_path_arg[i] = 0;
|
||||
}
|
||||
args = full_path_arg;
|
||||
} else if (is_cd_command) {
|
||||
// For cd: build full path with drive + current directory + relative path
|
||||
// Check if args starts with drive letter (e.g., "B:" or "A:")
|
||||
if (args[1] == ':') {
|
||||
// Has drive letter, use as-is
|
||||
cmd_strcpy(full_path_arg, args);
|
||||
} else if (args[0] == '/') {
|
||||
// Absolute path, just prepend drive
|
||||
full_path_arg[0] = cmd_state->current_drive;
|
||||
full_path_arg[1] = ':';
|
||||
int i = 2;
|
||||
int j = 0;
|
||||
while (args[j] && i < 509) {
|
||||
full_path_arg[i++] = args[j++];
|
||||
}
|
||||
full_path_arg[i] = 0;
|
||||
} else {
|
||||
// Relative path - need to build from current directory
|
||||
int i = 0;
|
||||
full_path_arg[i++] = cmd_state->current_drive;
|
||||
full_path_arg[i++] = ':';
|
||||
|
||||
// Add current directory
|
||||
const char *dir = cmd_state->current_dir;
|
||||
while (*dir && i < 509) {
|
||||
full_path_arg[i++] = *dir++;
|
||||
}
|
||||
|
||||
// Add separator if current dir doesn't end with /
|
||||
if (i > 2 && full_path_arg[i-1] != '/') {
|
||||
full_path_arg[i++] = '/';
|
||||
}
|
||||
|
||||
// Add the relative path argument
|
||||
int j = 0;
|
||||
while (args[j] && i < 509) {
|
||||
full_path_arg[i++] = args[j++];
|
||||
}
|
||||
full_path_arg[i] = 0;
|
||||
}
|
||||
args = full_path_arg;
|
||||
} else if (args[1] == ':') {
|
||||
// Already has drive letter
|
||||
cmd_strcpy(full_path_arg, args);
|
||||
args = full_path_arg;
|
||||
} else {
|
||||
// Add drive letter
|
||||
full_path_arg[0] = cmd_state->current_drive;
|
||||
full_path_arg[1] = ':';
|
||||
int i = 2;
|
||||
int j = 0;
|
||||
while (args[j] && i < 509) {
|
||||
full_path_arg[i++] = args[j++];
|
||||
}
|
||||
full_path_arg[i] = 0;
|
||||
args = full_path_arg;
|
||||
}
|
||||
} else if (is_ls_command || is_cd_command) {
|
||||
// For ls and cd with no args, pass current directory with drive
|
||||
full_path_arg[0] = cmd_state->current_drive;
|
||||
full_path_arg[1] = ':';
|
||||
int i = 2;
|
||||
const char *dir = cmd_state->current_dir;
|
||||
while (*dir && i < 509) {
|
||||
full_path_arg[i++] = *dir++;
|
||||
}
|
||||
full_path_arg[i] = 0;
|
||||
args = full_path_arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use command dispatch table
|
||||
for (int i = 0; commands[i].name != NULL; i++) {
|
||||
if (cmd_strcmp(cmd, commands[i].name) == 0) {
|
||||
@@ -531,12 +1024,19 @@ static void cmd_exec_single(char *cmd) {
|
||||
|
||||
// Check for executable in /Apps/
|
||||
char app_path[256];
|
||||
char *p = app_path;
|
||||
int app_idx = 0;
|
||||
|
||||
// Add drive letter if on different drive
|
||||
if (cmd_state && cmd_state->current_drive != 'A') {
|
||||
app_path[app_idx++] = cmd_state->current_drive;
|
||||
app_path[app_idx++] = ':';
|
||||
}
|
||||
|
||||
const char *prefix = "/Apps/";
|
||||
while (*prefix) *p++ = *prefix++;
|
||||
while (*prefix) app_path[app_idx++] = *prefix++;
|
||||
char *c = cmd;
|
||||
while (*c) *p++ = *c++;
|
||||
*p = 0;
|
||||
while (*c && app_idx < 255) app_path[app_idx++] = *c++;
|
||||
app_path[app_idx] = 0;
|
||||
|
||||
FAT32_FileHandle *app_fh = fat32_open(app_path, "r");
|
||||
if (app_fh) {
|
||||
@@ -847,7 +1347,7 @@ static void cmd_key(Window *target, char c) {
|
||||
if (c == '\n') { // Enter
|
||||
char cmd_buf[CMD_COLS + 1];
|
||||
int len = 0;
|
||||
int prompt_len = cmd_strlen(PROMPT);
|
||||
int prompt_len = 4;
|
||||
|
||||
for (int i = prompt_len; i < CMD_COLS; i++) {
|
||||
char ch = screen_buffer[cursor_row][i].c;
|
||||
@@ -864,13 +1364,13 @@ static void cmd_key(Window *target, char c) {
|
||||
|
||||
cmd_exec(cmd_buf);
|
||||
|
||||
cmd_write(PROMPT);
|
||||
cmd_print_prompt();
|
||||
} else if (c == 17) { // UP
|
||||
if (history_len > 0) {
|
||||
if (history_pos == -1) {
|
||||
// Save current line
|
||||
int len = 0;
|
||||
int prompt_len = cmd_strlen(PROMPT);
|
||||
int prompt_len = 4;
|
||||
for (int i = prompt_len; i < CMD_COLS; i++) {
|
||||
char ch = screen_buffer[cursor_row][i].c;
|
||||
if (ch == 0) break;
|
||||
@@ -900,7 +1400,7 @@ static void cmd_key(Window *target, char c) {
|
||||
}
|
||||
}
|
||||
} else if (c == 19) { // LEFT
|
||||
if (cursor_col > (int)cmd_strlen(PROMPT)) {
|
||||
if (cursor_col > 4) {
|
||||
cursor_col--;
|
||||
}
|
||||
} else if (c == 20) { // RIGHT
|
||||
@@ -908,7 +1408,7 @@ static void cmd_key(Window *target, char c) {
|
||||
cursor_col++;
|
||||
}
|
||||
} else if (c == '\b') { // Backspace
|
||||
if (cursor_col > (int)cmd_strlen(PROMPT)) {
|
||||
if (cursor_col > 4) {
|
||||
cursor_col--;
|
||||
screen_buffer[cursor_row][cursor_col].c = ' ';
|
||||
}
|
||||
@@ -928,7 +1428,7 @@ void cmd_reset(void) {
|
||||
cmd_write_int(msg_count);
|
||||
cmd_write(" new message(s) run \"msgrc\" to see your new message(s).\n");
|
||||
}
|
||||
cmd_write(PROMPT);
|
||||
cmd_print_prompt();
|
||||
}
|
||||
|
||||
static void create_test_files(void) {
|
||||
@@ -1164,6 +1664,16 @@ void cmd_init(void) {
|
||||
win_cmd.handle_click = NULL;
|
||||
win_cmd.handle_right_click = NULL;
|
||||
|
||||
// Initialize cmd state (per-window context)
|
||||
CmdState *state = (CmdState*)kmalloc(sizeof(CmdState));
|
||||
if (state) {
|
||||
state->current_drive = 'A';
|
||||
state->current_dir[0] = '/';
|
||||
state->current_dir[1] = 0;
|
||||
win_cmd.data = state;
|
||||
cmd_state = state; // Set static pointer
|
||||
}
|
||||
|
||||
cmd_reset();
|
||||
|
||||
if (!boot_time_init) {
|
||||
|
||||
38
src/kernel/disk.h
Normal file
38
src/kernel/disk.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef DISK_H
|
||||
#define DISK_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define SECTOR_SIZE 512
|
||||
|
||||
typedef enum {
|
||||
DISK_TYPE_RAM,
|
||||
DISK_TYPE_IDE,
|
||||
DISK_TYPE_SATA,
|
||||
DISK_TYPE_USB
|
||||
} DiskType;
|
||||
|
||||
typedef struct Disk {
|
||||
char letter;
|
||||
DiskType type;
|
||||
bool is_fat32;
|
||||
char name[32];
|
||||
|
||||
// Function pointers for driver operations
|
||||
int (*read_sector)(struct Disk *disk, uint32_t sector, uint8_t *buffer);
|
||||
int (*write_sector)(struct Disk *disk, uint32_t sector, const uint8_t *buffer);
|
||||
|
||||
// Private driver data
|
||||
void *driver_data;
|
||||
} Disk;
|
||||
|
||||
void disk_manager_init(void);
|
||||
void disk_manager_scan(void); // Scans for new disks
|
||||
Disk* disk_get_by_letter(char letter);
|
||||
char disk_get_next_free_letter(void);
|
||||
void disk_register(Disk *disk);
|
||||
int disk_get_count(void);
|
||||
Disk* disk_get_by_index(int index);
|
||||
|
||||
#endif
|
||||
301
src/kernel/disk_manager.c
Normal file
301
src/kernel/disk_manager.c
Normal file
@@ -0,0 +1,301 @@
|
||||
#include "disk.h"
|
||||
#include "pci.h"
|
||||
#include "memory_manager.h"
|
||||
#include "io.h"
|
||||
#include "wm.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#define MAX_DISKS 26
|
||||
|
||||
static Disk *disks[MAX_DISKS];
|
||||
static int disk_count = 0;
|
||||
|
||||
// === ATA Definitions ===
|
||||
|
||||
#define ATA_PRIMARY_IO 0x1F0
|
||||
#define ATA_PRIMARY_CTRL 0x3F6
|
||||
#define ATA_SECONDARY_IO 0x170
|
||||
#define ATA_SECONDARY_CTRL 0x376
|
||||
|
||||
#define ATA_REG_DATA 0x00
|
||||
#define ATA_REG_ERROR 0x01
|
||||
#define ATA_REG_FEATURES 0x01
|
||||
#define ATA_REG_SEC_COUNT0 0x02
|
||||
#define ATA_REG_LBA0 0x03
|
||||
#define ATA_REG_LBA1 0x04
|
||||
#define ATA_REG_LBA2 0x05
|
||||
#define ATA_REG_HDDEVSEL 0x06
|
||||
#define ATA_REG_COMMAND 0x07
|
||||
#define ATA_REG_STATUS 0x07
|
||||
|
||||
#define ATA_CMD_READ_PIO 0x20
|
||||
#define ATA_CMD_WRITE_PIO 0x30
|
||||
#define ATA_CMD_IDENTIFY 0xEC
|
||||
|
||||
#define ATA_SR_BSY 0x80 // Busy
|
||||
#define ATA_SR_DRDY 0x40 // Drive ready
|
||||
#define ATA_SR_DF 0x20 // Drive write fault
|
||||
#define ATA_SR_DSC 0x10 // Drive seek complete
|
||||
#define ATA_SR_DRQ 0x08 // Data request ready
|
||||
#define ATA_SR_CORR 0x04 // Corrected data
|
||||
#define ATA_SR_IDX 0x02 // Index
|
||||
#define ATA_SR_ERR 0x01 // Error
|
||||
|
||||
typedef struct {
|
||||
uint16_t port_base;
|
||||
bool slave;
|
||||
} ATADriverData;
|
||||
|
||||
// === Helpers ===
|
||||
|
||||
static void dm_strcpy(char *dest, const char *src) {
|
||||
while (*src) *dest++ = *src++;
|
||||
*dest = 0;
|
||||
}
|
||||
|
||||
void disk_register(Disk *disk);
|
||||
|
||||
|
||||
static int ramdisk_read(Disk *disk, uint32_t sector, uint8_t *buffer) {
|
||||
(void)disk; (void)sector; (void)buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ramdisk_write(Disk *disk, uint32_t sector, const uint8_t *buffer) {
|
||||
(void)disk; (void)sector; (void)buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void ata_wait_bsy(uint16_t port_base) {
|
||||
while (inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY);
|
||||
}
|
||||
|
||||
static void ata_wait_drq(uint16_t port_base) {
|
||||
while (!(inb(port_base + ATA_REG_STATUS) & ATA_SR_DRQ));
|
||||
}
|
||||
|
||||
// Returns 1 if drive exists, 0 otherwise
|
||||
static int ata_identify(uint16_t port_base, bool slave) {
|
||||
// Select Drive
|
||||
outb(port_base + ATA_REG_HDDEVSEL, slave ? 0xB0 : 0xA0);
|
||||
// Zero out sector count and LBA registers
|
||||
outb(port_base + ATA_REG_SEC_COUNT0, 0);
|
||||
outb(port_base + ATA_REG_LBA0, 0);
|
||||
outb(port_base + ATA_REG_LBA1, 0);
|
||||
outb(port_base + ATA_REG_LBA2, 0);
|
||||
|
||||
// Send Identify command
|
||||
outb(port_base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
|
||||
|
||||
// Check if status is 0 (no drive)
|
||||
uint8_t status = inb(port_base + ATA_REG_STATUS);
|
||||
if (status == 0) return 0;
|
||||
|
||||
// Wait until BSY clears
|
||||
while (inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY) {
|
||||
// Simple timeout could be added here
|
||||
status = inb(port_base + ATA_REG_STATUS);
|
||||
if (status == 0) return 0; // Check again
|
||||
}
|
||||
|
||||
// Check for error
|
||||
if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) {
|
||||
return 0; // Error, likely not ATA
|
||||
}
|
||||
|
||||
// Wait for DRQ or ERR
|
||||
while (!(inb(port_base + ATA_REG_STATUS) & (ATA_SR_DRQ | ATA_SR_ERR)));
|
||||
|
||||
if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) return 0;
|
||||
|
||||
// Read 256 words (512 bytes) of identity data
|
||||
for (int i = 0; i < 256; i++) {
|
||||
uint16_t data = inw(port_base + ATA_REG_DATA);
|
||||
(void)data; // We discard identity data for now, just checking presence
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ata_read_sector(Disk *disk, uint32_t lba, uint8_t *buffer) {
|
||||
ATADriverData *data = (ATADriverData*)disk->driver_data;
|
||||
uint16_t port_base = data->port_base;
|
||||
bool slave = data->slave;
|
||||
|
||||
ata_wait_bsy(port_base);
|
||||
|
||||
// Select drive and send highest 4 bits of LBA
|
||||
outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F));
|
||||
outb(port_base + ATA_REG_FEATURES, 0x00);
|
||||
outb(port_base + ATA_REG_SEC_COUNT0, 1);
|
||||
outb(port_base + ATA_REG_LBA0, (uint8_t)(lba));
|
||||
outb(port_base + ATA_REG_LBA1, (uint8_t)(lba >> 8));
|
||||
outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16));
|
||||
outb(port_base + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
|
||||
|
||||
ata_wait_bsy(port_base);
|
||||
ata_wait_drq(port_base);
|
||||
|
||||
uint16_t *ptr = (uint16_t*)buffer;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
ptr[i] = inw(port_base + ATA_REG_DATA);
|
||||
}
|
||||
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
static int ata_write_sector(Disk *disk, uint32_t lba, const uint8_t *buffer) {
|
||||
ATADriverData *data = (ATADriverData*)disk->driver_data;
|
||||
uint16_t port_base = data->port_base;
|
||||
bool slave = data->slave;
|
||||
|
||||
ata_wait_bsy(port_base);
|
||||
|
||||
outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F));
|
||||
outb(port_base + ATA_REG_FEATURES, 0x00);
|
||||
outb(port_base + ATA_REG_SEC_COUNT0, 1);
|
||||
outb(port_base + ATA_REG_LBA0, (uint8_t)(lba));
|
||||
outb(port_base + ATA_REG_LBA1, (uint8_t)(lba >> 8));
|
||||
outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16));
|
||||
outb(port_base + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
|
||||
|
||||
ata_wait_bsy(port_base);
|
||||
ata_wait_drq(port_base);
|
||||
|
||||
const uint16_t *ptr = (const uint16_t*)buffer;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
outw(port_base + ATA_REG_DATA, ptr[i]);
|
||||
}
|
||||
|
||||
// Flush / Sync
|
||||
outb(port_base + ATA_REG_COMMAND, 0xE7); // Cache Flush
|
||||
ata_wait_bsy(port_base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
char disk_get_next_free_letter(void) {
|
||||
for (int i = 0; i < MAX_DISKS; i++) {
|
||||
char letter = 'A' + i;
|
||||
bool used = false;
|
||||
for (int j = 0; j < disk_count; j++) {
|
||||
if (disks[j]->letter == letter) {
|
||||
used = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!used) return letter;
|
||||
}
|
||||
return 0; // No free letters
|
||||
}
|
||||
|
||||
void disk_register(Disk *disk) {
|
||||
if (disk_count >= MAX_DISKS) return;
|
||||
|
||||
// Ensure letter is unique
|
||||
if (disk->letter == 0) {
|
||||
disk->letter = disk_get_next_free_letter();
|
||||
}
|
||||
|
||||
disks[disk_count++] = disk;
|
||||
}
|
||||
|
||||
void disk_manager_init(void) {
|
||||
for (int i = 0; i < MAX_DISKS; i++) {
|
||||
disks[i] = NULL;
|
||||
}
|
||||
disk_count = 0;
|
||||
|
||||
// Register A: (Ramdisk)
|
||||
Disk *ramdisk = (Disk*)kmalloc(sizeof(Disk));
|
||||
ramdisk->letter = 'A';
|
||||
ramdisk->type = DISK_TYPE_RAM;
|
||||
ramdisk->is_fat32 = true; // Ramdisk is always formatted
|
||||
dm_strcpy(ramdisk->name, "RAM");
|
||||
ramdisk->read_sector = ramdisk_read;
|
||||
ramdisk->write_sector = ramdisk_write;
|
||||
ramdisk->driver_data = NULL;
|
||||
|
||||
disk_register(ramdisk);
|
||||
}
|
||||
|
||||
Disk* disk_get_by_letter(char letter) {
|
||||
// Uppercase
|
||||
if (letter >= 'a' && letter <= 'z') letter -= 32;
|
||||
|
||||
for (int i = 0; i < disk_count; i++) {
|
||||
if (disks[i]->letter == letter) {
|
||||
return disks[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int disk_get_count(void) {
|
||||
return disk_count;
|
||||
}
|
||||
|
||||
Disk* disk_get_by_index(int index) {
|
||||
if (index < 0 || index >= disk_count) return NULL;
|
||||
return disks[index];
|
||||
}
|
||||
|
||||
|
||||
// Check for FAT32 Signature in MBR/VBR
|
||||
static bool check_fat32_signature(Disk *disk) {
|
||||
uint8_t *buffer = (uint8_t*)kmalloc(512);
|
||||
if (!buffer) return false;
|
||||
|
||||
// Read Sector 0
|
||||
if (disk->read_sector(disk, 0, buffer) != 0) {
|
||||
kfree(buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check boot signature 0x55 0xAA at offset 510
|
||||
if (buffer[510] != 0x55 || buffer[511] != 0xAA) {
|
||||
kfree(buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
kfree(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void try_add_ata_drive(uint16_t port, bool slave, const char *name) {
|
||||
if (ata_identify(port, slave)) {
|
||||
Disk *new_disk = (Disk*)kmalloc(sizeof(Disk));
|
||||
if (!new_disk) return;
|
||||
|
||||
ATADriverData *data = (ATADriverData*)kmalloc(sizeof(ATADriverData));
|
||||
data->port_base = port;
|
||||
data->slave = slave;
|
||||
|
||||
new_disk->letter = 0; // Auto-assign
|
||||
new_disk->type = DISK_TYPE_IDE;
|
||||
dm_strcpy(new_disk->name, name);
|
||||
new_disk->read_sector = ata_read_sector;
|
||||
new_disk->write_sector = ata_write_sector;
|
||||
new_disk->driver_data = data;
|
||||
|
||||
// Check filesystem
|
||||
if (check_fat32_signature(new_disk)) {
|
||||
new_disk->is_fat32 = true;
|
||||
disk_register(new_disk);
|
||||
} else {
|
||||
|
||||
kfree(data);
|
||||
kfree(new_disk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void disk_manager_scan(void) {
|
||||
// Probe Standard ATA Ports
|
||||
try_add_ata_drive(ATA_PRIMARY_IO, false, "IDE1");
|
||||
try_add_ata_drive(ATA_PRIMARY_IO, true, "IDE2");
|
||||
try_add_ata_drive(ATA_SECONDARY_IO, false, "IDE3");
|
||||
try_add_ata_drive(ATA_SECONDARY_IO, true, "IDE4");
|
||||
}
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// === Text Editor State ===
|
||||
Window win_editor;
|
||||
|
||||
#define EDITOR_MAX_LINES 128
|
||||
@@ -192,13 +191,13 @@ static void editor_insert_char(char ch) {
|
||||
EditorLine *prev = &lines[cursor_line - 1];
|
||||
int merge_point = prev->length;
|
||||
|
||||
for (int i = 0; i < line->length; i++) {
|
||||
if (merge_point + i < EDITOR_MAX_LINE_LEN - 1) {
|
||||
prev->content[merge_point + i] = line->content[i];
|
||||
}
|
||||
int i = 0;
|
||||
while (i < line->length && (merge_point + i) < EDITOR_MAX_LINE_LEN - 1) {
|
||||
prev->content[merge_point + i] = line->content[i];
|
||||
i++;
|
||||
}
|
||||
prev->content[merge_point + line->length] = 0;
|
||||
prev->length = merge_point + line->length;
|
||||
prev->content[merge_point + i] = 0;
|
||||
prev->length = merge_point + i;
|
||||
|
||||
// Shift lines up
|
||||
for (int i = cursor_line; i < line_count - 1; i++) {
|
||||
@@ -419,13 +418,6 @@ static void editor_paint(Window *win) {
|
||||
// === Key Handler ===
|
||||
|
||||
static void editor_handle_key(Window *win, char c) {
|
||||
if (c == 'q' || c == 'Q') {
|
||||
if (file_modified) {
|
||||
}
|
||||
win->visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Arrow keys - UP
|
||||
if (c == 17) {
|
||||
if (cursor_line > 0) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "explorer.h"
|
||||
#include "graphics.h"
|
||||
#include "fat32.h"
|
||||
#include "disk.h"
|
||||
#include "wm.h"
|
||||
#include "memory_manager.h"
|
||||
#include "editor.h"
|
||||
@@ -377,8 +378,15 @@ bool explorer_delete_recursive(const char *path) {
|
||||
for (int k = i + 1; k < len; k++) filename[j++] = path[k];
|
||||
filename[j] = 0;
|
||||
|
||||
// Extract drive from path
|
||||
char drive_prefix[3] = "A:";
|
||||
if (path[0] && path[1] == ':') {
|
||||
drive_prefix[0] = path[0];
|
||||
}
|
||||
|
||||
char dest_path[256];
|
||||
explorer_strcpy(dest_path, "/RecycleBin/");
|
||||
explorer_strcpy(dest_path, drive_prefix);
|
||||
explorer_strcat(dest_path, "/RecycleBin/");
|
||||
explorer_strcat(dest_path, filename);
|
||||
|
||||
// Save origin
|
||||
@@ -946,11 +954,36 @@ static void explorer_paint(Window *win) {
|
||||
// Fill background
|
||||
draw_rect(offset_x, offset_y, win->w - 8, win->h - 28, COLOR_LTGRAY);
|
||||
|
||||
// Draw path bar
|
||||
// Draw Drive Button
|
||||
char drive_label[8];
|
||||
// Extract drive from the window's current_path instead of using global current_drive
|
||||
char current_drv = 'A';
|
||||
if (state->current_path[0] && state->current_path[1] == ':') {
|
||||
current_drv = state->current_path[0];
|
||||
} else if (state->current_path[0] && (state->current_path[0] >= 'A' && state->current_path[0] <= 'Z')) {
|
||||
current_drv = state->current_path[0];
|
||||
}
|
||||
|
||||
drive_label[0] = '[';
|
||||
drive_label[1] = ' ';
|
||||
drive_label[2] = current_drv;
|
||||
drive_label[3] = ':';
|
||||
drive_label[4] = ' ';
|
||||
drive_label[5] = 'v';
|
||||
drive_label[6] = ' ';
|
||||
drive_label[7] = ']';
|
||||
|
||||
// Button at x+4, y+4, w=60
|
||||
draw_button(win->x + 4, offset_y + 4, 60, 30, "", false);
|
||||
draw_string(win->x + 12, offset_y + 12, drive_label, COLOR_BLACK);
|
||||
|
||||
// Draw path bar (shifted right)
|
||||
int path_height = 30;
|
||||
draw_bevel_rect(offset_x + 4, offset_y + 4, win->w - 16, path_height, true);
|
||||
draw_string(offset_x + 10, offset_y + 10, "Path", COLOR_BLACK);
|
||||
draw_string(offset_x + 50, offset_y + 10, state->current_path, COLOR_BLACK);
|
||||
int path_x = offset_x + 64;
|
||||
int path_w = win->w - 16 - 64;
|
||||
draw_bevel_rect(path_x, offset_y + 4, path_w, path_height, true);
|
||||
draw_string(path_x + 6, offset_y + 10, "Path", COLOR_BLACK);
|
||||
draw_string(path_x + 46, offset_y + 10, state->current_path, COLOR_BLACK);
|
||||
|
||||
// Draw dropdown menu button (right-aligned, before back button)
|
||||
int dropdown_btn_x = win->x + win->w - 90;
|
||||
@@ -1004,6 +1037,39 @@ static void explorer_paint(Window *win) {
|
||||
graphics_clear_clipping();
|
||||
}
|
||||
|
||||
// Draw Drive Menu if visible
|
||||
if (state->drive_menu_visible) {
|
||||
int menu_x = win->x + 4;
|
||||
int menu_y = offset_y + 34;
|
||||
int menu_w = 80;
|
||||
int count = disk_get_count();
|
||||
int menu_h = count * 25;
|
||||
|
||||
draw_rect(menu_x, menu_y, menu_w, menu_h, COLOR_LTGRAY);
|
||||
draw_bevel_rect(menu_x, menu_y, menu_w, menu_h, true);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
Disk *d = disk_get_by_index(i);
|
||||
if (d) {
|
||||
char buf[16];
|
||||
buf[0] = d->letter;
|
||||
buf[1] = ':';
|
||||
buf[2] = ' ';
|
||||
// Copy name truncated
|
||||
int n = 0; while(d->name[n] && n < 10) { buf[3+n] = d->name[n]; n++; }
|
||||
buf[3+n] = 0;
|
||||
|
||||
// Highlight current
|
||||
if (d->letter == current_drv) {
|
||||
draw_rect(menu_x + 2, menu_y + i*25 + 2, menu_w - 4, 21, COLOR_BLUE);
|
||||
draw_string(menu_x + 5, menu_y + i*25 + 6, buf, COLOR_WHITE);
|
||||
} else {
|
||||
draw_string(menu_x + 5, menu_y + i*25 + 6, buf, COLOR_BLACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw dropdown menu if visible
|
||||
if (state->dropdown_menu_visible) {
|
||||
int menu_x = dropdown_btn_x;
|
||||
@@ -1323,6 +1389,35 @@ static void explorer_handle_click(Window *win, int x, int y) {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Drive Menu Selection
|
||||
if (state->drive_menu_visible) {
|
||||
int menu_x = 4; // Window relative
|
||||
int menu_y = 58; // 24+34
|
||||
int menu_w = 80;
|
||||
int count = disk_get_count();
|
||||
int menu_h = count * 25;
|
||||
|
||||
if (x >= menu_x && x < menu_x + menu_w && y >= menu_y && y < menu_y + menu_h) {
|
||||
int idx = (y - menu_y) / 25;
|
||||
Disk *d = disk_get_by_index(idx);
|
||||
if (d) {
|
||||
// Do not change global drive, just navigate explorer to it
|
||||
char path[4];
|
||||
path[0] = d->letter;
|
||||
path[1] = ':';
|
||||
path[2] = '/';
|
||||
path[3] = 0;
|
||||
explorer_load_directory(win, path);
|
||||
}
|
||||
state->drive_menu_visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Click outside closes menu
|
||||
state->drive_menu_visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle dropdown menu clicks
|
||||
if (state->dropdown_menu_visible) {
|
||||
int dropdown_btn_x = win->w - 90; // Window-relative
|
||||
@@ -1362,12 +1457,21 @@ static void explorer_handle_click(Window *win, int x, int y) {
|
||||
}
|
||||
|
||||
// x, y are already relative to window (0,0 is top-left of window content area)
|
||||
|
||||
// Check Drive Button
|
||||
int button_y = 28;
|
||||
if (x >= 4 && x < 64 && y >= button_y && y < button_y + 30) {
|
||||
state->drive_menu_visible = !state->drive_menu_visible;
|
||||
state->dropdown_menu_visible = false; // Close other menu
|
||||
return;
|
||||
}
|
||||
|
||||
// Check dropdown menu button
|
||||
int button_y = 28; // Position from top of window title bar
|
||||
if (x >= win->w - 90 && x < win->w - 55 &&
|
||||
y >= button_y && y < button_y + 30) {
|
||||
// Dropdown menu button clicked
|
||||
dropdown_menu_toggle(win);
|
||||
state->drive_menu_visible = false; // Close other menu
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1514,10 +1618,7 @@ static void explorer_handle_key(Window *win, char c) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == 'q' || c == 'Q') {
|
||||
win->visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Close dropdown menu if open with ESC
|
||||
if (state->dropdown_menu_visible && c == 27) {
|
||||
@@ -1899,12 +2000,15 @@ Window* explorer_create_window(const char *path) {
|
||||
state->explorer_scroll_row = 0;
|
||||
state->dialog_state = DIALOG_NONE;
|
||||
state->dropdown_menu_visible = false;
|
||||
state->drive_menu_visible = false;
|
||||
state->file_context_menu_visible = false;
|
||||
|
||||
explorer_wins[explorer_win_count++] = win;
|
||||
wm_add_window(win);
|
||||
wm_bring_to_front(win);
|
||||
explorer_load_directory(win, path);
|
||||
|
||||
if (explorer_strcmp(path, "/") == 0) explorer_load_directory(win, "A:/");
|
||||
else explorer_load_directory(win, path);
|
||||
return win;
|
||||
}
|
||||
|
||||
@@ -1924,13 +2028,14 @@ void explorer_init(void) {
|
||||
win_explorer.handle_right_click = explorer_handle_right_click;
|
||||
win_explorer.data = state;
|
||||
|
||||
state->drive_menu_visible = false;
|
||||
explorer_wins[explorer_win_count++] = &win_explorer;
|
||||
explorer_load_directory(&win_explorer, "/");
|
||||
explorer_load_directory(&win_explorer, "A:/");
|
||||
}
|
||||
void explorer_reset(void) {
|
||||
ExplorerState *state = (ExplorerState*)win_explorer.data;
|
||||
// Reset explorer to root directory on close/reopen
|
||||
explorer_load_directory(&win_explorer, "/");
|
||||
explorer_load_directory(&win_explorer, "A:/");
|
||||
win_explorer.focused = false;
|
||||
state->explorer_scroll_row = 0;
|
||||
}
|
||||
@@ -43,6 +43,7 @@ typedef struct {
|
||||
|
||||
// Dropdown menu state
|
||||
bool dropdown_menu_visible;
|
||||
bool drive_menu_visible;
|
||||
|
||||
// File context menu state
|
||||
bool file_context_menu_visible;
|
||||
|
||||
1493
src/kernel/fat32.c
1493
src/kernel/fat32.c
File diff suppressed because it is too large
Load Diff
@@ -85,6 +85,7 @@ typedef struct {
|
||||
bool valid; // Is this handle valid?
|
||||
uint32_t dir_sector; // Sector containing the directory entry
|
||||
uint32_t dir_offset; // Offset within that sector
|
||||
char drive; // Drive letter (A, B, ...)
|
||||
} FAT32_FileHandle;
|
||||
|
||||
// Directory Entry Info (for listing)
|
||||
@@ -123,6 +124,8 @@ int fat32_list_directory(const char *path, FAT32_FileInfo *entries, int max_entr
|
||||
// Working Directory
|
||||
bool fat32_chdir(const char *path);
|
||||
void fat32_get_current_dir(char *buffer, int size);
|
||||
bool fat32_change_drive(char drive);
|
||||
char fat32_get_current_drive(void);
|
||||
|
||||
// Utilities
|
||||
void fat32_normalize_path(const char *path, char *normalized);
|
||||
|
||||
@@ -11,6 +11,12 @@ static inline void outw(uint16_t port, uint16_t val) {
|
||||
asm volatile ("outw %0, %1" : : "a"(val), "Nd"(port));
|
||||
}
|
||||
|
||||
static inline uint16_t inw(uint16_t port) {
|
||||
uint16_t ret;
|
||||
asm volatile ("inw %1, %0" : "=a"(ret) : "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline uint8_t inb(uint16_t port) {
|
||||
uint8_t ret;
|
||||
asm volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port));
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "fat32.h"
|
||||
#include "memory_manager.h"
|
||||
#include "paint.h"
|
||||
#include "disk.h"
|
||||
|
||||
// --- State ---
|
||||
static int mx = 400, my = 300; // Mouse Pos
|
||||
@@ -1760,6 +1761,10 @@ void wm_refresh(void) {
|
||||
}
|
||||
|
||||
void wm_init(void) {
|
||||
disk_manager_init();
|
||||
disk_manager_scan();
|
||||
// Drives are now dynamically managed - only real drives are registered
|
||||
|
||||
notepad_init();
|
||||
cmd_init();
|
||||
calculator_init();
|
||||
|
||||
Reference in New Issue
Block a user