Add native self-hosted instance connection to fluxer_desktop
Trimmed monorepo checkout (fluxer_desktop + packages/voice_engine_v2 + tools/ci) with a "Connect to a Different Server" menu item and popout that lets the desktop app switch to any self-hosted Fluxer instance, plus fixes for well-known discovery on single-domain self-hosted deployments and a false-positive ERR_ABORTED on same-origin client redirects during the switch. Defaults to chat.fluxr.chat and uses an isolated userData directory from the official build.
This commit is contained in:
+156
@@ -0,0 +1,156 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "opengl-swapbuffers-fixture"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"windows",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
|
||||
dependencies = [
|
||||
"windows-collections",
|
||||
"windows-core",
|
||||
"windows-future",
|
||||
"windows-numerics",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-collections"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-link",
|
||||
"windows-result",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-future"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
"windows-threading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-numerics"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
|
||||
dependencies = [
|
||||
"windows-core",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-threading"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
[package]
|
||||
name = "opengl-swapbuffers-fixture"
|
||||
version = "0.0.0"
|
||||
edition = "2024"
|
||||
license = "AGPL-3.0-or-later"
|
||||
publish = false
|
||||
|
||||
# Empty workspace table keeps this fixture out of any parent Cargo workspace,
|
||||
# matching the sibling crates (hook/, vulkan-layer/) in this package.
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
|
||||
[[bin]]
|
||||
name = "opengl-swapbuffers-fixture"
|
||||
path = "src/main.rs"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
windows = {version = "0.62.2", features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Graphics_Gdi",
|
||||
"Win32_Graphics_OpenGL",
|
||||
"Win32_System_LibraryLoader",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
]}
|
||||
windows-core = "0.62.2"
|
||||
+484
@@ -0,0 +1,484 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn main() {
|
||||
eprintln!("opengl-swapbuffers-fixture is only functional on Windows");
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn main() -> windows_core::Result<()> {
|
||||
windows_impl::run()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
mod pattern {
|
||||
pub const BARS: [(u8, u8, u8); 8] = [
|
||||
(255, 255, 255),
|
||||
(255, 255, 0),
|
||||
(0, 255, 255),
|
||||
(0, 255, 0),
|
||||
(255, 0, 255),
|
||||
(255, 0, 0),
|
||||
(0, 0, 255),
|
||||
(0, 0, 0),
|
||||
];
|
||||
|
||||
pub const COUNTER_BLOCK: u32 = 16;
|
||||
|
||||
pub fn bar_colour(x: u32, width: u32) -> (u8, u8, u8) {
|
||||
let width = width.max(1);
|
||||
let index = ((x as u64 * BARS.len() as u64) / width as u64) as usize;
|
||||
BARS[index.min(BARS.len() - 1)]
|
||||
}
|
||||
|
||||
pub fn bar_x_range(index: usize, width: u32) -> (u32, u32) {
|
||||
let width = width.max(1) as u64;
|
||||
let len = BARS.len() as u64;
|
||||
let start = (index as u64 * width) / len;
|
||||
let end = ((index as u64 + 1) * width) / len;
|
||||
(start as u32, end.min(width) as u32)
|
||||
}
|
||||
|
||||
pub fn counter_colour(frame_index: u64) -> (u8, u8, u8) {
|
||||
let low = (frame_index & 0x00FF_FFFF) as u32;
|
||||
(
|
||||
(low & 0xFF) as u8,
|
||||
((low >> 8) & 0xFF) as u8,
|
||||
((low >> 16) & 0xFF) as u8,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn clear_colour(frame_index: u64) -> [f32; 3] {
|
||||
let (r, g, b) = BARS[(frame_index % BARS.len() as u64) as usize];
|
||||
[r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod cli {
|
||||
pub struct Options {
|
||||
pub frames: Option<u64>,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub borderless: bool,
|
||||
pub layer: bool,
|
||||
pub stress_pack_state: bool,
|
||||
pub resize_at: Option<u64>,
|
||||
pub frame_delay_ms: u64,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
frames: None,
|
||||
width: 1280,
|
||||
height: 720,
|
||||
borderless: false,
|
||||
layer: false,
|
||||
stress_pack_state: false,
|
||||
resize_at: None,
|
||||
frame_delay_ms: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Options {
|
||||
pub fn from_env() -> Self {
|
||||
let mut options = Options::default();
|
||||
let mut args = std::env::args().skip(1);
|
||||
while let Some(arg) = args.next() {
|
||||
match arg.as_str() {
|
||||
"--frames" => {
|
||||
if let Some(value) = args.next().and_then(|v| v.parse().ok()) {
|
||||
options.frames = Some(value);
|
||||
}
|
||||
}
|
||||
"--width" => {
|
||||
if let Some(value) = args.next().and_then(|v| v.parse().ok()) {
|
||||
options.width = value;
|
||||
}
|
||||
}
|
||||
"--height" => {
|
||||
if let Some(value) = args.next().and_then(|v| v.parse().ok()) {
|
||||
options.height = value;
|
||||
}
|
||||
}
|
||||
"--borderless" => options.borderless = true,
|
||||
"--windowed" => options.borderless = false,
|
||||
"--layer" => options.layer = true,
|
||||
"--stress-pack-state" => options.stress_pack_state = true,
|
||||
"--resize-at" => {
|
||||
if let Some(value) = args.next().and_then(|v| v.parse().ok()) {
|
||||
options.resize_at = Some(value);
|
||||
}
|
||||
}
|
||||
"--frame-delay-ms" => {
|
||||
if let Some(value) = args.next().and_then(|v| v.parse().ok()) {
|
||||
options.frame_delay_ms = value;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
options
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
mod windows_impl {
|
||||
use super::cli::Options;
|
||||
use super::pattern;
|
||||
use windows::Win32::Foundation::{HWND, LPARAM, LRESULT, WPARAM};
|
||||
use windows::Win32::Graphics::Gdi::{GetDC, HDC, ReleaseDC, WGL_SWAP_MAIN_PLANE};
|
||||
use windows::Win32::Graphics::OpenGL::{
|
||||
ChoosePixelFormat, GL_COLOR_BUFFER_BIT, GL_FRONT, GL_PACK_ALIGNMENT, GL_PACK_LSB_FIRST,
|
||||
GL_PACK_ROW_LENGTH, GL_PACK_SKIP_PIXELS, GL_PACK_SKIP_ROWS, GL_PACK_SWAP_BYTES,
|
||||
GL_READ_BUFFER, GL_SCISSOR_TEST, PFD_DOUBLEBUFFER, PFD_DRAW_TO_WINDOW, PFD_MAIN_PLANE,
|
||||
PFD_SUPPORT_OPENGL, PFD_TYPE_RGBA, PIXELFORMATDESCRIPTOR, SetPixelFormat, SwapBuffers,
|
||||
glClear, glClearColor, glDisable, glEnable, glGetIntegerv, glPixelStorei, glReadBuffer,
|
||||
glScissor, glViewport, wglCreateContext, wglDeleteContext, wglGetProcAddress,
|
||||
wglMakeCurrent, wglSwapLayerBuffers,
|
||||
};
|
||||
use windows::Win32::System::LibraryLoader::GetModuleHandleW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::{
|
||||
CS_HREDRAW, CS_OWNDC, CS_VREDRAW, CW_USEDEFAULT, CreateWindowExW, DefWindowProcW,
|
||||
DispatchMessageW, GetClientRect, MSG, MoveWindow, PM_REMOVE, PeekMessageW, PostQuitMessage,
|
||||
RegisterClassExW, SW_SHOW, ShowWindow, TranslateMessage, WM_DESTROY, WM_QUIT, WNDCLASSEXW,
|
||||
WS_OVERLAPPEDWINDOW, WS_POPUP, WS_VISIBLE,
|
||||
};
|
||||
use windows_core::{PCSTR, Result, w};
|
||||
|
||||
const GL_PIXEL_PACK_BUFFER: u32 = 0x88EB;
|
||||
const GL_PIXEL_PACK_BUFFER_BINDING: u32 = 0x88ED;
|
||||
const GL_STREAM_READ: u32 = 0x88E1;
|
||||
|
||||
type GlGenBuffersFn = unsafe extern "system" fn(i32, *mut u32);
|
||||
type GlBindBufferFn = unsafe extern "system" fn(u32, u32);
|
||||
type GlBufferDataFn = unsafe extern "system" fn(u32, isize, *const core::ffi::c_void, u32);
|
||||
type GlDeleteBuffersFn = unsafe extern "system" fn(i32, *const u32);
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct BufferProcs {
|
||||
gen_buffers: GlGenBuffersFn,
|
||||
bind_buffer: GlBindBufferFn,
|
||||
buffer_data: GlBufferDataFn,
|
||||
delete_buffers: GlDeleteBuffersFn,
|
||||
}
|
||||
|
||||
struct PackStateStress {
|
||||
procs: Option<BufferProcs>,
|
||||
pbo: u32,
|
||||
}
|
||||
|
||||
impl PackStateStress {
|
||||
unsafe fn new() -> Self {
|
||||
let Some(procs) = (unsafe { BufferProcs::load() }) else {
|
||||
return Self {
|
||||
procs: None,
|
||||
pbo: 0,
|
||||
};
|
||||
};
|
||||
let mut pbo = 0u32;
|
||||
unsafe {
|
||||
(procs.gen_buffers)(1, &mut pbo);
|
||||
}
|
||||
if pbo != 0 {
|
||||
let storage = [0u8; 16];
|
||||
unsafe {
|
||||
(procs.bind_buffer)(GL_PIXEL_PACK_BUFFER, pbo);
|
||||
(procs.buffer_data)(
|
||||
GL_PIXEL_PACK_BUFFER,
|
||||
storage.len() as isize,
|
||||
storage.as_ptr().cast(),
|
||||
GL_STREAM_READ,
|
||||
);
|
||||
(procs.bind_buffer)(GL_PIXEL_PACK_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
Self {
|
||||
procs: Some(procs),
|
||||
pbo,
|
||||
}
|
||||
}
|
||||
|
||||
fn pbo(&self) -> Option<u32> {
|
||||
(self.pbo != 0).then_some(self.pbo)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PackStateStress {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if let Some(procs) = self.procs {
|
||||
if self.pbo != 0 {
|
||||
(procs.bind_buffer)(GL_PIXEL_PACK_BUFFER, 0);
|
||||
(procs.delete_buffers)(1, &self.pbo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BufferProcs {
|
||||
unsafe fn load() -> Option<Self> {
|
||||
Some(Self {
|
||||
gen_buffers: unsafe { load_gl_proc(b"glGenBuffers\0")? },
|
||||
bind_buffer: unsafe { load_gl_proc(b"glBindBuffer\0")? },
|
||||
buffer_data: unsafe { load_gl_proc(b"glBufferData\0")? },
|
||||
delete_buffers: unsafe { load_gl_proc(b"glDeleteBuffers\0")? },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn load_gl_proc<T>(name: &'static [u8]) -> Option<T> {
|
||||
let proc = unsafe { wglGetProcAddress(PCSTR(name.as_ptr()))? };
|
||||
let address = proc as usize;
|
||||
if address <= 3 || address == usize::MAX {
|
||||
return None;
|
||||
}
|
||||
Some(unsafe { std::mem::transmute_copy::<_, T>(&proc) })
|
||||
}
|
||||
|
||||
extern "system" fn wnd_proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
|
||||
unsafe {
|
||||
match msg {
|
||||
WM_DESTROY => {
|
||||
PostQuitMessage(0);
|
||||
LRESULT(0)
|
||||
}
|
||||
_ => DefWindowProcW(hwnd, msg, wparam, lparam),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn create_window(width: u32, height: u32, borderless: bool) -> Result<HWND> {
|
||||
unsafe {
|
||||
let instance = GetModuleHandleW(None)?;
|
||||
let class_name = w!("FluxerOpenGLFixture");
|
||||
let wc = WNDCLASSEXW {
|
||||
cbSize: size_of::<WNDCLASSEXW>() as u32,
|
||||
style: CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
|
||||
lpfnWndProc: Some(wnd_proc),
|
||||
hInstance: instance.into(),
|
||||
lpszClassName: class_name,
|
||||
..Default::default()
|
||||
};
|
||||
RegisterClassExW(&wc);
|
||||
let style = if borderless {
|
||||
WS_POPUP | WS_VISIBLE
|
||||
} else {
|
||||
WS_OVERLAPPEDWINDOW | WS_VISIBLE
|
||||
};
|
||||
let hwnd = CreateWindowExW(
|
||||
Default::default(),
|
||||
class_name,
|
||||
w!("Fluxer OpenGL SwapBuffers Fixture"),
|
||||
style,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
width as i32,
|
||||
height as i32,
|
||||
None,
|
||||
None,
|
||||
Some(instance.into()),
|
||||
None,
|
||||
)?;
|
||||
let _ = ShowWindow(hwnd, SW_SHOW);
|
||||
Ok(hwnd)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn set_pixel_format(hdc: HDC) -> Result<()> {
|
||||
unsafe {
|
||||
let mut pfd = PIXELFORMATDESCRIPTOR {
|
||||
nSize: size_of::<PIXELFORMATDESCRIPTOR>() as u16,
|
||||
nVersion: 1,
|
||||
dwFlags: PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
|
||||
iPixelType: PFD_TYPE_RGBA,
|
||||
cColorBits: 32,
|
||||
cDepthBits: 24,
|
||||
cStencilBits: 8,
|
||||
iLayerType: PFD_MAIN_PLANE.0 as u8,
|
||||
..Default::default()
|
||||
};
|
||||
let format = ChoosePixelFormat(hdc, &pfd);
|
||||
if format == 0 {
|
||||
return Err(windows_core::Error::from_thread());
|
||||
}
|
||||
SetPixelFormat(hdc, format, &mut pfd)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn clear_rect(colour: (u8, u8, u8)) {
|
||||
unsafe {
|
||||
let (r, g, b) = colour;
|
||||
glClearColor(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn render_pattern(frame_index: u64, width: u32, height: u32) {
|
||||
unsafe {
|
||||
glViewport(0, 0, width as i32, height as i32);
|
||||
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
for index in 0..pattern::BARS.len() {
|
||||
let (start, end) = pattern::bar_x_range(index, width);
|
||||
if end <= start {
|
||||
continue;
|
||||
}
|
||||
glScissor(start as i32, 0, (end - start) as i32, height as i32);
|
||||
clear_rect(pattern::BARS[index]);
|
||||
}
|
||||
|
||||
let block = pattern::COUNTER_BLOCK.min(width).min(height);
|
||||
if block > 0 {
|
||||
let y = height - block;
|
||||
glScissor(0, y as i32, block as i32, block as i32);
|
||||
clear_rect(pattern::counter_colour(frame_index));
|
||||
}
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn client_size(hwnd: HWND) -> (u32, u32) {
|
||||
unsafe {
|
||||
let mut rect = Default::default();
|
||||
let _ = GetClientRect(hwnd, &mut rect);
|
||||
(
|
||||
((rect.right - rect.left).max(1)) as u32,
|
||||
((rect.bottom - rect.top).max(1)) as u32,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn stress_pack_state(stress: &PackStateStress) {
|
||||
unsafe {
|
||||
glReadBuffer(GL_FRONT);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
|
||||
glPixelStorei(GL_PACK_SKIP_ROWS, 0);
|
||||
glPixelStorei(GL_PACK_SWAP_BYTES, 1);
|
||||
glPixelStorei(GL_PACK_LSB_FIRST, 1);
|
||||
if let (Some(procs), Some(pbo)) = (stress.procs, stress.pbo()) {
|
||||
(procs.bind_buffer)(GL_PIXEL_PACK_BUFFER, pbo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn assert_stress_pack_state(stress: &PackStateStress) {
|
||||
unsafe {
|
||||
let mut read_buffer = 0i32;
|
||||
let mut pack_alignment = 0i32;
|
||||
let mut pack_row_length = 0i32;
|
||||
let mut pack_skip_pixels = 0i32;
|
||||
let mut pack_skip_rows = 0i32;
|
||||
let mut pack_swap_bytes = 0i32;
|
||||
let mut pack_lsb_first = 0i32;
|
||||
let mut pack_buffer = 0i32;
|
||||
|
||||
glGetIntegerv(GL_READ_BUFFER, &mut read_buffer);
|
||||
glGetIntegerv(GL_PACK_ALIGNMENT, &mut pack_alignment);
|
||||
glGetIntegerv(GL_PACK_ROW_LENGTH, &mut pack_row_length);
|
||||
glGetIntegerv(GL_PACK_SKIP_PIXELS, &mut pack_skip_pixels);
|
||||
glGetIntegerv(GL_PACK_SKIP_ROWS, &mut pack_skip_rows);
|
||||
glGetIntegerv(GL_PACK_SWAP_BYTES, &mut pack_swap_bytes);
|
||||
glGetIntegerv(GL_PACK_LSB_FIRST, &mut pack_lsb_first);
|
||||
glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &mut pack_buffer);
|
||||
|
||||
assert_eq!(
|
||||
read_buffer as u32, GL_FRONT,
|
||||
"GL_READ_BUFFER was not restored"
|
||||
);
|
||||
assert_eq!(pack_alignment, 1, "GL_PACK_ALIGNMENT was not restored");
|
||||
assert_eq!(pack_row_length, 0, "GL_PACK_ROW_LENGTH was not restored");
|
||||
assert_eq!(pack_skip_pixels, 0, "GL_PACK_SKIP_PIXELS was not restored");
|
||||
assert_eq!(pack_skip_rows, 0, "GL_PACK_SKIP_ROWS was not restored");
|
||||
assert_eq!(pack_swap_bytes, 1, "GL_PACK_SWAP_BYTES was not restored");
|
||||
assert_eq!(pack_lsb_first, 1, "GL_PACK_LSB_FIRST was not restored");
|
||||
if let Some(pbo) = stress.pbo() {
|
||||
assert_eq!(
|
||||
pack_buffer as u32, pbo,
|
||||
"GL_PIXEL_PACK_BUFFER_BINDING was not restored"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run() -> Result<()> {
|
||||
let options = Options::from_env();
|
||||
unsafe {
|
||||
let hwnd = create_window(options.width, options.height, options.borderless)?;
|
||||
|
||||
println!("HWND={}", hwnd.0 as isize);
|
||||
use std::io::Write;
|
||||
let _ = std::io::stdout().flush();
|
||||
|
||||
let hdc = GetDC(Some(hwnd));
|
||||
set_pixel_format(hdc)?;
|
||||
let context = wglCreateContext(hdc)?;
|
||||
wglMakeCurrent(hdc, context)?;
|
||||
let pack_state_stress = PackStateStress::new();
|
||||
|
||||
let mut frame_index: u64 = 0;
|
||||
let mut msg = MSG::default();
|
||||
let mut resized = false;
|
||||
'render: loop {
|
||||
while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() {
|
||||
if msg.message == WM_QUIT {
|
||||
break 'render;
|
||||
}
|
||||
let _ = TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
|
||||
if let Some(resize_at) = options.resize_at {
|
||||
if !resized && frame_index >= resize_at {
|
||||
let width = options.width.saturating_add(160).max(1) as i32;
|
||||
let height = options.height.saturating_add(96).max(1) as i32;
|
||||
let _ = MoveWindow(hwnd, 0, 0, width, height, true);
|
||||
resized = true;
|
||||
}
|
||||
}
|
||||
|
||||
let (width, height) = client_size(hwnd);
|
||||
render_pattern(frame_index, width, height);
|
||||
if options.stress_pack_state {
|
||||
stress_pack_state(&pack_state_stress);
|
||||
}
|
||||
|
||||
if options.layer {
|
||||
wglSwapLayerBuffers(hdc, WGL_SWAP_MAIN_PLANE)?;
|
||||
} else {
|
||||
SwapBuffers(hdc)?;
|
||||
}
|
||||
|
||||
if options.stress_pack_state {
|
||||
assert_stress_pack_state(&pack_state_stress);
|
||||
}
|
||||
|
||||
frame_index += 1;
|
||||
if options.frame_delay_ms > 0 {
|
||||
std::thread::sleep(std::time::Duration::from_millis(options.frame_delay_ms));
|
||||
}
|
||||
if let Some(limit) = options.frames {
|
||||
if frame_index >= limit {
|
||||
break 'render;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _ = wglMakeCurrent(
|
||||
HDC::default(),
|
||||
windows::Win32::Graphics::OpenGL::HGLRC::default(),
|
||||
);
|
||||
let _ = wglDeleteContext(context);
|
||||
ReleaseDC(Some(hwnd), hdc);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user