diff --git a/kernel/src/Api/UserMemory.hpp b/kernel/src/Api/UserMemory.hpp new file mode 100644 index 0000000..0064682 --- /dev/null +++ b/kernel/src/Api/UserMemory.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include +#include + +#include +#include + +namespace Montauk::UserMemory { + + static constexpr uint64_t USER_SPACE_END = 0x0000800000000000ULL; + + inline bool IsUserPtr(uint64_t addr) { + return addr == 0 || addr < USER_SPACE_END; + } + + inline bool ValidUserPtr(uint64_t addr) { + return addr != 0 && addr < USER_SPACE_END; + } + + inline uint64_t CurrentPml4() { + auto* proc = Sched::GetCurrentProcessPtr(); + return proc ? proc->pml4Phys : 0; + } + + inline bool Range(uint64_t addr, uint64_t size, bool requireWrite = false) { + if (size == 0) return true; + if (!ValidUserPtr(addr)) return false; + if (addr + size < addr) return false; + if (addr + size > USER_SPACE_END) return false; + + uint64_t pml4Phys = CurrentPml4(); + if (pml4Phys == 0) return false; + + return Memory::VMM::Paging::IsUserRangeAccessible(pml4Phys, addr, size, requireWrite); + } + + inline bool OptionalRange(uint64_t addr, uint64_t size, bool requireWrite = false) { + return addr == 0 || Range(addr, size, requireWrite); + } + + template + inline bool Readable(uint64_t addr) { + return Range(addr, sizeof(T), false); + } + + template + inline bool Writable(uint64_t addr) { + return Range(addr, sizeof(T), true); + } + + inline bool String(uint64_t addr, uint64_t maxLen) { + if (!ValidUserPtr(addr) || maxLen == 0) return false; + + uint64_t pml4Phys = CurrentPml4(); + if (pml4Phys == 0) return false; + + const char* str = (const char*)addr; + for (uint64_t i = 0; i < maxLen; i++) { + if (!Memory::VMM::Paging::IsUserRangeAccessible(pml4Phys, addr + i, 1, false)) { + return false; + } + if (str[i] == '\0') return true; + } + + return false; + } +} diff --git a/montaukos.org/docs/index.html b/montaukos.org/docs/index.html new file mode 100644 index 0000000..0a5551c --- /dev/null +++ b/montaukos.org/docs/index.html @@ -0,0 +1,147 @@ + + + + + + + Documentation - MontaukOS + + + + + + + + + +
+
+

Documentation

+
+ +
+ +

Welcome

+

+Welcome to the MontaukOS documentation. Here you will find information about the +operating system, its components, and how to use and develop for it. +

+ +

Documentation Index

+
    +
  • + Introduction +

    Installation, running on emulator, and your first steps with MontaukOS.

    +
  • +
  • + Kernel Architecture +

    Overview of the preemptive multitasking kernel and its subsystems.

    +
  • +
  • + Userspace Environment +

    The desktop environment, window manager, and system services.

    +
  • +
  • + Applications +

    Documentation for the applications included with MontaukOS.

    +
  • +
  • + Building from Source +

    How to compile and build MontaukOS yourself.

    +
  • +
  • + Contributing +

    Guidelines for contributing to the project.

    +
  • +
+ +
+ + + +
+ + diff --git a/montaukos.org/docs/introduction.html b/montaukos.org/docs/introduction.html new file mode 100644 index 0000000..a12c3eb --- /dev/null +++ b/montaukos.org/docs/introduction.html @@ -0,0 +1,105 @@ + + + + + + + Introduction - MontaukOS + + + + + + + + + +
+
+

Introduction

+
+ +
+ +

Welcome to MontaukOS

+

+MontaukOS is a modern operating system +

