mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 18:58:40 +00:00
FEAT: Lumos file searcher
This commit is contained in:
parent
8dd756f25b
commit
bb176f2193
10 changed files with 891 additions and 22 deletions
|
|
@ -269,3 +269,7 @@ uint64_t mouse_handler(registers_t *regs) {
|
||||||
void ps2_init(void) {
|
void ps2_init(void) {
|
||||||
mouse_init();
|
mouse_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ps2_shift_pressed(void) {
|
||||||
|
return shift_pressed;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,4 +13,6 @@ uint64_t timer_handler(registers_t *regs);
|
||||||
uint64_t keyboard_handler(registers_t *regs);
|
uint64_t keyboard_handler(registers_t *regs);
|
||||||
uint64_t mouse_handler(registers_t *regs);
|
uint64_t mouse_handler(registers_t *regs);
|
||||||
|
|
||||||
|
bool ps2_shift_pressed(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
477
src/sys/file_index.c
Normal file
477
src/sys/file_index.c
Normal file
|
|
@ -0,0 +1,477 @@
|
||||||
|
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||||
|
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||||
|
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||||
|
#include "file_index.h"
|
||||||
|
#include "vfs.h"
|
||||||
|
#include "memory_manager.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
static file_index_t g_file_index = {0};
|
||||||
|
static spinlock_t g_index_lock = SPINLOCK_INIT;
|
||||||
|
static bool g_index_valid = false;
|
||||||
|
|
||||||
|
static int str_len(const char *s) {
|
||||||
|
int n = 0;
|
||||||
|
while (s[n]) n++;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void str_copy(char *d, const char *s) {
|
||||||
|
while ((*d++ = *s++));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int str_cmp(const char *a, const char *b) {
|
||||||
|
while (*a && *a == *b) { a++; b++; }
|
||||||
|
return (unsigned char)*a - (unsigned char)*b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void str_cat(char *d, const char *s) {
|
||||||
|
while (*d) d++;
|
||||||
|
str_copy(d, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool str_starts_with(const char *str, const char *prefix) {
|
||||||
|
while (*prefix) {
|
||||||
|
if (*str++ != *prefix++) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fuzzy_match_score(const char *query, const char *filename) {
|
||||||
|
if (!query || !filename) return 0;
|
||||||
|
|
||||||
|
int score = 0;
|
||||||
|
int query_idx = 0;
|
||||||
|
int consecutive = 0;
|
||||||
|
|
||||||
|
for (int i = 0; filename[i] && query_idx < 256; i++) {
|
||||||
|
char fc = filename[i];
|
||||||
|
char qc = query[query_idx];
|
||||||
|
|
||||||
|
if (fc >= 'A' && fc <= 'Z') fc += 32;
|
||||||
|
if (qc >= 'A' && qc <= 'Z') qc += 32;
|
||||||
|
|
||||||
|
if (fc == qc) {
|
||||||
|
score += 10;
|
||||||
|
consecutive++;
|
||||||
|
if (consecutive > 1) score += 5;
|
||||||
|
query_idx++;
|
||||||
|
} else {
|
||||||
|
consecutive = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query_idx < str_len(query)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str_starts_with(filename, query)) {
|
||||||
|
score += 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void index_walk_directory(const char *path, int depth) {
|
||||||
|
if (depth > 16 || g_file_index.count >= FILE_INDEX_MAX_ENTRIES) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str_starts_with(path, "/proc") ||
|
||||||
|
str_starts_with(path, "/sys") ||
|
||||||
|
str_starts_with(path, "/dev")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vfs_dirent_t *entries = (vfs_dirent_t *)kmalloc(sizeof(vfs_dirent_t) * 1024);
|
||||||
|
if (!entries) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = vfs_list_directory(path, entries, 1024);
|
||||||
|
|
||||||
|
if (count <= 0 || count > 1024) {
|
||||||
|
kfree(entries);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
if (g_file_index.count >= FILE_INDEX_MAX_ENTRIES) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
vfs_dirent_t *entry = &entries[i];
|
||||||
|
if (!entry) continue;
|
||||||
|
|
||||||
|
|
||||||
|
if (!entry->name || entry->name[0] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (str_cmp(entry->name, ".color") == 0 ||
|
||||||
|
str_cmp(entry->name, ".origin") == 0 ||
|
||||||
|
str_cmp(entry->name, ".") == 0 ||
|
||||||
|
str_cmp(entry->name, "..") == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
char full_path[FILE_INDEX_MAX_PATH];
|
||||||
|
int path_len = 0;
|
||||||
|
|
||||||
|
|
||||||
|
for (int j = 0; path[j] && path_len < FILE_INDEX_MAX_PATH - 1; j++) {
|
||||||
|
full_path[path_len++] = path[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (path_len > 0 && full_path[path_len - 1] != '/' && path_len < FILE_INDEX_MAX_PATH - 1) {
|
||||||
|
full_path[path_len++] = '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (int j = 0; entry->name[j] && path_len < FILE_INDEX_MAX_PATH - 1; j++) {
|
||||||
|
full_path[path_len++] = entry->name[j];
|
||||||
|
}
|
||||||
|
full_path[path_len] = 0;
|
||||||
|
|
||||||
|
|
||||||
|
if (path_len >= FILE_INDEX_MAX_PATH - 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
file_index_entry_t *idx_entry = &g_file_index.entries[g_file_index.count];
|
||||||
|
str_copy(idx_entry->path, full_path);
|
||||||
|
idx_entry->size = entry->size;
|
||||||
|
idx_entry->mod_time_low = entry->write_date;
|
||||||
|
idx_entry->mod_time_high = entry->write_time;
|
||||||
|
idx_entry->is_directory = entry->is_directory;
|
||||||
|
|
||||||
|
g_file_index.count++;
|
||||||
|
|
||||||
|
|
||||||
|
if (entry->is_directory && !str_starts_with(full_path, "/proc") &&
|
||||||
|
!str_starts_with(full_path, "/sys") && !str_starts_with(full_path, "/dev")) {
|
||||||
|
index_walk_directory(full_path, depth + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
kfree(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void file_index_init(void) {
|
||||||
|
g_file_index.count = 0;
|
||||||
|
g_file_index.capacity = FILE_INDEX_MAX_ENTRIES;
|
||||||
|
g_index_valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool file_index_build(void) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
g_file_index.count = 0;
|
||||||
|
|
||||||
|
|
||||||
|
const char *safe_paths[] = {"/root", "/bin", "/Library", "/docs", NULL};
|
||||||
|
|
||||||
|
|
||||||
|
for (int p = 0; safe_paths[p] != NULL && g_file_index.count < FILE_INDEX_MAX_ENTRIES; p++) {
|
||||||
|
index_walk_directory(safe_paths[p], 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
|
||||||
|
g_index_valid = true;
|
||||||
|
spinlock_release_irqrestore(&g_index_lock, flags);
|
||||||
|
|
||||||
|
|
||||||
|
file_index_save();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool file_index_load(void) {
|
||||||
|
vfs_file_t *file = vfs_open(FILE_INDEX_CACHE_PATH, "r");
|
||||||
|
if (!file) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t version = 0;
|
||||||
|
if (vfs_read(file, &version, sizeof(version)) != sizeof(version)) {
|
||||||
|
vfs_close(file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (version != FILE_INDEX_VERSION) {
|
||||||
|
vfs_close(file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
if (vfs_read(file, &count, sizeof(count)) != sizeof(count)) {
|
||||||
|
vfs_close(file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (count < 0 || count > FILE_INDEX_MAX_ENTRIES) {
|
||||||
|
vfs_close(file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
file_index_entry_t temp_entries[FILE_INDEX_MAX_ENTRIES];
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
file_index_entry_t *entry = &temp_entries[i];
|
||||||
|
|
||||||
|
int bytes_read = vfs_read(file, entry->path, FILE_INDEX_MAX_PATH);
|
||||||
|
if (bytes_read != FILE_INDEX_MAX_PATH) {
|
||||||
|
vfs_close(file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vfs_read(file, &entry->size, sizeof(entry->size)) != sizeof(entry->size)) {
|
||||||
|
vfs_close(file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vfs_read(file, &entry->mod_time_low, sizeof(entry->mod_time_low)) != sizeof(entry->mod_time_low)) {
|
||||||
|
vfs_close(file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vfs_read(file, &entry->mod_time_high, sizeof(entry->mod_time_high)) != sizeof(entry->mod_time_high)) {
|
||||||
|
vfs_close(file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vfs_read(file, &entry->is_directory, sizeof(entry->is_directory)) != sizeof(entry->is_directory)) {
|
||||||
|
vfs_close(file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vfs_close(file);
|
||||||
|
|
||||||
|
|
||||||
|
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
|
||||||
|
g_file_index.count = 0;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
g_file_index.entries[i] = temp_entries[i];
|
||||||
|
g_file_index.count++;
|
||||||
|
}
|
||||||
|
g_index_valid = true;
|
||||||
|
spinlock_release_irqrestore(&g_index_lock, flags);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool file_index_save(void) {
|
||||||
|
if (!vfs_mkdir("/Library")) {
|
||||||
|
}
|
||||||
|
if (!vfs_mkdir("/Library/Index")) {
|
||||||
|
}
|
||||||
|
|
||||||
|
vfs_file_t *file = vfs_open(FILE_INDEX_CACHE_PATH, "w");
|
||||||
|
if (!file) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
|
||||||
|
int count = g_file_index.count;
|
||||||
|
file_index_entry_t entries[FILE_INDEX_MAX_ENTRIES];
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
entries[i] = g_file_index.entries[i];
|
||||||
|
}
|
||||||
|
spinlock_release_irqrestore(&g_index_lock, flags);
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t version = FILE_INDEX_VERSION;
|
||||||
|
if (vfs_write(file, &version, sizeof(version)) != sizeof(version)) {
|
||||||
|
vfs_close(file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vfs_write(file, &count, sizeof(count)) != sizeof(count)) {
|
||||||
|
vfs_close(file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
file_index_entry_t *entry = &entries[i];
|
||||||
|
|
||||||
|
if (vfs_write(file, entry->path, FILE_INDEX_MAX_PATH) != FILE_INDEX_MAX_PATH) {
|
||||||
|
vfs_close(file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vfs_write(file, &entry->size, sizeof(entry->size)) != sizeof(entry->size)) {
|
||||||
|
vfs_close(file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vfs_write(file, &entry->mod_time_low, sizeof(entry->mod_time_low)) != sizeof(entry->mod_time_low)) {
|
||||||
|
vfs_close(file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vfs_write(file, &entry->mod_time_high, sizeof(entry->mod_time_high)) != sizeof(entry->mod_time_high)) {
|
||||||
|
vfs_close(file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vfs_write(file, &entry->is_directory, sizeof(entry->is_directory)) != sizeof(entry->is_directory)) {
|
||||||
|
vfs_close(file);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vfs_close(file);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int file_index_find_fuzzy(const char *query, file_index_result_t *results, int max_results) {
|
||||||
|
if (!query || !results || max_results <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
|
||||||
|
|
||||||
|
int result_count = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < g_file_index.count && result_count < max_results; i++) {
|
||||||
|
if (i < 0 || i >= FILE_INDEX_MAX_ENTRIES) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *path = g_file_index.entries[i].path;
|
||||||
|
if (!path || path[0] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *filename = path;
|
||||||
|
|
||||||
|
for (int j = 0; path[j]; j++) {
|
||||||
|
if (path[j] == '/') {
|
||||||
|
filename = &path[j + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filename || filename[0] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int score = fuzzy_match_score(query, filename);
|
||||||
|
|
||||||
|
if (score > 0) {
|
||||||
|
results[result_count].entry = g_file_index.entries[i];
|
||||||
|
results[result_count].match_score = score;
|
||||||
|
result_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spinlock_release_irqrestore(&g_index_lock, flags);
|
||||||
|
|
||||||
|
for (int i = 0; i < result_count; i++) {
|
||||||
|
for (int j = i + 1; j < result_count; j++) {
|
||||||
|
if (results[j].match_score > results[i].match_score) {
|
||||||
|
file_index_result_t tmp = results[i];
|
||||||
|
results[i] = results[j];
|
||||||
|
results[j] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool file_index_add_entry(const char *path, uint32_t size, uint32_t mod_time_low,
|
||||||
|
uint32_t mod_time_high, bool is_dir) {
|
||||||
|
if (!path || g_file_index.count >= FILE_INDEX_MAX_ENTRIES) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
|
||||||
|
|
||||||
|
for (int i = 0; i < g_file_index.count; i++) {
|
||||||
|
if (str_cmp(g_file_index.entries[i].path, path) == 0) {
|
||||||
|
g_file_index.entries[i].size = size;
|
||||||
|
g_file_index.entries[i].mod_time_low = mod_time_low;
|
||||||
|
g_file_index.entries[i].mod_time_high = mod_time_high;
|
||||||
|
spinlock_release_irqrestore(&g_index_lock, flags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file_index_entry_t *entry = &g_file_index.entries[g_file_index.count];
|
||||||
|
str_copy(entry->path, path);
|
||||||
|
entry->size = size;
|
||||||
|
entry->mod_time_low = mod_time_low;
|
||||||
|
entry->mod_time_high = mod_time_high;
|
||||||
|
entry->is_directory = is_dir;
|
||||||
|
|
||||||
|
g_file_index.count++;
|
||||||
|
|
||||||
|
spinlock_release_irqrestore(&g_index_lock, flags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool file_index_remove_entry(const char *path) {
|
||||||
|
if (!path) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
|
||||||
|
|
||||||
|
for (int i = 0; i < g_file_index.count; i++) {
|
||||||
|
if (str_cmp(g_file_index.entries[i].path, path) == 0) {
|
||||||
|
for (int j = i; j < g_file_index.count - 1; j++) {
|
||||||
|
g_file_index.entries[j] = g_file_index.entries[j + 1];
|
||||||
|
}
|
||||||
|
g_file_index.count--;
|
||||||
|
spinlock_release_irqrestore(&g_index_lock, flags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spinlock_release_irqrestore(&g_index_lock, flags);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int file_index_get_entry_count(void) {
|
||||||
|
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
|
||||||
|
int count = g_file_index.count;
|
||||||
|
spinlock_release_irqrestore(&g_index_lock, flags);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_index_clear(void) {
|
||||||
|
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
|
||||||
|
g_file_index.count = 0;
|
||||||
|
g_index_valid = false;
|
||||||
|
spinlock_release_irqrestore(&g_index_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_index_invalidate_cache(void) {
|
||||||
|
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
|
||||||
|
g_index_valid = false;
|
||||||
|
spinlock_release_irqrestore(&g_index_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool file_index_is_valid(void) {
|
||||||
|
uint64_t flags = spinlock_acquire_irqsave(&g_index_lock);
|
||||||
|
bool valid = g_index_valid;
|
||||||
|
spinlock_release_irqrestore(&g_index_lock, flags);
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
50
src/sys/file_index.h
Normal file
50
src/sys/file_index.h
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||||
|
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||||
|
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||||
|
#ifndef FILE_INDEX_H
|
||||||
|
#define FILE_INDEX_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#define FILE_INDEX_MAX_ENTRIES 50000
|
||||||
|
#define FILE_INDEX_MAX_PATH 1024
|
||||||
|
#define FILE_INDEX_CACHE_PATH "/Library/Index/file_index.dat"
|
||||||
|
#define FILE_INDEX_VERSION 1
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char path[FILE_INDEX_MAX_PATH];
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t mod_time_low;
|
||||||
|
uint32_t mod_time_high;
|
||||||
|
bool is_directory;
|
||||||
|
} file_index_entry_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
file_index_entry_t entry;
|
||||||
|
int match_score;
|
||||||
|
} file_index_result_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
file_index_entry_t entries[FILE_INDEX_MAX_ENTRIES];
|
||||||
|
int count;
|
||||||
|
int capacity;
|
||||||
|
} file_index_t;
|
||||||
|
|
||||||
|
void file_index_init(void);
|
||||||
|
bool file_index_build(void);
|
||||||
|
bool file_index_load(void);
|
||||||
|
bool file_index_save(void);
|
||||||
|
|
||||||
|
int file_index_find_fuzzy(const char *query, file_index_result_t *results, int max_results);
|
||||||
|
|
||||||
|
bool file_index_add_entry(const char *path, uint32_t size, uint32_t mod_time_low, uint32_t mod_time_high, bool is_dir);
|
||||||
|
bool file_index_remove_entry(const char *path);
|
||||||
|
|
||||||
|
int file_index_get_entry_count(void);
|
||||||
|
void file_index_clear(void);
|
||||||
|
void file_index_invalidate_cache(void);
|
||||||
|
bool file_index_is_valid(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
11
src/wm/cmd.c
11
src/wm/cmd.c
|
|
@ -2273,17 +2273,6 @@ static void create_ramfs_files(void) {
|
||||||
}
|
}
|
||||||
fat32_close(fh);
|
fat32_close(fh);
|
||||||
}
|
}
|
||||||
|
|
||||||
fh = fat32_open("root/Apps/DOOM.c", "w");
|
|
||||||
if (fh) {
|
|
||||||
const char *content =
|
|
||||||
"int main(){\n"
|
|
||||||
" puts(\"To DOOM, or not to DOOM.\\n\");\n"
|
|
||||||
" puts(\"-Me\\n\");\n"
|
|
||||||
"}\n";
|
|
||||||
fat32_write(fh, (void *)content, cmd_strlen(content));
|
|
||||||
fat32_close(fh);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -805,7 +805,7 @@ void explorer_open_directory(const char *path) {
|
||||||
explorer_create_window(path);
|
explorer_create_window(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void explorer_open_target(const char *path) {
|
void explorer_open_target(const char *path) {
|
||||||
if (vfs_is_directory(path)) {
|
if (vfs_is_directory(path)) {
|
||||||
explorer_open_directory(path);
|
explorer_open_directory(path);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -70,10 +70,10 @@ typedef struct {
|
||||||
|
|
||||||
void explorer_init(void);
|
void explorer_init(void);
|
||||||
void explorer_reset(void);
|
void explorer_reset(void);
|
||||||
void explorer_open_directory(const char *path); // Creates a NEW window
|
void explorer_open_directory(const char *path);
|
||||||
|
void explorer_open_target(const char *path);
|
||||||
|
|
||||||
|
|
||||||
// Drag and Drop support
|
|
||||||
// This now needs to find WHICH explorer window is under the mouse
|
|
||||||
bool explorer_get_file_at(int screen_x, int screen_y, char *out_path, bool *is_dir);
|
bool explorer_get_file_at(int screen_x, int screen_y, char *out_path, bool *is_dir);
|
||||||
void explorer_import_file(Window *win, const char *source_path); // To focused or default
|
void explorer_import_file(Window *win, const char *source_path); // To focused or default
|
||||||
void explorer_import_file_to(Window *win, const char *source_path, const char *dest_dir);
|
void explorer_import_file_to(Window *win, const char *source_path, const char *dest_dir);
|
||||||
|
|
@ -81,18 +81,15 @@ void explorer_refresh(Window *win);
|
||||||
void explorer_refresh_all(void);
|
void explorer_refresh_all(void);
|
||||||
void explorer_clear_click_state(Window *win);
|
void explorer_clear_click_state(Window *win);
|
||||||
|
|
||||||
// String Helpers
|
|
||||||
size_t explorer_strlen(const char *str);
|
size_t explorer_strlen(const char *str);
|
||||||
void explorer_strcpy(char *dest, const char *src);
|
void explorer_strcpy(char *dest, const char *src);
|
||||||
void explorer_strcat(char *dest, const char *src);
|
void explorer_strcat(char *dest, const char *src);
|
||||||
|
|
||||||
// Clipboard (System-wide)
|
|
||||||
void explorer_clipboard_copy(const char *path);
|
void explorer_clipboard_copy(const char *path);
|
||||||
void explorer_clipboard_cut(const char *path);
|
void explorer_clipboard_cut(const char *path);
|
||||||
void explorer_clipboard_paste(Window *win, const char *dest_dir);
|
void explorer_clipboard_paste(Window *win, const char *dest_dir);
|
||||||
bool explorer_clipboard_has_content(void);
|
bool explorer_clipboard_has_content(void);
|
||||||
|
|
||||||
// File Operations
|
|
||||||
bool explorer_delete_permanently(const char *path);
|
bool explorer_delete_permanently(const char *path);
|
||||||
bool explorer_delete_recursive(const char *path);
|
bool explorer_delete_recursive(const char *path);
|
||||||
void explorer_create_shortcut(Window *win, const char *target_path);
|
void explorer_create_shortcut(Window *win, const char *target_path);
|
||||||
|
|
|
||||||
|
|
@ -402,8 +402,11 @@ void draw_rounded_rect_blurred(int x, int y, int w, int h, int radius, uint32_t
|
||||||
|
|
||||||
for (int r = 0; r < h; r++) {
|
for (int r = 0; r < h; r++) {
|
||||||
int g_y = y + r;
|
int g_y = y + r;
|
||||||
|
if (g_y < 0 || g_y >= sh) continue;
|
||||||
|
|
||||||
for (int c = 0; c < w; c++) {
|
for (int c = 0; c < w; c++) {
|
||||||
int g_x = x + c;
|
int g_x = x + c;
|
||||||
|
if (g_x < 0 || g_x >= sw) continue;
|
||||||
|
|
||||||
int r_sum = 0, g_sum = 0, b_sum = 0, count = 0;
|
int r_sum = 0, g_sum = 0, b_sum = 0, count = 0;
|
||||||
int start_kx = g_x - blur_radius;
|
int start_kx = g_x - blur_radius;
|
||||||
|
|
|
||||||
337
src/wm/wm.c
337
src/wm/wm.c
|
|
@ -13,6 +13,8 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include "wallpaper.h"
|
#include "wallpaper.h"
|
||||||
#include "fat32.h"
|
#include "fat32.h"
|
||||||
|
#include "file_index.h"
|
||||||
|
#include "../dev/ps2.h"
|
||||||
#define STBI_NO_STDIO
|
#define STBI_NO_STDIO
|
||||||
#include "userland/stb_image.h"
|
#include "userland/stb_image.h"
|
||||||
#include "memory_manager.h"
|
#include "memory_manager.h"
|
||||||
|
|
@ -87,6 +89,125 @@ static int notif_x_offset = 420; // Starts offscreen
|
||||||
static bool notif_active = false;
|
static bool notif_active = false;
|
||||||
extern bool ps2_ctrl_pressed;
|
extern bool ps2_ctrl_pressed;
|
||||||
|
|
||||||
|
static spotlight_state_t spotlight_state = {0};
|
||||||
|
static bool spotlight_index_built = false; // Track if index has been built
|
||||||
|
|
||||||
|
static bool force_redraw = true;
|
||||||
|
|
||||||
|
static void spotlight_update_search(void) {
|
||||||
|
// Note: Index must already be loaded/built before searching
|
||||||
|
// Don't build here - this runs on every keystroke!
|
||||||
|
|
||||||
|
int query_hash = 0;
|
||||||
|
for (int i = 0; spotlight_state.search_query[i] && i < 256; i++) {
|
||||||
|
query_hash = (query_hash * 31) + spotlight_state.search_query[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query_hash == spotlight_state.last_query_hash) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spotlight_state.last_query_hash = query_hash;
|
||||||
|
spotlight_state.result_count = 0;
|
||||||
|
spotlight_state.selected_index = 0;
|
||||||
|
|
||||||
|
if (spotlight_state.search_len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_index_result_t results[SPOTLIGHT_MAX_RESULTS];
|
||||||
|
int count = file_index_find_fuzzy(spotlight_state.search_query, results, SPOTLIGHT_MAX_RESULTS);
|
||||||
|
|
||||||
|
spotlight_state.result_count = count;
|
||||||
|
for (int i = 0; i < count && i < SPOTLIGHT_MAX_RESULTS; i++) {
|
||||||
|
spotlight_state.results[i] = results[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
int sw = get_screen_width();
|
||||||
|
int sh = get_screen_height();
|
||||||
|
int modal_height = SPOTLIGHT_SEARCH_HEIGHT + (spotlight_state.result_count * SPOTLIGHT_RESULT_HEIGHT) + 10;
|
||||||
|
int modal_y = (sh * 2 / 5) - (modal_height / 2);
|
||||||
|
|
||||||
|
graphics_mark_dirty(0, 0, sw, sh);
|
||||||
|
force_redraw = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wm_spotlight_handle_key(char c) {
|
||||||
|
if (c == 27) {
|
||||||
|
spotlight_state.visible = false;
|
||||||
|
force_redraw = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == '\n') {
|
||||||
|
if (spotlight_state.result_count > 0 && spotlight_state.selected_index < spotlight_state.result_count) {
|
||||||
|
const char *file_path = spotlight_state.results[spotlight_state.selected_index].entry.path;
|
||||||
|
explorer_open_target(file_path);
|
||||||
|
spotlight_state.visible = false;
|
||||||
|
force_redraw = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == 17) {
|
||||||
|
if (spotlight_state.selected_index > 0) {
|
||||||
|
spotlight_state.selected_index--;
|
||||||
|
force_redraw = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == 18) {
|
||||||
|
if (spotlight_state.selected_index < spotlight_state.result_count - 1) {
|
||||||
|
spotlight_state.selected_index++;
|
||||||
|
force_redraw = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == '\b' || c == 127) {
|
||||||
|
if (spotlight_state.cursor_pos > 0) {
|
||||||
|
for (int i = spotlight_state.cursor_pos - 1; i < spotlight_state.search_len; i++) {
|
||||||
|
spotlight_state.search_query[i] = spotlight_state.search_query[i + 1];
|
||||||
|
}
|
||||||
|
spotlight_state.search_len--;
|
||||||
|
spotlight_state.cursor_pos--;
|
||||||
|
spotlight_state.search_query[spotlight_state.search_len] = 0;
|
||||||
|
spotlight_update_search();
|
||||||
|
force_redraw = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == 19) {
|
||||||
|
if (spotlight_state.cursor_pos > 0) {
|
||||||
|
spotlight_state.cursor_pos--;
|
||||||
|
force_redraw = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == 20) {
|
||||||
|
if (spotlight_state.cursor_pos < spotlight_state.search_len) {
|
||||||
|
spotlight_state.cursor_pos++;
|
||||||
|
force_redraw = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c >= 32 && c <= 126 && spotlight_state.search_len < 255) {
|
||||||
|
for (int i = spotlight_state.search_len; i >= spotlight_state.cursor_pos; i--) {
|
||||||
|
spotlight_state.search_query[i + 1] = spotlight_state.search_query[i];
|
||||||
|
}
|
||||||
|
spotlight_state.search_query[spotlight_state.cursor_pos] = c;
|
||||||
|
spotlight_state.search_len++;
|
||||||
|
spotlight_state.cursor_pos++;
|
||||||
|
spotlight_state.search_query[spotlight_state.search_len] = 0;
|
||||||
|
spotlight_update_search();
|
||||||
|
force_redraw = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Dragging State
|
// Dragging State
|
||||||
static bool is_dragging = false;
|
static bool is_dragging = false;
|
||||||
static bool is_resizing = false;
|
static bool is_resizing = false;
|
||||||
|
|
@ -118,8 +239,8 @@ static Window *drag_src_win = NULL;
|
||||||
static Window *all_windows[32];
|
static Window *all_windows[32];
|
||||||
static int window_count = 0;
|
static int window_count = 0;
|
||||||
|
|
||||||
// Redraw system
|
// Redraw system (moved to be with spotlight state)
|
||||||
static bool force_redraw = true;
|
// static bool force_redraw = true; (moved earlier)
|
||||||
static uint32_t timer_ticks = 0;
|
static uint32_t timer_ticks = 0;
|
||||||
|
|
||||||
// Cursor state
|
// Cursor state
|
||||||
|
|
@ -1309,6 +1430,144 @@ bool rect_contains(int x, int y, int w, int h, int px, int py) {
|
||||||
return px >= x && px < x + w && py >= y && py < y + h;
|
return px >= x && px < x + w && py >= y && py < y + h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void wm_render_spotlight(int y_start, int y_end, DirtyRect dirty) {
|
||||||
|
if (!spotlight_state.visible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sw = get_screen_width();
|
||||||
|
int sh = get_screen_height();
|
||||||
|
|
||||||
|
int modal_width = SPOTLIGHT_MODAL_WIDTH;
|
||||||
|
int modal_height = SPOTLIGHT_SEARCH_HEIGHT + (spotlight_state.result_count * SPOTLIGHT_RESULT_HEIGHT) + 10;
|
||||||
|
|
||||||
|
if (spotlight_state.result_count == 0 && spotlight_state.search_len > 0) {
|
||||||
|
modal_height = SPOTLIGHT_SEARCH_HEIGHT + SPOTLIGHT_RESULT_HEIGHT + 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
int modal_x = (sw - modal_width) / 2;
|
||||||
|
int modal_y = (sh * 2 / 5) - (modal_height / 2);
|
||||||
|
|
||||||
|
if (modal_y + modal_height <= y_start || modal_y >= y_end) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw modal background - with subtle blur effect
|
||||||
|
draw_rounded_rect_blurred(modal_x, modal_y, modal_width, modal_height, 12, COLOR_DARK_PANEL, 1, 220);
|
||||||
|
|
||||||
|
int search_x = modal_x + 8;
|
||||||
|
int search_y = modal_y + 8;
|
||||||
|
int search_width = modal_width - 16;
|
||||||
|
int search_height = 32;
|
||||||
|
|
||||||
|
draw_rounded_rect_filled(search_x, search_y, search_width, search_height, 6, COLOR_DARK_BG);
|
||||||
|
|
||||||
|
if (spotlight_state.search_len > 0) {
|
||||||
|
draw_string(search_x + 8, search_y + 8, spotlight_state.search_query, COLOR_DARK_TEXT);
|
||||||
|
} else {
|
||||||
|
draw_string(search_x + 8, search_y + 8, "Search files...", COLOR_DKGRAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spotlight_state.cursor_pos <= spotlight_state.search_len && spotlight_state.search_len > 0) {
|
||||||
|
ttf_font_t *ttf = graphics_get_current_ttf();
|
||||||
|
char temp_query[256];
|
||||||
|
for (int i = 0; i < spotlight_state.cursor_pos && i < 255; i++) {
|
||||||
|
temp_query[i] = spotlight_state.search_query[i];
|
||||||
|
}
|
||||||
|
temp_query[spotlight_state.cursor_pos] = 0;
|
||||||
|
int cursor_x_offset = (ttf) ? font_manager_get_string_width(ttf, temp_query) : (spotlight_state.cursor_pos * 6);
|
||||||
|
draw_rect(search_x + 8 + cursor_x_offset, search_y + 8, 1, 16, COLOR_DARK_TEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < spotlight_state.result_count && i < SPOTLIGHT_MAX_RESULTS; i++) {
|
||||||
|
if (i < 0 || i >= SPOTLIGHT_MAX_RESULTS) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int result_x = modal_x + 4;
|
||||||
|
int result_y = modal_y + SPOTLIGHT_SEARCH_HEIGHT + 4 + (i * SPOTLIGHT_RESULT_HEIGHT);
|
||||||
|
int result_width = modal_width - 8;
|
||||||
|
|
||||||
|
if (i == spotlight_state.selected_index) {
|
||||||
|
draw_rounded_rect_filled(result_x, result_y, result_width, SPOTLIGHT_RESULT_HEIGHT - 2, 6, COLOR_DARK_BORDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *full_path = spotlight_state.results[i].entry.path;
|
||||||
|
if (!full_path || full_path[0] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *filename = full_path;
|
||||||
|
|
||||||
|
for (int j = 0; full_path[j]; j++) {
|
||||||
|
if (full_path[j] == '/') {
|
||||||
|
filename = &full_path[j + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filename || filename[0] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_string(result_x + 8, result_y + 8, filename, COLOR_DARK_TEXT);
|
||||||
|
|
||||||
|
if (!spotlight_state.results[i].entry.is_directory) {
|
||||||
|
char size_str[32];
|
||||||
|
uint32_t size = spotlight_state.results[i].entry.size;
|
||||||
|
|
||||||
|
if (size < 1024) {
|
||||||
|
// Bytes
|
||||||
|
size_str[0] = '0' + ((size / 1) % 10);
|
||||||
|
size_str[1] = 'B';
|
||||||
|
size_str[2] = 0;
|
||||||
|
} else if (size < 1024 * 1024) {
|
||||||
|
// Kilobytes - properly format for values up to 1023 KB
|
||||||
|
int kb = size / 1024;
|
||||||
|
if (kb >= 100) {
|
||||||
|
size_str[0] = '0' + (kb / 100);
|
||||||
|
size_str[1] = '0' + ((kb / 10) % 10);
|
||||||
|
size_str[2] = '0' + (kb % 10);
|
||||||
|
size_str[3] = 'K';
|
||||||
|
size_str[4] = 'B';
|
||||||
|
size_str[5] = 0;
|
||||||
|
} else {
|
||||||
|
size_str[0] = '0' + (kb / 10);
|
||||||
|
size_str[1] = '0' + (kb % 10);
|
||||||
|
size_str[2] = 'K';
|
||||||
|
size_str[3] = 'B';
|
||||||
|
size_str[4] = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Megabytes - properly format for any MB value
|
||||||
|
int mb = size / (1024 * 1024);
|
||||||
|
if (mb >= 100) {
|
||||||
|
size_str[0] = '0' + (mb / 100);
|
||||||
|
size_str[1] = '0' + ((mb / 10) % 10);
|
||||||
|
size_str[2] = '0' + (mb % 10);
|
||||||
|
size_str[3] = 'M';
|
||||||
|
size_str[4] = 'B';
|
||||||
|
size_str[5] = 0;
|
||||||
|
} else {
|
||||||
|
size_str[0] = '0' + (mb / 10);
|
||||||
|
size_str[1] = '0' + (mb % 10);
|
||||||
|
size_str[2] = 'M';
|
||||||
|
size_str[3] = 'B';
|
||||||
|
size_str[4] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int size_x = result_x + result_width - 8 - 32; // Account for wider size strings
|
||||||
|
draw_string(size_x, result_y + 8, size_str, COLOR_DKGRAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw "No results" message if needed
|
||||||
|
if (spotlight_state.search_len > 0 && spotlight_state.result_count == 0) {
|
||||||
|
int msg_y = modal_y + SPOTLIGHT_SEARCH_HEIGHT + 10;
|
||||||
|
draw_string(modal_x + 20, msg_y, "No results found", COLOR_DKGRAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Window *sorted_windows_cache[32];
|
static Window *sorted_windows_cache[32];
|
||||||
static int sorted_window_count_cache = 0;
|
static int sorted_window_count_cache = 0;
|
||||||
|
|
||||||
|
|
@ -1393,7 +1652,7 @@ static void wm_paint_region(int y_start, int y_end, DirtyRect dirty, int pass) {
|
||||||
if (dock_y < cy + ch && dock_y + dock_h > cy) {
|
if (dock_y < cy + ch && dock_y + dock_h > cy) {
|
||||||
int d_item_sz = 48, d_space = 10, d_total_w = 12 * (d_item_sz + d_space);
|
int d_item_sz = 48, d_space = 10, d_total_w = 12 * (d_item_sz + d_space);
|
||||||
int d_bg_x = (sw - d_total_w) / 2 - 12, d_bg_w = d_total_w + 24;
|
int d_bg_x = (sw - d_total_w) / 2 - 12, d_bg_w = d_total_w + 24;
|
||||||
draw_rounded_rect_blurred(d_bg_x, dock_y, d_bg_w, dock_h, 18, COLOR_DOCK_BG, 5, 140);
|
draw_rounded_rect_blurred(d_bg_x, dock_y, d_bg_w, dock_h, 18, COLOR_DOCK_BG, 1, 180);
|
||||||
int dx = (sw - d_total_w) / 2, dy = dock_y + 6;
|
int dx = (sw - d_total_w) / 2, dy = dock_y + 6;
|
||||||
draw_dock_files(dx, dy); dx += d_item_sz+d_space;
|
draw_dock_files(dx, dy); dx += d_item_sz+d_space;
|
||||||
draw_dock_settings(dx, dy); dx += d_item_sz+d_space;
|
draw_dock_settings(dx, dy); dx += d_item_sz+d_space;
|
||||||
|
|
@ -1466,6 +1725,9 @@ static void wm_paint_region(int y_start, int y_end, DirtyRect dirty, int pass) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render spotlight modal
|
||||||
|
wm_render_spotlight(cy, cy + ch, dirty);
|
||||||
|
|
||||||
if (wm_custom_paint_hook) wm_custom_paint_hook();
|
if (wm_custom_paint_hook) wm_custom_paint_hook();
|
||||||
|
|
||||||
if (is_dragging_file) {
|
if (is_dragging_file) {
|
||||||
|
|
@ -1679,6 +1941,29 @@ void wm_handle_click(int x, int y) {
|
||||||
int sh = get_screen_height();
|
int sh = get_screen_height();
|
||||||
int sw = get_screen_width();
|
int sw = get_screen_width();
|
||||||
|
|
||||||
|
if (spotlight_state.visible) {
|
||||||
|
int modal_width = SPOTLIGHT_MODAL_WIDTH;
|
||||||
|
int modal_height = SPOTLIGHT_SEARCH_HEIGHT + (spotlight_state.result_count * SPOTLIGHT_RESULT_HEIGHT) + 10;
|
||||||
|
int modal_x = (sw - modal_width) / 2;
|
||||||
|
int modal_y = (sh * 2 / 5) - (modal_height / 2);
|
||||||
|
|
||||||
|
if (rect_contains(modal_x, modal_y, modal_width, modal_height, x, y)) {
|
||||||
|
int result_click_y = y - (modal_y + SPOTLIGHT_SEARCH_HEIGHT + 4);
|
||||||
|
if (result_click_y >= 0 && result_click_y < spotlight_state.result_count * SPOTLIGHT_RESULT_HEIGHT) {
|
||||||
|
int result_idx = result_click_y / SPOTLIGHT_RESULT_HEIGHT;
|
||||||
|
if (result_idx >= 0 && result_idx < spotlight_state.result_count) {
|
||||||
|
spotlight_state.selected_index = result_idx;
|
||||||
|
const char *file_path = spotlight_state.results[result_idx].entry.path;
|
||||||
|
spotlight_state.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spotlight_state.visible = false;
|
||||||
|
}
|
||||||
|
force_redraw = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (msg_box_visible) {
|
if (msg_box_visible) {
|
||||||
int mw = 320;
|
int mw = 320;
|
||||||
int mh = 100;
|
int mh = 100;
|
||||||
|
|
@ -2712,12 +2997,42 @@ void wm_show_notification(const char *msg) {
|
||||||
force_redraw = true;
|
force_redraw = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wrapper for work queue - builds file index asynchronously
|
||||||
|
static void build_file_index_async(void *arg) {
|
||||||
|
(void)arg; // Unused
|
||||||
|
file_index_build();
|
||||||
|
}
|
||||||
|
|
||||||
void wm_handle_key(char c, bool pressed) {
|
void wm_handle_key(char c, bool pressed) {
|
||||||
if (pressed && c == 'p' && ps2_ctrl_pressed) {
|
if (pressed && c == 'p' && ps2_ctrl_pressed) {
|
||||||
process_create_elf("/bin/screenshot.elf", NULL);
|
process_create_elf("/bin/screenshot.elf", NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pressed && c == ' ' && ps2_ctrl_pressed && ps2_shift_pressed()) {
|
||||||
|
spotlight_state.visible = !spotlight_state.visible;
|
||||||
|
if (spotlight_state.visible) {
|
||||||
|
// Check current index status - it may still be building in background
|
||||||
|
spotlight_index_built = file_index_is_valid();
|
||||||
|
// Clear search state when opening
|
||||||
|
spotlight_state.search_len = 0;
|
||||||
|
spotlight_state.search_query[0] = 0;
|
||||||
|
spotlight_state.cursor_pos = 0;
|
||||||
|
spotlight_state.result_count = 0;
|
||||||
|
spotlight_state.selected_index = 0;
|
||||||
|
}
|
||||||
|
int sw = get_screen_width();
|
||||||
|
int sh = get_screen_height();
|
||||||
|
graphics_mark_dirty(0, 0, sw, sh);
|
||||||
|
force_redraw = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spotlight_state.visible && pressed) {
|
||||||
|
wm_spotlight_handle_key(c);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int next = (key_head + 1) % INPUT_QUEUE_SIZE;
|
int next = (key_head + 1) % INPUT_QUEUE_SIZE;
|
||||||
if (next != key_tail) {
|
if (next != key_tail) {
|
||||||
key_queue[key_head].c = c;
|
key_queue[key_head].c = c;
|
||||||
|
|
@ -2786,12 +3101,23 @@ void wm_process_deferred_thumbs(void) {
|
||||||
void wm_init(void) {
|
void wm_init(void) {
|
||||||
disk_manager_init();
|
disk_manager_init();
|
||||||
disk_manager_scan();
|
disk_manager_scan();
|
||||||
// Drives are now dynamically managed - only real drives are registered
|
|
||||||
|
|
||||||
cmd_init();
|
cmd_init();
|
||||||
explorer_init();
|
explorer_init();
|
||||||
wallpaper_init();
|
wallpaper_init();
|
||||||
|
|
||||||
|
file_index_init();
|
||||||
|
|
||||||
|
// Try to load the file index from persistent cache
|
||||||
|
// If it doesn't exist, queue an async build
|
||||||
|
if (!file_index_load()) {
|
||||||
|
// No cache exists - queue async build to background
|
||||||
|
work_queue_submit(build_file_index_async, NULL);
|
||||||
|
} else {
|
||||||
|
// Cache loaded - mark as ready
|
||||||
|
spotlight_index_built = true;
|
||||||
|
}
|
||||||
|
|
||||||
refresh_desktop_icons();
|
refresh_desktop_icons();
|
||||||
|
|
||||||
// Initialize z-indices
|
// Initialize z-indices
|
||||||
|
|
@ -2857,4 +3183,7 @@ void wm_timer_tick(void) {
|
||||||
|
|
||||||
void wm_notify_fs_change(void) {
|
void wm_notify_fs_change(void) {
|
||||||
periodic_refresh_pending = true;
|
periodic_refresh_pending = true;
|
||||||
|
|
||||||
|
file_index_invalidate_cache();
|
||||||
|
spotlight_index_built = false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
src/wm/wm.h
18
src/wm/wm.h
|
|
@ -68,6 +68,24 @@ struct Window {
|
||||||
bool resizable;
|
bool resizable;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define SPOTLIGHT_MAX_RESULTS 6
|
||||||
|
#define SPOTLIGHT_MODAL_WIDTH 520
|
||||||
|
#define SPOTLIGHT_RESULT_HEIGHT 40
|
||||||
|
#define SPOTLIGHT_SEARCH_HEIGHT 48
|
||||||
|
|
||||||
|
#include "../sys/file_index.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool visible;
|
||||||
|
char search_query[256];
|
||||||
|
int search_len;
|
||||||
|
int cursor_pos;
|
||||||
|
file_index_result_t results[SPOTLIGHT_MAX_RESULTS];
|
||||||
|
int result_count;
|
||||||
|
int selected_index;
|
||||||
|
int last_query_hash;
|
||||||
|
} spotlight_state_t;
|
||||||
|
|
||||||
void wm_init(void);
|
void wm_init(void);
|
||||||
void wm_handle_mouse(int dx, int dy, uint8_t buttons, int dz);
|
void wm_handle_mouse(int dx, int dy, uint8_t buttons, int dz);
|
||||||
void wm_handle_key(char c, bool pressed);
|
void wm_handle_key(char c, bool pressed);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue