2018-10-02 21:37:24 +00:00
use std ::{
cell ::RefCell ,
collections ::HashMap ,
io ::Error as IoError ,
2018-11-22 09:10:30 +00:00
os ::unix ::io ::{ AsRawFd , RawFd } ,
2018-10-02 21:37:24 +00:00
path ::PathBuf ,
rc ::Rc ,
sync ::{
atomic ::{ AtomicBool , Ordering } ,
Arc ,
} ,
} ;
2018-05-07 17:56:38 +00:00
2018-11-22 09:10:30 +00:00
use glium ::Surface as GliumSurface ;
2018-05-07 17:56:38 +00:00
use slog ::Logger ;
2018-10-02 21:37:24 +00:00
use smithay ::{
backend ::{
2018-11-22 09:10:30 +00:00
drm ::{
2018-11-23 14:10:51 +00:00
dev_t , device_bind ,
2018-11-22 09:10:30 +00:00
egl ::{ EglDevice , EglSurface } ,
gbm ::{ egl ::Gbm as EglGbmBackend , GbmDevice } ,
legacy ::LegacyDrmDevice ,
DevPath , Device , DeviceHandler , Surface ,
2018-10-02 21:37:24 +00:00
} ,
2018-11-22 09:10:30 +00:00
egl ::{ EGLDisplay , EGLGraphicsBackend } ,
graphics ::CursorBackend ,
2018-10-02 21:37:24 +00:00
input ::InputBackend ,
libinput ::{ libinput_bind , LibinputInputBackend , LibinputSessionInterface } ,
session ::{
auto ::{ auto_session_bind , AutoSession } ,
2018-11-23 14:10:51 +00:00
notify_multiplexer , AsSessionObserver , OFlag , Session , SessionNotifier ,
2018-10-02 21:37:24 +00:00
} ,
2018-11-22 09:10:30 +00:00
udev ::{ primary_gpu , udev_backend_bind , UdevBackend , UdevHandler } ,
2018-10-02 21:37:24 +00:00
} ,
2018-11-22 09:10:30 +00:00
drm ::control ::{
connector ::{ Info as ConnectorInfo , State as ConnectorState } ,
crtc ,
encoder ::Info as EncoderInfo ,
ResourceInfo ,
2018-10-02 21:37:24 +00:00
} ,
image ::{ ImageBuffer , Rgba } ,
input ::Libinput ,
wayland ::{
compositor ::CompositorToken ,
2018-11-22 14:37:31 +00:00
data_device ::{ default_action_chooser , init_data_device , set_data_device_focus } ,
2018-10-02 21:37:24 +00:00
output ::{ Mode , Output , PhysicalProperties } ,
seat ::{ Seat , XkbConfig } ,
shm ::init_shm_global ,
} ,
2018-11-23 14:10:51 +00:00
wayland_server ::{
calloop ::{
generic ::{ EventedFd , Generic } ,
EventLoop , LoopHandle , Source ,
} ,
protocol ::wl_output ,
Display ,
} ,
2018-10-02 21:37:24 +00:00
} ;
2018-05-07 17:56:38 +00:00
use glium_drawer ::GliumDrawer ;
2018-05-08 10:47:09 +00:00
use input_handler ::AnvilInputHandler ;
2018-09-24 22:32:09 +00:00
use shell ::{ init_shell , MyWindowMap , Roles , SurfaceData } ;
2018-01-05 19:04:46 +00:00
2018-11-22 09:10:30 +00:00
pub struct SessionFd ( RawFd ) ;
impl AsRawFd for SessionFd {
fn as_raw_fd ( & self ) -> RawFd {
self . 0
}
}
type RenderDevice =
EglDevice < EglGbmBackend < LegacyDrmDevice < SessionFd > > , GbmDevice < LegacyDrmDevice < SessionFd > > > ;
type RenderSurface =
EglSurface < EglGbmBackend < LegacyDrmDevice < SessionFd > > , GbmDevice < LegacyDrmDevice < SessionFd > > > ;
2018-09-24 22:30:39 +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-09-24 22:32:09 +00:00
init_shm_global ( & mut display . borrow_mut ( ) , vec! [ ] , log . clone ( ) ) ;
2017-10-01 17:21:12 +00:00
2018-09-24 22:32:09 +00:00
let ( compositor_token , _ , _ , window_map ) = init_shell ( & mut display . borrow_mut ( ) , 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 ( ( ) ) ? ;
2018-11-23 14:10:51 +00:00
let ( udev_observer , udev_notifier ) = notify_multiplexer ( ) ;
let udev_session_id = notifier . register ( udev_observer ) ;
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-11-22 09:10:30 +00:00
let udev_backend = UdevBackend ::new (
2018-01-07 21:30:38 +00:00
& context ,
UdevHandlerImpl {
2017-10-01 17:21:12 +00:00
compositor_token ,
2018-01-05 19:04:46 +00:00
active_egl_context ,
2018-11-22 09:10:30 +00:00
session : session . clone ( ) ,
2018-01-05 19:04:46 +00:00
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 ( ) ,
2018-11-23 14:10:51 +00:00
loop_handle : event_loop . handle ( ) ,
notifier : udev_notifier ,
2017-10-01 17:21:12 +00:00
logger : log . clone ( ) ,
2018-01-07 21:30:38 +00:00
} ,
2018-11-22 09:10:30 +00:00
seat . 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-11-21 21:24:18 +00:00
init_data_device (
& mut display . borrow_mut ( ) ,
| _ | { } ,
2018-11-22 14:37:31 +00:00
default_action_chooser ,
2018-11-21 21:24:18 +00:00
log . clone ( ) ,
) ;
2018-11-18 18:39:33 +00:00
2018-09-24 22:32:09 +00:00
let ( mut w_seat , _ ) = Seat ::new ( & mut display . borrow_mut ( ) , session . seat ( ) , 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
2018-11-18 18:39:33 +00:00
. add_keyboard ( XkbConfig ::default ( ) , 1000 , 500 , | seat , focus | {
set_data_device_focus ( seat , focus . and_then ( | s | s . client ( ) ) )
} ) . expect ( " Failed to initialize the keyboard " ) ;
2017-11-07 00:18:52 +00:00
2018-04-22 09:58:39 +00:00
let ( output , _output_global ) = Output ::new (
& mut display . borrow_mut ( ) ,
2017-11-07 00:18:52 +00:00
" Drm " . into ( ) ,
PhysicalProperties {
width : 0 ,
height : 0 ,
subpixel : wl_output ::Subpixel ::Unknown ,
2018-09-24 22:30:39 +00:00
make : " Smithay " . into ( ) ,
2017-11-07 00:18:52 +00:00
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-11-23 14:10:51 +00:00
let libinput_session_id = notifier . register ( libinput_context . observer ( ) ) ;
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-09-30 10:14:11 +00:00
let libinput_event_source = libinput_bind ( libinput_backend , event_loop . handle ( ) )
2018-11-22 09:10:30 +00:00
. map_err ( | e | -> IoError { e . into ( ) } )
2018-09-30 10:14:11 +00:00
. unwrap ( ) ;
let session_event_source = auto_session_bind ( notifier , & event_loop . handle ( ) )
. map_err ( | ( e , _ ) | e )
. unwrap ( ) ;
2018-11-22 09:10:30 +00:00
let udev_event_source = udev_backend_bind ( udev_backend , & event_loop . handle ( ) )
. map_err ( | e | -> IoError { e . into ( ) } )
. unwrap ( ) ;
2017-11-07 00:18:52 +00:00
2017-10-01 17:21:12 +00:00
while running . load ( Ordering ::SeqCst ) {
2018-09-24 22:32:09 +00:00
if event_loop
. dispatch ( Some ( ::std ::time ::Duration ::from_millis ( 16 ) ) , & mut ( ) )
. is_err ( )
{
2018-01-07 20:48:49 +00:00
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-11-07 00:18:52 +00:00
notifier . unregister ( libinput_session_id ) ;
2018-11-23 14:10:51 +00:00
notifier . unregister ( udev_session_id ) ;
2017-11-07 00:18:52 +00:00
libinput_event_source . remove ( ) ;
2018-09-24 22:30:39 +00:00
udev_event_source . remove ( ) ;
2017-10-01 17:21:12 +00:00
2018-05-07 17:56:38 +00:00
Ok ( ( ) )
2017-10-01 17:21:12 +00:00
}
2018-11-23 14:10:51 +00:00
struct UdevHandlerImpl < S : SessionNotifier , Data : 'static > {
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 > > > ,
2018-11-22 09:10:30 +00:00
session : AutoSession ,
backends : HashMap <
dev_t ,
(
2018-11-23 14:10:51 +00:00
S ::Id ,
Source < Generic < EventedFd < RenderDevice > > > ,
2018-11-22 09:10:30 +00:00
Rc < RefCell < HashMap < crtc ::Handle , GliumDrawer < RenderSurface > > > > ,
) ,
> ,
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 > > ,
2018-11-23 14:10:51 +00:00
loop_handle : LoopHandle < Data > ,
notifier : S ,
2017-10-01 17:21:12 +00:00
logger : ::slog ::Logger ,
}
2018-11-23 14:10:51 +00:00
impl < S : SessionNotifier , Data : 'static > UdevHandlerImpl < S , Data > {
2018-01-07 21:30:38 +00:00
pub fn scan_connectors (
2018-11-22 09:10:30 +00:00
device : & mut RenderDevice ,
2018-05-19 17:51:26 +00:00
egl_display : Rc < RefCell < Option < EGLDisplay > > > ,
2018-11-22 09:10:30 +00:00
logger : & ::slog ::Logger ,
) -> HashMap < crtc ::Handle , GliumDrawer < RenderSurface > > {
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-11-22 09:10:30 +00:00
. map ( | conn | device . resource_info ::< ConnectorInfo > ( * conn ) . unwrap ( ) )
2017-10-01 17:21:12 +00:00
. filter ( | conn | conn . connection_state ( ) = = ConnectorState ::Connected )
2018-11-22 09:10:30 +00:00
. inspect ( | conn | info! ( logger , " Connected: {:?} " , conn . connector_type ( ) ) )
2017-10-01 17:21:12 +00:00
. 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 ( )
2018-11-22 09:10:30 +00:00
. flat_map ( | encoder_handle | device . resource_info ::< EncoderInfo > ( * encoder_handle ) )
2018-01-07 21:30:38 +00:00
. 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
2018-11-22 09:10:30 +00:00
. create_surface ( crtc , mode , vec! [ connector_info . handle ( ) ] . into_iter ( ) )
2018-01-07 21:30:38 +00:00
. unwrap ( ) ,
2018-05-19 17:51:26 +00:00
egl_display . clone ( ) ,
2018-11-22 09:10:30 +00:00
logger . clone ( ) ,
2018-01-07 21:30:38 +00:00
) ;
2017-10-01 17:21:12 +00:00
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-11-23 14:10:51 +00:00
impl < S : SessionNotifier , Data : 'static > UdevHandler for UdevHandlerImpl < S , Data > {
2018-11-22 09:10:30 +00:00
fn device_added ( & mut self , _device : dev_t , path : PathBuf ) {
if let Some ( mut device ) = self
. session
. open (
& path ,
OFlag ::O_RDWR | OFlag ::O_CLOEXEC | OFlag ::O_NOCTTY | OFlag ::O_NONBLOCK ,
) . ok ( )
. and_then ( | fd | LegacyDrmDevice ::new ( SessionFd ( fd ) , self . logger . clone ( ) ) . ok ( ) )
. and_then ( | drm | GbmDevice ::new ( drm , self . logger . clone ( ) ) . ok ( ) )
. and_then ( | gbm | EglDevice ::new ( gbm , self . logger . clone ( ) ) . ok ( ) )
{
// init hardware acceleration on the primary gpu.
if path . canonicalize ( ) . ok ( ) = = self . primary_gpu {
* self . active_egl_context . borrow_mut ( ) = device . bind_wl_display ( & * self . display . borrow ( ) ) . ok ( ) ;
}
2018-01-05 19:04:46 +00:00
2018-11-23 14:10:51 +00:00
let backends = Rc ::new ( RefCell ::new ( UdevHandlerImpl ::< S , Data > ::scan_connectors (
2018-11-22 09:10:30 +00:00
& mut device ,
self . active_egl_context . clone ( ) ,
& self . logger ,
) ) ) ;
device . set_handler ( DrmHandlerImpl {
compositor_token : self . compositor_token ,
backends : backends . clone ( ) ,
window_map : self . window_map . clone ( ) ,
pointer_location : self . pointer_location . clone ( ) ,
logger : self . logger . clone ( ) ,
} ) ;
2018-11-23 14:10:51 +00:00
let device_session_id = self . notifier . register ( device . observer ( ) ) ;
let dev_id = device . device_id ( ) ;
let event_source = device_bind ( & self . loop_handle , device )
. map_err ( | e | -> IoError { e . into ( ) } )
. unwrap ( ) ;
for renderer in backends . borrow_mut ( ) . values ( ) {
// create cursor
renderer
. borrow ( )
. set_cursor_representation ( & self . pointer_image , ( 2 , 2 ) )
. unwrap ( ) ;
// render first frame
{
let mut frame = renderer . draw ( ) ;
frame . clear_color ( 0.8 , 0.8 , 0.9 , 1.0 ) ;
frame . finish ( ) . unwrap ( ) ;
}
}
self . backends
. insert ( dev_id , ( device_session_id , event_source , backends ) ) ;
2018-11-22 09:10:30 +00:00
}
2017-10-01 17:21:12 +00:00
}
2018-11-22 09:10:30 +00:00
fn device_changed ( & mut self , device : dev_t ) {
//quick and dirty, just re-init all backends
2018-11-23 14:10:51 +00:00
if let Some ( ( _ , ref mut evt_source , ref backends ) ) = self . backends . get_mut ( & device ) {
let source = evt_source . clone_inner ( ) ;
let mut evented = source . borrow_mut ( ) ;
let mut backends = backends . borrow_mut ( ) ;
* backends = UdevHandlerImpl ::< S , Data > ::scan_connectors (
& mut ( * evented ) . 0 ,
2018-11-22 09:10:30 +00:00
self . active_egl_context . clone ( ) ,
& self . logger ,
) ;
2018-11-23 14:10:51 +00:00
for renderer in backends . values ( ) {
// create cursor
renderer
. borrow ( )
. set_cursor_representation ( & self . pointer_image , ( 2 , 2 ) )
. unwrap ( ) ;
// render first frame
{
let mut frame = renderer . draw ( ) ;
frame . clear_color ( 0.8 , 0.8 , 0.9 , 1.0 ) ;
frame . finish ( ) . unwrap ( ) ;
}
}
2018-11-22 09:10:30 +00:00
}
2017-10-01 17:21:12 +00:00
}
2018-11-22 09:10:30 +00:00
fn device_removed ( & mut self , device : dev_t ) {
2018-01-05 19:04:46 +00:00
// drop the backends on this side
2018-11-23 14:10:51 +00:00
if let Some ( ( id , evt_source , _ ) ) = self . backends . remove ( & device ) {
2018-11-22 09:10:30 +00:00
// don't use hardware acceleration anymore, if this was the primary gpu
2018-11-23 14:10:51 +00:00
let source = evt_source . clone_inner ( ) ;
let evented = source . borrow ( ) ;
if ( * evented ) . 0. dev_path ( ) . and_then ( | path | path . canonicalize ( ) . ok ( ) ) = = self . primary_gpu {
2018-11-22 09:10:30 +00:00
* self . active_egl_context . borrow_mut ( ) = None ;
}
2018-11-23 14:10:51 +00:00
self . notifier . unregister ( id ) ;
2018-01-05 19:04:46 +00:00
}
2017-10-01 17:21:12 +00:00
}
}
pub struct DrmHandlerImpl {
2018-04-22 09:58:39 +00:00
compositor_token : CompositorToken < SurfaceData , Roles > ,
2018-11-22 09:10:30 +00:00
backends : Rc < RefCell < HashMap < crtc ::Handle , GliumDrawer < RenderSurface > > > > ,
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-11-22 09:10:30 +00:00
impl DeviceHandler for DrmHandlerImpl {
type Device = RenderDevice ;
fn vblank ( & mut self , crtc : crtc ::Handle ) {
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
2018-09-24 22:32:09 +00:00
drawer . draw_windows ( & * self . window_map . borrow ( ) , self . compositor_token , & self . logger ) ;
2017-10-01 17:21:12 +00:00
}
}
2018-11-22 09:10:30 +00:00
fn error ( & mut self , error : < RenderSurface as Surface > ::Error ) {
2017-10-01 17:21:12 +00:00
error! ( self . logger , " {:?} " , error ) ;
}
}