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)] #[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());

View File

@ -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) {

View File

@ -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">

View File

@ -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();
}
}

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 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};

View 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',
) )

View File

@ -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
} }