2015-04-04 03:54:38 +03:00
# Copyright (C) 2006-2008, 2015 Red Hat, Inc.
# Copyright (C) 2006 Daniel P. Berrange <berrange@redhat.com>
# Copyright (C) 2010 Marc-Andre Lureau <marcandre.lureau@redhat.com>
#
2018-04-04 16:35:41 +03:00
# This work is licensed under the GNU GPLv2 or later.
2018-03-20 22:00:02 +03:00
# See the COPYING file in the top-level directory.
2015-04-04 03:54:38 +03:00
from gi . repository import Gdk
2018-03-15 15:10:09 +03:00
from gi . repository import GObject
2015-04-04 03:54:38 +03:00
import gi
gi . require_version ( ' GtkVnc ' , ' 2.0 ' )
from gi . repository import GtkVnc
2015-08-13 23:00:33 +03:00
try :
gi . require_version ( ' SpiceClientGtk ' , ' 3.0 ' )
from gi . repository import SpiceClientGtk
from gi . repository import SpiceClientGLib
have_spice_gtk = True
2020-08-29 20:32:37 +03:00
except ( ValueError , ImportError ) : # pragma: no cover
2015-08-13 23:00:33 +03:00
have_spice_gtk = False
2015-04-04 03:54:38 +03:00
2019-06-17 04:12:39 +03:00
from virtinst import log
2015-04-04 03:54:38 +03:00
from . sshtunnels import SSHTunnels
2019-06-17 04:56:34 +03:00
from . . baseclass import vmmGObject
2015-04-04 03:54:38 +03:00
##################################
# VNC/Spice abstraction handling #
##################################
class Viewer ( vmmGObject ) :
"""
Base class for viewer abstraction
"""
__gsignals__ = {
2018-03-15 15:10:09 +03:00
" add-display-widget " : ( vmmGObject . RUN_FIRST , None , [ object ] ) ,
" size-allocate " : ( vmmGObject . RUN_FIRST , None , [ object ] ) ,
" pointer-grab " : ( vmmGObject . RUN_FIRST , None , [ ] ) ,
" pointer-ungrab " : ( vmmGObject . RUN_FIRST , None , [ ] ) ,
2020-09-09 16:33:58 +03:00
" keyboard-grab " : ( vmmGObject . RUN_FIRST , None , [ ] ) ,
" keyboard-ungrab " : ( vmmGObject . RUN_FIRST , None , [ ] ) ,
2018-03-15 15:10:09 +03:00
" connected " : ( vmmGObject . RUN_FIRST , None , [ ] ) ,
" disconnected " : ( vmmGObject . RUN_FIRST , None , [ str , str ] ) ,
" auth-error " : ( vmmGObject . RUN_FIRST , None , [ str , bool ] ) ,
" need-auth " : ( vmmGObject . RUN_FIRST , None , [ bool , bool ] ) ,
" agent-connected " : ( vmmGObject . RUN_FIRST , None , [ ] ) ,
" usb-redirect-error " : ( vmmGObject . RUN_FIRST , None , [ str ] ) ,
2015-04-04 03:54:38 +03:00
}
2016-05-08 01:08:25 +03:00
def __init__ ( self , vm , ginfo ) :
2015-04-04 03:54:38 +03:00
vmmGObject . __init__ ( self )
self . _display = None
2016-05-08 01:08:25 +03:00
self . _vm = vm
2015-04-12 04:39:24 +03:00
self . _ginfo = ginfo
self . _tunnels = SSHTunnels ( self . _ginfo )
2020-09-09 16:33:58 +03:00
self . _keyboard_grab = False
2015-04-04 03:54:38 +03:00
self . add_gsettings_handle (
self . config . on_keys_combination_changed ( self . _refresh_grab_keys ) )
self . connect ( " add-display-widget " , self . _common_init )
def _cleanup ( self ) :
self . close ( )
if self . _display :
self . _display . destroy ( )
self . _display = None
2016-05-08 01:08:25 +03:00
self . _vm = None
2015-04-04 03:54:38 +03:00
2015-04-12 20:04:32 +03:00
self . _tunnels . close_all ( )
2015-04-04 03:54:38 +03:00
########################
# Internal helper APIs #
########################
def _make_signal_proxy ( self , new_signal ) :
"""
Helper for redirecting a signal from self . _display out
through the viewer
"""
def _proxy_signal ( src , * args , * * kwargs ) :
ignore = src
self . emit ( new_signal , * args , * * kwargs )
return _proxy_signal
def _common_init ( self , ignore1 , ignore2 ) :
self . _refresh_grab_keys ( )
self . _display . connect ( " size-allocate " ,
self . _make_signal_proxy ( " size-allocate " ) )
#########################
# Generic internal APIs #
#########################
def _grab_focus ( self ) :
if self . _display :
self . _display . grab_focus ( )
def _set_size_request ( self , * args , * * kwargs ) :
return self . _display . set_size_request ( * args , * * kwargs )
def _size_allocate ( self , * args , * * kwargs ) :
return self . _display . size_allocate ( * args , * * kwargs )
def _get_pixbuf ( self ) :
return self . _display . get_pixbuf ( )
2016-05-08 00:53:10 +03:00
def _get_fd_for_open ( self ) :
2015-04-12 04:39:24 +03:00
if self . _ginfo . need_tunnel ( ) :
2016-05-08 00:53:10 +03:00
return self . _tunnels . open_new ( )
2016-05-08 01:14:58 +03:00
if self . _vm . conn . is_remote ( ) :
# OpenGraphics only works for local libvirtd connections
return None
2016-05-16 22:39:46 +03:00
if self . _ginfo . gtlsport and not self . _ginfo . gport :
# This makes spice loop requesting an fd. Disable until spice is
# fixed: https://bugzilla.redhat.com/show_bug.cgi?id=1334071
2020-08-29 20:32:37 +03:00
return None # pragma: no cover
2016-05-16 22:39:46 +03:00
2019-06-07 23:06:52 +03:00
if not self . _vm . conn . support . domain_open_graphics ( ) :
2016-05-08 01:14:58 +03:00
return None
2016-05-08 01:08:25 +03:00
return self . _vm . open_graphics_fd ( )
2016-05-08 00:53:10 +03:00
def _open ( self ) :
2016-05-18 23:57:38 +03:00
if self . _ginfo . bad_config ( ) :
raise RuntimeError ( self . _ginfo . bad_config ( ) )
2016-05-08 00:53:10 +03:00
fd = self . _get_fd_for_open ( )
2016-05-08 01:14:58 +03:00
if fd is not None :
2016-05-08 00:53:10 +03:00
self . _open_fd ( fd )
2015-04-04 03:54:38 +03:00
else :
2015-04-12 04:39:24 +03:00
self . _open_host ( )
2015-04-04 03:54:38 +03:00
def _get_grab_keys ( self ) :
return self . _display . get_grab_keys ( ) . as_string ( )
2016-05-16 23:27:23 +03:00
def _emit_disconnected ( self , errdetails = None ) :
2016-05-16 23:16:13 +03:00
ssherr = self . _tunnels . get_err_output ( )
2016-05-16 23:27:23 +03:00
self . emit ( " disconnected " , errdetails , ssherr )
2015-04-04 03:54:38 +03:00
#######################################################
# Internal API that will be overwritten by subclasses #
#######################################################
def close ( self ) :
raise NotImplementedError ( )
def _is_open ( self ) :
raise NotImplementedError ( )
def _set_username ( self , cred ) :
raise NotImplementedError ( )
def _set_password ( self , cred ) :
raise NotImplementedError ( )
def _send_keys ( self , keys ) :
raise NotImplementedError ( )
def _refresh_grab_keys ( self ) :
raise NotImplementedError ( )
2015-04-12 04:39:24 +03:00
def _open_host ( self ) :
2015-04-04 03:54:38 +03:00
raise NotImplementedError ( )
def _open_fd ( self , fd ) :
raise NotImplementedError ( )
def _get_desktop_resolution ( self ) :
raise NotImplementedError ( )
def _get_scaling ( self ) :
raise NotImplementedError ( )
2017-06-15 15:18:26 +03:00
def _set_scaling ( self , scaling ) :
2015-04-04 03:54:38 +03:00
raise NotImplementedError ( )
def _set_resizeguest ( self , val ) :
raise NotImplementedError ( )
def _get_resizeguest ( self ) :
raise NotImplementedError ( )
def _get_usb_widget ( self ) :
raise NotImplementedError ( )
def _has_usb_redirection ( self ) :
raise NotImplementedError ( )
def _has_agent ( self ) :
raise NotImplementedError ( )
####################################
# APIs accessed by vmmConsolePages #
####################################
def console_is_open ( self ) :
return self . _is_open ( )
def console_grab_focus ( self ) :
return self . _grab_focus ( )
2020-09-09 16:33:58 +03:00
def console_has_keyboard_grab ( self ) :
return bool ( self . _display and self . _keyboard_grab )
2015-04-04 03:54:38 +03:00
def console_set_size_request ( self , * args , * * kwargs ) :
return self . _set_size_request ( * args , * * kwargs )
def console_size_allocate ( self , * args , * * kwargs ) :
return self . _size_allocate ( * args , * * kwargs )
def console_get_pixbuf ( self ) :
return self . _get_pixbuf ( )
2015-04-12 04:39:24 +03:00
def console_open ( self ) :
return self . _open ( )
2015-04-04 03:54:38 +03:00
def console_set_password ( self , val ) :
return self . _set_password ( val )
def console_set_username ( self , val ) :
return self . _set_username ( val )
def console_send_keys ( self , keys ) :
return self . _send_keys ( keys )
def console_get_grab_keys ( self ) :
return self . _get_grab_keys ( )
def console_get_desktop_resolution ( self ) :
2016-05-20 01:27:35 +03:00
ret = self . _get_desktop_resolution ( )
if not ret :
2020-09-09 22:14:05 +03:00
return ret # pragma: no cover
2016-05-20 01:27:35 +03:00
# Don't pass on bogus resolutions
if ( ret [ 0 ] == 0 ) or ( ret [ 1 ] == 0 ) :
return None
return ret
2015-04-04 03:54:38 +03:00
def console_get_scaling ( self ) :
return self . _get_scaling ( )
def console_set_scaling ( self , val ) :
return self . _set_scaling ( val )
def console_get_resizeguest ( self ) :
return self . _get_resizeguest ( )
def console_set_resizeguest ( self , val ) :
return self . _set_resizeguest ( val )
def console_get_usb_widget ( self ) :
return self . _get_usb_widget ( )
def console_has_usb_redirection ( self ) :
return self . _has_usb_redirection ( )
def console_has_agent ( self ) :
return self . _has_agent ( )
2015-04-12 17:33:41 +03:00
def console_remove_display_from_widget ( self , widget ) :
if self . _display and self . _display in widget . get_children ( ) :
widget . remove ( self . _display )
2015-04-04 03:54:38 +03:00
####################
# VNC viewer class #
####################
class VNCViewer ( Viewer ) :
viewer_type = " vnc "
def __init__ ( self , * args , * * kwargs ) :
Viewer . __init__ ( self , * args , * * kwargs )
self . _display = None
self . _desktop_resolution = None
###################
# Private helpers #
###################
def _init_widget ( self ) :
2015-09-16 23:46:39 +03:00
self . _display = GtkVnc . Display ( )
2015-04-04 03:54:38 +03:00
# Make sure viewer doesn't force resize itself
self . _display . set_force_size ( False )
self . _display . set_pointer_grab ( True )
self . emit ( " add-display-widget " , self . _display )
self . _display . realize ( )
self . _display . connect ( " vnc-pointer-grab " ,
self . _make_signal_proxy ( " pointer-grab " ) )
self . _display . connect ( " vnc-pointer-ungrab " ,
self . _make_signal_proxy ( " pointer-ungrab " ) )
2020-09-09 16:33:58 +03:00
self . _display . connect ( " vnc-keyboard-grab " , self . _keyboard_grab_cb )
self . _display . connect ( " vnc-keyboard-ungrab " , self . _keyboard_ungrab_cb )
2015-04-04 03:54:38 +03:00
self . _display . connect ( " vnc-auth-credential " , self . _auth_credential )
2015-04-12 20:04:32 +03:00
self . _display . connect ( " vnc-auth-failure " , self . _auth_failure_cb )
2015-04-04 03:54:38 +03:00
self . _display . connect ( " vnc-initialized " , self . _connected_cb )
self . _display . connect ( " vnc-disconnected " , self . _disconnected_cb )
self . _display . connect ( " vnc-desktop-resize " , self . _desktop_resize )
self . _display . show ( )
2020-09-09 16:33:58 +03:00
def _keyboard_grab_cb ( self , src ) :
self . _keyboard_grab = True
self . emit ( " keyboard-grab " )
def _keyboard_ungrab_cb ( self , src ) :
self . _keyboard_grab = False
self . emit ( " keyboard-ungrab " )
2015-04-04 03:54:38 +03:00
def _connected_cb ( self , ignore ) :
2015-04-12 04:39:24 +03:00
self . _tunnels . unlock ( )
2015-04-04 03:54:38 +03:00
self . emit ( " connected " )
def _disconnected_cb ( self , ignore ) :
2015-04-12 04:39:24 +03:00
self . _tunnels . unlock ( )
2016-05-16 23:16:13 +03:00
self . _emit_disconnected ( )
2015-04-04 03:54:38 +03:00
def _desktop_resize ( self , src_ignore , w , h ) :
self . _desktop_resolution = ( w , h )
# Queue a resize
self . emit ( " size-allocate " , None )
2015-04-12 20:04:32 +03:00
def _auth_failure_cb ( self , ignore , msg ) :
2019-06-17 04:12:39 +03:00
log . debug ( " VNC auth failure. msg= %s " , msg )
2015-04-12 20:04:32 +03:00
self . emit ( " auth-error " , msg , True )
2015-04-04 03:54:38 +03:00
def _auth_credential ( self , src_ignore , credList ) :
values = [ ]
for idx in range ( int ( credList . n_values ) ) :
values . append ( credList . get_nth ( idx ) )
2020-08-29 20:32:37 +03:00
if self . config . CLITestOptions . fake_vnc_username :
values . append ( GtkVnc . DisplayCredential . USERNAME )
2015-04-04 03:54:38 +03:00
withUsername = False
withPassword = False
for cred in values :
2019-06-17 04:12:39 +03:00
log . debug ( " Got credential request %s " , cred )
2015-04-04 03:54:38 +03:00
if cred == GtkVnc . DisplayCredential . PASSWORD :
withPassword = True
elif cred == GtkVnc . DisplayCredential . USERNAME :
withUsername = True
2020-08-29 22:56:56 +03:00
elif cred == GtkVnc . DisplayCredential . CLIENTNAME : # pragma: no cover
2015-04-04 03:54:38 +03:00
self . _display . set_credential ( cred , " libvirt-vnc " )
2020-08-29 22:56:56 +03:00
else : # pragma: no cover
errmsg = (
_ ( " Unable to provide requested credentials to the VNC server. \n "
" The credential type %s is not supported " ) %
str ( cred . value_name ) )
self . emit ( " auth-error " , errmsg , True )
return
2015-04-04 03:54:38 +03:00
if withUsername or withPassword :
self . emit ( " need-auth " , withPassword , withUsername )
###############################
# Private API implementations #
###############################
def close ( self ) :
self . _display . close ( )
def _is_open ( self ) :
return self . _display . is_open ( )
def _get_scaling ( self ) :
2020-08-28 18:55:36 +03:00
if self . _display :
return self . _display . get_scaling ( )
2015-04-04 03:54:38 +03:00
def _set_scaling ( self , scaling ) :
2020-08-28 18:55:36 +03:00
if self . _display :
return self . _display . set_scaling ( scaling )
2015-04-04 03:54:38 +03:00
def _get_grab_keys ( self ) :
return self . _display . get_grab_keys ( ) . as_string ( )
def _refresh_grab_keys ( self ) :
if not self . _display :
2020-08-29 20:32:37 +03:00
return # pragma: no cover
2015-04-04 03:54:38 +03:00
try :
keys = self . config . get_keys_combination ( )
if not keys :
2020-08-29 20:32:37 +03:00
return # pragma: no cover
2015-04-04 03:54:38 +03:00
try :
keys = [ int ( k ) for k in keys . split ( ' , ' ) ]
2020-08-29 20:32:37 +03:00
except Exception : # pragma: no cover
2019-06-17 04:12:39 +03:00
log . debug ( " Error in grab_keys configuration in Gsettings " ,
2015-04-04 03:54:38 +03:00
exc_info = True )
return
seq = GtkVnc . GrabSequence . new ( keys )
self . _display . set_grab_keys ( seq )
2020-08-29 20:32:37 +03:00
except Exception as e : # pragma: no cover
2019-06-17 04:12:39 +03:00
log . debug ( " Error when getting the grab keys combination: %s " ,
2015-04-04 03:54:38 +03:00
str ( e ) )
def _send_keys ( self , keys ) :
return self . _display . send_keys ( [ Gdk . keyval_from_name ( k ) for k in keys ] )
def _get_desktop_resolution ( self ) :
return self . _desktop_resolution
def _set_username ( self , cred ) :
self . _display . set_credential ( GtkVnc . DisplayCredential . USERNAME , cred )
def _set_password ( self , cred ) :
self . _display . set_credential ( GtkVnc . DisplayCredential . PASSWORD , cred )
def _set_resizeguest ( self , val ) :
ignore = val
def _get_resizeguest ( self ) :
return False
def _get_usb_widget ( self ) :
2020-08-28 18:55:36 +03:00
return None # pragma: no cover
2015-04-04 03:54:38 +03:00
def _has_usb_redirection ( self ) :
return False
def _has_agent ( self ) :
2020-08-28 18:55:36 +03:00
return False # pragma: no cover
2015-04-04 03:54:38 +03:00
#######################
# Connection routines #
#######################
2017-06-15 15:18:26 +03:00
def _open ( self ) :
2015-04-04 03:54:38 +03:00
self . _init_widget ( )
2017-06-15 15:18:26 +03:00
return Viewer . _open ( self )
2015-04-04 03:54:38 +03:00
2015-04-12 04:39:24 +03:00
def _open_host ( self ) :
host , port , ignore = self . _ginfo . get_conn_host ( )
2020-08-29 23:34:20 +03:00
log . debug ( " VNC connecting to host= %s port= %s " , host , port )
self . _display . open_host ( host , port )
2015-04-04 03:54:38 +03:00
def _open_fd ( self , fd ) :
self . _display . open_fd ( fd )
######################
# Spice viewer class #
######################
2020-09-08 19:27:38 +03:00
class _SignalTracker :
# Helper class to more conveniently connect and disconnect signals
# from spice objects. This ensures we don't leave circular references
# at object destroy time
def __init__ ( self ) :
self . _sigmap = { }
def _add_hid ( self , obj , hid ) :
if obj not in self . _sigmap :
self . _sigmap [ obj ] = [ ]
self . _sigmap [ obj ] . append ( hid )
def connect ( self , obj , name , handler , * args ) :
hid = GObject . GObject . connect ( obj , name , handler , * args )
self . _add_hid ( obj , hid )
def connect_after ( self , obj , name , handler , * args ) :
hid = GObject . GObject . connect_after ( obj , name , handler , * args )
self . _add_hid ( obj , hid )
def disconnect_obj_signals ( self , obj ) :
for hid in self . _sigmap . get ( obj , [ ] ) :
GObject . GObject . disconnect ( obj , hid )
_SIGS = _SignalTracker ( )
2015-04-04 03:54:38 +03:00
class SpiceViewer ( Viewer ) :
viewer_type = " spice "
def __init__ ( self , * args , * * kwargs ) :
Viewer . __init__ ( self , * args , * * kwargs )
self . _spice_session = None
self . _display = None
2020-09-21 18:35:51 +03:00
self . _audio = None
2015-04-04 03:54:38 +03:00
self . _main_channel = None
self . _display_channel = None
self . _usbdev_manager = None
2020-09-08 19:27:38 +03:00
self . _channels = set ( )
2015-04-04 03:54:38 +03:00
###################
# Private helpers #
###################
2020-09-09 16:33:58 +03:00
def _mouse_grab_cb ( self , src , grab ) :
if grab :
self . emit ( " pointer-grab " )
else :
self . emit ( " pointer-ungrab " )
def _keyboard_grab_cb ( self , src , grab ) :
self . _keyboard_grab = grab
if grab :
self . emit ( " keyboard-grab " )
else :
self . emit ( " keyboard-ungrab " )
2015-04-04 03:54:38 +03:00
def _init_widget ( self ) :
self . emit ( " add-display-widget " , self . _display )
self . _display . realize ( )
2020-09-09 16:33:58 +03:00
self . _display . connect ( " mouse-grab " , self . _mouse_grab_cb )
self . _display . connect ( " keyboard-grab " , self . _keyboard_grab_cb )
2015-04-04 03:54:38 +03:00
self . _display . show ( )
def _create_spice_session ( self ) :
self . _spice_session = SpiceClientGLib . Session ( )
SpiceClientGLib . set_session_option ( self . _spice_session )
gtk_session = SpiceClientGtk . GtkSession . get ( self . _spice_session )
gtk_session . set_property ( " auto-clipboard " , True )
2020-09-08 19:27:38 +03:00
_SIGS . connect ( self . _spice_session , " channel-new " , self . _channel_new_cb )
2015-04-04 03:54:38 +03:00
2016-06-21 16:01:32 +03:00
# Distros might have usb redirection compiled out, like OpenBSD
# https://bugzilla.redhat.com/show_bug.cgi?id=1348479
try :
self . _usbdev_manager = SpiceClientGLib . UsbDeviceManager . get (
self . _spice_session )
2020-09-08 19:27:38 +03:00
_SIGS . connect (
self . _usbdev_manager , " auto-connect-failed " ,
self . _usbdev_redirect_error )
_SIGS . connect (
self . _usbdev_manager , " device-error " ,
self . _usbdev_redirect_error )
2016-06-21 16:01:32 +03:00
2018-03-16 21:38:22 +03:00
autoredir = self . config . get_auto_usbredir ( )
2016-06-21 16:01:32 +03:00
if autoredir :
gtk_session . set_property ( " auto-usbredir " , True )
2020-08-29 20:32:37 +03:00
except Exception : # pragma: no cover
2016-06-21 16:01:32 +03:00
self . _usbdev_manager = None
2019-06-17 04:12:39 +03:00
log . debug ( " Error initializing spice usb device manager " ,
2016-06-21 16:01:32 +03:00
exc_info = True )
2015-04-04 03:54:38 +03:00
#####################
# Channel listeners #
#####################
def _main_channel_event_cb ( self , channel , event ) :
2017-02-07 19:46:25 +03:00
self . _tunnels . unlock ( )
2015-04-04 03:54:38 +03:00
if event == SpiceClientGLib . ChannelEvent . CLOSED :
2016-05-16 23:16:13 +03:00
self . _emit_disconnected ( )
2015-04-04 03:54:38 +03:00
elif event == SpiceClientGLib . ChannelEvent . ERROR_AUTH :
2015-04-12 20:04:32 +03:00
if not self . _spice_session . get_property ( " password " ) :
2019-06-17 04:12:39 +03:00
log . debug ( " Spice channel received ERROR_AUTH, but no "
2015-04-12 20:04:32 +03:00
" password set, assuming it wants credentials. " )
self . emit ( " need-auth " , True , False )
else :
2019-06-17 04:12:39 +03:00
log . debug ( " Spice channel received ERROR_AUTH, but a "
2015-04-12 20:04:32 +03:00
" password is already set. Assuming authentication failed. " )
self . emit ( " auth-error " , channel . get_error ( ) . message , False )
2016-05-16 22:47:37 +03:00
elif " ERROR " in str ( event ) :
# SpiceClientGLib.ChannelEvent.ERROR_CONNECT
# SpiceClientGLib.ChannelEvent.ERROR_IO
# SpiceClientGLib.ChannelEvent.ERROR_LINK
# SpiceClientGLib.ChannelEvent.ERROR_TLS
error = None
if channel . get_error ( ) :
error = channel . get_error ( ) . message
2019-06-17 04:12:39 +03:00
log . debug ( " Spice channel event= %s message= %s " , event , error )
2016-05-16 23:27:23 +03:00
msg = _ ( " Encountered SPICE % (error-name)s " ) % {
" error-name " : event . value_nick }
if error :
msg + = " : %s " % error
self . _emit_disconnected ( msg )
2015-04-04 03:54:38 +03:00
def _fd_channel_event_cb ( self , channel , event ) :
# When we see any event from the channel, release the
# associated tunnel lock
channel . disconnect_by_func ( self . _fd_channel_event_cb )
self . _tunnels . unlock ( )
def _channel_open_fd_request ( self , channel , tls_ignore ) :
2015-04-12 19:17:55 +03:00
if not self . _tunnels :
# Can happen if we close the details window and clear self._tunnels
# while initially connecting to spice and channel FD requests
# are still rolling in
2020-08-29 20:32:37 +03:00
return # pragma: no cover
2015-04-12 19:17:55 +03:00
2019-06-17 04:12:39 +03:00
log . debug ( " Requesting fd for channel: %s " , channel )
2015-04-04 03:54:38 +03:00
channel . connect_after ( " channel-event " , self . _fd_channel_event_cb )
2016-05-08 00:53:10 +03:00
fd = self . _get_fd_for_open ( )
2015-04-04 03:54:38 +03:00
channel . open_fd ( fd )
def _channel_new_cb ( self , session , channel ) :
2020-09-08 19:27:38 +03:00
self . _channels . add ( channel )
_SIGS . connect ( channel , " open-fd " , self . _channel_open_fd_request )
2015-04-04 03:54:38 +03:00
2017-10-11 14:35:41 +03:00
if ( isinstance ( channel , SpiceClientGLib . MainChannel ) and
2015-04-04 03:54:38 +03:00
not self . _main_channel ) :
self . _main_channel = channel
2020-09-08 19:27:38 +03:00
_SIGS . connect_after (
self . _main_channel , " channel-event " ,
2015-04-04 03:54:38 +03:00
self . _main_channel_event_cb )
2020-09-08 19:27:38 +03:00
_SIGS . connect_after (
self . _main_channel , " notify::agent-connected " ,
2015-04-04 03:54:38 +03:00
self . _agent_connected_cb )
elif ( type ( channel ) == SpiceClientGLib . DisplayChannel and
2017-09-20 10:36:27 +03:00
not self . _display ) :
2015-04-04 03:54:38 +03:00
channel_id = channel . get_property ( " channel-id " )
2020-08-29 20:32:37 +03:00
if channel_id != 0 : # pragma: no cover
2019-06-17 04:12:39 +03:00
log . debug ( " Spice multi-head unsupported " )
2015-04-04 03:54:38 +03:00
return
self . _display_channel = channel
self . _display = SpiceClientGtk . Display . new ( self . _spice_session ,
channel_id )
self . _init_widget ( )
self . emit ( " connected " )
2020-09-21 18:35:51 +03:00
elif ( type ( channel ) in [ SpiceClientGLib . PlaybackChannel ,
SpiceClientGLib . RecordChannel ] and
not self . _audio ) :
# It's unclear why we need this audio handle, but it
# does matter:
# https://bugzilla.redhat.com/show_bug.cgi?id=1881080
self . _audio = SpiceClientGLib . Audio . get ( self . _spice_session , None )
2015-04-04 03:54:38 +03:00
def _agent_connected_cb ( self , src , val ) :
2020-08-29 20:32:37 +03:00
self . emit ( " agent-connected " ) # pragma: no cover
2015-04-04 03:54:38 +03:00
################################
# Internal API implementations #
################################
def close ( self ) :
if self . _spice_session is not None :
2020-09-08 19:27:38 +03:00
_SIGS . disconnect_obj_signals ( self . _spice_session )
2015-04-04 03:54:38 +03:00
self . _spice_session . disconnect ( )
self . _spice_session = None
2020-09-21 18:35:51 +03:00
self . _audio = None
2015-04-04 03:54:38 +03:00
if self . _display :
self . _display . destroy ( )
self . _display = None
self . _display_channel = None
2020-09-08 19:27:38 +03:00
for channel in self . _channels :
_SIGS . disconnect_obj_signals ( channel )
self . _channels = None
self . _main_channel = None
_SIGS . disconnect_obj_signals ( self . _usbdev_manager )
2015-04-04 03:54:38 +03:00
self . _usbdev_manager = None
def _is_open ( self ) :
return self . _spice_session is not None
def _refresh_grab_keys ( self ) :
if not self . _display :
2020-08-29 20:32:37 +03:00
return # pragma: no cover
2015-04-04 03:54:38 +03:00
try :
keys = self . config . get_keys_combination ( )
if not keys :
2020-08-29 20:32:37 +03:00
return # pragma: no cover
2015-04-04 03:54:38 +03:00
try :
keys = [ int ( k ) for k in keys . split ( ' , ' ) ]
2020-08-29 20:32:37 +03:00
except Exception : # pragma: no cover
2019-06-17 04:12:39 +03:00
log . debug ( " Error in grab_keys configuration in Gsettings " ,
2015-04-04 03:54:38 +03:00
exc_info = True )
return
seq = SpiceClientGtk . GrabSequence . new ( keys )
self . _display . set_grab_keys ( seq )
2020-08-29 20:32:37 +03:00
except Exception as e : # pragma: no cover
2019-06-17 04:12:39 +03:00
log . debug ( " Error when getting the grab keys combination: %s " ,
2015-04-04 03:54:38 +03:00
str ( e ) )
def _send_keys ( self , keys ) :
return self . _display . send_keys ( [ Gdk . keyval_from_name ( k ) for k in keys ] ,
SpiceClientGtk . DisplayKeyEvent . CLICK )
def _get_desktop_resolution ( self ) :
if not self . _display_channel :
2020-09-09 22:14:05 +03:00
return None # pragma: no cover
2015-04-04 03:54:38 +03:00
return self . _display_channel . get_properties ( " width " , " height " )
def _has_agent ( self ) :
if not self . _main_channel :
2020-08-29 20:32:37 +03:00
return False # pragma: no cover
2020-08-28 18:55:36 +03:00
return ( self . _main_channel . get_property ( " agent-connected " ) or
self . config . CLITestOptions . spice_agent )
2015-04-04 03:54:38 +03:00
2015-04-12 04:39:24 +03:00
def _open_host ( self ) :
host , port , tlsport = self . _ginfo . get_conn_host ( )
2015-04-04 03:54:38 +03:00
self . _create_spice_session ( )
2019-06-17 04:12:39 +03:00
log . debug ( " Spice connecting to host= %s port= %s tlsport= %s " ,
2016-02-15 20:54:46 +03:00
host , port , tlsport )
2015-04-04 03:54:38 +03:00
self . _spice_session . set_property ( " host " , str ( host ) )
if port :
self . _spice_session . set_property ( " port " , str ( port ) )
if tlsport :
self . _spice_session . set_property ( " tls-port " , str ( tlsport ) )
self . _spice_session . connect ( )
def _open_fd ( self , fd ) :
self . _create_spice_session ( )
self . _spice_session . open_fd ( fd )
2017-06-15 15:18:26 +03:00
def _set_username ( self , cred ) :
2020-08-29 20:32:37 +03:00
ignore = cred # pragma: no cover
2015-04-04 03:54:38 +03:00
def _set_password ( self , cred ) :
self . _spice_session . set_property ( " password " , cred )
2016-05-18 23:14:51 +03:00
fd = self . _get_fd_for_open ( )
if fd is not None :
self . _spice_session . open_fd ( fd )
2015-04-04 03:54:38 +03:00
else :
2020-08-29 20:32:37 +03:00
self . _spice_session . connect ( ) # pragma: no cover
2015-04-04 03:54:38 +03:00
def _get_scaling ( self ) :
2020-08-28 18:55:36 +03:00
if self . _display :
return self . _display . get_property ( " scaling " )
2015-04-04 03:54:38 +03:00
def _set_scaling ( self , scaling ) :
2020-08-28 18:55:36 +03:00
if self . _display :
self . _display . set_property ( " scaling " , scaling )
2015-04-04 03:54:38 +03:00
def _set_resizeguest ( self , val ) :
if self . _display :
self . _display . set_property ( " resize-guest " , val )
def _get_resizeguest ( self ) :
if self . _display :
return self . _display . get_property ( " resize-guest " )
2020-08-29 20:32:37 +03:00
return False # pragma: no cover
2015-04-04 03:54:38 +03:00
def _usbdev_redirect_error ( self , spice_usbdev_widget , spice_usb_device ,
2020-08-29 20:32:37 +03:00
errstr ) : # pragma: no cover
2015-04-04 03:54:38 +03:00
ignore = spice_usbdev_widget
ignore = spice_usb_device
self . emit ( " usb-redirect-error " , errstr )
def _get_usb_widget ( self ) :
if not self . _spice_session :
2020-08-29 20:32:37 +03:00
return # pragma: no cover
2015-04-04 03:54:38 +03:00
usbwidget = SpiceClientGtk . UsbDeviceWidget . new ( self . _spice_session ,
None )
usbwidget . connect ( " connect-failed " , self . _usbdev_redirect_error )
return usbwidget
def _has_usb_redirection ( self ) :
if not self . _spice_session or not self . _usbdev_manager :
2020-08-29 20:32:37 +03:00
return False # pragma: no cover
2015-04-04 03:54:38 +03:00
for c in self . _spice_session . get_channels ( ) :
if c . __class__ is SpiceClientGLib . UsbredirChannel :
return True
return False