+ +
+
+ + + +
+ + diff --git a/montaukos.org/fr_icons/accessories-calculator-symbolic.svg b/montaukos.org/fr_icons/accessories-calculator-symbolic.svg new file mode 100644 index 0000000..063765e --- /dev/null +++ b/montaukos.org/fr_icons/accessories-calculator-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/accessories-calculator.svg b/montaukos.org/fr_icons/accessories-calculator.svg new file mode 100644 index 0000000..80a0ff6 --- /dev/null +++ b/montaukos.org/fr_icons/accessories-calculator.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/accessories-text-editor-symbolic.svg b/montaukos.org/fr_icons/accessories-text-editor-symbolic.svg new file mode 100644 index 0000000..d63556e --- /dev/null +++ b/montaukos.org/fr_icons/accessories-text-editor-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/accessories-text-editor.svg b/montaukos.org/fr_icons/accessories-text-editor.svg new file mode 100644 index 0000000..95697eb --- /dev/null +++ b/montaukos.org/fr_icons/accessories-text-editor.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/application-pdf.svg b/montaukos.org/fr_icons/application-pdf.svg new file mode 100644 index 0000000..d8b8af8 --- /dev/null +++ b/montaukos.org/fr_icons/application-pdf.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/application-x-executable-symbolic.svg b/montaukos.org/fr_icons/application-x-executable-symbolic.svg new file mode 100644 index 0000000..40d3ef0 --- /dev/null +++ b/montaukos.org/fr_icons/application-x-executable-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/application-x-executable.svg b/montaukos.org/fr_icons/application-x-executable.svg new file mode 100644 index 0000000..ed117ed --- /dev/null +++ b/montaukos.org/fr_icons/application-x-executable.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/applications-science.svg b/montaukos.org/fr_icons/applications-science.svg new file mode 100644 index 0000000..4cbeac8 --- /dev/null +++ b/montaukos.org/fr_icons/applications-science.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/audio-player.svg b/montaukos.org/fr_icons/audio-player.svg new file mode 100644 index 0000000..bbf1275 --- /dev/null +++ b/montaukos.org/fr_icons/audio-player.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/audio-volume-high-symbolic.svg b/montaukos.org/fr_icons/audio-volume-high-symbolic.svg new file mode 100644 index 0000000..d115c2f --- /dev/null +++ b/montaukos.org/fr_icons/audio-volume-high-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/bluetooth.svg b/montaukos.org/fr_icons/bluetooth.svg new file mode 100644 index 0000000..4b4c938 --- /dev/null +++ b/montaukos.org/fr_icons/bluetooth.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/code.svg b/montaukos.org/fr_icons/code.svg new file mode 100644 index 0000000..3ae13c7 --- /dev/null +++ b/montaukos.org/fr_icons/code.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/com.visualstudio.code.oss.svg b/montaukos.org/fr_icons/com.visualstudio.code.oss.svg new file mode 100644 index 0000000..3ae13c7 --- /dev/null +++ b/montaukos.org/fr_icons/com.visualstudio.code.oss.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/computer-symbolic.svg b/montaukos.org/fr_icons/computer-symbolic.svg new file mode 100644 index 0000000..4d8fcb9 --- /dev/null +++ b/montaukos.org/fr_icons/computer-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/computer.svg b/montaukos.org/fr_icons/computer.svg new file mode 100644 index 0000000..97ce100 --- /dev/null +++ b/montaukos.org/fr_icons/computer.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/dialog-information-symbolic.svg b/montaukos.org/fr_icons/dialog-information-symbolic.svg new file mode 100644 index 0000000..80872b4 --- /dev/null +++ b/montaukos.org/fr_icons/dialog-information-symbolic.svg @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/montaukos.org/fr_icons/document-export-symbolic.svg b/montaukos.org/fr_icons/document-export-symbolic.svg new file mode 100644 index 0000000..0ab4ddf --- /dev/null +++ b/montaukos.org/fr_icons/document-export-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/document-save-symbolic.svg b/montaukos.org/fr_icons/document-save-symbolic.svg new file mode 100644 index 0000000..c71e843 --- /dev/null +++ b/montaukos.org/fr_icons/document-save-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/document-viewer.svg b/montaukos.org/fr_icons/document-viewer.svg new file mode 100644 index 0000000..afe466c --- /dev/null +++ b/montaukos.org/fr_icons/document-viewer.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/doom.svg b/montaukos.org/fr_icons/doom.svg new file mode 100644 index 0000000..5975a8c --- /dev/null +++ b/montaukos.org/fr_icons/doom.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/drive-harddisk.svg b/montaukos.org/fr_icons/drive-harddisk.svg new file mode 100644 index 0000000..01e2b87 --- /dev/null +++ b/montaukos.org/fr_icons/drive-harddisk.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/edit-copy.svg b/montaukos.org/fr_icons/edit-copy.svg new file mode 100644 index 0000000..43e5873 --- /dev/null +++ b/montaukos.org/fr_icons/edit-copy.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/edit-cut.svg b/montaukos.org/fr_icons/edit-cut.svg new file mode 100644 index 0000000..077e642 --- /dev/null +++ b/montaukos.org/fr_icons/edit-cut.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/montaukos.org/fr_icons/edit-paste.svg b/montaukos.org/fr_icons/edit-paste.svg new file mode 100644 index 0000000..f992fcc --- /dev/null +++ b/montaukos.org/fr_icons/edit-paste.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/edit-redo-symbolic.svg b/montaukos.org/fr_icons/edit-redo-symbolic.svg new file mode 100644 index 0000000..2309291 --- /dev/null +++ b/montaukos.org/fr_icons/edit-redo-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/edit-rename.svg b/montaukos.org/fr_icons/edit-rename.svg new file mode 100644 index 0000000..303cf28 --- /dev/null +++ b/montaukos.org/fr_icons/edit-rename.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/edit-undo-symbolic.svg b/montaukos.org/fr_icons/edit-undo-symbolic.svg new file mode 100644 index 0000000..39731c4 --- /dev/null +++ b/montaukos.org/fr_icons/edit-undo-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/folder-blue-desktop.svg b/montaukos.org/fr_icons/folder-blue-desktop.svg new file mode 100644 index 0000000..6affc2f --- /dev/null +++ b/montaukos.org/fr_icons/folder-blue-desktop.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/folder-blue-development.svg b/montaukos.org/fr_icons/folder-blue-development.svg new file mode 100644 index 0000000..74e2973 --- /dev/null +++ b/montaukos.org/fr_icons/folder-blue-development.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/folder-blue-documents.svg b/montaukos.org/fr_icons/folder-blue-documents.svg new file mode 100644 index 0000000..c7a49b2 --- /dev/null +++ b/montaukos.org/fr_icons/folder-blue-documents.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/folder-blue-downloads.svg b/montaukos.org/fr_icons/folder-blue-downloads.svg new file mode 100644 index 0000000..67908fd --- /dev/null +++ b/montaukos.org/fr_icons/folder-blue-downloads.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/folder-blue-home.svg b/montaukos.org/fr_icons/folder-blue-home.svg new file mode 100644 index 0000000..9c0414d --- /dev/null +++ b/montaukos.org/fr_icons/folder-blue-home.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/folder-blue-music.svg b/montaukos.org/fr_icons/folder-blue-music.svg new file mode 100644 index 0000000..2c6d482 --- /dev/null +++ b/montaukos.org/fr_icons/folder-blue-music.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/folder-blue-pictures.svg b/montaukos.org/fr_icons/folder-blue-pictures.svg new file mode 100644 index 0000000..5f0ca26 --- /dev/null +++ b/montaukos.org/fr_icons/folder-blue-pictures.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/folder-blue-videos.svg b/montaukos.org/fr_icons/folder-blue-videos.svg new file mode 100644 index 0000000..d9a04f6 --- /dev/null +++ b/montaukos.org/fr_icons/folder-blue-videos.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/folder-documents-symbolic.svg b/montaukos.org/fr_icons/folder-documents-symbolic.svg new file mode 100644 index 0000000..f04740f --- /dev/null +++ b/montaukos.org/fr_icons/folder-documents-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/folder-new.svg b/montaukos.org/fr_icons/folder-new.svg new file mode 100644 index 0000000..0ecc9d2 --- /dev/null +++ b/montaukos.org/fr_icons/folder-new.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/folder-symbolic.svg b/montaukos.org/fr_icons/folder-symbolic.svg new file mode 100644 index 0000000..148ccd6 --- /dev/null +++ b/montaukos.org/fr_icons/folder-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/folder.svg b/montaukos.org/fr_icons/folder.svg new file mode 100644 index 0000000..2c20935 --- /dev/null +++ b/montaukos.org/fr_icons/folder.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/format-indent-less-symbolic.svg b/montaukos.org/fr_icons/format-indent-less-symbolic.svg new file mode 100644 index 0000000..cb7b081 --- /dev/null +++ b/montaukos.org/fr_icons/format-indent-less-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/format-indent-more-symbolic.svg b/montaukos.org/fr_icons/format-indent-more-symbolic.svg new file mode 100644 index 0000000..068b83b --- /dev/null +++ b/montaukos.org/fr_icons/format-indent-more-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/format-justify-center-symbolic.svg b/montaukos.org/fr_icons/format-justify-center-symbolic.svg new file mode 100644 index 0000000..48e49c0 --- /dev/null +++ b/montaukos.org/fr_icons/format-justify-center-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/format-justify-left-symbolic.svg b/montaukos.org/fr_icons/format-justify-left-symbolic.svg new file mode 100644 index 0000000..5b50767 --- /dev/null +++ b/montaukos.org/fr_icons/format-justify-left-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/format-justify-right-symbolic.svg b/montaukos.org/fr_icons/format-justify-right-symbolic.svg new file mode 100644 index 0000000..c851529 --- /dev/null +++ b/montaukos.org/fr_icons/format-justify-right-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/gnome-logout.svg b/montaukos.org/fr_icons/gnome-logout.svg new file mode 100644 index 0000000..8d1c476 --- /dev/null +++ b/montaukos.org/fr_icons/gnome-logout.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/go-home-symbolic.svg b/montaukos.org/fr_icons/go-home-symbolic.svg new file mode 100644 index 0000000..440d9cb --- /dev/null +++ b/montaukos.org/fr_icons/go-home-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/go-next-symbolic.svg b/montaukos.org/fr_icons/go-next-symbolic.svg new file mode 100644 index 0000000..f72ea3b --- /dev/null +++ b/montaukos.org/fr_icons/go-next-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/go-previous-symbolic.svg b/montaukos.org/fr_icons/go-previous-symbolic.svg new file mode 100644 index 0000000..aab0ef6 --- /dev/null +++ b/montaukos.org/fr_icons/go-previous-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/go-up-symbolic.svg b/montaukos.org/fr_icons/go-up-symbolic.svg new file mode 100644 index 0000000..2c14a3e --- /dev/null +++ b/montaukos.org/fr_icons/go-up-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/help-about.svg b/montaukos.org/fr_icons/help-about.svg new file mode 100644 index 0000000..2300b65 --- /dev/null +++ b/montaukos.org/fr_icons/help-about.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/kolourpaint.svg b/montaukos.org/fr_icons/kolourpaint.svg new file mode 100644 index 0000000..8e98309 --- /dev/null +++ b/montaukos.org/fr_icons/kolourpaint.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/lock.svg b/montaukos.org/fr_icons/lock.svg new file mode 100644 index 0000000..06e1ccf --- /dev/null +++ b/montaukos.org/fr_icons/lock.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/media-forward.svg b/montaukos.org/fr_icons/media-forward.svg new file mode 100644 index 0000000..515e86e --- /dev/null +++ b/montaukos.org/fr_icons/media-forward.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/media-pause.svg b/montaukos.org/fr_icons/media-pause.svg new file mode 100644 index 0000000..f2f6b6e --- /dev/null +++ b/montaukos.org/fr_icons/media-pause.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/media-play.svg b/montaukos.org/fr_icons/media-play.svg new file mode 100644 index 0000000..449de22 --- /dev/null +++ b/montaukos.org/fr_icons/media-play.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/media-playlist-repeat-one-symbolic.svg b/montaukos.org/fr_icons/media-playlist-repeat-one-symbolic.svg new file mode 100644 index 0000000..c61ee42 --- /dev/null +++ b/montaukos.org/fr_icons/media-playlist-repeat-one-symbolic.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/montaukos.org/fr_icons/media-playlist-repeat-song.svg b/montaukos.org/fr_icons/media-playlist-repeat-song.svg new file mode 100644 index 0000000..17f171c --- /dev/null +++ b/montaukos.org/fr_icons/media-playlist-repeat-song.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/media-playlist-repeat-symbolic.svg b/montaukos.org/fr_icons/media-playlist-repeat-symbolic.svg new file mode 100644 index 0000000..1edb2cb --- /dev/null +++ b/montaukos.org/fr_icons/media-playlist-repeat-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/media-playlist-repeat.svg b/montaukos.org/fr_icons/media-playlist-repeat.svg new file mode 100644 index 0000000..c8c3818 --- /dev/null +++ b/montaukos.org/fr_icons/media-playlist-repeat.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/media-playlist-shuffle-symbolic.svg b/montaukos.org/fr_icons/media-playlist-shuffle-symbolic.svg new file mode 100644 index 0000000..e863ddf --- /dev/null +++ b/montaukos.org/fr_icons/media-playlist-shuffle-symbolic.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/montaukos.org/fr_icons/media-playlist-shuffle.svg b/montaukos.org/fr_icons/media-playlist-shuffle.svg new file mode 100644 index 0000000..6b7ca35 --- /dev/null +++ b/montaukos.org/fr_icons/media-playlist-shuffle.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/media-repeat-all.svg b/montaukos.org/fr_icons/media-repeat-all.svg new file mode 100644 index 0000000..f2fc097 --- /dev/null +++ b/montaukos.org/fr_icons/media-repeat-all.svg @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/montaukos.org/fr_icons/media-repeat-single.svg b/montaukos.org/fr_icons/media-repeat-single.svg new file mode 100644 index 0000000..9a2d3a0 --- /dev/null +++ b/montaukos.org/fr_icons/media-repeat-single.svg @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/montaukos.org/fr_icons/media-rewind.svg b/montaukos.org/fr_icons/media-rewind.svg new file mode 100644 index 0000000..22cfdad --- /dev/null +++ b/montaukos.org/fr_icons/media-rewind.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/media-stop.svg b/montaukos.org/fr_icons/media-stop.svg new file mode 100644 index 0000000..4bd183a --- /dev/null +++ b/montaukos.org/fr_icons/media-stop.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/multimedia-video-player.svg b/montaukos.org/fr_icons/multimedia-video-player.svg new file mode 100644 index 0000000..3e4ab3e --- /dev/null +++ b/montaukos.org/fr_icons/multimedia-video-player.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/network-wired-symbolic.svg b/montaukos.org/fr_icons/network-wired-symbolic.svg new file mode 100644 index 0000000..1351c08 --- /dev/null +++ b/montaukos.org/fr_icons/network-wired-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/network-wired.svg b/montaukos.org/fr_icons/network-wired.svg new file mode 100644 index 0000000..5539736 --- /dev/null +++ b/montaukos.org/fr_icons/network-wired.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/pavucontrol.svg b/montaukos.org/fr_icons/pavucontrol.svg new file mode 100644 index 0000000..43ecceb --- /dev/null +++ b/montaukos.org/fr_icons/pavucontrol.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/pdf-viewer.svg b/montaukos.org/fr_icons/pdf-viewer.svg new file mode 100644 index 0000000..132cb3a --- /dev/null +++ b/montaukos.org/fr_icons/pdf-viewer.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/montaukos.org/fr_icons/preferences-desktop-apps-symbolic.svg b/montaukos.org/fr_icons/preferences-desktop-apps-symbolic.svg new file mode 100644 index 0000000..fa11309 --- /dev/null +++ b/montaukos.org/fr_icons/preferences-desktop-apps-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/preferences-desktop-apps.svg b/montaukos.org/fr_icons/preferences-desktop-apps.svg new file mode 100644 index 0000000..9592dae --- /dev/null +++ b/montaukos.org/fr_icons/preferences-desktop-apps.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/printer-symbolic.svg b/montaukos.org/fr_icons/printer-symbolic.svg new file mode 100644 index 0000000..e5469e8 --- /dev/null +++ b/montaukos.org/fr_icons/printer-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/printer.svg b/montaukos.org/fr_icons/printer.svg new file mode 100644 index 0000000..ad4ed99 --- /dev/null +++ b/montaukos.org/fr_icons/printer.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/sensors-temperature-symbolic.svg b/montaukos.org/fr_icons/sensors-temperature-symbolic.svg new file mode 100644 index 0000000..863cfb0 --- /dev/null +++ b/montaukos.org/fr_icons/sensors-temperature-symbolic.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/montaukos.org/fr_icons/sleep.svg b/montaukos.org/fr_icons/sleep.svg new file mode 100644 index 0000000..75c3fa4 --- /dev/null +++ b/montaukos.org/fr_icons/sleep.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/system-file-manager-symbolic.svg b/montaukos.org/fr_icons/system-file-manager-symbolic.svg new file mode 100644 index 0000000..7a67e44 --- /dev/null +++ b/montaukos.org/fr_icons/system-file-manager-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/system-file-manager.svg b/montaukos.org/fr_icons/system-file-manager.svg new file mode 100644 index 0000000..3f5103e --- /dev/null +++ b/montaukos.org/fr_icons/system-file-manager.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/system-monitor.svg b/montaukos.org/fr_icons/system-monitor.svg new file mode 100644 index 0000000..30f2649 --- /dev/null +++ b/montaukos.org/fr_icons/system-monitor.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/system-reboot.svg b/montaukos.org/fr_icons/system-reboot.svg new file mode 100644 index 0000000..1495fb4 --- /dev/null +++ b/montaukos.org/fr_icons/system-reboot.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/system-shutdown.svg b/montaukos.org/fr_icons/system-shutdown.svg new file mode 100644 index 0000000..ed0d1a3 --- /dev/null +++ b/montaukos.org/fr_icons/system-shutdown.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/text-x-generic-symbolic.svg b/montaukos.org/fr_icons/text-x-generic-symbolic.svg new file mode 100644 index 0000000..6e8b7f8 --- /dev/null +++ b/montaukos.org/fr_icons/text-x-generic-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/text-x-generic.svg b/montaukos.org/fr_icons/text-x-generic.svg new file mode 100644 index 0000000..35542de --- /dev/null +++ b/montaukos.org/fr_icons/text-x-generic.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/text_line_spacing.svg b/montaukos.org/fr_icons/text_line_spacing.svg new file mode 100644 index 0000000..2edeee8 --- /dev/null +++ b/montaukos.org/fr_icons/text_line_spacing.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/trash-empty.svg b/montaukos.org/fr_icons/trash-empty.svg new file mode 100644 index 0000000..d37bd5a --- /dev/null +++ b/montaukos.org/fr_icons/trash-empty.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/unsettings.svg b/montaukos.org/fr_icons/unsettings.svg new file mode 100644 index 0000000..2c2c936 --- /dev/null +++ b/montaukos.org/fr_icons/unsettings.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/user-home-symbolic.svg b/montaukos.org/fr_icons/user-home-symbolic.svg new file mode 100644 index 0000000..5af7efb --- /dev/null +++ b/montaukos.org/fr_icons/user-home-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/user-home.svg b/montaukos.org/fr_icons/user-home.svg new file mode 100644 index 0000000..9c0414d --- /dev/null +++ b/montaukos.org/fr_icons/user-home.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/utilities-system-monitor-symbolic.svg b/montaukos.org/fr_icons/utilities-system-monitor-symbolic.svg new file mode 100644 index 0000000..fd41e5c --- /dev/null +++ b/montaukos.org/fr_icons/utilities-system-monitor-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/utilities-system-monitor.svg b/montaukos.org/fr_icons/utilities-system-monitor.svg new file mode 100644 index 0000000..30f2649 --- /dev/null +++ b/montaukos.org/fr_icons/utilities-system-monitor.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/utilities-terminal-symbolic.svg b/montaukos.org/fr_icons/utilities-terminal-symbolic.svg new file mode 100644 index 0000000..d4c0bea --- /dev/null +++ b/montaukos.org/fr_icons/utilities-terminal-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/utilities-terminal.svg b/montaukos.org/fr_icons/utilities-terminal.svg new file mode 100644 index 0000000..5b91996 --- /dev/null +++ b/montaukos.org/fr_icons/utilities-terminal.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/view-app-grid-symbolic.svg b/montaukos.org/fr_icons/view-app-grid-symbolic.svg new file mode 100644 index 0000000..19bd935 --- /dev/null +++ b/montaukos.org/fr_icons/view-app-grid-symbolic.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/montaukos.org/fr_icons/view-list-bullet-symbolic.svg b/montaukos.org/fr_icons/view-list-bullet-symbolic.svg new file mode 100644 index 0000000..c53f29a --- /dev/null +++ b/montaukos.org/fr_icons/view-list-bullet-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/view-list-ordered-symbolic.svg b/montaukos.org/fr_icons/view-list-ordered-symbolic.svg new file mode 100644 index 0000000..1378004 --- /dev/null +++ b/montaukos.org/fr_icons/view-list-ordered-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/view-media-equalizer.svg b/montaukos.org/fr_icons/view-media-equalizer.svg new file mode 100644 index 0000000..5ccbcef --- /dev/null +++ b/montaukos.org/fr_icons/view-media-equalizer.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/view-media-visualization.svg b/montaukos.org/fr_icons/view-media-visualization.svg new file mode 100644 index 0000000..48f07a2 --- /dev/null +++ b/montaukos.org/fr_icons/view-media-visualization.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/volume-level-high.svg b/montaukos.org/fr_icons/volume-level-high.svg new file mode 100644 index 0000000..8274a48 --- /dev/null +++ b/montaukos.org/fr_icons/volume-level-high.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/weather-clear-night.svg b/montaukos.org/fr_icons/weather-clear-night.svg new file mode 100644 index 0000000..e0cffc8 --- /dev/null +++ b/montaukos.org/fr_icons/weather-clear-night.svg @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/montaukos.org/fr_icons/weather-clear.svg b/montaukos.org/fr_icons/weather-clear.svg new file mode 100644 index 0000000..8a35b33 --- /dev/null +++ b/montaukos.org/fr_icons/weather-clear.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/weather-clouds-night.svg b/montaukos.org/fr_icons/weather-clouds-night.svg new file mode 100644 index 0000000..0db6194 --- /dev/null +++ b/montaukos.org/fr_icons/weather-clouds-night.svg @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/montaukos.org/fr_icons/weather-clouds.svg b/montaukos.org/fr_icons/weather-clouds.svg new file mode 100644 index 0000000..c79e94d --- /dev/null +++ b/montaukos.org/fr_icons/weather-clouds.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/weather-few-clouds-night.svg b/montaukos.org/fr_icons/weather-few-clouds-night.svg new file mode 100644 index 0000000..e3c4f87 --- /dev/null +++ b/montaukos.org/fr_icons/weather-few-clouds-night.svg @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/montaukos.org/fr_icons/weather-few-clouds.svg b/montaukos.org/fr_icons/weather-few-clouds.svg new file mode 100644 index 0000000..c55594a --- /dev/null +++ b/montaukos.org/fr_icons/weather-few-clouds.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/weather-fog.svg b/montaukos.org/fr_icons/weather-fog.svg new file mode 100644 index 0000000..1ce1a84 --- /dev/null +++ b/montaukos.org/fr_icons/weather-fog.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/montaukos.org/fr_icons/weather-freezing-rain.svg b/montaukos.org/fr_icons/weather-freezing-rain.svg new file mode 100644 index 0000000..eb98f17 --- /dev/null +++ b/montaukos.org/fr_icons/weather-freezing-rain.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/montaukos.org/fr_icons/weather-hail.svg b/montaukos.org/fr_icons/weather-hail.svg new file mode 100644 index 0000000..eb98f17 --- /dev/null +++ b/montaukos.org/fr_icons/weather-hail.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/montaukos.org/fr_icons/weather-many-clouds.svg b/montaukos.org/fr_icons/weather-many-clouds.svg new file mode 100644 index 0000000..a88947f --- /dev/null +++ b/montaukos.org/fr_icons/weather-many-clouds.svg @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/montaukos.org/fr_icons/weather-mist.svg b/montaukos.org/fr_icons/weather-mist.svg new file mode 100644 index 0000000..1ce1a84 --- /dev/null +++ b/montaukos.org/fr_icons/weather-mist.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/montaukos.org/fr_icons/weather-none-available.svg b/montaukos.org/fr_icons/weather-none-available.svg new file mode 100644 index 0000000..63cd5b7 --- /dev/null +++ b/montaukos.org/fr_icons/weather-none-available.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/montaukos.org/fr_icons/weather-overcast.svg b/montaukos.org/fr_icons/weather-overcast.svg new file mode 100644 index 0000000..a88947f --- /dev/null +++ b/montaukos.org/fr_icons/weather-overcast.svg @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/montaukos.org/fr_icons/weather-severe-alert.svg b/montaukos.org/fr_icons/weather-severe-alert.svg new file mode 100644 index 0000000..5eaf23b --- /dev/null +++ b/montaukos.org/fr_icons/weather-severe-alert.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/montaukos.org/fr_icons/weather-showers-scattered.svg b/montaukos.org/fr_icons/weather-showers-scattered.svg new file mode 100644 index 0000000..507b1dd --- /dev/null +++ b/montaukos.org/fr_icons/weather-showers-scattered.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/weather-showers.svg b/montaukos.org/fr_icons/weather-showers.svg new file mode 100644 index 0000000..6e808b6 --- /dev/null +++ b/montaukos.org/fr_icons/weather-showers.svg @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/montaukos.org/fr_icons/weather-snow-rain.svg b/montaukos.org/fr_icons/weather-snow-rain.svg new file mode 100644 index 0000000..62627fb --- /dev/null +++ b/montaukos.org/fr_icons/weather-snow-rain.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/weather-snow-scattered.svg b/montaukos.org/fr_icons/weather-snow-scattered.svg new file mode 100644 index 0000000..7a674aa --- /dev/null +++ b/montaukos.org/fr_icons/weather-snow-scattered.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/weather-snow.svg b/montaukos.org/fr_icons/weather-snow.svg new file mode 100644 index 0000000..6dc59e7 --- /dev/null +++ b/montaukos.org/fr_icons/weather-snow.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/weather-storm.svg b/montaukos.org/fr_icons/weather-storm.svg new file mode 100644 index 0000000..2fb8f6b --- /dev/null +++ b/montaukos.org/fr_icons/weather-storm.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/montaukos.org/fr_icons/weather-widget.svg b/montaukos.org/fr_icons/weather-widget.svg new file mode 100644 index 0000000..3713c6f --- /dev/null +++ b/montaukos.org/fr_icons/weather-widget.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/weather-windy.svg b/montaukos.org/fr_icons/weather-windy.svg new file mode 100644 index 0000000..d52ec2a --- /dev/null +++ b/montaukos.org/fr_icons/weather-windy.svg @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/montaukos.org/fr_icons/web-browser-symbolic.svg b/montaukos.org/fr_icons/web-browser-symbolic.svg new file mode 100644 index 0000000..5a4a340 --- /dev/null +++ b/montaukos.org/fr_icons/web-browser-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/fr_icons/web-browser.svg b/montaukos.org/fr_icons/web-browser.svg new file mode 100644 index 0000000..4dae422 --- /dev/null +++ b/montaukos.org/fr_icons/web-browser.svg @@ -0,0 +1 @@ + diff --git a/montaukos.org/fr_icons/window-close-symbolic.svg b/montaukos.org/fr_icons/window-close-symbolic.svg new file mode 100644 index 0000000..5c020b3 --- /dev/null +++ b/montaukos.org/fr_icons/window-close-symbolic.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/montaukos.org/fr_icons/window-maximize-symbolic.svg b/montaukos.org/fr_icons/window-maximize-symbolic.svg new file mode 100644 index 0000000..6c0db07 --- /dev/null +++ b/montaukos.org/fr_icons/window-maximize-symbolic.svg @@ -0,0 +1,3 @@ + + + diff --git a/montaukos.org/fr_icons/window-minimize-symbolic.svg b/montaukos.org/fr_icons/window-minimize-symbolic.svg new file mode 100644 index 0000000..3333f50 --- /dev/null +++ b/montaukos.org/fr_icons/window-minimize-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/montaukos.org/index.html b/montaukos.org/index.html new file mode 100644 index 0000000..c784dc0 --- /dev/null +++ b/montaukos.org/index.html @@ -0,0 +1,163 @@ + + + + + + + MontaukOS + + + + + + + + + +
+
+

