Meta: Add files to repository

This commit is contained in:
Alec Murphy 2025-03-25 07:32:23 -04:00
parent 80a0428b66
commit 39198164cd
1029 changed files with 78311 additions and 0 deletions

1112
System/Core/Compositor.HC Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,3 @@
"filesystem ";

67
System/Core/Menu.HC Normal file
View file

@ -0,0 +1,67 @@
// Core component for Menu functions
#define MENU_ITEM_MIN_HEIGHT 24
#define MENU_ITEM_MIN_WIDTH 256
I64 @menu_get_items_count(Window* win)
{
I64 count = 0;
@window_widgets_list* wl = win->widget;
while (wl) {
if (wl->widget) {
if (wl->widget->type == WIDGET_TYPE_MENU_ITEM)
count++;
}
wl = wl->next;
}
return count;
}
MenuItemWidget* @menu_add_item(Window* win, U8* text, Context2D* icon,
U64 callback, U8* path = NULL,
Window* submenu = NULL)
{
I64 items_count = @menu_get_items_count(win);
win->height = 8;
win->height += MENU_ITEM_MIN_HEIGHT * (items_count + 1);
MenuItemWidget* item = Gui.CreateWidget(
win, WIDGET_TYPE_MENU_ITEM, 0, MENU_ITEM_MIN_HEIGHT * items_count,
MENU_ITEM_MIN_WIDTH, MENU_ITEM_MIN_HEIGHT);
Gui.Widget.SetText(item, text);
if (icon)
item->icon = icon;
if (path)
item->path = StrNew(path);
if (submenu)
item->submenu = submenu;
if (callback)
Gui.Widget.SetCallback(item, "clicked", callback);
return item;
}
Window* @menu_new(U8* title = NULL)
{
Window* menu = Compositor.CreateWindow(
0, 0, MENU_ITEM_MIN_WIDTH, MENU_ITEM_MIN_HEIGHT,
WIN_FLAGS_NOHILIGHT | WIN_FLAGS_SKIP | WIN_FLAGS_MENU);
Gui.Window.Hide(menu);
if (title)
Gui.Window.SetTitle(menu, title);
else
Gui.Window.SetTitle(menu, "");
return menu;
}
class @menu
{
MenuItemWidget* (*AddItem)(Window* win, U8* text, Context2D* icon,
U64 callback, U8* path = NULL,
Window* submenu = NULL);
Window* (*New)(U8* title);
};
@menu Menu;
Menu.AddItem = &@menu_add_item;
Menu.New = &@menu_new;
"menu ";

108
System/Core/MessageBox.HC Normal file
View file

@ -0,0 +1,108 @@
// Core component for MessageBox functions
Context2D* MESSAGEBOX_ICON_ERR = Image.FileToContext2D(
"/Media/Themes/Umami/Icon/status/messagebox_critical.png");
Context2D* MESSAGEBOX_ICON_INFO = Image.FileToContext2D("/Media/Themes/Umami/Icon/status/messagebox_info.png");
Context2D* MESSAGEBOX_ICON_WARN = Image.FileToContext2D(
"/Media/Themes/Umami/Icon/status/messagebox_warning.png");
Context2D* MESSAGEBOX_WIN_ICON_ERR = Image.FileToContext2D(
"/Media/Themes/Umami/Icon/status/messagebox_critical_16x16.png");
Context2D* MESSAGEBOX_WIN_ICON_INFO = Image.FileToContext2D(
"/Media/Themes/Umami/Icon/status/messagebox_info_16x16.png");
Context2D* MESSAGEBOX_WIN_ICON_WARN = Image.FileToContext2D(
"/Media/Themes/Umami/Icon/status/messagebox_warning_16x16.png");
#define MESSAGEBOX_TYPE_ALERT 0
#define MESSAGEBOX_TYPE_ERROR 1
#define MESSAGEBOX_TYPE_INFO 2
U0 @messagebox_close_window(Window* window)
{
Compositor.DestroyWindow(window);
}
U0 @messagebox_close_widget(Widget* widget)
{
Compositor.DestroyWindow(widget->parent_win);
}
U0 @messagebox_msg(U8* str, I64 type, Context2D* win_icon, Context2D* icon,
U8* options = NULL, U64 callback = NULL)
{
U64 flags = WIN_FLAGS_MOVABLE | WIN_FLAGS_ICON | WIN_FLAGS_TITLE_BAR | WIN_FLAGS_CLOSE_BUTTON;
Window* win = Compositor.CreateWindow(0, 0, 320, 192, flags);
Gui.Window.SetIcon(win, win_icon);
switch (type) {
case MESSAGEBOX_TYPE_ALERT:
Gui.Window.SetTitle(win, "Alert");
break;
case MESSAGEBOX_TYPE_ERROR:
Gui.Window.SetTitle(win, "Error");
break;
case MESSAGEBOX_TYPE_INFO:
Gui.Window.SetTitle(win, "Info");
break;
}
Context2DWidget* ctx_icon = Gui.CreateWidget(win, WIDGET_TYPE_CONTEXT2D, 8, 16, 24, 24);
ctx_icon->ctx = icon;
TextLabelWidget* lbl_text = Gui.CreateWidget(win, WIDGET_TYPE_LABEL, 40, 16, 192, 96);
ButtonWidget* btn_ok = NULL;
ButtonWidget* btn_cancel = NULL;
Gui.Widget.SetText(lbl_text, str);
Gui.Window.SetCallback(win, "close", callback);
if (options) {
btn_ok = Gui.CreateWidget(win, WIDGET_TYPE_BUTTON, (win->width / 2) - 80,
win->height - 60, 64, 24);
btn_cancel = Gui.CreateWidget(win, WIDGET_TYPE_BUTTON, (win->width / 2) + 16,
win->height - 60, 64, 24);
Gui.Widget.SetText(btn_ok, " OK ");
Gui.Widget.SetText(btn_cancel, " Cancel ");
Gui.Widget.SetCallback(btn_ok, "clicked", callback);
Gui.Widget.SetCallback(btn_cancel, "clicked", callback);
btn_ok->tag = TRUE;
btn_cancel->tag = FALSE;
} else {
btn_ok = Gui.CreateWidget(win, WIDGET_TYPE_BUTTON, (win->width / 2) - 32,
win->height - 60, 64, 24);
Gui.Widget.SetText(btn_ok, " OK ");
Gui.Widget.SetCallback(btn_ok, "clicked", callback);
btn_ok->tag = TRUE;
}
Gui.Window.Center(win);
Gui.Window.SetFocus(win);
}
U0 @messagebox_alert(U8* str, U8* options = NULL, U64 callback = NULL)
{
@messagebox_msg(str, MESSAGEBOX_TYPE_ALERT, MESSAGEBOX_WIN_ICON_WARN,
MESSAGEBOX_ICON_WARN, options, callback);
}
U0 @messagebox_error(U8* str, U8* options = NULL, U64 callback = NULL)
{
@messagebox_msg(str, MESSAGEBOX_TYPE_ERROR, MESSAGEBOX_WIN_ICON_ERR,
MESSAGEBOX_ICON_ERR, options, callback);
}
U0 @messagebox_info(U8* str, U8* options = NULL, U64 callback = NULL)
{
@messagebox_msg(str, MESSAGEBOX_TYPE_INFO, MESSAGEBOX_WIN_ICON_INFO,
MESSAGEBOX_ICON_INFO, options, callback);
}
class @messagebox
{
U0* (*Alert)(U8* str, U8* options = NULL, U64 callback = NULL);
U0* (*Error)(U8* str, U8* options = NULL, U64 callback = NULL);
U0* (*Info)(U8* str, U8* options = NULL, U64 callback = NULL);
};
@messagebox MessageBox;
MessageBox.Alert = &@messagebox_alert;
MessageBox.Error = &@messagebox_error;
MessageBox.Info = &@messagebox_info;
"messagebox ";

79
System/Core/Scheduler.HC Normal file
View file

