このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
今日は SetEncodings メッセージだ。
これは種類 2 番のクライアントメッセージで、
クライアントが対応している画面データの符号化方式を、
サーバーに通知するために送られる。
RFB では、画面のデータを送る際の通信量を削減するために、
いくつもの符号化方式(圧縮形式等)を定めている。
符号化方式は多種多様であり、その内容は複雑である。
それら全てに対応するにはそれ相応のコード量が必要だ。
そのため、クライアントはその全てに対応する必要はなく、
自身が扱える符号化形式のみをサーバに通知することで、
サーバが送ってくる画面データの符号化を、
一部の方式に限定することができると言うわけだ。
このメッセージは可変長で、以下のような構造を持つ。
U8 messageType; // 常に 2
U8 reserved; // 1 バイトの詰め物
U16 count; // 対応している符号化方式の数
S32 types[]; // 対応している符号化方式の配列
符号化方式には一意のコードが割り当てられており、
types 配列には、クライアントの対応する符号化方式が入る。
types は可変なので、count にはその要素数を格納する。
では、最初に符号化方式を表す定数を定義しよう。
========== Encoding.java ==========
package jp.loafer.rfb;
/**
* 符号化方式の定数。
* @author kes
*/
public final class Encoding {
/**
* 符号化なし。
*/
public static final int RAW = 0;
/**
* Copy rectangle 符号化。
*/
public static final int COPY_RECT = 1;
/**
* Rise-and-run-length 符号化。
*/
public static final int RRE = 2;
/**
* Hex-tile 符号化。
*/
public static final int HEXTILE = 5;
/**
* Zlib Run-length 符号化。
*/
public static final int ZRLE = 16;
/**
* カーソル擬似符号化。
*/
public static final int PSEUDO_CURSOR = -239;
/**
* サイズ変更擬似符号化。
*/
public static final int PSEUDO_DESKTOP_SIZE = -223;
private Encoding() {
// インスタンスは不要
}
}
========== end of Encoding.java ==========
見慣れぬ名前がずらずらと並んでいるが、
現時点では定数値が分かればそれでいいので気にしない。
次に、SetEncodings メッセージのクラスを作ろう。
=========== SetEncodingsMessage.java ===========
package jp.loafer.rfb.message.client;
import java.io.IOException;
import jp.loafer.rfb.Encoding;
import jp.loafer.rfb.RFBContext;
import jp.loafer.rfb.io.RFBInputStream;
import jp.loafer.rfb.io.RFBOutputStream;
/**
* SetEncodings クライアントメッセージ。
* @author kes
*/
public class SetEncodingsMessage extends BaseClientMessage {
/**
* 永続化用既定コンストラクタ。
*/
public SetEncodingsMessage() {
//
}
/**
* SetEncodings メッセージを作成。
* @param encodingTypes 対応する符号化方式の配列。
*/
public SetEncodingsMessage(int[] encodingTypes) {
if (encodingTypes == null) throw new NullPointerException();
this.encodingTypes = encodingTypes;
}
/**
* 符号化方式の配列を取得。
* @return {@link Encoding}.XXX 符号化方式定数の配列。
*/
public int[] getEncodingTypes() {
return encodingTypes.clone();
}
/**
* 符号化方式の配列の要素を取得。
* @param index
* @return {@link Encoding}.XXX 符号化方式定数。
*/
public int getEncodingTypes(int index) {
return encodingTypes[index];
}
/**
* @see ClientMessage#getType()
*/
public int getType() {
return ClientMessage.SET_ENCODINGS;
}
/**
* @see ClientMessage#read(RFBContext, RFBInputStream)
*/
@Override
public void read(RFBContext context, RFBInputStream in) throws IOException {
super.read(context, in);
in.readU8();
int length = in.readU16();
encodingTypes = new int[length];
for (int i = 0; i < length; ++i) {
encodingTypes[i] = in.readS32();
}
}
/**
* @see ClientMessage#write(RFBContext, RFBOutputStream)
*/
@Override
public void write(RFBContext context, RFBOutputStream out) throws IOException {
super.write(context, out);
out.writeU8(0);
out.writeU16(encodingTypes.length);
for (int i = 0; i < encodingTypes.length; ++i) {
out.writeS32(encodingTypes[i]);
}
}
private int[] encodingTypes;
}
========== end of SetEncodingsMessage.java ==========
SetEncodings クラスは、
配列をフィールドとして持つ設計になるが、
それを getter で公開する際には注意が必要だ。
Java の配列はオブジェクトであり、
その要素は変更が可能である。
もし getter が配列型のフィールドをそのまま返すと、
呼び出し側で配列を操作することで、
フィールドの配列を直接編集できてしまうことになる。
SetEncodings クラスも不変とするためには、
getter では配列をコピーして返す必要がある。
また、配列型のフィールドを公開する場合は、
インデックス付きの getter を用意する手もある。
この場合、配列の要素のみを取得できるので、
配列全体をコピーする必要がなくなるのだ。