// Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. // // StratoVirt is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan // PSL v2. // You may obtain a copy of Mulan PSL v2 at: // http://license.coscl.org.cn/MulanPSL2 // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. use std::{cell::RefCell, rc::Rc}; use anyhow::Result; use gdk::{prelude::SeatExt, SeatCapabilities}; use gtk::{ cairo, gdk::{self, EventMask, ScrollDirection}, glib::{self, translate::IntoGlib}, prelude::WidgetExtManual, traits::WidgetExt, DrawingArea, Inhibit, }; use log::error; use crate::{ console::graphic_hardware_ui_info, gtk::GtkDisplayScreen, input::{ self, input_button, input_move_abs, input_point_sync, press_mouse, release_all_key, update_key_state, Axis, ABS_MAX, INPUT_BUTTON_WHEEL_DOWN, INPUT_BUTTON_WHEEL_LEFT, INPUT_BUTTON_WHEEL_RIGHT, INPUT_BUTTON_WHEEL_UP, INPUT_POINT_BACK, INPUT_POINT_FORWARD, INPUT_POINT_LEFT, INPUT_POINT_MIDDLE, INPUT_POINT_RIGHT, }, }; const GTK_INPUT_BUTTON_LEFT: u32 = 1; const GTK_INPUT_BUTTON_MIDDLE: u32 = 2; const GTK_INPUT_BUTTON_RIGHT: u32 = 3; const GTK_INPUT_BUTTON_BACK: u32 = 8; const GTK_INPUT_BUTTON_FORWARD: u32 = 9; pub(crate) fn set_callback_for_draw_area( draw_area: &DrawingArea, gs: Rc<RefCell<GtkDisplayScreen>>, ) -> Result<()> { draw_area.connect_draw( glib::clone!(@weak gs => @default-return Inhibit(false), move |_, cr| { da_draw_callback(&gs, cr).unwrap_or_else(|e| error!("Draw: {}", e)); Inhibit(false) }), ); draw_area.connect_event( glib::clone!(@weak gs => @default-return Inhibit(false), move |_, event| { da_event_callback(&gs, event).unwrap_or_else(|e| error!("Draw event: {}", e)); Inhibit(false)}), ); draw_area.connect_button_press_event( glib::clone!(@weak gs => @default-return Inhibit(false), move |_, button_event| { da_pointer_callback(button_event).unwrap_or_else(|e| error!("Press event: {}", e)); Inhibit(false) }), ); draw_area.connect_button_release_event( glib::clone!(@weak gs => @default-return Inhibit(false), move |_, button_event| { da_pointer_callback(button_event).unwrap_or_else(|e| error!("Release event: {}", e)); Inhibit(false) }), ); draw_area.connect_scroll_event( glib::clone!(@weak gs => @default-return Inhibit(false), move |_, scroll_event| { da_scroll_callback(scroll_event).unwrap_or_else(|e| error!("Scroll event: {}", e)); Inhibit(false) }), ); draw_area.connect_key_press_event( glib::clone!(@weak gs => @default-return Inhibit(true), move |_, key_event| { da_key_callback(&gs,key_event, true).unwrap_or_else(|e|error!("Press event: {}", e)); Inhibit(true)} ), ); draw_area.connect_key_release_event( glib::clone!(@weak gs => @default-return Inhibit(true), move |_, key_event| { da_key_callback(&gs,key_event, false).unwrap_or_else(|e|error!("Key event: {}", e)); Inhibit(true)} ), ); draw_area.connect_configure_event( glib::clone!(@weak gs => @default-return false, move |_, event_configure| { da_configure_callback(&gs, event_configure).unwrap_or_else(|e|error!("Configure event: {}", e)); false} ), ); draw_area.connect_focus_out_event( glib::clone!(@weak gs => @default-return Inhibit(false), move |_, _| { da_focus_out_callback().unwrap_or_else(|e|error!("Focus out event: {:?}", e)); Inhibit(false)} ), ); draw_area.connect_enter_notify_event( glib::clone!(@weak gs => @default-return Inhibit(false), move |_,enter_event| { da_enter_callback(&gs, enter_event).unwrap_or_else(|e|error!("Enter event: {:?}", e)); Inhibit(false)} ), ); draw_area.connect_leave_notify_event( glib::clone!(@weak gs => @default-return Inhibit(false), move |_, leave_event| { da_leave_callback(&gs, leave_event).unwrap_or_else(|e|error!("Leave event: {:?}", e)); Inhibit(false)} ), ); let event_mask = EventMask::BUTTON_PRESS_MASK | EventMask::BUTTON_RELEASE_MASK | EventMask::BUTTON_MOTION_MASK | EventMask::SCROLL_MASK | EventMask::SMOOTH_SCROLL_MASK | EventMask::KEY_PRESS_MASK | EventMask::KEY_RELEASE_MASK | EventMask::BUTTON1_MOTION_MASK | EventMask::FOCUS_CHANGE_MASK | EventMask::ENTER_NOTIFY_MASK | EventMask::LEAVE_NOTIFY_MASK | EventMask::POINTER_MOTION_MASK; draw_area.add_events(event_mask); Ok(()) } fn da_enter_callback( gs: &Rc<RefCell<GtkDisplayScreen>>, _event: &gdk::EventCrossing, ) -> Result<()> { trace::gtk_enter_callback(&"enter".to_string()); update_keyboard_grab(gs, true); Ok(()) } fn da_leave_callback( gs: &Rc<RefCell<GtkDisplayScreen>>, _event: &gdk::EventCrossing, ) -> Result<()> { trace::gtk_enter_callback(&"leave".to_string()); update_keyboard_grab(gs, false); Ok(()) } fn update_keyboard_grab(gs: &Rc<RefCell<GtkDisplayScreen>>, grab: bool) { let borrowed_gs = gs.borrow(); let display = borrowed_gs.draw_area.display(); if let Some(seat) = display.default_seat() { if grab { if let Some(w) = borrowed_gs.draw_area.window() { seat.grab(&w, SeatCapabilities::KEYBOARD, false, None, None, None); } } else { seat.ungrab(); } } } /// When the window size changes, /// the image resolution adapts to the window. fn da_configure_callback( gs: &Rc<RefCell<GtkDisplayScreen>>, event_configure: &gdk::EventConfigure, ) -> Result<()> { trace::gtk_configure_callback(&event_configure.size().0, &event_configure.size().1); let borrowed_gs = gs.borrow(); if !borrowed_gs.scale_mode.borrow().is_free_scale() { return Ok(()); } let con = match borrowed_gs.con.upgrade() { Some(c) => c, None => return Ok(()), }; drop(borrowed_gs); let (width, height) = event_configure.size(); graphic_hardware_ui_info(con, width, height) } fn da_focus_out_callback() -> Result<()> { release_all_key() } fn da_key_callback( gs: &Rc<RefCell<GtkDisplayScreen>>, key_event: &gdk::EventKey, press: bool, ) -> Result<()> { let keysym2keycode = gs.borrow().keysym2keycode.clone(); let org_key_value = key_event.keyval().into_glib() as i32; let key_value: u16 = key_event.keyval().to_lower().into_glib() as u16; let keycode: u16 = match keysym2keycode.borrow().get(&key_value) { Some(k) => *k, None => 0, }; trace::gtk_key_event_callback(&key_value, &press); update_key_state(press, org_key_value, keycode)?; input::key_event(keycode, press)?; Ok(()) } fn da_event_callback(gs: &Rc<RefCell<GtkDisplayScreen>>, event: &gdk::Event) -> Result<()> { // Cursor movement. if event.event_type() == gdk::EventType::MotionNotify { gd_cursor_move_event(gs, event).unwrap_or_else(|e| error!("Cursor movement: {:?}", e)); } Ok(()) } /// Cursor Movement. fn gd_cursor_move_event(gs: &Rc<RefCell<GtkDisplayScreen>>, event: &gdk::Event) -> Result<()> { let mut borrowed_gs = gs.borrow_mut(); let (width, height) = match &borrowed_gs.cairo_image { Some(image) => (image.width() as f64, image.height() as f64), None => return Ok(()), }; let (x, y) = match event.coords() { Some(value) => value, None => return Ok(()), }; trace::gtk_cursor_move_event(&x, &y); let (real_x, real_y) = borrowed_gs.convert_coord(x, y)?; let standard_x = ((real_x * (ABS_MAX as f64)) / width) as u16; let standard_y = ((real_y * (ABS_MAX as f64)) / height) as u16; input_move_abs(Axis::X, standard_x as u32)?; input_move_abs(Axis::Y, standard_y as u32)?; input_point_sync() } fn da_pointer_callback(button_event: &gdk::EventButton) -> Result<()> { let button_mask = match button_event.button() { GTK_INPUT_BUTTON_LEFT => INPUT_POINT_LEFT, GTK_INPUT_BUTTON_RIGHT => INPUT_POINT_RIGHT, GTK_INPUT_BUTTON_MIDDLE => INPUT_POINT_MIDDLE, GTK_INPUT_BUTTON_BACK => INPUT_POINT_BACK, GTK_INPUT_BUTTON_FORWARD => INPUT_POINT_FORWARD, _ => return Ok(()), }; trace::gtk_pointer_callback(&button_mask); match button_event.event_type() { gdk::EventType::ButtonRelease => { input_button(button_mask, false)?; input_point_sync() } gdk::EventType::ButtonPress => { input_button(button_mask, true)?; input_point_sync() } gdk::EventType::DoubleButtonPress => { press_mouse(button_mask)?; press_mouse(button_mask) } _ => Ok(()), } } fn da_scroll_callback(scroll_event: &gdk::EventScroll) -> Result<()> { trace::gtk_scroll_callback(&scroll_event.direction()); match scroll_event.direction() { ScrollDirection::Up => press_mouse(INPUT_BUTTON_WHEEL_UP), ScrollDirection::Down => press_mouse(INPUT_BUTTON_WHEEL_DOWN), ScrollDirection::Left => press_mouse(INPUT_BUTTON_WHEEL_LEFT), ScrollDirection::Right => press_mouse(INPUT_BUTTON_WHEEL_RIGHT), ScrollDirection::Smooth => match scroll_event.scroll_deltas() { Some((delta_x, delta_y)) => { if delta_x.eq(&0.0) && delta_y.eq(&0.0) { return Ok(()); } // Horizontal scrolling. if delta_x.gt(&0.0) { press_mouse(INPUT_BUTTON_WHEEL_RIGHT)?; } else if delta_x.lt(&0.0) { press_mouse(INPUT_BUTTON_WHEEL_LEFT)?; } // Vertical scrolling. if delta_y.gt(&0.0) { press_mouse(INPUT_BUTTON_WHEEL_DOWN)?; } else if delta_y.lt(&0.0) { press_mouse(INPUT_BUTTON_WHEEL_UP)?; } Ok(()) } None => Ok(()), }, _ => Ok(()), } } /// Draw_area callback func for draw signal. fn da_draw_callback(gs: &Rc<RefCell<GtkDisplayScreen>>, cr: &cairo::Context) -> Result<()> { let mut borrowed_gs = gs.borrow_mut(); let scale_mode = borrowed_gs.scale_mode.clone(); let (mut surface_width, mut surface_height) = match &borrowed_gs.cairo_image { Some(image) => (image.width() as f64, image.height() as f64), None => return Ok(()), }; if surface_width.le(&0.0) || surface_height.le(&0.0) { return Ok(()); } let (window_width, window_height); match borrowed_gs.get_window_size() { Some((w, h)) => (window_width, window_height) = (w, h), None => return Ok(()), }; if scale_mode.borrow().is_full_screen() || scale_mode.borrow().is_free_scale() { borrowed_gs.scale_x = window_width / surface_width; borrowed_gs.scale_y = window_height / surface_height; } surface_width *= borrowed_gs.scale_x; surface_height *= borrowed_gs.scale_y; let mut mx: f64 = 0.0; let mut my: f64 = 0.0; if window_width.gt(&surface_width) { mx = (window_width - surface_width) / (2.0); } if window_height.gt(&surface_height) { my = (window_height - surface_height) / (2.0); } cr.rectangle(0.0, 0.0, window_width, window_height); cr.rectangle(mx + surface_width, my, surface_width * -1.0, surface_height); cr.fill()?; cr.scale(borrowed_gs.scale_x, borrowed_gs.scale_y); if let Some(image) = &borrowed_gs.cairo_image { cr.set_source_surface(image, mx / borrowed_gs.scale_x, my / borrowed_gs.scale_y)?; } cr.paint()?; Ok(()) }