@ -0,0 +1,79 @@
/* clang-format off */
U0 @scheduler_restore_page_table(U64 i) {
U64 reg R15 _pt = i.u32[1];
if (_pt) { // Use CTask's page table entries
asm {
MOV RAX, R15
MOV_CR3_RAX
}
} else { // Use identity mapping
asm {
MOV EAX, [MEM_PML4]
MOV_CR3_RAX
}
}
}
asm {
ERYTHROS_TASK_CONTEXT_RESTORE_START::
XOR RAX,RAX
INC U64 GS:CCPU.swap_cnter[RAX]
MOV RSI,FS:CTask.addr[RAX]
BT U32 CTask.rflags[RSI],RFLAGf_INT
JNC @@05
BTS U32 GS:CCPU.cpu_flags[RAX],CPUf_RAN_A_TASK
@@05: BT U64 CTask.task_flags[RSI],TASKf_DISABLE_BPTS
JC @@15
MOV RDX,U64 CTask.bpt_lst[RSI]
@@10: TEST RDX,RDX
JZ @@15
MOV RDI,U64 CBpt.addr[RDX]
MOV U8 [RDI],OC_BPT
MOV RDX,U64 CBpt.next[RDX]
JMP @@10
@@15: INC U64 CTask.swap_cnter[RSI]
MOV RAX, U64 CTask.user_data[RSI]
PUSH RAX
CALL I32 &@scheduler_restore_page_table
MOV RAX,U64 CTask.fpu_mmx[RSI]
FXRSTOR U64 [RAX]
MOV RAX,RSP
LEA RSP,U64 CTask.rcx[RSI]
POP RCX
POP RDX
POP RBX
POP RBP
POP RDI
POP R8
POP R9
POP R10
POP R11
POP R12
POP R13
POP R14
POP R15
MOV RSP,RAX
MOV RAX,U64 CTask.rax[RSI]
PUSH CGDT.ds
PUSH U64 CTask.rsp[RSI]
PUSH U64 CTask.rflags[RSI]
PUSH CGDT.cs64
PUSH U64 CTask.rip[RSI]
MOV RSI,U64 CTask.rsi[RSI]
IRET
ERYTHROS_TASK_CONTEXT_RESTORE_END::
NOP
//************************************
}
/* clang-format on */
Function.Patch(_TASK_CONTEXT_RESTORE, ERYTHROS_TASK_CONTEXT_RESTORE_START);
"scheduler ";

282
System/Core/Shell.HC Normal file
View file

