1
0
mirror of https://gitlab.com/marcandre.lureau/qemu-display.git synced 2025-04-01 20:14:13 +00:00

gtk: pass the scanout down to ConsoleArea

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
This commit is contained in:
Marc-André Lureau 2021-02-09 22:57:59 +04:00
parent 54f299599f
commit d1e0ba0dde
8 changed files with 140 additions and 53 deletions

View File

@ -32,12 +32,12 @@ pub trait Console {
#[derive(derivative::Derivative)]
#[derivative(Debug)]
pub struct Console<'c> {
pub struct Console {
#[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> {
let proxy = ConsoleProxy::new_for_owned_path(
conn.clone(),
@ -89,7 +89,7 @@ impl<'c> Console<'c> {
}
#[cfg(feature = "glib")]
impl<'c> Console<'c> {
impl Console {
pub fn glib_listen(&self) -> Result<glib::Receiver<Event>> {
let (p0, p1) = UnixStream::pair()?;
let (tx, rx) = glib::MainContext::channel(glib::source::Priority::default());

View File

@ -2,9 +2,29 @@ use std::cell::RefCell;
use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::Rc;
use std::sync::mpsc::{SendError, Sender};
use std::ops::Drop;
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
#[derive(Debug)]
pub enum Event {
@ -18,15 +38,6 @@ pub enum Event {
w: i32,
h: i32,
},
Scanout {
fd: RawFd,
width: u32,
height: u32,
stride: u32,
fourcc: u32,
modifier: u64,
y0_top: bool,
},
MouseSet {
x: i32,
y: i32,
@ -39,6 +50,7 @@ pub enum Event {
hot_y: i32,
data: Vec<u8>,
},
Scanout(Scanout),
}
pub(crate) trait EventSender {
@ -85,7 +97,7 @@ impl<E: 'static + EventSender> Listener<E> {
y0_top: bool,
) {
let fd = unsafe { libc::dup(fd.as_raw_fd()) };
self.send(Event::Scanout {
self.send(Event::Scanout(Scanout {
fd,
width,
height,
@ -93,7 +105,7 @@ impl<E: 'static + EventSender> Listener<E> {
fourcc,
modifier,
y0_top,
})
}))
}
fn mouse_set(&mut self, x: i32, y: i32, on: i32) {

View File

@ -7,7 +7,7 @@
<child>
<object class="GtkNotebook">
<child>
<object class="GtkGLArea" id="glarea"/>
<object class="QemuConsoleArea" id="area"/>
</child>
<child type="tab">
<object class="GtkLabel" id="notebook-tab">

View File

@ -1,18 +1,25 @@
use glib::subclass::prelude::*;
use glib::clone;
use gtk::prelude::*;
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 {
use super::*;
use glib::subclass;
use gtk::subclass::prelude::*;
#[derive(Debug, CompositeTemplate)]
#[derive(Debug, CompositeTemplate, Default)]
#[template(resource = "/org/qemu/gtk4/console.ui")]
pub struct QemuConsole {
#[template_child]
pub area: TemplateChild<crate::console_area::QemuConsoleArea>,
#[template_child]
pub label: TemplateChild<gtk::Label>,
pub console: OnceCell<Console>,
}
impl ObjectSubclass for QemuConsole {
@ -26,9 +33,7 @@ mod imp {
glib::object_subclass!();
fn new() -> Self {
Self {
label: TemplateChild::default(),
}
Self::default()
}
fn class_init(klass: &mut Self::Class) {
@ -65,3 +70,27 @@ mod imp {
glib::wrapper! {
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();
}
}

View 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: &gtk::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));
}
}

View File

@ -5,6 +5,7 @@ mod application;
mod config;
mod window;
mod console;
mod console_area;
use application::QemuApplication;
use config::{GETTEXT_PACKAGE, LOCALEDIR, RESOURCES_FILE};

View File

@ -21,6 +21,8 @@ run_command(
sources = files(
'application.rs',
'config.rs',
'console.rs',
'console_area.rs',
'main.rs',
'window.rs',
)

View File

@ -1,14 +1,13 @@
use crate::application::QemuApplication;
use crate::console::QemuConsole;
use crate::config::{APP_ID, PROFILE};
use glib::clone;
use glib::signal::Inhibit;
use gtk::subclass::prelude::*;
use gtk::{self, prelude::*};
use gtk::{gio, glib, CompositeTemplate};
use log::warn;
use qemu_display_listener::{Console as ConsoleListener, Event};
use qemu_display_listener::Console;
mod imp {
use super::*;
@ -21,10 +20,6 @@ mod imp {
pub headerbar: TemplateChild<gtk::HeaderBar>,
#[template_child]
pub console: TemplateChild<QemuConsole>,
// #[template_child]
// pub glarea: TemplateChild<gtk::GLArea>,
// #[template_child]
// pub label: TemplateChild<gtk::Label>,
pub settings: gio::Settings,
}
@ -42,8 +37,6 @@ mod imp {
Self {
headerbar: TemplateChild::default(),
console: TemplateChild::default(),
// label: TemplateChild::default(),
// glarea: TemplateChild::default(),
settings: gio::Settings::new(APP_ID),
}
}
@ -96,37 +89,15 @@ glib::wrapper! {
}
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");
window.set_application(Some(app));
let win = &imp::QemuApplicationWindow::from_instance(&window);
// win.glarea.connect_render(clone!(@weak window as win => move |area, ctxt| {
// dbg!("render");
// Inhibit(false)
// }));
win.console.set_qemu_console(console);
// Set icons for shell
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
}