feat: TrueType (TTF) font rendering, many new desktop applications and DOOM support, among other improvements
This commit is contained in:
@@ -82,9 +82,9 @@ CA_CERTS := $(BINDIR)/etc/ca-certificates.crt
|
||||
# Home directory placeholder.
|
||||
HOMEKEEP := $(BINDIR)/home/.keep
|
||||
|
||||
.PHONY: all clean doom fetch wiki desktop icons bearssl libc
|
||||
.PHONY: all clean doom fetch wiki desktop icons fonts bearssl libc
|
||||
|
||||
all: bearssl libc $(TARGETS) fetch wiki doom desktop icons $(GAME_DATA) $(MANDST) $(WWWDST) $(CA_CERTS) $(HOMEKEEP)
|
||||
all: bearssl libc $(TARGETS) fetch wiki doom desktop icons fonts $(GAME_DATA) $(MANDST) $(WWWDST) $(CA_CERTS) $(HOMEKEEP)
|
||||
|
||||
# Build BearSSL static library.
|
||||
bearssl:
|
||||
@@ -110,6 +110,10 @@ desktop:
|
||||
icons:
|
||||
../scripts/copy_icons.sh
|
||||
|
||||
# Copy TTF fonts for the desktop into bin/fonts/.
|
||||
fonts:
|
||||
../scripts/copy_fonts.sh
|
||||
|
||||
# Build doom via its own Makefile.
|
||||
doom:
|
||||
$(MAKE) -C src/doom
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,93 @@
|
||||
Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
@@ -0,0 +1,79 @@
|
||||
JetBrains Mono Variable Font
|
||||
============================
|
||||
|
||||
This download contains JetBrains Mono as both variable fonts and static fonts.
|
||||
|
||||
JetBrains Mono is a variable font with this axis:
|
||||
wght
|
||||
|
||||
This means all the styles are contained in these files:
|
||||
JetBrains_Mono/JetBrainsMono-VariableFont_wght.ttf
|
||||
JetBrains_Mono/JetBrainsMono-Italic-VariableFont_wght.ttf
|
||||
|
||||
If your app fully supports variable fonts, you can now pick intermediate styles
|
||||
that aren’t available as static fonts. Not all apps support variable fonts, and
|
||||
in those cases you can use the static font files for JetBrains Mono:
|
||||
JetBrains_Mono/static/JetBrainsMono-Thin.ttf
|
||||
JetBrains_Mono/static/JetBrainsMono-ExtraLight.ttf
|
||||
JetBrains_Mono/static/JetBrainsMono-Light.ttf
|
||||
JetBrains_Mono/static/JetBrainsMono-Regular.ttf
|
||||
JetBrains_Mono/static/JetBrainsMono-Medium.ttf
|
||||
JetBrains_Mono/static/JetBrainsMono-SemiBold.ttf
|
||||
JetBrains_Mono/static/JetBrainsMono-Bold.ttf
|
||||
JetBrains_Mono/static/JetBrainsMono-ExtraBold.ttf
|
||||
JetBrains_Mono/static/JetBrainsMono-ThinItalic.ttf
|
||||
JetBrains_Mono/static/JetBrainsMono-ExtraLightItalic.ttf
|
||||
JetBrains_Mono/static/JetBrainsMono-LightItalic.ttf
|
||||
JetBrains_Mono/static/JetBrainsMono-Italic.ttf
|
||||
JetBrains_Mono/static/JetBrainsMono-MediumItalic.ttf
|
||||
JetBrains_Mono/static/JetBrainsMono-SemiBoldItalic.ttf
|
||||
JetBrains_Mono/static/JetBrainsMono-BoldItalic.ttf
|
||||
JetBrains_Mono/static/JetBrainsMono-ExtraBoldItalic.ttf
|
||||
|
||||
Get started
|
||||
-----------
|
||||
|
||||
1. Install the font files you want to use
|
||||
|
||||
2. Use your app's font picker to view the font family and all the
|
||||
available styles
|
||||
|
||||
Learn more about variable fonts
|
||||
-------------------------------
|
||||
|
||||
https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts
|
||||
https://variablefonts.typenetwork.com
|
||||
https://medium.com/variable-fonts
|
||||
|
||||
In desktop apps
|
||||
|
||||
https://theblog.adobe.com/can-variable-fonts-illustrator-cc
|
||||
https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts
|
||||
|
||||
Online
|
||||
|
||||
https://developers.google.com/fonts/docs/getting_started
|
||||
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide
|
||||
https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts
|
||||
|
||||
Installing fonts
|
||||
|
||||
MacOS: https://support.apple.com/en-us/HT201749
|
||||
Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux
|
||||
Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
|
||||
|
||||
Android Apps
|
||||
|
||||
https://developers.google.com/fonts/docs/android
|
||||
https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
|
||||
|
||||
License
|
||||
-------
|
||||
Please read the full license text (OFL.txt) to understand the permissions,
|
||||
restrictions and requirements for usage, redistribution, and modification.
|
||||
|
||||
You can use them in your products & projects – print or digital,
|
||||
commercial or otherwise.
|
||||
|
||||
This isn't legal advice, please consider consulting a lawyer and see the full
|
||||
license for all details.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,93 @@
|
||||
Copyright 2011 The Roboto Project Authors (https://github.com/googlefonts/roboto-classic)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
@@ -0,0 +1,118 @@
|
||||
Roboto Variable Font
|
||||
====================
|
||||
|
||||
This download contains Roboto as both variable fonts and static fonts.
|
||||
|
||||
Roboto is a variable font with these axes:
|
||||
wdth
|
||||
wght
|
||||
|
||||
This means all the styles are contained in these files:
|
||||
Roboto/Roboto-VariableFont_wdth,wght.ttf
|
||||
Roboto/Roboto-Italic-VariableFont_wdth,wght.ttf
|
||||
|
||||
If your app fully supports variable fonts, you can now pick intermediate styles
|
||||
that aren’t available as static fonts. Not all apps support variable fonts, and
|
||||
in those cases you can use the static font files for Roboto:
|
||||
Roboto/static/Roboto_Condensed-Thin.ttf
|
||||
Roboto/static/Roboto_Condensed-ExtraLight.ttf
|
||||
Roboto/static/Roboto_Condensed-Light.ttf
|
||||
Roboto/static/Roboto_Condensed-Regular.ttf
|
||||
Roboto/static/Roboto_Condensed-Medium.ttf
|
||||
Roboto/static/Roboto_Condensed-SemiBold.ttf
|
||||
Roboto/static/Roboto_Condensed-Bold.ttf
|
||||
Roboto/static/Roboto_Condensed-ExtraBold.ttf
|
||||
Roboto/static/Roboto_Condensed-Black.ttf
|
||||
Roboto/static/Roboto_SemiCondensed-Thin.ttf
|
||||
Roboto/static/Roboto_SemiCondensed-ExtraLight.ttf
|
||||
Roboto/static/Roboto_SemiCondensed-Light.ttf
|
||||
Roboto/static/Roboto_SemiCondensed-Regular.ttf
|
||||
Roboto/static/Roboto_SemiCondensed-Medium.ttf
|
||||
Roboto/static/Roboto_SemiCondensed-SemiBold.ttf
|
||||
Roboto/static/Roboto_SemiCondensed-Bold.ttf
|
||||
Roboto/static/Roboto_SemiCondensed-ExtraBold.ttf
|
||||
Roboto/static/Roboto_SemiCondensed-Black.ttf
|
||||
Roboto/static/Roboto-Thin.ttf
|
||||
Roboto/static/Roboto-ExtraLight.ttf
|
||||
Roboto/static/Roboto-Light.ttf
|
||||
Roboto/static/Roboto-Regular.ttf
|
||||
Roboto/static/Roboto-Medium.ttf
|
||||
Roboto/static/Roboto-SemiBold.ttf
|
||||
Roboto/static/Roboto-Bold.ttf
|
||||
Roboto/static/Roboto-ExtraBold.ttf
|
||||
Roboto/static/Roboto-Black.ttf
|
||||
Roboto/static/Roboto_Condensed-ThinItalic.ttf
|
||||
Roboto/static/Roboto_Condensed-ExtraLightItalic.ttf
|
||||
Roboto/static/Roboto_Condensed-LightItalic.ttf
|
||||
Roboto/static/Roboto_Condensed-Italic.ttf
|
||||
Roboto/static/Roboto_Condensed-MediumItalic.ttf
|
||||
Roboto/static/Roboto_Condensed-SemiBoldItalic.ttf
|
||||
Roboto/static/Roboto_Condensed-BoldItalic.ttf
|
||||
Roboto/static/Roboto_Condensed-ExtraBoldItalic.ttf
|
||||
Roboto/static/Roboto_Condensed-BlackItalic.ttf
|
||||
Roboto/static/Roboto_SemiCondensed-ThinItalic.ttf
|
||||
Roboto/static/Roboto_SemiCondensed-ExtraLightItalic.ttf
|
||||
Roboto/static/Roboto_SemiCondensed-LightItalic.ttf
|
||||
Roboto/static/Roboto_SemiCondensed-Italic.ttf
|
||||
Roboto/static/Roboto_SemiCondensed-MediumItalic.ttf
|
||||
Roboto/static/Roboto_SemiCondensed-SemiBoldItalic.ttf
|
||||
Roboto/static/Roboto_SemiCondensed-BoldItalic.ttf
|
||||
Roboto/static/Roboto_SemiCondensed-ExtraBoldItalic.ttf
|
||||
Roboto/static/Roboto_SemiCondensed-BlackItalic.ttf
|
||||
Roboto/static/Roboto-ThinItalic.ttf
|
||||
Roboto/static/Roboto-ExtraLightItalic.ttf
|
||||
Roboto/static/Roboto-LightItalic.ttf
|
||||
Roboto/static/Roboto-Italic.ttf
|
||||
Roboto/static/Roboto-MediumItalic.ttf
|
||||
Roboto/static/Roboto-SemiBoldItalic.ttf
|
||||
Roboto/static/Roboto-BoldItalic.ttf
|
||||
Roboto/static/Roboto-ExtraBoldItalic.ttf
|
||||
Roboto/static/Roboto-BlackItalic.ttf
|
||||
|
||||
Get started
|
||||
-----------
|
||||
|
||||
1. Install the font files you want to use
|
||||
|
||||
2. Use your app's font picker to view the font family and all the
|
||||
available styles
|
||||
|
||||
Learn more about variable fonts
|
||||
-------------------------------
|
||||
|
||||
https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts
|
||||
https://variablefonts.typenetwork.com
|
||||
https://medium.com/variable-fonts
|
||||
|
||||
In desktop apps
|
||||
|
||||
https://theblog.adobe.com/can-variable-fonts-illustrator-cc
|
||||
https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts
|
||||
|
||||
Online
|
||||
|
||||
https://developers.google.com/fonts/docs/getting_started
|
||||
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide
|
||||
https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts
|
||||
|
||||
Installing fonts
|
||||
|
||||
MacOS: https://support.apple.com/en-us/HT201749
|
||||
Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux
|
||||
Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
|
||||
|
||||
Android Apps
|
||||
|
||||
https://developers.google.com/fonts/docs/android
|
||||
https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
|
||||
|
||||
License
|
||||
-------
|
||||
Please read the full license text (OFL.txt) to understand the permissions,
|
||||
restrictions and requirements for usage, redistribution, and modification.
|
||||
|
||||
You can use them in your products & projects – print or digital,
|
||||
commercial or otherwise.
|
||||
|
||||
This isn't legal advice, please consider consulting a lawyer and see the full
|
||||
license for all details.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -66,6 +66,20 @@ namespace Zenith {
|
||||
static constexpr uint64_t SYS_CHILDIO_WRITEKEY = 52;
|
||||
static constexpr uint64_t SYS_CHILDIO_SETTERMSZ = 53;
|
||||
|
||||
// Window server syscalls
|
||||
static constexpr uint64_t SYS_WINCREATE = 54;
|
||||
static constexpr uint64_t SYS_WINDESTROY = 55;
|
||||
static constexpr uint64_t SYS_WINPRESENT = 56;
|
||||
static constexpr uint64_t SYS_WINPOLL = 57;
|
||||
static constexpr uint64_t SYS_WINENUM = 58;
|
||||
static constexpr uint64_t SYS_WINMAP = 59;
|
||||
static constexpr uint64_t SYS_WINSENDEVENT = 60;
|
||||
|
||||
// Process management syscalls
|
||||
static constexpr uint64_t SYS_PROCLIST = 61;
|
||||
static constexpr uint64_t SYS_KILL = 62;
|
||||
static constexpr uint64_t SYS_DEVLIST = 63;
|
||||
|
||||
static constexpr int SOCK_TCP = 1;
|
||||
static constexpr int SOCK_UDP = 2;
|
||||
|
||||
@@ -118,4 +132,46 @@ namespace Zenith {
|
||||
uint8_t buttons;
|
||||
};
|
||||
|
||||
// Window server shared types
|
||||
struct WinEvent {
|
||||
uint8_t type; // 0=key, 1=mouse, 2=resize, 3=close
|
||||
uint8_t _pad[3];
|
||||
union {
|
||||
KeyEvent key;
|
||||
struct { int32_t x, y, scroll; uint8_t buttons, prev_buttons; } mouse;
|
||||
struct { int32_t w, h; } resize;
|
||||
};
|
||||
};
|
||||
|
||||
struct WinInfo {
|
||||
int32_t id;
|
||||
int32_t ownerPid;
|
||||
char title[64];
|
||||
int32_t width, height;
|
||||
uint8_t dirty;
|
||||
uint8_t _pad2[3];
|
||||
};
|
||||
|
||||
struct WinCreateResult {
|
||||
int32_t id; // -1 on failure
|
||||
uint32_t _pad;
|
||||
uint64_t pixelVa; // VA of pixel buffer in caller's address space
|
||||
};
|
||||
|
||||
struct DevInfo {
|
||||
uint8_t category; // 0=CPU, 1=Interrupt, 2=Timer, 3=Input, 4=USB, 5=Network, 6=Display, 7=PCI
|
||||
uint8_t _pad[3];
|
||||
char name[48];
|
||||
char detail[48];
|
||||
};
|
||||
|
||||
struct ProcInfo {
|
||||
int32_t pid;
|
||||
int32_t parentPid;
|
||||
uint8_t state; // 0=Free, 1=Ready, 2=Running, 3=Terminated
|
||||
uint8_t _pad[3];
|
||||
char name[64];
|
||||
uint64_t heapUsed; // heapNext - UserHeapBase (bytes)
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -99,6 +99,10 @@ struct Canvas {
|
||||
// ---- Text ----
|
||||
|
||||
void text(int x, int y, const char* str, Color c) {
|
||||
if (fonts::system_font && fonts::system_font->valid) {
|
||||
fonts::system_font->draw_to_buffer(pixels, w, h, x, y, str, c, fonts::UI_SIZE);
|
||||
return;
|
||||
}
|
||||
uint32_t px = c.to_pixel();
|
||||
for (int i = 0; str[i] && x + (i + 1) * FONT_WIDTH <= w; i++) {
|
||||
const uint8_t* glyph = &font_data[(unsigned char)str[i] * FONT_HEIGHT];
|
||||
@@ -118,6 +122,10 @@ struct Canvas {
|
||||
}
|
||||
|
||||
void text_2x(int x, int y, const char* str, Color c) {
|
||||
if (fonts::system_font && fonts::system_font->valid) {
|
||||
fonts::system_font->draw_to_buffer(pixels, w, h, x, y, str, c, fonts::LARGE_SIZE);
|
||||
return;
|
||||
}
|
||||
uint32_t px = c.to_pixel();
|
||||
for (int i = 0; str[i] && x + (i + 1) * FONT_WIDTH * 2 <= w; i++) {
|
||||
const uint8_t* glyph = &font_data[(unsigned char)str[i] * FONT_HEIGHT];
|
||||
@@ -141,6 +149,14 @@ struct Canvas {
|
||||
}
|
||||
}
|
||||
|
||||
void text_mono(int x, int y, const char* str, Color c) {
|
||||
if (fonts::mono && fonts::mono->valid) {
|
||||
fonts::mono->draw_to_buffer(pixels, w, h, x, y, str, c, fonts::TERM_SIZE);
|
||||
return;
|
||||
}
|
||||
text(x, y, str, c);
|
||||
}
|
||||
|
||||
// ---- Icons ----
|
||||
|
||||
void icon(int x, int y, const SvgIcon& ic) {
|
||||
@@ -176,7 +192,8 @@ struct Canvas {
|
||||
|
||||
// ---- High-level helpers ----
|
||||
|
||||
void kv_line(int x, int* y, const char* line, Color c, int line_h = FONT_HEIGHT + 6) {
|
||||
void kv_line(int x, int* y, const char* line, Color c, int line_h = 0) {
|
||||
if (line_h == 0) line_h = system_font_height() + 6;
|
||||
text(x, *y, line, c);
|
||||
*y += line_h;
|
||||
}
|
||||
@@ -190,8 +207,9 @@ struct Canvas {
|
||||
Color bg, Color fg, int radius = 4) {
|
||||
fill_rounded_rect(x, y, bw, bh, radius, bg);
|
||||
int tw = text_width(label);
|
||||
int fh = system_font_height();
|
||||
int tx = x + (bw - tw) / 2;
|
||||
int ty = y + (bh - FONT_HEIGHT) / 2;
|
||||
int ty = y + (bh - fh) / 2;
|
||||
text(tx, ty, label, fg);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -18,6 +18,25 @@ namespace gui {
|
||||
static constexpr int MAX_WINDOWS = 8;
|
||||
static constexpr int PANEL_HEIGHT = 32;
|
||||
|
||||
struct DesktopSettings {
|
||||
// Background
|
||||
bool bg_gradient; // true = gradient, false = solid
|
||||
Color bg_solid; // solid background color
|
||||
Color bg_grad_top; // gradient top color
|
||||
Color bg_grad_bottom; // gradient bottom color
|
||||
|
||||
// Panel
|
||||
Color panel_color; // panel background color
|
||||
|
||||
// Accent
|
||||
Color accent_color; // buttons, highlights, active indicators
|
||||
|
||||
// Display
|
||||
bool show_shadows; // window shadows on/off
|
||||
bool clock_24h; // 24-hour clock format
|
||||
int ui_scale; // 0=Small, 1=Default, 2=Large
|
||||
};
|
||||
|
||||
struct DesktopState {
|
||||
Framebuffer fb;
|
||||
Window windows[MAX_WINDOWS];
|
||||
@@ -54,6 +73,11 @@ struct DesktopState {
|
||||
SvgIcon icon_settings;
|
||||
SvgIcon icon_reboot;
|
||||
|
||||
SvgIcon icon_doom;
|
||||
SvgIcon icon_procmgr;
|
||||
SvgIcon icon_mandelbrot;
|
||||
SvgIcon icon_devexplorer;
|
||||
|
||||
bool ctx_menu_open;
|
||||
int ctx_menu_x, ctx_menu_y;
|
||||
|
||||
@@ -63,6 +87,8 @@ struct DesktopState {
|
||||
Rect net_icon_rect;
|
||||
|
||||
int screen_w, screen_h;
|
||||
|
||||
DesktopSettings settings;
|
||||
};
|
||||
|
||||
// Forward declarations - implemented in main.cpp
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
/*
|
||||
* font.hpp
|
||||
* ZenithOS 8x16 VGA bitmap font rendering
|
||||
* ZenithOS text rendering — TrueType with bitmap fallback
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "gui/gui.hpp"
|
||||
#include "gui/framebuffer.hpp"
|
||||
#include "gui/truetype.hpp"
|
||||
|
||||
namespace gui {
|
||||
|
||||
@@ -16,6 +17,30 @@ static constexpr int FONT_HEIGHT = 16;
|
||||
// Defined in font_data.cpp
|
||||
extern const uint8_t font_data[256 * 16];
|
||||
|
||||
// Dynamic font height: TTF line height or 16 (bitmap fallback)
|
||||
inline int system_font_height() {
|
||||
if (fonts::system_font && fonts::system_font->valid)
|
||||
return fonts::system_font->get_line_height(fonts::UI_SIZE);
|
||||
return FONT_HEIGHT;
|
||||
}
|
||||
|
||||
// Dynamic mono font cell dimensions
|
||||
inline int mono_cell_width() {
|
||||
if (fonts::mono && fonts::mono->valid) {
|
||||
// Monospace: all glyphs have the same advance
|
||||
GlyphCache* gc = fonts::mono->get_cache(fonts::TERM_SIZE);
|
||||
CachedGlyph* g = fonts::mono->get_glyph(gc, 'M');
|
||||
if (g) return g->advance;
|
||||
}
|
||||
return FONT_WIDTH;
|
||||
}
|
||||
|
||||
inline int mono_cell_height() {
|
||||
if (fonts::mono && fonts::mono->valid)
|
||||
return fonts::mono->get_line_height(fonts::TERM_SIZE);
|
||||
return FONT_HEIGHT;
|
||||
}
|
||||
|
||||
inline void draw_char(Framebuffer& fb, int x, int y, char c, Color fg) {
|
||||
const uint8_t* glyph = &font_data[(unsigned char)c * FONT_HEIGHT];
|
||||
for (int row = 0; row < FONT_HEIGHT; row++) {
|
||||
@@ -43,18 +68,29 @@ inline void draw_char_bg(Framebuffer& fb, int x, int y, char c, Color fg, Color
|
||||
}
|
||||
|
||||
inline void draw_text(Framebuffer& fb, int x, int y, const char* text, Color fg) {
|
||||
if (fonts::system_font && fonts::system_font->valid) {
|
||||
fonts::system_font->draw(fb, x, y, text, fg, fonts::UI_SIZE);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; text[i]; i++) {
|
||||
draw_char(fb, x + i * FONT_WIDTH, y, text[i], fg);
|
||||
}
|
||||
}
|
||||
|
||||
inline void draw_text_bg(Framebuffer& fb, int x, int y, const char* text, Color fg, Color bg) {
|
||||
if (fonts::system_font && fonts::system_font->valid) {
|
||||
fonts::system_font->draw_bg(fb, x, y, text, fg, bg, fonts::UI_SIZE);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; text[i]; i++) {
|
||||
draw_char_bg(fb, x + i * FONT_WIDTH, y, text[i], fg, bg);
|
||||
}
|
||||
}
|
||||
|
||||
inline int text_width(const char* text) {
|
||||
if (fonts::system_font && fonts::system_font->valid) {
|
||||
return fonts::system_font->measure_text(text, fonts::UI_SIZE);
|
||||
}
|
||||
int len = 0;
|
||||
while (text[len]) len++;
|
||||
return len * FONT_WIDTH;
|
||||
|
||||
@@ -25,13 +25,13 @@ inline fixed_t fixed_from_parts(int whole, int frac_num, int frac_den) {
|
||||
struct Color {
|
||||
uint8_t r, g, b, a;
|
||||
|
||||
static Color from_rgb(uint8_t r, uint8_t g, uint8_t b) { return {r, g, b, 255}; }
|
||||
static Color from_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { return {r, g, b, a}; }
|
||||
static Color from_hex(uint32_t hex) {
|
||||
static constexpr Color from_rgb(uint8_t r, uint8_t g, uint8_t b) { return {r, g, b, 255}; }
|
||||
static constexpr Color from_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { return {r, g, b, a}; }
|
||||
static constexpr Color from_hex(uint32_t hex) {
|
||||
return {(uint8_t)((hex >> 16) & 0xFF), (uint8_t)((hex >> 8) & 0xFF), (uint8_t)(hex & 0xFF), 255};
|
||||
}
|
||||
|
||||
uint32_t to_pixel() const { return ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; }
|
||||
constexpr uint32_t to_pixel() const { return ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; }
|
||||
};
|
||||
|
||||
// Named colors for the desktop theme
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* stb_math.h
|
||||
* Math functions for stb_truetype in freestanding environment
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#ifndef STB_MATH_H
|
||||
#define STB_MATH_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static inline double stb_floor(double x) {
|
||||
double i = (double)(long long)x;
|
||||
return (x < i) ? i - 1.0 : i;
|
||||
}
|
||||
|
||||
static inline double stb_ceil(double x) {
|
||||
double f = stb_floor(x);
|
||||
return (x > f) ? f + 1.0 : f;
|
||||
}
|
||||
|
||||
static inline double stb_fabs(double x) {
|
||||
return x < 0.0 ? -x : x;
|
||||
}
|
||||
|
||||
static inline double stb_fmod(double x, double y) {
|
||||
if (y == 0.0) return 0.0;
|
||||
return x - (double)((long long)(x / y)) * y;
|
||||
}
|
||||
|
||||
static inline double stb_sqrt(double x) {
|
||||
if (x <= 0.0) return 0.0;
|
||||
double guess = x;
|
||||
for (int i = 0; i < 30; i++)
|
||||
guess = (guess + x / guess) * 0.5;
|
||||
return guess;
|
||||
}
|
||||
|
||||
static inline double stb_pow(double base, double exp) {
|
||||
if (exp == 0.0) return 1.0;
|
||||
if (exp == 1.0) return base;
|
||||
if (base == 0.0) return 0.0;
|
||||
// Integer exponent fast path
|
||||
if (exp == (double)(long long)exp) {
|
||||
long long e = (long long)exp;
|
||||
int neg = 0;
|
||||
if (e < 0) { neg = 1; e = -e; }
|
||||
double r = 1.0;
|
||||
double b = base;
|
||||
while (e > 0) {
|
||||
if (e & 1) r *= b;
|
||||
b *= b;
|
||||
e >>= 1;
|
||||
}
|
||||
return neg ? 1.0 / r : r;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
static inline double stb_cos(double x) {
|
||||
// Reduce to [0, 2*pi]
|
||||
const double PI = 3.14159265358979323846;
|
||||
const double TWO_PI = 6.28318530717958647692;
|
||||
x = stb_fmod(stb_fabs(x), TWO_PI);
|
||||
// Taylor series: cos(x) = 1 - x^2/2! + x^4/4! - x^6/6! + ...
|
||||
double x2 = x * x;
|
||||
double term = 1.0;
|
||||
double result = 1.0;
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
term *= -x2 / (double)((2 * i - 1) * (2 * i));
|
||||
result += term;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline double stb_acos(double x) {
|
||||
// Clamp input
|
||||
if (x <= -1.0) return 3.14159265358979323846;
|
||||
if (x >= 1.0) return 0.0;
|
||||
// Polynomial approximation (Abramowitz & Stegun style)
|
||||
double ax = stb_fabs(x);
|
||||
double result = (-0.0187293 * ax + 0.0742610) * ax - 0.2121144;
|
||||
result = (result * ax + 1.5707288) * stb_sqrt(1.0 - ax);
|
||||
if (x < 0.0)
|
||||
return 3.14159265358979323846 - result;
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // STB_MATH_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -423,6 +423,11 @@ static inline void terminal_feed(TerminalState* t, const char* data, int len) {
|
||||
}
|
||||
|
||||
static inline void terminal_render(TerminalState* t, uint32_t* pixels, int pw, int ph) {
|
||||
int cell_w = mono_cell_width();
|
||||
int cell_h = mono_cell_height();
|
||||
bool use_ttf = fonts::mono && fonts::mono->valid;
|
||||
GlyphCache* gc = use_ttf ? fonts::mono->get_cache(fonts::TERM_SIZE) : nullptr;
|
||||
|
||||
// Fill background
|
||||
uint32_t bg_px = colors::TERM_BG.to_pixel();
|
||||
int total = pw * ph;
|
||||
@@ -431,8 +436,8 @@ static inline void terminal_render(TerminalState* t, uint32_t* pixels, int pw, i
|
||||
}
|
||||
|
||||
// Render each visible cell
|
||||
int visible_rows = ph / FONT_HEIGHT;
|
||||
int visible_cols = pw / FONT_WIDTH;
|
||||
int visible_rows = ph / cell_h;
|
||||
int visible_cols = pw / cell_w;
|
||||
if (visible_rows > t->rows) visible_rows = t->rows;
|
||||
if (visible_cols > t->cols) visible_cols = t->cols;
|
||||
|
||||
@@ -441,17 +446,16 @@ static inline void terminal_render(TerminalState* t, uint32_t* pixels, int pw, i
|
||||
int idx = r * t->cols + c;
|
||||
TermCell& cell = t->cells[idx];
|
||||
|
||||
int px = c * FONT_WIDTH;
|
||||
int py = r * FONT_HEIGHT;
|
||||
int px = c * cell_w;
|
||||
int py = r * cell_h;
|
||||
|
||||
uint32_t cell_bg = cell.bg.to_pixel();
|
||||
uint32_t cell_fg = cell.fg.to_pixel();
|
||||
|
||||
// Draw cell background
|
||||
for (int fy = 0; fy < FONT_HEIGHT; fy++) {
|
||||
for (int fy = 0; fy < cell_h; fy++) {
|
||||
int dy = py + fy;
|
||||
if (dy >= ph) break;
|
||||
for (int fx = 0; fx < FONT_WIDTH; fx++) {
|
||||
for (int fx = 0; fx < cell_w; fx++) {
|
||||
int dx = px + fx;
|
||||
if (dx >= pw) break;
|
||||
pixels[dy * pw + dx] = cell_bg;
|
||||
@@ -460,16 +464,23 @@ static inline void terminal_render(TerminalState* t, uint32_t* pixels, int pw, i
|
||||
|
||||
// Draw character glyph
|
||||
if (cell.ch > 32 || cell.ch < 0) {
|
||||
const uint8_t* glyph = &font_data[(unsigned char)cell.ch * FONT_HEIGHT];
|
||||
for (int fy = 0; fy < FONT_HEIGHT; fy++) {
|
||||
int dy = py + fy;
|
||||
if (dy >= ph) break;
|
||||
uint8_t bits = glyph[fy];
|
||||
for (int fx = 0; fx < FONT_WIDTH; fx++) {
|
||||
if (bits & (0x80 >> fx)) {
|
||||
int dx = px + fx;
|
||||
if (dx >= pw) break;
|
||||
pixels[dy * pw + dx] = cell_fg;
|
||||
if (use_ttf) {
|
||||
int baseline = py + gc->ascent;
|
||||
fonts::mono->draw_char_to_buffer(pixels, pw, ph,
|
||||
px, baseline, (unsigned char)cell.ch, cell.fg, gc);
|
||||
} else {
|
||||
uint32_t cell_fg = cell.fg.to_pixel();
|
||||
const uint8_t* glyph = &font_data[(unsigned char)cell.ch * FONT_HEIGHT];
|
||||
for (int fy = 0; fy < FONT_HEIGHT; fy++) {
|
||||
int dy = py + fy;
|
||||
if (dy >= ph) break;
|
||||
uint8_t bits = glyph[fy];
|
||||
for (int fx = 0; fx < FONT_WIDTH; fx++) {
|
||||
if (bits & (0x80 >> fx)) {
|
||||
int dx = px + fx;
|
||||
if (dx >= pw) break;
|
||||
pixels[dy * pw + dx] = cell_fg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -479,13 +490,13 @@ static inline void terminal_render(TerminalState* t, uint32_t* pixels, int pw, i
|
||||
|
||||
// Draw cursor
|
||||
if (t->cursor_visible && t->cursor_x < visible_cols && t->cursor_y < visible_rows) {
|
||||
int cx = t->cursor_x * FONT_WIDTH;
|
||||
int cy = t->cursor_y * FONT_HEIGHT;
|
||||
int cx = t->cursor_x * cell_w;
|
||||
int cy = t->cursor_y * cell_h;
|
||||
uint32_t cursor_px = colors::WHITE.to_pixel();
|
||||
for (int fy = 0; fy < FONT_HEIGHT; fy++) {
|
||||
for (int fy = 0; fy < cell_h; fy++) {
|
||||
int dy = cy + fy;
|
||||
if (dy >= ph) break;
|
||||
for (int fx = 0; fx < FONT_WIDTH; fx++) {
|
||||
for (int fx = 0; fx < cell_w; fx++) {
|
||||
int dx = cx + fx;
|
||||
if (dx >= pw) break;
|
||||
pixels[dy * pw + dx] = cursor_px;
|
||||
@@ -496,17 +507,23 @@ static inline void terminal_render(TerminalState* t, uint32_t* pixels, int pw, i
|
||||
int idx = t->cursor_y * t->cols + t->cursor_x;
|
||||
char ch = t->cells[idx].ch;
|
||||
if (ch > 32 || ch < 0) {
|
||||
const uint8_t* glyph = &font_data[(unsigned char)ch * FONT_HEIGHT];
|
||||
uint32_t black_px = colors::BLACK.to_pixel();
|
||||
for (int fy = 0; fy < FONT_HEIGHT; fy++) {
|
||||
int dy = cy + fy;
|
||||
if (dy >= ph) break;
|
||||
uint8_t bits = glyph[fy];
|
||||
for (int fx = 0; fx < FONT_WIDTH; fx++) {
|
||||
if (bits & (0x80 >> fx)) {
|
||||
int dx = cx + fx;
|
||||
if (dx >= pw) break;
|
||||
pixels[dy * pw + dx] = black_px;
|
||||
if (use_ttf) {
|
||||
int baseline = cy + gc->ascent;
|
||||
fonts::mono->draw_char_to_buffer(pixels, pw, ph,
|
||||
cx, baseline, (unsigned char)ch, colors::BLACK, gc);
|
||||
} else {
|
||||
const uint8_t* glyph = &font_data[(unsigned char)ch * FONT_HEIGHT];
|
||||
uint32_t black_px = colors::BLACK.to_pixel();
|
||||
for (int fy = 0; fy < FONT_HEIGHT; fy++) {
|
||||
int dy = cy + fy;
|
||||
if (dy >= ph) break;
|
||||
uint8_t bits = glyph[fy];
|
||||
for (int fx = 0; fx < FONT_WIDTH; fx++) {
|
||||
if (bits & (0x80 >> fx)) {
|
||||
int dx = cx + fx;
|
||||
if (dx >= pw) break;
|
||||
pixels[dy * pw + dx] = black_px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -515,6 +532,58 @@ static inline void terminal_render(TerminalState* t, uint32_t* pixels, int pw, i
|
||||
}
|
||||
}
|
||||
|
||||
static inline void terminal_resize(TerminalState* t, int new_cols, int new_rows) {
|
||||
if (new_cols == t->cols && new_rows == t->rows) return;
|
||||
if (new_cols < 1 || new_rows < 1) return;
|
||||
|
||||
int new_total = new_cols * new_rows;
|
||||
TermCell* new_cells = (TermCell*)zenith::alloc(new_total * sizeof(TermCell));
|
||||
TermCell* new_alt = (TermCell*)zenith::alloc(new_total * sizeof(TermCell));
|
||||
|
||||
// Clear new buffers
|
||||
for (int i = 0; i < new_total; i++) {
|
||||
new_cells[i] = {' ', colors::TERM_FG, colors::TERM_BG};
|
||||
new_alt[i] = {' ', colors::TERM_FG, colors::TERM_BG};
|
||||
}
|
||||
|
||||
// Copy existing content (as much as fits)
|
||||
int copy_rows = t->rows < new_rows ? t->rows : new_rows;
|
||||
int copy_cols = t->cols < new_cols ? t->cols : new_cols;
|
||||
|
||||
// If cursor is beyond the new grid, scroll content up to keep cursor visible
|
||||
int row_offset = 0;
|
||||
if (t->cursor_y >= new_rows) {
|
||||
row_offset = t->cursor_y - new_rows + 1;
|
||||
}
|
||||
|
||||
for (int r = 0; r < copy_rows && (r + row_offset) < t->rows; r++) {
|
||||
for (int c = 0; c < copy_cols; c++) {
|
||||
new_cells[r * new_cols + c] = t->cells[(r + row_offset) * t->cols + c];
|
||||
}
|
||||
}
|
||||
|
||||
if (t->cells) zenith::mfree(t->cells);
|
||||
if (t->alt_cells) zenith::mfree(t->alt_cells);
|
||||
|
||||
t->cells = new_cells;
|
||||
t->alt_cells = new_alt;
|
||||
|
||||
int old_cols = t->cols;
|
||||
t->cols = new_cols;
|
||||
t->rows = new_rows;
|
||||
|
||||
// Adjust cursor position
|
||||
t->cursor_y -= row_offset;
|
||||
if (t->cursor_x >= new_cols) t->cursor_x = new_cols - 1;
|
||||
if (t->cursor_y >= new_rows) t->cursor_y = new_rows - 1;
|
||||
if (t->cursor_y < 0) t->cursor_y = 0;
|
||||
|
||||
// Notify child process of new terminal size
|
||||
if (t->child_pid > 0) {
|
||||
zenith::childio_settermsz(t->child_pid, new_cols, new_rows);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void terminal_handle_key(TerminalState* t, const Zenith::KeyEvent& key) {
|
||||
if (t->child_pid > 0) {
|
||||
zenith::childio_writekey(t->child_pid, &key);
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
* truetype.hpp
|
||||
* ZenithOS TrueType font rendering via stb_truetype
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <zenith/syscall.h>
|
||||
#include <zenith/heap.h>
|
||||
#include <zenith/string.h>
|
||||
#include "gui/gui.hpp"
|
||||
#include "gui/framebuffer.hpp"
|
||||
|
||||
// Forward-declare stbtt_fontinfo to avoid including stb_truetype.h in every TU.
|
||||
// The actual struct is defined in stb_truetype.h and only used in truetype.hpp
|
||||
// method bodies (which are inline but only instantiated where stb_truetype.h
|
||||
// is also included — i.e. stb_truetype_impl.cpp). We include the full header
|
||||
// here since our inline methods need the complete type.
|
||||
#include "gui/stb_math.h"
|
||||
|
||||
// We need the stb macros defined before including stb_truetype.h (header-only mode)
|
||||
#ifndef STBTT_ifloor
|
||||
#define STBTT_ifloor(x) ((int) stb_floor(x))
|
||||
#define STBTT_iceil(x) ((int) stb_ceil(x))
|
||||
#define STBTT_sqrt(x) stb_sqrt(x)
|
||||
#define STBTT_pow(x,y) stb_pow(x,y)
|
||||
#define STBTT_fmod(x,y) stb_fmod(x,y)
|
||||
#define STBTT_cos(x) stb_cos(x)
|
||||
#define STBTT_acos(x) stb_acos(x)
|
||||
#define STBTT_fabs(x) stb_fabs(x)
|
||||
#define STBTT_malloc(x,u) ((void)(u), zenith::malloc(x))
|
||||
#define STBTT_free(x,u) ((void)(u), zenith::mfree(x))
|
||||
#define STBTT_memcpy(d,s,n) zenith::memcpy(d,s,n)
|
||||
#define STBTT_memset(d,v,n) zenith::memset(d,v,n)
|
||||
#define STBTT_strlen(x) zenith::slen(x)
|
||||
#define STBTT_assert(x) ((void)(x))
|
||||
#endif
|
||||
|
||||
#include "gui/stb_truetype.h"
|
||||
|
||||
namespace gui {
|
||||
|
||||
struct CachedGlyph {
|
||||
uint8_t* bitmap;
|
||||
int width, height;
|
||||
int xoff, yoff;
|
||||
int advance;
|
||||
bool loaded;
|
||||
};
|
||||
|
||||
struct GlyphCache {
|
||||
CachedGlyph glyphs[128];
|
||||
int pixel_size;
|
||||
float scale;
|
||||
int ascent, descent, line_gap;
|
||||
int line_height;
|
||||
};
|
||||
|
||||
struct TrueTypeFont {
|
||||
stbtt_fontinfo info;
|
||||
uint8_t* data;
|
||||
GlyphCache caches[4];
|
||||
int cache_count;
|
||||
bool valid;
|
||||
|
||||
bool init(const char* vfs_path) {
|
||||
valid = false;
|
||||
data = nullptr;
|
||||
cache_count = 0;
|
||||
|
||||
int fd = zenith::open(vfs_path);
|
||||
if (fd < 0) return false;
|
||||
|
||||
uint64_t size = zenith::getsize(fd);
|
||||
if (size == 0 || size > 1024 * 1024) {
|
||||
zenith::close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
data = (uint8_t*)zenith::alloc(size);
|
||||
if (!data) {
|
||||
zenith::close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
zenith::read(fd, data, 0, size);
|
||||
zenith::close(fd);
|
||||
|
||||
if (!stbtt_InitFont(&info, data, stbtt_GetFontOffsetForIndex(data, 0))) {
|
||||
zenith::free(data);
|
||||
data = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
valid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
GlyphCache* get_cache(int pixel_size) {
|
||||
// Search existing caches
|
||||
for (int i = 0; i < cache_count; i++) {
|
||||
if (caches[i].pixel_size == pixel_size)
|
||||
return &caches[i];
|
||||
}
|
||||
|
||||
// Create new cache
|
||||
if (cache_count >= 4) return &caches[0]; // fallback to first
|
||||
|
||||
GlyphCache* gc = &caches[cache_count++];
|
||||
gc->pixel_size = pixel_size;
|
||||
gc->scale = stbtt_ScaleForPixelHeight(&info, (float)pixel_size);
|
||||
|
||||
int asc, desc, lg;
|
||||
stbtt_GetFontVMetrics(&info, &asc, &desc, &lg);
|
||||
gc->ascent = (int)(asc * gc->scale);
|
||||
gc->descent = (int)(desc * gc->scale);
|
||||
gc->line_gap = (int)(lg * gc->scale);
|
||||
gc->line_height = gc->ascent - gc->descent + gc->line_gap;
|
||||
|
||||
for (int i = 0; i < 128; i++) {
|
||||
gc->glyphs[i].bitmap = nullptr;
|
||||
gc->glyphs[i].loaded = false;
|
||||
}
|
||||
|
||||
return gc;
|
||||
}
|
||||
|
||||
CachedGlyph* get_glyph(GlyphCache* gc, int codepoint) {
|
||||
if (codepoint < 0 || codepoint >= 128) return nullptr;
|
||||
CachedGlyph* g = &gc->glyphs[codepoint];
|
||||
if (g->loaded) return g;
|
||||
|
||||
g->loaded = true;
|
||||
int advance, lsb;
|
||||
stbtt_GetCodepointHMetrics(&info, codepoint, &advance, &lsb);
|
||||
g->advance = (int)(advance * gc->scale);
|
||||
|
||||
int x0, y0, x1, y1;
|
||||
stbtt_GetCodepointBitmapBox(&info, codepoint, gc->scale, gc->scale,
|
||||
&x0, &y0, &x1, &y1);
|
||||
g->width = x1 - x0;
|
||||
g->height = y1 - y0;
|
||||
g->xoff = x0;
|
||||
g->yoff = y0;
|
||||
|
||||
if (g->width > 0 && g->height > 0) {
|
||||
g->bitmap = (uint8_t*)zenith::malloc(g->width * g->height);
|
||||
stbtt_MakeCodepointBitmap(&info, g->bitmap, g->width, g->height,
|
||||
g->width, gc->scale, gc->scale, codepoint);
|
||||
}
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
int measure_text(const char* text, int pixel_size) {
|
||||
if (!valid) return 0;
|
||||
GlyphCache* gc = get_cache(pixel_size);
|
||||
int w = 0;
|
||||
for (int i = 0; text[i]; i++) {
|
||||
CachedGlyph* g = get_glyph(gc, (unsigned char)text[i]);
|
||||
if (g) w += g->advance;
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
int get_line_height(int pixel_size) {
|
||||
if (!valid) return 16;
|
||||
GlyphCache* gc = get_cache(pixel_size);
|
||||
return gc->line_height;
|
||||
}
|
||||
|
||||
void draw(Framebuffer& fb, int x, int y, const char* text,
|
||||
Color color, int pixel_size) {
|
||||
if (!valid) return;
|
||||
GlyphCache* gc = get_cache(pixel_size);
|
||||
int cx = x;
|
||||
int baseline = y + gc->ascent;
|
||||
|
||||
for (int i = 0; text[i]; i++) {
|
||||
CachedGlyph* g = get_glyph(gc, (unsigned char)text[i]);
|
||||
if (!g) continue;
|
||||
|
||||
if (g->bitmap) {
|
||||
int gx = cx + g->xoff;
|
||||
int gy = baseline + g->yoff;
|
||||
for (int row = 0; row < g->height; row++) {
|
||||
for (int col = 0; col < g->width; col++) {
|
||||
uint8_t alpha = g->bitmap[row * g->width + col];
|
||||
if (alpha > 0) {
|
||||
Color c = {color.r, color.g, color.b, alpha};
|
||||
fb.put_pixel_alpha(gx + col, gy + row, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cx += g->advance;
|
||||
}
|
||||
}
|
||||
|
||||
void draw_bg(Framebuffer& fb, int x, int y, const char* text,
|
||||
Color fg, Color bg, int pixel_size) {
|
||||
if (!valid) return;
|
||||
GlyphCache* gc = get_cache(pixel_size);
|
||||
|
||||
// Fill background for the text extent
|
||||
int tw = measure_text(text, pixel_size);
|
||||
fb.fill_rect(x, y, tw, gc->line_height, bg);
|
||||
|
||||
// Then draw foreground
|
||||
draw(fb, x, y, text, fg, pixel_size);
|
||||
}
|
||||
|
||||
void draw_to_buffer(uint32_t* pixels, int buf_w, int buf_h,
|
||||
int x, int y, const char* text,
|
||||
Color color, int pixel_size) {
|
||||
if (!valid) return;
|
||||
GlyphCache* gc = get_cache(pixel_size);
|
||||
int cx = x;
|
||||
int baseline = y + gc->ascent;
|
||||
|
||||
for (int i = 0; text[i]; i++) {
|
||||
CachedGlyph* g = get_glyph(gc, (unsigned char)text[i]);
|
||||
if (!g) continue;
|
||||
|
||||
if (g->bitmap) {
|
||||
int gx = cx + g->xoff;
|
||||
int gy = baseline + g->yoff;
|
||||
for (int row = 0; row < g->height; row++) {
|
||||
int dy = gy + row;
|
||||
if (dy < 0 || dy >= buf_h) continue;
|
||||
for (int col = 0; col < g->width; col++) {
|
||||
int dx = gx + col;
|
||||
if (dx < 0 || dx >= buf_w) continue;
|
||||
uint8_t alpha = g->bitmap[row * g->width + col];
|
||||
if (alpha == 0) continue;
|
||||
|
||||
if (alpha == 255) {
|
||||
pixels[dy * buf_w + dx] =
|
||||
0xFF000000 | ((uint32_t)color.r << 16) |
|
||||
((uint32_t)color.g << 8) | color.b;
|
||||
} else {
|
||||
uint32_t dst = pixels[dy * buf_w + dx];
|
||||
uint8_t dr = (dst >> 16) & 0xFF;
|
||||
uint8_t dg = (dst >> 8) & 0xFF;
|
||||
uint8_t db = dst & 0xFF;
|
||||
uint32_t a = alpha, inv_a = 255 - alpha;
|
||||
uint32_t rr = (a * color.r + inv_a * dr + 128) / 255;
|
||||
uint32_t gg = (a * color.g + inv_a * dg + 128) / 255;
|
||||
uint32_t bb = (a * color.b + inv_a * db + 128) / 255;
|
||||
pixels[dy * buf_w + dx] =
|
||||
0xFF000000 | (rr << 16) | (gg << 8) | bb;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cx += g->advance;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw single character to buffer, returning advance width
|
||||
int draw_char_to_buffer(uint32_t* pixels, int buf_w, int buf_h,
|
||||
int x, int baseline, int codepoint,
|
||||
Color color, GlyphCache* gc) {
|
||||
CachedGlyph* g = get_glyph(gc, codepoint);
|
||||
if (!g) return 0;
|
||||
|
||||
if (g->bitmap) {
|
||||
int gx = x + g->xoff;
|
||||
int gy = baseline + g->yoff;
|
||||
for (int row = 0; row < g->height; row++) {
|
||||
int dy = gy + row;
|
||||
if (dy < 0 || dy >= buf_h) continue;
|
||||
for (int col = 0; col < g->width; col++) {
|
||||
int dx = gx + col;
|
||||
if (dx < 0 || dx >= buf_w) continue;
|
||||
uint8_t alpha = g->bitmap[row * g->width + col];
|
||||
if (alpha == 0) continue;
|
||||
|
||||
if (alpha == 255) {
|
||||
pixels[dy * buf_w + dx] =
|
||||
0xFF000000 | ((uint32_t)color.r << 16) |
|
||||
((uint32_t)color.g << 8) | color.b;
|
||||
} else {
|
||||
uint32_t dst = pixels[dy * buf_w + dx];
|
||||
uint8_t dr = (dst >> 16) & 0xFF;
|
||||
uint8_t dg = (dst >> 8) & 0xFF;
|
||||
uint8_t db = dst & 0xFF;
|
||||
uint32_t a = alpha, inv_a = 255 - alpha;
|
||||
uint32_t rr = (a * color.r + inv_a * dr + 128) / 255;
|
||||
uint32_t gg = (a * color.g + inv_a * dg + 128) / 255;
|
||||
uint32_t bb = (a * color.b + inv_a * db + 128) / 255;
|
||||
pixels[dy * buf_w + dx] =
|
||||
0xFF000000 | (rr << 16) | (gg << 8) | bb;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return g->advance;
|
||||
}
|
||||
};
|
||||
|
||||
// Global font manager
|
||||
namespace fonts {
|
||||
inline TrueTypeFont* system_font = nullptr;
|
||||
inline TrueTypeFont* system_bold = nullptr;
|
||||
inline TrueTypeFont* mono = nullptr;
|
||||
inline TrueTypeFont* mono_bold = nullptr;
|
||||
|
||||
inline int UI_SIZE = 18;
|
||||
inline int TITLE_SIZE = 18;
|
||||
inline int TERM_SIZE = 18;
|
||||
inline int LARGE_SIZE = 28;
|
||||
|
||||
inline bool init() {
|
||||
auto load = [](const char* path) -> TrueTypeFont* {
|
||||
TrueTypeFont* f = (TrueTypeFont*)zenith::malloc(sizeof(TrueTypeFont));
|
||||
zenith::memset(f, 0, sizeof(TrueTypeFont));
|
||||
if (!f->init(path)) {
|
||||
zenith::mfree(f);
|
||||
return nullptr;
|
||||
}
|
||||
return f;
|
||||
};
|
||||
|
||||
system_font = load("0:/fonts/Roboto-Medium.ttf");
|
||||
system_bold = load("0:/fonts/Roboto-Bold.ttf");
|
||||
mono = load("0:/fonts/JetBrainsMono-Regular.ttf");
|
||||
mono_bold = load("0:/fonts/JetBrainsMono-Bold.ttf");
|
||||
|
||||
return system_font != nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
@@ -79,7 +79,7 @@ struct Button {
|
||||
// Center text
|
||||
int tw = text_width(text);
|
||||
int tx = bounds.x + (bounds.w - tw) / 2;
|
||||
int ty = bounds.y + (bounds.h - FONT_HEIGHT) / 2;
|
||||
int ty = bounds.y + (bounds.h - system_font_height()) / 2;
|
||||
draw_text(fb, tx, ty, text, fg);
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ struct IconButton {
|
||||
|
||||
// Draw text
|
||||
if (text) {
|
||||
int ty = bounds.y + (bounds.h - FONT_HEIGHT) / 2;
|
||||
int ty = bounds.y + (bounds.h - system_font_height()) / 2;
|
||||
draw_text(fb, content_x, ty, text, text_color);
|
||||
}
|
||||
}
|
||||
@@ -206,13 +206,18 @@ struct TextBox {
|
||||
|
||||
// Draw text with 4px padding
|
||||
int tx = bounds.x + 4;
|
||||
int ty = bounds.y + (bounds.h - FONT_HEIGHT) / 2;
|
||||
int fh = system_font_height();
|
||||
int ty = bounds.y + (bounds.h - fh) / 2;
|
||||
draw_text(fb, tx, ty, text, fg);
|
||||
|
||||
// Draw cursor if focused
|
||||
if (focused) {
|
||||
int cx = tx + cursor * FONT_WIDTH;
|
||||
draw_vline(fb, cx, ty, FONT_HEIGHT, cursor_color);
|
||||
// Measure text up to cursor position for proportional fonts
|
||||
char prefix[256];
|
||||
for (int i = 0; i < cursor && i < 255; i++) prefix[i] = text[i];
|
||||
prefix[cursor < 255 ? cursor : 255] = '\0';
|
||||
int cx = tx + text_width(prefix);
|
||||
draw_vline(fb, cx, ty, fh, cursor_color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,9 @@ struct Window {
|
||||
WindowPollCallback on_poll;
|
||||
void* app_data;
|
||||
|
||||
bool external; // true = shared-memory window from external process
|
||||
int ext_win_id; // window server ID (valid when external == true)
|
||||
|
||||
Rect titlebar_rect() const {
|
||||
return {frame.x, frame.y, frame.w, TITLEBAR_HEIGHT};
|
||||
}
|
||||
|
||||
@@ -284,4 +284,38 @@ namespace zenith {
|
||||
return (int)syscall3(Zenith::SYS_CHILDIO_SETTERMSZ, (uint64_t)childPid, (uint64_t)cols, (uint64_t)rows);
|
||||
}
|
||||
|
||||
// Process listing / kill
|
||||
inline int proclist(Zenith::ProcInfo* buf, int max) {
|
||||
return (int)syscall2(Zenith::SYS_PROCLIST, (uint64_t)buf, (uint64_t)max);
|
||||
}
|
||||
inline int kill(int pid) {
|
||||
return (int)syscall1(Zenith::SYS_KILL, (uint64_t)pid);
|
||||
}
|
||||
inline int devlist(Zenith::DevInfo* buf, int max) {
|
||||
return (int)syscall2(Zenith::SYS_DEVLIST, (uint64_t)buf, (uint64_t)max);
|
||||
}
|
||||
|
||||
// Window server
|
||||
inline int win_create(const char* title, int w, int h, Zenith::WinCreateResult* result) {
|
||||
return (int)syscall4(Zenith::SYS_WINCREATE, (uint64_t)title, (uint64_t)w, (uint64_t)h, (uint64_t)result);
|
||||
}
|
||||
inline int win_destroy(int id) {
|
||||
return (int)syscall1(Zenith::SYS_WINDESTROY, (uint64_t)id);
|
||||
}
|
||||
inline int win_present(int id) {
|
||||
return (int)syscall1(Zenith::SYS_WINPRESENT, (uint64_t)id);
|
||||
}
|
||||
inline int win_poll(int id, Zenith::WinEvent* event) {
|
||||
return (int)syscall2(Zenith::SYS_WINPOLL, (uint64_t)id, (uint64_t)event);
|
||||
}
|
||||
inline int win_enumerate(Zenith::WinInfo* info, int max) {
|
||||
return (int)syscall2(Zenith::SYS_WINENUM, (uint64_t)info, (uint64_t)max);
|
||||
}
|
||||
inline uint64_t win_map(int id) {
|
||||
return (uint64_t)syscall1(Zenith::SYS_WINMAP, (uint64_t)id);
|
||||
}
|
||||
inline int win_sendevent(int id, const Zenith::WinEvent* event) {
|
||||
return (int)syscall2(Zenith::SYS_WINSENDEVENT, (uint64_t)id, (uint64_t)event);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,26 +1,12 @@
|
||||
/*
|
||||
* main.cpp
|
||||
* clear - Clear terminal screen and framebuffer
|
||||
* clear - Clear terminal screen
|
||||
* Copyright (c) 2025-2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include <zenith/syscall.h>
|
||||
|
||||
extern "C" void _start() {
|
||||
// Clear the raw framebuffer (needed after graphical programs like DOOM)
|
||||
Zenith::FbInfo fb;
|
||||
zenith::fb_info(&fb);
|
||||
uint8_t* pixels = (uint8_t*)zenith::fb_map();
|
||||
if (pixels) {
|
||||
for (uint64_t y = 0; y < fb.height; y++) {
|
||||
uint32_t* row = (uint32_t*)(pixels + y * fb.pitch);
|
||||
for (uint64_t x = 0; x < fb.width; x++) {
|
||||
row[x] = 0x00000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the text console
|
||||
zenith::print("\033[2J"); // Clear entire screen
|
||||
zenith::print("\033[H"); // Move cursor to top-left
|
||||
zenith::exit(0);
|
||||
|
||||
@@ -20,7 +20,7 @@ LINK_LD := ../../link.ld
|
||||
BINDIR := ../../bin
|
||||
OBJDIR := obj
|
||||
|
||||
# ---- Compiler flags ----
|
||||
# ---- C++ compiler flags ----
|
||||
|
||||
CXXFLAGS := \
|
||||
-std=gnu++20 \
|
||||
@@ -40,10 +40,8 @@ CXXFLAGS := \
|
||||
-fdata-sections \
|
||||
-m64 \
|
||||
-march=x86-64 \
|
||||
-mno-80387 \
|
||||
-mno-mmx \
|
||||
-mno-sse \
|
||||
-mno-sse2 \
|
||||
-msse \
|
||||
-msse2 \
|
||||
-mno-red-zone \
|
||||
-mcmodel=small \
|
||||
-I $(PROG_INC) \
|
||||
@@ -61,9 +59,9 @@ LDFLAGS := \
|
||||
-z max-page-size=0x1000 \
|
||||
-T $(LINK_LD)
|
||||
|
||||
# ---- Source files ----
|
||||
# ---- C++ source files ----
|
||||
|
||||
SRCS := main.cpp font_data.cpp app_terminal.cpp app_filemanager.cpp app_sysinfo.cpp app_calculator.cpp app_texteditor.cpp app_klog.cpp app_wiki.cpp app_settings.cpp
|
||||
SRCS := main.cpp font_data.cpp stb_truetype_impl.cpp app_terminal.cpp app_filemanager.cpp app_sysinfo.cpp app_calculator.cpp app_texteditor.cpp app_klog.cpp app_wiki.cpp app_procmgr.cpp app_mandelbrot.cpp app_devexplorer.cpp app_settings.cpp app_doom.cpp
|
||||
OBJS := $(addprefix $(OBJDIR)/,$(SRCS:.cpp=.o))
|
||||
|
||||
# ---- Target ----
|
||||
@@ -78,6 +76,7 @@ $(TARGET): $(OBJS) $(LINK_LD) Makefile
|
||||
mkdir -p $(BINDIR)/os
|
||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o $@
|
||||
|
||||
# C++ sources
|
||||
$(OBJDIR)/%.o: %.cpp Makefile
|
||||
mkdir -p $(OBJDIR)
|
||||
$(CXX) $(CXXFLAGS) -c $< -o $@
|
||||
|
||||
@@ -231,10 +231,18 @@ static void calculator_on_draw(Window* win, Framebuffer& fb) {
|
||||
c.fill_rect(0, 0, c.w, CALC_DISPLAY_H, Color::from_rgb(0x2D, 0x2D, 0x2D));
|
||||
|
||||
// Display text (right-aligned, 2x scale)
|
||||
int text_len = zenith::slen(cs->display_str);
|
||||
int text_w = text_len * FONT_WIDTH * 2;
|
||||
int text_w;
|
||||
int large_h;
|
||||
if (fonts::system_font && fonts::system_font->valid) {
|
||||
text_w = fonts::system_font->measure_text(cs->display_str, fonts::LARGE_SIZE);
|
||||
large_h = fonts::system_font->get_line_height(fonts::LARGE_SIZE);
|
||||
} else {
|
||||
int text_len = zenith::slen(cs->display_str);
|
||||
text_w = text_len * FONT_WIDTH * 2;
|
||||
large_h = FONT_HEIGHT * 2;
|
||||
}
|
||||
int tx = c.w - text_w - 12;
|
||||
int ty = (CALC_DISPLAY_H - FONT_HEIGHT * 2) / 2;
|
||||
int ty = (CALC_DISPLAY_H - large_h) / 2;
|
||||
if (tx < 4) tx = 4;
|
||||
c.text_2x(tx, ty, cs->display_str, colors::WHITE);
|
||||
|
||||
@@ -271,9 +279,9 @@ static void calculator_on_draw(Window* win, Framebuffer& fb) {
|
||||
// Button text
|
||||
const char* label = calc_labels[row][col];
|
||||
Color label_color = (col == 3) ? colors::WHITE : colors::TEXT_COLOR;
|
||||
int label_w = zenith::slen(label) * FONT_WIDTH;
|
||||
int label_w = text_width(label);
|
||||
int lx = bx + (bw - label_w) / 2;
|
||||
int ly = by + (CALC_BTN_H - FONT_HEIGHT) / 2;
|
||||
int ly = by + (CALC_BTN_H - system_font_height()) / 2;
|
||||
c.text(lx, ly, label, label_color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,475 @@
|
||||
/*
|
||||
* app_devexplorer.cpp
|
||||
* ZenithOS Desktop - Device Explorer (lists hardware detected by the kernel)
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "apps_common.hpp"
|
||||
|
||||
// ============================================================================
|
||||
// Device Explorer state
|
||||
// ============================================================================
|
||||
|
||||
static constexpr int DE_TOOLBAR_H = 36;
|
||||
static constexpr int DE_CAT_H = 28;
|
||||
static constexpr int DE_ITEM_H = 24;
|
||||
static constexpr int DE_MAX_DEVS = 64;
|
||||
static constexpr int DE_POLL_MS = 2000;
|
||||
static constexpr int DE_INDENT = 28;
|
||||
|
||||
// Category names matching DevInfo.category values
|
||||
static const char* category_names[] = {
|
||||
"CPU", // 0
|
||||
"Interrupt", // 1
|
||||
"Timer", // 2
|
||||
"Input", // 3
|
||||
"USB", // 4
|
||||
"Network", // 5
|
||||
"Display", // 6
|
||||
"PCI", // 7
|
||||
};
|
||||
static constexpr int NUM_CATEGORIES = 8;
|
||||
|
||||
static Color category_colors[] = {
|
||||
Color::from_rgb(0x33, 0x66, 0xCC), // CPU - blue
|
||||
Color::from_rgb(0x88, 0x44, 0xAA), // Interrupt - purple
|
||||
Color::from_rgb(0x22, 0x88, 0x22), // Timer - green
|
||||
Color::from_rgb(0xCC, 0x88, 0x00), // Input - amber
|
||||
Color::from_rgb(0x00, 0x88, 0x88), // USB - teal
|
||||
Color::from_rgb(0xCC, 0x55, 0x22), // Network - orange
|
||||
Color::from_rgb(0x44, 0x66, 0xCC), // Display - indigo
|
||||
Color::from_rgb(0x66, 0x66, 0x66), // PCI - gray
|
||||
};
|
||||
|
||||
struct DevExplorerState {
|
||||
DesktopState* desktop;
|
||||
Zenith::DevInfo devs[DE_MAX_DEVS];
|
||||
int dev_count;
|
||||
bool collapsed[NUM_CATEGORIES]; // per-category collapse state
|
||||
int selected_row; // index into visible display rows (-1 = none)
|
||||
int scroll_y; // scroll offset in display rows
|
||||
uint64_t last_poll_ms;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Display row model
|
||||
// ============================================================================
|
||||
|
||||
enum RowType { ROW_CATEGORY, ROW_DEVICE };
|
||||
|
||||
struct DisplayRow {
|
||||
RowType type;
|
||||
int category; // category index (0..7)
|
||||
int dev_index; // index into devs[] (only valid for ROW_DEVICE)
|
||||
};
|
||||
|
||||
static constexpr int MAX_DISPLAY_ROWS = DE_MAX_DEVS + NUM_CATEGORIES;
|
||||
|
||||
static int build_display_rows(DevExplorerState* de, DisplayRow* rows) {
|
||||
int count = 0;
|
||||
for (int cat = 0; cat < NUM_CATEGORIES; cat++) {
|
||||
// Count devices in this category
|
||||
int cat_count = 0;
|
||||
for (int d = 0; d < de->dev_count; d++) {
|
||||
if (de->devs[d].category == cat) cat_count++;
|
||||
}
|
||||
if (cat_count == 0) continue;
|
||||
|
||||
// Emit category header
|
||||
rows[count].type = ROW_CATEGORY;
|
||||
rows[count].category = cat;
|
||||
rows[count].dev_index = -1;
|
||||
count++;
|
||||
|
||||
// Emit device rows if expanded
|
||||
if (!de->collapsed[cat]) {
|
||||
for (int d = 0; d < de->dev_count; d++) {
|
||||
if (de->devs[d].category == cat) {
|
||||
rows[count].type = ROW_DEVICE;
|
||||
rows[count].category = cat;
|
||||
rows[count].dev_index = d;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Triangle drawing helpers
|
||||
// ============================================================================
|
||||
|
||||
static void draw_triangle_right(Canvas& c, int x, int y, int size, Color col) {
|
||||
// Right-pointing filled triangle (▶)
|
||||
int half = size / 2;
|
||||
for (int row = 0; row < size; row++) {
|
||||
int dist = (row <= half) ? row : (size - 1 - row);
|
||||
for (int col_px = 0; col_px <= dist; col_px++) {
|
||||
c.put_pixel(x + col_px, y + row, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_triangle_down(Canvas& c, int x, int y, int size, Color col) {
|
||||
// Down-pointing filled triangle (▼)
|
||||
int half = size / 2;
|
||||
for (int row = 0; row < half + 1; row++) {
|
||||
int w = size - row * 2;
|
||||
for (int col_px = 0; col_px < w; col_px++) {
|
||||
c.put_pixel(x + row + col_px, y + row, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Callbacks
|
||||
// ============================================================================
|
||||
|
||||
static void devexplorer_on_poll(Window* win) {
|
||||
DevExplorerState* de = (DevExplorerState*)win->app_data;
|
||||
if (!de) return;
|
||||
|
||||
uint64_t now = zenith::get_milliseconds();
|
||||
if (now - de->last_poll_ms < DE_POLL_MS) return;
|
||||
de->last_poll_ms = now;
|
||||
|
||||
de->dev_count = zenith::devlist(de->devs, DE_MAX_DEVS);
|
||||
}
|
||||
|
||||
static void devexplorer_on_draw(Window* win, Framebuffer& fb) {
|
||||
DevExplorerState* de = (DevExplorerState*)win->app_data;
|
||||
if (!de) return;
|
||||
|
||||
Canvas c(win);
|
||||
c.fill(colors::WINDOW_BG);
|
||||
|
||||
int fh = system_font_height();
|
||||
|
||||
// --- Toolbar ---
|
||||
c.fill_rect(0, 0, c.w, DE_TOOLBAR_H, Color::from_rgb(0xF5, 0xF5, 0xF5));
|
||||
c.hline(0, DE_TOOLBAR_H - 1, c.w, colors::BORDER);
|
||||
|
||||
// "Refresh" button
|
||||
int btn_w = 80;
|
||||
int btn_h = 26;
|
||||
int btn_x = 8;
|
||||
int btn_y = (DE_TOOLBAR_H - btn_h) / 2;
|
||||
c.button(btn_x, btn_y, btn_w, btn_h, "Refresh",
|
||||
Color::from_rgb(0x33, 0x66, 0xCC), colors::WHITE, 4);
|
||||
|
||||
// Device count on right
|
||||
char count_str[24];
|
||||
snprintf(count_str, sizeof(count_str), "%d devices", de->dev_count);
|
||||
int cw = text_width(count_str);
|
||||
c.text(c.w - cw - 12, (DE_TOOLBAR_H - fh) / 2, count_str, colors::TEXT_COLOR);
|
||||
|
||||
// --- Build display rows ---
|
||||
DisplayRow rows[MAX_DISPLAY_ROWS];
|
||||
int row_count = build_display_rows(de, rows);
|
||||
|
||||
// --- Compute visible area ---
|
||||
int list_y = DE_TOOLBAR_H;
|
||||
int list_h = c.h - list_y;
|
||||
if (list_h < 1) return;
|
||||
|
||||
// Clamp scroll
|
||||
// Calculate total content height
|
||||
int total_h = 0;
|
||||
for (int i = 0; i < row_count; i++) {
|
||||
total_h += (rows[i].type == ROW_CATEGORY) ? DE_CAT_H : DE_ITEM_H;
|
||||
}
|
||||
int max_scroll_px = total_h - list_h;
|
||||
if (max_scroll_px < 0) max_scroll_px = 0;
|
||||
|
||||
// Convert scroll_y (in rows) to pixel offset for simplicity,
|
||||
// but keep the row-based model: scroll_y = row offset
|
||||
// We'll compute cumulative pixel offsets
|
||||
int scroll_px = 0;
|
||||
for (int i = 0; i < de->scroll_y && i < row_count; i++) {
|
||||
scroll_px += (rows[i].type == ROW_CATEGORY) ? DE_CAT_H : DE_ITEM_H;
|
||||
}
|
||||
if (scroll_px > max_scroll_px) {
|
||||
scroll_px = max_scroll_px;
|
||||
// Recalculate scroll_y to match
|
||||
de->scroll_y = 0;
|
||||
int acc = 0;
|
||||
for (int i = 0; i < row_count; i++) {
|
||||
int rh = (rows[i].type == ROW_CATEGORY) ? DE_CAT_H : DE_ITEM_H;
|
||||
if (acc + rh > max_scroll_px) break;
|
||||
acc += rh;
|
||||
de->scroll_y = i + 1;
|
||||
}
|
||||
}
|
||||
if (de->scroll_y < 0) de->scroll_y = 0;
|
||||
|
||||
// Clamp selected_row
|
||||
if (de->selected_row >= row_count) de->selected_row = row_count - 1;
|
||||
|
||||
// --- Draw rows ---
|
||||
int draw_y = list_y - scroll_px;
|
||||
// Advance to first visible area by recomputing from scroll offset
|
||||
draw_y = list_y;
|
||||
int first_visible = de->scroll_y;
|
||||
|
||||
// Compute starting y by accumulating heights of skipped rows
|
||||
// Actually, let's just iterate all rows and skip those above the viewport
|
||||
draw_y = list_y;
|
||||
for (int i = 0; i < first_visible && i < row_count; i++) {
|
||||
draw_y -= (rows[i].type == ROW_CATEGORY) ? DE_CAT_H : DE_ITEM_H;
|
||||
}
|
||||
|
||||
// Draw from first_visible onward
|
||||
int cur_y = list_y;
|
||||
for (int i = first_visible; i < row_count; i++) {
|
||||
int row_h = (rows[i].type == ROW_CATEGORY) ? DE_CAT_H : DE_ITEM_H;
|
||||
if (cur_y >= c.h) break; // Past bottom of window
|
||||
|
||||
if (rows[i].type == ROW_CATEGORY) {
|
||||
// Category header
|
||||
int cat = rows[i].category;
|
||||
c.fill_rect(0, cur_y, c.w, DE_CAT_H, Color::from_rgb(0xF0, 0xF0, 0xF0));
|
||||
|
||||
// Highlight if selected
|
||||
if (i == de->selected_row) {
|
||||
c.fill_rect(0, cur_y, c.w, DE_CAT_H, colors::MENU_HOVER);
|
||||
}
|
||||
|
||||
// Expand/collapse triangle
|
||||
int tri_x = 10;
|
||||
int tri_y = cur_y + (DE_CAT_H - 8) / 2;
|
||||
Color tri_color = Color::from_rgb(0x55, 0x55, 0x55);
|
||||
if (de->collapsed[cat]) {
|
||||
draw_triangle_right(c, tri_x, tri_y, 8, tri_color);
|
||||
} else {
|
||||
draw_triangle_down(c, tri_x, tri_y, 8, tri_color);
|
||||
}
|
||||
|
||||
// Colored dot
|
||||
int dot_x = 24;
|
||||
int dot_y = cur_y + (DE_CAT_H - 8) / 2;
|
||||
Color cat_col = (cat >= 0 && cat < NUM_CATEGORIES)
|
||||
? category_colors[cat] : colors::TEXT_COLOR;
|
||||
c.fill_rounded_rect(dot_x, dot_y, 8, 8, 4, cat_col);
|
||||
|
||||
// Category name
|
||||
const char* cat_name = (cat >= 0 && cat < NUM_CATEGORIES)
|
||||
? category_names[cat] : "?";
|
||||
int text_y = cur_y + (DE_CAT_H - fh) / 2;
|
||||
c.text(36, text_y, cat_name, Color::from_rgb(0x33, 0x33, 0x33));
|
||||
|
||||
// Device count in parentheses on right
|
||||
int cat_count = 0;
|
||||
for (int d = 0; d < de->dev_count; d++) {
|
||||
if (de->devs[d].category == cat) cat_count++;
|
||||
}
|
||||
char cnt_buf[16];
|
||||
snprintf(cnt_buf, sizeof(cnt_buf), "(%d)", cat_count);
|
||||
int name_w = text_width(cat_name);
|
||||
c.text(36 + name_w + 8, text_y, cnt_buf,
|
||||
Color::from_rgb(0x88, 0x88, 0x88));
|
||||
|
||||
// Bottom border
|
||||
c.hline(0, cur_y + DE_CAT_H - 1, c.w, Color::from_rgb(0xE0, 0xE0, 0xE0));
|
||||
} else {
|
||||
// Device item row
|
||||
int di = rows[i].dev_index;
|
||||
|
||||
// Selected row highlight
|
||||
if (i == de->selected_row) {
|
||||
c.fill_rect(0, cur_y, c.w, DE_ITEM_H, colors::MENU_HOVER);
|
||||
}
|
||||
|
||||
int text_y = cur_y + (DE_ITEM_H - fh) / 2;
|
||||
|
||||
// Device name (indented)
|
||||
c.text(DE_INDENT + 10, text_y, de->devs[di].name, colors::TEXT_COLOR);
|
||||
|
||||
// Detail string (right column, gray)
|
||||
int detail_x = c.w / 2 + 20;
|
||||
c.text(detail_x, text_y, de->devs[di].detail,
|
||||
Color::from_rgb(0x66, 0x66, 0x66));
|
||||
}
|
||||
|
||||
cur_y += row_h;
|
||||
}
|
||||
|
||||
// --- Scrollbar ---
|
||||
if (total_h > list_h) {
|
||||
int sb_x = c.w - 6;
|
||||
int thumb_h = (list_h * list_h) / total_h;
|
||||
if (thumb_h < 20) thumb_h = 20;
|
||||
int thumb_y = list_y + (scroll_px * (list_h - thumb_h)) / max_scroll_px;
|
||||
c.fill_rect(sb_x, list_y, 4, list_h, Color::from_rgb(0xE0, 0xE0, 0xE0));
|
||||
c.fill_rect(sb_x, thumb_y, 4, thumb_h, Color::from_rgb(0xAA, 0xAA, 0xAA));
|
||||
}
|
||||
}
|
||||
|
||||
static void devexplorer_on_mouse(Window* win, MouseEvent& ev) {
|
||||
DevExplorerState* de = (DevExplorerState*)win->app_data;
|
||||
if (!de) return;
|
||||
|
||||
Rect cr = win->content_rect();
|
||||
int lx = ev.x - cr.x;
|
||||
int ly = ev.y - cr.y;
|
||||
|
||||
// Scroll
|
||||
if (ev.scroll != 0) {
|
||||
de->scroll_y -= ev.scroll;
|
||||
if (de->scroll_y < 0) de->scroll_y = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev.left_pressed()) {
|
||||
// Check "Refresh" button click
|
||||
int btn_w = 80;
|
||||
int btn_h = 26;
|
||||
int btn_x = 8;
|
||||
int btn_y = (DE_TOOLBAR_H - btn_h) / 2;
|
||||
Rect btn_rect = {btn_x, btn_y, btn_w, btn_h};
|
||||
if (btn_rect.contains(lx, ly)) {
|
||||
de->last_poll_ms = 0; // force refresh
|
||||
return;
|
||||
}
|
||||
|
||||
// Check row click in list area
|
||||
int list_y = DE_TOOLBAR_H;
|
||||
if (ly >= list_y) {
|
||||
// Build display rows to map click to row
|
||||
DisplayRow rows[MAX_DISPLAY_ROWS];
|
||||
int row_count = build_display_rows(de, rows);
|
||||
|
||||
// Walk rows from scroll offset, accumulating y
|
||||
int cur_y = list_y;
|
||||
for (int i = de->scroll_y; i < row_count; i++) {
|
||||
int row_h = (rows[i].type == ROW_CATEGORY) ? DE_CAT_H : DE_ITEM_H;
|
||||
if (ly >= cur_y && ly < cur_y + row_h) {
|
||||
// Hit this row
|
||||
if (rows[i].type == ROW_CATEGORY) {
|
||||
// Toggle collapse
|
||||
int cat = rows[i].category;
|
||||
de->collapsed[cat] = !de->collapsed[cat];
|
||||
de->selected_row = -1;
|
||||
} else {
|
||||
de->selected_row = i;
|
||||
}
|
||||
return;
|
||||
}
|
||||
cur_y += row_h;
|
||||
if (cur_y >= win->content_h) break;
|
||||
}
|
||||
// Clicked below all rows
|
||||
de->selected_row = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void devexplorer_on_key(Window* win, const Zenith::KeyEvent& key) {
|
||||
DevExplorerState* de = (DevExplorerState*)win->app_data;
|
||||
if (!de || !key.pressed) return;
|
||||
|
||||
DisplayRow rows[MAX_DISPLAY_ROWS];
|
||||
int row_count = build_display_rows(de, rows);
|
||||
if (row_count == 0) return;
|
||||
|
||||
if (key.scancode == 0x48) { // Up arrow
|
||||
if (de->selected_row <= 0) {
|
||||
de->selected_row = 0;
|
||||
} else {
|
||||
de->selected_row--;
|
||||
}
|
||||
// Scroll to keep selection visible
|
||||
if (de->selected_row < de->scroll_y) {
|
||||
de->scroll_y = de->selected_row;
|
||||
}
|
||||
} else if (key.scancode == 0x50) { // Down arrow
|
||||
if (de->selected_row < row_count - 1) {
|
||||
de->selected_row++;
|
||||
}
|
||||
// Scroll to keep selection visible — estimate visible rows
|
||||
int list_h = win->content_h - DE_TOOLBAR_H;
|
||||
int cur_h = 0;
|
||||
int last_visible = de->scroll_y;
|
||||
for (int i = de->scroll_y; i < row_count; i++) {
|
||||
int rh = (rows[i].type == ROW_CATEGORY) ? DE_CAT_H : DE_ITEM_H;
|
||||
if (cur_h + rh > list_h) break;
|
||||
cur_h += rh;
|
||||
last_visible = i;
|
||||
}
|
||||
if (de->selected_row > last_visible) {
|
||||
de->scroll_y += (de->selected_row - last_visible);
|
||||
}
|
||||
} else if (key.scancode == 0x4B) { // Left arrow — collapse
|
||||
if (de->selected_row >= 0 && de->selected_row < row_count) {
|
||||
int cat = rows[de->selected_row].category;
|
||||
if (!de->collapsed[cat]) {
|
||||
de->collapsed[cat] = true;
|
||||
// If we were on a device row, move selection to the category header
|
||||
if (rows[de->selected_row].type == ROW_DEVICE) {
|
||||
// Find the category header row for this category
|
||||
for (int i = de->selected_row - 1; i >= 0; i--) {
|
||||
if (rows[i].type == ROW_CATEGORY && rows[i].category == cat) {
|
||||
de->selected_row = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// After collapsing, rebuild and clamp
|
||||
int new_count = build_display_rows(de, rows);
|
||||
if (de->selected_row >= new_count)
|
||||
de->selected_row = new_count - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (key.scancode == 0x4D) { // Right arrow — expand
|
||||
if (de->selected_row >= 0 && de->selected_row < row_count) {
|
||||
int cat = rows[de->selected_row].category;
|
||||
de->collapsed[cat] = false;
|
||||
}
|
||||
} else if (key.scancode == 0x1C) { // Enter — toggle on category row
|
||||
if (de->selected_row >= 0 && de->selected_row < row_count) {
|
||||
if (rows[de->selected_row].type == ROW_CATEGORY) {
|
||||
int cat = rows[de->selected_row].category;
|
||||
de->collapsed[cat] = !de->collapsed[cat];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void devexplorer_on_close(Window* win) {
|
||||
if (win->app_data) {
|
||||
zenith::mfree(win->app_data);
|
||||
win->app_data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Device Explorer launcher
|
||||
// ============================================================================
|
||||
|
||||
void open_devexplorer(DesktopState* ds) {
|
||||
int idx = desktop_create_window(ds, "Devices", 140, 70, 640, 460);
|
||||
if (idx < 0) return;
|
||||
|
||||
Window* win = &ds->windows[idx];
|
||||
|
||||
DevExplorerState* de = (DevExplorerState*)zenith::malloc(sizeof(DevExplorerState));
|
||||
zenith::memset(de, 0, sizeof(DevExplorerState));
|
||||
de->desktop = ds;
|
||||
de->selected_row = -1;
|
||||
de->scroll_y = 0;
|
||||
de->last_poll_ms = 0;
|
||||
|
||||
// All categories start expanded
|
||||
for (int i = 0; i < NUM_CATEGORIES; i++)
|
||||
de->collapsed[i] = false;
|
||||
|
||||
// Initial poll
|
||||
de->dev_count = zenith::devlist(de->devs, DE_MAX_DEVS);
|
||||
|
||||
win->app_data = de;
|
||||
win->on_draw = devexplorer_on_draw;
|
||||
win->on_mouse = devexplorer_on_mouse;
|
||||
win->on_key = devexplorer_on_key;
|
||||
win->on_close = devexplorer_on_close;
|
||||
win->on_poll = devexplorer_on_poll;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* app_doom.cpp
|
||||
* ZenithOS Desktop - DOOM launcher (spawns standalone doom.elf)
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "apps_common.hpp"
|
||||
|
||||
void open_doom(DesktopState* ds) {
|
||||
(void)ds;
|
||||
zenith::spawn("0:/games/doom.elf");
|
||||
}
|
||||
@@ -373,7 +373,7 @@ static void filemanager_on_draw(Window* win, Framebuffer& fb) {
|
||||
int tx = cell_x + (FM_GRID_CELL_W - tw) / 2;
|
||||
if (tx < cell_x) tx = cell_x;
|
||||
int ty = icon_y + FM_GRID_ICON + 2;
|
||||
if (ty >= list_y && ty + FONT_HEIGHT <= c.h)
|
||||
if (ty >= list_y && ty + system_font_height() <= c.h)
|
||||
c.text(tx, ty, label, colors::TEXT_COLOR);
|
||||
}
|
||||
} else {
|
||||
@@ -447,19 +447,20 @@ static void filemanager_on_draw(Window* win, Framebuffer& fb) {
|
||||
|
||||
// Name
|
||||
int tx = 30;
|
||||
int ty = iy + (FM_ITEM_H - FONT_HEIGHT) / 2;
|
||||
if (ty >= list_y && ty + FONT_HEIGHT <= c.h)
|
||||
int fm_sfh = system_font_height();
|
||||
int ty = iy + (FM_ITEM_H - fm_sfh) / 2;
|
||||
if (ty >= list_y && ty + fm_sfh <= c.h)
|
||||
c.text(tx, ty, fm->entry_names[i], colors::TEXT_COLOR);
|
||||
|
||||
// Size
|
||||
if (size_col_x > 100 && !fm->is_dir[i] && ty >= list_y && ty + FONT_HEIGHT <= c.h) {
|
||||
if (size_col_x > 100 && !fm->is_dir[i] && ty >= list_y && ty + fm_sfh <= c.h) {
|
||||
char size_str[16];
|
||||
format_size(size_str, fm->entry_sizes[i]);
|
||||
c.text(size_col_x, ty, size_str, dim);
|
||||
}
|
||||
|
||||
// Type
|
||||
if (type_col_x > 160 && ty >= list_y && ty + FONT_HEIGHT <= c.h) {
|
||||
if (type_col_x > 160 && ty >= list_y && ty + fm_sfh <= c.h) {
|
||||
const char* type_str = "File";
|
||||
if (fm->entry_types[i] == 1) type_str = "Dir";
|
||||
else if (fm->entry_types[i] == 2) type_str = "Exec";
|
||||
|
||||
@@ -119,8 +119,8 @@ void open_klog(DesktopState* ds) {
|
||||
|
||||
Window* win = &ds->windows[idx];
|
||||
Rect cr = win->content_rect();
|
||||
int cols = cr.w / FONT_WIDTH;
|
||||
int rows = cr.h / FONT_HEIGHT;
|
||||
int cols = cr.w / mono_cell_width();
|
||||
int rows = cr.h / mono_cell_height();
|
||||
|
||||
KlogState* klog = (KlogState*)zenith::malloc(sizeof(KlogState));
|
||||
zenith::memset(klog, 0, sizeof(KlogState));
|
||||
|
||||
@@ -0,0 +1,296 @@
|
||||
/*
|
||||
* app_mandelbrot.cpp
|
||||
* ZenithOS Desktop - Mandelbrot set visualizer
|
||||
* Supports zoom (scroll wheel), pan (drag), and reset (R key)
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "apps_common.hpp"
|
||||
|
||||
// ============================================================================
|
||||
// Fixed-point Mandelbrot (avoids FPU/SSE dependency issues)
|
||||
// Uses 28.36 fixed-point: 36 fractional bits gives ~1e-10 precision
|
||||
// ============================================================================
|
||||
|
||||
using fp_t = int64_t;
|
||||
static constexpr int FP_SHIFT = 36;
|
||||
static constexpr fp_t FP_ONE = (fp_t)1 << FP_SHIFT;
|
||||
|
||||
static inline fp_t fp_from_int(int v) { return (fp_t)v << FP_SHIFT; }
|
||||
|
||||
static inline fp_t fp_mul(fp_t a, fp_t b) {
|
||||
// Split to avoid overflow: a * b >> SHIFT
|
||||
// Use 128-bit intermediate via compiler builtin
|
||||
__int128 r = (__int128)a * b;
|
||||
return (fp_t)(r >> FP_SHIFT);
|
||||
}
|
||||
|
||||
// fp_div for small numerators (avoids __divti3 by using 64-bit division)
|
||||
// Only valid when a is small enough that a << FP_SHIFT fits in int64_t
|
||||
static inline fp_t fp_div_small(int a, int b) {
|
||||
return ((fp_t)a << FP_SHIFT) / (fp_t)b;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// State
|
||||
// ============================================================================
|
||||
|
||||
static constexpr int MB_MAX_ITER = 256;
|
||||
static constexpr int MB_TOOLBAR_H = 32;
|
||||
|
||||
struct MandelbrotState {
|
||||
DesktopState* desktop;
|
||||
fp_t center_x, center_y; // center of view in fractal coords
|
||||
fp_t scale; // units per pixel (fixed-point)
|
||||
int max_iter;
|
||||
bool needs_render;
|
||||
// Drag state
|
||||
bool dragging;
|
||||
int drag_start_x, drag_start_y;
|
||||
fp_t drag_start_cx, drag_start_cy;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Color palette
|
||||
// ============================================================================
|
||||
|
||||
static uint32_t mandelbrot_color(int iter, int max_iter) {
|
||||
if (iter >= max_iter) return 0xFF000000; // black for inside set
|
||||
|
||||
// Smooth cycling palette using bit manipulation
|
||||
int t = (iter * 7) & 0xFF;
|
||||
int phase = (iter * 7) >> 8;
|
||||
|
||||
uint8_t r, g, b;
|
||||
switch (phase % 6) {
|
||||
case 0: r = 255; g = (uint8_t)t; b = 0; break;
|
||||
case 1: r = (uint8_t)(255 - t); g = 255; b = 0; break;
|
||||
case 2: r = 0; g = 255; b = (uint8_t)t; break;
|
||||
case 3: r = 0; g = (uint8_t)(255 - t); b = 255; break;
|
||||
case 4: r = (uint8_t)t; g = 0; b = 255; break;
|
||||
default: r = 255; g = 0; b = (uint8_t)(255 - t); break;
|
||||
}
|
||||
|
||||
return 0xFF000000 | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Render
|
||||
// ============================================================================
|
||||
|
||||
static void mandelbrot_render(MandelbrotState* mb, uint32_t* pixels, int w, int h) {
|
||||
int render_h = h - MB_TOOLBAR_H;
|
||||
if (render_h <= 0) return;
|
||||
|
||||
fp_t half_w = fp_mul(fp_from_int(w / 2), mb->scale);
|
||||
fp_t half_h = fp_mul(fp_from_int(render_h / 2), mb->scale);
|
||||
fp_t x_min = mb->center_x - half_w;
|
||||
fp_t y_min = mb->center_y - half_h;
|
||||
fp_t bailout = fp_from_int(4);
|
||||
|
||||
for (int py = 0; py < render_h; py++) {
|
||||
fp_t ci = y_min + fp_mul(fp_from_int(py), mb->scale);
|
||||
uint32_t* row = pixels + (py + MB_TOOLBAR_H) * w;
|
||||
|
||||
for (int px = 0; px < w; px++) {
|
||||
fp_t cr = x_min + fp_mul(fp_from_int(px), mb->scale);
|
||||
|
||||
fp_t zr = 0, zi = 0;
|
||||
int iter = 0;
|
||||
|
||||
while (iter < mb->max_iter) {
|
||||
fp_t zr2 = fp_mul(zr, zr);
|
||||
fp_t zi2 = fp_mul(zi, zi);
|
||||
if (zr2 + zi2 > bailout) break;
|
||||
fp_t new_zr = zr2 - zi2 + cr;
|
||||
zi = fp_mul(fp_from_int(2), fp_mul(zr, zi)) + ci;
|
||||
zr = new_zr;
|
||||
iter++;
|
||||
}
|
||||
|
||||
row[px] = mandelbrot_color(iter, mb->max_iter);
|
||||
}
|
||||
}
|
||||
|
||||
mb->needs_render = false;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Callbacks
|
||||
// ============================================================================
|
||||
|
||||
static void mb_on_draw(Window* win, Framebuffer& fb) {
|
||||
MandelbrotState* mb = (MandelbrotState*)win->app_data;
|
||||
if (!mb) return;
|
||||
|
||||
Canvas c(win);
|
||||
|
||||
if (mb->needs_render) {
|
||||
mandelbrot_render(mb, win->content, c.w, c.h);
|
||||
}
|
||||
|
||||
// Draw toolbar over the render
|
||||
c.fill_rect(0, 0, c.w, MB_TOOLBAR_H, Color::from_rgb(0xF5, 0xF5, 0xF5));
|
||||
c.hline(0, MB_TOOLBAR_H - 1, c.w, colors::BORDER);
|
||||
|
||||
// Reset button
|
||||
c.button(8, 4, 60, 24, "Reset", colors::ACCENT, colors::WHITE, 4);
|
||||
|
||||
// Iter +/- buttons
|
||||
char iter_str[24];
|
||||
snprintf(iter_str, sizeof(iter_str), "Iter: %d", mb->max_iter);
|
||||
int fh = system_font_height();
|
||||
c.text(80, (MB_TOOLBAR_H - fh) / 2, iter_str, colors::TEXT_COLOR);
|
||||
|
||||
int iter_text_w = text_width(iter_str);
|
||||
int btn_x = 80 + iter_text_w + 8;
|
||||
c.button(btn_x, 4, 24, 24, "-", Color::from_rgb(0xAA, 0xAA, 0xAA), colors::WHITE, 4);
|
||||
c.button(btn_x + 28, 4, 24, 24, "+", Color::from_rgb(0xAA, 0xAA, 0xAA), colors::WHITE, 4);
|
||||
|
||||
// Hint
|
||||
const char* hint = "Scroll=zoom Drag=pan";
|
||||
int hw = text_width(hint);
|
||||
c.text(c.w - hw - 8, (MB_TOOLBAR_H - fh) / 2, hint, Color::from_rgb(0x99, 0x99, 0x99));
|
||||
}
|
||||
|
||||
static void mb_on_mouse(Window* win, MouseEvent& ev) {
|
||||
MandelbrotState* mb = (MandelbrotState*)win->app_data;
|
||||
if (!mb) return;
|
||||
|
||||
Rect cr = win->content_rect();
|
||||
int lx = ev.x - cr.x;
|
||||
int ly = ev.y - cr.y;
|
||||
|
||||
// Handle toolbar clicks
|
||||
if (ev.left_pressed() && ly < MB_TOOLBAR_H) {
|
||||
// Reset button
|
||||
Rect reset_r = {8, 4, 60, 24};
|
||||
if (reset_r.contains(lx, ly)) {
|
||||
mb->center_x = -fp_from_int(1) / 2; // -0.5
|
||||
mb->center_y = 0;
|
||||
mb->scale = fp_div_small(3, cr.w > 0 ? cr.w : 400);
|
||||
mb->max_iter = MB_MAX_ITER;
|
||||
mb->needs_render = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Iter buttons
|
||||
char iter_str[24];
|
||||
snprintf(iter_str, sizeof(iter_str), "Iter: %d", mb->max_iter);
|
||||
int iter_text_w = text_width(iter_str);
|
||||
int btn_x = 80 + iter_text_w + 8;
|
||||
|
||||
Rect minus_r = {btn_x, 4, 24, 24};
|
||||
Rect plus_r = {btn_x + 28, 4, 24, 24};
|
||||
|
||||
if (minus_r.contains(lx, ly)) {
|
||||
if (mb->max_iter > 32) { mb->max_iter /= 2; mb->needs_render = true; }
|
||||
return;
|
||||
}
|
||||
if (plus_r.contains(lx, ly)) {
|
||||
if (mb->max_iter < 4096) { mb->max_iter *= 2; mb->needs_render = true; }
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Drag panning
|
||||
if (ev.left_pressed() && ly >= MB_TOOLBAR_H) {
|
||||
mb->dragging = true;
|
||||
mb->drag_start_x = lx;
|
||||
mb->drag_start_y = ly;
|
||||
mb->drag_start_cx = mb->center_x;
|
||||
mb->drag_start_cy = mb->center_y;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mb->dragging && ev.left_held()) {
|
||||
int dx = lx - mb->drag_start_x;
|
||||
int dy = ly - mb->drag_start_y;
|
||||
mb->center_x = mb->drag_start_cx - fp_mul(fp_from_int(dx), mb->scale);
|
||||
mb->center_y = mb->drag_start_cy - fp_mul(fp_from_int(dy), mb->scale);
|
||||
mb->needs_render = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mb->dragging && !ev.left_held()) {
|
||||
mb->dragging = false;
|
||||
}
|
||||
|
||||
// Scroll zoom (centered on mouse position)
|
||||
if (ev.scroll != 0 && ly >= MB_TOOLBAR_H) {
|
||||
int render_h = cr.h - MB_TOOLBAR_H;
|
||||
// Mouse position in fractal coords before zoom
|
||||
fp_t mx_frac = mb->center_x + fp_mul(fp_from_int(lx - cr.w / 2), mb->scale);
|
||||
fp_t my_frac = mb->center_y + fp_mul(fp_from_int((ly - MB_TOOLBAR_H) - render_h / 2), mb->scale);
|
||||
|
||||
if (ev.scroll < 0) {
|
||||
// Zoom in
|
||||
mb->scale = fp_mul(mb->scale, fp_from_int(3) / 4); // * 0.75
|
||||
} else {
|
||||
// Zoom out
|
||||
mb->scale = fp_mul(mb->scale, fp_from_int(4) / 3); // * 1.33
|
||||
}
|
||||
|
||||
// Adjust center so mouse stays at same fractal point
|
||||
fp_t new_mx = mb->center_x + fp_mul(fp_from_int(lx - cr.w / 2), mb->scale);
|
||||
fp_t new_my = mb->center_y + fp_mul(fp_from_int((ly - MB_TOOLBAR_H) - render_h / 2), mb->scale);
|
||||
mb->center_x += mx_frac - new_mx;
|
||||
mb->center_y += my_frac - new_my;
|
||||
|
||||
mb->needs_render = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void mb_on_key(Window* win, const Zenith::KeyEvent& key) {
|
||||
MandelbrotState* mb = (MandelbrotState*)win->app_data;
|
||||
if (!mb || !key.pressed) return;
|
||||
|
||||
if (key.ascii == 'r' || key.ascii == 'R') {
|
||||
Rect cr = win->content_rect();
|
||||
mb->center_x = -fp_from_int(1) / 2;
|
||||
mb->center_y = 0;
|
||||
mb->scale = fp_div_small(3, cr.w > 0 ? cr.w : 400);
|
||||
mb->max_iter = MB_MAX_ITER;
|
||||
mb->needs_render = true;
|
||||
} else if (key.ascii == '+' || key.ascii == '=') {
|
||||
if (mb->max_iter < 4096) { mb->max_iter *= 2; mb->needs_render = true; }
|
||||
} else if (key.ascii == '-') {
|
||||
if (mb->max_iter > 32) { mb->max_iter /= 2; mb->needs_render = true; }
|
||||
}
|
||||
}
|
||||
|
||||
static void mb_on_close(Window* win) {
|
||||
if (win->app_data) {
|
||||
zenith::mfree(win->app_data);
|
||||
win->app_data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Mandelbrot launcher
|
||||
// ============================================================================
|
||||
|
||||
void open_mandelbrot(DesktopState* ds) {
|
||||
int idx = desktop_create_window(ds, "Mandelbrot", 120, 60, 500, 400);
|
||||
if (idx < 0) return;
|
||||
|
||||
Window* win = &ds->windows[idx];
|
||||
Rect cr = win->content_rect();
|
||||
|
||||
MandelbrotState* mb = (MandelbrotState*)zenith::malloc(sizeof(MandelbrotState));
|
||||
zenith::memset(mb, 0, sizeof(MandelbrotState));
|
||||
mb->desktop = ds;
|
||||
mb->center_x = -fp_from_int(1) / 2; // -0.5
|
||||
mb->center_y = 0;
|
||||
mb->scale = fp_div_small(3, cr.w > 0 ? cr.w : 400);
|
||||
mb->max_iter = MB_MAX_ITER;
|
||||
mb->needs_render = true;
|
||||
mb->dragging = false;
|
||||
|
||||
win->app_data = mb;
|
||||
win->on_draw = mb_on_draw;
|
||||
win->on_mouse = mb_on_mouse;
|
||||
win->on_key = mb_on_key;
|
||||
win->on_close = mb_on_close;
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* app_procmgr.cpp
|
||||
* ZenithOS Desktop - Process Manager
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "apps_common.hpp"
|
||||
|
||||
// ============================================================================
|
||||
// Process Manager state
|
||||
// ============================================================================
|
||||
|
||||
static constexpr int PM_TOOLBAR_H = 36;
|
||||
static constexpr int PM_HEADER_H = 24;
|
||||
static constexpr int PM_ITEM_H = 28;
|
||||
static constexpr int PM_MAX_PROCS = 16;
|
||||
static constexpr int PM_POLL_MS = 1000;
|
||||
|
||||
struct ProcMgrState {
|
||||
DesktopState* desktop;
|
||||
Zenith::ProcInfo procs[PM_MAX_PROCS];
|
||||
int proc_count;
|
||||
int selected; // selected row index (-1 = none)
|
||||
uint64_t last_poll_ms;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Callbacks
|
||||
// ============================================================================
|
||||
|
||||
static void procmgr_on_poll(Window* win) {
|
||||
ProcMgrState* pm = (ProcMgrState*)win->app_data;
|
||||
if (!pm) return;
|
||||
|
||||
uint64_t now = zenith::get_milliseconds();
|
||||
if (now - pm->last_poll_ms < PM_POLL_MS) return;
|
||||
pm->last_poll_ms = now;
|
||||
|
||||
// Remember previously selected PID
|
||||
int prev_pid = -1;
|
||||
if (pm->selected >= 0 && pm->selected < pm->proc_count) {
|
||||
prev_pid = pm->procs[pm->selected].pid;
|
||||
}
|
||||
|
||||
pm->proc_count = zenith::proclist(pm->procs, PM_MAX_PROCS);
|
||||
|
||||
// Restore selection by matching PID
|
||||
pm->selected = -1;
|
||||
if (prev_pid >= 0) {
|
||||
for (int i = 0; i < pm->proc_count; i++) {
|
||||
if (pm->procs[i].pid == prev_pid) {
|
||||
pm->selected = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void procmgr_on_draw(Window* win, Framebuffer& fb) {
|
||||
ProcMgrState* pm = (ProcMgrState*)win->app_data;
|
||||
if (!pm) return;
|
||||
|
||||
Canvas c(win);
|
||||
c.fill(colors::WINDOW_BG);
|
||||
|
||||
int fh = system_font_height();
|
||||
|
||||
// --- Toolbar ---
|
||||
c.fill_rect(0, 0, c.w, PM_TOOLBAR_H, Color::from_rgb(0xF5, 0xF5, 0xF5));
|
||||
c.hline(0, PM_TOOLBAR_H - 1, c.w, colors::BORDER);
|
||||
|
||||
// "End Process" button
|
||||
int btn_w = 100;
|
||||
int btn_h = 26;
|
||||
int btn_x = 8;
|
||||
int btn_y = (PM_TOOLBAR_H - btn_h) / 2;
|
||||
Color btn_bg = (pm->selected >= 0 && pm->procs[pm->selected].pid != 0)
|
||||
? Color::from_rgb(0xCC, 0x33, 0x33)
|
||||
: Color::from_rgb(0xAA, 0xAA, 0xAA);
|
||||
c.button(btn_x, btn_y, btn_w, btn_h, "End Process", btn_bg, colors::WHITE, 4);
|
||||
|
||||
// Process count on right
|
||||
char count_str[24];
|
||||
snprintf(count_str, sizeof(count_str), "%d processes", pm->proc_count);
|
||||
int cw = text_width(count_str);
|
||||
c.text(c.w - cw - 12, (PM_TOOLBAR_H - fh) / 2, count_str, colors::TEXT_COLOR);
|
||||
|
||||
// --- Header row ---
|
||||
int header_y = PM_TOOLBAR_H;
|
||||
c.fill_rect(0, header_y, c.w, PM_HEADER_H, Color::from_rgb(0xF0, 0xF0, 0xF0));
|
||||
|
||||
int col_pid = 12;
|
||||
int col_name = 64;
|
||||
int col_mem = c.w - 120;
|
||||
int col_state = c.w - 52;
|
||||
|
||||
int ty = header_y + (PM_HEADER_H - fh) / 2;
|
||||
c.text(col_pid, ty, "PID", Color::from_rgb(0x66, 0x66, 0x66));
|
||||
c.text(col_name, ty, "Name", Color::from_rgb(0x66, 0x66, 0x66));
|
||||
c.text(col_mem, ty, "Mem", Color::from_rgb(0x66, 0x66, 0x66));
|
||||
c.text(col_state, ty, "St", Color::from_rgb(0x66, 0x66, 0x66));
|
||||
|
||||
c.hline(0, header_y + PM_HEADER_H - 1, c.w, colors::BORDER);
|
||||
|
||||
// --- Process rows ---
|
||||
int list_y = PM_TOOLBAR_H + PM_HEADER_H;
|
||||
for (int i = 0; i < pm->proc_count; i++) {
|
||||
int row_y = list_y + i * PM_ITEM_H;
|
||||
if (row_y + PM_ITEM_H > c.h) break;
|
||||
|
||||
// Selected row highlight
|
||||
if (i == pm->selected) {
|
||||
c.fill_rect(0, row_y, c.w, PM_ITEM_H, colors::MENU_HOVER);
|
||||
}
|
||||
|
||||
int ry = row_y + (PM_ITEM_H - fh) / 2;
|
||||
|
||||
// PID (right-aligned in PID column)
|
||||
char pid_str[8];
|
||||
snprintf(pid_str, sizeof(pid_str), "%d", (int)pm->procs[i].pid);
|
||||
int pid_w = text_width(pid_str);
|
||||
c.text(col_pid + 32 - pid_w, ry, pid_str, colors::TEXT_COLOR);
|
||||
|
||||
// Name (truncated to fit)
|
||||
c.text(col_name, ry, pm->procs[i].name, colors::TEXT_COLOR);
|
||||
|
||||
// Memory
|
||||
char mem_str[16];
|
||||
format_size(mem_str, (int)pm->procs[i].heapUsed);
|
||||
c.text(col_mem, ry, mem_str, colors::TEXT_COLOR);
|
||||
|
||||
// State
|
||||
const char* st_str;
|
||||
Color st_color;
|
||||
switch (pm->procs[i].state) {
|
||||
case 2: // Running
|
||||
st_str = "Run";
|
||||
st_color = Color::from_rgb(0x22, 0x88, 0x22);
|
||||
break;
|
||||
case 1: // Ready
|
||||
st_str = "Rdy";
|
||||
st_color = Color::from_rgb(0x33, 0x66, 0xCC);
|
||||
break;
|
||||
case 3: // Terminated
|
||||
st_str = "Term";
|
||||
st_color = Color::from_rgb(0xCC, 0x33, 0x33);
|
||||
break;
|
||||
default:
|
||||
st_str = "?";
|
||||
st_color = colors::TEXT_COLOR;
|
||||
break;
|
||||
}
|
||||
c.text(col_state, ry, st_str, st_color);
|
||||
}
|
||||
}
|
||||
|
||||
static void procmgr_on_mouse(Window* win, MouseEvent& ev) {
|
||||
ProcMgrState* pm = (ProcMgrState*)win->app_data;
|
||||
if (!pm) return;
|
||||
|
||||
Rect cr = win->content_rect();
|
||||
int lx = ev.x - cr.x;
|
||||
int ly = ev.y - cr.y;
|
||||
|
||||
if (ev.left_pressed()) {
|
||||
// Check "End Process" button click
|
||||
int btn_w = 100;
|
||||
int btn_h = 26;
|
||||
int btn_x = 8;
|
||||
int btn_y = (PM_TOOLBAR_H - btn_h) / 2;
|
||||
Rect btn_rect = {btn_x, btn_y, btn_w, btn_h};
|
||||
if (btn_rect.contains(lx, ly)) {
|
||||
if (pm->selected >= 0 && pm->selected < pm->proc_count
|
||||
&& pm->procs[pm->selected].pid != 0) {
|
||||
zenith::kill(pm->procs[pm->selected].pid);
|
||||
pm->last_poll_ms = 0; // force refresh
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check row click
|
||||
int list_y = PM_TOOLBAR_H + PM_HEADER_H;
|
||||
if (ly >= list_y) {
|
||||
int row = (ly - list_y) / PM_ITEM_H;
|
||||
if (row >= 0 && row < pm->proc_count) {
|
||||
pm->selected = row;
|
||||
} else {
|
||||
pm->selected = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void procmgr_on_key(Window* win, const Zenith::KeyEvent& key) {
|
||||
ProcMgrState* pm = (ProcMgrState*)win->app_data;
|
||||
if (!pm || !key.pressed) return;
|
||||
|
||||
if (key.scancode == 0x48) { // Up arrow
|
||||
if (pm->selected > 0) pm->selected--;
|
||||
else if (pm->proc_count > 0) pm->selected = 0;
|
||||
} else if (key.scancode == 0x50) { // Down arrow
|
||||
if (pm->selected < pm->proc_count - 1) pm->selected++;
|
||||
} else if (key.scancode == 0x53) { // Delete key
|
||||
if (pm->selected >= 0 && pm->selected < pm->proc_count
|
||||
&& pm->procs[pm->selected].pid != 0) {
|
||||
zenith::kill(pm->procs[pm->selected].pid);
|
||||
pm->last_poll_ms = 0; // force refresh
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void procmgr_on_close(Window* win) {
|
||||
if (win->app_data) {
|
||||
zenith::mfree(win->app_data);
|
||||
win->app_data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Process Manager launcher
|
||||
// ============================================================================
|
||||
|
||||
void open_procmgr(DesktopState* ds) {
|
||||
int idx = desktop_create_window(ds, "Processes", 180, 80, 520, 400);
|
||||
if (idx < 0) return;
|
||||
|
||||
Window* win = &ds->windows[idx];
|
||||
|
||||
ProcMgrState* pm = (ProcMgrState*)zenith::malloc(sizeof(ProcMgrState));
|
||||
zenith::memset(pm, 0, sizeof(ProcMgrState));
|
||||
pm->desktop = ds;
|
||||
pm->selected = -1;
|
||||
pm->last_poll_ms = 0;
|
||||
|
||||
// Initial poll
|
||||
pm->proc_count = zenith::proclist(pm->procs, PM_MAX_PROCS);
|
||||
|
||||
win->app_data = pm;
|
||||
win->on_draw = procmgr_on_draw;
|
||||
win->on_mouse = procmgr_on_mouse;
|
||||
win->on_key = procmgr_on_key;
|
||||
win->on_close = procmgr_on_close;
|
||||
win->on_poll = procmgr_on_poll;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user