Package org.jdesktop.wonderland.modules.xremwin.client

Source Code of org.jdesktop.wonderland.modules.xremwin.client.ClientXrw$Statistics

/**
* Project Wonderland
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., All Rights Reserved
*
* Redistributions in source code form must reproduce the above
* copyright and this condition.
*
* The contents of this file are subject to the GNU General Public
* License, Version 2 (the "License"); you may not use this file
* except in compliance with the License. A copy of the License is
* available at http://www.opensource.org/licenses/gpl-license.php.
*
* Sun designates this particular file as subject to the "Classpath"
* exception as provided by Sun in the License file that accompanied
* this code.
*/
package org.jdesktop.wonderland.modules.xremwin.client;

import java.awt.Toolkit;
import java.io.IOException;
import java.util.logging.Level;
import com.jme.math.Vector3f;
import org.jdesktop.wonderland.modules.xremwin.client.Proto.CreateWindowMsgArgs;
import org.jdesktop.wonderland.modules.xremwin.client.Proto.DestroyWindowMsgArgs;
import org.jdesktop.wonderland.modules.xremwin.client.Proto.ShowWindowMsgArgs;
import org.jdesktop.wonderland.modules.xremwin.client.Proto.ConfigureWindowMsgArgs;
import org.jdesktop.wonderland.modules.xremwin.client.Proto.PositionWindowMsgArgs;
import org.jdesktop.wonderland.modules.xremwin.client.Proto.RestackWindowMsgArgs;
import org.jdesktop.wonderland.modules.xremwin.client.Proto.WindowSetDecoratedMsgArgs;
import org.jdesktop.wonderland.modules.xremwin.client.Proto.WindowSetBorderWidthMsgArgs;
import org.jdesktop.wonderland.modules.xremwin.client.Proto.WindowSetUserDisplMsgArgs;
import org.jdesktop.wonderland.modules.xremwin.client.Proto.WindowSetRotateYMsgArgs;
import org.jdesktop.wonderland.modules.xremwin.client.Proto.DisplayPixelsMsgArgs;
import org.jdesktop.wonderland.modules.xremwin.client.Proto.CopyAreaMsgArgs;
import org.jdesktop.wonderland.modules.xremwin.client.Proto.ControllerStatusMsgArgs;
import org.jdesktop.wonderland.modules.xremwin.client.Proto.MessageArgs;
import org.jdesktop.wonderland.modules.xremwin.client.Proto.ServerMessageType;
import org.jdesktop.wonderland.modules.appbase.client.utils.stats.StatisticsReporter;
import org.jdesktop.wonderland.modules.appbase.client.utils.stats.StatisticsSet;
import org.jdesktop.wonderland.modules.appbase.client.ProcessReporter;
import org.jdesktop.wonderland.common.ExperimentalAPI;
import org.jdesktop.wonderland.modules.appbase.client.ControlArb;
import org.jdesktop.wonderland.common.cell.CellTransform;
import javax.swing.SwingUtilities;
import java.io.EOFException;

// TODO: 0.4 protocol: temporarily insert
import org.jdesktop.wonderland.modules.xremwin.client.Proto.DisplayCursorMsgArgs;
import org.jdesktop.wonderland.modules.xremwin.client.Proto.MoveCursorMsgArgs;
import org.jdesktop.wonderland.modules.xremwin.client.Proto.ShowCursorMsgArgs;

/*
* The generic Xrw client superclass. This is a protocol interpreter
* for the Xremwin protocol. It communicates with a Server which
* represents either an Xremwin server or Xremwin master.
*
* Known Subclasses: ClientXrwMaster, ClientXrwSlave
*
* @author deronj
*/
@ExperimentalAPI
public abstract class ClientXrw implements Runnable {

    // TODO: Bugs in parsing the incoming stream from the server
    // end up trying to create huge windows and blowing the java heap
    // Prevent this. Eventually need to do cleaner.
    // It would be cleaner to base these numbers on the max tex width/height of the
    // graphics card of the user display.
    private static final int WINDOW_MAX_WIDTH  = 4000;
    private static final int WINDOW_MAX_HEIGHT = 4000;

    // The connection to the XRemwin server or master.
    protected ServerProxy serverProxy;

    // The unique ID of this client connecting to the app (assigned by the master).
    protected int clientId;
    private CreateWindowMsgArgs createWinMsgArgs = new CreateWindowMsgArgs();
    private DestroyWindowMsgArgs destroyWinMsgArgs = new DestroyWindowMsgArgs();
    private ShowWindowMsgArgs showWinMsgArgs = new ShowWindowMsgArgs();
    private ConfigureWindowMsgArgs configureWinMsgArgs = new ConfigureWindowMsgArgs();
    private PositionWindowMsgArgs positionWinMsgArgs = new PositionWindowMsgArgs();
    private RestackWindowMsgArgs restackWinMsgArgs = new RestackWindowMsgArgs();
    private WindowSetDecoratedMsgArgs winSetDecoratedMsgArgs = new WindowSetDecoratedMsgArgs();
    private WindowSetBorderWidthMsgArgs winSetBorderWidthMsgArgs = new WindowSetBorderWidthMsgArgs();
    private WindowSetUserDisplMsgArgs winSetUserDisplMsgArgs = new WindowSetUserDisplMsgArgs();
    private WindowSetRotateYMsgArgs winSetRotateYMsgArgs = new WindowSetRotateYMsgArgs();
    private DisplayPixelsMsgArgs displayPixelsMsgArgs = new DisplayPixelsMsgArgs();
    private CopyAreaMsgArgs copyAreaMsgArgs = new CopyAreaMsgArgs();
    private ControllerStatusMsgArgs controllerStatusMsgArgs = new ControllerStatusMsgArgs();

