feat: add support for shutdown/reboot via UEFI Runtime Services
This commit is contained in:
+20
-10
@@ -6,23 +6,33 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <Efi/UEFI.hpp>
|
||||||
|
#include <Memory/Paging.hpp>
|
||||||
|
|
||||||
namespace Zenith {
|
namespace Zenith {
|
||||||
|
|
||||||
static void Sys_Reset() {
|
static void Sys_Reset() {
|
||||||
/*
|
if (Efi::g_ResetSystem) {
|
||||||
Triple fault for now; TODO: implement UEFI runtime function for clean reboot.
|
/* Switch to kernel PML4 which has identity-mapped UEFI runtime regions */
|
||||||
|
Memory::VMM::LoadCR3(Memory::VMM::g_paging->PML4);
|
||||||
|
Efi::g_ResetSystem(Efi::EfiResetCold, 0, 0, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
We implement the triple fault by loading a null IDT into the IDT register,
|
/* Fallback: triple fault via null IDT */
|
||||||
and then immediately triggering an interrupt.
|
|
||||||
|
|
||||||
This technique should pretty much work across the board but it's of course
|
|
||||||
better to use the UEFI runtime API as it has a method for this purpose,
|
|
||||||
along with shutdown.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct [[gnu::packed]] { uint16_t limit; uint64_t base; } nullIdt = {0, 0};
|
struct [[gnu::packed]] { uint16_t limit; uint64_t base; } nullIdt = {0, 0};
|
||||||
asm volatile("lidt %0; int $0x03" :: "m"(nullIdt));
|
asm volatile("lidt %0; int $0x03" :: "m"(nullIdt));
|
||||||
__builtin_unreachable();
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void Sys_Shutdown() {
|
||||||
|
if (Efi::g_ResetSystem) {
|
||||||
|
/* Switch to kernel PML4 which has identity-mapped UEFI runtime regions */
|
||||||
|
Memory::VMM::LoadCR3(Memory::VMM::g_paging->PML4);
|
||||||
|
Efi::g_ResetSystem(Efi::EfiResetShutdown, 0, 0, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No fallback for shutdown; halt the CPU */
|
||||||
|
asm volatile("cli; hlt");
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -109,8 +109,8 @@ namespace Zenith {
|
|||||||
Sys_Reset();
|
Sys_Reset();
|
||||||
return 0;
|
return 0;
|
||||||
case SYS_SHUTDOWN:
|
case SYS_SHUTDOWN:
|
||||||
/* Unimplemented */
|
Sys_Shutdown();
|
||||||
return -1;
|
return 0;
|
||||||
case SYS_GETTIME:
|
case SYS_GETTIME:
|
||||||
Sys_GetTime((DateTime*)frame->arg1);
|
Sys_GetTime((DateTime*)frame->arg1);
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+13
-1
@@ -5,7 +5,9 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <limine.h>
|
||||||
#include <Memory/HHDM.hpp>
|
#include <Memory/HHDM.hpp>
|
||||||
|
#include <Memory/Paging.hpp>
|
||||||
#include <Timekeeping/Time.hpp>
|
#include <Timekeeping/Time.hpp>
|
||||||
|
|
||||||
namespace Efi {
|
namespace Efi {
|
||||||
@@ -269,7 +271,9 @@ namespace Efi {
|
|||||||
void *ConfigurationTable;
|
void *ConfigurationTable;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void Init(SystemTable* ST) {
|
inline EFI_RESET_SYSTEM g_ResetSystem = nullptr;
|
||||||
|
|
||||||
|
inline void Init(SystemTable* ST, limine_efi_memmap_response* efiMemmap) {
|
||||||
Kt::KernelLogStream(Kt::OK, "UEFI") << "ST Minor Revision: " << ST->Header.Revision.MinorRevision;
|
Kt::KernelLogStream(Kt::OK, "UEFI") << "ST Minor Revision: " << ST->Header.Revision.MinorRevision;
|
||||||
Kt::KernelLogStream(Kt::OK, "UEFI") << "ST Major Revision: " << ST->Header.Revision.MajorRevision;
|
Kt::KernelLogStream(Kt::OK, "UEFI") << "ST Major Revision: " << ST->Header.Revision.MajorRevision;
|
||||||
|
|
||||||
@@ -278,6 +282,12 @@ namespace Efi {
|
|||||||
if (ST->RuntimeServices != nullptr) {
|
if (ST->RuntimeServices != nullptr) {
|
||||||
Kt::KernelLogStream(Kt::OK, "UEFI") << "EFI Runtime Service API is available.";
|
Kt::KernelLogStream(Kt::OK, "UEFI") << "EFI Runtime Service API is available.";
|
||||||
|
|
||||||
|
/* Identity-map EFI runtime service regions so firmware code
|
||||||
|
can reference its own data at physical addresses */
|
||||||
|
if (Memory::VMM::g_paging) {
|
||||||
|
Memory::VMM::g_paging->MapEfiRuntime(efiMemmap);
|
||||||
|
}
|
||||||
|
|
||||||
EFI_TIME Time;
|
EFI_TIME Time;
|
||||||
EFI_TIME_CAPABILITIES TimeCapabilities;
|
EFI_TIME_CAPABILITIES TimeCapabilities;
|
||||||
|
|
||||||
@@ -285,6 +295,8 @@ namespace Efi {
|
|||||||
_GetTime(&Time, &TimeCapabilities);
|
_GetTime(&Time, &TimeCapabilities);
|
||||||
|
|
||||||
Timekeeping::Init(Time.Year, Time.Month, Time.Day, Time.Hour, Time.Minute, Time.Second);
|
Timekeeping::Init(Time.Year, Time.Month, Time.Day, Time.Hour, Time.Minute, Time.Second);
|
||||||
|
|
||||||
|
g_ResetSystem = (EFI_RESET_SYSTEM)Memory::HHDM((void*)RT->ResetSystem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
+2
-2
@@ -166,7 +166,7 @@ extern "C" void kmain() {
|
|||||||
g_paging.MapWC(phys, Memory::HHDM(phys));
|
g_paging.MapWC(phys, Memory::HHDM(phys));
|
||||||
}
|
}
|
||||||
|
|
||||||
asm volatile("mov %%cr3, %%rax; mov %%rax, %%cr3" ::: "rax", "memory");
|
Memory::VMM::FlushTLB();
|
||||||
|
|
||||||
Kt::KernelLogStream(OK, "Graphics") << "Framebuffer mapped as Write-Combining ("
|
Kt::KernelLogStream(OK, "Graphics") << "Framebuffer mapped as Write-Combining ("
|
||||||
<< kcp::dec << numPages << " pages)";
|
<< kcp::dec << numPages << " pages)";
|
||||||
@@ -211,7 +211,7 @@ extern "C" void kmain() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
Efi::SystemTable* ST = (Efi::SystemTable*)Memory::HHDM(system_table_request.response->address);
|
Efi::SystemTable* ST = (Efi::SystemTable*)Memory::HHDM(system_table_request.response->address);
|
||||||
Efi::Init(ST);
|
Efi::Init(ST, efi_memmap_request.response);
|
||||||
|
|
||||||
// Initialize ramdisk from Limine modules
|
// Initialize ramdisk from Limine modules
|
||||||
if (module_request.response != nullptr && module_request.response->module_count > 0) {
|
if (module_request.response != nullptr && module_request.response->module_count > 0) {
|
||||||
|
|||||||
@@ -267,4 +267,34 @@ namespace Memory::VMM {
|
|||||||
std::uint64_t Paging::GetPhysAddr(std::uint64_t virtualAddress) {
|
std::uint64_t Paging::GetPhysAddr(std::uint64_t virtualAddress) {
|
||||||
return GetPhysAddr((std::uint64_t)PML4, virtualAddress, false);
|
return GetPhysAddr((std::uint64_t)PML4, virtualAddress, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Paging::MapEfiRuntime(limine_efi_memmap_response* efiMemmap) {
|
||||||
|
if (!efiMemmap) return;
|
||||||
|
|
||||||
|
auto* base = (uint8_t*)efiMemmap->memmap;
|
||||||
|
uint64_t descSize = efiMemmap->desc_size;
|
||||||
|
uint64_t count = efiMemmap->memmap_size / descSize;
|
||||||
|
|
||||||
|
struct EfiMemDesc {
|
||||||
|
uint32_t Type;
|
||||||
|
uint64_t PhysicalStart;
|
||||||
|
uint64_t VirtualStart;
|
||||||
|
uint64_t NumberOfPages;
|
||||||
|
uint64_t Attribute;
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr uint64_t EFI_MEMORY_RUNTIME = 0x8000000000000000ULL;
|
||||||
|
|
||||||
|
for (uint64_t i = 0; i < count; i++) {
|
||||||
|
auto* desc = (EfiMemDesc*)(base + i * descSize);
|
||||||
|
if (desc->Attribute & EFI_MEMORY_RUNTIME) {
|
||||||
|
for (uint64_t p = 0; p < desc->NumberOfPages; p++) {
|
||||||
|
uint64_t addr = desc->PhysicalStart + p * 0x1000;
|
||||||
|
Map(addr, addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlushTLB();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -108,10 +108,18 @@ public:
|
|||||||
|
|
||||||
// Map a page into an arbitrary PML4 with User + Write-Combining attributes.
|
// Map a page into an arbitrary PML4 with User + Write-Combining attributes.
|
||||||
static void MapUserInWC(std::uint64_t pml4Phys, std::uint64_t physicalAddress, std::uint64_t virtualAddress);
|
static void MapUserInWC(std::uint64_t pml4Phys, std::uint64_t physicalAddress, std::uint64_t virtualAddress);
|
||||||
|
|
||||||
|
// Identity-map EFI runtime service regions so firmware code can
|
||||||
|
// reference its own data at physical addresses.
|
||||||
|
void MapEfiRuntime(limine_efi_memmap_response* efiMemmap);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Paging* g_paging;
|
extern Paging* g_paging;
|
||||||
|
|
||||||
extern "C" uint64_t GetCR3();
|
extern "C" uint64_t GetCR3();
|
||||||
extern "C" void LoadCR3(PageTable* PML4);
|
extern "C" void LoadCR3(PageTable* PML4);
|
||||||
|
|
||||||
|
inline void FlushTLB() {
|
||||||
|
asm volatile("mov %%cr3, %%rax; mov %%rax, %%cr3" ::: "rax", "memory");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -52,6 +52,13 @@ namespace {
|
|||||||
.response = nullptr
|
.response = nullptr
|
||||||
};
|
};
|
||||||
|
|
||||||
|
__attribute__((used, section(".limine_requests")))
|
||||||
|
volatile limine_efi_memmap_request efi_memmap_request = {
|
||||||
|
.id = LIMINE_EFI_MEMMAP_REQUEST,
|
||||||
|
.revision = 0,
|
||||||
|
.response = nullptr
|
||||||
|
};
|
||||||
|
|
||||||
__attribute__((used, section(".limine_requests")))
|
__attribute__((used, section(".limine_requests")))
|
||||||
volatile limine_rsdp_request rsdp_request = {
|
volatile limine_rsdp_request rsdp_request = {
|
||||||
.id = LIMINE_RSDP_REQUEST,
|
.id = LIMINE_RSDP_REQUEST,
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ struct DesktopState {
|
|||||||
|
|
||||||
SvgIcon icon_settings;
|
SvgIcon icon_settings;
|
||||||
SvgIcon icon_reboot;
|
SvgIcon icon_reboot;
|
||||||
|
SvgIcon icon_shutdown;
|
||||||
|
|
||||||
SvgIcon icon_weather;
|
SvgIcon icon_weather;
|
||||||
|
|
||||||
|
|||||||
@@ -174,4 +174,5 @@ void open_devexplorer(DesktopState* ds);
|
|||||||
void open_settings(DesktopState* ds);
|
void open_settings(DesktopState* ds);
|
||||||
void open_doom(DesktopState* ds);
|
void open_doom(DesktopState* ds);
|
||||||
void open_reboot_dialog(DesktopState* ds);
|
void open_reboot_dialog(DesktopState* ds);
|
||||||
|
void open_shutdown_dialog(DesktopState* ds);
|
||||||
void desktop_poll_external_windows(DesktopState* ds);
|
void desktop_poll_external_windows(DesktopState* ds);
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ void gui::desktop_init(DesktopState* ds) {
|
|||||||
|
|
||||||
ds->icon_settings = svg_load("0:/icons/help-about.svg", 20, 20, defColor);
|
ds->icon_settings = svg_load("0:/icons/help-about.svg", 20, 20, defColor);
|
||||||
ds->icon_reboot = svg_load("0:/icons/system-reboot.svg", 20, 20, defColor);
|
ds->icon_reboot = svg_load("0:/icons/system-reboot.svg", 20, 20, defColor);
|
||||||
|
ds->icon_shutdown = svg_load("0:/icons/system-shutdown.svg", 20, 20, defColor);
|
||||||
|
|
||||||
ds->icon_weather = svg_load("0:/icons/weather-widget.svg", 20, 20, defColor);
|
ds->icon_weather = svg_load("0:/icons/weather-widget.svg", 20, 20, defColor);
|
||||||
|
|
||||||
@@ -424,7 +425,7 @@ struct MenuRow {
|
|||||||
int app_id; // -1 for category headers / dividers
|
int app_id; // -1 for category headers / dividers
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr int MENU_ROW_COUNT = 19;
|
static constexpr int MENU_ROW_COUNT = 20;
|
||||||
static const MenuRow menu_rows[MENU_ROW_COUNT] = {
|
static const MenuRow menu_rows[MENU_ROW_COUNT] = {
|
||||||
{ true, "Applications", -1 },
|
{ true, "Applications", -1 },
|
||||||
{ false, "Terminal", 0 },
|
{ false, "Terminal", 0 },
|
||||||
@@ -445,6 +446,7 @@ static const MenuRow menu_rows[MENU_ROW_COUNT] = {
|
|||||||
{ true, "", -1 }, // divider
|
{ true, "", -1 }, // divider
|
||||||
{ false, "Settings", 11 },
|
{ false, "Settings", 11 },
|
||||||
{ false, "Reboot", 12 },
|
{ false, "Reboot", 12 },
|
||||||
|
{ false, "Shutdown", 14 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int menu_row_height(const MenuRow& row) {
|
static int menu_row_height(const MenuRow& row) {
|
||||||
@@ -474,7 +476,7 @@ static void desktop_draw_app_menu(DesktopState* ds) {
|
|||||||
draw_rect(fb, menu_x, menu_y, MENU_W, menu_h, colors::BORDER);
|
draw_rect(fb, menu_x, menu_y, MENU_W, menu_h, colors::BORDER);
|
||||||
|
|
||||||
// Icon lookup by app_id
|
// Icon lookup by app_id
|
||||||
SvgIcon* icons[14] = {
|
SvgIcon* icons[15] = {
|
||||||
&ds->icon_terminal, // 0
|
&ds->icon_terminal, // 0
|
||||||
&ds->icon_filemanager, // 1
|
&ds->icon_filemanager, // 1
|
||||||
&ds->icon_sysinfo, // 2
|
&ds->icon_sysinfo, // 2
|
||||||
@@ -489,6 +491,7 @@ static void desktop_draw_app_menu(DesktopState* ds) {
|
|||||||
&ds->icon_settings, // 11
|
&ds->icon_settings, // 11
|
||||||
&ds->icon_reboot, // 12
|
&ds->icon_reboot, // 12
|
||||||
&ds->icon_weather, // 13
|
&ds->icon_weather, // 13
|
||||||
|
&ds->icon_shutdown, // 14
|
||||||
};
|
};
|
||||||
|
|
||||||
int mx = ds->mouse.x;
|
int mx = ds->mouse.x;
|
||||||
@@ -524,7 +527,7 @@ static void desktop_draw_app_menu(DesktopState* ds) {
|
|||||||
// Icon
|
// Icon
|
||||||
int icon_x = item_rect.x + 8;
|
int icon_x = item_rect.x + 8;
|
||||||
int icon_y = item_rect.y + (row_h - 20) / 2;
|
int icon_y = item_rect.y + (row_h - 20) / 2;
|
||||||
if (row.app_id >= 0 && row.app_id < 14) {
|
if (row.app_id >= 0 && row.app_id < 15) {
|
||||||
SvgIcon* icon = icons[row.app_id];
|
SvgIcon* icon = icons[row.app_id];
|
||||||
if (icon && icon->pixels) {
|
if (icon && icon->pixels) {
|
||||||
fb.blit_alpha(icon_x, icon_y, icon->width, icon->height, icon->pixels);
|
fb.blit_alpha(icon_x, icon_y, icon->width, icon->height, icon->pixels);
|
||||||
@@ -709,6 +712,118 @@ void open_reboot_dialog(DesktopState* ds) {
|
|||||||
win->on_close = reboot_dialog_on_close;
|
win->on_close = reboot_dialog_on_close;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Shutdown Dialog
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
struct ShutdownDialogState {
|
||||||
|
DesktopState* ds;
|
||||||
|
int btn_w, btn_h, btn_y, shutdown_x, cancel_x;
|
||||||
|
bool hover_shutdown, hover_cancel;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void shutdown_dialog_on_draw(Window* win, Framebuffer& fb) {
|
||||||
|
ShutdownDialogState* ss = (ShutdownDialogState*)win->app_data;
|
||||||
|
if (!ss) return;
|
||||||
|
|
||||||
|
Canvas c(win);
|
||||||
|
c.fill(colors::WINDOW_BG);
|
||||||
|
|
||||||
|
const char* msg = "Shut down the system?";
|
||||||
|
int tw = text_width(msg);
|
||||||
|
c.text((c.w - tw) / 2, 30, msg, colors::TEXT_COLOR);
|
||||||
|
|
||||||
|
int btn_w = 100;
|
||||||
|
int btn_h = 32;
|
||||||
|
int btn_y = c.h - btn_h - 20;
|
||||||
|
int gap = 20;
|
||||||
|
int total_w = btn_w * 2 + gap;
|
||||||
|
int bx = (c.w - total_w) / 2;
|
||||||
|
ss->btn_w = btn_w;
|
||||||
|
ss->btn_h = btn_h;
|
||||||
|
ss->btn_y = btn_y;
|
||||||
|
ss->shutdown_x = bx;
|
||||||
|
ss->cancel_x = bx + btn_w + gap;
|
||||||
|
|
||||||
|
Color shutdown_bg = ss->hover_shutdown
|
||||||
|
? Color::from_rgb(0xDD, 0x44, 0x44)
|
||||||
|
: Color::from_rgb(0xCC, 0x33, 0x33);
|
||||||
|
c.button(ss->shutdown_x, btn_y, btn_w, btn_h, "Shut Down", shutdown_bg, colors::WHITE, 4);
|
||||||
|
|
||||||
|
Color cancel_bg = ss->hover_cancel
|
||||||
|
? Color::from_rgb(0x99, 0x99, 0x99)
|
||||||
|
: Color::from_rgb(0x88, 0x88, 0x88);
|
||||||
|
c.button(ss->cancel_x, btn_y, btn_w, btn_h, "Cancel", cancel_bg, colors::WHITE, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void shutdown_dialog_on_mouse(Window* win, MouseEvent& ev) {
|
||||||
|
ShutdownDialogState* ss = (ShutdownDialogState*)win->app_data;
|
||||||
|
if (!ss) return;
|
||||||
|
|
||||||
|
Rect cr = win->content_rect();
|
||||||
|
int lx = ev.x - cr.x;
|
||||||
|
int ly = ev.y - cr.y;
|
||||||
|
|
||||||
|
Rect sb = {ss->shutdown_x, ss->btn_y, ss->btn_w, ss->btn_h};
|
||||||
|
Rect cb = {ss->cancel_x, ss->btn_y, ss->btn_w, ss->btn_h};
|
||||||
|
ss->hover_shutdown = sb.contains(lx, ly);
|
||||||
|
ss->hover_cancel = cb.contains(lx, ly);
|
||||||
|
|
||||||
|
if (ev.left_pressed()) {
|
||||||
|
if (ss->hover_shutdown) zenith::shutdown();
|
||||||
|
if (ss->hover_cancel) {
|
||||||
|
for (int i = 0; i < ss->ds->window_count; i++) {
|
||||||
|
if (ss->ds->windows[i].app_data == ss) {
|
||||||
|
desktop_close_window(ss->ds, i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void shutdown_dialog_on_key(Window* win, const Zenith::KeyEvent& key) {
|
||||||
|
ShutdownDialogState* ss = (ShutdownDialogState*)win->app_data;
|
||||||
|
if (!ss || !key.pressed) return;
|
||||||
|
|
||||||
|
if (key.ascii == '\n' || key.ascii == '\r') {
|
||||||
|
zenith::shutdown();
|
||||||
|
}
|
||||||
|
if (key.scancode == 0x01) { // Escape
|
||||||
|
for (int i = 0; i < ss->ds->window_count; i++) {
|
||||||
|
if (ss->ds->windows[i].app_data == ss) {
|
||||||
|
desktop_close_window(ss->ds, i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void shutdown_dialog_on_close(Window* win) {
|
||||||
|
if (win->app_data) {
|
||||||
|
zenith::mfree(win->app_data);
|
||||||
|
win->app_data = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_shutdown_dialog(DesktopState* ds) {
|
||||||
|
int wx = (ds->screen_w - 300) / 2;
|
||||||
|
int wy = (ds->screen_h - 150) / 2;
|
||||||
|
int idx = desktop_create_window(ds, "Shut Down", wx, wy, 300, 150);
|
||||||
|
if (idx < 0) return;
|
||||||
|
|
||||||
|
Window* win = &ds->windows[idx];
|
||||||
|
ShutdownDialogState* ss = (ShutdownDialogState*)zenith::malloc(sizeof(ShutdownDialogState));
|
||||||
|
zenith::memset(ss, 0, sizeof(ShutdownDialogState));
|
||||||
|
ss->ds = ds;
|
||||||
|
|
||||||
|
win->app_data = ss;
|
||||||
|
win->on_draw = shutdown_dialog_on_draw;
|
||||||
|
win->on_mouse = shutdown_dialog_on_mouse;
|
||||||
|
win->on_key = shutdown_dialog_on_key;
|
||||||
|
win->on_close = shutdown_dialog_on_close;
|
||||||
|
}
|
||||||
|
|
||||||
static gui::ResizeEdge hit_test_resize_edge(const gui::Rect& f, int mx, int my) {
|
static gui::ResizeEdge hit_test_resize_edge(const gui::Rect& f, int mx, int my) {
|
||||||
using namespace gui;
|
using namespace gui;
|
||||||
int G = RESIZE_GRAB;
|
int G = RESIZE_GRAB;
|
||||||
@@ -808,7 +923,7 @@ void gui::desktop_compose(DesktopState* ds) {
|
|||||||
if (ds->ctx_menu_open) {
|
if (ds->ctx_menu_open) {
|
||||||
static constexpr int CTX_MENU_W = 180;
|
static constexpr int CTX_MENU_W = 180;
|
||||||
static constexpr int CTX_ITEM_H = 36;
|
static constexpr int CTX_ITEM_H = 36;
|
||||||
static constexpr int CTX_ITEM_COUNT = 4;
|
static constexpr int CTX_ITEM_COUNT = 5;
|
||||||
int cmx = ds->ctx_menu_x;
|
int cmx = ds->ctx_menu_x;
|
||||||
int cmy = ds->ctx_menu_y;
|
int cmy = ds->ctx_menu_y;
|
||||||
int cmh = CTX_ITEM_H * CTX_ITEM_COUNT + 8;
|
int cmh = CTX_ITEM_H * CTX_ITEM_COUNT + 8;
|
||||||
@@ -827,6 +942,7 @@ void gui::desktop_compose(DesktopState* ds) {
|
|||||||
{ "Files", &ds->icon_filemanager },
|
{ "Files", &ds->icon_filemanager },
|
||||||
{ "About", &ds->icon_settings },
|
{ "About", &ds->icon_settings },
|
||||||
{ "Reboot", &ds->icon_reboot },
|
{ "Reboot", &ds->icon_reboot },
|
||||||
|
{ "Shutdown", &ds->icon_shutdown },
|
||||||
};
|
};
|
||||||
|
|
||||||
int mmx = ds->mouse.x;
|
int mmx = ds->mouse.x;
|
||||||
@@ -913,7 +1029,7 @@ void gui::desktop_handle_mouse(DesktopState* ds) {
|
|||||||
if (left_pressed) {
|
if (left_pressed) {
|
||||||
static constexpr int CTX_MENU_W = 180;
|
static constexpr int CTX_MENU_W = 180;
|
||||||
static constexpr int CTX_ITEM_H = 36;
|
static constexpr int CTX_ITEM_H = 36;
|
||||||
static constexpr int CTX_ITEM_COUNT = 4;
|
static constexpr int CTX_ITEM_COUNT = 5;
|
||||||
int cmx = ds->ctx_menu_x;
|
int cmx = ds->ctx_menu_x;
|
||||||
int cmy = ds->ctx_menu_y;
|
int cmy = ds->ctx_menu_y;
|
||||||
int cmh = CTX_ITEM_H * CTX_ITEM_COUNT + 8;
|
int cmh = CTX_ITEM_H * CTX_ITEM_COUNT + 8;
|
||||||
@@ -931,6 +1047,7 @@ void gui::desktop_handle_mouse(DesktopState* ds) {
|
|||||||
case 1: open_filemanager(ds); break;
|
case 1: open_filemanager(ds); break;
|
||||||
case 2: open_settings(ds); break;
|
case 2: open_settings(ds); break;
|
||||||
case 3: open_reboot_dialog(ds); break;
|
case 3: open_reboot_dialog(ds); break;
|
||||||
|
case 4: open_shutdown_dialog(ds); break;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1105,6 +1222,7 @@ void gui::desktop_handle_mouse(DesktopState* ds) {
|
|||||||
case 11: open_settings(ds); break;
|
case 11: open_settings(ds); break;
|
||||||
case 12: open_reboot_dialog(ds); break;
|
case 12: open_reboot_dialog(ds); break;
|
||||||
case 13: open_weather(ds); break;
|
case 13: open_weather(ds); break;
|
||||||
|
case 14: open_shutdown_dialog(ds); break;
|
||||||
}
|
}
|
||||||
ds->app_menu_open = false;
|
ds->app_menu_open = false;
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
Binary file not shown.
@@ -52,6 +52,7 @@ ICONS=(
|
|||||||
"mimetypes/scalable/application-x-executable.svg"
|
"mimetypes/scalable/application-x-executable.svg"
|
||||||
"categories/scalable/help-about.svg"
|
"categories/scalable/help-about.svg"
|
||||||
"categories/scalable/system-reboot.svg"
|
"categories/scalable/system-reboot.svg"
|
||||||
|
"categories/scalable/system-shutdown.svg"
|
||||||
"apps/scalable/doom.svg"
|
"apps/scalable/doom.svg"
|
||||||
"apps/scalable/system-monitor.svg"
|
"apps/scalable/system-monitor.svg"
|
||||||
"apps/scalable/applications-science.svg"
|
"apps/scalable/applications-science.svg"
|
||||||
|
|||||||
Reference in New Issue
Block a user