@ -0,0 +1,282 @@
#define SHELL_OPTS_ERR_INVALID_OPT -1
#define SHELL_OPTS_ERR_EXTRA_OPD -2
U0 @shell_free_args(I64 argc, U8** argv)
{
I64 i;
for (i = 0; i < argc; i++)
Free(argv[i]);
if (argv)
Free(argv);
}
U8** @shell_parse_args(@shell* sh, U8* str,
I64* argc)
{ // Return argc, argv from str.
Bool quoted = FALSE;
I64 _argc = 0;
U8** _argv = NULL;
U8** _tmp = CAlloc(sizeof(U64) * StrLen(str));
I64 i = 0;
I64 s = 0;
I64 len;
while (i < StrLen(str) + 1) {
switch (str[i]) {
case 0:
case ' ':
if (!quoted) {
len = (str + i) - (str + s - 1);
if (str[i - 1] == '"')
len--;
if (len - 1) {
_tmp[_argc] = CAlloc(len);
MemCpy(_tmp[_argc], str + s, len - 1);
_argc++;
}
s = i + 1;
}
break;
case '"':
quoted = !quoted;
if (quoted)
s = i + 1;
break;
default:
break;
}
i++;
}
*argc = _argc;
_argv = CAlloc(sizeof(U64) * _argc);
MemCpy(_argv, _tmp, sizeof(U64) * _argc);
Free(_tmp);
return _argv;
}
I64 @shell_parse_opts(@shell* sh, U8* op_lst, I64 argc, U8** argv, I64* flags,
U64* op_err, Bool ignore_extra_opd = FALSE)
{
I64 i, j;
U8 op_chr[2];
op_chr[1] = 0;
for (i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
for (j = 1; j < StrLen(argv[i]); j++) {
op_chr[0] = argv[i][j];
if (StrFind(&op_chr, op_lst))
*flags |= 1 << (StrFind(&op_chr, op_lst) - op_lst);
else {
*op_err = StrNew(&op_chr);
return SHELL_OPTS_ERR_INVALID_OPT;
}
}
} else {
if (!ignore_extra_opd) {
*op_err = StrNew(argv[i]);
return SHELL_OPTS_ERR_EXTRA_OPD;
}
}
}
return 0;
}
U8* @shell_expand_relative_path(@shell* sh, U8* path)
{
if (!path || !sh)
return NULL;
if (StrLen(path) < 1)
return NULL;
switch (path[0]) {
case '/':
return StrNew(path);
break;
default:
U8* abs_path = CAlloc(StrLen(path) + StrLen(&sh->cwd) + 4);
StrPrint(abs_path, "%s/%s", &sh->cwd, path);
return abs_path;
break;
}
}
U8* @shell_get_env_var(@shell* sh, U8* key)
{
@shell_env_var* var = sh->env->next;
while (var) {
if (StrLen(&var->key) && StrLen(&var->value))
if (!StrCmp(&var->key, key))
return &var->value;
var = var->next;
}
return "";
}
U0 @shell_set_env_var(@shell* sh, U8* key, U8* value)
{
if (!sh || !key || !value)
return;
@shell_env_var* var = sh->env->next;
while (var->next) {
if (!StrCmp(&var->key, key)) {
StrCpy(&var->value, value);
return;
}
var = var->next;
}
@shell_env_var* new = CAlloc(sizeof(@shell_env_var));
StrCpy(&new->key, key);
StrCpy(&new->value, value);
new->prev = var;
var->next = new;
}
U0 @shell_unset_env_var(@shell* sh, U8* key)
{
@shell_env_var* var = sh->env->next;
@shell_env_var* prev = NULL;
@shell_env_var* next = NULL;
while (var) {
if (!StrCmp(&var->key, key)) {
prev = var->prev;
next = var->next;
if (prev)
prev->next = next;
if (next)
next->prev = prev;
Free(var);
return;
}
var = var->next;
}
}
U0 @shell_history_append(@shell* sh, U8* str)
{
if (!sh || !str)
return;
if (!StrCmp(str, ""))
return;
I64 i;
sh->history.entries[sh->history.pos] = StrNew(str);
sh->history.pos++;
if (sh->history.pos > SHELL_HISTORY_LIMIT - 1) {
Free(sh->history.entries[0]);
for (i = 0; i < SHELL_HISTORY_LIMIT; i++)
sh->history.entries[i] = sh->history.entries[i + 1];
sh->history.pos--;
}
}
U0 @shell_process_args(@shell* sh, I64 argc, U8** argv)
{
if (argc < 1 || !argv)
return;
I64 i;
U8 buf[256];
for (i = 0; i < argc; i++) {
if (argv[i][0] == '\d') {
switch (argv[i][1]) {
case '?':
Free(argv[i]);
StrPrint(&buf, "%d", sh->answer);
argv[i] = StrNew(&buf);
break;
case '0':
Free(argv[i]);
argv[i] = StrNew("esh");
break;
default:
Free(argv[i]);
argv[i] = StrNew(@shell_get_env_var(sh, argv[i] + 1));
break;
}
}
if (!StrCmp(argv[i], "~")) {
Free(argv[i]);
argv[i] = StrNew(&sh->session->home);
}
}
}
U0 @shell_update_prompts(@shell* sh)
{
U8 buf[512];
U8 buf2[512];
StrCpy(&buf, &sh->cwd);
StrPrint(buf2, "/home/%s", &sh->session->user.name);
if (!StrCmp(&buf, &buf2))
StrCpy(&buf, "~");
else
StrCpy(&buf, StrLastOcc(&buf, "/") + 1);
StrPrint(&sh->PS1, "[%s@%s %s]\d ", &sh->session->user.name,
&sh->session->hostname, &buf);
}
I64 @shell_input_loop(@shell* sh)
{
CHashFun* cmd;
I64 argc;
U8** argv;
U8 buf[4096];
Bool exit = FALSE;
I64 i;
I64 (*@shell_exec)(@shell* sh, I64 argc, U8** argv);
while (!exit) {
@shell_update_prompts(sh);
Stdio.WriteLine(sh, &sh->PS1);
Stdio.ReadLine(sh, &buf);
@shell_history_append(sh, &buf);
argv = @shell_parse_args(sh, &buf, &argc);
if (argc) {
if (!StrCmp(argv[0], "exit")) {
exit = TRUE;
goto @shell_exit;
}
@shell_process_args(sh, argc, argv);
StrPrint(&buf, "@shell_cmd_%s", argv[0]);
cmd = HashFind(&buf, adam_task->hash_table, HTT_FUN);
if (cmd) {
@shell_exec = cmd->exe_addr;
sh->answer = @shell_exec(sh, argc, argv);
sh->break = FALSE;
FifoU8Flush(sh->input);
} else {
StrPrint(&buf, "%s: command not found\n", argv[0]);
Stdio.WriteLine(sh, &buf);
sh->answer = 0;
}
}
@shell_exit : @shell_free_args(argc, argv);
}
return 0;
}
U0 @shell_instance(@shell* sh)
{
@shell_input_loop(sh);
sh->exit = TRUE;
}
U0 @shell_init(@shell* sh)
{
sh->env = CAlloc(sizeof(@shell_env_var));
sh->history.limit = SHELL_HISTORY_LIMIT;
sh->history.pos = 0;
sh->history.entries = CAlloc(sizeof(U64) * SHELL_HISTORY_LIMIT);
sh->input = FifoU8New(SHELL_INPUT_FIFO_SIZE);
sh->task = Spawn(&@shell_instance, sh);
}
@shell* @shell_new(Bool headless = FALSE)
{
@shell* sh = CAlloc(sizeof(@shell));
if (!headless)
@shell_init(sh);
StrCpy(&sh->cwd, &Compositor.session.home);
return sh;
}
"shell ";

