Skip to content

Application Development for MaslOS2

Masl edited this page Mar 25, 2024 · 88 revisions

Here are the most important things to start developing a program for MaslOS2.

Please read all of the things you need carefully, as MaslOS2 is not POSIX compliant and differs in a few ways to most other APIs and OS's.

Basics

MaslOS2 is written in mostly C++. Most programs are therefore also in C++.

All programs and modules for it will be added into the ramdisk which is called bruh.

So most paths will look like bruh:programs/test/test.elf.

The code for programs and modules is located in the programs and modules folders respectively.

Programs are normal userspace programs and modules run in kernel-space. Modules are meant for drivers and important things. Most applications should be programs! (Also the modules are not very fleshed out yet)

Creating your first program

  • Firstly, create a folder in the programs folder.
    • NOTE: The folder name will also be the application name.
  • Create or copy over the GNUmakefile from any other program essentially.
    • It should only consist of one line, it being include ../program.mk.
    • You can change it of course, but for most programs it should be fine.
  • Create a main.cpp file. (Can also be a .c file if you want)
  • Add your main function int main(int argc, char** argv) { return 0; }
  • Enter your shell and run the ./CRUN.sh file. (Or ./CRUN_KVM.sh if you have kvm enabled, which is much faster)
  • Hopefully the OS should compile just fine and you should be able to find your application in the programs folder in the explorer, which you can launch with F10.

Other basics

Compiling and Running

To compile the os you can use ./CRUN.sh or ./CRUN_KVM.sh.

This should be used when you compile it initially and if you have any major changes.

Otherwise you can just use ./RUN.sh or ./RUN_KVM.sh respectively. They don't recompile the entire OS every time, which makes it much faster!

Stdlib and includes

You cannot use most of the c/c++ stdlib, but there is a custom stdlib I have made. It is called libm.. (Feel free to use <stdint.h> and <stddef.h> though)

To include things you need, just add an include to the needed file. For libm you can use #include <libm/...>.

Hello, world!

Here is the code for a simple hello world program:

#include <libm/stdio/stdio.h> // include needed things for stdio

using namespace STDIO; // So we dont need to prepend everything with `STDIO::`

int main(int argc, char** argv)
{
   initStdio(false); // This will initialise the stdio. (Dont mind the `false`)
   println("Hello, world!"); // will print "Hello, world" to stdio.
   return 0;
}

NOTE: you will need to launch this program in the shell to see any output! This will be explained in the stdio section.

Running a program in the shell

  • Open the shell with F12
  • navigate to the programs with cd programs.
  • navigate to your program folder with cd [YOUR PROGRAM NAME HERE].
  • run your program with run [YOUR PROGRAM NAME HERE].elf.

If the program hangs, you can force quit it with CTRL+C.

You can also attach command line arguments to the run command:

run test.elf
run test.elf 1234 abc
run test.elf "Text with spaces" "hello" 123

The command line arguments will not have the surrounding quotes and all things will be strings.

Command Line Arguments

In your main function, any passed command line arguments will be in the argv variable.

You can see the amount of arguments in the argc variable.

Memory Management

Most of the codebase doesn't use new C++ features. This is reflected in the memory management.

I would not recommend using smartpointers and such here and just managing the memory by yourself.

The main functions for memory stuff can be found in <libm/heap/heap.h> and <libm/memStuff.h>

Important

new and delete keyword

You can use the new keyword!

But you cannot use the delete keyword.

int[] vals = new int[4];

// ...

_Free(vals);

Memory Management with libm classes.

Most of the classes in libm have a Free or a Destroy Function.

  • Free

    • Will free all data associated with a class.
    • (If a class has a pointer to somewhere else for example)
    • Calling Free will not free the instance itself!!!
  • Destroy (mostly used in the GUI Framework)

    • Will free all data associated with a class and the instance itself!

An Example for Free:

// Alloc
List<int>* values = new List<int>();

// Doing stuff
values->Add(1);
values->Add(2);
values->Add(3);
// ...

// Freeing
values->Free();
_Free(values);

The Heap

void* _Malloc(int64_t size);
// Will malloc `size` bytes and return a pointer to it.

void* _Malloc(int64_t size, const char* text);
// Will malloc `size` bytes and return a pointer to it. 
// It will add a text connected to the malloc. (This is only for debug stuff)

void _Free(void* addr);
// This will free the data at the provided location

void* _Realloc(void* addr, int64_t size);
// This will reallocate memory with a new size.
// All data from the malloc will be transferred over.

All the functions are actually macros. You should use them like normal functions though. The reason being extra debug information being sent to the functions, to help with debugging memory leaks.

An example for memory allocation is:

int* vals = (int*)_Malloc(sizeof(int) * 4);

vals[0] = 10;
vals[1] = 99;
vals[3] = -1;
vals[2] = 5;

int sum = 0;
for (int i = 0; i < 4; i++)
   sum += vals[i];

// yay

_Free(vals);

Global Heap Manager

Every application has its own Heap Manager and therefore all mallocs are actually going through that.

Under normal circumstances, you do not need to touch this!

The HeapManager is located in the Heap namespace!

This allows viewing the amount of active mallocs and malloced bytes.

NOTE: Please do not modify those values and only read them!

class HeapManager
{
    int64_t _heapCount; 
    // Total count of heap segments. (Also includes freed segments!)

    int64_t _usedHeapCount;
    // Count of heap segments which are currently in use. (Holding an active malloc)

    int64_t _usedHeapAmount;
    // Amount of Bytes that are currently allocated. (Not including all header bytes)

    int64_t _activeMemFlagVal; 
    // Used to identify current mallocs. This is can be used to analyze memory leaks easier.
    // Do not worry about it for now.

    // ...
};

extern HeapManager* GlobalHeapManager;

The Global Heap Manager can be used for finding memory leaks by looking at those values.

All Mallocs can be analyzed in greater detail, but I will explain that at a later date.

Example to access data regarding memory:

int mallocCount = Heap::GlobalHeapManager->_usedHeapCount;

Memory Functions

NOTE: please take a careful look, as the functions are not standardized!

void _memcpy(void* src, void* dest, uint64_t size);
// Copies `size` bytes from `src` to `dest`.

void _memset(void* dest, uint8_t value, uint64_t size);
// Sets `size` bytes starting at `dest` to a value.

void _memmove(void* src, void* dest, uint64_t size);
// Moves `size` bytes from `src` to `dest`.
// Its like `_memcpy`, but its good if `src` and `dest` overlap!

Dealing with strings

MaslOS2 uses const char* terminated with a \0 for strings. The strings are currently ASCII and NOT Unicode.

There are quite a few methods to deal with strings, which can be found at <libm/cstrTools.h> and <libm/cstr.h>.

String Methods

NOTE: most of the methods returning strings will return a new malloced string, which you will need to free after use!

bool StrEquals(const char* a, const char* b);
// Checks if two strings are equal in content and lenght.

bool StrEquals(const char* a, const char* b, int len);
// Checks if the first `len` bytes of two strings are identical.


char* StrCopy(const char* og);
// Will create a new copy of a string.


char* StrPadLeft(const char* a, char pad, int totalLen, bool freeA);
// Will pad a string with a char on the left side. 
// if `freeA` is true, it will free the first string, which can be useful for chaining

char* StrPadRight(const char* a, char pad, int totalLen, bool freeA);
// Will pad a string with a char on the right side. 
// if `freeA` is true, it will free the first string, which can be useful for chaining


char* StrSubstr(const char* og, int index);
// Will get the substring of a string, starting at an index.

char* StrSubstr(const char* og, int index, int len);
// Will get the substring of a string with a given lenght, starting at an index.


bool StrStartsWith(const char* og, const char* with);
// Checks if a string starts with another string.

bool StrEndsWith(const char* og, const char* with);
// Checks if a string ends with another string.


char* StrCombineAndFree(const char* a, const char* b);
// Returns a string that is a and b combined.
// Will free a, which is useful for chaining.

char* StrCombine(const char* a, const char* b);
// Returns a string that is a and b combined.

char* StrCombine(const char* a, const char* b, const char* c);
// Returns a string that is a, b and c combined.

char* StrCombine(const char* a, const char* b, const char* c, const char* d);
// Returns a string that is a, b, c and d combined.


char* StrReplaceStartingStuffWith(const char* og, const char* toReplace, const char* toReplaceWith);
// Replaces the starting part of a string with another if possible.
// StrReplaceStartingStuffWith("ThisIsATest", "ThisIs", "IAm") -> "IAmATest"


uint64_t StrCountChr(const char* og, char chr);
// Counts the amount of times a character appears in a string.

int32_t StrLen(const char* og);
// Gets the lenght of a string.


int32_t StrIndexOf(const char* str, char chr);
// Gets the first index of a char in a string.
// Returns -1 if the character does not appear.

int32_t StrLastIndexOf(const char* str, char chr);
// Gets the last index of a char in a string.
// Returns -1 if the character does not appear.

int32_t StrIndexOf(const char* str, char chr, int ignoreCount);
// Gets the first xth index of a char in a string.
// Returns -1 if the character does not appear.
// StrIndexOf("123123", '2', 0) -> 1
// StrIndexOf("123123", '2', 1) -> 4
// StrIndexOf("123123", '2', 2) -> -1

int32_t StrLastIndexOf(const char* str, char chr, int ignoreCount);
// Gets the last xth index of a char in a string.
// Returns -1 if the character does not appear.
// StrLastIndexOf("123123", '2', 0) -> 4
// StrLastIndexOf("123123", '2', 1) -> 1
// StrLastIndexOf("123123", '2', 2) -> -1

String To/From Conversion

const char* to_string(type value); 
// Supports uint64_t, int64_t, int, double, bool and char.
// will convert a type to a string.
// NOTE: This will not create a new string, so you will need to StrCopy it.

const char* to_string(double value, uint8_t places);
// Like the other to_string, but for double values.
// You can define the amount of places.


int64_t to_int(const char* string);
// Converts a string to an integer.


uint32_t ConvertStringToHex(const char* data);
// This will convert a HEX-String to a 32-bit integer
// NOTE: The strings should not start with `0x`

uint64_t ConvertStringToLongHex(const char* data);
// This will convert a HEX-String to a 64-bit integer
// NOTE: The strings should not start with `0x`


const char* ConvertHexToString(uint64_t hex);
// Supports uint8_t, uint16_t, uint32_t and uint64_t.
// Will convert a type to a HEX-String.
// NOTE: This will not create a new string, so you will need to StrCopy it.

const char* ConvertHexToString(uint64_t hex, uint8_t size);
// Will convert a 64 bit value to a HEX-String.
// The size specifies how many characters you want the string to have. (One character represents 4 bits)
// NOTE: This will not create a new string, so you will need to StrCopy it.

Processes

Processes in MaslOS2 have a PID.

You can start/close processes/files from within a program using some syscalls.

The syscalls can be found in <libm/syscallManager.h>.

bool closeProcess(uint64_t pid);
// Will close a process with a given PID.
// NOTE: This can also close threads.
// NOTE: If you close a thread, it will not close the parent process, 
//       BUT if you close a process/thread it will close all child threads.

uint64_t startProcess(const char* path, int argc, const char** argv, const char* workingDirectory);
// Will try to start a process and return its PID or 0 if it failed.
// It requires an absolute path to an ELF file.
// You can pass it any command line arguments.
//  -> If there are no arguments, you can pass in 0 for argc and NULL for argv.
//  -> NOTE: The argv will not be freed!
// You can pass it a working directory. (If not, you can just pass "", but NOT NULL).

uint64_t startFile(const char* path, const char* workingDirectory);
// Will try to open the provided file with an appropriate program and return its PID or 0 if it failed.
// You can pass it a working directory. (If not, you can just pass "", but NOT NULL).

uint64_t startThread(void* func);
// Will start a new thread with a given function as the entry point.
// NOTE: The function needs to call programExit or something similar before returning, or else it will crash!

void waitUntilThreadClosed(uint64_t pid);
// Will wait until a thread (or process) was closed.
// Is essentially just a `while(pidExists()) programYield();` loop

Stdio

Stdio works between 2 processes. Internally it uses IPC to send and receive data.

the methods and classes can be found in <libm/stdio/stdio.h> and are in the STDIO namespace.

There are always two versions for each stdio method.

  • One for communicating with the parent
  • One for communication with a child

The structure of a StdioInst looks like this:

struct StdioInst
{
    StdioInst(uint64_t pid);
    // Constructor, not needed from the outside

    void Free();
    // Will Free the contents.

    uint64_t pid;
    // The PID (process id) of the other process
};

Printf / Printlnf formatting

// Formatting options:
//  -  %s ->   string
//  -  %c ->   char
//  -  %d/i -> int (32 bit)
//  -  %D/I -> int (64 bit)
//  -  %x ->   hex (32 bit)
//  -  %X ->   hex (64 bit)
//  -  %b ->   byte
//  -  %B ->   bool
//  -  %f ->   float
//  -  %F ->   double
//  -  %% ->   %

Stdio as the child process

Most applications will use stdio as the child process, since they will be launched from somewhere else.

extern StdioInst* parent;
// The parent Stdio Instance.

void initStdio(bool needLoggerWindow);
// Initializes Stdio
// If needLoggerWindow is true and there is no Stdio attached from the parent, it will create an extra logger window.


// Printing
void print(char chr);
void print(const char* str);

void println();
void println(char chr);
void println(const char* str);

void printlnf(const char* str, ...);
void printf(const char* str, ...);


void clear();
// Will clear the screen.


int read(); 
// Returns a char or -1 if there is no data

bool available();
// Returns true if a char is avaiable to read.

int bytesAvailable();
// Returns how many chars/bytes are avaiable.


void sendBytes(uint8_t* bytes, uint64_t size);
// Send x bytes to the parent

uint64_t readBytes(uint8_t* bytes, uint64_t size);
// Tries to read x bytes from the parent into the buffer
// Returns the amount of bytes read.


const char* readLine();
// Will read a line from the parent and return the string.
// NOTE: this will be a new instance, so you will need to free it after use!

Here is an example as the child process:

// Init Stdio and create a logger window if dont have a stdio attached.
initStdio(true);

// Ask for name
println("What is your name?");

// Read user input
const char* name = readLine();
if (name== NULL)
    return;
println();

// Write greeting
print("Hello, ");
print(name);
println("!");

// ...

// Free
_Free(name);

Stdio as the parent process

If you launch a new process, you can attach stdio to it, so you can communicate with it.

StdioInst* initStdio(uint64_t pid);
// Initializes/Attaches Stdio to a child process
// It will return a new Stdio Instance.
// NOTE: you will need to free it after use!


void print(char chr, StdioInst* other);
void print(const char* str, StdioInst* other);

void println(StdioInst* other);
void println(char chr, StdioInst* other);
void println(const char* str, StdioInst* other);

void printlnf(StdioInst* other, const char* str, ...);
void printf(StdioInst* other, const char* str, ...);


void clear(StdioInst* other);
// Will clear the screen.


int read(StdioInst* other);
// Returns a char or -1 if there is no data

bool available(StdioInst* other);
// Returns true if a char is avaiable to read.

int bytesAvailable(StdioInst* other);
// Returns how many chars/bytes are avaiable.


void sendBytes(uint8_t* bytes, uint64_t size, StdioInst* other);
// Sends x bytes to the other instance

uint64_t readBytes(uint8_t* bytes, uint64_t size, StdioInst* other);
// Tries to read x bytes from the parent into the buffer
// Returns the amount of bytes read.


const char* readLine(StdioInst* other);
// Will read a line from the other and return the string.
// NOTE: this will be a new instance, so you will need to free it after use!

Here is an example as the parent process:

// Launch a process
uint64_t pid = startProcess("bruh:programs/logger/logger.elf", 0, NULL, "");
if (pid == 0)
    return;

// Create the Stdio instance
StdioInst* stdio = initStdio(pid);
if (stdio == NULL)
    return NULL;

// Write "Hello, world!"
println("Hello, world!", currStdio);

// ...

// Free
stdio->Free();
_Free(stdio);

Stdio as a client with other processes.

You can talk with several other "parent" processes as a child.

NOTE: Under normal circumstances you will not need this.

This is useful if your process expects to be a parent process for whatever reason.

You will need to use the same methods as above, but the initialization is different.

StdioInst* initStdioClient(uint64_t pid);

Here is an example as a client:

// Launch a process
uint64_t pid = startProcess("bruh:programs/magic/magic.elf", 0, NULL, "");
if (pid == 0)
    return;

// Create the Stdio instance
StdioInst* stdio = initStdioClient(pid);
if (stdio == NULL)
    return NULL;

// Write "Hello, world!"
println("Hello, world!", currStdio);

// ...

// Free
stdio->Free();
_Free(stdio);

Random

There are a few methods for getting random values can be found in <libm/rnd/rnd.h>

The programs generate their own random numbers but start with a random seed provided by the kernel.

The methods are in the RND namespace.

double RandomDouble();
// Will get a random double between 0 and 1.

uint64_t RandomInt();
// Will get a random 64 bit Integer.

You can also get a fully random 64 bit integer from the kernel using a syscall:

uint64_t randomUint64();

Time / RTC

Time since boot

You can get the time since boot in milliseconds roughly using a syscall:

uint64_t envGetTimeMs();

RTC

You can get the RTC info using a syscall.

The method can be found in <libm/syscallManager.h>.

RTC_Info* envGetTimeRTC();

This will either return a new instance of the RTC_Info struct or NULL.

NOTE: The instance will need to be freed after use!

struct RTC_Info
{
    int Second, Minute, Hour, Day, Month, Year;
};

IPC

Inter process Communication works through messages in MaslOS2.

You can send a message to any process if you know its PID. You can also see which PID sent you which message.

But keeping track of several conversations can still be very messy.

That's why conversations exist.

Simply explained you can send a message and create a random conversation id.

The other process can then respond with the same conversation id.

You can easily filter out all messages with a given conversation id, which makes life a lot easier!

A lot of things use messages and conversations in MaslOS2.

To send and or receive messages, you need some syscalls, which can be found in <libm/syscallManager.h>.

NOTE: When you send a packet, it will get copied. This means that you still need to Free your packet after sending it!

Message Packet

A message, or rather a message packet looks like this:

struct GenericMessagePacket
{
    uint64_t Size;
    // Size of the Data

    uint8_t* Data;
    // The actual data

    uint64_t FromPID;
    // Who the packet is from

    uint64_t ConvoID;
    // The conversation id, 0 if there is none

    MessagePacketType Type;
    // The packet type

    
    GenericMessagePacket(uint64_t size, MessagePacketType type);
    // Will create a GenericMessagePacket with the given type and size
    // NOTE: the data will be random and not cleared, so you will need to set it before sending it!

    GenericMessagePacket(MessagePacketType type, uint8_t* data, uint64_t size);
    // Will create a GenericMessagePacket with the given type, data and size
    // NOTE: the data will be copied, so your original data will need to be freed if it was allocated!

    GenericMessagePacket* Copy();
    // Will create a copy of the packet

    void Free();
    // Will free the data but NOT the instance itself
};

The MessagePacketType enum looks like this:

enum MessagePacketType : uint8_t
{
    NONE,
    GENERIC_DATA,
    GENERIC_MESSAGE,
    KEY_EVENT,
    MOUSE_EVENT,
    WINDOW_BUFFER_EVENT,
    WINDOW_CREATE_EVENT,
    WINDOW_DELETE_EVENT,
    WINDOW_GET_EVENT,
    WINDOW_SET_EVENT,
    STDIO_INIT_EVENT
};

Normal Messages

int msgGetCount();
// Returns the count of unprocessed messages.

GenericMessagePacket* msgGetMessage();
// Will return the next message packet and remove it from the queue.
// Will return NULL if there was no packet in the queue.

bool msgSendMessage(GenericMessagePacket* packet, uint64_t targetPid);
// Will try to send a packet to a process.
// Will return true if it worked and false if it failed.

void programWaitMsg();
// Will halt the program until it receives any message.

An example of some message sending code:

const char* str = "Hello from a test program!";
int size = StrLen(str) + 1; // so we also send the '\0'.

GenericMessagePacket* packet = new GenericMessagePacket(MessagePacketType::GENERIC_DATA, (uint8_t*)str, size);
msgSendMessage(packet, pid);

packet->Free();
_Free(packet);

Conversations

GenericMessagePacket* msgGetConv(uint64_t convoId);
// Will try to get a message with the given conversation id. 
// Will return NULL if there isn't any yet.


uint64_t msgSendConv(GenericMessagePacket* packet, uint64_t targetPid);
// Will start a conversation with this packet and return the new conversation id.

uint64_t msgSendConv(GenericMessagePacket* packet, uint64_t targetPid, uint64_t convoId);
// Will send a packet to a process with a specific covnresation

uint64_t msgRespondConv(GenericMessagePacket* og, GenericMessagePacket* reply);
// Will reply to a packet with another packet.
// This is the same as the method above, just that it extracts the target pid and conversation id from the packet.
// NOTE: this will not free the original or the reply packet!


