2018-05-07 17:56:38 +00:00
use std ::cell ::RefCell ;
use std ::collections ::HashMap ;
use std ::io ::Error as IoError ;
use std ::path ::PathBuf ;
use std ::rc ::Rc ;
use std ::sync ::Arc ;
use std ::sync ::atomic ::{ AtomicBool , Ordering } ;
use std ::time ::Duration ;
2018-05-08 18:08:17 +00:00
use glium ::Surface ;
2018-05-07 17:56:38 +00:00
use smithay ::image ::{ ImageBuffer , Rgba } ;
use slog ::Logger ;
use smithay ::drm ::control ::{ Device as ControlDevice , ResourceInfo } ;
use smithay ::drm ::control ::connector ::{ Info as ConnectorInfo , State as ConnectorState } ;
use smithay ::drm ::control ::crtc ;
use smithay ::drm ::control ::encoder ::Info as EncoderInfo ;
use smithay ::drm ::result ::Error as DrmError ;
2018-01-07 21:30:38 +00:00
use smithay ::backend ::drm ::{ DevPath , DrmBackend , DrmDevice , DrmHandler } ;
2017-11-07 00:18:52 +00:00
use smithay ::backend ::graphics ::GraphicsBackend ;
2018-05-08 18:08:17 +00:00
use smithay ::backend ::graphics ::egl ::wayland ::{ EGLDisplay , EGLWaylandExtensions } ;
2018-05-08 10:47:09 +00:00
use smithay ::backend ::input ::InputBackend ;
2018-03-22 15:09:58 +00:00
use smithay ::backend ::libinput ::{ libinput_bind , LibinputInputBackend , LibinputSessionInterface } ;
2017-11-07 00:18:52 +00:00
use smithay ::backend ::session ::{ Session , SessionNotifier } ;
2018-01-27 12:07:35 +00:00
use smithay ::backend ::session ::auto ::{ auto_session_bind , AutoSession } ;
2018-01-07 21:30:38 +00:00
use smithay ::backend ::udev ::{ primary_gpu , udev_backend_bind , SessionFdDrmDevice , UdevBackend , UdevHandler } ;
2018-05-08 18:08:17 +00:00
use smithay ::wayland ::compositor ::CompositorToken ;
2017-11-07 00:18:52 +00:00
use smithay ::wayland ::output ::{ Mode , Output , PhysicalProperties } ;
2018-05-08 10:47:09 +00:00
use smithay ::wayland ::seat ::Seat ;
2017-10-01 17:21:12 +00:00
use smithay ::wayland ::shm ::init_shm_global ;
2018-05-07 17:56:38 +00:00
use smithay ::wayland_server ::{ Display , EventLoop } ;
use smithay ::wayland_server ::commons ::downcast_impl ;
2018-05-08 10:47:09 +00:00
use smithay ::wayland_server ::protocol ::wl_output ;
use smithay ::input ::Libinput ;
2018-05-07 17:56:38 +00:00
use glium_drawer ::GliumDrawer ;
2018-05-08 18:08:17 +00:00
use shell ::{ init_shell , MyWindowMap , Roles , SurfaceData } ;
2018-05-08 10:47:09 +00:00
use input_handler ::AnvilInputHandler ;
2018-01-05 19:04:46 +00:00
2018-05-08 10:47:09 +00:00
pub fn run_udev ( mut display : Display , mut event_loop : EventLoop , log : Logger ) -> Result < ( ) , ( ) > {
2018-05-07 17:56:38 +00:00
let name = display . add_socket_auto ( ) . unwrap ( ) . into_string ( ) . unwrap ( ) ;
info! ( log , " Listening on wayland socket " ; " name " = > name . clone ( ) ) ;
::std ::env ::set_var ( " WAYLAND_DISPLAY " , name ) ;
2017-10-01 17:21:12 +00:00
2018-05-07 17:56:38 +00:00
let active_egl_context = Rc ::new ( RefCell ::new ( None ) ) ;
2017-10-01 17:21:12 +00:00
2018-04-22 09:58:39 +00:00
let display = Rc ::new ( RefCell ::new ( display ) ) ;
2018-01-05 19:04:46 +00:00
2017-10-01 17:21:12 +00:00
/*
* Initialize the compositor
* /
2018-04-22 09:58:39 +00:00
init_shm_global (
& mut display . borrow_mut ( ) ,
event_loop . token ( ) ,
vec! [ ] ,
log . clone ( ) ,
) ;
2017-10-01 17:21:12 +00:00
2018-04-23 09:40:41 +00:00
let ( compositor_token , _ , _ , window_map ) = init_shell (
2018-04-22 09:58:39 +00:00
& mut display . borrow_mut ( ) ,
event_loop . token ( ) ,
log . clone ( ) ,
) ;
2017-10-01 17:21:12 +00:00
/*
2018-01-27 12:07:35 +00:00
* Initialize session
2017-10-01 17:21:12 +00:00
* /
2018-05-07 17:56:38 +00:00
let ( session , mut notifier ) = AutoSession ::new ( log . clone ( ) ) . ok_or ( ( ) ) ? ;
2017-10-01 17:21:12 +00:00
let running = Arc ::new ( AtomicBool ::new ( true ) ) ;
2017-11-25 12:28:49 +00:00
let pointer_location = Rc ::new ( RefCell ::new ( ( 0.0 , 0.0 ) ) ) ;
2017-11-07 00:18:52 +00:00
2017-10-01 17:21:12 +00:00
/*
* Initialize the udev backend
* /
2018-05-07 17:56:38 +00:00
let context = ::smithay ::udev ::Context ::new ( ) . map_err ( | _ | ( ) ) ? ;
2018-01-05 19:04:46 +00:00
let seat = session . seat ( ) ;
let primary_gpu = primary_gpu ( & context , & seat ) . unwrap_or_default ( ) ;
2018-05-07 17:56:38 +00:00
let bytes = include_bytes! ( " ../resources/cursor2.rgba " ) ;
2018-01-31 16:23:05 +00:00
let mut udev_backend = UdevBackend ::new (
2018-04-22 09:58:39 +00:00
event_loop . token ( ) ,
2018-01-07 21:30:38 +00:00
& context ,
session . clone ( ) ,
UdevHandlerImpl {
2017-10-01 17:21:12 +00:00
compositor_token ,
2018-01-05 19:04:46 +00:00
active_egl_context ,
backends : HashMap ::new ( ) ,
display : display . clone ( ) ,
primary_gpu ,
2017-10-01 17:21:12 +00:00
window_map : window_map . clone ( ) ,
2017-11-07 00:18:52 +00:00
pointer_location : pointer_location . clone ( ) ,
pointer_image : ImageBuffer ::from_raw ( 64 , 64 , bytes . to_vec ( ) ) . unwrap ( ) ,
2017-10-01 17:21:12 +00:00
logger : log . clone ( ) ,
2018-01-07 21:30:38 +00:00
} ,
log . clone ( ) ,
2018-05-07 17:56:38 +00:00
) . map_err ( | _ | ( ) ) ? ;
2017-10-01 17:21:12 +00:00
2018-01-31 16:23:05 +00:00
let udev_session_id = notifier . register ( & mut udev_backend ) ;
2017-11-07 00:18:52 +00:00
2018-04-22 09:58:39 +00:00
let ( mut w_seat , _ ) = Seat ::new (
& mut display . borrow_mut ( ) ,
event_loop . token ( ) ,
session . seat ( ) . into ( ) ,
log . clone ( ) ,
) ;
2017-11-07 00:18:52 +00:00
2018-04-22 09:58:39 +00:00
let pointer = w_seat . add_pointer ( ) ;
let keyboard = w_seat
2017-11-07 00:18:52 +00:00
. add_keyboard ( " " , " " , " " , None , 1000 , 500 )
. expect ( " Failed to initialize the keyboard " ) ;
2018-04-22 09:58:39 +00:00
let ( output , _output_global ) = Output ::new (
& mut display . borrow_mut ( ) ,
event_loop . token ( ) ,
2017-11-07 00:18:52 +00:00
" Drm " . into ( ) ,
PhysicalProperties {
width : 0 ,
height : 0 ,
subpixel : wl_output ::Subpixel ::Unknown ,
maker : " Smithay " . into ( ) ,
model : " Generic DRM " . into ( ) ,
} ,
log . clone ( ) ,
) ;
let ( w , h ) = ( 1920 , 1080 ) ; // Hardcode full-hd res
2018-04-22 09:58:39 +00:00
output . change_current_state (
Some ( Mode {
2017-11-07 00:18:52 +00:00
width : w as i32 ,
height : h as i32 ,
refresh : 60_000 ,
2018-04-22 09:58:39 +00:00
} ) ,
None ,
None ,
) ;
output . set_preferred ( Mode {
width : w as i32 ,
height : h as i32 ,
refresh : 60_000 ,
} ) ;
2017-11-07 00:18:52 +00:00
/*
* Initialize libinput backend
* /
2018-01-27 12:50:24 +00:00
let mut libinput_context =
Libinput ::new_from_udev ::< LibinputSessionInterface < AutoSession > > ( session . clone ( ) . into ( ) , & context ) ;
2018-01-31 16:23:05 +00:00
let libinput_session_id = notifier . register ( & mut libinput_context ) ;
2017-11-25 12:28:49 +00:00
libinput_context . udev_assign_seat ( & seat ) . unwrap ( ) ;
2017-11-07 00:18:52 +00:00
let mut libinput_backend = LibinputInputBackend ::new ( libinput_context , log . clone ( ) ) ;
2018-05-08 10:47:09 +00:00
libinput_backend . set_handler ( AnvilInputHandler ::new_with_session (
log . clone ( ) ,
2018-04-22 09:58:39 +00:00
pointer ,
keyboard ,
2018-05-08 10:47:09 +00:00
window_map . clone ( ) ,
( w , h ) ,
running . clone ( ) ,
2018-04-22 09:58:39 +00:00
pointer_location ,
2018-05-08 10:47:09 +00:00
session ,
) ) ;
2018-04-22 09:58:39 +00:00
let libinput_event_source = libinput_bind ( libinput_backend , event_loop . token ( ) )
2018-02-21 19:26:10 +00:00
. map_err ( | ( err , _ ) | err )
. unwrap ( ) ;
2017-11-07 00:18:52 +00:00
2018-04-22 09:58:39 +00:00
let session_event_source = auto_session_bind ( notifier , & event_loop . token ( ) )
2018-02-21 19:26:10 +00:00
. map_err ( | ( err , _ ) | err )
. unwrap ( ) ;
2018-04-22 09:58:39 +00:00
let udev_event_source = udev_backend_bind ( & event_loop . token ( ) , udev_backend )
2018-02-21 19:26:10 +00:00
. map_err ( | ( err , _ ) | err )
. unwrap ( ) ;
2017-11-07 00:18:52 +00:00
2017-10-01 17:21:12 +00:00
while running . load ( Ordering ::SeqCst ) {
2018-01-07 20:48:49 +00:00
if let Err ( _ ) = event_loop . dispatch ( Some ( 16 ) ) {
running . store ( false , Ordering ::SeqCst ) ;
} else {
2018-04-22 09:58:39 +00:00
display . borrow_mut ( ) . flush_clients ( ) ;
2018-01-07 20:48:49 +00:00
window_map . borrow_mut ( ) . refresh ( ) ;
}
2017-10-01 17:21:12 +00:00
}
2018-01-28 17:18:06 +00:00
let mut notifier = session_event_source . unbind ( ) ;
2017-10-01 17:21:12 +00:00
notifier . unregister ( udev_session_id ) ;
2017-11-07 00:18:52 +00:00
notifier . unregister ( libinput_session_id ) ;
libinput_event_source . remove ( ) ;
2017-10-01 17:21:12 +00:00
2018-01-31 16:23:05 +00:00
// destroy the udev backend freeing the drm devices
2018-04-24 09:00:39 +00:00
//
// udev_event_source.remove() returns a Box<Implementation<..>>, downcast_impl
// allows us to cast it back to its original type, storing it back into its original
// variable to simplify type inference.
2018-04-22 09:58:39 +00:00
udev_backend = * ( downcast_impl ( udev_event_source . remove ( ) ) . unwrap_or_else ( | _ | unreachable! ( ) ) ) ;
udev_backend . close ( ) ;
2018-05-07 17:56:38 +00:00
Ok ( ( ) )
2017-10-01 17:21:12 +00:00
}
struct UdevHandlerImpl {
2018-04-22 09:58:39 +00:00
compositor_token : CompositorToken < SurfaceData , Roles > ,
2018-01-05 19:04:46 +00:00
active_egl_context : Rc < RefCell < Option < EGLDisplay > > > ,
backends : HashMap < u64 , Rc < RefCell < HashMap < crtc ::Handle , GliumDrawer < DrmBackend < SessionFdDrmDevice > > > > > > ,
2018-04-22 09:58:39 +00:00
display : Rc < RefCell < Display > > ,
2018-01-05 19:04:46 +00:00
primary_gpu : Option < PathBuf > ,
2017-10-01 17:21:12 +00:00
window_map : Rc < RefCell < MyWindowMap > > ,
2017-11-25 12:28:49 +00:00
pointer_location : Rc < RefCell < ( f64 , f64 ) > > ,
2017-11-07 00:18:52 +00:00
pointer_image : ImageBuffer < Rgba < u8 > , Vec < u8 > > ,
2017-10-01 17:21:12 +00:00
logger : ::slog ::Logger ,
}
impl UdevHandlerImpl {
2018-01-07 21:30:38 +00:00
pub fn scan_connectors (
2018-04-22 09:58:39 +00:00
& self ,
device : & mut DrmDevice < SessionFdDrmDevice > ,
2018-05-19 17:51:26 +00:00
egl_display : Rc < RefCell < Option < EGLDisplay > > > ,
2018-01-07 21:30:38 +00:00
) -> HashMap < crtc ::Handle , GliumDrawer < DrmBackend < SessionFdDrmDevice > > > {
2017-10-01 17:21:12 +00:00
// Get a set of all modesetting resource handles (excluding planes):
let res_handles = device . resource_handles ( ) . unwrap ( ) ;
// Use first connected connector
let connector_infos : Vec < ConnectorInfo > = res_handles
. connectors ( )
. iter ( )
2018-01-07 21:30:38 +00:00
. map ( | conn | ConnectorInfo ::load_from_device ( device , * conn ) . unwrap ( ) )
2017-10-01 17:21:12 +00:00
. filter ( | conn | conn . connection_state ( ) = = ConnectorState ::Connected )
. inspect ( | conn | info! ( self . logger , " Connected: {:?} " , conn . connector_type ( ) ) )
. collect ( ) ;
2018-01-05 19:04:46 +00:00
let mut backends = HashMap ::new ( ) ;
2017-10-01 17:21:12 +00:00
// very naive way of finding good crtc/encoder/connector combinations. This problem is np-complete
for connector_info in connector_infos {
2018-01-07 21:30:38 +00:00
let encoder_infos = connector_info
. encoders ( )
. iter ( )
. flat_map ( | encoder_handle | EncoderInfo ::load_from_device ( device , * encoder_handle ) )
. collect ::< Vec < EncoderInfo > > ( ) ;
2017-10-01 17:21:12 +00:00
for encoder_info in encoder_infos {
for crtc in res_handles . filter_crtcs ( encoder_info . possible_crtcs ( ) ) {
2018-01-05 19:04:46 +00:00
if ! backends . contains_key ( & crtc ) {
2017-10-01 17:21:12 +00:00
let mode = connector_info . modes ( ) [ 0 ] ; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.)
2018-01-07 21:30:38 +00:00
// create a backend
2018-05-19 17:51:26 +00:00
let renderer = GliumDrawer ::init (
2018-01-07 21:30:38 +00:00
device
. create_backend ( crtc , mode , vec! [ connector_info . handle ( ) ] )
. unwrap ( ) ,
2018-05-19 17:51:26 +00:00
egl_display . clone ( ) ,
self . logger . clone ( ) ,
2018-01-07 21:30:38 +00:00
) ;
2017-10-01 17:21:12 +00:00
2017-11-07 00:18:52 +00:00
// create cursor
2018-01-07 21:30:38 +00:00
renderer
2018-02-26 17:29:52 +00:00
. borrow ( )
2018-01-07 21:30:38 +00:00
. set_cursor_representation ( & self . pointer_image , ( 2 , 2 ) )
. unwrap ( ) ;
2017-11-07 00:18:52 +00:00
2017-10-01 17:21:12 +00:00
// render first frame
{
let mut frame = renderer . draw ( ) ;
frame . clear_color ( 0.8 , 0.8 , 0.9 , 1.0 ) ;
frame . finish ( ) . unwrap ( ) ;
}
2018-01-05 19:04:46 +00:00
backends . insert ( crtc , renderer ) ;
2017-10-01 17:21:12 +00:00
break ;
}
}
}
}
2018-01-05 19:04:46 +00:00
backends
2017-10-01 17:21:12 +00:00
}
}
2018-01-05 19:04:46 +00:00
impl UdevHandler < DrmHandlerImpl > for UdevHandlerImpl {
2018-04-22 09:58:39 +00:00
fn device_added ( & mut self , device : & mut DrmDevice < SessionFdDrmDevice > ) -> Option < DrmHandlerImpl > {
2018-01-05 19:04:46 +00:00
// init hardware acceleration on the primary gpu.
if device . dev_path ( ) . and_then ( | path | path . canonicalize ( ) . ok ( ) ) = = self . primary_gpu {
2018-04-22 09:58:39 +00:00
* self . active_egl_context . borrow_mut ( ) = device . bind_wl_display ( & * self . display . borrow ( ) ) . ok ( ) ;
2018-01-05 19:04:46 +00:00
}
2018-05-19 17:51:26 +00:00
let backends = Rc ::new ( RefCell ::new ( self . scan_connectors (
device ,
self . active_egl_context . clone ( ) ,
) ) ) ;
2018-01-05 19:04:46 +00:00
self . backends . insert ( device . device_id ( ) , backends . clone ( ) ) ;
2017-10-01 17:21:12 +00:00
Some ( DrmHandlerImpl {
compositor_token : self . compositor_token . clone ( ) ,
2018-01-05 19:04:46 +00:00
backends ,
2017-10-01 17:21:12 +00:00
window_map : self . window_map . clone ( ) ,
2017-11-07 00:18:52 +00:00
pointer_location : self . pointer_location . clone ( ) ,
2017-10-01 17:21:12 +00:00
logger : self . logger . clone ( ) ,
} )
}
2018-04-22 09:58:39 +00:00
fn device_changed ( & mut self , device : & mut DrmDevice < SessionFdDrmDevice > ) {
2018-01-05 19:04:46 +00:00
//quick and dirt, just re-init all backends
2018-01-31 16:23:05 +00:00
let backends = self . backends . get ( & device . device_id ( ) ) . unwrap ( ) ;
2018-05-19 17:51:26 +00:00
* backends . borrow_mut ( ) = self . scan_connectors ( device , self . active_egl_context . clone ( ) ) ;
2017-10-01 17:21:12 +00:00
}
2018-04-22 09:58:39 +00:00
fn device_removed ( & mut self , device : & mut DrmDevice < SessionFdDrmDevice > ) {
2018-01-05 19:04:46 +00:00
// drop the backends on this side
self . backends . remove ( & device . device_id ( ) ) ;
// don't use hardware acceleration anymore, if this was the primary gpu
if device . dev_path ( ) . and_then ( | path | path . canonicalize ( ) . ok ( ) ) = = self . primary_gpu {
* self . active_egl_context . borrow_mut ( ) = None ;
}
2017-10-01 17:21:12 +00:00
}
2018-04-22 09:58:39 +00:00
fn error ( & mut self , error : IoError ) {
2017-10-01 17:21:12 +00:00
error! ( self . logger , " {:?} " , error ) ;
}
}
pub struct DrmHandlerImpl {
2018-04-22 09:58:39 +00:00
compositor_token : CompositorToken < SurfaceData , Roles > ,
2018-01-05 19:04:46 +00:00
backends : Rc < RefCell < HashMap < crtc ::Handle , GliumDrawer < DrmBackend < SessionFdDrmDevice > > > > > ,
2017-10-01 17:21:12 +00:00
window_map : Rc < RefCell < MyWindowMap > > ,
2017-11-25 12:28:49 +00:00
pointer_location : Rc < RefCell < ( f64 , f64 ) > > ,
2017-10-01 17:21:12 +00:00
logger : ::slog ::Logger ,
}
2018-01-05 19:04:46 +00:00
impl DrmHandler < SessionFdDrmDevice > for DrmHandlerImpl {
2018-01-07 21:30:38 +00:00
fn ready (
2018-04-22 09:58:39 +00:00
& mut self ,
_device : & mut DrmDevice < SessionFdDrmDevice > ,
crtc : crtc ::Handle ,
_frame : u32 ,
_duration : Duration ,
2018-01-07 21:30:38 +00:00
) {
2018-01-05 19:04:46 +00:00
if let Some ( drawer ) = self . backends . borrow ( ) . get ( & crtc ) {
{
let ( x , y ) = * self . pointer_location . borrow ( ) ;
2018-02-26 17:29:52 +00:00
let _ = drawer
. borrow ( )
. set_cursor_position ( x . trunc ( ) . abs ( ) as u32 , y . trunc ( ) . abs ( ) as u32 ) ;
2018-01-05 19:04:46 +00:00
}
2018-05-08 18:08:17 +00:00
drawer . draw_windows (
& * self . window_map . borrow ( ) ,
self . compositor_token ,
& self . logger ,
) ;
2017-10-01 17:21:12 +00:00
}
}
2018-04-22 09:58:39 +00:00
fn error ( & mut self , _device : & mut DrmDevice < SessionFdDrmDevice > , error : DrmError ) {
2017-10-01 17:21:12 +00:00
error! ( self . logger , " {:?} " , error ) ;
}
}