View file

@ -0,0 +1,13 @@
extern I64 @systemstarter_open(@shell* sh, I64 argc, U8** argv);
CDirEntry* sc_de = FilesFind("M:/System/Shell/Commands/*.HC");
CDirEntry* sc_de2 = sc_de;
while (sc_de2) {
if (!(!StrCmp(sc_de2->name, ".") && !StrCmp(sc_de2->name, ".."))) {
ExeDoc(DocRead(sc_de2->full_name));
}
sc_de2 = sc_de2->next;
}
DirTreeDel(sc_de);
"shellcommands ";

View file

@ -0,0 +1,214 @@
#define SYSSTART_MSG_NULL 0
#define SYSSTART_MSG_SPAWN 1
#define SYSSTART_MSG_KILL 2
class @systemtask
{
U8* path;
U8* name;
};
class @systemstarter
{
CTask* task;
U0 (*CreateTask)(U8* path, U8* name);
U0 (*Init)();
U0 (*Spawn)(U8* path, U8* name);
U0 (*Task)();
};
@systemstarter SystemStarter;
U32 @systemstarter_ext(U8* path)
{
U32 res = NULL;
U8* ext = FileSystem.GetFileExtension(path);
MemCpy(&res, ext, StrLen(ext));
return res;
}
U0 @systemstarter_init() { }
I64 @systemstarter_open(@shell* sh, I64 argc, U8** argv)
{
U8 buf[512];
U8* path = @shell_expand_relative_path(sh, argv[1]);
if (!FileSystem.PathExists(path)) {
Stdio.WriteLine(sh, "error: path does not exist: ");
Stdio.WriteLine(sh, path);
Stdio.WriteLine(sh, "\n");
Free(path);
return 1;
}
switch (@systemstarter_ext(path)) {
case 'app':
StrCpy(&buf, "");
String.Append(&buf, "I64 @exe_doc_buf_size = NULL; ");
String.Append(&buf,
"U8 *@exe_doc_buf = FileSystem.ReadFile(\"%s/Run.HC\", "
"&@exe_doc_buf_size); ",
path);
String.Append(&buf, "ExePutS(@exe_doc_buf); ");
String.Append(&buf, "Free(@exe_doc_buf); ");
// System.Log(Fs, &buf);
CTask* task = User;
TaskExe(task, NULL, "Raw(ON);\n", 0);
TaskExe(task, NULL, &buf, 0);
return 0;
break;
default:
Stdio.WriteLine(sh, "error: unknown or unsupported file type.\n");
// Free(path);
return 1;
break;
};
}
U0 @systemstarter_spawn(U8* path, U8* name)
{
CTask* task = Spawn(&UserCmdLine);
Sleep(Rand * 100);
U8 change_path_str[512];
StrPrint(task->task_name, name);
StrPrint(change_path_str, "Cd(\"%s\");\n", path);
TaskExe(task, NULL, "Raw(ON);\n", 0);
TaskExe(task, NULL, change_path_str, 0);
TaskExe(task, NULL, "ExeFile(\"Run.HC\");\n", 0);
Sleep(Rand * 100);
}
U0 @systemstarter_load_applets()
{
U8 applet_name[512];
CDirEntry* de = FilesFind("M:/Applets/*.applet");
CDirEntry* tmpde = de;
while (tmpde) {
StrCpy(&applet_name, StrLastOcc(tmpde->full_name, "/") + 1);
*(StrFirstOcc(&applet_name, ".")) = NULL;
SystemStarter.Spawn(tmpde->full_name, &applet_name);
tmpde = tmpde->next;
}
DirTreeDel(de);
}
U0 @systemstarter_play_user_startup_sound()
{
U8 path[512];
StrPrint(&path, "/home/%s/.sounds/startup.wav",
&Compositor.session.user.name);
U8** argv = CAlloc(sizeof(U64) * 2);
argv[0] = "aplay";
argv[1] = &path;
@shell* sh = @shell_new(TRUE);
sh->session = &Compositor.session;
//@shell_cmd_aplay(sh, 2, argv);
Free(sh);
}
U0 @systemstarter_set_user_wallpaper()
{
U8 path[512];
StrPrint(&path, "/home/%s/.wallpaper/wallpaper.png",
&Compositor.session.user.name);
U8** argv = CAlloc(sizeof(U64) * 2);
argv[0] = "wpset";
argv[1] = &path;
//@shell_cmd_wpset(NULL, 2, argv);
}
U0 @systemstarter_user_startup()
{
// Set User wallpaper
@systemstarter_set_user_wallpaper();
// Play User startup sound
@systemstarter_play_user_startup_sound();
}
U0 @systemstarter_startup()
{
// Set user-specific startup preferences
Spawn(&@systemstarter_user_startup);
// Initialize Clipboard
Clipboard.Init();
// Spawn Clipboard Task
Spawn(Clipboard.Task, , "Clipboard", T(mp_cnt > 3, 2, 1));
// Initialize SystemTray
SystemTray.Init();
// Spawn SystemTray Task
Spawn(SystemTray.Task, , "SystemTray", T(mp_cnt > 3, 2, 1));
SystemStarter.Spawn("M:/Applications/OS/Wallpaper.app", "Wallpaper");
SystemStarter.Spawn("M:/Applications/OS/MenuBar.app", "MenuBar");
SystemStarter.Spawn("M:/Applications/OS/TaskSwitcher.app",
"TaskSwitcher");
// Load SystemTray Applets
@systemstarter_load_applets;
}
U0 @systemstarter_ipc_queue_process()
{
IpcMessage* msg;
@systemtask* st = NULL;
msg = Ipc.MsgRecv();
if (msg) {
switch (msg->type) {
case SYSSTART_MSG_SPAWN:
if (msg->payload) {
st = msg->payload;
if (st->path && st->name) {
SystemStarter.Spawn(st->path, st->name);
System.Log(Fs, "Received message ← CreateTask (%s)", st->name);
Free(st->name);
Free(st->path);
}
Free(st);
}
break;
case SYSSTART_MSG_KILL:
break;
default:
break;
}
Free(msg);
}
}
U0 @systemstarter_task()
{
Ipc.InitQueue(Fs);
System.Log(Fs, "Task running at 0x%08x", Fs);
SystemStarter.task = Fs;
Spawn(&@systemstarter_startup, , , T(mp_cnt, 1, 0));
while (1) {
@systemstarter_ipc_queue_process;
Sleep(1);
}
}
U0 @systemstarter_create_task(U8* path, U8* name)
{
@systemtask* st = CAlloc(sizeof(@systemtask));
IpcMessage* msg = CAlloc(sizeof(IpcMessage));
st->path = StrNew(path);
st->name = StrNew(name);
msg->client = NULL;
msg->type = SYSSTART_MSG_SPAWN;
msg->payload = st;
Ipc.MsgSend(SystemStarter.task, msg);
}
SystemStarter.CreateTask = &@systemstarter_create_task;
SystemStarter.Init = &@systemstarter_init;
SystemStarter.Spawn = &@systemstarter_spawn;
SystemStarter.Task = &@systemstarter_task;
"systemstarter ";

