2017-01-16 20:59:50 +01:00
/ * Copyright ( c ) Citrix Systems , Inc .
2013-06-24 13:41:48 +02:00
* All rights reserved .
*
* Redistribution and use in source and binary forms ,
* with or without modification , are permitted provided
* that the following conditions are met :
*
* * Redistributions of source code must retain the above
* copyright notice , this list of conditions and the
* following disclaimer .
* * Redistributions in binary form must reproduce the above
* copyright notice , this list of conditions and the
* following disclaimer in the documentation and / or other
* materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES ,
* INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL ,
* SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING ,
* BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY ,
* WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING
* NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE .
* /
using System ;
2021-11-29 16:20:33 +01:00
using System.Collections.Generic ;
2013-06-24 13:41:48 +02:00
using System.ComponentModel ;
2021-11-29 16:20:33 +01:00
using System.Diagnostics ;
2013-06-24 13:41:48 +02:00
using System.Drawing ;
2021-11-29 16:20:33 +01:00
using System.IO ;
2013-06-24 13:41:48 +02:00
using System.Linq ;
using System.Runtime.InteropServices ;
using System.Threading ;
2021-11-29 16:20:33 +01:00
using System.Windows.Forms ;
2013-06-24 13:41:48 +02:00
using XenAdmin.Commands ;
2016-08-24 02:38:51 +02:00
using XenAdmin.Controls.ConsoleTab ;
2021-04-30 18:39:57 +02:00
using XenAdmin.Controls.GradientPanel ;
2021-11-29 16:20:33 +01:00
using XenAdmin.Core ;
using XenAdmin.Dialogs ;
using XenAdmin.Network ;
using XenAPI ;
2016-07-05 00:05:51 +02:00
2013-06-24 13:41:48 +02:00
namespace XenAdmin.ConsoleView
{
public partial class VNCTabView : UserControl
{
private static readonly string UseRDP = Messages . VNC_RDESKTOP ;
2015-01-09 04:24:26 +01:00
private static readonly string enableRDP = Messages . VNC_RDESKTOP_TURN_ON ;
2013-06-24 13:41:48 +02:00
// public only for the automated tests.
public static readonly string UseVNC = Messages . VNC_VIRTUAL_CONSOLE ;
public static readonly string UseXVNC = Messages . VNC_X_CONSOLE ;
private static readonly string UseStandardDesktop = Messages . VNC_DEFAULT_DESKTOP ;
public const int INS_KEY_TIMEOUT = 500 ;
private static readonly log4net . ILog log = log4net . LogManager . GetLogger ( System . Reflection . MethodBase . GetCurrentMethod ( ) . DeclaringType ) ;
private readonly XSVNCScreen vncScreen ;
private readonly VNCView parentVNCView ;
private readonly VM source ;
private readonly Host targetHost ;
2014-08-22 18:36:41 +02:00
private VM_guest_metrics guestMetrics = null ;
2016-08-24 14:51:50 +02:00
private FullScreenForm fullscreenForm ;
2016-08-24 02:38:51 +02:00
private FullScreenHint fullscreenHint ;
2013-06-24 13:41:48 +02:00
private Size LastDesktopSize ;
private bool switchOnTabOpened = false ;
2021-04-30 18:39:57 +02:00
private Font titleLabelFont = new Font ( DefaultFont . FontFamily , DefaultFont . Size + 1f , FontStyle . Bold ) ;
2013-06-24 13:41:48 +02:00
/// <summary>
/// Whether to ignore VNC resize events. We turn this on when changing
/// the scaling settings, because this in turn triggers a VNC resize.
/// </summary>
2022-03-29 16:46:31 +02:00
private bool ignoringResizes ;
2013-06-24 13:41:48 +02:00
2022-03-29 16:46:31 +02:00
private bool ignoreScaleChange ;
2013-06-24 13:41:48 +02:00
internal readonly ConsoleKeyHandler KeyHandler = new ConsoleKeyHandler ( ) ;
2022-03-29 16:46:31 +02:00
private bool HasRDP = > source ! = null & & source . HasRDP ( ) ;
2014-08-22 18:36:41 +02:00
2022-03-29 16:46:31 +02:00
private bool RDPEnabled = > source ! = null & & source . RDPEnabled ( ) ;
2014-12-04 09:35:36 +01:00
2022-03-29 16:46:31 +02:00
private bool RDPControlEnabled = > source ! = null & & source . RDPControlEnabled ( ) ;
2015-01-27 07:13:50 +01:00
2015-01-28 03:02:19 +01:00
public bool IsRDPControlEnabled ( ) { return RDPControlEnabled ; }
2013-06-24 13:41:48 +02:00
public VNCTabView ( VNCView parent , VM source , string elevatedUsername , string elevatedPassword )
{
Program . AssertOnEventThread ( ) ;
InitializeComponent ( ) ;
2017-11-13 17:51:56 +01:00
var tooltipForGeneralInformationMessage = new ToolTip ( ) ;
tooltipForGeneralInformationMessage . SetToolTip ( labelGeneralInformationMessage , labelGeneralInformationMessage . Text ) ;
2021-04-30 18:39:57 +02:00
HostLabel . Font = titleLabelFont ;
HostLabel . ForeColor = HorizontalGradientPanel . TextColor ;
multipleDvdIsoList1 . LabelSingleDvdForeColor = HorizontalGradientPanel . TextColor ;
multipleDvdIsoList1 . LabelNewCdForeColor = HorizontalGradientPanel . TextColor ;
multipleDvdIsoList1 . LinkLabelLinkColor = HorizontalGradientPanel . TextColor ;
2013-06-24 13:41:48 +02:00
#pragma warning disable 0219
// Force the handle to be created, because resize events
// could be fired before this component is placed on-screen.
IntPtr _ = Handle ;
#pragma warning restore 0219
2022-03-29 16:46:31 +02:00
parentVNCView = parent ;
scaleCheckBox . Checked = false ;
2013-06-24 13:41:48 +02:00
this . source = source ;
2022-03-29 16:46:31 +02:00
guestMetrics = source . Connection . Resolve ( source . guest_metrics ) ;
if ( guestMetrics ! = null )
2014-12-04 09:35:36 +01:00
guestMetrics . PropertyChanged + = guestMetrics_PropertyChanged ;
2017-09-03 04:33:29 +02:00
log . DebugFormat ( "'{0}' console: Register Server_PropertyChanged event listener on {0}" , this . source . Name ( ) ) ;
2013-06-24 13:41:48 +02:00
this . source . PropertyChanged + = Server_PropertyChanged ;
Host_CollectionChangedWithInvoke = Program . ProgramInvokeHandler ( Host_CollectionChanged ) ;
VM_CollectionChangedWithInvoke = Program . ProgramInvokeHandler ( VM_CollectionChanged ) ;
source . Connection . Cache . RegisterCollectionChanged < VM > ( VM_CollectionChangedWithInvoke ) ;
2021-02-24 16:44:38 +01:00
if ( source . IsControlDomainZero ( out Host host ) )
2013-06-24 13:41:48 +02:00
{
2021-02-24 16:44:38 +01:00
log . DebugFormat ( "'{0}' console: Register Server_PropertyChanged event listener on {1}" , this . source . Name ( ) , host . Name ( ) ) ;
host . PropertyChanged + = Server_PropertyChanged ;
Host_metrics hostMetrics = source . Connection . Resolve ( host . metrics ) ;
if ( hostMetrics ! = null )
2013-06-24 13:41:48 +02:00
{
2021-02-24 16:44:38 +01:00
log . DebugFormat ( "'{0}' console: Register Server_PropertyChanged event listener on host metrics" , this . source . Name ( ) ) ;
hostMetrics . PropertyChanged + = Server_PropertyChanged ;
}
2013-06-24 13:41:48 +02:00
2021-02-24 16:44:38 +01:00
HostLabel . Text = string . Format ( Messages . CONSOLE_HOST , host . Name ( ) ) ;
HostLabel . Visible = true ;
}
else if ( source . IsSrDriverDomain ( out SR sr ) )
{
log . DebugFormat ( "'{0}' console: Register Server_PropertyChanged event listener on {1}" , this . source . Name ( ) , sr . Name ( ) ) ;
sr . PropertyChanged + = Server_PropertyChanged ;
2013-06-24 13:41:48 +02:00
2021-02-24 16:44:38 +01:00
HostLabel . Text = string . Format ( Messages . CONSOLE_SR_DRIVER_DOMAIN , sr . Name ( ) ) ;
HostLabel . Visible = true ;
2013-06-24 13:41:48 +02:00
}
else
{
source . Connection . Cache . RegisterCollectionChanged < Host > ( Host_CollectionChangedWithInvoke ) ;
targetHost = source . GetStorageHost ( false ) ;
2021-11-29 16:20:33 +01:00
2013-06-24 13:41:48 +02:00
foreach ( Host cachedHost in source . Connection . Cache . Hosts )
{
log . DebugFormat ( "'{0}' console: Register Server_EnabledPropertyChanged event listener on {1}" ,
2017-09-03 04:33:29 +02:00
source . Name ( ) , cachedHost . Name ( ) ) ;
2013-06-24 13:41:48 +02:00
cachedHost . PropertyChanged + = Server_EnabledPropertyChanged ;
}
2021-11-29 16:20:33 +01:00
2013-06-24 13:41:48 +02:00
HostLabel . Visible = false ;
}
2017-09-03 04:33:29 +02:00
log . DebugFormat ( "'{0}' console: Update power state (on VNCTabView constructor)" , this . source . Name ( ) ) ;
2013-06-24 13:41:48 +02:00
updatePowerState ( ) ;
2022-03-29 16:46:31 +02:00
vncScreen = new XSVNCScreen ( source , new EventHandler ( RDPorVNCResizeHandler ) , this , elevatedUsername , elevatedPassword ) ;
2016-08-24 12:48:42 +02:00
ShowGpuWarningIfRequired ( vncScreen . MustConnectRemoteDesktop ( ) ) ;
vncScreen . GpuStatusChanged + = ShowGpuWarningIfRequired ;
2013-06-24 13:41:48 +02:00
2022-03-29 16:46:31 +02:00
if ( source . IsControlDomainZero ( out var _ ) | | source . IsHVM ( ) & & ! HasRDP ) //Linux HVM guests should only have one console: the console switch button vanishes altogether.
2013-06-24 13:41:48 +02:00
{
toggleConsoleButton . Visible = false ;
}
else
{
toggleConsoleButton . Visible = true ;
2022-03-29 16:46:31 +02:00
vncScreen . UserCancelledAuth + = OnUserCancelledAuth ;
vncScreen . VncConnectionAttemptCancelled + = OnVncConnectionAttemptCancelled ;
2013-06-24 13:41:48 +02:00
}
2022-03-29 16:46:31 +02:00
vncScreen . OnDetectRDP = OnDetectRDP ;
vncScreen . OnDetectVNC = OnDetectVNC ;
2022-03-29 16:34:09 +02:00
2013-06-24 13:41:48 +02:00
LastDesktopSize = vncScreen . DesktopSize ;
2022-03-29 16:46:31 +02:00
insKeyTimer = new System . Threading . Timer ( new TimerCallback ( notInsKeyPressed ) ) ;
2013-06-24 13:41:48 +02:00
Properties . Settings . Default . PropertyChanged + = Default_PropertyChanged ;
registerShortCutKeys ( ) ;
//
// Ctlr - Alt - Ins send Ctrl - Alt - Delete, and cancels and pending full screen.
//
KeyHandler . AddKeyHandler ( ConsoleShortcutKey . CTRL_ALT_INS , cancelWaitForInsKeyAndSendCAD ) ;
2022-03-29 16:46:31 +02:00
vncScreen . Parent = contentPanel ;
vncScreen . Dock = DockStyle . Fill ;
2013-06-24 13:41:48 +02:00
string rdpLabel = GuessNativeConsoleLabel ( source ) ;
2022-03-29 16:46:31 +02:00
toggleConsoleButton . Text = rdpLabel ;
2013-06-24 13:41:48 +02:00
UpdateFullScreenButton ( ) ;
UpdateDockButton ( ) ;
setupCD ( ) ;
UpdateParentMinimumSize ( ) ;
2019-01-31 00:00:56 +01:00
UpdateTooltipOfToggleButton ( ) ;
2015-02-13 07:16:17 +01:00
2015-04-29 12:32:14 +02:00
UpdateOpenSSHConsoleButtonState ( ) ;
2013-06-24 13:41:48 +02:00
toggleConsoleButton . EnabledChanged + = toggleConsoleButton_EnabledChanged ;
2015-03-24 06:40:22 +01:00
2015-05-06 13:11:50 +02:00
//If RDP enabled and AutoSwitchToRDP selected, switch RDP connection will be done when VNC already get the correct screen resolution.
2015-03-24 06:40:22 +01:00
//This change is only for Cream, because RDP port scan was removed in Cream.
2021-11-29 16:20:33 +01:00
if ( Properties . Settings . Default . AutoSwitchToRDP & & RDPEnabled )
2015-05-06 13:11:50 +02:00
vncScreen . AutoSwitchRDPLater = true ;
2013-06-24 13:41:48 +02:00
}
2017-11-13 17:51:56 +01:00
void ShowOrHideRdpVersionWarning ( )
{
pictureBoxGeneralInformationMessage . Visible = labelGeneralInformationMessage . Visible = vncScreen . RdpVersionWarningNeeded ;
}
2016-07-05 00:05:51 +02:00
public bool IsScaled
{
get { return scaleCheckBox . Checked ; }
set { scaleCheckBox . Checked = value ; }
}
2013-06-24 13:41:48 +02:00
//CA-75479 - add to aid debugging
private void toggleConsoleButton_EnabledChanged ( object sender , EventArgs e )
{
ButtonBase button = sender as ButtonBase ;
2021-11-29 16:20:33 +01:00
if ( button = = null )
2013-06-24 13:41:48 +02:00
return ;
string format = "Console tab 'Switch to...' button disabled for VM '{0}'" ;
2021-11-29 16:20:33 +01:00
2013-06-24 13:41:48 +02:00
if ( button . Enabled )
format = "Console tab 'Switch to...' button enabled for VM '{0}'" ;
log . DebugFormat ( format , source = = null ? "unknown/null" : source . name_label ) ;
}
private void VM_CollectionChanged ( object sender , CollectionChangeEventArgs e )
{
if ( e . Action = = CollectionChangeAction . Remove )
{
VM vm = e . Element as VM ;
if ( source ! = null & & vm . uuid = = source . uuid )
{
// the VM we are looking at has gone away. We should redock if necessary, otherwise it
// avoids the destroy (and re-create in the case of dom0) when the tab itself goes.
2022-02-02 12:13:14 +01:00
if ( ! parentVNCView . IsDocked )
2015-11-10 14:12:13 +01:00
parentVNCView . DockUnDock ( ) ;
2013-06-24 13:41:48 +02:00
}
}
}
private void Host_CollectionChanged ( object sender , CollectionChangeEventArgs e )
{
2021-02-24 16:44:38 +01:00
if ( source . IsControlDomainZero ( out _ ) )
2013-06-24 13:41:48 +02:00
return ;
2021-02-24 16:44:38 +01:00
if ( e . Element is Host host )
2013-06-24 13:41:48 +02:00
{
if ( e . Action = = CollectionChangeAction . Add )
{
log . DebugFormat ( "'{0}' console: Register Server_EnabledPropertyChanged event listener on {1}" ,
2017-09-03 04:33:29 +02:00
source . Name ( ) , host . Name ( ) ) ;
2013-06-24 13:41:48 +02:00
host . PropertyChanged - = Server_EnabledPropertyChanged ;
host . PropertyChanged + = Server_EnabledPropertyChanged ;
}
else if ( e . Action = = CollectionChangeAction . Remove )
{
log . DebugFormat ( "'{0}' console: Unregister Server_EnabledPropertyChanged event listener on {1}" ,
2017-09-03 04:33:29 +02:00
source . Name ( ) , host . Name ( ) ) ;
2013-06-24 13:41:48 +02:00
host . PropertyChanged - = Server_EnabledPropertyChanged ;
}
}
}
private void UnregisterEventListeners ( )
{
Properties . Settings . Default . PropertyChanged - = new PropertyChangedEventHandler ( Default_PropertyChanged ) ;
if ( source = = null )
return ;
2017-09-03 04:33:29 +02:00
log . DebugFormat ( "'{0}' console: Unregister Server_PropertyChanged event listener on {0}" , source . Name ( ) ) ;
2013-06-24 13:41:48 +02:00
source . PropertyChanged - = new PropertyChangedEventHandler ( Server_PropertyChanged ) ;
source . Connection . Cache . DeregisterCollectionChanged < VM > ( VM_CollectionChangedWithInvoke ) ;
2022-03-29 16:46:31 +02:00
if ( guestMetrics ! = null )
guestMetrics . PropertyChanged - = guestMetrics_PropertyChanged ;
2014-09-08 13:04:57 +02:00
2021-02-24 16:44:38 +01:00
if ( source . IsControlDomainZero ( out Host host ) )
2013-06-24 13:41:48 +02:00
{
2021-02-24 16:44:38 +01:00
log . DebugFormat ( "'{0}' console: Unregister Server_PropertyChanged event listener on {1}" ,
source . Name ( ) , host . Name ( ) ) ;
host . PropertyChanged - = Server_PropertyChanged ;
2013-06-24 13:41:48 +02:00
2021-02-24 16:44:38 +01:00
Host_metrics hostMetrics = source . Connection . Resolve < Host_metrics > ( host . metrics ) ;
if ( hostMetrics ! = null )
{
log . DebugFormat ( "'{0}' console: Unregister Server_PropertyChanged event listener on host metrics" ,
source . Name ( ) ) ;
hostMetrics . PropertyChanged - = Server_PropertyChanged ;
2013-06-24 13:41:48 +02:00
}
}
2021-02-24 16:44:38 +01:00
else if ( source . IsSrDriverDomain ( out SR sr ) )
{
log . DebugFormat ( "'{0}' console: Unregister Server_PropertyChanged event listener on {1}" ,
source . Name ( ) , sr . Name ( ) ) ;
sr . PropertyChanged - = Server_PropertyChanged ;
}
2013-06-24 13:41:48 +02:00
else
{
source . Connection . Cache . DeregisterCollectionChanged < Host > ( Host_CollectionChangedWithInvoke ) ;
foreach ( Host cachedHost in source . Connection . Cache . Hosts )
{
log . DebugFormat ( "'{0}' console: Unregister Server_EnabledPropertyChanged event listener on {1}" ,
2017-09-03 04:33:29 +02:00
source . Name ( ) , cachedHost . Name ( ) ) ;
2013-06-24 13:41:48 +02:00
cachedHost . PropertyChanged - = Server_EnabledPropertyChanged ;
}
}
}
/// <summary>
/// Occurs when one of the properties in the preferences / options window changes.
/// </summary>
private void Default_PropertyChanged ( object sender , PropertyChangedEventArgs e )
{
2015-04-16 15:17:02 +02:00
Program . Invoke ( this , delegate
2013-06-24 13:41:48 +02:00
{
deregisterShortCutKeys ( ) ;
registerShortCutKeys ( ) ;
UpdateParentMinimumSize ( ) ;
} ) ;
}
internal void UpdateParentMinimumSize ( )
{
if ( Parent ! = null )
{
int [ ] bottomTableWidths = tableLayoutPanel1 . GetColumnWidths ( ) ;
int bottomPanelWidth = bottomTableWidths . Where ( ( t , i ) = > tableLayoutPanel1 . ColumnStyles [ i ] . SizeType ! = SizeType . Percent ) . Sum ( ) ;
2017-12-06 16:59:13 +01:00
Parent . MinimumSize = new Size ( bottomPanelWidth + 100 , 400 ) ;
2013-06-24 13:41:48 +02:00
}
}
private void registerShortCutKeys ( )
{
Program . AssertOnEventThread ( ) ;
if ( vncScreen = = null )
return ;
if ( Properties . Settings . Default . FullScreenShortcutKey = = 0 )
{
// Ctrl + Alt
KeyHandler . AddKeyHandler ( ConsoleShortcutKey . CTRL_ALT , waitForInsKey ) ;
}
else if ( Properties . Settings . Default . FullScreenShortcutKey = = 1 )
{
// Ctrl + Alt + F
KeyHandler . AddKeyHandler ( ConsoleShortcutKey . CTRL_ALT_F , toggleFullscreen ) ;
}
else if ( Properties . Settings . Default . FullScreenShortcutKey = = 2 )
{
// F12
KeyHandler . AddKeyHandler ( ConsoleShortcutKey . F12 , toggleFullscreen ) ;
}
else if ( Properties . Settings . Default . FullScreenShortcutKey = = 3 )
{
// Ctrl + Enter
KeyHandler . AddKeyHandler ( ConsoleShortcutKey . CTRL_ENTER , toggleFullscreen ) ;
}
UpdateFullScreenButton ( ) ;
// CA-10943
if ( Properties . Settings . Default . DockShortcutKey = = 1 )
{
// Alt + Shift + U
2021-11-29 16:20:33 +01:00
KeyHandler . AddKeyHandler ( ConsoleShortcutKey . ALT_SHIFT_U , toggleDockUnDock ) ;
}
2013-06-24 13:41:48 +02:00
else if ( Properties . Settings . Default . DockShortcutKey = = 2 )
{
// F11
2021-11-29 16:20:33 +01:00
KeyHandler . AddKeyHandler ( ConsoleShortcutKey . F11 , toggleDockUnDock ) ;
2013-06-24 13:41:48 +02:00
}
else if ( Properties . Settings . Default . DockShortcutKey = = 0 )
{
// <none>
KeyHandler . RemoveKeyHandler ( ConsoleShortcutKey . ALT_SHIFT_U ) ;
2021-11-29 16:20:33 +01:00
KeyHandler . RemoveKeyHandler ( ConsoleShortcutKey . F11 ) ;
2013-06-24 13:41:48 +02:00
}
UpdateDockButton ( ) ;
// Uncapture keyboard and mouse Key
if ( Properties . Settings . Default . UncaptureShortcutKey = = 0 )
{
// Right Ctrl
2021-11-29 16:20:33 +01:00
KeyHandler . AddKeyHandler ( ConsoleShortcutKey . RIGHT_CTRL , ToggleConsoleFocus ) ;
2013-06-24 13:41:48 +02:00
}
else if ( Properties . Settings . Default . UncaptureShortcutKey = = 1 )
{
// Left Alt
2021-11-29 16:20:33 +01:00
KeyHandler . AddKeyHandler ( ConsoleShortcutKey . LEFT_ALT , ToggleConsoleFocus ) ;
2013-06-24 13:41:48 +02:00
}
}
private void deregisterShortCutKeys ( )
{
Program . AssertOnEventThread ( ) ;
if ( vncScreen = = null )
return ;
if ( Properties . Settings . Default . FullScreenShortcutKey ! = 0 )
{
// Ctrl + Alt
KeyHandler . RemoveKeyHandler ( ConsoleShortcutKey . CTRL_ALT ) ;
}
if ( Properties . Settings . Default . FullScreenShortcutKey ! = 1 )
{
// Ctrl + Alt + F
KeyHandler . RemoveKeyHandler ( ConsoleShortcutKey . CTRL_ALT_F ) ;
}
if ( Properties . Settings . Default . FullScreenShortcutKey ! = 2 )
{
// F12
KeyHandler . RemoveKeyHandler ( ConsoleShortcutKey . F12 ) ;
}
if ( Properties . Settings . Default . FullScreenShortcutKey ! = 3 )
{
// Ctrl + Enter
2021-11-29 16:20:33 +01:00
KeyHandler . RemoveKeyHandler ( ConsoleShortcutKey . CTRL_ENTER ) ;
2013-06-24 13:41:48 +02:00
}
if ( Properties . Settings . Default . DockShortcutKey ! = 1 )
{
// Alt + Shift + U
2021-11-29 16:20:33 +01:00
KeyHandler . RemoveKeyHandler ( ConsoleShortcutKey . ALT_SHIFT_U ) ;
2013-06-24 13:41:48 +02:00
}
if ( Properties . Settings . Default . DockShortcutKey ! = 2 )
{
// F11
2021-11-29 16:20:33 +01:00
KeyHandler . RemoveKeyHandler ( ConsoleShortcutKey . F11 ) ;
2013-06-24 13:41:48 +02:00
}
// Uncapture keyboard and mouse Key
if ( Properties . Settings . Default . UncaptureShortcutKey ! = 0 )
{
// Right Ctrl
2021-11-29 16:20:33 +01:00
KeyHandler . RemoveKeyHandler ( ConsoleShortcutKey . RIGHT_CTRL ) ;
2013-06-24 13:41:48 +02:00
}
if ( Properties . Settings . Default . UncaptureShortcutKey ! = 1 )
{
// Left Alt
2021-11-29 16:20:33 +01:00
KeyHandler . RemoveKeyHandler ( ConsoleShortcutKey . LEFT_ALT ) ;
2013-06-24 13:41:48 +02:00
}
}
public void UpdateDockButton ( )
{
2022-02-02 12:13:14 +01:00
dockButton . Text = parentVNCView . IsDocked ? Messages . VNC_UNDOCK : Messages . VNC_REDOCK ;
2013-06-24 13:41:48 +02:00
if ( Properties . Settings . Default . DockShortcutKey = = 1 )
{
dockButton . Text + = Messages . VNC_DOCK_ALT_SHIFT_U ;
}
else if ( Properties . Settings . Default . DockShortcutKey = = 2 )
{
dockButton . Text + = Messages . VNC_DOCK_F11 ;
}
2022-02-02 12:13:14 +01:00
dockButton . Image = parentVNCView . IsDocked ? Images . StaticImages . detach_24 : Images . StaticImages . attach_24 ;
2013-06-24 13:41:48 +02:00
}
public void UpdateFullScreenButton ( )
{
switch ( Properties . Settings . Default . FullScreenShortcutKey )
{
case 0 :
fullscreenButton . Text = Messages . VNC_FULLSCREEN_CTRL_ALT ;
break ;
case 1 :
fullscreenButton . Text = Messages . VNC_FULLSCREEN_CTRL_ALT_F ;
break ;
case 2 :
fullscreenButton . Text = Messages . VNC_FULLSCREEN_F12 ;
break ;
default :
fullscreenButton . Text = Messages . VNC_FULLSCREEN_CTRL_ENTER ;
break ;
}
}
private void Server_PropertyChanged ( object sender , PropertyChangedEventArgs e )
{
if ( e . PropertyName = = "power_state"
| | e . PropertyName = = "live"
| | e . PropertyName = = "allowed_operations" )
{
log . DebugFormat ( "'{0}' console: Update power state, after receiving property change notification, PropertyName='{1}'" , sender . ToString ( ) , e . PropertyName ) ;
updatePowerState ( ) ;
}
else if ( e . PropertyName = = "VBDs" )
{
//The CD device may have changed
2015-04-16 15:17:02 +02:00
Program . Invoke ( this , setupCD ) ;
2013-06-24 13:41:48 +02:00
}
2014-08-22 18:36:41 +02:00
else if ( e . PropertyName = = "guest_metrics" )
{
2014-08-22 18:36:41 +02:00
var newGuestMetrics = source . Connection . Resolve ( source . guest_metrics ) ;
2021-11-29 16:20:33 +01:00
2014-08-22 18:36:41 +02:00
//unsubscribing from the previous instance's event
2022-03-29 16:46:31 +02:00
if ( guestMetrics ! = null )
guestMetrics . PropertyChanged - = guestMetrics_PropertyChanged ;
2021-11-29 16:20:33 +01:00
2022-03-29 16:46:31 +02:00
guestMetrics = newGuestMetrics ;
if ( guestMetrics ! = null )
2014-09-09 16:46:26 +02:00
guestMetrics . PropertyChanged + = guestMetrics_PropertyChanged ;
2014-08-22 18:36:41 +02:00
EnableRDPIfCapable ( ) ;
2015-04-29 12:32:14 +02:00
UpdateOpenSSHConsoleButtonState ( ) ; //guest_metrics change when there is an IP address change on a VIF
}
else if ( e . PropertyName = = "VIFs" | | e . PropertyName = = "PIFs" )
{
UpdateOpenSSHConsoleButtonState ( ) ;
2014-08-22 18:36:41 +02:00
}
2021-02-24 16:44:38 +01:00
if ( e . PropertyName = = "name_label" )
2013-06-24 13:41:48 +02:00
{
2021-02-24 16:44:38 +01:00
string text = null ;
if ( source . IsControlDomainZero ( out Host host ) )
text = string . Format ( Messages . CONSOLE_HOST , host . Name ( ) ) ;
else if ( source . IsSrDriverDomain ( out SR sr ) )
text = string . Format ( Messages . CONSOLE_SR_DRIVER_DOMAIN , sr . Name ( ) ) ;
2016-07-08 10:19:39 +02:00
2021-02-24 16:44:38 +01:00
if ( text ! = null )
{
HostLabel . Text = text ;
if ( parentVNCView ? . undockedForm ! = null )
parentVNCView . undockedForm . Text = text ;
}
2013-06-24 13:41:48 +02:00
}
}
2014-08-22 18:36:41 +02:00
private void guestMetrics_PropertyChanged ( object sender , PropertyChangedEventArgs e )
{
if ( e . PropertyName = = "other" )
{
2015-01-09 04:24:26 +01:00
if ( RDPEnabled )
2015-01-14 04:29:52 +01:00
{ // If current connection is VNC, will enable RDP connection as following conditions:
// 1. Click "Turn on RDP which cause tryToConnectRDP = true"
// 2. RDP status changed by turned on RDP in-guest and with "Automatically switch to the Remote
// Desktop console when it becomes available" is on. But if user already choose connection type by click "Turn on/Switch to Remote Desktop"
// or "Switch to Default desktop", we will take AutoSwitchToRDP as no effect
2015-01-09 04:24:26 +01:00
if ( vncScreen . UseVNC & & ( tryToConnectRDP | | ( ! vncScreen . UserWantsToSwitchProtocol & & Properties . Settings . Default . AutoSwitchToRDP ) ) )
{
tryToConnectRDP = false ;
2015-01-14 04:29:52 +01:00
2015-01-09 04:24:26 +01:00
ThreadPool . QueueUserWorkItem ( TryToConnectRDP ) ;
}
}
else
2015-02-13 07:16:17 +01:00
EnableRDPIfCapable ( ) ;
2014-12-04 09:35:36 +01:00
UpdateButtons ( ) ;
2014-08-22 18:36:41 +02:00
}
2020-06-23 13:44:43 +02:00
else if ( e . PropertyName = = "networks" )
{
UpdateOpenSSHConsoleButtonState ( ) ;
}
2014-08-22 18:36:41 +02:00
}
private void EnableRDPIfCapable ( )
{
2022-03-29 16:34:09 +02:00
var enable = source . CanUseRDP ( ) ;
if ( enable )
2021-11-29 16:20:33 +01:00
log . DebugFormat ( "'{0}' console: Enabling RDP button, because RDP capability has appeared." , source ) ;
2022-03-29 16:34:09 +02:00
toggleConsoleButton . Visible = toggleConsoleButton . Enabled = enable ;
2014-08-22 18:36:41 +02:00
}
2013-06-24 13:41:48 +02:00
private void Server_EnabledPropertyChanged ( object sender , PropertyChangedEventArgs e )
{
2021-02-24 16:44:38 +01:00
if ( e . PropertyName ! = "enabled" | | source . IsControlDomainZero ( out _ ) )
2013-06-24 13:41:48 +02:00
return ;
Host host = sender as Host ;
if ( host = = null )
return ;
if ( targetHost = = null | | targetHost . Equals ( host ) )
{
log . DebugFormat (
"'{0}' console: Update power state, after receiving property change notification, PropertyName='{1}'" ,
2019-02-01 13:19:42 +01:00
sender , e . PropertyName ) ;
2013-06-24 13:41:48 +02:00
updatePowerState ( ) ;
}
}
public void setupCD ( )
{
multipleDvdIsoList1 . VM = source ;
}
private void updatePowerState ( )
{
2021-02-24 16:44:38 +01:00
if ( source . IsControlDomainZero ( out Host host ) )
2013-06-24 13:41:48 +02:00
{
2021-02-24 16:44:38 +01:00
Host_metrics hostMetrics = source . Connection . Resolve ( host . metrics ) ;
2013-06-24 13:41:48 +02:00
if ( hostMetrics = = null )
return ;
if ( hostMetrics . live )
{
2015-04-16 15:17:02 +02:00
Program . Invoke ( this , showTopBarContents ) ;
2013-06-24 13:41:48 +02:00
}
else
{
2015-04-16 15:17:02 +02:00
Program . Invoke ( this , hideTopBarContents ) ;
2013-06-24 13:41:48 +02:00
}
}
else
{
switch ( source . power_state )
{
case vm_power_state . Halted :
case vm_power_state . Paused :
case vm_power_state . Suspended :
2015-04-16 15:17:02 +02:00
Program . Invoke ( this , hideTopBarContents ) ;
2013-06-24 13:41:48 +02:00
break ;
case vm_power_state . Running :
2015-04-16 15:17:02 +02:00
Program . Invoke ( this , showTopBarContents ) ;
Program . Invoke ( this , maybeEnableButton ) ;
2013-06-24 13:41:48 +02:00
break ;
}
}
2015-04-29 12:32:14 +02:00
UpdateOpenSSHConsoleButtonState ( ) ;
2013-06-24 13:41:48 +02:00
}
/// <summary>
/// CA-8966: No way to get from graphical to text console if vm's networking is broken on startup
/// </summary>
private void maybeEnableButton ( )
{
if ( vncScreen ! = null & & ( ! vncScreen . UseVNC | | ! vncScreen . UseSource ) )
{
toggleConsoleButton . Enabled = true ;
}
}
private void EnablePowerStateLabel ( String label )
{
powerStateLabel . Enabled = true ;
powerStateLabel . Text = label ;
powerStateLabel . Cursor = Cursors . Hand ;
}
private void DisablePowerStateLabel ( String label )
{
powerStateLabel . Enabled = false ;
powerStateLabel . Text = label ;
powerStateLabel . Cursor = Cursors . Default ;
}
private void hideTopBarContents ( )
{
VMPowerOff ( ) ;
2021-02-24 16:44:38 +01:00
if ( source . IsControlDomainZero ( out _ ) )
2013-06-24 13:41:48 +02:00
{
2017-09-03 04:33:29 +02:00
log . DebugFormat ( "'{0}' console: Hide top bar contents, server is unavailable" , source . Name ( ) ) ;
2013-06-24 13:41:48 +02:00
DisablePowerStateLabel ( Messages . CONSOLE_HOST_DEAD ) ;
}
else
{
2017-09-03 04:33:29 +02:00
log . DebugFormat ( "'{0}' console: Hide top bar contents, powerstate='{1}'" , source . Name ( ) , vm_power_state_helper . ToString ( source . power_state ) ) ;
2013-06-24 13:41:48 +02:00
if ( source . power_state = = vm_power_state . Halted )
{
if ( source . allowed_operations . Contains ( vm_operations . start ) & &
Helpers . EnabledTargetExists ( targetHost , source . Connection ) )
{
EnablePowerStateLabel ( Messages . CONSOLE_POWER_STATE_HALTED_START ) ;
}
else
{
DisablePowerStateLabel ( Messages . CONSOLE_POWER_STATE_HALTED ) ;
}
}
else if ( source . power_state = = vm_power_state . Paused )
{
2021-09-14 14:21:52 +02:00
// CA-12637: Pause/UnPause is not supported in the GUI. Comment out
2021-11-15 10:27:56 +01:00
// the EnablePowerStateLabel because it gives the impression that we
2021-09-14 14:21:52 +02:00
// support unpause via the GUI.
DisablePowerStateLabel ( Messages . CONSOLE_POWER_STATE_PAUSED ) ;
2013-06-24 13:41:48 +02:00
}
else if ( source . power_state = = vm_power_state . Suspended )
{
if ( source . allowed_operations . Contains ( vm_operations . resume ) & &
Helpers . EnabledTargetExists ( targetHost , source . Connection ) )
{
EnablePowerStateLabel ( Messages . CONSOLE_POWER_STATE_SUSPENDED_RESUME ) ;
}
else
{
DisablePowerStateLabel ( Messages . CONSOLE_POWER_STATE_SUSPENDED ) ;
}
}
}
powerStateLabel . Show ( ) ;
}
private void showTopBarContents ( )
{
2017-09-03 04:33:29 +02:00
log . DebugFormat ( "'{0}' console: Show top bar contents, source is running" , source . Name ( ) ) ;
2013-06-24 13:41:48 +02:00
Program . AssertOnEventThread ( ) ;
VMPowerOn ( ) ;
powerStateLabel . Hide ( ) ;
}
private void powerStateLabel_Click ( object sender , EventArgs e )
{
if ( ! powerStateLabel . Enabled )
{
return ;
}
switch ( source . power_state )
{
case vm_power_state . Halted :
DisablePowerStateLabel ( Messages . CONSOLE_POWER_STATE_HALTED ) ;
if ( source . allowed_operations . Contains ( vm_operations . start ) )
{
DisablePowerStateLabel ( powerStateLabel . Text ) ;
2021-08-31 12:31:16 +02:00
new StartVMCommand ( Program . MainWindow , source ) . Run ( ) ;
2013-06-24 13:41:48 +02:00
}
break ;
case vm_power_state . Paused :
DisablePowerStateLabel ( Messages . CONSOLE_POWER_STATE_PAUSED ) ;
//if(source.allowed_operations.Contains(vm_operations.unpause))
// new Actions.VmAction(source.Connection, VmActionKind.UnpauseActiveView, source, null).RunAsync();
break ;
case vm_power_state . Suspended :
DisablePowerStateLabel ( Messages . CONSOLE_POWER_STATE_SUSPENDED ) ;
if ( source . allowed_operations . Contains ( vm_operations . resume ) )
{
DisablePowerStateLabel ( powerStateLabel . Text ) ;
2021-08-31 12:31:16 +02:00
new ResumeVMCommand ( Program . MainWindow , source ) . Run ( ) ;
2013-06-24 13:41:48 +02:00
}
break ;
}
}
public void Pause ( )
{
if ( vncScreen ! = null & & ! isFullscreen )
vncScreen . Pause ( ) ;
}
public void Unpause ( )
{
if ( vncScreen ! = null )
vncScreen . Unpause ( ) ;
}
2019-01-31 00:00:56 +01:00
private bool CanEnableRDP ( )
2015-01-09 04:24:26 +01:00
{
2019-01-31 00:00:56 +01:00
return RDPControlEnabled & & ! RDPEnabled ;
2015-01-09 04:24:26 +01:00
}
2013-06-24 13:41:48 +02:00
// Make the 'enable RDP' button show something sensible if we can...
private string GuessNativeConsoleLabel ( VM source )
{
string label = Messages . VNC_LOOKING ;
if ( source = = null )
return label ;
XenRef < VM_guest_metrics > gm = source . guest_metrics ;
if ( gm = = null )
return label ;
if ( Program . MainWindow . SelectionManager . Selection . GetConnectionOfFirstItem ( ) = = null )
return label ;
VM_guest_metrics gmo = Program . MainWindow . SelectionManager . Selection . GetConnectionOfFirstItem ( ) . Resolve < VM_guest_metrics > ( gm ) ;
if ( gmo = = null )
return label ;
if ( gmo ! = null & & gmo . os_version ! = null )
{
if ( gmo . os_version . ContainsKey ( "name" ) )
{
string osString = gmo . os_version [ "name" ] ;
if ( osString ! = null )
{
if ( osString . Contains ( "Microsoft" ) )
2019-01-31 00:00:56 +01:00
label = CanEnableRDP ( ) ? enableRDP : UseRDP ;
2013-06-24 13:41:48 +02:00
else
label = UseXVNC ;
}
}
}
return label ;
}
private void RDPorVNCResizeHandler ( object sender , EventArgs e )
{
Program . AssertOnEventThread ( ) ;
if ( vncScreen = = null )
return ; // This is the first event when vncScreen is created.
VNCResizeHandler_ ( ) ;
}
private void VNCResizeHandler_ ( )
{
if ( ! ignoringResizes & &
DesktopSizeHasChanged ( ) & &
! scaleCheckBox . Checked )
{
MaybeScale ( ) ;
}
}
public void MaybeScale ( )
{
Program . AssertOnEventThread ( ) ;
if ( vncScreen . DesktopSize . Width > 10 & &
contentPanel . Width < vncScreen . DesktopSize . Width )
{
if ( ! Properties . Settings . Default . PreserveScaleWhenSwitchBackToVNC )
{
scaleCheckBox . Checked = true ;
}
else
{
scaleCheckBox . Checked = oldScaleValue | | firstTime ;
firstTime = false ;
}
}
else if ( Properties . Settings . Default . PreserveScaleWhenSwitchBackToVNC )
{
scaleCheckBox . Checked = oldScaleValue ;
}
scaleCheckBox_CheckedChanged ( null , null ) ;
}
private bool DesktopSizeHasChanged ( )
{
if ( ! LastDesktopSize . Equals ( vncScreen . DesktopSize ) )
{
LastDesktopSize = vncScreen . DesktopSize ;
return true ;
}
else
{
return false ;
}
}
public Size GrowToFit ( )
{
Program . AssertOnEventThread ( ) ;
ignoringResizes = true ;
try
{
scaleCheckBox . Checked = false ;
Size working_area = Screen . FromControl ( this ) . WorkingArea . Size ;
if ( vncScreen . DesktopSize . Width > 10 & &
vncScreen . DesktopSize . Width < working_area . Width & &
vncScreen . DesktopSize . Height < working_area . Height )
{
int twoTimeBorderPadding = VNCGraphicsClient . BORDER_PADDING * 2 ;
return new Size ( vncScreen . DesktopSize . Width + twoTimeBorderPadding ,
2016-09-17 02:23:55 +02:00
vncScreen . DesktopSize . Height + gradientPanel1 . Height + tableLayoutPanel1 . Height + twoTimeBorderPadding ) ;
2013-06-24 13:41:48 +02:00
}
else
{
scaleCheckBox . Checked = true ;
return new Size ( ( working_area . Width * 2 ) / 3 , ( working_area . Height * 2 ) / 3 ) ;
}
}
finally
{
ignoringResizes = false ;
}
}
private void scaleCheckBox_CheckedChanged ( object sender , EventArgs e )
{
if ( ignoreScaleChange )
return ;
try
{
ignoringResizes = true ;
2022-03-29 16:46:31 +02:00
vncScreen . Scaling = scaleCheckBox . Checked ;
2013-06-24 13:41:48 +02:00
}
finally
{
ignoringResizes = false ;
}
FocusVNC ( ) ;
}
private void sendCAD_Click ( object sender , EventArgs e )
{
2022-03-29 16:46:31 +02:00
vncScreen . SendCAD ( ) ;
2013-06-24 13:41:48 +02:00
FocusVNC ( ) ;
}
private void dockButton_Click ( object sender , EventArgs e )
{
if ( isFullscreen )
return ;
2022-03-29 16:46:31 +02:00
parentVNCView . DockUnDock ( ) ;
2013-06-24 13:41:48 +02:00
}
private void fullscreenButton_Click ( object sender , EventArgs e )
{
toggleFullscreen ( ) ;
}
private System . Threading . Timer insKeyTimer ;
private void waitForInsKey ( )
{
2022-03-29 16:46:31 +02:00
lock ( insKeyTimer )
2013-06-24 13:41:48 +02:00
{
2022-03-29 16:46:31 +02:00
insKeyTimer . Change ( INS_KEY_TIMEOUT , Timeout . Infinite ) ;
2013-06-24 13:41:48 +02:00
}
}
private void cancelWaitForInsKeyAndSendCAD ( )
{
2022-03-29 16:46:31 +02:00
lock ( insKeyTimer )
2013-06-24 13:41:48 +02:00
{
// We have seen the INS key, so lets cancel the timer and send CAD
2022-03-29 16:46:31 +02:00
insKeyTimer . Change ( Timeout . Infinite , Timeout . Infinite ) ;
vncScreen . SendCAD ( ) ;
2013-06-24 13:41:48 +02:00
}
}
private void notInsKeyPressed ( Object o )
{
Program . AssertOffEventThread ( ) ;
2021-11-29 16:20:33 +01:00
Program . Invoke ( this , delegate ( )
2013-06-24 13:41:48 +02:00
{
2022-03-29 16:46:31 +02:00
lock ( insKeyTimer )
2013-06-24 13:41:48 +02:00
{
// We have not seen the INS key, so lets toggleFullscreen and cancel the timer
2022-03-29 16:46:31 +02:00
toggleFullscreen ( ) ;
insKeyTimer . Change ( Timeout . Infinite , Timeout . Infinite ) ;
2013-06-24 13:41:48 +02:00
}
} ) ;
}
private bool isFullscreen
{
get
{
return fullscreenForm ! = null ;
}
}
// CA-10943
private bool inToggleDockUnDock = false ;
private void toggleDockUnDock ( )
{
Program . AssertOnEventThread ( ) ;
if ( inToggleDockUnDock )
return ;
inToggleDockUnDock = true ;
dockButton_Click ( null , null ) ;
inToggleDockUnDock = false ;
}
private bool inToggleFullscreen = false ;
private void toggleFullscreen ( )
{
Program . AssertOnEventThread ( ) ;
if ( inToggleFullscreen )
return ;
inToggleFullscreen = true ;
if ( ! isFullscreen )
{
2016-08-24 14:51:50 +02:00
fullscreenForm = new FullScreenForm ( this ) ;
fullscreenForm . FormClosing + = delegate { toggleFullscreen ( ) ; } ;
2013-06-24 13:41:48 +02:00
if ( source ! = null & & source . Connection ! = null )
source . Connection . BeforeConnectionEnd + = Connection_BeforeConnectionEnd ;
2016-08-24 14:51:50 +02:00
fullscreenForm . AttachVncScreen ( vncScreen ) ;
2013-06-24 13:41:48 +02:00
vncScreen . DisplayFocusRectangle = false ;
2016-08-24 02:38:51 +02:00
fullscreenHint = new FullScreenHint ( ) ;
2013-06-24 13:41:48 +02:00
fullscreenForm . Show ( ) ;
2016-09-06 11:32:49 +02:00
fullscreenHint . Show ( fullscreenForm ) ;
2013-06-24 13:41:48 +02:00
FocusVNC ( ) ;
vncScreen . CaptureKeyboardAndMouse ( ) ;
2017-02-27 19:00:46 +01:00
UpdateRDPResolution ( true ) ;
2013-06-24 13:41:48 +02:00
}
else
{
if ( source ! = null & & source . Connection ! = null )
source . Connection . BeforeConnectionEnd - = Connection_BeforeConnectionEnd ;
2016-08-24 14:51:50 +02:00
fullscreenForm . DetachVncScreen ( vncScreen ) ;
2022-03-29 16:46:31 +02:00
vncScreen . Parent = contentPanel ;
2013-06-24 13:41:48 +02:00
vncScreen . DisplayFocusRectangle = true ;
FocusVNC ( ) ;
vncScreen . CaptureKeyboardAndMouse ( ) ;
fullscreenForm . Hide ( ) ;
fullscreenForm . Dispose ( ) ;
fullscreenForm = null ;
2017-04-25 15:07:29 +02:00
UpdateRDPResolution ( false ) ;
2013-06-24 13:41:48 +02:00
}
//Everytime we toggle full screen I'm going to force an unpause to make sure we don't acidentally undock / dock a pause VNC
vncScreen . Unpause ( ) ;
inToggleFullscreen = false ;
// CA-30477: This refresh stops a scroll bar being painted on the fullscreen form under vista
if ( fullscreenForm ! = null )
fullscreenForm . Refresh ( ) ;
}
2019-10-27 16:52:15 +01:00
void Connection_BeforeConnectionEnd ( IXenConnection conn )
2013-06-24 13:41:48 +02:00
{
2015-04-16 15:17:02 +02:00
Program . Invoke ( this , toggleFullscreen ) ;
2013-06-24 13:41:48 +02:00
}
private const bool RDP = true ;
private const bool XVNC = false ;
private bool toggleToXVNCorRDP = RDP ;
public void DisableToggleVNCButton ( )
{
Program . AssertOnEventThread ( ) ;
toggleConsoleButton . Enabled = false ;
}
2014-12-04 09:35:36 +01:00
public void EnableToggleVNCButton ( )
{
Program . AssertOnEventThread ( ) ;
toggleConsoleButton . Enabled = true ;
}
2013-06-24 13:41:48 +02:00
private void OnDetectRDP ( )
{
2015-04-16 15:17:02 +02:00
Program . Invoke ( this , OnDetectRDP_ ) ;
2013-06-24 13:41:48 +02:00
}
private void OnDetectRDP_ ( )
{
try
{
log . DebugFormat ( "RDP detected for VM '{0}'" , source = = null ? "unknown/null" : source . name_label ) ;
2019-01-31 00:00:56 +01:00
toggleToXVNCorRDP = RDP ;
2014-12-04 09:35:36 +01:00
if ( vncScreen . UseVNC )
2019-01-31 00:00:56 +01:00
toggleConsoleButton . Text = CanEnableRDP ( ) ? enableRDP : UseRDP ;
2022-03-29 16:34:09 +02:00
EnableRDPIfCapable ( ) ;
2019-01-31 00:00:56 +01:00
tip . SetToolTip ( toggleConsoleButton , null ) ;
2021-11-29 16:20:33 +01:00
if ( ! vncScreen . UserWantsToSwitchProtocol & & Properties . Settings . Default . AutoSwitchToRDP )
2013-06-24 13:41:48 +02:00
{
if ( Program . MainWindow . TheTabControl . SelectedTab = = Program . MainWindow . TabPageConsole )
toggleConsoleButton_Click ( null , null ) ;
else
switchOnTabOpened = true ;
}
}
catch ( InvalidOperationException exn )
{
log . Warn ( exn , exn ) ;
}
}
private void OnDetectVNC ( )
{
2015-04-16 15:17:02 +02:00
Program . Invoke ( this , OnDetectVNC_ ) ;
2013-06-24 13:41:48 +02:00
}
private void OnDetectVNC_ ( )
{
try
{
log . DebugFormat ( "VNC detected for VM '{0}'" , source = = null ? "unknown/null" : source . name_label ) ;
2022-03-29 16:46:31 +02:00
toggleToXVNCorRDP = XVNC ;
toggleConsoleButton . Text = vncScreen . UseSource ? UseXVNC : UseVNC ;
toggleConsoleButton . Enabled = true ;
tip . SetToolTip ( toggleConsoleButton , null ) ;
2013-06-24 13:41:48 +02:00
}
catch ( InvalidOperationException exn )
{
log . Warn ( exn , exn ) ;
}
}
private bool firstTime = true ;
private bool oldScaleValue = false ;
2015-01-09 04:24:26 +01:00
private bool tryToConnectRDP = false ; // This parameter will be set after click "TURN ON Rremote Desktop" and will connect RDP when RDP status changed.
2013-06-24 13:41:48 +02:00
/// <summary>
/// Switch between graphical and text consoles.
/// </summary>
private void toggleConsoleButton_Click ( object sender , EventArgs e )
{
bool rdp = ( toggleToXVNCorRDP = = RDP ) ;
try
{
if ( rdp )
{
if ( vncScreen . UseVNC )
oldScaleValue = scaleCheckBox . Checked ;
vncScreen . UseVNC = ! vncScreen . UseVNC ;
vncScreen . UserWantsToSwitchProtocol = true ;
2014-12-04 09:35:36 +01:00
2019-01-31 00:00:56 +01:00
if ( CanEnableRDP ( ) )
2014-12-04 09:35:36 +01:00
{
2016-06-20 11:49:12 +02:00
DialogResult dialogResult ;
2020-04-22 15:47:03 +02:00
using ( ThreeButtonDialog dlg = new NoIconDialog ( Messages . FORCE_ENABLE_RDP ,
ThreeButtonDialog . ButtonYes , ThreeButtonDialog . ButtonNo )
2021-11-29 16:20:33 +01:00
{ HelpNameSetter = "EnableRDPonVM" } )
2016-06-20 11:49:12 +02:00
{
dialogResult = dlg . ShowDialog ( Program . MainWindow ) ;
}
if ( dialogResult = = DialogResult . Yes )
2014-12-04 09:35:36 +01:00
{
2015-01-09 04:24:26 +01:00
Session session = source . Connection . DuplicateSession ( ) ;
Dictionary < string , string > _arguments = new Dictionary < string , string > ( ) ;
2022-03-29 16:46:31 +02:00
VM . call_plugin ( session , source . opaque_ref , "guest-agent-operation" , "request-rdp-on" , _arguments ) ;
2015-01-09 04:24:26 +01:00
tryToConnectRDP = true ;
2014-12-04 09:35:36 +01:00
}
}
2019-02-01 13:19:42 +01:00
// disable toggleConsoleButton; it will be re-enabled in TryToConnectRDP() when rdp port polling is complete (CA-102755)
2022-03-29 16:40:34 +02:00
if ( vncScreen . RdpIp = = null )
2013-06-24 13:41:48 +02:00
toggleConsoleButton . Enabled = false ;
2019-02-01 13:19:42 +01:00
ThreadPool . QueueUserWorkItem ( TryToConnectRDP ) ;
2013-06-24 13:41:48 +02:00
}
else
{
oldScaleValue = scaleCheckBox . Checked ;
vncScreen . UseSource = ! vncScreen . UseSource ;
}
Unpause ( ) ;
UpdateButtons ( ) ;
}
2021-11-29 16:20:33 +01:00
catch ( COMException ex )
2013-06-24 13:41:48 +02:00
{
log . DebugFormat ( "Disabling toggle-console button as COM related exception thrown: {0}" , ex . Message ) ;
toggleConsoleButton . Enabled = false ;
}
}
private void UpdateButtons ( )
{
bool rdp = ( toggleToXVNCorRDP = = RDP ) ;
if ( rdp )
2019-01-31 00:00:56 +01:00
toggleConsoleButton . Text = vncScreen . UseVNC
? CanEnableRDP ( ) ? enableRDP : UseRDP
: UseStandardDesktop ;
2013-06-24 13:41:48 +02:00
else
toggleConsoleButton . Text = vncScreen . UseSource ? UseXVNC : UseVNC ;
2015-05-26 12:45:55 +02:00
2019-01-31 00:00:56 +01:00
UpdateTooltipOfToggleButton ( ) ;
2015-02-13 07:16:17 +01:00
2013-06-24 13:41:48 +02:00
scaleCheckBox . Visible = ! rdp | | vncScreen . UseVNC ;
2015-06-01 16:33:17 +02:00
sendCAD . Enabled = ! rdp | | vncScreen . UseVNC ;
2013-06-24 13:41:48 +02:00
FocusVNC ( ) ;
ignoreScaleChange = true ;
if ( ! Properties . Settings . Default . PreserveScaleWhenSwitchBackToVNC )
{
scaleCheckBox . Checked = false ;
}
ignoreScaleChange = false ;
scaleCheckBox_CheckedChanged ( null , null ) ; // make sure scale setting is now correct: CA-84324
2017-11-13 17:51:56 +01:00
ShowOrHideRdpVersionWarning ( ) ;
2013-06-24 13:41:48 +02:00
}
2019-01-31 00:00:56 +01:00
private void UpdateTooltipOfToggleButton ( )
2015-05-26 12:45:55 +02:00
{
2019-01-31 00:00:56 +01:00
if ( RDPEnabled | | RDPControlEnabled )
2022-03-29 16:46:31 +02:00
tip . SetToolTip ( toggleConsoleButton , null ) ;
2015-05-26 12:45:55 +02:00
}
2013-06-24 13:41:48 +02:00
private void TryToConnectRDP ( object x )
{
2022-03-29 16:40:34 +02:00
bool hasToReconnect = vncScreen . RdpIp = = null ;
vncScreen . RdpIp = vncScreen . PollPort ( XSVNCScreen . RDP_PORT , true ) ;
2015-04-16 15:17:02 +02:00
Program . Invoke ( this , ( MethodInvoker ) ( ( ) = >
2013-06-24 13:41:48 +02:00
{
if ( hasToReconnect )
{
vncScreen . UseVNC = true ;
vncScreen . UseVNC = false ;
}
Unpause ( ) ;
UpdateButtons ( ) ;
toggleConsoleButton . Enabled = true ; // make sure the toggleConsoleButton is enabled after rdp port polling (CA-102755)
} ) ) ;
}
/// <summary>
/// Called when the user cancelled VNC authentication.
/// </summary>
private void OnUserCancelledAuth ( object sender , EventArgs e )
{
// Switch back to the text console
toggleConsoleButton_Click ( null , null ) ;
}
2015-04-30 12:37:39 +02:00
/// <summary>
/// Called when the a connection attempt to VNC is cancelled.
/// </summary>
private void OnVncConnectionAttemptCancelled ( object sender , EventArgs e )
{
// Switch back to the text console
toggleConsoleButton_Click ( null , null ) ;
}
2013-06-24 13:41:48 +02:00
protected override void OnGotFocus ( EventArgs e )
{
base . OnGotFocus ( e ) ;
FocusVNC ( ) ;
}
internal void SendCAD ( )
{
2022-03-29 16:46:31 +02:00
if ( vncScreen ! = null )
vncScreen . SendCAD ( ) ;
2013-06-24 13:41:48 +02:00
}
2015-11-10 14:12:13 +01:00
internal void focus_vnc ( )
2013-06-24 13:41:48 +02:00
{
2022-03-29 16:46:31 +02:00
if ( vncScreen ! = null )
2015-11-10 14:12:13 +01:00
FocusVNC ( ) ;
2013-06-24 13:41:48 +02:00
}
// Focus the VNC screen, as long as we're in the right place at the moment.
// Otherwise ignore the request to focus or it will steal the keyboard from
// the rightful owner: see CA-41120.
2015-11-10 14:12:13 +01:00
private void FocusVNC ( )
2013-06-24 13:41:48 +02:00
{
2015-11-10 14:12:13 +01:00
if ( Program . MainWindow . ContainsFocus & & Program . MainWindow . TheTabControl . SelectedTab = = Program . MainWindow . TabPageConsole )
2013-06-24 13:41:48 +02:00
vncScreen . Focus ( ) ;
}
internal void VMPowerOff ( )
{
toggleConsoleButton . Enabled = false ;
VBD cddrive = source . FindVMCDROM ( ) ;
bool allowEject = cddrive ! = null ? cddrive . allowed_operations . Contains ( vbd_operations . eject ) : false ;
bool allowInsert = cddrive ! = null ? cddrive . allowed_operations . Contains ( vbd_operations . insert ) : false ;
multipleDvdIsoList1 . Enabled = ( source . power_state = = vm_power_state . Halted ) & & ( allowEject | | allowInsert ) ;
sendCAD . Enabled = false ;
}
internal void VMPowerOn ( )
{
//No need to reenable toggleConsoleButton, polling will do it.
multipleDvdIsoList1 . Enabled = true ;
2015-06-01 16:33:17 +02:00
sendCAD . Enabled = true ;
2013-06-24 13:41:48 +02:00
}
internal void RdpDisconnectedHandler ( object sender , EventArgs e )
{
Program . AssertOnEventThread ( ) ;
if ( vncScreen = = null )
return ; // This is the first event when vncScreen is created.
rdpDisconnected ( ) ;
}
private void rdpDisconnected ( )
{
if ( ! vncScreen . UseVNC )
toggleConsoleButton_Click ( null , null ) ;
2019-01-31 00:00:56 +01:00
if ( ! RDPControlEnabled )
2015-05-06 04:53:20 +02:00
toggleConsoleButton . Enabled = false ;
2021-11-29 16:20:33 +01:00
2013-06-24 13:41:48 +02:00
vncScreen . imediatelyPollForConsole ( ) ;
}
internal void SwitchIfRequired ( )
{
if ( switchOnTabOpened )
{
toggleConsoleButton_Click ( null , null ) ;
switchOnTabOpened = false ;
}
}
bool droppedDown = false ;
private readonly CollectionChangeEventHandler Host_CollectionChangedWithInvoke ;
private readonly CollectionChangeEventHandler VM_CollectionChangedWithInvoke ;
private void LifeCycleButton_MouseClick ( object sender , EventArgs e )
{
if ( droppedDown )
{
LifeCycleMenuStrip . Hide ( ) ;
return ;
}
2021-02-24 16:44:38 +01:00
2013-06-24 13:41:48 +02:00
if ( source = = null )
return ;
ContextMenuItemCollection contextMenuItems = new ContextMenuItemCollection ( ) ;
2021-02-24 16:44:38 +01:00
if ( source . IsControlDomainZero ( out Host host ) )
2013-06-24 13:41:48 +02:00
{
// We're looking at the host console
if ( host . Connection . IsConnected )
{
2014-01-22 13:39:32 +01:00
contextMenuItems . Add ( new ShutDownHostCommand ( Program . MainWindow , host , this ) ) ;
contextMenuItems . Add ( new RebootHostCommand ( Program . MainWindow , host , this ) ) ;
2013-06-24 13:41:48 +02:00
}
}
else
{
2014-01-22 13:39:32 +01:00
contextMenuItems . AddIfEnabled ( new ShutDownVMCommand ( Program . MainWindow , source , this ) ) ;
contextMenuItems . AddIfEnabled ( new RebootVMCommand ( Program . MainWindow , source , this ) ) ;
contextMenuItems . AddIfEnabled ( new SuspendVMCommand ( Program . MainWindow , source , this ) ) ;
contextMenuItems . AddIfEnabled ( new InstallToolsCommand ( Program . MainWindow , source , this ) ) ;
contextMenuItems . AddIfEnabled ( new ForceVMShutDownCommand ( Program . MainWindow , source , this ) ) ;
contextMenuItems . AddIfEnabled ( new ForceVMRebootCommand ( Program . MainWindow , source , this ) ) ;
2013-06-24 13:41:48 +02:00
}
LifeCycleMenuStrip . Items . Clear ( ) ;
LifeCycleMenuStrip . Items . AddRange ( contextMenuItems . ToArray ( ) ) ;
LifeCycleMenuStrip . Show ( pictureBox1 , pictureBox1 . Left , pictureBox1 . Bottom ) ;
}
public void showHeaderBar ( bool showHeaderBar , bool showLifecycleIcon )
{
2016-09-17 02:23:55 +02:00
gradientPanel1 . Visible = showHeaderBar ;
2013-06-24 13:41:48 +02:00
pictureBox1 . Visible = showLifecycleIcon ;
}
private void pictureBox1_MouseEnter ( object sender , EventArgs e )
{
2020-06-18 02:20:29 +02:00
pictureBox1 . Image = Images . StaticImages . lifecycle_hot ;
2013-06-24 13:41:48 +02:00
}
private void pictureBox1_MouseLeave ( object sender , EventArgs e )
{
2020-06-18 02:20:29 +02:00
pictureBox1 . Image = droppedDown ? Images . StaticImages . lifecycle_pressed : Images . StaticImages . _001_LifeCycle_h32bit_24 ;
2013-06-24 13:41:48 +02:00
}
private void pictureBox1_MouseDown ( object sender , MouseEventArgs e )
{
2020-06-18 02:20:29 +02:00
pictureBox1 . Image = Images . StaticImages . lifecycle_pressed ;
2013-06-24 13:41:48 +02:00
}
private void pictureBox1_MouseUp ( object sender , MouseEventArgs e )
{
2020-06-18 02:20:29 +02:00
pictureBox1 . Image = droppedDown ? Images . StaticImages . lifecycle_pressed : Images . StaticImages . lifecycle_hot ;
2013-06-24 13:41:48 +02:00
}
private void LifeCycleMenuStrip_Opened ( object sender , EventArgs e )
{
droppedDown = true ;
}
private void LifeCycleMenuStrip_Closing ( object sender , ToolStripDropDownClosingEventArgs e )
{
2022-03-29 16:46:31 +02:00
if ( e . CloseReason ! = ToolStripDropDownCloseReason . AppClicked | | ! pictureBox1 . ClientRectangle . Contains ( PointToClient ( MousePosition ) ) )
2013-06-24 13:41:48 +02:00
{
droppedDown = false ;
2020-06-18 02:20:29 +02:00
pictureBox1 . Image = Images . StaticImages . _001_LifeCycle_h32bit_24 ;
2013-06-24 13:41:48 +02:00
}
else
{
e . Cancel = true ;
}
}
internal Image Snapshot ( )
{
if ( vncScreen ! = null )
{
return vncScreen . Snapshot ( ) ;
}
return null ;
}
2017-02-27 19:00:46 +01:00
private bool inToggleConsoleFocus = false ;
private void ToggleConsoleFocus ( )
2013-06-24 13:41:48 +02:00
{
Program . AssertOnEventThread ( ) ;
2017-02-27 19:00:46 +01:00
if ( inToggleConsoleFocus )
2013-06-24 13:41:48 +02:00
return ;
2017-02-27 19:00:46 +01:00
inToggleConsoleFocus = true ;
2013-06-24 13:41:48 +02:00
if ( vncScreen . Focused & & vncScreen . ActiveControl = = null )
vncScreen . CaptureKeyboardAndMouse ( ) ; // focus console
else
{
vncScreen . UncaptureKeyboardAndMouse ( ) ; // defocus console
vncScreen . Refresh ( ) ;
}
2017-02-27 19:00:46 +01:00
inToggleConsoleFocus = false ;
2013-06-24 13:41:48 +02:00
}
2016-08-24 12:48:42 +02:00
private void ShowGpuWarningIfRequired ( bool mustConnectRemoteDesktop )
2013-06-24 13:41:48 +02:00
{
2016-08-24 12:48:42 +02:00
dedicatedGpuWarning . Visible = mustConnectRemoteDesktop ;
2013-06-24 13:41:48 +02:00
}
2017-02-27 19:00:46 +01:00
public void UpdateRDPResolution ( bool fullscreen = false )
{
if ( vncScreen ! = null )
vncScreen . UpdateRDPResolution ( fullscreen ) ;
}
2021-11-29 16:20:33 +01:00
2015-04-29 12:32:14 +02:00
#region SSH Console methods
private void buttonSSH_Click ( object sender , EventArgs e )
{
2021-11-29 16:20:33 +01:00
if ( ! IsSSHConsoleSupported | | ! CanStartSSHConsole )
return ;
var customSshConsole = Properties . Settings . Default . CustomSshConsole ;
var sshConsolePath = GetConsolePath ( customSshConsole ) ;
2022-01-04 16:27:34 +01:00
if ( string . IsNullOrEmpty ( sshConsolePath ) )
2015-04-29 12:32:14 +02:00
{
2022-01-12 14:29:00 +01:00
OpenSshConsoleWarningDialog ( Messages . CONFIGURE_SSH_CONSOLE_FILE_NOT_CONFIGURED ) ;
2021-11-29 16:20:33 +01:00
return ;
}
2015-05-07 18:54:42 +02:00
2022-01-10 14:38:34 +01:00
if ( ! File . Exists ( sshConsolePath ) )
{
2022-01-12 14:29:00 +01:00
OpenSshConsoleWarningDialog ( Messages . CONFIGURE_SSH_CONSOLE_FILE_NOT_FOUND ) ;
2022-01-10 14:38:34 +01:00
return ;
}
2021-11-29 16:20:33 +01:00
try
{
var command = $"{source.IPAddressForSSH()}" ;
if ( customSshConsole = = SshConsole . OpenSSH )
2015-05-07 18:54:42 +02:00
{
2021-11-29 16:20:33 +01:00
// OpenSSH doesn't have an option to prompt the user for the username.
// We use the session's subject.
var currentSubject = source . Connection . Resolve ( source . Connection . Session . SessionSubject ) ;
command = $"{currentSubject?.SubjectName ?? " root "}@{source.IPAddressForSSH()}" ;
2015-05-07 18:54:42 +02:00
}
2021-11-29 16:20:33 +01:00
Process . Start ( new ProcessStartInfo ( sshConsolePath , command ) ) ;
}
catch ( Exception ex )
{
log . Error ( "Could not start the selected SSH console." , ex ) ;
OpenSshConsoleErrorDialog ( ) ;
}
}
private static string GetConsolePath ( SshConsole customSshConsole )
{
switch ( customSshConsole )
{
case SshConsole . Putty :
return Properties . Settings . Default . PuttyLocation ;
case SshConsole . OpenSSH :
return Properties . Settings . Default . OpenSSHLocation ;
default :
return null ;
}
}
2022-01-12 14:29:00 +01:00
private void OpenSshConsoleWarningDialog ( string message )
2021-11-29 16:20:33 +01:00
{
2022-01-04 16:27:34 +01:00
var configureSshClientButton = new ThreeButtonDialog . TBDButton ( Messages . CONFIGURE_SSH_CONSOLE_TITLE ,
DialogResult . OK , ThreeButtonDialog . ButtonType . ACCEPT , true ) ;
2021-11-29 16:20:33 +01:00
2022-01-12 14:29:00 +01:00
using ( var dlg = new WarningDialog ( message , configureSshClientButton , ThreeButtonDialog . ButtonCancel ) )
2022-01-10 14:38:34 +01:00
{
if ( dlg . ShowDialog ( Parent ) = = DialogResult . OK )
{
OpenExternalToolsPage ( ) ;
}
}
2021-11-29 16:20:33 +01:00
}
2015-05-08 14:56:22 +02:00
2021-11-29 16:20:33 +01:00
private void OpenSshConsoleErrorDialog ( )
{
2022-01-04 16:27:34 +01:00
var configureSshClientButton = new ThreeButtonDialog . TBDButton ( Messages . CONFIGURE_SSH_CONSOLE_TITLE ,
DialogResult . OK , ThreeButtonDialog . ButtonType . ACCEPT , true ) ;
2021-11-29 16:20:33 +01:00
2022-01-04 16:27:34 +01:00
using ( var dlg = new ErrorDialog ( Messages . CONFIGURE_SSH_CONSOLE_ERROR ,
configureSshClientButton , ThreeButtonDialog . ButtonCancel ) )
2021-11-29 16:20:33 +01:00
{
2022-01-04 16:27:34 +01:00
if ( dlg . ShowDialog ( Parent ) = = DialogResult . OK )
2021-11-29 16:20:33 +01:00
{
OpenExternalToolsPage ( ) ;
2015-05-07 18:54:42 +02:00
}
2015-04-29 12:32:14 +02:00
}
}
2021-11-29 16:20:33 +01:00
private void OpenExternalToolsPage ( )
{
using ( var optionsDialog = new OptionsDialog ( Program . MainWindow . PluginManager ) )
{
optionsDialog . SelectExternalToolsPage ( ) ;
optionsDialog . ShowDialog ( ) ;
}
}
2015-04-29 12:32:14 +02:00
private void UpdateOpenSSHConsoleButtonState ( )
{
2020-06-23 13:44:43 +02:00
var isSshConsoleSupported = IsSSHConsoleSupported ;
buttonSSH . Visible = isSshConsoleSupported & & source . power_state ! = vm_power_state . Halted ;
buttonSSH . Enabled = isSshConsoleSupported & & CanStartSSHConsole ;
2015-06-10 16:12:37 +02:00
}
private bool IsSSHConsoleSupported
2021-11-29 16:20:33 +01:00
{
2015-04-29 12:32:14 +02:00
get
{
2017-09-03 04:33:29 +02:00
if ( source . IsWindows ( ) )
2015-06-10 16:12:37 +02:00
return false ;
2015-04-29 12:32:14 +02:00
2021-02-24 16:44:38 +01:00
if ( source . IsControlDomainZero ( out Host host ) )
2015-04-29 12:32:14 +02:00
{
2021-02-24 16:44:38 +01:00
Host_metrics hostMetrics = source . Connection . Resolve ( host . metrics ) ;
2015-04-29 12:32:14 +02:00
if ( hostMetrics = = null )
return false ;
if ( ! hostMetrics . live )
return false ;
}
2015-06-10 16:12:37 +02:00
return true ;
}
}
2020-06-23 13:44:43 +02:00
private bool CanStartSSHConsole = >
source . power_state = = vm_power_state . Running & & ! string . IsNullOrEmpty ( source . IPAddressForSSH ( ) ) ;
2015-04-29 12:32:14 +02:00
#endregion SSH Console methods
2013-06-24 13:41:48 +02:00
}
}