    // TODO: 0.4 protocol: temporarily insert
    private DisplayCursorMsgArgs displayCursorMsgArgs = new DisplayCursorMsgArgs();
    private MoveCursorMsgArgs moveCursorMsgArgs = new MoveCursorMsgArgs();
    private ShowCursorMsgArgs showCursorMsgArgs = new ShowCursorMsgArgs();

    // true indicates the client thread should stop running
    protected boolean stop;

    // For debug
    //private static final boolean ENABLE_XREMWIN_STATS = false;
    private static boolean ENABLE_XREMWIN_STATS = false;
    private StatisticsReporter statReporter;
    private long numRequests = 0;
    private long displayPixelsNumBytes = 0;
    private long numCopyAreas = 0;

    // For debug
    private static boolean verbose = false;
    private static Level loggerLevelOrig;


    static {
        /* For debug
        System.err.println("logger level obj = " + AppXrw.logger.getLevel());
        if (AppXrw.logger.getLevel() != null) {
        System.err.println("logger level int = " + AppXrw.logger.getLevel().intValue());
        } else {
        System.exit(1);
        }
         */
        loggerLevelOrig = AppXrw.logger.getLevel();
        if (verbose) {
            AppXrw.logger.setLevel(Level.FINER);
        }
    }

    // For debug
    public static void toggleXremwinStatsEnable() {
        ENABLE_XREMWIN_STATS = !ENABLE_XREMWIN_STATS;
        System.err.println("Xremwin statistics are " +
                (ENABLE_XREMWIN_STATS ? "enabled" : "disabled"));
    }

    // For debug
    public static void toggleVerbosity() {
        verbose = !verbose;
        if (verbose) {
            AppXrw.logger.setLevel(Level.FINER);
        } else {
            AppXrw.logger.setLevel(loggerLevelOrig);
        }
        System.err.println("Xremwin verbosity is " +
                (verbose ? "enabled" : "disabled"));
    }
    /** The associated application */
    protected AppXrw app;
    /**
     * The control arbitrator used by this app.
     */
    protected ControlArb controlArb;
    /**
     * The protocol interpreter thread (the main loop of the client)
     */
    protected Thread thread;
    /** The output reporter */
    protected ProcessReporter reporter;
    /** Is the server connected? */
    protected boolean serverConnected;
    /** Whether the client is enabled. */
    protected boolean enable;
    /** Lock object used for enable. */
    private final Object enableLock = new Object();
    /** Used by the logging messages in this class */
    private int messageCounter = 0;

    /**
     * Create a new instance of ClientXrw.
     *
     * @param app The application for whom the client is operating.
     * @param controlArb The control arbiter for the app.
     * @param reporter Report output and exit status to this.
     * @throws InstantiationException If it could not make contact with the server.
     */
    public ClientXrw(AppXrw app, ControlArb controlArb, ProcessReporter reporter)
            throws InstantiationException {
        this.app = app;
        this.controlArb = controlArb;
        this.reporter = reporter;

        // TODO: it would be nice to put the app instance name here
        thread = new Thread(this, "Remote Window Client");

    /* TODO
    if (ENABLE_XREMWIN_STATS) {
    statReporter = new StatisticsReporter(15, new Statistics(),
    new Statistics(),
    new Statistics());
    statReporter.start();
    }
     */
    }

    /**
     * Release resources held.
     */
    public void cleanup() {
        if (!stop && thread != null) {
            stop = true;
            /* Note: can't join thread here. Conflicts with app base shutdown hook.
            try {
                thread.join();
            } catch (InterruptedException ex) {
            }
            */
            thread = null;
        }

        if (reporter != null) {
            reporter.cleanup();
            reporter = null;
        }

        app = null;

        if (serverProxy != null) {
            serverProxy.cleanup();
            serverProxy = null;
        }

        enable = false;
        AppXrw.logger.severe("ClientXrw cleaned up");
    }

    /**
     * Start the interpreter thread.
     */
    protected void start() {
        thread.start();
    }

    /**
     * The app associated with this client.
     */
    public AppXrw getApp() {
        return app;
    }

    /**
     * After the client loop is first started it will wait to make the first window visible
     * until the client is enabled.
     */
    public void enable () {
        synchronized (enableLock) {
            if (enable) return;
            enable = true;
            enableLock.notifyAll();
        }
    }

    /**
     * The  main loop of the client.
     */
    public void run() {

        while (serverConnected && !stop && serverProxy != null) {

            try {

                // Read message type from the server.
                ServerMessageType msgType = serverProxy.getMessageType();
                //if (msgType != Proto.ServerMessageType.DISPLAY_PIXELS){
                AppXrw.logger.info("msgType " + (++messageCounter) + ": " + msgType);
                //}

                /* TODO
                   if (ENABLE_XREMWIN_STATS) {
                   synchronized (this) {
                   numRequests++;
                   }
                   }
                */

                // Get the full message
                MessageArgs msgArgs = readMessageArgs(msgType);
                if (msgArgs != null) {
                    //if (msgType != Proto.ServerMessageType.DISPLAY_PIXELS){
                    AppXrw.logger.info("msgArgs: " + msgArgs);
                    //}

                    /* For debug: an example of how to ignore the firefox heartbeat which occurs on igoogle
                       if (msgType == ServerMessageType.DISPLAY_PIXELS) {
                       if (displayPixelsMsgArgs.x == 1261 &&
                       displayPixelsMsgArgs.y == 3 &&
                       displayPixelsMsgArgs.w == 17 &&
                       displayPixelsMsgArgs.h == 17) {
                       } else {
                       AppXrw.logger.info("msgType " + (++messageCounter) + ": " + msgType + ", msgArgs: " + msgArgs);
                       }
                       } else {
                       AppXrw.logger.info("msgType " + (++messageCounter) + ": " + msgType + ", msgArgs: " + msgArgs);
                       }
                    */
                }

                // Process the message
                processMessage(msgType);

            } catch (Throwable throwable) {
                if (serverProxy != null) {
                    throwable.printStackTrace();
                    stop = true;
                    cleanup();

                    if (app.isInSas()) {
                        System.err.println("SAS provider aborted.");
                        System.exit(1);
                    }
                }
            }
        }
    }

    /**
     * Read the specific message arguments for the given message type.
     *
     * @param msgType The message type.
     */
    protected MessageArgs readMessageArgs(ServerMessageType msgType) throws EOFException {
        if (serverProxy == null) return null;

        switch (msgType) {

            case SERVER_DISCONNECT:
                return null;

            case CREATE_WINDOW:
                serverProxy.getData(createWinMsgArgs);
                return createWinMsgArgs;

            case DESTROY_WINDOW:
                serverProxy.getData(destroyWinMsgArgs);
                return destroyWinMsgArgs;

            case SHOW_WINDOW:
                serverProxy.getData(showWinMsgArgs);
                return showWinMsgArgs;

            case CONFIGURE_WINDOW:
                serverProxy.getData(configureWinMsgArgs);
                return configureWinMsgArgs;

            case POSITION_WINDOW:
                serverProxy.getData(positionWinMsgArgs);
                return positionWinMsgArgs;

            case RESTACK_WINDOW:
                serverProxy.getData(restackWinMsgArgs);
                return restackWinMsgArgs;

            case WINDOW_SET_DECORATED:
                serverProxy.getData(winSetDecoratedMsgArgs);
                return winSetDecoratedMsgArgs;

            case WINDOW_SET_BORDER_WIDTH:
                serverProxy.getData(winSetBorderWidthMsgArgs);
                return winSetBorderWidthMsgArgs;

            case WINDOW_SET_USER_DISPLACEMENT:
                serverProxy.getData(winSetUserDisplMsgArgs);
                return winSetUserDisplMsgArgs;

            case WINDOW_SET_ROTATE_Y:
                serverProxy.getData(winSetRotateYMsgArgs);
                return winSetRotateYMsgArgs;

            case BEEP:
                serverProxy.getData();
                return null;

            case DISPLAY_PIXELS:
                serverProxy.getData(displayPixelsMsgArgs);
                return displayPixelsMsgArgs;

            case COPY_AREA:
                serverProxy.getData(copyAreaMsgArgs);
                return copyAreaMsgArgs;

            case CONTROLLER_STATUS:
                serverProxy.getData(controllerStatusMsgArgs);
                return controllerStatusMsgArgs;

            // TODO: 0.4 protocol: temporarily insert
            case DISPLAY_CURSOR:
                serverProxy.getData(displayCursorMsgArgs);
                return displayCursorMsgArgs;

            // TODO: 0.4 protocol: temporarily insert
            case MOVE_CURSOR:
                serverProxy.getData(moveCursorMsgArgs);
                return moveCursorMsgArgs;

            // TODO: 0.4 protocol: temporarily insert
            case SHOW_CURSOR:
                serverProxy.getData(showCursorMsgArgs);
                return moveCursorMsgArgs;

            default:
                throw new RuntimeException("Unknown server message: " + msgType);
        }
    }

