# KX SDK for Java - Getting Started

## Introduction

This document gives a short overview of the most important APIs of the KX SDK for Java. The guide does not cover the complete API!
The best way to understand all the APIs and methods of the SDK is to read and step through the example program which is part of this SDK.

There is also the full API documentation in HTML available.

The SDK consists of several parts:

- Commonly shared code (nn.pp.core)
- KVM Client code (nn.pp.rccore)
- Virtual Media code (nn.pp.vmcore)
- Audio code (nn.pp.audiocore)

## Requirements

The KX SDK for Java requires a Java 8 JRE. It is written and tested against the latest Oracle JRE version 8 and OpenJDK version 8. Newer Java runtime environments may also work, but that is not guaranteed.

## The KVM Client libraries (nn.pp.rccore)

The functionality of the KVM Client library is in the nn.pp.rccore.RCCore interface. To use it, one needs to load an object instance implementing this interface using the factory methods of nn.pp.rccore.RCCoreFactory, register event listeners and call the required methods.

### Loading an instance of RCCore

To load an instance of the RCCore interface, the factory provides 3 different methods:

- RCCoreFactory.loadGraphicalRCCore(Logger logger)
  Create a new instance of RCCore which has a graphical back-end. This instance can be used to put it into any window or dialog GUI. It has full keyboard and mouse functionality.
- RCCoreFactory.loadGraphicalReadOnlyRCCore(Logger logger)
  Create a new instance of RCCore which has a graphical back-end. This instance can be used to put it into any window or dialog GUI. It has no keyboard and mouse functionality (i.e. is read-only).
- RCCoreFactory.loadMemoryBufferRCCore(Logger logger)
  Create a new instance of RCCore which has a memory back-end. This instance does not have graphical capabilities. It works in-memory only. This may be used for testing or scripting.

#### Retrieving the KVM Canvas Component

If a graphical variant of RCCore is used, then users need to retrieve the KVM canvas to put it into their GUI. The canvas is derived from the Java Swing javax.swing.JComponent class. To retrieve it, use the following method:

- JComponent getRCJComponent()

The KVM Component can be used like any other Component. It reports its current size via the "preferred size" methods. On resolution changed events, users just have to re-layout the GUI again to update the GUI and have it adopt to the new size.

#### Example

    JFrame wnd = [ ... ];
    RCCore rcCore = RCCoreFactory.loadGraphicalRCCore(null);
    JComponent kvmControl = rcCore.getRCJComponent();
    [ ... put kvmControl into the GUI ]

### Connecting to the target

To connect to the target, 2 methods are available. All of them need login credentials and the port information to connect to.

- void connectRCWithUserLogin(String host, int port, boolean sslMode, String portID, String username, String password)
  Use a username/password combination to login.
- connectRCWithEricKey(String host, int port, boolean sslMode, String portID, String ericKey)
  Use a so-called eric session ID (or eric key) to login. To obtain this session ID, an HTTP session to the KX device needs to be established. The session ID can then be read out from a cookie.
  Note: If the client uses username/password authentication, it receives the eric key through an event once connected (KX 3.3 or newer only) in order to use the session key for Virtual Media or Audio sessions.

When using the connect methods, a portID parameter is required. This may be either:

- The "Unique Port ID" of the port. This is a arbitrary, unique identifier for a particular port. Each port on each KX will use a different one.
- The port number of the port as string. This is the number of the port as written to the back side panel of the KX (starting with 1).
  Please note that connecting to Blade Chassis ports or Tiering ports is not possible using the port number only due to compatibility reasons. You need to specify the Unique Port ID in this case.

### Disconnecting from the target

To disconnect from the target, call this method:

- void disconnect()

### Switching to a different port

To switch from one to another port, use the following method:

- void SwitchKvmPort(int index, String portID)
  The index parameter is not used anymore. It is there for backwards compatibility reasons only. Please use the portID parameter only.

Please note that - in opposite to connecting to a port - it is required to provide the unique port ID for the portID parameter. Please see Retrieving Port Information about the list of KVM ports.

### Events

The nn.pp.rccore.RCCore interface uses a lot of events to inform the user about status changes and other things happening during the connection. Therefore, a lot of event listener interfaces are provided. Users may implement these interface, or they may make use of the RCAdapter class, which provides stub implementations of all event handlers, and functional methods to be used with Java lambdas. Please refer to the API documentation for a list of all events.

The most important events are:

- ConnectionEventListener.connected
  The Connection has successfully been established and the initial protocol handshake has been processed
- ConnectionEventListener.switchedToPort
  Successfully switched to a target port.
- ConnectionEventListener.disconnected
  The Connection has been disconnected by the user or by the KX device.
- NotificationListener.receivedNotification
  A INotificationEvent has been received and shall be delivered.
- ConnectionEventListener.portListChanged
  The KVM Port List has been received or has changed.
- VideoEventListener.resolutionChanged
  The video resolution of the target server has changed.

### Event Dispatching

The events are created internally on several different threads. In order to dispatch the events to the listener, by default all events are dispatched into the Java Swing event dispatch thread. For GUI applications, this is usually the thread where you want to receive the events.

If you want to change this, you need to install your own event dispatcher in the nn.pp.core.EventDispatcher class. You need to implement an instance of this class which implements the 3 abstract methods:

- EventDispatcher.isEventDispatchThread()
  Returns whether the current thread is the thread that events should be emitted on. If true is returned, then the event's runnable is called directly. If false is returned, then the event is dispatched via one of the next two methods, depending on the type of the event.
- EventDispatcher.invokeOnEventDispatchThread(Runnable runnable)
  Invoke a runnable on the event dispatch thread. The event is dispatched and is it not waited for the result. The strategy is "fire and forget".
- EventDispatcher.invokeOnEventDispatchThreadAndWait(Runnable runnable)
  Invoke a runnable on the event dispatch thread. The event is dispatched and the method waits for the event to end.

Then, you need to register the dispatcher:

- EventDispatcher.setEventDispatcher(EventDispatcher dispatcher)

In case you want to set an event dispatcher that does not care about threads and runs the event on whatever thread it occurred, you can install a very simple dispatcher like this:

    EventDispatcher.setEventDispatcher(new EventDispatcher() {
        @Override
        public boolean isEventDispatchThread() {
            return true;
        }

        @Override
        public void invokeOnEventDispatchThread(Runnable runnable) {
            runnable.run();
        }

        @Override
        public void invokeOnEventDispatchThreadAndWait(Runnable runnable) {
            runnable.run();
        }
    });

### Example for the basics

    RCCore rcCore = RCCoreFactory.loadGraphicalRCCore(null);
    JComponent kvmControl = rcCore.getRCJComponent();
    [ ... put kvmControl into the GUI ... ]

    // add event listeners
    rcCore.addConnectionEventListener(RCAdapter.connected(() -> System.out.println("Successfully connected")), 0);
    rcCore.addConnectionEventListener(RCAdapter.switchedToPort(portId -> System.out.println("Successfully switched to port " + portId)), ConnectionEventListener.SWITCHED_TO_PORT);
    rcCore.addConnectionEventListener(RCAdapter.disconnected(exception -> System.out.println("Disconnected due to: " + exception)), 0);

    try {
        rcCore.connectRCWithUserLogin("192.168.1.1", 443, true, "1", "admin", "pass");
    } catch (Exception ex) {
        [ ... error processing here ... ]
    }

    [ ... later in the code ... ]
    rcCore.disconnect();
    rcCore.dispose();

### Sending mouse events

For sending mouse events, use the following method:

- void sendMouseEvent(boolean relative, int x, int y, int buttonMask)

This method has the following parameters:

- boolean relative: true to send a relative mouse event (based on the current pointer position), false to send absolute coordinates. Most of the times, you will send absolute events (set this parameter to false)
- int x: The X coordinate of the mouse event or relative movement in X direction
- int y: The Y coordinate of the mouse event or relative movement in Y direction
- int buttonMask: The button mask. This is a bit mask of mouse buttons which should be pressed. 1 (bit 0) refers to the left mouse button, 2 (bit 1) to the middle mouse button and 4 (bit 2) to the right button. These bits can be combined by using bitwise OR.

For sending mouse wheel events, use the following method:

- void sendMouseWheelEvent(int rotation, int buttonMask)

#### Example

    // move the mouse to a given position
    int x = 180, y = 150;
    rcCore.sendMouseEvent(false, x, y, 0);
    Thread.sleep(100);

    // and simulate a double click
    rcCore.sendMouseEvent(false, x, y, 1); // left button down
    Thread.sleep(50);
    rcCore.sendMouseEvent(false, x, y, 0); // left button up
    Thread.sleep(100);
    rcCore.sendMouseEvent(false, x, y, 1); // left button down second time
    Thread.sleep(50);
    rcCore.sendMouseEvent(false, x, y, 0); // left button up second time --> double click

### Sending keyboard sequences

For sending key sequences to the target, RCCore uses the nn.pp.rccore.KeyboardMacro class. One can add key presses, key releases or delays to a keyboard sequence. It is also possible to send keyboard events direct (without using keyboard macros).

#### Sending single keyboard events