GenericMessagePacket* msgWaitConv(uint64_t convoId, uint64_t timeoutMs);
// Will halt the program and wait for a reply to the conversation or time out after `timeoutMs` milliseconds.

An example of how a conversation could look like:

const char* str = "Hello from a test program!";
int size = StrLen(str) + 1; // so we also send the '\0'.

uint64_t convoId;
{
    // Send a packet and "start" the conversation
    GenericMessagePacket* packet = new GenericMessagePacket(MessagePacketType::GENERIC_DATA, (uint8_t*)str, size);
    convoId = msgSendConv(packet, pid);
    packet->Free();
}

// Wait for a reply and timeout after 3 seconds.
GenericMessagePacket* reply = msgWaitConv(convoId, 3000);
if (reply == NULL)
    return; // We did not get a reply in time.

// ...

// Free reply after use
reply->Free();
_Free(reply);

Specific Message Packets

You can also send/get specific packets and convert them to/from generic packets.

Most of the time you will not need to worry about them though.

Key Packet

Primarily internally used by the desktop to communicate with the GUI-Framework that a key has been clicked in the window.

If you are not using the GUI-Framework, you will get the packet and should handle it.

Packet can be found in <libm/msgPackets/keyPacket/keyPacket.h>.

class KeyMessagePacket
{
    KeyMessagePacketType Type;
    int Scancode;
    char KeyChar;

    KeyMessagePacket(KeyMessagePacketType type, int scancode, char keyChar);
};
enum KeyMessagePacketType : uint8_t
{
    KEY_PRESSED,
    KEY_RELEASE
};

Mouse Packets

Primarily internally used by the desktop to communicate with the GUI-Framework that a mouse action has happened in the window.

If you are not using the GUI-Framework, you will get the packet and should handle it.

Packet can be found in <libm/msgPackets/mousePacket/mousePacket.h>.

struct MouseMessagePacket
{
    int MouseX, MouseY;
    MouseMessagePacketType Type;
    bool Left, Right, Middle;
    bool PrevLeft, PrevRight, PrevMiddle;

    MouseMessagePacket(int mouseX, int mouseY);
};
enum MouseMessagePacketType : uint8_t
{
    MOUSE_MOVE,
    MOUSE_CLICK,
};

Window Buffer Update Packet

Primarily interally used by the GUI-Framework to tell the desktop, that the window buffer has changed.

If you are not using the GUI-Framework you will either need to send this packet manually, or indirectly by for example using the SendWindowFrameBufferUpdate() function.

Packet can be found in <libm/msgPackets/windowBufferUpdatePacket/windowBufferUpdatePacket.h>.

class WindowBufferUpdatePacket
{
    int X;
    int Y;
    int Width;
    int Height;
    uint64_t WindowId;
    uint32_t* Buffer;

    WindowBufferUpdatePacket(int x, int y, int width, int height, uint64_t windowId, uint32_t* buffer);
    // This will create a packet with a copy of the buffer
    WindowBufferUpdatePacket(int x, int y, int width, int height, uint64_t windowId, uint32_t* buffer, bool isRef);
    // This will create a packet with a copy or reference of the buffer. (Depending on `isRef`)
    // NOTE: If you use a reference, then do not free the source buffer before freeing the created WindowBufferUpdatePacket!

    WindowBufferUpdatePacket(GenericMessagePacket* genericMessagePacket);
    // This will create a packet with a reference to the message packet
    // NOTE: Do NOT free the source packet before freeing the created WindowBufferUpdatePacket!

    GenericMessagePacket* ToGenericMessagePacket();

    void Free();
};

Window Object Packet

Primarily interally used by the GUI-Framework and desktop to synchronise the Window object.

You will send the packets indirectly often, by calling functions such as setWindow().

Packet can be found in <libm/msgPackets/windowObjPacket/windowObjPacket.h>.

class WindowObjectPacket
{
    Window* PartialWindow;
    bool Set; // true -> set, false -> get

    WindowObjectPacket(Window* window, bool set);
    WindowObjectPacket(GenericMessagePacket* genericMessagePacket);
    GenericMessagePacket* ToGenericMessagePacket();

    void Free();
};

User Input (Global)

There are 2 ways to get user input:

  • By globally polling the data
  • By handling events caused by the GUI system.

This section focuses on globally getting the data.

To get the data, you will need to use syscalls.

The syscalls are located in <libm/syscallManager.h>.

Keyboard

There is a syscall to check if a scancode was pressed.

bool envGetKeyState(int scancode);

Most scancodes can be found here.

NOTE: MaslOS2 has a few special scancodes as well. (Like GeneralShift, RightAlt, GeneralControl, etc.) (General means either left or right)

The defines for these can be found in <libm/keyboard.h>

Mouse

You can get the global Mouse State using this function:

MouseState* envGetMouseState();

It will either return a new instance of MouseState or NULL.

NOTE: You will need to free the instance after use!

The Mouse State looks like this:

struct MouseState
{
    int MouseX, MouseY;
    bool Left, Right, Middle;
};

Math Stuff

There are a few math functions you can use.

They can be found in <libm/math.h>.

Most math functions like sin, cos, etc. were added by AptRock327.

int min(int a, int b);
int max(int a, int b);
int abs(int a);

double pow(double x, int n);

double sin(double x);
double cos(double x);
double tan(double x);

double csc(double x);
double sec(double x);
double ctan(double x);

double Asin(double x);
double Acos(double x);
double Atan(double x);

double approx_derivative(double (*f)(double), double point, double precision);
double approx_integral(double (*f)(double), double a, double b, double precision);

Filesystem

You can interact with the filesystem with several syscalls.

NOTE: Currently the FS API only supports absolute files!

Also most methods which require a string will copy it, meaning that you can pass it a static string or if not, need to free the passed string after use!

The syscalls are located in <libm/syscallManager.h>.

bool fsCreateFile(const char* path);
// Will try to create an empty file in the full path and return true or false depending on the success.

bool fsCreateFileWithSize(const char* path, uint64_t size);
// Will try to create a file with the given size in the full path and return true or false depending on the success.

bool fsCreateFolder(const char* path);
// Will try to create an empty folder in the full path and return true or false depending on the success.


bool fsDeleteFile(const char* path);
// Will try to delete a file.

bool fsDeleteFolder(const char* path);
// Will try to delete a folder and all files in it.


bool fsRenameFile(const char* path, const char* newPath);
// Will try to rename a file.

bool fsRenameFolder(const char* path, const char* newPath);
// Will try to rename a folder.


bool fsCopyFile(const char* path, const char* newPath);
//Will try to copy a file.

bool fsCopyFolder(const char* path, const char* newPath);
//Will try to copy a folder.


bool fsFileExists(const char* path);
// Checks if a file exists.

bool fsFolderExists(const char* path);
// Checks if a folder exists.


const char** fsGetFilesInPath(const char* path, uint64_t* count);
// Will return a new array of filenames with the full paths of each file.
// And it will set the uint64 to the amount of files.
// It can also return NULL on error.
// NOTE: The path should end with a '/'! ("bruh:test/")
//       -> You will need to free the array and all of its entries after use!