139
System/Core/SystemTray.HC Normal file
View file

@ -0,0 +1,139 @@
#define SYSTRAY_MSG_NULL 0x0
#define SYSTRAY_MSG_REGISTER 0x1
#define SYSTRAY_MSG_UNREGISTER 0x2
class @systemtray
{
CTask* task;
@window_widgets_list* item;
U0 (*Init)();
U0 (*Task)();
Context2DWidget* (*RegisterItem)();
U0 (*UnregisterItem)(Widget* widget);
U0 (*SetIcon)(Context2DWidget* widget, U8* path);
};
@systemtray SystemTray;
U0 @systemtray_register_item(I64 addr)
{
Context2DWidget* item = Gui.CreateWidget(
Compositor.menubar.win, WIDGET_TYPE_CONTEXT2D, -24, 0, 24, 24);
item->ctx = NewContext2D(item->width, item->height);
Fill2D(item->ctx, Color(0, 0, 0, 0));
MemSetI64(addr, item, 1);
}
U0 @systemtray_unregister_item(Widget* item)
{
IpcMessage* msg = CAlloc(sizeof(IpcMessage));
msg->client = NULL;
msg->type = CPZ_MSG_WIN_WIDGET_DESTROY;
msg->payload = item;
System.Log(Fs, "Sent message → WidgetDestroy");
Ipc.MsgSend(Compositor.menubar.task, msg);
}
U0 @systemtray_reindex_items()
{
I64 x = Display.Width() - 100;
@window_widgets_list* item = Compositor.menubar.win->widget;
while (item->next)
item = item->next;
while (item->widget->type == WIDGET_TYPE_CONTEXT2D) {
x -= item->widget->width + 4;
item->widget->x = x;
item = item->prev;
}
Gui.Window.Refresh(Compositor.menubar.win);
}
U0 @systemtray_ipc_queue_process()
{
IpcMessage* msg;
msg = Ipc.MsgRecv();
if (msg) {
switch (msg->type) {
case SYSTRAY_MSG_REGISTER:
@systemtray_register_item(msg->payload);
@systemtray_reindex_items;
break;
case SYSTRAY_MSG_UNREGISTER:
@systemtray_unregister_item(msg->payload);
@systemtray_reindex_items;
break;
default:
break;
}
Free(msg);
}
}
U0 @systemtray_init() { }
U0 @systemtray_task()
{
Ipc.InitQueue(Fs);
SystemTray.task = Fs;
System.Log(Fs, "Task running at 0x%08x", Fs);
while (!Compositor.menubar.win) // Wait for instance
Sleep(1);
while (1) {
@systemtray_ipc_queue_process();
Sleep(1);
}
}
Context2DWidget* @systemtray_client_register_item()
{
Context2DWidget* item = NULL;
IpcMessage* msg = CAlloc(sizeof(IpcMessage));
msg->client = NULL;
msg->type = SYSTRAY_MSG_REGISTER;
msg->payload = &item;
System.Log(Fs, "Sent message → SystrayRegisterItem");
Ipc.MsgSend(SystemTray.task, msg);
while (!item)
Sleep(1);
return item;
}
U0 @systemtray_client_unregister_item(Widget* item)
{
IpcMessage* msg = CAlloc(sizeof(IpcMessage));
msg->client = NULL;
msg->type = SYSTRAY_MSG_UNREGISTER;
msg->payload = item;
System.Log(Fs, "Sent message → SystrayUnRegisterItem");
Ipc.MsgSend(SystemTray.task, msg);
while (item->type)
Sleep(1);
@systemtray_reindex_items;
}
U0 @systemtray_set_icon(Context2DWidget* widget, U8* path)
{
if (!widget || !path)
return;
U8 full_path[512];
if (2 == 3) // FIXME
StrCpy(&full_path, path);
else {
StrCpy(&full_path, "M:/Media/Themes/Umami/Icon/");
String.Append(&full_path, path);
}
if (!FileFind(&full_path))
return;
Context2D* icon = Image.FileToContext2D(&full_path);
CopyRect2D(widget->ctx, 0, 0, icon);
DelContext2D(icon);
Gui.Window.Refresh(Compositor.menubar.win);
}
SystemTray.Init = &@systemtray_init;
SystemTray.RegisterItem = &@systemtray_client_register_item;
SystemTray.SetIcon = &@systemtray_set_icon;
SystemTray.Task = &@systemtray_task;
SystemTray.UnregisterItem = &@systemtray_client_unregister_item;
"systemtray ";