/*****************************************************************************
* Copyright (c) 2006, 2007 g-Eclipse Consortium
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Initial development of the original code was made for the
* g-Eclipse project founded by European Union
* project number: FP6-IST-034327 http://www.geclipse.eu/
*
* Contributors:
* Thomas Koeckerbauer GUP, JKU - initial API and implementation
*****************************************************************************/
package eu.geclipse.gvid.internal;
import java.io.IOException;
import org.eclipse.core.runtime.IStatus;
import eu.geclipse.gvid.Activator;
import eu.geclipse.gvid.IEvents;
class ActiveEvent {
/** 0 if window isn't active anymore, other value if window gained activeness */
byte gain;
}
class KeyboardEvent {
/** Keycode. */
short key;
/** Bitmask containing modifier information. */
short modifier;
}
class MouseMotionEvent {
/** Bitmask containing the state of the mouse buttons. */
byte state;
/** New x position of cursor in window. */
short x;
/** New y position of cursor in window. */
short y;
/** Relative mouse movement in x direction. */
short xrel;
/** Relative mouse movement in y direction. */
short yrel;
}
class MouseButtonEvent {
/** Mouse button number. */
byte button;
/** X position of the mouse cursor. */
short x;
/** Y position of the mouse cursor. */
short y;
}
class ResizeEvent {
/** New width of window. */
short w;
/** New height of window. */
short h;
}
class FrameFinishedEvent {
/** Number of frame. */
int frame_num;
/** Encryption time in usec */
int enc_time;
/** Compression time in usec */
int comp_time;
}
class Event {
/** Event type constant (from EventTypes enum). */
byte type;
/** Active event (focus gained/focus lost) */
ActiveEvent active = new ActiveEvent();
/** Keyboard event (key pressed/released). */
KeyboardEvent key = new KeyboardEvent();
/** Mouse motion event. */
MouseMotionEvent motion = new MouseMotionEvent();
/** Mouse button event (button pressed/released). */
MouseButtonEvent button = new MouseButtonEvent();
/** Window resize event. */
ResizeEvent resize = new ResizeEvent();
FrameFinishedEvent frame_finished = new FrameFinishedEvent();
}
/**
* GVid event handling class.
*/
public class Events implements IEvents {
/** No event occurred. */
public final static int NO_EVENT = 0;
/** Window activation/deactivation. */
public final static int ACTIVE_EVENT = 1;
/** Key on keyboard pressed. */
public final static int KEY_PRESS_EVENT = 2;
/** Key on keyboard released.*/
public final static int KEY_RELEASE_EVENT = 3;
/** Mouse moved. */
public final static int MOUSE_MOVE_EVENT = 4;
/** Mouse button pressed. */
public final static int MOUSE_PRESS_EVENT = 5;
/** Mouse button released. */
public final static int MOUSE_RELEASE_EVENT = 6;
/** Application quit. */
public final static int QUIT_EVENT = 7;
/** Window resize. */
public final static int VIDEO_RESIZE_EVENT = 8;
/** Frame decoded. */
public final static int FRAME_FINISHED = 9;
/** Trigger redraw. */
public final static int REDRAW = 10;
private Connection connection;
private boolean haveOldMotion;
private Event oldMotion, redrawEvent, quitEvent;
private int lastDecoderFrameNr;
/**
* Creates a Event handling class for the specified Connection.
* @param connection connection on which the events should be sent
*/
public Events( final Connection connection ) {
this.connection = connection;
this.haveOldMotion = false;
this.oldMotion = new Event();
this.oldMotion.motion.x = 0;
this.oldMotion.motion.y = 0;
this.oldMotion.motion.xrel = 0;
this.oldMotion.motion.yrel = 0;
this.oldMotion.type = MOUSE_MOVE_EVENT;
this.redrawEvent = new Event();
this.redrawEvent.type = REDRAW;
this.quitEvent = new Event();
this.quitEvent.type = QUIT_EVENT;
this.lastDecoderFrameNr = -1;
}
/**
* Reads a Event from the Connection. If no data is available from the
* connection null is returned.
* @return a new Event object.
*/
Event recvEvent() throws IOException {
Event event = null;
if( this.connection.getNumBytesInBuffer() != 0 ) {
event = new Event();
event.type = this.connection.readByte();
switch( event.type ) {
case VIDEO_RESIZE_EVENT:
event.resize.w = this.connection.readUint16();
event.resize.h = this.connection.readUint16();
break;
case ACTIVE_EVENT:
event.active.gain = this.connection.readByte();
break;
case KEY_PRESS_EVENT:
case KEY_RELEASE_EVENT:
event.key.key = this.connection.readUint16();
event.key.modifier = this.connection.readUint16();
break;
case MOUSE_MOVE_EVENT:
event.motion.state = this.connection.readByte();
event.motion.x = this.connection.readUint16();
event.motion.y = this.connection.readUint16();
event.motion.xrel = this.connection.readUint16();
event.motion.yrel = this.connection.readUint16();
break;
case MOUSE_PRESS_EVENT:
case MOUSE_RELEASE_EVENT:
event.button.button = this.connection.readByte();
event.button.x = this.connection.readUint16();
event.button.y = this.connection.readUint16();
break;
case FRAME_FINISHED:
event.frame_finished.frame_num = this.connection.readUint32();
event.frame_finished.enc_time = this.connection.readUint32();
event.frame_finished.comp_time = this.connection.readUint32();
this.lastDecoderFrameNr = event.frame_finished.frame_num;
// if (encoder != NULL)
// encoder.setDecoderFrameNumber(event.frame_finished.frame_num,
// event.frame_finished.enc_time, event.frame_finished.comp_time);
break;
case QUIT_EVENT:
case REDRAW:
case NO_EVENT:
break;
default:
Activator.logMessage( IStatus.ERROR,
Messages.getString( "Events.invalidEventType" ) ); //$NON-NLS-1$
break;
}
}
return event;
}
int getLastDecoderFrameNr() {
return this.lastDecoderFrameNr;
}
/**
* Sends the specified Event object over the Connection.
* @param event Event object to send.
*/
void sendEvent( final Event event ) throws IOException {
synchronized( this.connection ) {
if( !( event == this.oldMotion ) ) { // skip if called from flush
if( this.haveOldMotion
&& ( event.type != MOUSE_MOVE_EVENT
|| event.motion.state != this.oldMotion.motion.state ) ) {
flush();
}
if( event.type == MOUSE_MOVE_EVENT
&& ( !this.haveOldMotion
|| this.oldMotion.motion.state == event.motion.state ) ) {
this.oldMotion.motion.state = event.motion.state;
this.oldMotion.motion.x = event.motion.x;
this.oldMotion.motion.y = event.motion.y;
this.oldMotion.motion.xrel += event.motion.xrel;
this.oldMotion.motion.yrel += event.motion.yrel;
this.haveOldMotion = true;
return;
}
}
byte[] sendBuffer = new byte[ 30/* sizeof(Event) */]; // TODO use real event size
int pos = 0;
sendBuffer[ pos++ ] = event.type;
switch( event.type ) {
case VIDEO_RESIZE_EVENT:
pos = putUint16( pos, sendBuffer, event.resize.w );
pos = putUint16( pos, sendBuffer, event.resize.h );
break;
case ACTIVE_EVENT:
sendBuffer[ pos++ ] = event.active.gain;
break;
case KEY_PRESS_EVENT:
case KEY_RELEASE_EVENT:
pos = putUint16( pos, sendBuffer, event.key.key );
pos = putUint16( pos, sendBuffer, event.key.modifier );
break;
case MOUSE_MOVE_EVENT:
sendBuffer[ pos++ ] = event.motion.state;
pos = putUint16( pos, sendBuffer, event.motion.x );
pos = putUint16( pos, sendBuffer, event.motion.y );
pos = putUint16( pos, sendBuffer, event.motion.xrel );
pos = putUint16( pos, sendBuffer, event.motion.yrel );
break;
case MOUSE_PRESS_EVENT:
case MOUSE_RELEASE_EVENT:
sendBuffer[ pos++ ] = event.button.button;
pos = putUint16( pos, sendBuffer, event.button.x );
pos = putUint16( pos, sendBuffer, event.button.y );
break;
case FRAME_FINISHED:
pos = putUint32( pos, sendBuffer, event.frame_finished.frame_num );
pos = putUint32( pos, sendBuffer, event.frame_finished.enc_time );
pos = putUint32( pos, sendBuffer, event.frame_finished.comp_time );
break;
case QUIT_EVENT:
case REDRAW:
case NO_EVENT:
break;
default:
pos = 0;
Activator.logMessage( IStatus.ERROR,
Messages.getString( "Events.invalidEventType" ) ); //$NON-NLS-1$
break;
}
if( pos != 0 || event.type == REDRAW ) {
this.connection.writeData( sendBuffer, pos );
}
}
}
/**
* Sends an Event to the server that the image should be redrawn an
* newly encoded.
*/
void sendRedrawEvent() throws IOException {
sendEvent( this.redrawEvent );
}
void sendQuitEvent() throws IOException {
sendEvent( this.quitEvent );
}
/* (non-Javadoc)
* @see eu.geclipse.gvid.internal.IEvents#sendFrameFinished(int, int, int)
*/
public void sendFrameFinished( final int frame_num, final int enc_time,
final int comp_time ) throws IOException {
Event event = new Event();
event.type = FRAME_FINISHED;
event.frame_finished.frame_num = frame_num;
event.frame_finished.enc_time = enc_time;
event.frame_finished.comp_time = comp_time;
sendEvent( event );
}
/**
* Sends all events which are buffered. Mouse motion events can be
* buffered because successive mouse motion events get merged together
* to a single one.
*/
void flush() throws IOException {
synchronized( this.connection ) {
if( this.haveOldMotion ) {
sendEvent( this.oldMotion );
this.connection.flush();
this.oldMotion.motion.x = 0;
this.oldMotion.motion.y = 0;
this.oldMotion.motion.xrel = 0;
this.oldMotion.motion.yrel = 0;
this.haveOldMotion = false;
}
}
}
int putUint16( final int pos, final byte[] buffer, final short val ) {
int newPos = pos;
buffer[ newPos++ ] = ( byte )( val >> 8 );
buffer[ newPos++ ] = ( byte )( val & 0x00FF );
return newPos;
}
int putUint32( final int pos, final byte[] buffer, final int val ) {
int newPos = pos;
buffer[ newPos++ ] = ( byte )( val >> 24 );
buffer[ newPos++ ] = ( byte )( ( val & 0x00FF0000 ) >> 16 );
buffer[ newPos++ ] = ( byte )( ( val & 0x0000FF00 ) >> 8 );
buffer[ newPos++ ] = ( byte )( val & 0x000000FF );
return newPos;
}
}