const char** fsGetFoldersInPath(const char* path, uint64_t* count);
// Will return a new array of foldernames with the full paths of each folder.
// And it will set the uint64 to the amount of folders.
// It can also return NULL on error.
// NOTE: The path should end with a '/'! ("bruh:test/")
//       -> The path should end with a '/'! ("bruh:test/")
//       -> You will need to free the array and all of its entries after use!

const char** fsGetDrivesInRoot(uint64_t* count);
// Will return a new array of drive names.
// And it will set the uint64 to the amount of drives.
// It can also return NULL on error.
// NOTE: The drivenames will not include ':'.
//       -> You will need to free the array and all of its entries after use!


FsInt::FileInfo* fsGetFileInfo(const char* path);
// Will try to get information on a file and return a new instance of FileInfo or NULL on error.
// NOTE: Please take a look at the FileInfo docs below to know how to use it.

FsInt::FolderInfo* fsGetFolderInfo(const char* path);
// Will try to get information on a folder and return a new instance of FolderInfo or NULL on error.
// NOTE: Please take a look at the FolderInfo docs below to know how to use it.


bool fsReadFileIntoBuffer(const char* path, void* buffer, uint64_t start, uint64_t byteCount);
// Will try to read an amount of data into a buffer from a file starting at any point and return true or false depending on the success.

bool fsReadFileIntoBuffer(const char* path, void* buffer, uint64_t byteCount);
// Will try to read an amount of data into a buffer from a file starting at the start and return true or false depending on the success.

bool fsReadFile(const char* path, void** buffer, uint64_t* byteCount);
// Will try to read all contents of a file and write it into a newly created buffer and return true or false depending on the success.
// It will also save the file size into the uint64t pointed to.
// The buffer can also be set to NULL.
// NOTE: You will need to free the buffer after use!


bool fsWriteFileFromBuffer(const char* path, void* buffer, uint64_t byteCount);
// Will try to write data into a a file and will return true or false depending on the success.
// NOTE: The buffer will be copied, so you need to free it after use!

Info Structs

They contain information for a file from the filesystem.

The structs can be found in the FilesystemInterface (or FsInt) namespace, which is located in <libm/fsStuff/fsInfo/fileSystemStructs.h>.

Base Info

The Base Info struct looks like this:

struct BaseInfo
{
    const char* path;
    uint16_t pathLen;
    
    bool writeProtected;
    bool hidden;
    bool systemFile;

    void Destroy();
    // NOTE: This will free the data and the instance itself!
};

File Info

The FileInfo struct looks like this:

struct FileInfo
{
    BaseInfo* baseInfo;

    uint64_t sizeInBytes;
    uint64_t locationInBytes;

    void Destroy();
    // NOTE: This will free the data and the instance itself!
    //       -> This will also free the Base Info struct instance!
};

Folder Info

The FolderInfo struct looks like this:

struct FolderInfo
{
    BaseInfo* baseInfo;

    void Destroy();
    // NOTE: This will free the data and the instance itself!
    //       -> This will also free the Base Info struct instance!
};

There are a few extra methods to do things with paths.

They are located in the FS_EXTRA namespace which can be found in <libm/fsStuff/extra/fsExtra.h>.

NOTE: They return a new instance, which you will need to free after use!

char* GetDriveNameFromFullPath(const char* path);
// Will get the drive name from a path or NULL if it failed.
// "bruh:test/123/abc.txt" -> "bruh"

char* GetFilePathFromFullPath(const char* path);
// Will get the pure file name from a path or NULL if it failed.
// "bruh:test/123/abc.txt" -> "abc.txt"

char* GetFolderPathFromFullPath(const char* path);
// Will get the folder path from a pathor NULL if it failed.
"bruh:test/123/abc.txt" -> "test/123"

Other Syscalls

There are several syscalls that have been covered already.

Here are the remaining ones:

The syscalls are located in <libm/syscallManager.h>.

Environment

int getArgC();
// Returns argc.

char** getArgV();
// Creates a new array with the arguments.
// Should be freed after use!

ENV_DATA* getEnvData();
// Will return an instance of ENV_DATE or NULL.
// This has global stuff and will have pointers to internal kernel functions.
// Should only be needed for kernel modules.

const char* getElfPath();
// Will return a new string with the path of the elf.
// Should be freed after use!

const char* getWorkingPath();
// Will return a new string with the working path of the process. 
// The working path can be set by the caller process.
// Should be freed after use!

const char* getElfPath(uint64_t pid);
// Will return a new string with the path of the elf of the given PID.
// Should be freed after use!

uint64_t getPid();
// Returns the PID (process id) of this process.

uint64_t getParentPid();
// Returns the PID of the parent process or 0 if there is none.

uint64_t envGetDesktopPid();
// Returns the PID of the desktop, this is only needed internally for the GUI-Framework and wmStuff.

uint64_t envGetStartMenuPid();
// Returns the PID of the start menu window, this is only needed internally for the desktop.

bool pidExists(uint64_t pid);
// Returns if there is a process with a given PID.

int envGetMouseSens();
// Will get the current mouse sensitivity.
// NOTE: Values are in percent, meaning that 100 means 100%.

int envSetMouseSens(int sens);
// Will try to set the mouse sensitivity and will return the new mouse sensitivity.
// NOTE: Values are in percent, meaning that 100 means 100%.

Pages (Dont touch)

void* requestNextPage();
// NOTE: Do not touch if using the heap!!! (It will break the heap!)
// Will request a page from the kernel.

void* requestNextPages(int count);
// NOTE: Do not touch if using the heap!!! (It will break the heap!)
// Will request x pages from the kernel.

Serial

void serialPrint(const char* str);
// Will print a string to the serial port.

void serialPrintLn(const char* str);
// Will print a string along with \r\n to the serial port.

void serialPrintChar(char c);
// Will print a single char to the serial port.

bool serialCanReadChar();
// Returns if there is a char avaiable to read.

char serialReadChar();
// Returns a char read from the serial port or 0 if there is none.

Global (Will soon be removed)

void globalPrint(const char* str);
// Will draw a string on the display.

void globalPrintLn(const char* str);
// Will draw a string along with a new line on the display.

void globalPrintChar(char chr);
// Will draw a singular character on the display.

void globalCls();
// Will clear the display.

Exiting, Yielding and Waiting

void programExit(int code);
// Will exit the program with a specified code.
// NOTE: If this is called from within a thread, it will ONLY close the thread!

void programCrash();
// Will crash the program.
// NOTE: If this is called from within a thread, it will ONLY crash the thread!

void programYield();
// Will yield the program. 
// Essentially just give up its processing time for other programs.
// Will resume after a few milliseconds normally.
// NOTE: If this is called from within a thread, it will ONLY yield the thread!

void programWait(int timeMs);
// Will wait atleast `timeMs` milliseconds.
// NOTE: If this is called from within a thread, it will ONLY pause the thread!


