cf515f5584
The 60fps cap still left 16ms gaps with no present; on windowed G-Sync/FreeSync DWM keeps moving the window in and out of the VRR path across those gaps and the refresh rate swings, which the panel shows as flicker. Render continuously (request_repaint every frame) with vsync on so the window stays continuously in VRR at the display's own variable refresh.
122 lines
4.2 KiB
Rust
122 lines
4.2 KiB
Rust
mod account_monitor;
|
|
mod account_sync;
|
|
mod app;
|
|
mod arm;
|
|
mod config;
|
|
mod fifa17_capability;
|
|
mod game_launch;
|
|
mod health;
|
|
mod launch;
|
|
mod local_services;
|
|
mod logs;
|
|
mod netcheck;
|
|
mod preflight;
|
|
mod setup;
|
|
mod theme;
|
|
|
|
fn main() -> eframe::Result<()> {
|
|
let options = eframe::NativeOptions {
|
|
viewport: egui::ViewportBuilder::default()
|
|
.with_title("OpenFUT Launcher")
|
|
.with_app_id("openfut-launcher")
|
|
.with_icon(app_icon())
|
|
.with_inner_size([1040.0, 720.0])
|
|
.with_min_inner_size([880.0, 600.0]),
|
|
// Pair vsync with the display's VRR (G-Sync + Vsync is the recommended
|
|
// combination): frames present on the monitor's own variable refresh.
|
|
vsync: true,
|
|
..Default::default()
|
|
};
|
|
|
|
eframe::run_native(
|
|
"OpenFUT Launcher",
|
|
options,
|
|
Box::new(|cc| Ok(Box::new(app::LauncherApp::new(cc)))),
|
|
)
|
|
}
|
|
|
|
/// The application / taskbar icon: the same "OF" monogram the header wordmark
|
|
/// shows, drawn white on the signature accent tile. Generated in code (no PNG
|
|
/// dependency) at 4x supersampling and box-downsampled to a crisp 64x64 RGBA —
|
|
/// scales cleanly to the 32x32 the WM typically renders. Colours come from the
|
|
/// theme palette so the icon never drifts from the in-app brand.
|
|
fn app_icon() -> egui::IconData {
|
|
const SIZE: usize = 64; // output edge
|
|
const SS: usize = 4; // supersampling factor
|
|
|
|
let accent = theme::ACCENT;
|
|
let fg = theme::ON_ACCENT;
|
|
|
|
// Rounded-square background: point inside the [0,SIZE]² square with corners
|
|
// rounded to `round_r` (transparent outside, so the icon reads as a tile).
|
|
let round_r = 13.0_f32;
|
|
let inside_bg = |x: f32, y: f32| -> bool {
|
|
let s = SIZE as f32;
|
|
let cx = x.clamp(round_r, s - round_r);
|
|
let cy = y.clamp(round_r, s - round_r);
|
|
let (dx, dy) = (x - cx, y - cy);
|
|
dx * dx + dy * dy <= round_r * round_r
|
|
};
|
|
|
|
// "O" — an elliptical ring on the left.
|
|
let inside_o = |x: f32, y: f32| -> bool {
|
|
let (cx, cy) = (21.0_f32, 32.0_f32);
|
|
let (dx, dy) = (x - cx, y - cy);
|
|
let outer = (dx / 9.0).powi(2) + (dy / 14.0).powi(2) <= 1.0;
|
|
let inner = (dx / 4.8).powi(2) + (dy / 9.5).powi(2) < 1.0;
|
|
outer && !inner
|
|
};
|
|
|
|
// "F" — a stem plus a top and middle bar on the right.
|
|
let inside_f = |x: f32, y: f32| -> bool {
|
|
let stem = (34.0..=39.0).contains(&x) && (18.0..=46.0).contains(&y);
|
|
let top = (34.0..=52.0).contains(&x) && (18.0..=23.0).contains(&y);
|
|
let mid = (34.0..=48.0).contains(&x) && (29.5..=34.0).contains(&y);
|
|
stem || top || mid
|
|
};
|
|
|
|
// Premultiplied-alpha accumulation per output pixel so antialiased edges
|
|
// (both the rounded tile and the letters) never fringe dark.
|
|
let mut rgba = vec![0u8; SIZE * SIZE * 4];
|
|
for oy in 0..SIZE {
|
|
for ox in 0..SIZE {
|
|
let (mut ar, mut ag, mut ab, mut aa) = (0.0_f32, 0.0_f32, 0.0_f32, 0.0_f32);
|
|
for sy in 0..SS {
|
|
for sx in 0..SS {
|
|
let x = ox as f32 + (sx as f32 + 0.5) / SS as f32;
|
|
let y = oy as f32 + (sy as f32 + 0.5) / SS as f32;
|
|
let (r, g, b, a) = if inside_o(x, y) || inside_f(x, y) {
|
|
(fg.r(), fg.g(), fg.b(), 255u16)
|
|
} else if inside_bg(x, y) {
|
|
(accent.r(), accent.g(), accent.b(), 255u16)
|
|
} else {
|
|
(0, 0, 0, 0)
|
|
};
|
|
let af = a as f32 / 255.0;
|
|
ar += r as f32 * af;
|
|
ag += g as f32 * af;
|
|
ab += b as f32 * af;
|
|
aa += af;
|
|
}
|
|
}
|
|
let samples = (SS * SS) as f32;
|
|
let idx = (oy * SIZE + ox) * 4;
|
|
let (r, g, b) = if aa > 0.0 {
|
|
(ar / aa, ag / aa, ab / aa)
|
|
} else {
|
|
(0.0, 0.0, 0.0)
|
|
};
|
|
rgba[idx] = r.round() as u8;
|
|
rgba[idx + 1] = g.round() as u8;
|
|
rgba[idx + 2] = b.round() as u8;
|
|
rgba[idx + 3] = (aa / samples * 255.0).round() as u8;
|
|
}
|
|
}
|
|
|
|
egui::IconData {
|
|
rgba,
|
|
width: SIZE as u32,
|
|
height: SIZE as u32,
|
|
}
|
|
}
|