MontaukOS

+

Modern and unique operating bare metal operating system

+
+ +
+ +

About

+

+MontaukOS is a novel multi-user operating system that runs on bare metal. +It features its own preemptive multitasking kernel and modern userspace with its own desktop environment, +targeting both emulators and real hardware. +

+ +

Features

+
    +
  • Real hardware support: Intel GPU, e100e Ethernet, HDA audio, Bluetooth
  • +
  • Storage: AHCI/NVMe SSDs, GPT partitions, ext2 and FAT32 filesystems
  • +
  • ACPI support including S3 sleep and shutdown
  • +
  • USB and PS/2 input device support
  • +
  • Desktop environment with 12+ applications
  • +
+ +

Applications

+
+
+
+ Terminal + Terminal +
+
+ File Manager + File Manager +
+ +
+ Weather App + Weather App +
+
+ PDF Viewer + PDF Viewer +
+
+ Music Player + Music Player +
+
+ DOOM + DOOM +
+
+

+
+ + + +
+ + diff --git a/programs/include/gui/mtk.hpp b/programs/include/gui/mtk.hpp new file mode 100644 index 0000000..fa8d743 --- /dev/null +++ b/programs/include/gui/mtk.hpp @@ -0,0 +1,12 @@ +/* + * mtk.hpp + * Umbrella include for the Montauk Toolkit + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once + +#include "gui/mtk/theme.hpp" +#include "gui/mtk/widgets.hpp" +#include "gui/mtk/table.hpp" +#include "gui/mtk/hosts.hpp" diff --git a/programs/include/gui/mtk/hosts.hpp b/programs/include/gui/mtk/hosts.hpp new file mode 100644 index 0000000..d0dcc32 --- /dev/null +++ b/programs/include/gui/mtk/hosts.hpp @@ -0,0 +1,63 @@ +/* + * hosts.hpp + * Montauk Toolkit host adapters for desktop and standalone apps + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once + +#include "gui/canvas.hpp" +#include "gui/standalone.hpp" +#include "gui/window.hpp" + +namespace gui::mtk { + +struct DesktopHost { + Window* window; + + explicit DesktopHost(Window* win) : window(win) {} + + Canvas canvas() const { + return window ? Canvas(window) : Canvas(nullptr, 0, 0); + } + + Rect bounds() const { + return {0, 0, window ? window->content_w : 0, window ? window->content_h : 0}; + } + + bool map_mouse(const MouseEvent& ev, int* out_x, int* out_y) const { + if (!window || !out_x || !out_y) return false; + Rect cr = window->content_rect(); + *out_x = ev.x - cr.x; + *out_y = ev.y - cr.y; + return true; + } + + void invalidate() const { + if (window) window->dirty = true; + } +}; + +struct StandaloneHost { + WsWindow* window; + + explicit StandaloneHost(WsWindow* win) : window(win) {} + + Canvas canvas() const { + return window ? window->canvas() : Canvas(nullptr, 0, 0); + } + + Rect bounds() const { + return {0, 0, window ? window->width : 0, window ? window->height : 0}; + } + + void present() const { + if (window) window->present(); + } + + void set_cursor(int cursor) const { + if (window) window->set_cursor(cursor); + } +}; + +} // namespace gui::mtk diff --git a/programs/include/gui/mtk/table.hpp b/programs/include/gui/mtk/table.hpp new file mode 100644 index 0000000..f240f3c --- /dev/null +++ b/programs/include/gui/mtk/table.hpp @@ -0,0 +1,150 @@ +/* + * table.hpp + * Montauk Toolkit table/list helpers + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once + +#include "gui/canvas.hpp" +#include "gui/mtk/theme.hpp" + +namespace gui::mtk { + +struct TableColumn { + const char* label; + int x; + int w; +}; + +struct TableLayout { + Rect header; + Rect body; + int row_h; +}; + +struct TableStyle { + Color header_bg; + Color header_border; + Color header_text; + Color row_selected_bg; + Color row_hover_bg; +}; + +inline TableStyle make_table_style(const Theme& theme) { + return { + theme.surface_alt, + theme.border, + theme.text_subtle, + theme.accent_soft, + colors::TRANSPARENT, + }; +} + +inline int table_visible_rows(const TableLayout& layout) { + if (layout.row_h <= 0 || layout.body.h <= 0) return 0; + return layout.body.h / layout.row_h; +} + +inline int table_max_scroll(const TableLayout& layout, int row_count) { + int visible = table_visible_rows(layout); + if (row_count > visible && visible > 0) return row_count - visible; + return 0; +} + +inline void clamp_table_scroll(int* scroll, const TableLayout& layout, int row_count) { + if (!scroll) return; + *scroll = gui_clamp(*scroll, 0, table_max_scroll(layout, row_count)); +} + +inline void ensure_table_row_visible(int* scroll, + const TableLayout& layout, + int row_count, + int selected) { + if (!scroll || selected < 0) return; + int visible = table_visible_rows(layout); + if (visible <= 0) return; + if (selected < *scroll) { + *scroll = selected; + } else if (selected >= *scroll + visible) { + *scroll = selected - visible + 1; + } + clamp_table_scroll(scroll, layout, row_count); +} + +inline Rect table_row_rect(const TableLayout& layout, int scroll, int row_index) { + return { + layout.body.x, + layout.body.y + (row_index - scroll) * layout.row_h, + layout.body.w, + layout.row_h + }; +} + +inline Rect table_cell_rect(const Rect& row_rect, const TableColumn& col) { + return {row_rect.x + col.x, row_rect.y, col.w, row_rect.h}; +} + +inline int table_hit_row(const TableLayout& layout, + int scroll, + int row_count, + int mx, + int my) { + if (!layout.body.contains(mx, my) || layout.row_h <= 0) return -1; + int row = (my - layout.body.y) / layout.row_h; + int index = scroll + row; + if (index < 0 || index >= row_count) return -1; + return index; +} + +using TableRowRenderer = void (*)(Canvas& c, + void* userdata, + int row_index, + const Rect& row_rect, + int text_y, + const TableColumn* cols, + int col_count, + const Theme& theme, + const TableStyle& style); + +inline void draw_table(Canvas& c, + const TableLayout& layout, + const TableColumn* cols, + int col_count, + int row_count, + int scroll, + int selected, + int hovered, + const Theme& theme, + const TableStyle& style, + TableRowRenderer draw_row, + void* userdata) { + c.fill_rect(layout.header.x, layout.header.y, layout.header.w, layout.header.h, style.header_bg); + c.hline(layout.header.x, layout.header.y + layout.header.h - 1, layout.header.w, style.header_border); + + int fh = system_font_height(); + int header_text_y = layout.header.y + (layout.header.h - fh) / 2; + for (int i = 0; i < col_count; i++) { + c.text(layout.header.x + cols[i].x, header_text_y, cols[i].label, style.header_text); + } + + int visible = table_visible_rows(layout); + for (int row = 0; row < visible; row++) { + int index = scroll + row; + if (index >= row_count) break; + + Rect row_rect = table_row_rect(layout, scroll, index); + if (index == selected) { + c.fill_rect(row_rect.x, row_rect.y, row_rect.w, row_rect.h, style.row_selected_bg); + } else if (index == hovered && style.row_hover_bg.a > 0) { + c.fill_rect(row_rect.x, row_rect.y, row_rect.w, row_rect.h, style.row_hover_bg); + } + + if (draw_row) { + int text_y = row_rect.y + (layout.row_h - fh) / 2; + draw_row(c, userdata, index, row_rect, text_y, cols, col_count, theme, style); + } + } +} + +} // namespace gui::mtk diff --git a/programs/include/gui/mtk/theme.hpp b/programs/include/gui/mtk/theme.hpp new file mode 100644 index 0000000..a868511 --- /dev/null +++ b/programs/include/gui/mtk/theme.hpp @@ -0,0 +1,113 @@ +/* + * theme.hpp + * Montauk Toolkit theme tokens and color helpers + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once + +#include "gui/gui.hpp" + +namespace gui::mtk { + +inline Color mix(Color base, Color tint, uint8_t tint_alpha) { + auto blend = [tint_alpha](uint8_t a, uint8_t b) -> uint8_t { + return (uint8_t)((((uint16_t)a * (255 - tint_alpha)) + + ((uint16_t)b * tint_alpha) + 127) / 255); + }; + return Color::from_rgb(blend(base.r, tint.r), + blend(base.g, tint.g), + blend(base.b, tint.b)); +} + +inline Color lighten(Color c, uint8_t amount) { + return mix(c, colors::WHITE, amount); +} + +inline Color darken(Color c, uint8_t amount) { + return mix(c, colors::BLACK, amount); +} + +struct Theme { + Color window_bg; + Color surface; + Color surface_alt; + Color surface_hover; + Color border; + Color text; + Color text_muted; + Color text_subtle; + Color text_inverse; + + Color accent; + Color accent_hover; + Color accent_soft; + Color accent_fg; + Color selection; + + Color danger; + Color danger_hover; + Color danger_soft; + Color danger_fg; + + Color disabled_bg; + Color disabled_fg; + + Color badge_admin_bg; + Color badge_admin_fg; + Color badge_user_bg; + Color badge_user_fg; + + int radius_sm; + int radius_md; + int control_h; + int tab_h; + int gap_xs; + int gap_sm; + int gap_md; + int gap_lg; +}; + +inline Theme make_theme(Color accent = colors::ACCENT) { + Theme theme {}; + theme.window_bg = colors::WINDOW_BG; + theme.surface = Color::from_rgb(0xF5, 0xF5, 0xF5); + theme.surface_alt = Color::from_rgb(0xF8, 0xF8, 0xF8); + theme.surface_hover = mix(theme.surface, accent, 18); + theme.border = colors::BORDER; + theme.text = colors::TEXT_COLOR; + theme.text_muted = Color::from_rgb(0x88, 0x88, 0x88); + theme.text_subtle = Color::from_rgb(0x66, 0x66, 0x66); + theme.text_inverse = colors::WHITE; + + theme.accent = accent; + theme.accent_hover = darken(accent, 32); + theme.accent_soft = lighten(accent, 214); + theme.accent_fg = colors::WHITE; + theme.selection = accent; + + theme.danger = Color::from_rgb(0xD0, 0x3E, 0x3E); + theme.danger_hover = Color::from_rgb(0xDD, 0x44, 0x44); + theme.danger_soft = Color::from_rgb(0xF7, 0xDF, 0xDF); + theme.danger_fg = colors::WHITE; + + theme.disabled_bg = Color::from_rgb(0xCC, 0xCC, 0xCC); + theme.disabled_fg = colors::WHITE; + + theme.badge_admin_bg = Color::from_rgb(0xE8, 0xD8, 0xF0); + theme.badge_admin_fg = Color::from_rgb(0x7B, 0x3E, 0xB8); + theme.badge_user_bg = Color::from_rgb(0xE0, 0xE8, 0xF0); + theme.badge_user_fg = Color::from_rgb(0x36, 0x7B, 0xF0); + + theme.radius_sm = 3; + theme.radius_md = 4; + theme.control_h = 30; + theme.tab_h = 36; + theme.gap_xs = 2; + theme.gap_sm = 6; + theme.gap_md = 10; + theme.gap_lg = 16; + return theme; +} + +} // namespace gui::mtk diff --git a/programs/include/gui/mtk/widgets.hpp b/programs/include/gui/mtk/widgets.hpp new file mode 100644 index 0000000..2c9f282 --- /dev/null +++ b/programs/include/gui/mtk/widgets.hpp @@ -0,0 +1,365 @@ +/* + * widgets.hpp + * Montauk Toolkit immediate-mode widgets + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once + +#include "gui/canvas.hpp" +#include "gui/font.hpp" +#include "gui/mtk/theme.hpp" + +namespace gui::mtk { + +enum ButtonVariant : uint8_t { + BUTTON_PRIMARY = 0, + BUTTON_SECONDARY, + BUTTON_DANGER, + BUTTON_GHOST, + BUTTON_TONAL, +}; + +struct WidgetState { + bool active; + bool hovered; + bool enabled; +}; + +inline WidgetState widget_state(bool active = false, bool hovered = false, bool enabled = true) { + return {active, hovered, enabled}; +} + +struct ButtonColors { + Color bg; + Color border; + Color fg; +}; + +inline int text_len(const char* text) { + int len = 0; + while (text && text[len]) len++; + return len; +} + +inline bool same_color(Color a, Color b) { + return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; +} + +inline void draw_rounded_frame(Canvas& c, + const Rect& bounds, + int radius, + Color fill, + Color border, + int border_w = 1) { + if (bounds.empty()) return; + if (border_w <= 0 || same_color(fill, border)) { + c.fill_rounded_rect(bounds.x, bounds.y, bounds.w, bounds.h, gui_max(radius, 0), fill); + return; + } + + c.fill_rounded_rect(bounds.x, bounds.y, bounds.w, bounds.h, gui_max(radius, 0), border); + int inner_w = bounds.w - border_w * 2; + int inner_h = bounds.h - border_w * 2; + if (inner_w <= 0 || inner_h <= 0) return; + c.fill_rounded_rect(bounds.x + border_w, bounds.y + border_w, + inner_w, inner_h, gui_max(radius - border_w, 0), fill); +} + +inline ButtonColors resolve_button_colors(ButtonVariant variant, + const WidgetState& state, + const Theme& theme) { + if (!state.enabled) { + return {theme.disabled_bg, theme.disabled_bg, theme.disabled_fg}; + } + + switch (variant) { + case BUTTON_PRIMARY: + return { + state.hovered ? theme.accent_hover : theme.accent, + state.hovered ? theme.accent_hover : theme.accent, + theme.accent_fg + }; + case BUTTON_DANGER: + return { + state.hovered ? theme.danger_hover : theme.danger, + state.hovered ? theme.danger_hover : theme.danger, + theme.danger_fg + }; + case BUTTON_GHOST: + if (state.active) { + return {theme.accent_soft, theme.accent_soft, theme.accent}; + } + return { + state.hovered ? theme.surface_hover : theme.window_bg, + theme.border, + theme.text + }; + case BUTTON_TONAL: + { + Color fill = state.hovered ? mix(theme.surface, theme.accent, 40) + : mix(theme.surface, theme.accent, 24); + return {fill, fill, theme.text}; + } + case BUTTON_SECONDARY: + default: + if (state.active) { + return { + state.hovered ? theme.accent_hover : theme.accent, + state.hovered ? theme.accent_hover : theme.accent, + theme.accent_fg + }; + } + return { + state.hovered ? theme.surface_hover : theme.window_bg, + theme.border, + theme.text + }; + } +} + +inline void draw_button(Canvas& c, + const Rect& bounds, + const char* label, + ButtonVariant variant, + const WidgetState& state, + const Theme& theme) { + if (bounds.empty()) return; + + ButtonColors colors = resolve_button_colors(variant, state, theme); + draw_rounded_frame(c, bounds, theme.radius_md, colors.bg, colors.border); + + if (label && label[0]) { + int tw = text_width(label); + int fh = system_font_height(); + int tx = bounds.x + (bounds.w - tw) / 2; + int ty = bounds.y + (bounds.h - fh) / 2; + c.text(tx, ty, label, colors.fg); + } +} + +inline void draw_separator(Canvas& c, int x, int y, int w, const Theme& theme) { + c.hline(x, y, w, theme.border); +} + +inline Rect tab_rect(const Rect& bar, int count, int index) { + if (count <= 0) return {bar.x, bar.y, 0, bar.h}; + int slot_w = bar.w / count; + int rx = bar.x + slot_w * index; + int rw = (index == count - 1) ? (bar.w - slot_w * index) : slot_w; + return {rx, bar.y, rw, bar.h}; +} + +inline void draw_tab_bar(Canvas& c, + const Rect& bar, + const char* const* labels, + int count, + int active, + const Theme& theme) { + c.fill_rect(bar.x, bar.y, bar.w, bar.h, theme.surface); + c.hline(bar.x, bar.y + bar.h - 1, bar.w, theme.border); + + int fh = system_font_height(); + for (int i = 0; i < count; i++) { + Rect item = tab_rect(bar, count, i); + bool is_active = (i == active); + if (is_active) { + c.fill_rect(item.x, item.y, item.w, item.h, theme.window_bg); + if (item.w > 8) { + c.fill_rect(item.x + 4, item.y + item.h - 3, item.w - 8, 3, theme.selection); + } + } + + Color tc = is_active ? theme.accent : theme.text_subtle; + int tw = text_width(labels[i]); + c.text(item.x + (item.w - tw) / 2, + item.y + (item.h - fh) / 2, + labels[i], tc); + } +} + +inline int hit_tab_bar(const Rect& bar, int count, int mx, int my) { + if (!bar.contains(mx, my) || count <= 0) return -1; + for (int i = 0; i < count; i++) { + if (tab_rect(bar, count, i).contains(mx, my)) return i; + } + return -1; +} + +inline Rect radio_indicator_rect(const Rect& option) { + int size = 14; + return {option.x, option.y + (option.h - size) / 2, size, size}; +} + +inline void draw_radio(Canvas& c, + const Rect& option, + const char* label, + bool selected, + const Theme& theme) { + Rect bubble = radio_indicator_rect(option); + int radius = bubble.w / 2; + c.fill_rounded_rect(bubble.x, bubble.y, bubble.w, bubble.h, radius, theme.border); + c.fill_rounded_rect(bubble.x + 1, bubble.y + 1, bubble.w - 2, bubble.h - 2, + gui_max(radius - 1, 1), colors::WHITE); + if (selected) { + int inner = gui_max(bubble.w - 8, 2); + c.fill_rounded_rect(bubble.x + 4, bubble.y + 4, inner, inner, + gui_max(inner / 2, 1), theme.accent); + } + + int fh = system_font_height(); + c.text(bubble.x + bubble.w + theme.gap_sm, + option.y + (option.h - fh) / 2, + label, theme.text); +} + +inline Rect swatch_rect(int row_x, int row_y, int index, int size = 24, int gap = 6) { + return {row_x + index * (size + gap), row_y, size, size}; +} + +inline int hit_swatch_row(int mx, int my, + int row_x, int row_y, + int count, int size = 24, int gap = 6) { + for (int i = 0; i < count; i++) { + if (swatch_rect(row_x, row_y, i, size, gap).contains(mx, my)) return i; + } + return -1; +} + +inline void draw_swatch_row(Canvas& c, + int row_x, int row_y, + const Color* palette, + int count, + int selected_index, + const Theme& theme, + int size = 24, + int gap = 6) { + for (int i = 0; i < count; i++) { + Rect swatch = swatch_rect(row_x, row_y, i, size, gap); + if (i == selected_index) { + c.fill_rounded_rect(swatch.x - 2, swatch.y - 2, + swatch.w + 4, swatch.h + 4, 4, theme.accent); + } + c.fill_rounded_rect(swatch.x, swatch.y, swatch.w, swatch.h, 3, palette[i]); + c.rect(swatch.x, swatch.y, swatch.w, swatch.h, Color::from_rgb(0xCC, 0xCC, 0xCC)); + } +} + +inline void draw_list_row(Canvas& c, + const Rect& row, + bool selected, + bool alternate, + const Theme& theme) { + if (selected) { + c.fill_rounded_rect(row.x, row.y, row.w, row.h, theme.radius_md, theme.selection); + } else if (alternate) { + c.fill_rounded_rect(row.x, row.y, row.w, row.h, theme.radius_md, theme.surface_alt); + } +} + +inline int badge_width(const char* label, const Theme& theme) { + return text_width(label) + theme.gap_sm * 2; +} + +inline int badge_height() { + return system_font_height() + 4; +} + +inline void draw_badge(Canvas& c, + int x, int y, + const char* label, + Color bg, Color fg, + const Theme& theme) { + int bw = badge_width(label, theme); + int bh = badge_height(); + c.fill_rounded_rect(x, y, bw, bh, bh / 2, bg); + c.text(x + theme.gap_sm, y + 2, label, fg); +} + +inline const char* mask_text(const char* value, char* buffer, int cap) { + int len = 0; + while (value && value[len] && len < cap - 1) { + buffer[len] = '*'; + len++; + } + buffer[len] = '\0'; + return buffer; +} + +inline void copy_prefix(char* buffer, int cap, const char* value, int count, bool masked) { + if (cap <= 0) return; + int len = 0; + while (value && value[len] && len < count && len < cap - 1) { + buffer[len] = masked ? '*' : value[len]; + len++; + } + buffer[len] = '\0'; +} + +inline void draw_text_field(Canvas& c, + const Rect& bounds, + const char* value, + int cursor, + bool focused, + bool masked, + const Theme& theme) { + const char* text = value ? value : ""; + char masked_text[65]; + const char* shown = masked ? mask_text(text, masked_text, (int)sizeof(masked_text)) : text; + + draw_rounded_frame(c, bounds, theme.radius_sm, theme.surface, + focused ? theme.accent : theme.border); + + int fh = system_font_height(); + int tx = bounds.x + 8; + int ty = bounds.y + (bounds.h - fh) / 2; + c.text(tx, ty, shown, theme.text); + + if (focused) { + int clamped = gui_clamp(cursor, 0, text_len(text)); + char prefix[65]; + copy_prefix(prefix, (int)sizeof(prefix), text, clamped, masked); + int cx = tx + text_width(prefix); + c.fill_rect(cx, bounds.y + 6, 2, gui_max(bounds.h - 12, 1), theme.accent); + } +} + +inline Rect labeled_text_input_rect(int x, int y, int w, const Theme& theme, int field_h = 0) { + if (field_h <= 0) field_h = theme.control_h; + return {x, y + system_font_height() + theme.gap_xs, w, field_h}; +} + +inline int labeled_text_height(const Theme& theme, int field_h = 0) { + if (field_h <= 0) field_h = theme.control_h; + return system_font_height() + theme.gap_xs + field_h; +} + +inline void draw_labeled_text_field(Canvas& c, + int x, int y, int w, + const char* label, + const char* value, + int cursor, + bool focused, + bool masked, + const Theme& theme, + int field_h = 0) { + if (field_h <= 0) field_h = theme.control_h; + c.text(x, y, label, theme.text_muted); + draw_text_field(c, labeled_text_input_rect(x, y, w, theme, field_h), + value, cursor, focused, masked, theme); +} + +inline void draw_modal_actions(Canvas& c, + const Rect& primary, + const char* primary_label, + ButtonVariant primary_variant, + const WidgetState& primary_state, + const Rect& secondary, + const char* secondary_label, + const WidgetState& secondary_state, + const Theme& theme) { + draw_button(c, primary, primary_label, primary_variant, primary_state, theme); + draw_button(c, secondary, secondary_label, BUTTON_SECONDARY, secondary_state, theme); +} + +} // namespace gui::mtk