    /**
     * Process the message that has been read for the given message type.
     *
     * @param msgType The message type.
     */
    protected void processMessage(ServerMessageType msgType) throws EOFException {
        WindowXrw win;

        switch (msgType) {

            case SERVER_DISCONNECT:
                serverConnected = false;
                break;

            case CREATE_WINDOW:

                // We can't make windows visible until we are enable.
                synchronized (enableLock) {
                    while (!enable) {
                        try { enableLock.wait(); } catch (InterruptedException ex) {}
                    }
                }

                win = lookupWindow(createWinMsgArgs.wid);
                if (win != null) {
                    AppXrw.logger.warning("CreateWindow: redundant create: wid = " + createWinMsgArgs.wid);
                } else {
                    createWindow(createWinMsgArgs);
                }
                break;

            case DESTROY_WINDOW:
                win = lookupWindow(destroyWinMsgArgs.wid);
                if (win == null) {
                    AppXrw.logger.warning("DestroyWindow: window doesn't exist: wid = " + destroyWinMsgArgs.wid);
                } else {
                    destroyWindow(win);
                }
                break;

            case SHOW_WINDOW:
                win = lookupWindow(showWinMsgArgs.wid);
                if (win == null) {
                    AppXrw.logger.warning("ShowWindow: window doesn't exist: wid = " + showWinMsgArgs.wid);
                } else {
                    /* TODO: 0.4 protocol:
                    WindowXrw transientFor = lookupWindow(showWinMsgArgs.transientFor);
                    win.setVisibleApp(showWinMsgArgs.show, transientFor);
                     */
                    win.setVisibleApp(showWinMsgArgs.show, showWinMsgArgs.isTransient);
                }
                break;

            case CONFIGURE_WINDOW:
                win = lookupWindow(configureWinMsgArgs.wid);
                if (win == null) {
                    AppXrw.logger.warning("ConfigureWindow: window doesn't exist: wid = " + configureWinMsgArgs.wid);
                } else {
                    configureWindow(win, configureWinMsgArgs);
                }
                break;

            case POSITION_WINDOW:
                // If the move was made interactively by this client, ignore it */
                if (positionWinMsgArgs.clientId != clientId) {
                    win = lookupWindow(positionWinMsgArgs.wid);
                    if (win == null) {
                        AppXrw.logger.warning("PositionWindow: window doesn't exist: wid = " + positionWinMsgArgs.wid);
                    } else {
                        win.setScreenPosition/*TODO:Local*/(positionWinMsgArgs.x, positionWinMsgArgs.y);
                    }
                }
                break;

            case RESTACK_WINDOW:
                // If the move was made interactively by this client, ignore it */
                if (restackWinMsgArgs.clientId != clientId) {
                    win = lookupWindow(restackWinMsgArgs.wid);
                    if (win == null) {
                        AppXrw.logger.warning("RestackWindow: window doesn't exist: wid = " + restackWinMsgArgs.wid);
                    } else {
                        WindowXrw sibwin = lookupWindow(restackWinMsgArgs.sibid);
                        if (sibwin == null) {
                            AppXrw.logger.warning("RestackWindow: sibling window doesn't exist: sibid = " +
                                    restackWinMsgArgs.sibid);
                        } else {
                            win.restackAbove/*TODO:winconfig:Local*/(sibwin);
                        }
                    }
                }
                break;

            case WINDOW_SET_DECORATED:
                win = lookupWindow(winSetDecoratedMsgArgs.wid);
                if (win == null) {
                    AppXrw.logger.warning("WindowSetDecorated: window doesn't exist: wid = " + winSetDecoratedMsgArgs.wid);
                } else {
                    win.setDecorated(winSetDecoratedMsgArgs.decorated);
                }
                break;

            case WINDOW_SET_BORDER_WIDTH:
                win = lookupWindow(winSetDecoratedMsgArgs.wid);
                if (win == null) {
                    AppXrw.logger.warning("WindowSetBorderWidth: window doesn't exist: wid = " +
                            winSetBorderWidthMsgArgs.wid);
                } else {
                    win.setBorderWidth(winSetBorderWidthMsgArgs.borderWidth);
                }

                break;

            case WINDOW_SET_USER_DISPLACEMENT:
                // If this was performed interactively by this client, ignore it
                if (winSetUserDisplMsgArgs.clientId != clientId) {
                    win = lookupWindow(winSetUserDisplMsgArgs.wid);
                    if (win == null) {
                        AppXrw.logger.warning("WindowSetUserDispl: window doesn't exist: wid = " +
                                winSetUserDisplMsgArgs.wid);
                    } else {
                        CellTransform transform = new CellTransform(null, winSetUserDisplMsgArgs.userDispl);
                        win.setUserTransformCellLocal(transform);
                    }
                    break;
                }

            case WINDOW_SET_ROTATE_Y:
                /* TODO:someday: not yet supported for secondaries (Part 1)
                // If this was performed interactively by this client, ignore it
                if (winSetRotateYMsgArgs.clientId != clientId) {
                    win = lookupWindow(winSetRotateYMsgArgs.wid);
                    if (win == null) {
                        AppXrw.logger.warning("WindowSetRotateY: window doesn't exist: wid = " + winSetRotateYMsgArgs.wid);
                    } else {
                        win.setRotateY(win, winSetRotateYMsgArgs.roty);
                    }
                }
                */
                break;

            case BEEP:
                Toolkit.getDefaultToolkit().beep();
                break;

            case DISPLAY_PIXELS:
                win = lookupWindow(displayPixelsMsgArgs.wid);
                if (win == null) {
                    AppXrw.logger.warning("DisplayPixels: invalid window ID = " + displayPixelsMsgArgs.wid);
                    return;
                }
                processDisplayPixels(win, displayPixelsMsgArgs);
                break;

            case COPY_AREA:
                win = lookupWindow(copyAreaMsgArgs.wid);
                if (win == null) {
                    AppXrw.logger.warning("CopyArea: window doesn't exist: wid = " + copyAreaMsgArgs.wid);
                } else {
                    synchronized (this) {
                        numCopyAreas++;
                    }

                    win.copyArea(copyAreaMsgArgs.srcX, copyAreaMsgArgs.srcY,
                            copyAreaMsgArgs.width, copyAreaMsgArgs.height,
                            copyAreaMsgArgs.dstX, copyAreaMsgArgs.dstY);
                }
                break;

            case CONTROLLER_STATUS:
                processControllerStatus(controllerStatusMsgArgs);
                break;

            // TODO: 0.4 protocol: temporarily insert
            case DISPLAY_CURSOR:
            case MOVE_CURSOR:
            case SHOW_CURSOR:
                break;

            default:
                throw new RuntimeException("Internal error: no handler for message type : " + msgType);
        }
    }

    /**
     * Handle the ControllerStatus Message.
     *
     * @param msgArgs The arguments which have been read for the ControllerStatus message.
     */
    protected void processControllerStatus(ControllerStatusMsgArgs msgArgs) {
        if (!(controlArb instanceof ControlArbXrw)) {
            return;
        }

        switch (msgArgs.status) {

            case REFUSED:
                // We only care about our attempts that are refused
                if (msgArgs.clientId == clientId) {
                    SwingUtilities.invokeLater(new Runnable() {
                        public void run () {
                            ((ControlArbXrw)controlArb).controlRefused();
                        }
                    });
                }
                break;

            case GAINED:
                // We only care about our attempts that succeed
                if (msgArgs.clientId == clientId) {
                    SwingUtilities.invokeLater(new Runnable() {
                        public void run () {
                            ((ControlArbXrw)controlArb).controlGained();
                        }
                    });
                }
                break;

            case LOST:
                if (msgArgs.clientId == clientId) {
                    SwingUtilities.invokeLater(new Runnable() {
                        public void run () {
                            ((ControlArbXrw)controlArb).controlLost();
                        }
                    });
                } else {
                    // Update control highlighting for other clients besides control loser
                    SwingUtilities.invokeLater(new Runnable() {
                        public void run () {
                            ((ControlArbXrw)controlArb).setController(null);
                        }
                    });
                }
                break;
        }
    }

    /**
     * Given a window ID return the associated window.
     *
     * @param wid The X11 window ID.
     */
    protected WindowXrw lookupWindow(int wid) {
        if (app == null) return null;
        WindowXrw win = ((AppXrw)app).getWindowForWid(wid);
        if (win == null) return null;
        if (win.isZombie()) return null;
        return win;
    }

    /**
     * Associate this window ID with this window.
     *
     * @param wid The X11 window ID.
     * @param window The window to associate with the wid.
     */
    protected void addWindow(int wid, WindowXrw window) {
        ((AppXrw)app).addWindow(wid, window);
    }

    /**
     * Remove the association of fhis window ID with its window.
     *
     * @param window The window to disassociate from the wid.
     */
    protected void removeWindow(WindowXrw win) {
        ((AppXrw)app).removeWindow(win.getWid());
    }

    /**
     * Create a window.
     *
     * @param msg The message arguments which have been read for the CreateWindow message.
     */
    protected WindowXrw createWindow(CreateWindowMsgArgs msg) {
        try {
            WindowXrw win = app.createWindow(msg.x, msg.y, msg.wAndBorder, msg.hAndBorder, msg.borderWidth,
                    msg.decorated, msg.wid);
            addWindow(msg.wid, win);
            return win;
        } catch (IllegalStateException ex) {
            AppXrw.logger.warning("CreateWindow: Cannot create window " + msg.wid);
            return null;
        }
    }

    /**
     * Destroy the given window.
     *
     * @param win The window to destroy.
     */
    private void destroyWindow(WindowXrw win) {
        removeWindow(win);
        win.cleanup();
    }

    /**
     * Configure (that is, resize, move or restack) a window.
     *
     * @param win The window to configure.
     * @param msg The message arguments which have been read for the ConfigureWindow message.
     */
    private void configureWindow(WindowXrw win, ConfigureWindowMsgArgs msg) {

        // Is this a configure from ourselves or some other client?
        if (msg.clientId == clientId) {

            // Self configure: see if this is a size change
            if (msg.wAndBorder != win.getWidth() ||
                msg.hAndBorder != win.getHeight()) {

                // Accept this self resize. This is because the user resize operation
                // is not completely finished until setDimensions is called with
                // the new width and height
            } else {
                // Not a resize. It's a move-or-restack-only from ourselves. Ignore it.
            }
        }

        if (msg.wAndBorder > WINDOW_MAX_WIDTH) {
            msg.wAndBorder = WINDOW_MAX_WIDTH;
            AppXrw.logger.warning("createWindow: width " + msg.wAndBorder + " was truncated to maximum width");
        }
        if (msg.hAndBorder > WINDOW_MAX_HEIGHT) {
            msg.hAndBorder = WINDOW_MAX_HEIGHT;
            AppXrw.logger.warning("createWindow: height " + msg.hAndBorder + " was truncated to maximum height");
        }

        WindowXrw sibWin = lookupWindow(msg.sibid);
        win.setScreenPosition(msg.x, msg.y);
        win.setSize(msg.wAndBorder, msg.hAndBorder);
        win.restackAbove(sibWin);
    }

    /**
     * Process the display pixels message.
     *
     * @param win The window in which to display pixels.
     * @param displayPixelsMsgArgs The message arguments which have been read for the message.
     */
    private void processDisplayPixels(WindowXrw win, DisplayPixelsMsgArgs displayPixelsMsgArgs)
        throws EOFException
    {
        switch (displayPixelsMsgArgs.encoding) {

            case UNCODED:
                //displayRect(win, displayPixelsMsgArgs);
                throw new RuntimeException("UNCODED pixels from the xremwin server is no longersupported.");

            case RLE24:
                displayRectRle24(win, displayPixelsMsgArgs);
                break;

            default:
                throw new RuntimeException("Unknown pixel encoding " + displayPixelsMsgArgs.encoding);
        }
    }
    /**
     * Uncoded DisplayPixels (OBSOLETE)
     * If win == null we read the pixels but discard them.
    private void displayRect (WindowXrw win, DisplayPixelsMsgArgs dpMsgArgs) {

    //AppXrw.logger.finer("displayRect");
    //AppXrw.logger.finer("x = " + dpMsgArgs.x);
    //AppXrw.logger.finer("y = " + dpMsgArgs.y);
    //AppXrw.logger.finer("w = " + dpMsgArgs.w);
    //AppXrw.logger.finer("h = " + dpMsgArgs.h);

    serverProxy.setScanLineWidth(dpMsgArgs.w);

    int[] winPixels = new int[dpMsgArgs.w * dpMsgArgs.h];

    int dstIdx = 0;
    int dstNextLineIdx = 0;

    for (int y = 0; y < dpMsgArgs.h; y++) {
    dstNextLineIdx += dpMsgArgs.w;

    // Reads into scanLineBuf
    byte[] scanLineBytes = serverProxy.readScanLine();

    int srcIdx = 0;

    for (int i = 0;
    i < dpMsgArgs.w && srcIdx < scanLineBytes.length - 2;
    i++, srcIdx += 4) {

    //AppXrw.logger.finer("dstIdx = " + dstIdx);
    //AppXrw.logger.finer("srcIdx = " + srcIdx);
    //AppXrw.logger.finer("winPixels.length = " + winPixels.length);
    //AppXrw.logger.finer("scanLineBytes.length = " + scanLineBytes.length);

    // Note: source format is BGRX and dest format is XBGR
    winPixels[dstIdx++] =
    ((scanLineBytes[srcIdx + 2] & 0xff) << 16) |
    ((scanLineBytes[srcIdx + 1] & 0xff) <<  8) |
    (scanLineBytes[srcIdx + 0] & 0xff);
    }

    dstIdx = dstNextLineIdx;
    }

    //Debug: print all scanlines collected
    //printRLBegin();
    //for (int y = 0; y < dpMsgArgs.h; y++) {
    //    int idx = y * dpMsgArgs.w ;
    //    for (int i = 0; i < dpMsgArgs.w; i++) {
    //  printRL(winPixels[idx + i]);
    //    }
    //}
    //printRLEnd();

    if (win != null) {
    win.displayPixels(dpMsgArgs.x, dpMsgArgs.y, dpMsgArgs.w, dpMsgArgs.h, winPixels);
    }
    }
     */
    private int maxVerboseRuns = 120;
    private int numRunsReceived = 0;
    private byte[] chunkBuf = null;
    private int chunkBufSize = 0;

    /**
     * Decode a run-length encoded DisplayPixels message. If win == null we read the pixels but discard them.
     *
     * @param win The window in which to display pixels.
     * @param dpMsgArgs The message arguments which have been read for the message.
     */
    private void displayRectRle24(WindowXrw win, DisplayPixelsMsgArgs dpMsgArgs) throws EOFException {

        synchronized (this) {
            displayPixelsNumBytes += dpMsgArgs.w * dpMsgArgs.h * 4;
        }

        int h = dpMsgArgs.h;
        int numChunks = serverProxy.readRleInt();

        /*
        AppXrw.logger.finer("displayRectRle24");
        AppXrw.logger.finer("x = " + dpMsgArgs.x);
        AppXrw.logger.finer("y = " + dpMsgArgs.y);
        AppXrw.logger.finer("w = " + dpMsgArgs.w);
        AppXrw.logger.finer("h = " + h);
        AppXrw.logger.finer("numChunks = " + numChunks);
         */

        int[] winPixels = new int[dpMsgArgs.w * dpMsgArgs.h];

        int dstIdx = 0;
        int x = 0;
        int chunkCount = 0;

        while (numChunks-- > 0) {

            int chunkHeight = serverProxy.readRleInt();
            int chunkBytes = serverProxy.readRleInt();
            if (chunkBytes > chunkBufSize) {
                chunkBuf = new byte[chunkBytes];
                chunkBufSize = chunkBytes;
            }
            //AppXrw.logger.finer("chunkCount = " + chunkCount);
            //AppXrw.logger.finer("chunkBytes = " + chunkBytes);

            int dstNextLineIdx = dstIdx + chunkHeight * dpMsgArgs.w;

            // Read first chunk of data from server
            serverProxy.readRleChunk(chunkBuf, chunkBytes);

            int chunkOffset = 0;

            while (chunkBytes > 0) {
                int count = chunkBuf[chunkOffset + 3] & 0xFF;
                int pixel = ((chunkBuf[chunkOffset + 2] + 256) & 0xFF) << 16 |
                        ((chunkBuf[chunkOffset + 1] + 256) & 0xFF) << 8 |
                        ((chunkBuf[chunkOffset + 0] + 256) & 0xFF);
                //System.err.println("pixel = " + Integer.toHexString(pixel));

                // Make the pixels opaque so that we can copy them with Graphics.drawImage
                pixel |= 0xff000000;

                /*
                if (numRunsReceived++ < maxVerboseRuns) {
                AppXrw.logger.finer("numRunsReceived = " + numRunsReceived);
                AppXrw.logger.finer("count = " + count);
                AppXrw.logger.finer("pixel = " + Integer.toHexString(pixel));
                }
                 */

                for (int i = 0; i < count; i++, x++) {
                    if (x >= dpMsgArgs.w) {
                        x = 0;
                        dstIdx += dpMsgArgs.w;
                    }
                    try {
                        // TODO: this works around an index-out-of-bounds problem. Why?
                        if ((dstIdx + x) < winPixels.length) {
                            winPixels[dstIdx + x] = pixel;
                        }
                    } catch (ArrayIndexOutOfBoundsException ex) {
                        AppXrw.logger.finer("*********** Array out of bounds!!!!!!!!!!!!!!!!");
                        AppXrw.logger.finer("winPixels.length = " + winPixels.length);
                        AppXrw.logger.finer("dstIdx + x = " + (dstIdx + x));
                        AppXrw.logger.finer("dstIdx = " + dstIdx);
                        AppXrw.logger.finer("x = " + x);
                        AppXrw.logger.finer("chunkCount = " + chunkCount);
                        AppXrw.logger.finer("chunkBytes = " + chunkBytes);
                    }
                }

                chunkOffset += 4;
                chunkBytes -= 4;
            }

            dstIdx = dstNextLineIdx;
            x = 0;

            chunkCount++;
        }

        // Now transfer the decoded pixels into the texture
        if (win != null) {
            win.displayPixels(dpMsgArgs.x, dpMsgArgs.y, dpMsgArgs.w, h, winPixels);
        }
    }

    /**
     * Sends updates to the user displacement to the server.
     *
     * @param win The window being displaced.
     * @param userDispl The new displacement vector.
     */
    public void windowSetUserDisplacement(WindowXrw win, Vector3f userDispl) {
        int wid = ((WindowXrw) win).getWid();
        AppXrw.logger.finer("To server: SetUserDispl: wid = " + wid + ", userDispl = " + userDispl);

        try {
            serverProxy.windowSetUserDisplacement(clientId, wid, userDispl);
        } catch (IOException ex) {
            AppXrw.logger.warning("Client cannot send user displacement for window " + wid);
        }
    }

    /**
     * Sends updates to the window size to the server.
     *
     * @param win The window being displaced.
     * @param w The new width of the window.
     * @param h The new height of the window.
     */
    public void windowSetSize(WindowXrw win, int w, int h) {
        int wid = ((WindowXrw) win).getWid();
        AppXrw.logger.finer("To server: SetSize: wid = " + wid + ", wh = " + w + ", " + h);

        try {
            serverProxy.windowSetSize(clientId, wid, w, h);
        } catch (IOException ex) {
            AppXrw.logger.warning("Client cannot send size for window " + wid);
        }
    }

    /**
     * Sends updates to the window's Y rotation to the server.
     *
     * @param win The window being rotated.
     * @param angle The new Y rotation angle of the window.
     */
    public void windowSetRotateY(WindowXrw win, float angle) {
        /* TODO:someday. not yet supported for secondaries  (Part 2)
        int wid = ((WindowXrw) win).getWid();
        AppXrw.logger.finer("To server: SetRotateY: wid = " + wid + ", angle = " + angle);

        try {
            serverProxy.windowSetRotateY(clientId, wid, angle);
        } catch (IOException ex) {
            AppXrw.logger.warning("Client cannot send rotation Y for window " + wid);
        }
        */
    }

    /**
     * Sends a message to the server telling it that the window has been moved to the front
     * of all other windows on the stack.
     *
     * @param win The window whose stack position has changed.
     */
    public void windowToFront(WindowXrw win) {
        int wid = win.getWid();
        AppXrw.logger.finer("To server: ToFront: wid = " + wid);

        try {
            serverProxy.windowToFront(clientId, wid);
        } catch (IOException ex) {
            AppXrw.logger.warning("Client cannot send toFront for window " + wid);
        }
    }

    /**
     * Called when the user closes the given window.
     *
     * @param win The window to close.
     */
    public abstract void windowCloseUser(WindowXrw win);

    /**
     * Returns whether the client is connected to the server.
     */
    public boolean isConnected () {
        return serverConnected;
    }

    /*
     ** For Debug: Print pixel run lengths
     */
    private boolean printRLLastValueValid;
    private int printRLLastValue;
    private int printRLLastValueCount;

    private void printScanLine(byte[] scanLineBuf, int width) {
        printRLBegin();
        for (int i = 0; i < width; i++) {
            int m = (scanLineBuf[i * 4 + 2] & 0xFF) << 16 |
                    (scanLineBuf[i * 4 + 1] & 0xFF) << 8 |
                    (scanLineBuf[i * 4] & 0xFF);
            printRL(m);
        }
        printRLEnd();
    }

    private void printRLRun() {
        if (printRLLastValueCount > 0) {
            System.err.print(printRLLastValue);
            if (printRLLastValueCount > 1) {
                System.err.println(" x " + printRLLastValueCount);
            } else {
                System.err.println();
            }
        }
    }

    private void printRLBegin() {
        printRLLastValueValid = false;
        printRLLastValueCount = 0;
    }

    private void printRL(int value) {
        if (printRLLastValueValid && value == printRLLastValue) {
            printRLLastValueCount++;
        } else {
            printRLRun();

            printRLLastValueCount = 1;
            printRLLastValue = value;
            printRLLastValueValid = true;
        }
    }

    private void printRLEnd() {
        printRLRun();
    }

    private class Statistics extends StatisticsSet {

        // The number of requests of any type received
        private long numRequests;

        // The number of Display Pixels message bytes received
        private long displayPixelsNumBytes;

        // The number of Copy Area messages received
        private long numCopyAreas;

        protected Statistics() {
            super("Xremwin");
        }

        @Override
        protected boolean hasTriggered() {
            // Don't print stats for silent windows
            return numRequests != 0;
        }

        protected void probe() {
            synchronized (ClientXrw.this) {
                numRequests += ClientXrw.this.numRequests;
                displayPixelsNumBytes = ClientXrw.this.displayPixelsNumBytes;
                numCopyAreas = ClientXrw.this.numCopyAreas;
            }
        }

        protected void reset() {
            synchronized (ClientXrw.this) {
                ClientXrw.this.numRequests = 0;
                ClientXrw.this.displayPixelsNumBytes = 0;
                ClientXrw.this.numCopyAreas = 0;
            }
        }

        protected void accumulate(StatisticsSet cumulativeStats) {
            Statistics stats = (Statistics) cumulativeStats;
            stats.numRequests += numRequests;
            stats.displayPixelsNumBytes += displayPixelsNumBytes;
            stats.numCopyAreas += numCopyAreas;
        }

        protected void max(StatisticsSet maxStats) {
            Statistics stats = (Statistics) maxStats;
            stats.numRequests += max(stats.numRequests, numRequests);
            stats.displayPixelsNumBytes = max(stats.displayPixelsNumBytes, displayPixelsNumBytes);
            stats.numCopyAreas = max(stats.numCopyAreas, numCopyAreas);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected void appendStats(StringBuffer sb) {
            sb.append("numRequests = " + numRequests + "\n");
            sb.append("displayPixelsNumBytes = " + displayPixelsNumBytes + "\n");
            sb.append("numCopyAreas = " + numCopyAreas + "\n");
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected void appendStatsAndRates(StringBuffer sb, double timeSecs) {
            appendStats(sb);

            // Calculate and print rates
            double numRequestsPerSec = numRequests / timeSecs;
            sb.append("numRequestsPerSec = " + numRequestsPerSec + "\n");
            double displayPixelsNumBytesPerSec = displayPixelsNumBytes / timeSecs;
            sb.append("displayPixelsNumBytes = " + displayPixelsNumBytesPerSec + "\n");
            double numCopyAreasPerSec = numCopyAreas / timeSecs;
            sb.append("numCopyAreas = " + numCopyAreasPerSec + "\n");
        }
    }
}
TOP

Related Classes of org.jdesktop.wonderland.modules.xremwin.client.ClientXrw$Statistics

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.