652 lines
21 KiB
C++
652 lines
21 KiB
C++
/*
|
|
* main.cpp
|
|
* MontaukOS Display and Graphics settings
|
|
* Copyright (c) 2026 Daniel Hammer
|
|
*/
|
|
|
|
#include <montauk/syscall.h>
|
|
#include <montauk/string.h>
|
|
#include <montauk/config.h>
|
|
#include <gui/mtk.hpp>
|
|
#include <gui/mtk/settings.hpp>
|
|
#include <gui/standalone.hpp>
|
|
|
|
extern "C" {
|
|
#include <stdio.h>
|
|
}
|
|
|
|
using namespace gui;
|
|
|
|
static constexpr int WIN_W = 620;
|
|
static constexpr int WIN_H = 500;
|
|
static constexpr int TAB_H = 36;
|
|
static constexpr int FOOTER_H = 44;
|
|
static constexpr int PAD = 18;
|
|
static constexpr int GAP = 12;
|
|
static constexpr int BUTTON_W = 88;
|
|
static constexpr int SLIDER_H = 6;
|
|
static constexpr int KNOB_R = 8;
|
|
static constexpr int MODE_ROW_H = 36;
|
|
static constexpr int VISIBLE_MODES = 6;
|
|
static constexpr int MAX_MODES = 32;
|
|
static constexpr int INFO_LABEL_W = 175;
|
|
|
|
enum Tab {
|
|
TAB_DISPLAY = 0,
|
|
TAB_GRAPHICS = 1,
|
|
TAB_COUNT = 2,
|
|
};
|
|
|
|
static const char* const kTabLabels[TAB_COUNT] = {
|
|
"Display",
|
|
"Graphics",
|
|
};
|
|
|
|
static WsWindow g_win;
|
|
static Tab g_tab = TAB_DISPLAY;
|
|
static int g_mouse_x = -1;
|
|
static int g_mouse_y = -1;
|
|
static bool g_brightness_drag = false;
|
|
static Color g_accent = colors::ACCENT;
|
|
|
|
static montauk::abi::DisplayInfo g_info = {};
|
|
static montauk::abi::DisplayModeInfo g_modes[MAX_MODES] = {};
|
|
static int g_mode_count = 0;
|
|
static bool g_driver_available = false;
|
|
static int g_selected_mode = -1;
|
|
static int g_pending_brightness = -1;
|
|
static bool g_tear_free = true;
|
|
static bool g_saved_tear_free = true;
|
|
static int g_mode_scroll = 0;
|
|
static char g_status[128] = {};
|
|
static uint64_t g_status_time = 0;
|
|
static uint64_t g_last_refresh = 0;
|
|
|
|
static mtk::Theme app_theme() {
|
|
mtk::Theme theme = mtk::make_theme(g_accent);
|
|
theme.danger_soft = Color::from_rgb(0xF8, 0xE0, 0xE0);
|
|
return theme;
|
|
}
|
|
|
|
static Rect tab_bar_rect() {
|
|
return {0, 0, g_win.width, TAB_H};
|
|
}
|
|
|
|
static Rect footer_rect() {
|
|
return {0, g_win.height - FOOTER_H, g_win.width, FOOTER_H};
|
|
}
|
|
|
|
static int display_heading_y() {
|
|
return TAB_H + 20;
|
|
}
|
|
|
|
static int brightness_row_y() {
|
|
return display_heading_y() + system_font_height() + 18;
|
|
}
|
|
|
|
static Rect brightness_slider_rect() {
|
|
int y = brightness_row_y() + (system_font_height() - SLIDER_H) / 2;
|
|
return {PAD + INFO_LABEL_W, y,
|
|
g_win.width - PAD * 2 - INFO_LABEL_W - 44, SLIDER_H};
|
|
}
|
|
|
|
static int modes_separator_y() {
|
|
return brightness_row_y() + system_font_height() + 26;
|
|
}
|
|
|
|
static int modes_header_y() {
|
|
return modes_separator_y() + 16;
|
|
}
|
|
|
|
static int mode_list_y() {
|
|
return modes_header_y() + system_font_height() + 8;
|
|
}
|
|
|
|
static Rect mode_row_rect(int visibleIndex) {
|
|
return {PAD, mode_list_y() + visibleIndex * MODE_ROW_H,
|
|
g_win.width - PAD * 2, MODE_ROW_H};
|
|
}
|
|
|
|
static Rect apply_button() {
|
|
Rect foot = footer_rect();
|
|
return {foot.x + foot.w - PAD - BUTTON_W, foot.y + 7, BUTTON_W, 30};
|
|
}
|
|
|
|
static Rect revert_button() {
|
|
Rect apply = apply_button();
|
|
return {apply.x - GAP - BUTTON_W, apply.y, BUTTON_W, apply.h};
|
|
}
|
|
|
|
static Rect refresh_button() {
|
|
Rect apply = apply_button();
|
|
return {apply.x - GAP - BUTTON_W, apply.y, BUTTON_W, apply.h};
|
|
}
|
|
|
|
static int graphics_table_y() {
|
|
return TAB_H + 24 + system_font_height() + 18;
|
|
}
|
|
|
|
static int graphics_row_step() {
|
|
return system_font_height() + 10;
|
|
}
|
|
|
|
static int graphics_separator_y() {
|
|
return graphics_table_y() + graphics_row_step() * 5 + 4;
|
|
}
|
|
|
|
static int graphics_section_y() {
|
|
return graphics_separator_y() + 16;
|
|
}
|
|
|
|
static Rect tear_free_option() {
|
|
return {PAD, graphics_section_y() + system_font_height() + 8,
|
|
g_win.width - PAD * 2, 24};
|
|
}
|
|
|
|
static bool mouse_in(const Rect& rect) {
|
|
return rect.contains(g_mouse_x, g_mouse_y);
|
|
}
|
|
|
|
static mtk::WidgetState button_state(const Rect& rect, bool enabled = true) {
|
|
return mtk::widget_state(false, mouse_in(rect), enabled);
|
|
}
|
|
|
|
static void set_status(const char* msg) {
|
|
snprintf(g_status, sizeof(g_status), "%s", msg ? msg : "");
|
|
g_status_time = montauk::get_milliseconds();
|
|
}
|
|
|
|
static bool status_visible() {
|
|
return g_status[0] && montauk::get_milliseconds() - g_status_time < 5000;
|
|
}
|
|
|
|
static void load_graphics_preference() {
|
|
char user[64] = {};
|
|
if (montauk::getuser(user, sizeof(user)) > 0 && user[0]) {
|
|
auto doc = montauk::config::load_user(user, "display");
|
|
g_tear_free = doc.get_bool("graphics.tear_free", true);
|
|
doc.destroy();
|
|
} else {
|
|
auto doc = montauk::config::load("display");
|
|
g_tear_free = doc.get_bool("graphics.tear_free", true);
|
|
doc.destroy();
|
|
}
|
|
g_saved_tear_free = g_tear_free;
|
|
}
|
|
|
|
static bool save_graphics_preference() {
|
|
montauk::toml::Doc doc;
|
|
doc.init();
|
|
montauk::config::set_bool(&doc, "graphics.tear_free", g_tear_free);
|
|
|
|
char user[64] = {};
|
|
int result;
|
|
if (montauk::getuser(user, sizeof(user)) > 0 && user[0])
|
|
result = montauk::config::save_user(user, "display", &doc);
|
|
else
|
|
result = montauk::config::save("display", &doc);
|
|
doc.destroy();
|
|
if (result >= 0) g_saved_tear_free = g_tear_free;
|
|
return result >= 0;
|
|
}
|
|
|
|
static void clamp_mode_scroll() {
|
|
int max_scroll = g_mode_count > VISIBLE_MODES
|
|
? g_mode_count - VISIBLE_MODES : 0;
|
|
if (g_mode_scroll < 0) g_mode_scroll = 0;
|
|
if (g_mode_scroll > max_scroll) g_mode_scroll = max_scroll;
|
|
}
|
|
|
|
static void refresh_state(bool resetPending) {
|
|
montauk::abi::DisplayInfo next = {};
|
|
if (montauk::display_info(&next) < 0) {
|
|
g_driver_available = false;
|
|
g_mode_count = 0;
|
|
g_last_refresh = montauk::get_milliseconds();
|
|
return;
|
|
}
|
|
|
|
g_info = next;
|
|
int count = montauk::display_modes(g_modes, MAX_MODES);
|
|
g_mode_count = count > 0 ? count : 0;
|
|
g_driver_available = true;
|
|
if (resetPending) {
|
|
g_selected_mode = g_info.currentMode;
|
|
g_pending_brightness = g_info.brightness;
|
|
if (g_selected_mode >= VISIBLE_MODES)
|
|
g_mode_scroll = g_selected_mode - VISIBLE_MODES + 1;
|
|
}
|
|
clamp_mode_scroll();
|
|
g_last_refresh = montauk::get_milliseconds();
|
|
}
|
|
|
|
static bool dirty() {
|
|
bool display_dirty = g_driver_available
|
|
&& (g_selected_mode != g_info.currentMode
|
|
|| (g_info.brightness >= 0
|
|
&& g_pending_brightness != g_info.brightness));
|
|
return display_dirty || g_tear_free != g_saved_tear_free;
|
|
}
|
|
|
|
static void format_mode(char* out, int cap,
|
|
const montauk::abi::DisplayModeInfo& mode) {
|
|
uint32_t hz = (mode.refreshMilliHz + 500) / 1000;
|
|
snprintf(out, cap, "%ux%u %u Hz",
|
|
(unsigned)mode.width, (unsigned)mode.height, (unsigned)hz);
|
|
}
|
|
|
|
static void fit_text(char* out, int outCap, const char* text, int maxWidth) {
|
|
if (!out || outCap <= 0) return;
|
|
out[0] = '\0';
|
|
if (!text || !text[0] || maxWidth <= 0) return;
|
|
if (text_width(text) <= maxWidth) {
|
|
snprintf(out, outCap, "%s", text);
|
|
return;
|
|
}
|
|
|
|
static const char* ellipsis = "...";
|
|
int len = 0;
|
|
while (text[len] && len < outCap - 4) {
|
|
out[len] = text[len];
|
|
out[len + 1] = '\0';
|
|
if (text_width(out) + text_width(ellipsis) > maxWidth) break;
|
|
len++;
|
|
}
|
|
out[len++] = '.';
|
|
out[len++] = '.';
|
|
out[len++] = '.';
|
|
out[len] = '\0';
|
|
}
|
|
|
|
static void draw_text_fit(Canvas& c, int x, int y, const char* text,
|
|
int maxWidth, Color color) {
|
|
char fitted[160];
|
|
fit_text(fitted, sizeof(fitted), text, maxWidth);
|
|
c.text(x, y, fitted, color);
|
|
}
|
|
|
|
static void draw_kv(Canvas& c, int* y, const char* key, const char* value,
|
|
const mtk::Theme& theme) {
|
|
c.text(PAD, *y, key, theme.text_muted);
|
|
draw_text_fit(c, PAD + INFO_LABEL_W, *y, value,
|
|
g_win.width - PAD * 2 - INFO_LABEL_W, theme.text);
|
|
*y += graphics_row_step();
|
|
}
|
|
|
|
static void draw_slider(Canvas& c, const Rect& rect, int value,
|
|
const mtk::Theme& theme) {
|
|
Color track = mtk::mix(theme.surface, theme.border, 80);
|
|
c.fill_rounded_rect(rect.x, rect.y, rect.w, rect.h, rect.h / 2, track);
|
|
int fill_w = (value * rect.w) / 100;
|
|
if (fill_w > 0)
|
|
c.fill_rounded_rect(rect.x, rect.y, fill_w, rect.h,
|
|
rect.h / 2, theme.accent);
|
|
int kx = rect.x + fill_w;
|
|
int ky = rect.y + rect.h / 2;
|
|
fill_circle(c, kx, ky, KNOB_R, theme.accent);
|
|
fill_circle(c, kx, ky, KNOB_R - 3, colors::WHITE);
|
|
}
|
|
|
|
static void draw_display_tab(Canvas& c, const mtk::Theme& theme) {
|
|
int y = display_heading_y();
|
|
if (!g_driver_available) {
|
|
c.text(PAD, y, "Intel display driver unavailable", theme.danger);
|
|
c.text(PAD, y + 28,
|
|
"The firmware framebuffer remains active, but hardware controls are unavailable.",
|
|
theme.text_subtle);
|
|
return;
|
|
}
|
|
|
|
char headline[128];
|
|
snprintf(headline, sizeof(headline), "%s on %s",
|
|
g_info.monitorName[0] ? g_info.monitorName : "Display",
|
|
g_info.connectorName[0] ? g_info.connectorName : "unknown connector");
|
|
draw_text_fit(c, PAD, y, headline, g_win.width - PAD * 2,
|
|
g_info.connected ? theme.text : theme.danger);
|
|
|
|
y = brightness_row_y();
|
|
c.text(PAD, y, "Brightness", theme.text_subtle);
|
|
if (g_info.brightness >= 0) {
|
|
Rect slider = brightness_slider_rect();
|
|
draw_slider(c, slider, g_pending_brightness, theme);
|
|
char percent[16];
|
|
snprintf(percent, sizeof(percent), "%d%%", g_pending_brightness);
|
|
c.text(slider.x + slider.w + 14, slider.y - 6, percent, theme.text);
|
|
} else {
|
|
draw_text_fit(c, PAD + INFO_LABEL_W, y,
|
|
"Controlled by the external display",
|
|
g_win.width - PAD * 2 - INFO_LABEL_W, theme.text_muted);
|
|
}
|
|
|
|
y = modes_separator_y();
|
|
mtk::draw_separator(c, PAD, y, g_win.width - PAD * 2, theme);
|
|
y = modes_header_y();
|
|
c.text(PAD, y, "AVAILABLE MODES", theme.text_muted);
|
|
const char* hint = "Grey modes require link training";
|
|
c.text(g_win.width - PAD - text_width(hint), y, hint, theme.text_muted);
|
|
|
|
for (int row = 0; row < VISIBLE_MODES; row++) {
|
|
int index = g_mode_scroll + row;
|
|
if (index >= g_mode_count) break;
|
|
Rect option = mode_row_rect(row);
|
|
bool valid = (g_modes[index].flags
|
|
& montauk::abi::DISPLAY_MODE_DRIVER_VALID) != 0;
|
|
bool selected = index == g_selected_mode;
|
|
if (selected)
|
|
c.fill_rounded_rect(option.x, option.y + 2, option.w, option.h - 4,
|
|
4, theme.accent_soft);
|
|
Rect radio = {option.x + 4, option.y, 18, option.h};
|
|
mtk::draw_radio(c, radio, "", selected, theme);
|
|
|
|
char mode_text[64];
|
|
format_mode(mode_text, sizeof(mode_text), g_modes[index]);
|
|
Color text = valid ? theme.text : theme.text_muted;
|
|
int textY = option.y + (option.h - system_font_height()) / 2;
|
|
c.text(option.x + 34, textY, mode_text, text);
|
|
|
|
const char* state = nullptr;
|
|
Color stateColor = theme.text_subtle;
|
|
if (g_modes[index].flags & montauk::abi::DISPLAY_MODE_CURRENT) {
|
|
state = "Current";
|
|
stateColor = theme.accent;
|
|
} else if (g_modes[index].flags & montauk::abi::DISPLAY_MODE_PREFERRED) {
|
|
state = "Preferred";
|
|
} else if (!valid) {
|
|
state = "Link training required";
|
|
stateColor = theme.text_muted;
|
|
}
|
|
if (state)
|
|
c.text(option.x + option.w - text_width(state) - 10,
|
|
textY, state, stateColor);
|
|
}
|
|
|
|
if (g_mode_count == 0) {
|
|
c.text(PAD + 4, mode_list_y() + 9,
|
|
"No EDID modes were reported; the firmware mode remains active.",
|
|
theme.text_subtle);
|
|
}
|
|
|
|
if (g_mode_count > VISIBLE_MODES) {
|
|
char page[48];
|
|
snprintf(page, sizeof(page), "Modes %d-%d of %d (scroll)",
|
|
g_mode_scroll + 1,
|
|
g_mode_scroll + VISIBLE_MODES < g_mode_count
|
|
? g_mode_scroll + VISIBLE_MODES : g_mode_count,
|
|
g_mode_count);
|
|
c.text(PAD, footer_rect().y - 22, page, theme.text_muted);
|
|
}
|
|
}
|
|
|
|
static const char* yes_no(bool value) {
|
|
return value ? "Available" : "Unavailable";
|
|
}
|
|
|
|
static void draw_graphics_tab(Canvas& c, const mtk::Theme& theme) {
|
|
int y = TAB_H + 24;
|
|
c.text(PAD, y, "Intel Graphics", theme.text);
|
|
y = graphics_table_y();
|
|
|
|
if (!g_driver_available) {
|
|
c.text(PAD, y, "No managed Intel GPU was detected.", theme.danger);
|
|
return;
|
|
}
|
|
|
|
char device[96];
|
|
snprintf(device, sizeof(device), "%s (Gen %u, PCI %04X)",
|
|
g_info.gpuName, (unsigned)g_info.generation,
|
|
(unsigned)g_info.deviceId);
|
|
draw_kv(c, &y, "Adapter", device, theme);
|
|
draw_kv(c, &y, "EDID modes",
|
|
yes_no((g_info.capabilities & montauk::abi::DISPLAY_CAP_EDID) != 0),
|
|
theme);
|
|
draw_kv(c, &y, "Page flipping",
|
|
yes_no((g_info.capabilities & montauk::abi::DISPLAY_CAP_PAGE_FLIP) != 0),
|
|
theme);
|
|
draw_kv(c, &y, "VBlank interrupt",
|
|
yes_no((g_info.capabilities & montauk::abi::DISPLAY_CAP_VBLANK_IRQ) != 0),
|
|
theme);
|
|
draw_kv(c, &y, "Safe mode switch",
|
|
yes_no((g_info.capabilities & montauk::abi::DISPLAY_CAP_SAFE_SWITCH) != 0),
|
|
theme);
|
|
|
|
y = graphics_separator_y();
|
|
mtk::draw_separator(c, PAD, y, g_win.width - PAD * 2, theme);
|
|
y = graphics_section_y();
|
|
c.text(PAD, y, "PRESENTATION", theme.text_muted);
|
|
|
|
Rect option = tear_free_option();
|
|
bool flipAvailable =
|
|
(g_info.capabilities & montauk::abi::DISPLAY_CAP_PAGE_FLIP) != 0;
|
|
mtk::draw_checkbox(c, option, "Prefer tear-free hardware page flipping",
|
|
mtk::check_state(g_tear_free), theme,
|
|
flipAvailable, mouse_in(option));
|
|
c.text(option.x + 24, option.y + option.h + 2,
|
|
flipAvailable
|
|
? "Applied when the next desktop session starts."
|
|
: "Unavailable on this boot; the software copy path is active.",
|
|
flipAvailable ? theme.text_subtle : theme.text_muted);
|
|
}
|
|
|
|
static void draw_footer(Canvas& c, const mtk::Theme& theme) {
|
|
Rect foot = footer_rect();
|
|
c.fill_rect(foot.x, foot.y, foot.w, foot.h, theme.surface);
|
|
mtk::draw_separator(c, 0, foot.y, g_win.width, theme);
|
|
|
|
const char* message = status_visible()
|
|
? g_status : (dirty() ? "Unsaved display changes" : "Display settings ready");
|
|
c.text(PAD, foot.y + (foot.h - system_font_height()) / 2,
|
|
message, dirty() ? theme.text : theme.text_subtle);
|
|
|
|
if (g_tab == TAB_DISPLAY || dirty()) {
|
|
Rect revert = revert_button();
|
|
Rect apply = apply_button();
|
|
mtk::draw_button(c, revert, "Revert", mtk::BUTTON_SECONDARY,
|
|
button_state(revert, dirty()), theme);
|
|
mtk::draw_button(c, apply, "Apply", mtk::BUTTON_PRIMARY,
|
|
button_state(apply, dirty()), theme);
|
|
} else {
|
|
Rect refresh = refresh_button();
|
|
mtk::draw_button(c, refresh, "Refresh", mtk::BUTTON_SECONDARY,
|
|
button_state(refresh), theme);
|
|
}
|
|
}
|
|
|
|
static void render() {
|
|
mtk::StandaloneHost host(&g_win);
|
|
Canvas c = host.canvas();
|
|
mtk::Theme theme = app_theme();
|
|
c.fill(theme.window_bg);
|
|
mtk::draw_tab_bar(c, tab_bar_rect(), kTabLabels, TAB_COUNT, (int)g_tab, theme);
|
|
if (g_tab == TAB_DISPLAY) draw_display_tab(c, theme);
|
|
else draw_graphics_tab(c, theme);
|
|
draw_footer(c, theme);
|
|
host.present();
|
|
}
|
|
|
|
static int brightness_from_x(int mx) {
|
|
Rect slider = brightness_slider_rect();
|
|
int value = ((mx - slider.x) * 100) / slider.w;
|
|
if (value < 0) value = 0;
|
|
if (value > 100) value = 100;
|
|
return value;
|
|
}
|
|
|
|
static bool slider_hit(int mx, int my) {
|
|
Rect slider = brightness_slider_rect();
|
|
return mx >= slider.x - KNOB_R && mx <= slider.x + slider.w + KNOB_R
|
|
&& my >= slider.y - KNOB_R - 4 && my <= slider.y + slider.h + KNOB_R + 4;
|
|
}
|
|
|
|
static void revert_changes() {
|
|
g_selected_mode = g_info.currentMode;
|
|
g_pending_brightness = g_info.brightness;
|
|
g_tear_free = g_saved_tear_free;
|
|
set_status("Changes reverted");
|
|
}
|
|
|
|
static void apply_changes() {
|
|
if (!dirty()) return;
|
|
bool graphics_changed = g_tear_free != g_saved_tear_free;
|
|
|
|
if (g_driver_available && g_info.brightness >= 0
|
|
&& g_pending_brightness != g_info.brightness) {
|
|
int result = montauk::display_brightness(g_pending_brightness);
|
|
if (result < 0) {
|
|
set_status("Could not change panel brightness");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (g_driver_available && g_selected_mode != g_info.currentMode) {
|
|
int result = montauk::display_set_mode(g_selected_mode);
|
|
if (result < 0) {
|
|
set_status(result == -2
|
|
? "Mode needs unsupported display-link training"
|
|
: "Mode change failed; previous mode restored");
|
|
refresh_state(true);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (graphics_changed && !save_graphics_preference()) {
|
|
set_status("Could not save graphics preference");
|
|
return;
|
|
}
|
|
|
|
refresh_state(true);
|
|
set_status(graphics_changed
|
|
? "Saved; takes effect next desktop session"
|
|
: "Display settings applied");
|
|
}
|
|
|
|
static bool handle_mouse(const montauk::abi::WinEvent& ev) {
|
|
int mx = ev.mouse.x;
|
|
int my = ev.mouse.y;
|
|
uint8_t buttons = ev.mouse.buttons;
|
|
uint8_t previous = ev.mouse.prev_buttons;
|
|
bool pressed = (buttons & 1) && !(previous & 1);
|
|
bool released = !(buttons & 1) && (previous & 1);
|
|
|
|
if (pressed) {
|
|
int tab = mtk::hit_tab_bar(tab_bar_rect(), TAB_COUNT, mx, my);
|
|
if (tab >= 0) {
|
|
g_tab = (Tab)tab;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (g_tab == TAB_DISPLAY && g_info.brightness >= 0) {
|
|
if (pressed && slider_hit(mx, my)) g_brightness_drag = true;
|
|
if (g_brightness_drag && (buttons & 1))
|
|
g_pending_brightness = brightness_from_x(mx);
|
|
if (released) g_brightness_drag = false;
|
|
}
|
|
|
|
if (g_tab == TAB_DISPLAY && ev.mouse.scroll != 0) {
|
|
g_mode_scroll += ev.mouse.scroll > 0 ? -1 : 1;
|
|
clamp_mode_scroll();
|
|
return true;
|
|
}
|
|
|
|
if (!pressed) return g_brightness_drag;
|
|
|
|
if (g_tab == TAB_DISPLAY) {
|
|
for (int row = 0; row < VISIBLE_MODES; row++) {
|
|
int index = g_mode_scroll + row;
|
|
if (index >= g_mode_count) break;
|
|
if (mode_row_rect(row).contains(mx, my)
|
|
&& (g_modes[index].flags
|
|
& montauk::abi::DISPLAY_MODE_DRIVER_VALID)) {
|
|
g_selected_mode = index;
|
|
return true;
|
|
}
|
|
}
|
|
} else if (tear_free_option().contains(mx, my)
|
|
&& (g_info.capabilities
|
|
& montauk::abi::DISPLAY_CAP_PAGE_FLIP)) {
|
|
g_tear_free = !g_tear_free;
|
|
return true;
|
|
}
|
|
|
|
if (revert_button().contains(mx, my) && dirty()) {
|
|
revert_changes();
|
|
return true;
|
|
}
|
|
if (apply_button().contains(mx, my) && dirty()) {
|
|
apply_changes();
|
|
return true;
|
|
}
|
|
if (refresh_button().contains(mx, my) && !dirty()) {
|
|
refresh_state(true);
|
|
set_status("Display status refreshed");
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool handle_key(const montauk::abi::KeyEvent& key) {
|
|
if (!key.pressed) return false;
|
|
if (key.scancode == 0x01) {
|
|
g_win.closed = true;
|
|
return true;
|
|
}
|
|
if (key.ascii == '\t') {
|
|
g_tab = g_tab == TAB_DISPLAY ? TAB_GRAPHICS : TAB_DISPLAY;
|
|
return true;
|
|
}
|
|
if (key.ascii == '\n' || key.ascii == '\r') {
|
|
apply_changes();
|
|
return true;
|
|
}
|
|
if (key.ascii == 'r' || key.ascii == 'R') {
|
|
refresh_state(!dirty());
|
|
set_status("Display status refreshed");
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
extern "C" void _start() {
|
|
if (!fonts::init()) montauk::exit(1);
|
|
g_accent = mtk::load_system_accent();
|
|
load_graphics_preference();
|
|
|
|
if (!g_win.create("Display", WIN_W, WIN_H)) montauk::exit(1);
|
|
refresh_state(true);
|
|
render();
|
|
|
|
while (g_win.id >= 0 && !g_win.closed) {
|
|
montauk::abi::WinEvent ev;
|
|
int result = g_win.poll(&ev);
|
|
bool redraw = false;
|
|
if (result < 0) break;
|
|
if (result == 0) {
|
|
uint64_t now = montauk::get_milliseconds();
|
|
if (!dirty() && now - g_last_refresh >= 3000) {
|
|
refresh_state(true);
|
|
redraw = true;
|
|
}
|
|
if (status_visible()) redraw = true;
|
|
if (redraw) render();
|
|
montauk::sleep_ms(16);
|
|
continue;
|
|
}
|
|
|
|
if (ev.type == 3) break;
|
|
if (ev.type == 2 || ev.type == 4) {
|
|
redraw = true;
|
|
} else if (ev.type == 1) {
|
|
g_mouse_x = ev.mouse.x;
|
|
g_mouse_y = ev.mouse.y;
|
|
redraw = true;
|
|
handle_mouse(ev);
|
|
} else if (ev.type == 0) {
|
|
redraw = handle_key(ev.key);
|
|
}
|
|
if (redraw) render();
|
|
}
|
|
|
|
g_win.destroy();
|
|
montauk::exit(0);
|
|
}
|