The keyboard implementation is based on hardware key codes (called rcKeyCode) which are independent of any keyboard layout or language. There are also translation methods available to transform a Java AWT KeyEvent into a rcKeyCode, and convenience methods to directly send a KeyEvent to the target. This way, users of the SDK can grab keyboard events and have them directly sent to the target, or they can manually create a KeyEvent instance.

To make this translation between a KeyEvent and a rcKeyCode work, users have to set the current keyboard layout to the RCCore object. Reason is that, for example, the key for KeyEvent.VK_Z may be between 'T' and 'U' on a German keyboard, or left of 'X' on a US keyboard.

It is not possible to directly translate characters into keyboard events; it is up to the user of this SDK to implement this translation. Reason is the large variety of different keyboard layouts available.

Some methods for the keyboard implementation:

- int getKeyCode(int vkCode, char keyChar, int keyLocation)
- int getKeyCode(int vkCode, char keyChar, int keyLocation, int modifiers)
- int getKeyCode(KeyEvent keyEvent)
- void sendKeyboardEvent(int rcKeyCode, boolean released)
- void sendKeyboardEvent(KeyEvent ke)
- void setLocalKeyboardMappingToRfb(Locale mapping)

#### Sending keyboard macros

Larger key sequences can also be grouped together in a keyboard macro. This is a re-usable object containing multiple key events. To generate a keyboard macro, you first have to create a KeyboardMacro.KeyCode representing one key event. Then, put these KeyboardMacro.KeyCode instances into a List to create the KeyboardMacro. The entries of the keyboard macros are always hardware key codes (rcKeyCode). See the example below. Use this method of RCCore to send the keyboard macro to the target:

- void sendKeyboardMacro(IKeyboardMacro macro, boolean prependModifiers)

Note: keyboard macros are sent asynchronously using background thread. If you have to wait for a keyboard macro to be finished, subscribe to the following event:

- KeyboardListener.keyboardMacroSent

It is, however, guaranteed that multiple keyboard macros do not interleave each other.

#### Examples (send "Hello World")

    // send "Hello" directly using the RCCore API
    rcCore.sendKeyboardEvent(new KeyEvent(rcComponent, KeyEvent.KEY_PRESSED, 0, 0, KeyEvent.VK_SHIFT, (char)0, KeyEvent.KEY_LOCATION_LEFT));
    rcCore.sendKeyboardEvent(new KeyEvent(rcComponent, KeyEvent.KEY_PRESSED, 0, KeyEvent.SHIFT_DOWN_MASK, KeyEvent.VK_H, 'H', KeyEvent.KEY_LOCATION_STANDARD));
    rcCore.sendKeyboardEvent(new KeyEvent(rcComponent, KeyEvent.KEY_RELEASED, 0, KeyEvent.SHIFT_DOWN_MASK, KeyEvent.VK_H, 'H', KeyEvent.KEY_LOCATION_STANDARD));
    rcCore.sendKeyboardEvent(new KeyEvent(rcComponent, KeyEvent.KEY_RELEASED, 0, 0, KeyEvent.VK_SHIFT, (char)0, KeyEvent.KEY_LOCATION_LEFT));
    rcCore.sendKeyboardEvent(new KeyEvent(rcComponent, KeyEvent.KEY_PRESSED, 0, KeyEvent.SHIFT_DOWN_MASK, KeyEvent.VK_E, 'E', KeyEvent.KEY_LOCATION_STANDARD));
    rcCore.sendKeyboardEvent(new KeyEvent(rcComponent, KeyEvent.KEY_RELEASED, 0, KeyEvent.SHIFT_DOWN_MASK, KeyEvent.VK_E, 'E', KeyEvent.KEY_LOCATION_STANDARD));
    rcCore.sendKeyboardEvent(new KeyEvent(rcComponent, KeyEvent.KEY_PRESSED, 0, KeyEvent.SHIFT_DOWN_MASK, KeyEvent.VK_L, 'L', KeyEvent.KEY_LOCATION_STANDARD));
    rcCore.sendKeyboardEvent(new KeyEvent(rcComponent, KeyEvent.KEY_RELEASED, 0, KeyEvent.SHIFT_DOWN_MASK, KeyEvent.VK_L, 'L', KeyEvent.KEY_LOCATION_STANDARD));
    rcCore.sendKeyboardEvent(new KeyEvent(rcComponent, KeyEvent.KEY_PRESSED, 0, KeyEvent.SHIFT_DOWN_MASK, KeyEvent.VK_L, 'L', KeyEvent.KEY_LOCATION_STANDARD));
    rcCore.sendKeyboardEvent(new KeyEvent(rcComponent, KeyEvent.KEY_RELEASED, 0, KeyEvent.SHIFT_DOWN_MASK, KeyEvent.VK_L, 'L', KeyEvent.KEY_LOCATION_STANDARD));
    rcCore.sendKeyboardEvent(new KeyEvent(rcComponent, KeyEvent.KEY_PRESSED, 0, KeyEvent.SHIFT_DOWN_MASK, KeyEvent.VK_O, 'O', KeyEvent.KEY_LOCATION_STANDARD));
    rcCore.sendKeyboardEvent(new KeyEvent(rcComponent, KeyEvent.KEY_RELEASED, 0, KeyEvent.SHIFT_DOWN_MASK, KeyEvent.VK_O, 'O', KeyEvent.KEY_LOCATION_STANDARD));

    // send the Space using key codes
    int spaceKeyCode = rcCore.getKeyCode(KeyEvent.VK_SPACE, ' ', KeyEvent.KEY_LOCATION_STANDARD);
    if (spaceKeyCode != -1) {
        rcCore.sendKeyboardEvent(spaceKeyCode, false);
        rcCore.sendKeyboardEvent(spaceKeyCode, true);
    }

    // send "World" as a keyboard macro
    List keys = new ArrayList();
    int shift = rcCore.getKeyCode(KeyEvent.VK_SHIFT, ' ', KeyEvent.KEY_LOCATION_LEFT),
        w = rcCore.getKeyCode(KeyEvent.VK_W, 'W', KeyEvent.KEY_LOCATION_STANDARD),
        o = rcCore.getKeyCode(KeyEvent.VK_O, 'O', KeyEvent.KEY_LOCATION_STANDARD),
        r = rcCore.getKeyCode(KeyEvent.VK_R, 'R', KeyEvent.KEY_LOCATION_STANDARD),
        l = rcCore.getKeyCode(KeyEvent.VK_L, 'L', KeyEvent.KEY_LOCATION_STANDARD),
        d = rcCore.getKeyCode(KeyEvent.VK_D, 'D', KeyEvent.KEY_LOCATION_STANDARD);
    keys.add(new KeyboardMacro.KeyCode(shift, true));
    keys.add(new KeyboardMacro.KeyCode(w, true));
    keys.add(new KeyboardMacro.KeyCode(w, false));
    keys.add(new KeyboardMacro.KeyCode(shift, false));
    keys.add(new KeyboardMacro.KeyCode(o, true));
    keys.add(new KeyboardMacro.KeyCode(o, false));
    keys.add(new KeyboardMacro.KeyCode(r, true));
    keys.add(new KeyboardMacro.KeyCode(r, false));
    keys.add(new KeyboardMacro.KeyCode(l, true));
    keys.add(new KeyboardMacro.KeyCode(l, false));
    keys.add(new KeyboardMacro.KeyCode(d, true));
    keys.add(new KeyboardMacro.KeyCode(d, false));
    KeyboardMacro m = new KeyboardMacro("World", "World", keys);
    rcCore.sendKeyboardMacro(m, true);

### Mouse Modes

The KX KVM Client supports two different operation modes for the mouse:

- Double Cursor Mode
  This is the regular moude operation mode. Two mouse cursors are visible: the local one on the client and the remote mouse cursor of the target system. Both may or may not be in sync.
- Single Cursor Mode
  Only the remote mouse cursor is visible. The local cursor on the client is hidden from the user. This way, there are no mouse synchronization issues. However, a hotkey needs to be defined by the user of the SDK to leave Single Cursor Mode.

To set (and get) the mouse operation mode and set single cursor hotkey, check the following methods:

- isSingleCursorModeSupported
- isSingleCursorMode
- setSingleCursorMode
- temporarilyLeaveSingleCursorMode
- setCaptureRightAway
- enforceSMMHotKeyCheck
- setSingleMouseModeHotkey

Besides these 3 operational modes, KX supports 3 mouse modes for Double Cursor Mode. These mouse modes distinguish on how mouse synchronization between the client and the remote target is handled.

- Absolute mouse mode
  This emulates a USB mouse submitting absolute coordinates. This mouse mode will keep the mouse pointers always in sync. However, not all target operating system support that.
- Automatic mouse synchronization mode
  In this mode, the KX tries to automatically detect the mouse pointer acceleration setting on the target. This works for many systems, but there are situations where this fails. See the documentation for further details.
- Standard mouse mode
  There is no translation between local and target mouse coordinates. The client mouse movements will be sent to the target 1:1.

To set (and get) the mouse mode and the supported mouse modes, check the following methods:

- isMouseModeChangeSupported
- getSupportedMouseModes
- getMouseMode
- setMouseMode

### Video Modes

The KX supports different modes for the video transmission. One can choose between two compression algorithms and color formats.

To get the supported encoding algorithms and color depth settings and to get and set the currently used values, see the following methods:

- getSupportedEncodingColorDepths
- getSupportedEncodingCompressions
- getEncodingColorDepth
- getEncodingCompression
- setEncodingColorDepth
- setEncodingCompression

#### KX2 Video Modes

The KX2 supports only one video mode, however, the compression level and the color depth can be adjusted.

- The compression parameter must be one of the following:
  - RCCore.Compression.VIDEO_OPTIMIZED
  - RCCore.Compression.UNCOMPRESSED
  - RCCore.Compression.LEVEL_1
  - RCCore.Compression.LEVEL_2
  - RCCore.Compression.LEVEL_3
  - RCCore.Compression.LEVEL_4
  - RCCore.Compression.LEVEL_5
  - RCCore.Compression.LEVEL_6
  - RCCore.Compression.LEVEL_7
  - RCCore.Compression.LEVEL_8
  - RCCore.Compression.LEVEL_9
  Both VIDEO_OPTIMIZED and UNCOMPRESSED settings deliver uncompressed video, however, VIDEO_OPTIMIZED tries to avoid tearing as much as possible. If there is only one client conencted to the port, there is no tearing at all. However, the frame rate is slightly lower than with regular UNCOMPRESSED mode.
  The LEVEL_x modes are zlib compressed modes. The higher x is, the lower is the required bandwidth. However, higher compression rates result in lower speed and frame rates.
- The color depth must be one of these:
  - RCCore.ColorDepth.COLOR_16_BIT (16 bit color mode, thousands of colors)
  - RCCore.ColorDepth.COLOR_8_BIT (8 bit color mode, 256 colors)
  - RCCore.ColorDepth.COLOR_4_BIT (4 bit color mode, 16 colors)
  - RCCore.ColorDepth.GREY_4_BIT (4 bit greyscale, 16 grey levels)
  - RCCore.ColorDepth.GREY_2_BIT (2 bit greyscale, 4 grey levels)
  - RCCore.ColorDepth.BW_1_BIT (2 bit greyscale, black/white)

By default, RCCore uses uncompressed video with 16 bit color depth.

#### KX3 Video Modes

The KX3 supports two video compression modes:

1. A JPEG like compression with different compression levels
   - The compression parameter must be one of the following:
     - RCCore.Compression.JPEG_92_5
     - RCCore.Compression.JPEG_90
     - RCCore.Compression.JPEG_85
     - RCCore.Compression.JPEG_80
     - RCCore.Compression.JPEG_75
     - RCCore.Compression.JPEG_70
     - RCCore.Compression.JPEG_65
     - RCCore.Compression.JPEG_60
     - RCCore.Compression.JPEG_50
     - RCCore.Compression.JPEG_42
     - RCCore.Compression.JPEG_36
     - RCCore.Compression.JPEG_31
     - RCCore.Compression.JPEG_25
     - RCCore.Compression.JPEG_18
     - RCCore.Compression.JPEG_12_5
     - RCCore.Compression.JPEG_9
     The number represents the quality value in percent. Higher is better quality.
   - The color depth must be set to RCCore.ColorDepth.COLOR_24_BIT.
2. A "Low Bandwidth Mode" with reduced color depths. These work in YCbCr color space or greyscale.
   - The compression value must be set to RCCore.Compression.LOW_BANDWIDTH_MODE
   - The color depth must be one of these:
     - RCCore.ColorDepth.SUB_YCBCR_444 (4 bit for Y, 4 bit for Cb and Cr)
     - RCCore.ColorDepth.SUB_YCBCR_433 (4 bit for Y, 3 bit for Cb and Cr)
     - RCCore.ColorDepth.SUB_YCBCR_344 (3 bit for Y, 4 bit for Cb and Cr)
     - RCCore.ColorDepth.SUB_YCBCR_333 (3 bit for Y, 3 bit for Cb and Cr)
     - RCCore.ColorDepth.SUB_YCBCR_244 (2 bit for Y, 4 bit for Cb and Cr)
     - RCCore.ColorDepth.SUB_YCBCR_233 (2 bit for Y, 3 bit for Cb and Cr)
     - RCCore.ColorDepth.GREY_4_BIT (4 bit greyscale, 16 grey levels)
     - RCCore.ColorDepth.GREY_3_BIT (3 bit greyscale, 8 grey levels)
     - RCCore.ColorDepth.GREY_2_BIT (2 bit greyscale, 4 grey levels)

By default, RCCore uses JPEG compression at 75% quality with 24 bit color depth.

In addition to the video modes, the client can do some post-processing to the video data. Therefore, RCCore supports to set a preferred connection type. The RCCore.PreferredConnectionType enum can be used to either prefer text readability (RCCore.PreferredConnectionType.TEXT), which enables some sharpening to improve the text, or to prefer video (RCCore.PreferredConnectionType.VIDEO), which will preserve color as much as possible.

The methods to set or get the preferred video mode are:

- getPreferredConnectionType
- setPreferredConnectionType

KX3 does not support setting of any color subsamplings (RCCore.ColorSubsampling). For KX3, a 4:2:0 subsampling is always used.

#### KX4 Video Modes

The KX4 (similar to KX3) supports two video compression modes:

1. A JPEG like compression with different compression levels
   - The compression parameter must be one of the following:
     - RCCore.Compression.JPEG_92_5
     - RCCore.Compression.JPEG_90
     - RCCore.Compression.JPEG_85
     - RCCore.Compression.JPEG_80
     - RCCore.Compression.JPEG_75
     - RCCore.Compression.JPEG_70
     - RCCore.Compression.JPEG_65
     - RCCore.Compression.JPEG_60
     - RCCore.Compression.JPEG_50
     - RCCore.Compression.JPEG_42
     - RCCore.Compression.JPEG_36
     - RCCore.Compression.JPEG_31
     - RCCore.Compression.JPEG_25
     - RCCore.Compression.JPEG_18
     - RCCore.Compression.JPEG_12_5
     - RCCore.Compression.JPEG_9
     The number represents the quality value in percent. Higher is better quality.
   - The color depth must be set to RCCore.ColorDepth.COLOR_24_BIT.
2. A "Low Bandwidth Mode" with reduced color depths. These work in YCbCr color space or greyscale.
   - The compression value must be set to RCCore.Compression.LOW_BANDWIDTH_MODE
   - The color depth must be one of these:
     - RCCore.ColorDepth.SUB_YCBCR_655 (6 bit for Y, 5 bit for Cb and Cr)
     - RCCore.ColorDepth.SUB_YCBCR_644 (6 bit for Y, 4 bit for Cb and Cr)
     - RCCore.ColorDepth.SUB_YCBCR_555 (5 bit for Y, 5 bit for Cb and Cr)
     - RCCore.ColorDepth.SUB_YCBCR_544 (5 bit for Y, 4 bit for Cb and Cr)
     - RCCore.ColorDepth.SUB_YCBCR_444 (4 bit for Y, 4 bit for Cb and Cr)
     - RCCore.ColorDepth.SUB_YCBCR_433 (4 bit for Y, 3 bit for Cb and Cr)
     - RCCore.ColorDepth.SUB_YCBCR_344 (3 bit for Y, 4 bit for Cb and Cr)
     - RCCore.ColorDepth.SUB_YCBCR_333 (3 bit for Y, 3 bit for Cb and Cr)
     - RCCore.ColorDepth.SUB_YCBCR_244 (2 bit for Y, 4 bit for Cb and Cr)
     - RCCore.ColorDepth.SUB_YCBCR_233 (2 bit for Y, 3 bit for Cb and Cr)
     - RCCore.ColorDepth.GREY_6_BIT (6 bit greyscale, 64 grey levels)
     - RCCore.ColorDepth.GREY_5_BIT (5 bit greyscale, 32 grey levels)
     - RCCore.ColorDepth.GREY_4_BIT (4 bit greyscale, 16 grey levels)
     - RCCore.ColorDepth.GREY_3_BIT (3 bit greyscale, 8 grey levels)
     - RCCore.ColorDepth.GREY_2_BIT (2 bit greyscale, 4 grey levels)

By default, RCCore uses JPEG compression at 75% quality with 24 bit color depth.

The setting of the RCCore.PreferredConnectionType (see KX3 chapter) does not have any effect for KX4. The client's postprocessing is completely disabled due to performance considerations.

In addition to seting the video modes, KX4 supports different color subsampling schemes. The following modes are supported:

- RCCore.ColorSubsampling.SUB_420 4:2:0 color subsampling
- RCCore.ColorSubsampling.SUB_422 4:2:2 color subsampling
- RCCore.ColorSubsampling.SUB_444 4:4:4 color subsampling

Use the following methods to get or set the color subsampling:

- getSupportedEncodingColorSubsamplings
- getEncodingColorSubsampling
- getActualColorSubsampling
- setEncodingColorSubsampling

The color subsampling is the practice of encoding images by implementing less resolution for chroma information than for luma information, taking advantage of the human visual system's lower acuity for color differences than for luminance. With 4:4:4 subsampling, the color (chroma) information has the same resolution as the luminance information, hence there is no compression, and no artifacts. The 4:2:2 subsampling has 2 pixels of color information for 4 pixels of luminance information, and the 4:2:0 color subsampling has only 1 pixel color information for 4 pixels luminance information. Compared to 4:4:4, 4:2:0 reduces the overall bandwidth by 50%. However, color subsamplings other than 4:4:4 may reduce the video quality (artifacts). It depends on the content of the video whether this is visible or not.

### Retrieving Port Information

The KX sends information about its ports to the KVM client, and thus, the SDK is also able to receive this port information. Information about the port is given in the IKvmPort interface.

The port list is available with the void RCCore.GetPortList() method call and the ConnectionEventListener.portListChanged() event.

The KX sends the port list in the following cases:

- At initial connection. Clients receive the port information right after connecting to the KX.
- After each switch to another port. See Switching to a different port on how to switch ports.
- After the client explicitely requested the port list using the method void RCCore.requestPortListRefresh().
  Please note that this is only available in KX3 firmware 3.4 or newer. It is not supported by KX2. Please look at the method bool RCCore.isPortListRefreshSupported() and the event ConnectionEventListener.portListRefreshSupportChanged().

Please note that due to compatibility reasons, the port list may be lacking some information. In case of the initial receiving and after the initial KVM switch (usually also received within the initial protocol handshake), the following are missing in the port list:

- Cascaded ports (blade chassis and tiering ports) are missing from the list
- The CIM type is not set (getCimType() will return null)
- The VM port information (isVmPortType()) is not set properly (always set to false)
- The Power Permissions are not set (getPowerPermission() will return null)

The above restrictions also apply to KX2 and KX3 firmwares 3.3 and older. Only KX3 3.4 and newer support the full port information.

### Multi Monitor Connections

Some KVM target computers may have more than one monitor attached. All these monitors may be used with KVM. In that case, the KVM client or the KX firmware needs to be aware of that, especially the mouse handling is problematic in such cases.

Whenever Multi Port KVM sessions are created, users need to create separate KVM sessions to each of the ports. For each session, a separate nn.pp.rccore.RCCore needs to be created and used. In order to make a multi port connection out of these separate sessions, these nn.pp.rccore.RCCore objecty need to be linked or associated with each other.

#### KX3 Dual Port Groups

The KX3 Device has built-in support for Dual Monitor Targets via port groups. In this case, the mouse handling is handled on the KX3. In order to inform KX3 about the connection to dual port groups, an "association" message is sent from the client to the KX3. Users of nn.pp.rccore.RCCore need to set an "association tag" to the nn.pp.rccore.RCCore objects. For both the nn.pp.rccore.RCCore objects forming the dual monitor group, the same association tag must be used; the tag itself should be unique. This tag needs to be set before the actual connection is made.

Example:

    RCCore rcCoreLeft = RCCoreFactory.loadGraphicalRCCore(null);
    RCCore rcCoreRight = RCCoreFactory.loadGraphicalRCCore(null);
    String uniqueTag = "some_unique_tag";
    Map propsLeft = new HashMap();
    Map propsRight = new HashMap();
    propsLeft.put(RCCore.ClientSessionInitProperties.MULTI_MONITOR_ASSOCIATION_ID, uniqueTag);
    propsRight.put(RCCore.ClientSessionInitProperties.MULTI_MONITOR_ASSOCIATION_ID, uniqueTag);
    rcCoreLeft.setClientSessionInitProperties(propsLeft);
    rcCoreRight.setClientSessionInitProperties(propsRight);
    rcCoreLeft.connect[....]
    propsRight.connect[....]

With this light association, both nn.pp.rccore.RCCore objects act independently; the mouse is associated within the KX3 server code. Mouse and keyboard work on both connections, but they are not associated in the KVM clients. Hence, moving the mouse from one KVM Control to the other works, but the mouse is handled independently. When a mouse button is pressed while moving from left to right (or the other way), the mouse button is released when leaving the first KVM Control and pressed again when entering the second one. In order to avoid this, the KVM client's mouse need to be associated as well. See the chapter about KX4 Multi Monitor Targets how to achieve this.

#### KX4 Multi Monitor Targets

KX4-101 devices do not support multi monitor targets because they offer just one port. In order to achieve this functionality, it must be done in the client. Therefore, the KVM clients need to know all nn.pp.rccore.RCCore objects which form this "virtual" port group, and they must know the positions of all the connections in the extended desktop of the target system in order to adjust the mouse movements. nn.pp.rccore.RCCore offers an API to set such associations.

Note: For KX3 connections, setting the associations may also be helpful in order to improve mouse behavior, e.g. when moving with a pressed mouse button from one KVM Client window the other (moving a window on the target).

The following API is available to set these associations:

- The nn.pp.rccore.Association class.
  It describes the position of an nn.pp.rccore.RCCore object amongst the several connections. It has the following parameters:
  - monitorIndex: A consecutive number, starting with 0 for the primary monitor. The primary is often the top-left monitor, but not necessarily.
  - monitorPositionX: The X axis position of this nn.pp.rccore.RCCore object. The top-left display starts with 0, the one right has 1. A third monitor right to the second has 2, and so on. If the ports are associated vertically, then all have 0 X position (but different Y positions).
  - monitorPositionY: The Y axis position of this nn.pp.rccore.RCCore object. The top-left display starts with 0, the one under it has 1. A third monitor under to the second has 3, and so on. If the ports are associated horizontally, then all have 0 Y position (but different X positions).
- The nn.pp.rccore.AssociatedRCCore class.
  It binds an nn.pp.rccore.RCCore object to its nn.pp.rccore.Association.
- The nn.pp.rccore.RCCore.setAssociatedRcCores method.
  This method sets the associations for this nn.pp.rccore.RCCore object by setting both the position of this connection objects and the other nn.pp.rccore.RCCore objects with their positions. It has the following parameters:
  - associated: A list of all other RCCore objects, along with their position on the extended desktop.
  - association: The position of this RCCore object on the extended desktop.
  - clientSideMouseAssociations: A boolean value, indicating whether the mouse adjustments should be done in the KVM client (true; use for KX4-101 "virtual" multi monitor ports) or is handled in the KX server code (false; use for KX3 Dual Port groups).
  Note: This method must be called after the connection is made. A good place is either the ConnectionEventListener.connected or ConnectionEventListener.switchedToPort event handler.

Example:

    RCCore rcCoreLeft = RCCoreFactory.loadGraphicalRCCore(logger);
    RCCore rcCoreRight = RCCoreFactory.loadGraphicalRCCore(logger);

    rcCoreLeft.addConnectionEventListener(RCAdapter.switchedToPort(portId -> {
        // location of the left port:
        // * monitor index is 0 (--> primary port)
        // * X coordinate is 0 (--> left column window)
        // * Y coordinate is 0 (--> top row window)
        Association association = new Association(0, 0, 0);

        // create the list of the associated cores
        List assocRcCores = new ArrayList();

        // add the right window to the associations:
        // * monitor index is 1 (--> secondary port)
        // * X position is 1 (--> right column)
        // * Y position is 0 (--> top row)
        // RCCore is the right RCCore
        AssociatedRCCore rightAssoc = new AssociatedRCCore(1, 1, 0, rcCoreRight);
        assocRcCores.add(rightAssoc);

        // now tell the left RcCore about the associations
        rcCoreLeft.setAssociatedRcCores(assocRcCores, association, true);
    }), ConnectionEventListener.SWITCHED_TO_PORT);

    rcCoreRight.addConnectionEventListener(RCAdapter.switchedToPort(portId -> {
        // location of the right port:
        // * monitor index is 1 (--> secondary port)
        // * X coordinate is 1 (--> right column window)
        // * Y coordinate is 0 (--> top row window)
        Association association = new Association(1, 1, 0);

        // create the list of the associated cores
        List assocRcCores = new ArrayList();

        // add the left window to the associations:
        // * monitor index is 0 (--> primary port)
        // * X position is 0 (--> left column)
        // * Y position is 0 (--> top row)
        // RCCore is the left RCCore
        AssociatedRCCore leftAssoc = new AssociatedRCCore(0, 0, 0, rcCoreLeft);
        assocRcCores.add(leftAssoc);

        // now tell the right RcCore about the associations
        rcCoreRight.setAssociatedRcCores(assocRcCores, association, true);
    }), ConnectionEventListener.SWITCHED_TO_PORT);

    rcCoreLeft.connect...();
    rcCoreRight.connect...();

#### Static Multi Monitor Configurations

There might be circumstances when the KVM Client cannot guarantee a fully working mouse, e.g. when there are monitors attached to the target which are not part of KVM. For example, the target computer has one local monitor attached and one monitor is used with KVM (but the both monitors are not configured to "mirroring"). In such cases, it is required to do a static mouse adjustment in the client, because otherwise, the client would make wrong assumptions about the desktop dimensions.

RCCore offers the following API to achieve that:

RCCore.setMultipleScreenConfiguration

This method has the following parameters:

- screenOffset: a Point, describing the offset of this monitor in the extended desktop configuration. In other words, the pixel coordinates of the upper left corner of this monitor.
- desktopResolution: a Dimension, describing the resolution of the whole extended desktop (with all monitors, including this one). If the extended desktop does not form a rectangle (e.g. 3 monitors, not forming a row, or monitors with different resolutions), then this is the smallest possible rectangle which is large enough to include all monitor coordinates.