mirror of
https://github.com/rustdesk/qemu-display.git
synced 2025-08-17 16:25:39 +00:00
gtk: pass the scanout down to ConsoleArea
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
This commit is contained in:
parent
54f299599f
commit
d1e0ba0dde
@ -32,12 +32,12 @@ pub trait Console {
|
|||||||
|
|
||||||
#[derive(derivative::Derivative)]
|
#[derive(derivative::Derivative)]
|
||||||
#[derivative(Debug)]
|
#[derivative(Debug)]
|
||||||
pub struct Console<'c> {
|
pub struct Console {
|
||||||
#[derivative(Debug = "ignore")]
|
#[derivative(Debug = "ignore")]
|
||||||
proxy: ConsoleProxy<'c>,
|
proxy: ConsoleProxy<'static>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'c> Console<'c> {
|
impl Console {
|
||||||
pub fn new(conn: &zbus::Connection, idx: u32) -> Result<Self> {
|
pub fn new(conn: &zbus::Connection, idx: u32) -> Result<Self> {
|
||||||
let proxy = ConsoleProxy::new_for_owned_path(
|
let proxy = ConsoleProxy::new_for_owned_path(
|
||||||
conn.clone(),
|
conn.clone(),
|
||||||
@ -89,7 +89,7 @@ impl<'c> Console<'c> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "glib")]
|
#[cfg(feature = "glib")]
|
||||||
impl<'c> Console<'c> {
|
impl Console {
|
||||||
pub fn glib_listen(&self) -> Result<glib::Receiver<Event>> {
|
pub fn glib_listen(&self) -> Result<glib::Receiver<Event>> {
|
||||||
let (p0, p1) = UnixStream::pair()?;
|
let (p0, p1) = UnixStream::pair()?;
|
||||||
let (tx, rx) = glib::MainContext::channel(glib::source::Priority::default());
|
let (tx, rx) = glib::MainContext::channel(glib::source::Priority::default());
|
||||||
|
@ -2,9 +2,29 @@ use std::cell::RefCell;
|
|||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::mpsc::{SendError, Sender};
|
use std::sync::mpsc::{SendError, Sender};
|
||||||
|
use std::ops::Drop;
|
||||||
|
|
||||||
use zbus::{dbus_interface, export::zvariant::Fd};
|
use zbus::{dbus_interface, export::zvariant::Fd};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Scanout {
|
||||||
|
fd: RawFd,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
stride: u32,
|
||||||
|
fourcc: u32,
|
||||||
|
modifier: u64,
|
||||||
|
y0_top: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Scanout {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.fd >= 0 {
|
||||||
|
unsafe { libc::close(self.fd); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: replace events mpsc with async traits
|
// TODO: replace events mpsc with async traits
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
@ -18,15 +38,6 @@ pub enum Event {
|
|||||||
w: i32,
|
w: i32,
|
||||||
h: i32,
|
h: i32,
|
||||||
},
|
},
|
||||||
Scanout {
|
|
||||||
fd: RawFd,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
stride: u32,
|
|
||||||
fourcc: u32,
|
|
||||||
modifier: u64,
|
|
||||||
y0_top: bool,
|
|
||||||
},
|
|
||||||
MouseSet {
|
MouseSet {
|
||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
@ -39,6 +50,7 @@ pub enum Event {
|
|||||||
hot_y: i32,
|
hot_y: i32,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
},
|
},
|
||||||
|
Scanout(Scanout),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait EventSender {
|
pub(crate) trait EventSender {
|
||||||
@ -85,7 +97,7 @@ impl<E: 'static + EventSender> Listener<E> {
|
|||||||
y0_top: bool,
|
y0_top: bool,
|
||||||
) {
|
) {
|
||||||
let fd = unsafe { libc::dup(fd.as_raw_fd()) };
|
let fd = unsafe { libc::dup(fd.as_raw_fd()) };
|
||||||
self.send(Event::Scanout {
|
self.send(Event::Scanout(Scanout {
|
||||||
fd,
|
fd,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
@ -93,7 +105,7 @@ impl<E: 'static + EventSender> Listener<E> {
|
|||||||
fourcc,
|
fourcc,
|
||||||
modifier,
|
modifier,
|
||||||
y0_top,
|
y0_top,
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_set(&mut self, x: i32, y: i32, on: i32) {
|
fn mouse_set(&mut self, x: i32, y: i32, on: i32) {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<child>
|
<child>
|
||||||
<object class="GtkNotebook">
|
<object class="GtkNotebook">
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkGLArea" id="glarea"/>
|
<object class="QemuConsoleArea" id="area"/>
|
||||||
</child>
|
</child>
|
||||||
<child type="tab">
|
<child type="tab">
|
||||||
<object class="GtkLabel" id="notebook-tab">
|
<object class="GtkLabel" id="notebook-tab">
|
||||||
|
@ -1,18 +1,25 @@
|
|||||||
use glib::subclass::prelude::*;
|
use glib::subclass::prelude::*;
|
||||||
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk::subclass::widget::WidgetImplExt;
|
use gtk::subclass::widget::WidgetImplExt;
|
||||||
use gtk::{gio, glib, CompositeTemplate};
|
use gtk::{glib, CompositeTemplate};
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
|
|
||||||
|
use qemu_display_listener::{Console, Event};
|
||||||
|
|
||||||
mod imp {
|
mod imp {
|
||||||
use super::*;
|
use super::*;
|
||||||
use glib::subclass;
|
use glib::subclass;
|
||||||
use gtk::subclass::prelude::*;
|
use gtk::subclass::prelude::*;
|
||||||
|
|
||||||
#[derive(Debug, CompositeTemplate)]
|
#[derive(Debug, CompositeTemplate, Default)]
|
||||||
#[template(resource = "/org/qemu/gtk4/console.ui")]
|
#[template(resource = "/org/qemu/gtk4/console.ui")]
|
||||||
pub struct QemuConsole {
|
pub struct QemuConsole {
|
||||||
|
#[template_child]
|
||||||
|
pub area: TemplateChild<crate::console_area::QemuConsoleArea>,
|
||||||
#[template_child]
|
#[template_child]
|
||||||
pub label: TemplateChild<gtk::Label>,
|
pub label: TemplateChild<gtk::Label>,
|
||||||
|
pub console: OnceCell<Console>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectSubclass for QemuConsole {
|
impl ObjectSubclass for QemuConsole {
|
||||||
@ -26,9 +33,7 @@ mod imp {
|
|||||||
glib::object_subclass!();
|
glib::object_subclass!();
|
||||||
|
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self::default()
|
||||||
label: TemplateChild::default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
fn class_init(klass: &mut Self::Class) {
|
||||||
@ -65,3 +70,27 @@ mod imp {
|
|||||||
glib::wrapper! {
|
glib::wrapper! {
|
||||||
pub struct QemuConsole(ObjectSubclass<imp::QemuConsole>) @extends gtk::Widget;
|
pub struct QemuConsole(ObjectSubclass<imp::QemuConsole>) @extends gtk::Widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl QemuConsole {
|
||||||
|
pub fn set_qemu_console(&self, console: Console) {
|
||||||
|
let priv_ = imp::QemuConsole::from_instance(self);
|
||||||
|
let rx = console
|
||||||
|
.glib_listen()
|
||||||
|
.expect("Failed to listen to the console");
|
||||||
|
rx.attach(
|
||||||
|
None,
|
||||||
|
clone!(@weak self as con => move |t| {
|
||||||
|
let con = imp::QemuConsole::from_instance(&con);
|
||||||
|
match t {
|
||||||
|
Event::Scanout(s) => {
|
||||||
|
con.label.set_label(&format!("{:?}", s));
|
||||||
|
con.area.set_scanout(s);
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
Continue(true)
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
priv_.console.set(console).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
72
qemu-gtk4/src/console_area.rs
Normal file
72
qemu-gtk4/src/console_area.rs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
use std::cell::Cell;
|
||||||
|
use glib::subclass::prelude::*;
|
||||||
|
use glib::clone;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use gtk::{glib, graphene, gdk};
|
||||||
|
|
||||||
|
use qemu_display_listener::Scanout;
|
||||||
|
|
||||||
|
mod imp {
|
||||||
|
use super::*;
|
||||||
|
use glib::subclass;
|
||||||
|
use gtk::subclass::prelude::*;
|
||||||
|
|
||||||
|
pub struct QemuConsoleArea {
|
||||||
|
pub scanout: Cell<Option<Scanout>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectSubclass for QemuConsoleArea {
|
||||||
|
const NAME: &'static str = "QemuConsoleArea";
|
||||||
|
type Type = super::QemuConsoleArea;
|
||||||
|
type ParentType = gtk::Widget;
|
||||||
|
type Interfaces = ();
|
||||||
|
type Instance = subclass::simple::InstanceStruct<Self>;
|
||||||
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
glib::object_subclass!();
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
scanout: Cell::new(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectImpl for QemuConsoleArea {
|
||||||
|
fn constructed(&self, obj: &Self::Type) {
|
||||||
|
self.parent_constructed(obj);
|
||||||
|
|
||||||
|
let ec = gtk::EventControllerLegacy::new();
|
||||||
|
// XXX: where are the key events?
|
||||||
|
// ec.set_propagation_phase(gtk::PropagationPhase::Bubble);
|
||||||
|
obj.add_controller(&ec);
|
||||||
|
ec.connect_event(clone!(@weak obj => move |_, e| {
|
||||||
|
dbg!(e);
|
||||||
|
true
|
||||||
|
}));
|
||||||
|
obj.set_focusable(true);
|
||||||
|
obj.set_focus_on_click(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WidgetImpl for QemuConsoleArea {
|
||||||
|
fn snapshot(&self, widget: &Self::Type, snapshot: >k::Snapshot) {
|
||||||
|
let (width, height) = (widget.get_width() as f32, widget.get_height() as f32);
|
||||||
|
let whole = &graphene::Rect::new(0_f32, 0_f32, width, height);
|
||||||
|
// TODO: make this a CSS style?
|
||||||
|
snapshot.append_color(&gdk::RGBA::black(), whole);
|
||||||
|
//snapshot.append_texture(priv_.texture, whole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glib::wrapper! {
|
||||||
|
pub struct QemuConsoleArea(ObjectSubclass<imp::QemuConsoleArea>) @extends gtk::Widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QemuConsoleArea {
|
||||||
|
pub fn set_scanout(&self, s: Scanout) {
|
||||||
|
let priv_ = imp::QemuConsoleArea::from_instance(self);
|
||||||
|
priv_.scanout.replace(Some(s));
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ mod application;
|
|||||||
mod config;
|
mod config;
|
||||||
mod window;
|
mod window;
|
||||||
mod console;
|
mod console;
|
||||||
|
mod console_area;
|
||||||
|
|
||||||
use application::QemuApplication;
|
use application::QemuApplication;
|
||||||
use config::{GETTEXT_PACKAGE, LOCALEDIR, RESOURCES_FILE};
|
use config::{GETTEXT_PACKAGE, LOCALEDIR, RESOURCES_FILE};
|
||||||
|
@ -21,6 +21,8 @@ run_command(
|
|||||||
sources = files(
|
sources = files(
|
||||||
'application.rs',
|
'application.rs',
|
||||||
'config.rs',
|
'config.rs',
|
||||||
|
'console.rs',
|
||||||
|
'console_area.rs',
|
||||||
'main.rs',
|
'main.rs',
|
||||||
'window.rs',
|
'window.rs',
|
||||||
)
|
)
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
use crate::application::QemuApplication;
|
use crate::application::QemuApplication;
|
||||||
use crate::console::QemuConsole;
|
use crate::console::QemuConsole;
|
||||||
use crate::config::{APP_ID, PROFILE};
|
use crate::config::{APP_ID, PROFILE};
|
||||||
use glib::clone;
|
|
||||||
use glib::signal::Inhibit;
|
use glib::signal::Inhibit;
|
||||||
use gtk::subclass::prelude::*;
|
use gtk::subclass::prelude::*;
|
||||||
use gtk::{self, prelude::*};
|
use gtk::{self, prelude::*};
|
||||||
use gtk::{gio, glib, CompositeTemplate};
|
use gtk::{gio, glib, CompositeTemplate};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
|
|
||||||
use qemu_display_listener::{Console as ConsoleListener, Event};
|
use qemu_display_listener::Console;
|
||||||
|
|
||||||
mod imp {
|
mod imp {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -21,10 +20,6 @@ mod imp {
|
|||||||
pub headerbar: TemplateChild<gtk::HeaderBar>,
|
pub headerbar: TemplateChild<gtk::HeaderBar>,
|
||||||
#[template_child]
|
#[template_child]
|
||||||
pub console: TemplateChild<QemuConsole>,
|
pub console: TemplateChild<QemuConsole>,
|
||||||
// #[template_child]
|
|
||||||
// pub glarea: TemplateChild<gtk::GLArea>,
|
|
||||||
// #[template_child]
|
|
||||||
// pub label: TemplateChild<gtk::Label>,
|
|
||||||
pub settings: gio::Settings,
|
pub settings: gio::Settings,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,8 +37,6 @@ mod imp {
|
|||||||
Self {
|
Self {
|
||||||
headerbar: TemplateChild::default(),
|
headerbar: TemplateChild::default(),
|
||||||
console: TemplateChild::default(),
|
console: TemplateChild::default(),
|
||||||
// label: TemplateChild::default(),
|
|
||||||
// glarea: TemplateChild::default(),
|
|
||||||
settings: gio::Settings::new(APP_ID),
|
settings: gio::Settings::new(APP_ID),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,37 +89,15 @@ glib::wrapper! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl QemuApplicationWindow {
|
impl QemuApplicationWindow {
|
||||||
pub fn new(app: &QemuApplication, console: ConsoleListener) -> Self {
|
pub fn new(app: &QemuApplication, console: Console) -> Self {
|
||||||
let window: Self = glib::Object::new(&[]).expect("Failed to create QemuApplicationWindow");
|
let window: Self = glib::Object::new(&[]).expect("Failed to create QemuApplicationWindow");
|
||||||
window.set_application(Some(app));
|
window.set_application(Some(app));
|
||||||
|
|
||||||
let win = &imp::QemuApplicationWindow::from_instance(&window);
|
let win = &imp::QemuApplicationWindow::from_instance(&window);
|
||||||
// win.glarea.connect_render(clone!(@weak window as win => move |area, ctxt| {
|
win.console.set_qemu_console(console);
|
||||||
// dbg!("render");
|
|
||||||
// Inhibit(false)
|
|
||||||
// }));
|
|
||||||
|
|
||||||
// Set icons for shell
|
// Set icons for shell
|
||||||
gtk::Window::set_default_icon_name(APP_ID);
|
gtk::Window::set_default_icon_name(APP_ID);
|
||||||
|
|
||||||
let rx = console
|
|
||||||
.glib_listen()
|
|
||||||
.expect("Failed to listen to the console");
|
|
||||||
rx.attach(
|
|
||||||
None,
|
|
||||||
clone!(@weak window as win => move |t| {
|
|
||||||
let win = &imp::QemuApplicationWindow::from_instance(&win);
|
|
||||||
match t {
|
|
||||||
Event::Scanout { .. } => {
|
|
||||||
// win.label.set_text(&format!("{:?}", t));
|
|
||||||
// win.glarea.queue_render();
|
|
||||||
}
|
|
||||||
_ => ()
|
|
||||||
}
|
|
||||||
Continue(true)
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
window
|
window
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user