void programWaitMsg();
// Will wait until it gets a message (related to IPC and Stdio)
// NOTE: If this is called from within a thread, it will ONLY pause the thread!


int programSetPriority(int priority);
// Will try to set the priority of the process and return the actual priority it now has.
// The lower the priority, the more often it will be called. (Excluding Priority 0, which means no priority)
// The lowest priority for user programs is 5.
// NOTE: kernel modules can set the priority to 1.
//       -> It is NOT recommended to do that, as that will essentially make only that process run if possible!!!

Window Stuff

Using windows in MaslOS2 is actually very straight forward.

Most of the functions you will be using are located in <libm/wmStuff/wmStuff.h>.

Window Object

The Window class looks like this:

class Window
{
    const char* Title;
    // A string representing the window title.
    // If you want to change it, please free the data that it points to first and then set it to a malloced instance
    // NOTE: NEVER set it to a static string like window->Title = "asdf"; !! DO NOT !!

    WindowDimension Dimensions = WindowDimension();
    // Current Window Dimensions
    // Includes the Position and Size


    // A bunch of properties you can read and set
    bool ShowTitleBar;
    bool ShowBorder;
    bool Hidden;
    bool Moveable;
    bool Resizeable;
    bool Closeable;

    bool CaptureMouse;
    // If set, the window will capture the mouse if it is active.
    // This should be used with things like games.
    // NOTE: The desktop will send MOUSE MOVE packets to the window with the relative offset of the mouse movement.


    // Status properties
    bool IsActive; 
    // Will be set from the outside normally
    // Active means that the window is currently selected in the desktop. (Normally will glow bright green)

    bool IsFrozen; 
    // Should be set from the inside if the window is frozen, currently being ignored.

    bool IsCapturing;
    // Will be set from the outside normally.
    // If true, the mouse is currently being captured by this window.

    // Colors
    uint32_t DefaultBorderColor;
    uint32_t SelectedBorderColor;
    uint32_t DefaultTitleColor;
    uint32_t SelectedTitleColor;
    uint32_t DefaultTitleBackgroundColor;
    uint32_t DefaultBackgroundColor;

    uint32_t CurrentBorderColor;
    uint32_t CurrentTitleColor;
    uint32_t CurrentTitleBackgroundColor;


    // Window ID and Process ID
    uint64_t ID;
    uint64_t PID;


    void UpdateCheck();
    // Check for updates
    // Is used internally by the GUI-Framework

    void Free();
    // Will free all data in the struct
    // NOTE: Will NOT free the instance itself
};

Functions to do things with windows

void initWindowManagerStuff();
// Will initilaize Stuff for Window Managemenet stuff.
// NOTE: You should call this function at the start of your program!

Window* requestWindow();
// Will request a new window from the desktop.
// Will return NULL if it failed.


void deleteWindow(Window* window);
// Will try to delete the window
// NOTE: it will not free the window instance!

void deleteWindow(uint64_t id);
// Will try to delete the window based on its window id.

bool CheckForWindowClosed(Window* window);
// Returns true if the window was closed.


void updateWindow(Window* window);
// Will synchronise the window with the desktop version.
// Normally called inside the GUI-Framework or skipped by just getting a window update packet.


void setWindow(Window* window);
// Will send the window data to the desktop to update it there.


Window* getPartialWindow(uint64_t id);
// Used internally by updateWindow();
// Returns a basic window object with just the essential fields set.
// NOTE: This is not a full window and should only be used with the `UpdateUsingPartialWindow();` function of the other window instance you want to sync it with.


bool SendWindowFrameBufferUpdate(Window* window, int x1, int y1, int x2, int y2);
// Will send the specified area of the window to the desktop to be updated there.

bool SendWindowFrameBufferUpdate(Window* window);
// Will send the whole window buffer to the desktop to be updated there.

Basic Rendering

If you don't want to use the GUI-Framework you can do a lot of rendering yourself.

For that there is a TempRenderer class, which can be found in <libm/rendering/basicRenderer.h>.

The class looks like this:

class TempRenderer
{
    Point CursorPosition;
    Framebuffer* framebuffer;
    PSF1_FONT* psf1_font;
    unsigned int color;
    bool overwrite = false;
    void putChar(char chr, int64_t xoff, int64_t yoff, uint32_t fg, uint32_t bg);
    void putChar(char chr, int64_t xoff, int64_t yoff);
    void delChar(int64_t xoff, int64_t yoff, uint32_t col);
    void delChar(int64_t xoff, int64_t yoff);
    void putStr(const char* chrs, int64_t xoff, int64_t yoff);
    void putStr(const char *chrs, int64_t xoff, int64_t yoff, uint32_t col);

    void printStr(const char* chrs);
    void printStr(const char* chrs, bool allowEscape);
    void printStr(const char* chrs, const char* var);
    void printStr(const char* chrs, const char* var, bool allowEscape);

    void Println();

    void Print(char chr);
    void Print(const char* chrs);
    void Print(const char* chrs, bool allowEscape);
    void Println(const char* chrs);
    void Print(const char* chrs, const char* var);
    void Println(const char* chrs, const char* var);

    void Print(const char* chrs, uint32_t col);
    void Println(const char* chrs, uint32_t col);
    void Print(const char* chrs, const char* var, uint32_t col);
    void Println(const char* chrs, const char* var, uint32_t col);

    void Clear(uint32_t col);
    void Clear(int64_t x1, int64_t y1, int64_t x2, int64_t y2, uint32_t col);
    void Clear(uint32_t col, bool resetCursor);
    void ClearDotted(uint32_t col, bool resetCursor);
    void ClearDotted(uint32_t col);
    void ClearButDont();

    void Cls();
    
    TempRenderer(Framebuffer* framebuffer, PSF1_FONT* psf1_font);

    void DrawImage(ImageStuff::BitmapImage* image, int64_t x, int64_t y, int64_t sx, int64_t sy);
};

The renderer needs a pointer to a Framebuffer and a pointer to a PSF1_FONT struct.

The psf1 struct can be taken from defaultRenderFont which is located in <libm/userEnvStuff.h>

Here is an example to use the renderer:

Window* window = requestWindow();

if (window == NULL)
    return;

// Create a new TempRenderer
TempRenderer* renderer = new TempRenderer(window->Buffer, defaultRenderFont);

// Clear the buffer and write "Hello, world!".
renderer->Clear(Colors.black);
renderer->Println("Hello, world!");

// Send the whole Buffer to the Desktop to update it.
SendWindowFrameBufferUpdate(window);

// ...


// Free the renderer
_Free(renderer);

GUI Framework

Since there is quite a lot of stuff regarding the GUI Framework, I have a seperate wiki page for it here.

Dialogs

There are several methods to display simple Dialogs planned. (Very WIP right now)

The methods can be found in <libm/dialogStuff/dialogStuff.h> and are in the namespace Dialog.

OpenFileDialog

const char* OpenFileDialog(); 
// Will launch an OpenFileDialog in the root directory.

const char* OpenFileDialog(const char* path); 
// Will launch an OpenFileDialog in a specific directory. 
// NOTE: Make sure to end the path with a "/"! (Example "bruh:programs/")

Calling the dialog functions will make the program wait until the dialog is finished or closed. If a file was selected successfully, it will return a string with the complete filepath. (Which you will need to free after use) If the Dialog was closed or cancelled, it will return NULL.

SaveFileDialog

const char* SaveFileDialog();
// Will launch a SaveFileDialog in the root directory.

const char* SaveFileDialog(const char* path);
// Will launch a SaveFileDialog in a specific directory. 
// NOTE: Make sure to end the path with a "/"! (Example "bruh:programs/")

Calling the dialog functions will make the program wait until the dialog is finished or closed. If a file was selected successfully, it will return a string with the complete filepath. (Which you will need to free after use) If the Dialog was closed or cancelled, it will return NULL.

BasicInfoDialog

not implemented yet.

Basic YesNoDialog

not implemented yet.

BasicTextDialog

not implemented yet.

Having assets in your program

If you want to include assets in your program its quite easy.

You only need to add an assets folder in the root of your program folder.

All assets will be packaged along with your finished executable. They will also be in the assets directory.

NOTE: Even if you don't have an assets folder in your source code, there will still be an empty one on the ramdisk.

Having a program icon

Similar to assets you can have a little program icon that gets displayed in the taskbar.

The image needs to be 24x24 pixels, can have transparency and needs to be in the MBIF format.

To add the icon, just put the file into your assets folder as icon.mbif.

I plan on adding support for more image types later but for now its MBIF only.

Other imporant things

Libm (the MaslOS2 Stdlib)

Libm also holds a few other things, which can be useful.

Collections

There are several collection classes.

NOTE: The main issue with them is, that even though they are generic, your custom types will NOT work out of the box!

You would need to add a reference to your datatype in the include headers for the collection classes.

They will work with most basic types though! (int, float, double, short, char, long, void*, const char*, etc.)

List

A List is a simple collection that holds items.

You can include the List Class from <libm/list/list_all.h>.

template <typename T> class List
{
    List<T>(uint64_t capacity);
    List<T>();
    // Constructs a List.

    void Free();
    // Will free the internal array, but not the instance itself. (If it was created using new)

    List<T> Clone(); 
    // Will clone the list.

    uint64_t GetCount();
    uint64_t GetCapacity();

    int64_t GetIndexOf(T item);
    bool Contains(T item);

    void Add(T item);
    // Will try to add an element to the end of the list.
    void AddIfUnique(T item);
    // Will add an element if it doesnt exist in the list already.
    void InsertAt(T item, int64_t index);
    // Will try to insert an item into the list at a specific position.

    void Clear();
    void RemoveAt(int64_t index);
    void RemoveLast();
    void RemoveFirst();
    void RemoveFirst(int count);

    T ElementAt(int64_t index);
    T LastElement();

    void Set(int64_t index, T item);
    T& operator[](int64_t index);
};

Queue

You can include the Queue Class from <libm/queue/queue_all.h>.

template <typename T> class Queue
{
    Queue<T>(int64_t size);
    Queue<T>();
    // Constructs a Queue.

    void Free();
    // Will free the internal array, but not the instance itself. (If it was created using new)


    int64_t GetCount();
    // Will get the amount of items in the queue.


    void Enqueue(T item);
    // Will enqueue an item to the end of the queue.

    T Dequeue();
    // Will try to dequeue the first item in the queue. 
    // NOTE: it will panic if the queue is empty!

    void Clear();
    // Will clear the queue.


    T* First(bool (*condFunction)(T));
    // This will get a pointer to the first element where the specified function would return true.
    // NOTE: You should not use this under normal circumstances and be extremely careful!

    void Remove(T* item);
    // This will remove a pointer from a queue. 
    // NOTE: This should only be used with the method above!
};

SyncedList

The synced List is a List that is synced up with another list.

The special thing is that the orders of the contents do not matter, only the contents themselves do.

For now this is only needed inside the Desktop.

This is meant to have a synchronized list where you can move the elements around only.

You can include the SyncedList Class from <libm/syncedList/syncedList_all.h>.

template <typename T> class SyncedList
{
    SyncedList<T>(List<T>* list);
    // Will construct it with the link to the other list instance.

    void Free();
    // Will free the internal list but not the instance itself.

    uint64_t GetCount();
    uint64_t GetCapacity();
    int64_t GetIndexOf(T item);

    void Sync();
    // Will synchronize itself with the remote list.


    void Move(int64_t from, int64_t to);
    // Will move an element from one position to another.

    void Move(T item, int64_t to);
    // Will move the first occurence of the item to the specified position.


    T ElementAt(uint64_t index);

    T& operator[](uint64_t index);
};

File Classes

There a few classes to work with the custom file formats so you can parse them and or write them.

They are a bit wonky, outdated and weird to use but they will be improved upon at a later time.

NOTE: Please read the things carefully, as the File classes behave differently from most things in MaslOS2!

Default File

They are the base file struct, which can be used to convert them to other files.

The class can be found in <libm/files/defaultFile.h>.

struct DefaultFile
{
    int64_t size;
    int32_t filenameSize;
    
    char* filename;
    void* fileData;
};

You can construct a DefaultFile instance using this method:

DefaultFile* GetDefaultFileFromBuffer(const char* path, void* buffer, uint64_t size);
// This will create a new DefaultFile instance with the data and path and size.
// NOTE: The Buffer will only be referenced and NOT copied. !! PLEASE WATCH OUT !!
//       -> The path will be copied though.
//       -> This means that if you free it, you need to:
//          -> Free the filename
//          -> Free the buffer (if it's not used anywhere)
//          -> Free the instance itself.

ZIP/Archive Files

The classes are in the ZipStuff namespace, which can be found in <libm/zips>.

Basic Zip File (.mbzf)

They are a custom format for simple archive files.

They can only store files in them and not folders.

Though they can of course store more .mbzf files in them.

The class can be found in <libm/zips/basicZip.h>.

struct ZIPFile
{
    int64_t size;
    int32_t fileCount;
    
    FileStuff::DefaultFile* files;
};

There are also a few methods to deal with them.

These are located in the inner ZIP namespace

ZIPFile* GetZIPFromDefaultFile(FileStuff::DefaultFile* file);
// This will create a new zip file from a default file or NULL if it failed.
// NOTE: This is identical to calling the `GetZIPFromBuffer()` below with the file data. 
//       -> Please take a look at the notes there!

ZIPFile* GetZIPFromBuffer(void* buffer, uint64_t size);
// This will create a new zip file from a buffer or NULL if it failed.
// NOTE: The Buffer will only be referenced and NOT copied. !! PLEASE WATCH OUT !!
//       -> Some things will be allocated!
//       -> This means that if you free it, you need to:
//          -> Go through the file array
//             -> Free the filename of every file entry
//          -> Free the file array
//          -> Free the instance itself.
//       -> I should probably add a free/destroy function for it, since this can cause issues. (will do someday)

FileStuff::DefaultFile* GetFileFromFileName(ZIPFile* zip, const char* filename);
// This will search through the zip for a file with a given filename.
// This will return a pointer to the file entry in the zip files array or NULL.
// NOTE: This will not create a new instance or copy any data.
//      -> This means that you do NOT have to free this pointer
//      -> This also means that you should NOT USE this after freeing the main zip file!

