feat: Use gtk4-layer-shell for true Wayland native overlays
This commit is contained in:
parent
8a6a576193
commit
884fe3227a
8 changed files with 749 additions and 419 deletions
945
Cargo.lock
generated
945
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -8,10 +8,13 @@ ashpd = { version = "0.13.13", features = ["screencast"] }
|
|||
crossbeam-channel = "0.5.16"
|
||||
ffmpeg-next = "9.0.0"
|
||||
ffmpeg-sys-next = "9.0.0"
|
||||
gdk = "0.18.2"
|
||||
glib = "0.22.9"
|
||||
global-hotkey = "0.8.0"
|
||||
gtk-layer-shell = "0.8.2"
|
||||
gtk = { version = "0.18.2", package = "gtk" }
|
||||
libc = "0.2.189"
|
||||
memmap2 = "0.9.11"
|
||||
minifb = "0.28.0"
|
||||
mp4 = "0.14.0"
|
||||
pipewire = "0.10.1"
|
||||
ringbuf = "0.5.1"
|
||||
|
|
|
|||
19
scratch/gtk_fix.patch
Normal file
19
scratch/gtk_fix.patch
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
--- src/overlay.rs
|
||||
+++ src/overlay.rs
|
||||
@@ -16,11 +16,11 @@
|
||||
.default_height(80)
|
||||
.build();
|
||||
|
||||
- window.init_layer_shell();
|
||||
- window.set_layer(Layer::Overlay);
|
||||
- window.set_namespace("vrec-overlay");
|
||||
- window.set_margin(gtk_layer_shell::Edge::Top, 40);
|
||||
- window.set_anchor(gtk_layer_shell::Edge::Top, true);
|
||||
+ gtk_layer_shell::init_for_window(&window);
|
||||
+ gtk_layer_shell::set_layer(&window, Layer::Overlay);
|
||||
+ gtk_layer_shell::set_namespace(&window, "vrec-overlay");
|
||||
+ gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Top, 40);
|
||||
+ gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Top, true);
|
||||
|
||||
// Make it transparent and borderless
|
||||
let css_provider = CssProvider::new();
|
||||
19
scratch/gtk_fix2.patch
Normal file
19
scratch/gtk_fix2.patch
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
--- src/overlay.rs
|
||||
+++ src/overlay.rs
|
||||
@@ -16,11 +16,11 @@
|
||||
.default_height(80)
|
||||
.build();
|
||||
|
||||
- gtk_layer_shell::init_for_window(&window);
|
||||
- gtk_layer_shell::set_layer(&window, Layer::Overlay);
|
||||
- gtk_layer_shell::set_namespace(&window, "vrec-overlay");
|
||||
- gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Top, 40);
|
||||
- gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Top, true);
|
||||
+ window.init_layer_shell();
|
||||
+ window.set_layer(Layer::Overlay);
|
||||
+ window.set_layer_shell_namespace("vrec-overlay");
|
||||
+ window.set_layer_shell_margin(gtk_layer_shell::Edge::Top, 40);
|
||||
+ window.set_anchor(gtk_layer_shell::Edge::Top, true);
|
||||
|
||||
// Make it transparent and borderless
|
||||
let css_provider = CssProvider::new();
|
||||
26
src/debug.rs
26
src/debug.rs
|
|
@ -1,26 +0,0 @@
|
|||
use minifb::{Window, WindowOptions};
|
||||
|
||||
pub struct DebugWindow {
|
||||
window: Window,
|
||||
}
|
||||
|
||||
impl DebugWindow {
|
||||
pub fn new(width: usize, height: usize) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let window = Window::new(
|
||||
"vrec debug capture",
|
||||
width,
|
||||
height,
|
||||
WindowOptions::default(),
|
||||
)?;
|
||||
Ok(Self { window })
|
||||
}
|
||||
|
||||
pub fn update(&mut self, buffer: &[u32], width: usize, height: usize) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
self.window.update_with_buffer(buffer, width, height)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_open(&self) -> bool {
|
||||
self.window.is_open() && !self.window.is_key_down(minifb::Key::Escape)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,5 @@ pub mod encoder;
|
|||
pub mod ring;
|
||||
pub mod muxer;
|
||||
pub mod hotkey;
|
||||
pub mod debug;
|
||||
pub mod ipc;
|
||||
pub mod overlay;
|
||||
|
|
|
|||
|
|
@ -1,45 +1,58 @@
|
|||
use minifb::{Window, WindowOptions};
|
||||
use gtk::prelude::*;
|
||||
use gtk::{Application, ApplicationWindow, Label, CssProvider, StyleContext};
|
||||
use gtk_layer_shell::{LayerShell, Layer};
|
||||
use std::time::Duration;
|
||||
|
||||
pub fn show_saved_overlay() {
|
||||
let width = 300;
|
||||
let height = 100;
|
||||
|
||||
let mut window = match Window::new(
|
||||
"VRec Overlay",
|
||||
width,
|
||||
height,
|
||||
WindowOptions {
|
||||
borderless: true,
|
||||
title: false,
|
||||
resize: false,
|
||||
scale: minifb::Scale::X1,
|
||||
scale_mode: minifb::ScaleMode::Stretch,
|
||||
topmost: true,
|
||||
transparency: true,
|
||||
none: false,
|
||||
}
|
||||
) {
|
||||
Ok(win) => win,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to create overlay window: {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Create a semi-transparent green box or just dark gray
|
||||
let buffer: Vec<u32> = vec![0xFF222222; width * height];
|
||||
|
||||
window.set_target_fps(60);
|
||||
|
||||
let start_time = std::time::Instant::now();
|
||||
let display_duration = Duration::from_secs(2);
|
||||
|
||||
while window.is_open() && start_time.elapsed() < display_duration {
|
||||
// We could render text here, but for now we just show a flash/banner.
|
||||
if let Err(e) = window.update_with_buffer(&buffer, width, height) {
|
||||
eprintln!("Overlay update error: {}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let app = Application::builder()
|
||||
.application_id("com.vrec.overlay")
|
||||
.build();
|
||||
|
||||
app.connect_activate(|app| {
|
||||
let window = ApplicationWindow::builder()
|
||||
.application(app)
|
||||
.default_width(300)
|
||||
.default_height(80)
|
||||
.build();
|
||||
|
||||
window.init_layer_shell();
|
||||
window.set_layer(Layer::Overlay);
|
||||
window.set_namespace("vrec-overlay");
|
||||
window.set_layer_shell_margin(gtk_layer_shell::Edge::Top, 40);
|
||||
window.set_anchor(gtk_layer_shell::Edge::Top, true);
|
||||
|
||||
// Make it transparent and borderless
|
||||
let css_provider = CssProvider::new();
|
||||
let css = r#"
|
||||
window {
|
||||
background-color: rgba(30, 30, 30, 0.9);
|
||||
border-radius: 12px;
|
||||
color: #ffffff;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
padding: 15px;
|
||||
box-shadow: 0px 4px 15px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
"#;
|
||||
css_provider.load_from_data(css.as_bytes()).unwrap();
|
||||
StyleContext::add_provider_for_screen(
|
||||
&gdk::Screen::default().unwrap(),
|
||||
&css_provider,
|
||||
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
|
||||
);
|
||||
|
||||
let label = Label::new(Some("✅ Replay Saved!"));
|
||||
window.add(&label);
|
||||
|
||||
window.show_all();
|
||||
|
||||
// Close after 2 seconds
|
||||
let window_clone = window.clone();
|
||||
glib::timeout_add_local(Duration::from_secs(2), move || {
|
||||
window_clone.close();
|
||||
glib::ControlFlow::Break
|
||||
});
|
||||
});
|
||||
|
||||
app.run_with_args(&[] as &[&str]);
|
||||
}
|
||||
|
|
|
|||
58
src/overlay.rs.orig
Normal file
58
src/overlay.rs.orig
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
use gtk::prelude::*;
|
||||
use gtk::{Application, ApplicationWindow, Label, CssProvider, StyleContext};
|
||||
use gtk_layer_shell::{LayerShell, Layer};
|
||||
use std::time::Duration;
|
||||
|
||||
pub fn show_saved_overlay() {
|
||||
let app = Application::builder()
|
||||
.application_id("com.vrec.overlay")
|
||||
.build();
|
||||
|
||||
app.connect_activate(|app| {
|
||||
let window = ApplicationWindow::builder()
|
||||
.application(app)
|
||||
.default_width(300)
|
||||
.default_height(80)
|
||||
.build();
|
||||
|
||||
gtk_layer_shell::init_for_window(&window);
|
||||
gtk_layer_shell::set_layer(&window, Layer::Overlay);
|
||||
gtk_layer_shell::set_namespace(&window, "vrec-overlay");
|
||||
gtk_layer_shell::set_margin(&window, gtk_layer_shell::Edge::Top, 40);
|
||||
gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Top, true);
|
||||
|
||||
// Make it transparent and borderless
|
||||
let css_provider = CssProvider::new();
|
||||
let css = r#"
|
||||
window {
|
||||
background-color: rgba(30, 30, 30, 0.9);
|
||||
border-radius: 12px;
|
||||
color: #ffffff;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
padding: 15px;
|
||||
box-shadow: 0px 4px 15px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
"#;
|
||||
css_provider.load_from_data(css.as_bytes()).unwrap();
|
||||
StyleContext::add_provider_for_screen(
|
||||
&gdk::Screen::default().unwrap(),
|
||||
&css_provider,
|
||||
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
|
||||
);
|
||||
|
||||
let label = Label::new(Some("✅ Replay Saved!"));
|
||||
window.add(&label);
|
||||
|
||||
window.show_all();
|
||||
|
||||
// Close after 2 seconds
|
||||
let window_clone = window.clone();
|
||||
glib::timeout_add_local(Duration::from_secs(2), move || {
|
||||
window_clone.close();
|
||||
glib::ControlFlow::Break
|
||||
});
|
||||
});
|
||||
|
||||
app.run_with_args(&[] as &[&str]);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue