view src/main/java/com/glavsoft/rfb/protocol/Protocol.java @ 419:7e811f33b99d

Adhoc implement multi display
author Tatsuki IHA <e125716@ie.u-ryukyu.ac.jp>
date Fri, 01 Jan 2016 02:39:25 +0900
parents 6a8b661367c6
children cee43ceac9b3
line wrap: on
line source

// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC.
// All rights reserved.
//
//-------------------------------------------------------------------------
// This file is part of the TightVNC software.  Please visit our Web site:
//
//                       http://www.tightvnc.com/
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//-------------------------------------------------------------------------
//

package com.glavsoft.rfb.protocol;

import com.glavsoft.core.SettingsChangedEvent;
import com.glavsoft.exceptions.*;
import com.glavsoft.rfb.*;
import com.glavsoft.rfb.client.ClientToServerMessage;
import com.glavsoft.rfb.client.FramebufferUpdateRequestMessage;
import com.glavsoft.rfb.client.SetEncodingsMessage;
import com.glavsoft.rfb.client.SetPixelFormatMessage;
import com.glavsoft.rfb.encoding.PixelFormat;
import com.glavsoft.rfb.encoding.decoder.DecodersContainer;
import com.glavsoft.rfb.protocol.state.HandshakeState;
import com.glavsoft.rfb.protocol.state.ProtocolState;
import com.glavsoft.transport.Reader;
import com.glavsoft.transport.Writer;

import java.util.logging.Logger;

import jp.ac.u_ryukyu.treevnc.TreeRFBProto;

public class Protocol implements ProtocolContext, IChangeSettingsListener {
    private ProtocolState state;
    private final Logger logger;
    private final IPasswordRetriever passwordRetriever;
    private final ProtocolSettings settings;
    private int fbWidth;
    private int fbHeight;
    private PixelFormat pixelFormat;
    private final Reader reader;
    private final Writer writer;
    private String remoteDesktopName;
    private MessageQueue messageQueue;
    private final DecodersContainer decoders;
    private SenderTask senderTask;
    private ReceiverTask receiverTask;
    private IRfbSessionListener rfbSessionListener;
    private IRepaintController repaintController;
    private PixelFormat serverPixelFormat;
    private Thread senderThread;
    private Thread receiverThread;
    private boolean isTight;
    private String protocolVersion;
    private byte[] initData;
    private TreeRFBProto rfb;

    public Protocol(Reader reader, Writer writer,
                    IPasswordRetriever passwordRetriever, ProtocolSettings settings, TreeRFBProto myRfb) {
        this.reader = reader;
        this.writer = writer;
        this.passwordRetriever = passwordRetriever;
        this.settings = settings;
        decoders = new DecodersContainer();
        decoders.instantiateDecodersWhenNeeded(settings.encodings);
        state = new HandshakeState(this);
        logger = Logger.getLogger(getClass().getName());
        this.rfb = myRfb;
    }


    @Override
    public void changeStateTo(ProtocolState state) {
        this.state = state;
    }

    public void handshake() throws UnsupportedProtocolVersionException, UnsupportedSecurityTypeException,
    AuthenticationFailedException, TransportException, FatalException {
        while (state.next()) {
            // continue;
        }
        this.messageQueue = new MessageQueue();
        // ここでsoundpacketqueueを作成する
    }

    @Override
    public PixelFormat getPixelFormat() {
        return pixelFormat;
    }

    @Override
    public void setPixelFormat(PixelFormat pixelFormat) {
        this.pixelFormat = pixelFormat;
        if (repaintController != null) {
            repaintController.setPixelFormat(pixelFormat);
        }
    }

    @Override
    public String getRemoteDesktopName() {
        return remoteDesktopName;
    }

    @Override
    public void setRemoteDesktopName(String name) {
        remoteDesktopName = name;
    }

    @Override
    public int getFbWidth() {
        return fbWidth;
    }

    @Override
    public void setFbWidth(int fbWidth) {
        this.fbWidth = fbWidth;
    }

    @Override
    public int getFbHeight() {
        return fbHeight;
    }

    @Override 
    public byte[] getInitData() {
        return initData;
    }

    @Override
    public void setInitData(byte[] initData) {
        this.initData = initData;
    }

    @Override
    public void setFbHeight(int fbHeight) {
        this.fbHeight = fbHeight;
    }

    @Override
    public IPasswordRetriever getPasswordRetriever() {
        return passwordRetriever;
    }

    @Override
    public ProtocolSettings getSettings() {
        return settings;
    }

    @Override
    public Writer getWriter() {
        return writer;
    }

    @Override
    public Reader getReader() {
        return reader;
    }

    /**
     * Following the server initialisation message it's up to the client to send
     * whichever protocol messages it wants.  Typically it will send a
     * SetPixelFormat message and a SetEncodings message, followed by a
     * FramebufferUpdateRequest.  From then on the server will send
     * FramebufferUpdate messages in response to the client's
     * FramebufferUpdateRequest messages.  The client should send
     * FramebufferUpdateRequest messages with incremental set to true when it has
     * finished processing one FramebufferUpdate and is ready to process another.
     * With a first client, the rate at which FramebufferUpdateRequests are sent
     * should be regulated to avoid hogging the network.
     */
    public void startNormalHandling(IRfbSessionListener rfbSessionListener,
            IRepaintController repaintController, ClipboardController clipboardController,
            TreeRFBProto rfb) 
    {
        receiverTask = new ReceiverTask(
                reader, repaintController,
                clipboardController,
                decoders, this, rfb);
        startNormalHandling1(rfbSessionListener, repaintController, clipboardController);
    }

    public void startNormalHandling1(IRfbSessionListener rfbSessionListener,
            IRepaintController repaintController, ClipboardController clipboardController) {
        this.rfbSessionListener = rfbSessionListener;
        this.repaintController = repaintController;
        //		if (settings.getColorDepth() == 0) {
        //			settings.setColorDepth(pixelFormat.depth); // the same the server sent when not initialized yet
        //		}
        serverPixelFormat = pixelFormat;
        correctServerPixelFormat();
        setPixelFormat(createPixelFormat(settings));
        sendMessage(new SetPixelFormatMessage(pixelFormat));
        logger.fine("sent: " + pixelFormat);

        sendSupportedEncodingsMessage(settings);
        settings.addListener(this); // to support pixel format (color depth), and encodings changes
        settings.addListener(repaintController);
        if (rfb.isTreeManager()) {
            sendRefreshMessage();
        }
        senderTask = new SenderTask(messageQueue, writer, this);
        senderThread = new Thread(senderTask, "RfbSenderTask");
        senderThread.start();
        decoders.resetDecoders();

        receiverThread = new Thread(receiverTask, "RfbReceiverTask");
        receiverThread.start();
    }

    private void correctServerPixelFormat() {
        // correct true color flag - we don't support color maps, so always set it up
        serverPixelFormat.trueColourFlag = 1;
        // correct .depth to use actual depth 24 instead of incorrect 32, used by ex. UltraVNC server, that cause
        // protocol incompatibility in ZRLE encoding
        final long significant = serverPixelFormat.redMax << serverPixelFormat.redShift |
                serverPixelFormat.greenMax << serverPixelFormat.greenShift |
                serverPixelFormat.blueMax << serverPixelFormat.blueShift;
        if (32 == serverPixelFormat.bitsPerPixel &&
                ((significant & 0x00ff000000L) == 0 || (significant & 0x000000ffL) == 0) &&
                32 == serverPixelFormat.depth) {
            serverPixelFormat.depth = 24;
        }
    }

    @Override
    public void sendMessage(ClientToServerMessage message) {
        messageQueue.put(message);
    }

    private void sendSupportedEncodingsMessage(ProtocolSettings settings) {
        decoders.instantiateDecodersWhenNeeded(settings.encodings);
        SetEncodingsMessage encodingsMessage = new SetEncodingsMessage(settings.encodings);
        sendMessage(encodingsMessage);
        logger.fine("sent: " + encodingsMessage.toString());
    }

    /**
     * create pixel format by bpp
     */
    private PixelFormat createPixelFormat(ProtocolSettings settings) {
        int serverBigEndianFlag = serverPixelFormat.bigEndianFlag;
        switch (settings.getColorDepth()) {
        case ProtocolSettings.COLOR_DEPTH_24:
            return PixelFormat.create24bitColorDepthPixelFormat(serverBigEndianFlag);
        case ProtocolSettings.COLOR_DEPTH_16:
            return PixelFormat.create16bitColorDepthPixelFormat(serverBigEndianFlag);
        case ProtocolSettings.COLOR_DEPTH_8:
            return PixelFormat.create8bitColorDepthBGRPixelFormat(serverBigEndianFlag);
        case ProtocolSettings.COLOR_DEPTH_6:
            return PixelFormat.create6bitColorDepthPixelFormat(serverBigEndianFlag);
        case ProtocolSettings.COLOR_DEPTH_3:
            return PixelFormat.create3bppPixelFormat(serverBigEndianFlag);
        case ProtocolSettings.COLOR_DEPTH_SERVER_SETTINGS:
            return serverPixelFormat;
        default:
            // unsupported bpp, use default
            return PixelFormat.create24bitColorDepthPixelFormat(serverBigEndianFlag);
        }
    }

    @Override
    public void settingsChanged(SettingsChangedEvent e) {
        ProtocolSettings settings = (ProtocolSettings) e.getSource();
        if (settings.isChangedEncodings()) {
            sendSupportedEncodingsMessage(settings);
        }
        if (settings.isChangedColorDepth() && receiverTask != null) {
            receiverTask.queueUpdatePixelFormat(createPixelFormat(settings));
        }
    }

    @Override
    public void sendRefreshMessage() {
        sendMessage(new FramebufferUpdateRequestMessage(rfb.getX(), rfb.getY(), rfb.frameSizeWidth * rfb.getRetinaScale(), rfb.frameSizeHeight * rfb.getRetinaScale(), false));
        // sendMessage(new FramebufferUpdateRequestMessage(0, 0, 1920, 1080, false));
        logger.fine("sent: full FB Refresh");
    }

    @Override
    public void cleanUpSession(String message) {
        cleanUpSession();
        if (message!=null)
            rfbSessionListener.rfbSessionStopped(message);
    }

    public synchronized void cleanUpSession() {
        if (senderTask != null) {
            // stop senderTask to VNCServer task.
            senderTask.stopTask(); 
            try {
                senderThread.join();
            } catch (InterruptedException e) {
                System.out.println("any thread has interrupted the current thread");
            }
            senderTask = null;
        }
        stopReceiverTask();
    }

    @Override
    public void stopReceiverTask() {
        if (receiverTask != null) {
            receiverTask.stopTask(); 
            try {
                receiverThread.join();
            } catch (InterruptedException e) {
                System.out.println("any thread has interrupted the current thread");
            }
            receiverTask = null;
        }
    }

    @Override
    public int getNumberOfRectangle() {
        int numberOfRectangles = receiverTask.numberOfRectangles;
        return numberOfRectangles;
    }

    @Override
    public TreeRFBProto getRfb() {
        return rfb;
    }

    @Override
    public void setTight(boolean isTight) {
        this.isTight = isTight;
    }

    @Override
    public boolean isTight() {
        return isTight;
    }

    @Override
    public void setProtocolVersion(String protocolVersion) {
        this.protocolVersion = protocolVersion;
    }

    @Override
    public String getProtocolVersion() {
        return protocolVersion;
    }




    @Override
    public void resetDecoder() {
        decoders.resetDecoders();
    }
}