Images

Here are classes surrounding image types and formats.

They are in the ImageStuff namespace, which is located in <libm/images>.

Bitmap Image File (.mbif)

This class is responsible for parsing Image Files.

Currently it can deal with .mbif files, which are a custom image format.

I plan on adding support for .bmp files soon-ish.

The class can be found in <libm/images/bitmapImage.h>.

The struct looks like this:

struct BitmapImage
{
    int32_t width, height, xOff, yOff;
    int64_t size;
    void* imageBuffer;
};

There are two main methods:

BitmapImage* ConvertFileToBitmapImage(FileStuff::DefaultFile* file);
// This will create a new bitmap image file from a default file or NULL if it failed.
// NOTE: This is identical to calling the `ConvertBufferToBitmapImage()` below with the file data. 
//       -> Please take a look at the notes there!

BitmapImage* ConvertBufferToBitmapImage(char* buffer, uint64_t size);
// This will return a new instance of a Bitmap Image with the image data or return NULL if it failed.
// NOTE: The data will be copied over, which means that the Bitmap Image does not depend on the buffer after creation!
//       -> This also means that you will need to free the Buffer and instance itself though!

Other Classes

There are also other special classes.

Lock

Locks are used to lock variables, that could be used by several threads at once.

For now this is only needed inside the actual kernel.

You can include the Lock Class from <libm/lock/lock_all.h>.

template <typename T> class Lockable
{
    T obj;
    // The actual object which the Lock references.

    Lockable<T>(T obj);
    // Create a Lock referencing this object.

    void Lock();
    // Will lock it. (Will panic if already locked)

    void Unlock();
    // Will unlock it.

    bool IsLocked();
    // Returns if the item is Locked
};

Multithreading

MaslOS2 now supports basic multithreading!

Threads are essentially just seperate processes that have a "parent" process and that share their memory space with it.

Other than that, they are essentially seperate processes.

It's very new and hasn't been tested too much, but the fundamental things seem to work.

Mallocs and Frees should be thread safe too. All the rest is probably not threadsafe and you will need to create locks and stuff yourself!

NOTE: This is still experimnental ig? So if you experience any issues, feel free to reach out / create an issue and I'll look into it!

you can start a process with ``.

Here is a simple example program:

#include <libm/syscallManager.h>
#include <libm/cstr.h>

void threadTest();

int val;

int main(int argc, char** argv)
{
    val = 123;

    // Prints out the value
    serialPrint("VAL (1): ");
    serialPrintLn(to_string(val));

    // Starts a new thread with the entry point being the `threadTest` method.
    uint64_t threadId = startThread((void*)threadTest);

    // Waits until the thread has finished. (Uses the `pidExists` syscall internally)
    waitUntilThreadClosed(threadId);

    // Prints out the (hopefully modified) value
    serialPrint("VAL (2): ");
    serialPrintLn(to_string(val));

    // exits the program
    return 0;
}

// The multithreaded function
void threadTest()
{
    serialPrintLn("<THREAD START>");
    programWait(500);

    // Will change the value to 456
    serialPrintLn("Setting val to 456");
    val = 456;

    programWait(500);

    serialPrintLn("<THREAD END>");
    programExit(0); // NOTE: This will actually just close the thread and not the whole process!
}

If you want to close the whole program and not just the thread from within a thread, you will need to get the mainPID of your thread and do closeProcess(pid).

(The mainPID is the process that created the thread, which could also be a thread technically)

You can also do IPC with threads just fine, as they act like seperate processes for the most part. (NOTE: Tho this is very untested!)

Sound

MaslOS2 has an Audio System, tho its still a bit WIP. Applications can use the Audio Interface which is located at <libm/audio/audioInterface.h>.

Audio System

The Audio System has a few important things needed to get audio working in your app:

// You need to call one of the init functions to initialize the internal audio buffer and connection to the kernel.
// The audio system is isolated per application.

void initAudioStuff(int sampleRate, int sampleCount, int bitsPerSample, int channelCount);
// This will initialize the audio system with an internal buffer, having the specified sample rate, sample count, etc.
// It is recommended to have a sample count of 1/2 (or at least 1/5th) the sample rate, meaning a buffer with a time of 0.5s (or 0.5).
// You can of course use more. (you can also use less but it is generally not recommended)

void initAudioStuff();
// This will initialize the audio system with an internal 48kHz buffer with 4800 samples, 16 bits per sample and 2 channels.

void initAudioStuff(Audio::AudioBuffer* buffer);
// This will initialize the audio system with an internal buffer matching the buffer passed to it.


void DoAudioCheck();
// This method needs to be called every frame, or roughly every buffer duration or every event.
// The kernel normally sends an event every time it requires data, 
// so you could just call it every frame and have a `programWaitMsg` in the loop.

Audio::BasicAudioDestination* globalAudioDest;
// This is the Audio destination that is connected to the kernel and transmits the audio.
// Any audio you want to play will have to be connected to it.

Audio Components

To interact with the Audio System, you need to use components, such as buffers or audio sources. The classes can be found in the internal audio header located at <libm/audio/internal/audio.h>.

Audio Buffer

A

Basic Audio Source

A

Basic Audio Destination

A

Example

This little example program will generate an audio buffer with a square wave and then play it and exit afterwards.

#include <libm/syscallManager.h>
#include <libm/audio/audioInterface.h>

int main(int argc, char** argv)
{
    // Initialise the audio system with default parameters
    initAudioStuff();

    // Create our Audiosource with a given sample rate, sample count, 16 bit and mono audio
    int sampleRate = 44100;
    int sampleCount = sampleRate * 1;
    Audio::BasicAudioSource* audioSource = new Audio::BasicAudioSource(
        Audio::AudioBuffer::Create16BitMonoBuffer(sampleRate, sampleCount)
    );

    // Connect it to the Global Audio Destination
    audioSource->ConnectTo(globalAudioDest);

    // Fill the buffer with some audio
    // This creates a square wave slowly rising in pitch
    uint16_t* arr = (uint16_t*)audioSource->buffer->data;
    int dif = 20;
    for (int i = 0; i < dif; i++)
        Audio::FillArray(arr, (i * sampleCount)/dif, sampleCount/dif, ((1000*(i+1)) / dif), sampleRate);
    

    // Set the sample count and reset the samples sent
    audioSource->buffer->sampleCount = sampleCount;
    audioSource->samplesSent = 0;

    // Set the ready to send flag
    audioSource->readyToSend = true;


    // Wait until the audio is done playing
    while (audioSource->readyToSend)
    {
        DoAudioCheck();
        programWaitMsg();
    }

    // Give it some time to finish playing before closing the task
    programWait(200);
    return 0;
}

Networking

There is no Networking in MaslOS2 yet. :(

Exceptions

The try-catch infrastructure is currently not implemented and not avaible yet.

If an exception occurs, it will close the program and report it on the screen and in the serial output.

The exceptions are explained in detail here.

Clone this wiki locally