Initial import

This commit is contained in:
Tom Marshall 2011-12-23 15:28:12 -08:00
commit b20498caa4
61 changed files with 5700 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
bin/**
gen/**

18
AndroidManifest.xml Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="tdm.xserver"
android:versionCode="1"
android:versionName="1.0">
<!-- We need SDK v10 (v2.3.x) for android.view.InputDevice -->
<uses-sdk android:minSdkVersion="10" />
<uses-permission android:name="android.permission.INTERNET" />
<application android:label="@string/app_name" android:icon="@drawable/icon">
<activity android:name="XServer"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

17
ant.properties Normal file
View File

@ -0,0 +1,17 @@
# This file is used to override default values used by the Ant build system.
#
# This file must be checked in Version Control Systems, as it is
# integral to the build system of your project.
# This file is only used by the Ant script.
# You can use this to override default values such as
# 'source.dir' for the location of your java source folder and
# 'out.dir' for the location of your output folder.
# You can also use it define how the release builds are signed by declaring
# the following properties:
# 'key.store' for the location of your keystore and
# 'key.alias' for the name of the key to use.
# The password will be asked during the build when you use the 'release' target.

85
build.xml Normal file
View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="XServer" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
Version Control Systems. -->
<property file="local.properties" />
<!-- The ant.properties file can be created by you. It is only edited by the
'android' tool to add properties to it.
This is the place to change some Ant specific build properties.
Here are some properties you may want to change/update:
source.dir
The name of the source directory. Default is 'src'.
out.dir
The name of the output directory. Default is 'bin'.
For other overridable properties, look at the beginning of the rules
files in the SDK, at tools/ant/build.xml
Properties related to the SDK location or the project target should
be updated using the 'android' tool with the 'update' action.
This file is an integral part of the build system for your
application and should be checked into Version Control Systems.
-->
<property file="ant.properties" />
<!-- The project.properties file is created and updated by the 'android'
tool, as well as ADT.
This contains project specific properties such as project target, and library
dependencies. Lower level build properties are stored in ant.properties
(or in .classpath for Eclipse projects).
This file is an integral part of the build system for your
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
<!-- quick check on sdk.dir -->
<fail
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var"
unless="sdk.dir"
/>
<!-- extension targets. Uncomment the ones where you want to do custom work
in between standard targets -->
<!--
<target name="-pre-build">
</target>
<target name="-pre-compile">
</target>
/* This is typically used for code obfuscation.
Compiled code location: ${out.classes.absolute.dir}
If this is not done in place, override ${out.dex.input.absolute.dir} */
<target name="-post-compile">
</target>
-->
<!-- Import the actual build file.
To customize existing targets, there are two options:
- Customize only one target:
- copy/paste the target into this file, *before* the
<import> task.
- customize it to your needs.
- Customize the whole content of build.xml
- copy/paste the content of the rules files (minus the top node)
into this file, replacing the <import> task.
- customize to your needs.
***********************
****** IMPORTANT ******
***********************
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
in order to avoid having your file be overridden by tools such as "android update project"
-->
<!-- version-tag: 1 -->
<import file="${sdk.dir}/tools/ant/build.xml" />
</project>

7
main.xml Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
</AbsoluteLayout>

40
proguard.cfg Normal file
View File

@ -0,0 +1,40 @@
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
-keepclasseswithmembernames class * {
native <methods>;
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}

11
project.properties Normal file
View File

@ -0,0 +1,11 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "ant.properties", and override values to adapt the script to your
# project structure.
# Project target.
target=android-10

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
res/drawable-hdpi/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
res/drawable-ldpi/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
res/drawable-mdpi/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

20
res/menu/options_menu.xml Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_save"
android:icon="@drawable/ic_menu_save"
android:alphabeticShortcut='s'
android:title="@string/menu_save" />
<group android:id="@+id/menu_group_edit">
<item android:id="@+id/menu_revert"
android:icon="@drawable/ic_menu_revert"
android:title="@string/menu_revert" />
<item android:id="@+id/menu_delete"
android:icon="@drawable/ic_menu_delete"
android:title="@string/menu_delete" />
</group>
<group android:id="@+id/menu_group_insert">
<item android:id="@+id/menu_discard"
android:icon="@drawable/ic_menu_discard"
android:title="@string/menu_discard" />
</group>
</menu>

9
res/values/strings.xml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">XServer</string>
<string name="menu_save">Save</string>
<string name="menu_delete">Delete</string>
<string name="menu_revert">Revert changes</string>
<string name="menu_discard">Discard</string>
</resources>

View File

@ -0,0 +1,152 @@
package tdm.xserver;
import android.util.Log;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
class ByteQueue
{
ByteBuffer mData;
ByteQueue() {
mData = ByteBuffer.allocate(1);
}
ByteQueue(int len) {
mData = ByteBuffer.allocate(len);
mData.limit(0);
}
ByteQueue(byte[] src, int pos, int len) {
mData = ByteBuffer.allocate(len);
mData.put(src, pos, len);
mData.position(0);
}
ByteQueue(byte[] src) {
mData = ByteBuffer.allocate(src.length);
mData.put(src);
mData.position(0);
}
void dump() {
Log.e(XServer.TAG, "ByteQueue.dump: limit=" + mData.limit() +
", capacity=" + mData.capacity() + ", pos=" + mData.position());
StringBuffer hexbuf = new StringBuffer();
int x, y;
for (y = 0; y < mData.limit(); y += 16) {
hexbuf.append(String.format("%04x: ", y));
for (x = 0; x < 15 && y+x < mData.limit(); ++x) {
if (x > 0) {
hexbuf.append(" ");
}
hexbuf.append(String.format("%02x", mData.get(y+x)));
}
Log.e(XServer.TAG, " " + hexbuf);
hexbuf = new StringBuffer();
}
}
void clear() { mData.position(0); mData.limit(0); }
void resize(int len) {
if (len == 0) {
Log.e("X", "ByteQueue.resize: new len is zero");
len = 1;
}
if (mData.capacity() < len) {
int newlen = mData.capacity();
if (newlen == 0) {
Log.e("X", "ByteQueue.resize: capacity is zero");
Thread.currentThread().dumpStack();
newlen = 1;
}
while (newlen < len) { newlen *= 2; } //XXX: better way? overflow?
ByteBuffer newbuf = ByteBuffer.allocate(newlen);
newbuf.order(mData.order());
System.arraycopy(mData.array(), 0, newbuf.array(), 0, mData.limit());
newbuf.position(mData.position());
mData = newbuf;
}
mData.limit(len);
}
ByteOrder endian() { return mData.order(); }
void endian(ByteOrder val) { mData.order(val); }
int pos() { return mData.position(); }
void pos(int val) { mData.position(val); }
int length() { return mData.limit(); }
int remain() { return length() - pos(); }
void compact() {
mData.compact();
mData.limit(mData.position());
}
byte[] getBytes() {
byte[] b = new byte[mData.position()];
System.arraycopy(mData.array(), 0, b, 0, mData.position());
return b;
}
void deqSkip(int n) {
int pos = mData.position();
mData.position(pos+n);
}
void deqAlign(int n) {
int pos = mData.position();
mData.position(MathX.roundup(pos, n));
}
byte deqByte() { return mData.get(); }
short deqShort() { return mData.getShort(); }
int deqInt() { return mData.getInt(); }
byte[] deqArray(int len) {
byte[] val = new byte[len];
mData.get(val);
return val;
}
ByteQueue deqData(int len) {
byte[] val = deqArray(len);
ByteQueue data = new ByteQueue(val);
data.mData.order(mData.order());
return data;
}
String deqString(int len) {
byte[] val = deqArray(len);
return new String(val);
}
void enqSkip(int n) {
int pos = mData.position();
int newpos = pos+n;
resize(newpos);
mData.position(newpos);
}
void enqAlign(int n) {
int pos = mData.position();
int newpos = MathX.roundup(pos, n);
resize(newpos);
mData.position(newpos);
}
void enqByte(byte val) { resize(mData.position() + 1); mData.put(val); }
void enqShort(short val) { resize(mData.position() + 2); mData.putShort(val); }
void enqInt(int val) { resize(mData.position() + 4); mData.putInt(val); }
void enqArray(byte[] val, int pos, int len) {
resize(mData.position() + len);
mData.put(val, pos, len);
}
void enqArray(byte[] val) {
resize(mData.position() + val.length);
mData.put(val);
}
void enqData(ByteQueue val) {
resize(mData.position() + val.pos());
mData.put(val.mData.array(), 0, val.pos());
}
void enqString(String val) {
enqArray(val.getBytes());
}
}

View File

@ -0,0 +1,129 @@
package tdm.xserver;
import android.util.Log;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.view.View;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
class ClientView extends View
{
X11Window mWindow;
X11Client mClient;
ClientView(Context ctx, X11Window w) {
super(ctx);
Log.d("X", "ClientView ctor");
mWindow = w;
}
void destroy() {
mClient = null;
mWindow = null;
}
protected void onMeasure(int wms, int hms) {
Log.d("X", "ClientView#"+mWindow.mId+".onMeasure: wms=" + wms + ", hms=" + hms +
", w=" + mWindow.mRect.w + ", h=" + mWindow.mRect.h);
setMeasuredDimension(mWindow.mRect.w, mWindow.mRect.h);
}
public void onDraw(Canvas canvas) {
Log.d("X", "ClientView#"+mWindow.mId+".onDraw");
canvas.drawBitmap(mWindow.mBitmap, 0, 0, null);
}
public boolean onGenericMotionEvent(MotionEvent event) {
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
// Do not handle non-pointer motion (eg. joystick)
return true; //XXX? super.onGenericMotionEvent(event);
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: // 0
Log.d("X", "MotionEvent: ACTION_DOWN");
mWindow.onButtonPress(mClient, (int)event.getX(), (int)event.getY(), 0);
break;
case MotionEvent.ACTION_UP: // 1
Log.d("X", "MotionEvent: ACTION_UP");
mWindow.onButtonRelease(mClient, (int)event.getX(), (int)event.getY(), 0);
break;
case MotionEvent.ACTION_MOVE: // 2
Log.d("X", "MotionEvent: ACTION_MOVE");
mWindow.onMotion(mClient, (int)event.getX(), (int)event.getY());
break;
// ACTION_CANCEL == 3
// ACTION_OUTSIDE == 4
case MotionEvent.ACTION_POINTER_DOWN: // 5
Log.d("X", "MotionEvent: ACTION_POINTER_DOWN");
mWindow.onButtonPress(mClient, (int)event.getX(), (int)event.getY(), 0);
break;
case MotionEvent.ACTION_POINTER_UP: // 6
Log.d("X", "MotionEvent: ACTION_POINTER_UP");
mWindow.onButtonRelease(mClient, (int)event.getX(), (int)event.getY(), 0);
break;
//XXX: MotionEvent.ACTION_HOVER_MOVE
//XXX: MotionEvent.ACTION_SCROLL
}
return true;
}
public boolean onTouchEvent(MotionEvent event) {
//XXX: MotienEvent.getSource() is apparently not in FroYo :/
// E/AndroidRuntime( 1992): java.lang.NoSuchMethodError: android.view.MotionEvent.getSource
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
// Do not handle non-pointer motion (eg. joystick)
return true; //XXX? super.onTouchEvent(event);
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: // 0
Log.d("X", "TouchEvent: ACTION_DOWN");
mWindow.onButtonPress(mClient, (int)event.getX(), (int)event.getY(), 0);
break;
case MotionEvent.ACTION_UP: // 1
Log.d("X", "TouchEvent: ACTION_UP");
mWindow.onButtonRelease(mClient, (int)event.getX(), (int)event.getY(), 0);
break;
case MotionEvent.ACTION_MOVE: // 2
Log.d("X", "TouchEvent: ACTION_MOVE");
mWindow.onMotion(mClient, (int)event.getX(), (int)event.getY());
break;
// ACTION_CANCEL == 3
// ACTION_OUTSIDE == 4
case MotionEvent.ACTION_POINTER_DOWN: // 5
Log.d("X", "TouchEvent: ACTION_POINTER_DOWN");
mWindow.onButtonPress(mClient, (int)event.getX(), (int)event.getY(), 0);
break;
case MotionEvent.ACTION_POINTER_UP: // 6
Log.d("X", "TouchEvent: ACTION_POINTER_UP");
mWindow.onButtonRelease(mClient, (int)event.getX(), (int)event.getY(), 0);
break;
//XXX: MotionEvent.ACTION_HOVER_MOVE
//XXX: MotionEvent.ACTION_SCROLL
}
return true;
}
public boolean onKeyDown(int code, KeyEvent event) {
Log.d("X", "ClientView#"+mWindow.mId+".onKeyDown("+code+"): char="+event.getUnicodeChar());
mWindow.onKeyPress(mClient, code);
mClient.mServer.mKeyboard.onKeyDown(code);
return true;
}
public boolean onKeyUp(int code, KeyEvent event) {
Log.d("X", "ClientView#"+mWindow.mId+".onKeyUp("+code+")");
mWindow.onKeyRelease(mClient, code);
mClient.mServer.mKeyboard.onKeyUp(code);
return true;
}
protected void onFocusChanged(boolean gainFocus, int direction, Rect prev) {
Log.d("X", "ClientView#"+mWindow.mId+".onFocusChanged("+gainFocus+")");
}
}

View File

@ -0,0 +1,141 @@
package tdm.xserver;
import android.util.Log;
import android.graphics.Bitmap;
import java.util.ArrayList;
import java.io.File;
class FontData
{
static void globalInit(X11Server server) {
FontDataPCF.globalInit(server);
FontDataNative.globalInit(server);
}
String mName;
boolean mMetadataLoaded;
boolean mGlyphsLoaded;
X11CharInfo mMinBounds;
X11CharInfo mMaxBounds;
short mMinCharOrByte2;
short mMaxCharOrByte2;
short mDefaultChar;
byte mDrawDirection;
byte mMinByte1;
byte mMaxByte1;
byte mAllCharsExist;
short mFontAscent;
short mFontDescent;
ArrayList<X11FontProp> mProperties;
ArrayList<X11CharInfo> mCharInfos;
ArrayList<Bitmap> mImages;
FontData(String name) {
mName = name;
mMetadataLoaded = false;
mGlyphsLoaded = false;
mMinBounds = new X11CharInfo();
mMinBounds.left_side_bearing = 0;
mMinBounds.right_side_bearing = 0;
mMinBounds.character_width = 0;
mMinBounds.ascent = 0;
mMinBounds.descent = 0;
mMinBounds.attributes = 0;
mMaxBounds = new X11CharInfo();
mMaxBounds.left_side_bearing = 0;
mMaxBounds.right_side_bearing = 0;
mMaxBounds.character_width = 0;
mMaxBounds.ascent = 0;
mMaxBounds.descent = 0;
mMaxBounds.attributes = 0;
mMinCharOrByte2 = 0;
mMaxCharOrByte2 = 0;
mDefaultChar = 0;
mDrawDirection = 0;
mMinByte1 = 0;
mMaxByte1 = 0;
mAllCharsExist = 1;
mFontAscent = 0;
mFontDescent = 0;
mProperties = new ArrayList<X11FontProp>();
mCharInfos = new ArrayList<X11CharInfo>();
mImages = new ArrayList<Bitmap>();
}
void loadMetadata(X11Server server) throws Exception { mMetadataLoaded = true; }
void loadGlyphs(X11Server server) throws Exception { mGlyphsLoaded = true; }
boolean match(String pattern) {
String[] patv = pattern.split("-");
String[] v = mName.split("-");
if (patv.length != v.length) {
return false;
}
for (int n = 0; n < v.length; ++n) {
if (!patv[n].equals("*") && !patv[n].equals(v[n])) {
return false;
}
}
return true;
}
Bitmap getBitmap(int idx) {
return mImages.get(idx);
}
void enqueueInfo(ByteQueue q) {
q.enqShort(mMinBounds.left_side_bearing);
q.enqShort(mMinBounds.right_side_bearing);
q.enqShort(mMinBounds.character_width);
q.enqShort(mMinBounds.ascent);
q.enqShort(mMinBounds.descent);
q.enqShort(mMinBounds.attributes);
q.enqSkip(4);
q.enqShort(mMaxBounds.left_side_bearing);
q.enqShort(mMaxBounds.right_side_bearing);
q.enqShort(mMaxBounds.character_width);
q.enqShort(mMaxBounds.ascent);
q.enqShort(mMaxBounds.descent);
q.enqShort(mMaxBounds.attributes);
q.enqSkip(4);
q.enqShort(mMinCharOrByte2);
q.enqShort(mMaxCharOrByte2);
q.enqShort(mDefaultChar);
q.enqShort((short)mProperties.size());
q.enqByte(mDrawDirection);
q.enqByte(mMinByte1);
q.enqByte(mMaxByte1);
q.enqByte(mAllCharsExist);
q.enqShort(mFontAscent);
q.enqShort(mFontDescent);
}
void enqueueProperties(ByteQueue q) {
for (X11FontProp prop : mProperties) {
q.enqInt(prop.name);
q.enqInt(prop.value);
}
}
void enqueueCharInfoCount(ByteQueue q) {
q.enqInt((int)mCharInfos.size());
}
void enqueueCharInfo(ByteQueue q) {
for (X11CharInfo info : mCharInfos) {
q.enqShort(info.left_side_bearing);
q.enqShort(info.right_side_bearing);
q.enqShort(info.character_width);
q.enqShort(info.ascent);
q.enqShort(info.descent);
q.enqShort(info.attributes);
}
}
}

View File

@ -0,0 +1,36 @@
package tdm.xserver;
import android.graphics.Typeface;
import java.io.File;
class FontDataNative extends FontData
{
static void globalInit(X11Server server) {
// Built-in Android fonts
server.registerFont(new FontDataNative("sans", Typeface.SANS_SERIF));
server.registerFont(new FontDataNative("serif", Typeface.SERIF));
server.registerFont(new FontDataNative("monospace", Typeface.MONOSPACE));
//server.registerFontAlias("fixed", "monospace");
File dir = new File("/system/fonts/");
for (File f : dir.listFiles()) {
String filename = f.getName();
if (filename.endsWith(".ttf")) {
String fontname = filename.substring(0, filename.lastIndexOf('.'));
server.registerFont(new FontDataNative(fontname, f));
}
}
}
Typeface mTypeFace;
FontDataNative(String name, Typeface tf) {
super(name);
mTypeFace = tf;
}
FontDataNative(String name, File f) {
super(name);
mTypeFace = Typeface.createFromFile(f);
}
}

View File

@ -0,0 +1,636 @@
package tdm.xserver;
import android.content.res.AssetManager;
import android.util.Log;
import android.graphics.Bitmap;
import android.graphics.Color;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.zip.GZIPInputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
class FontDataPCF extends FontData
{
static final int PATH_TYPE_FILE = 1;
static final int PATH_TYPE_ASSET = 2;
static final int PCF_PROPERTIES = (1<<0);
static final int PCF_ACCELERATORS = (1<<1);
static final int PCF_METRICS = (1<<2);
static final int PCF_BITMAPS = (1<<3);
static final int PCF_INK_METRICS = (1<<4);
static final int PCF_BDF_ENCODINGS = (1<<5);
static final int PCF_SWIDTHS = (1<<6);
static final int PCF_GLYPH_NAMES = (1<<7);
static final int PCF_BDF_ACCELERATORS = (1<<8);
static final int PCF_DEFAULT_FORMAT = 0x00000000;
static final int PCF_INKBOUNDS = 0x00000200;
static final int PCF_ACCEL_W_INKBOUNDS = 0x00000100;
static final int PCF_COMPRESSED_METRICS = 0x00000100;
static final int PCF_GLYPH_PAD_MASK = (3<<0); // See the bitmap table for explanation
static final int PCF_BYTE_MASK = (1<<2); // If set then Most Sig Byte First
static final int PCF_BIT_MASK = (1<<3); // If set then Most Sig Bit First
static final int PCF_SCAN_UNIT_MASK = (3<<4); // See the bitmap table for explanation
class pcf_toc_entry
{
int type;
int format;
int size;
int offset;
}
class pcf_toc_order_by_offset implements Comparator
{
public int compare(Object o1, Object o2) {
pcf_toc_entry e1 = (pcf_toc_entry)o1;
pcf_toc_entry e2 = (pcf_toc_entry)o2;
return e1.offset - e2.offset;
}
}
class pcf_property
{
int name_offset;
byte is_string;
int value;
}
class pcf_metrics
{
short left_side_bearing;
short right_side_bearing;
short character_width;
short character_ascent;
short character_descent;
short character_attributes;
}
class pcf_accelerator
{
byte no_overlap;
byte constant_metrics;
byte terminal_font;
byte constant_width;
byte ink_inside;
byte ink_metrics;
byte draw_direction;
byte padding;
int font_ascent;
int font_descent;
int max_overlap;
pcf_metrics min_bounds;
pcf_metrics max_bounds;
pcf_metrics ink_min_bounds;
pcf_metrics ink_max_bounds;
pcf_accelerator() {
min_bounds = new pcf_metrics();
max_bounds = new pcf_metrics();
ink_min_bounds = new pcf_metrics();
ink_max_bounds = new pcf_metrics();
}
}
class pcf_encoding
{
short min_char_or_byte2;
short max_char_or_byte2;
short min_byte1;
short max_byte1;
short default_char;
short[] glyph_indices;
}
static void globalInit(X11Server server) {
BufferedReader br;
String s;
try {
AssetManager am = server.mContext.getAssets();
br = new BufferedReader(new InputStreamReader(am.open("fonts/fonts.dir")));
br.readLine(); // Skip first line
while ((s = br.readLine()) != null) {
String[] fields = s.split(" ", 2);
String pathname = "fonts/" + fields[0];
FontDataPCF pcf = new FontDataPCF(fields[1], PATH_TYPE_ASSET, pathname);
server.registerFont(pcf);
}
br = new BufferedReader(new InputStreamReader(am.open("fonts/fonts.alias")));
while ((s = br.readLine()) != null) {
if (s.length() == 0 || s.charAt(0) == '!') {
continue;
}
String[] fields = s.split("[ \t]{1,}", 2);
server.registerFontAlias(fields[0].toLowerCase(), fields[1].toLowerCase());
}
}
catch (Exception e) {
Log.e("X", "Failed to init PCF fonts from assets");
}
try {
br = new BufferedReader(new InputStreamReader(new FileInputStream("/data/local/fonts/fonts.dir")));
br.readLine(); // Skip first line
while ((s = br.readLine()) != null) {
if (s.length() == 0 || s.charAt(0) == '!') {
continue;
}
String[] fields = s.split(" ", 2);
if (fields.length != 2) {
Log.e("X", "bad line in fonts.dir: fields=" + fields.length + ", line=" + s);
continue;
}
String pathname = "/data/local/fonts/" + fields[0];
FontDataPCF pcf = new FontDataPCF(fields[1], PATH_TYPE_FILE, pathname);
server.registerFont(pcf);
}
br = new BufferedReader(new InputStreamReader(new FileInputStream("/data/local/fonts/fonts.alias")));
while ((s = br.readLine()) != null) {
if (s.length() == 0 || s.charAt(0) == '!') {
continue;
}
String[] fields = s.split("[ \t]{1,}", 2);
if (fields.length != 2) {
Log.e("X", "bad line in fonts.alias: fields=" + fields.length + ", line=" + s);
continue;
}
server.registerFontAlias(fields[0].toLowerCase(), fields[1].toLowerCase());
}
}
catch (Exception e) {
Log.e("X", "Failed to init PCF fonts from dir");
e.printStackTrace();
}
}
int mPathtype; // FILE or ASSET
String mPathname;
ArrayList<X11CharInfo> mGlyphInfos;
FontDataPCF(String name, int pathtype, String pathname) {
super(name);
mPathtype = pathtype;
mPathname = pathname;
mGlyphInfos = new ArrayList<X11CharInfo>();
}
private void readPropertyTable(X11Server server, int fmt, ByteBuffer b) {
b.order(ByteOrder.LITTLE_ENDIAN);
int format = b.getInt();
b.order((format & PCF_BYTE_MASK) != 0 ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
int nprops = b.getInt();
pcf_property[] props = new pcf_property[nprops];
int n;
for (n = 0; n < nprops; ++n) {
pcf_property prop = new pcf_property();
prop.name_offset = b.getInt();
prop.is_string = b.get();
prop.value = b.getInt();
props[n] = prop;
}
b.position(MathX.roundup(b.position(), 4));
int string_size = b.getInt();
byte[] string_table = new byte[string_size];
b.get(string_table);
mProperties = new ArrayList<X11FontProp>(nprops);
for (n = 0; n < nprops; ++n) {
String s = new String(string_table, props[n].name_offset);
X11FontProp prop = new X11FontProp();
prop.name = server.doInternAtom(s);
if (props[n].is_string != 0) {
String val = new String(string_table, props[n].value);
prop.value = server.doInternAtom(val);
}
else {
prop.value = props[n].value;
}
mProperties.add(n, prop);
}
}
private void readAcceleratorTable(int fmt, ByteBuffer b) {
b.order(ByteOrder.LITTLE_ENDIAN);
int format = b.getInt();
b.order((format & PCF_BYTE_MASK) != 0 ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
pcf_accelerator a = new pcf_accelerator();
a.no_overlap = b.get();
a.constant_metrics = b.get();
a.terminal_font = b.get();
a.constant_width = b.get();
a.ink_inside = b.get();
a.ink_metrics = b.get();
a.draw_direction = b.get();
a.padding = b.get();
a.font_ascent = b.getInt();
a.font_descent = b.getInt();
a.max_overlap = b.getInt();
a.min_bounds.left_side_bearing = b.getShort();
a.min_bounds.right_side_bearing = b.getShort();
a.min_bounds.character_width = b.getShort();
a.min_bounds.character_ascent = b.getShort();
a.min_bounds.character_descent = b.getShort();
a.min_bounds.character_attributes = b.getShort();
a.max_bounds.left_side_bearing = b.getShort();
a.max_bounds.right_side_bearing = b.getShort();
a.max_bounds.character_width = b.getShort();
a.max_bounds.character_ascent = b.getShort();
a.max_bounds.character_descent = b.getShort();
a.max_bounds.character_attributes = b.getShort();
if ((fmt & PCF_ACCEL_W_INKBOUNDS) != 0) {
a.ink_min_bounds.left_side_bearing = b.getShort();
a.ink_min_bounds.right_side_bearing = b.getShort();
a.ink_min_bounds.character_width = b.getShort();
a.ink_min_bounds.character_ascent = b.getShort();
a.ink_min_bounds.character_descent = b.getShort();
a.ink_min_bounds.character_attributes = b.getShort();
a.ink_max_bounds.left_side_bearing = b.getShort();
a.ink_max_bounds.right_side_bearing = b.getShort();
a.ink_max_bounds.character_width = b.getShort();
a.ink_max_bounds.character_ascent = b.getShort();
a.ink_max_bounds.character_descent = b.getShort();
a.ink_max_bounds.character_attributes = b.getShort();
}
else {
a.ink_min_bounds = a.min_bounds;
a.ink_max_bounds = a.max_bounds;
}
mMinBounds.left_side_bearing = a.min_bounds.left_side_bearing;
mMinBounds.right_side_bearing = a.min_bounds.right_side_bearing;
mMinBounds.character_width = a.min_bounds.character_width;
mMinBounds.ascent = a.min_bounds.character_ascent;
mMinBounds.descent = a.min_bounds.character_descent;
mMinBounds.attributes = a.min_bounds.character_attributes;
mMaxBounds.left_side_bearing = a.max_bounds.left_side_bearing;
mMaxBounds.right_side_bearing = a.max_bounds.right_side_bearing;
mMaxBounds.character_width = a.max_bounds.character_width;
mMaxBounds.ascent = a.max_bounds.character_ascent;
mMaxBounds.descent = a.max_bounds.character_descent;
mMaxBounds.attributes = a.max_bounds.character_attributes;
mDrawDirection = a.draw_direction;
mAllCharsExist = 0 /* false */; //XXX?
mFontAscent = (short)a.font_ascent;
mFontDescent = (short)a.font_descent;
}
short decompress_metric(byte b) {
if (b < 0) {
return (short)(b & 0x7f);
}
return (short)(b - 0x80);
}
private void readMetricsTable(int fmt, ByteBuffer b, ArrayList<X11CharInfo> infos) {
b.order(ByteOrder.LITTLE_ENDIAN);
int format = b.getInt();
b.order((format & PCF_BYTE_MASK) != 0 ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
if ((fmt & PCF_COMPRESSED_METRICS) != 0) {
short count = b.getShort();
short n;
for (n = 0; n < count; ++n) {
X11CharInfo info = new X11CharInfo();
info.left_side_bearing = decompress_metric(b.get());
info.right_side_bearing = decompress_metric(b.get());
info.character_width = decompress_metric(b.get());
info.ascent = decompress_metric(b.get());
info.descent = decompress_metric(b.get());
info.attributes = 0;
infos.add(n, info);
}
}
else {
int count = b.getInt();
int n;
for (n = 0; n < count; ++n) {
X11CharInfo info = new X11CharInfo();
info.left_side_bearing = b.getShort();
info.right_side_bearing = b.getShort();
info.character_width = b.getShort();
info.ascent = b.getShort();
info.descent = b.getShort();
info.attributes = b.getShort();
infos.add(n, info);
}
}
}
private void showBitmap(char ch) {
Bitmap bmp = mImages.get((int)ch);
Log.d("X", "Show bitmap for <"+ch+">: w="+bmp.getWidth()+", h="+bmp.getHeight()+" ...");
Log.d("X", "-----------");
for (int y = 0; y < bmp.getHeight(); ++y) {
StringBuffer buf = new StringBuffer();
for (int x = 0; x < bmp.getWidth(); ++x) {
if (bmp.getPixel(x, y) == Color.BLACK) {
buf.append(".");
}
else {
buf.append("X");
}
}
Log.d("X", " " + buf);
}
Log.d("X", "-----------");
}
private void readBitmapTable(int fmt, ByteBuffer b) {
b.order(ByteOrder.LITTLE_ENDIAN);
int format = b.getInt();
b.order((format & PCF_BYTE_MASK) != 0 ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
int glyph_count = b.getInt();
int n;
int[] offsets = new int[glyph_count];
for (n = 0; n < glyph_count; ++n) {
offsets[n] = b.getInt();
}
int[] bitmap_sizes = new int[4];
bitmap_sizes[0] = b.getInt();
bitmap_sizes[1] = b.getInt();
bitmap_sizes[2] = b.getInt();
bitmap_sizes[3] = b.getInt();
int bitmap_data_len = bitmap_sizes[format&3];
ByteBuffer bitmap_data = b.slice();
bitmap_data.limit(bitmap_data_len);
int bitmap_byte_order = (format&4) >> 2; // 1=LSByteFirst, 0=MSByteFirst
int bitmap_bit_order = (format&8) >> 3; // 1=LSBitFirst, 0=MSBitFirst
mImages = new ArrayList<Bitmap>(glyph_count);
int row_pad_bytes = (1<<(format&3));
int elem_bytes = (1<<((format>>4)&3));
int elem_bits = elem_bytes*8;
Log.d("X", "readBitmapTable: name="+mName+", glyph_count="+glyph_count+", bitmap_data_len="+bitmap_data_len);
for (n = 0; n < glyph_count; ++n) {
X11CharInfo info = mGlyphInfos.get(n);
//XXX: should width be rsb-lsb or width?
short w = info.character_width;
short h = (short)(info.ascent + info.descent);
if (w == 0 || h == 0) {
mImages.add(n, null);
continue;
}
Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
int elem_per_row = (w+elem_bits-1)/elem_bits;
int bytes_per_row = ((elem_per_row*elem_bytes+row_pad_bytes-1)/row_pad_bytes)*row_pad_bytes;
short row, col;
int row_off, col_off, idx;
int elem;
int nbits, bit;
for (row = 0; row < h; ++row) {
row_off = row * bytes_per_row;
for (col = 0; col < w; ++col) {
int elem_off = (col/elem_bits)*elem_bytes;
int elem_byte_off;
if (bitmap_byte_order == 1 /*LSByteFirst*/) {
elem_byte_off = (col%elem_bits)/8;
}
else {
elem_byte_off = elem_bytes-1 - (col%elem_bits)/8;
}
int elem_bit_off;
if (bitmap_bit_order == 1 /*LSBitFirst*/) {
elem_bit_off = 8-1 - (col%8);
}
else {
elem_bit_off = (col%8);
}
int valoff = offsets[n] + row_off + elem_off + elem_byte_off;
byte val = bitmap_data.get(offsets[n] + row_off + elem_off + elem_byte_off);
int pixel = (val >> elem_bit_off) & 1;
int color = (pixel != 0 ? Color.WHITE : Color.BLACK);
bmp.setPixel(col, row, color);
}
}
mImages.add(n, bmp);
}
}
private void readEncodingTable(int fmt, ByteBuffer b) {
b.order(ByteOrder.LITTLE_ENDIAN);
int format = b.getInt();
b.order((format & PCF_BYTE_MASK) != 0 ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN);
mMinCharOrByte2 = b.getShort();
mMaxCharOrByte2 = b.getShort();
mMinByte1 = (byte)b.getShort();
mMaxByte1 = (byte)b.getShort();
mDefaultChar = b.getShort();
//XXX: this fails on cu-pua12.pcf.gz
int num_indices = (mMaxCharOrByte2 -
mMinCharOrByte2 + 1) *
(mMaxByte1 -
mMinByte1 + 1);
short[] glyph_indices = new short[num_indices];
int n;
for (n = 0; n < num_indices; ++n) {
glyph_indices[n] = b.getShort();
}
}
private void readScalableWidthsTable(int fmt, ByteBuffer b) {
//XXX: Skip this data
}
private void readGlyphNamesTable(int fmt, ByteBuffer b) {
//XXX: Skip this data
}
void loadMetadata(X11Server server) throws Exception {
if (mMetadataLoaded) {
return;
}
InputStream is = null;
if (mPathtype == PATH_TYPE_ASSET) {
AssetManager am = server.mContext.getAssets();
is = am.open(mPathname);
}
else {
is = new FileInputStream(mPathname);
}
//XXX: should check for gzip signature, but what about rewind?
if (mPathname.endsWith(".gz")) {
is = new GZIPInputStream(is);
}
long pos = 0; //XXX: seek() sure would be nice
ByteBuffer b;
b = ByteBuffer.allocate(4);
is.read(b.array(), 0, 4); pos += 4;
byte[] sig = b.array();
if (sig[0] != 1 || sig[1] != 'f' || sig[2] != 'c' || sig[3] != 'p') {
throw new Exception("Bad PCF header");
}
b = ByteBuffer.allocate(4);
b.order(ByteOrder.LITTLE_ENDIAN);
is.read(b.array(), 0, 4); pos += 4;
int table_count = b.getInt();
int n;
pcf_toc_entry[] toc = new pcf_toc_entry[table_count];
for (n = 0; n < table_count; ++n) {
b = ByteBuffer.allocate(16);
b.order(ByteOrder.LITTLE_ENDIAN);
is.read(b.array(), 0, 16); pos += 16;
pcf_toc_entry ent = new pcf_toc_entry();
ent.type = b.getInt();
ent.format = b.getInt();
ent.size = b.getInt();
ent.offset = b.getInt();
toc[n] = ent;
}
Arrays.sort(toc, new pcf_toc_order_by_offset());
for (pcf_toc_entry e : toc) {
if (pos != e.offset) {
is.skip(e.offset - pos);
pos = e.offset;
}
b = ByteBuffer.allocate(e.size);
int off = 0;
while (off < e.size) {
int nread = is.read(b.array(), off, e.size-off);
if (nread <= 0) {
Log.w("X", "FontDataPCF.loadMetadata: short read in " + mPathname);
break;
}
off += nread;
pos += nread;
}
switch (e.type) {
case PCF_PROPERTIES: readPropertyTable(server, e.format, b); break;
case PCF_ACCELERATORS: readAcceleratorTable(e.format, b); break;
case PCF_METRICS: readMetricsTable(e.format, b, mGlyphInfos); break;
case PCF_BITMAPS: /* Ignore */ break;
case PCF_INK_METRICS: readMetricsTable(e.format, b, mCharInfos); break;
case PCF_BDF_ENCODINGS: readEncodingTable(e.format, b); break;
case PCF_SWIDTHS: readScalableWidthsTable(e.format, b); break;
case PCF_GLYPH_NAMES: readGlyphNamesTable(e.format, b); break;
case PCF_BDF_ACCELERATORS: readAcceleratorTable(e.format, b); break;
default: throw new Exception("Bad PCF toc");
}
}
mMetadataLoaded = true;
}
void loadGlyphs(X11Server server) throws Exception {
if (mGlyphsLoaded) {
return;
}
Log.d("X", "FontDataPCF.loadGlyphs");
InputStream is = null;
if (mPathtype == PATH_TYPE_ASSET) {
AssetManager am = server.mContext.getAssets();
is = am.open(mPathname);
}
else {
is = new FileInputStream(mPathname);
}
//XXX: should check for gzip signature, but what about rewind?
if (mPathname.endsWith(".gz")) {
is = new GZIPInputStream(is);
}
long pos = 0; //XXX: seek() sure would be nice
ByteBuffer b;
b = ByteBuffer.allocate(4);
is.read(b.array(), 0, 4); pos += 4;
byte[] sig = b.array();
if (sig[0] != 1 || sig[1] != 'f' || sig[2] != 'c' || sig[3] != 'p') {
throw new Exception("Bad PCF header");
}
b = ByteBuffer.allocate(4);
b.order(ByteOrder.LITTLE_ENDIAN);
is.read(b.array(), 0, 4); pos += 4;
int table_count = b.getInt();
int n;
pcf_toc_entry[] toc = new pcf_toc_entry[table_count];
for (n = 0; n < table_count; ++n) {
b = ByteBuffer.allocate(16);
b.order(ByteOrder.LITTLE_ENDIAN);
is.read(b.array(), 0, 16); pos += 16;
pcf_toc_entry ent = new pcf_toc_entry();
ent.type = b.getInt();
ent.format = b.getInt();
ent.size = b.getInt();
ent.offset = b.getInt();
toc[n] = ent;
}
Arrays.sort(toc, new pcf_toc_order_by_offset());
for (pcf_toc_entry e : toc) {
if (pos != e.offset) {
is.skip(e.offset - pos);
pos = e.offset;
}
b = ByteBuffer.allocate(e.size);
int off = 0;
while (off < e.size) {
int nread = is.read(b.array(), off, e.size-off);
if (nread <= 0) {
Log.w("X", "FontDataPCF.loadGlyphs: short read in " + mPathname);
break;
}
off += nread;
pos += nread;
}
switch (e.type) {
case PCF_BITMAPS: readBitmapTable(e.format, b); break;
default: /* Ignore */ break;
}
}
mGlyphsLoaded = true;
}
}

View File

@ -0,0 +1,18 @@
package tdm.xserver;
class MathX
{
public static short divceil(short val, int n) {
return (short)((val+n-1)/n);
}
public static int divceil(int val, int n) {
return (val+n-1)/n;
}
public static short roundup(short val, int n) {
return (short)(divceil(val,n)*n);
}
public static int roundup(int val, int n) {
return divceil(val,n)*n;
}
}

View File

@ -0,0 +1,104 @@
package tdm.xserver;
import android.util.Log;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.graphics.drawable.BitmapDrawable;
import android.view.View;
import android.view.ViewGroup;
import android.view.Gravity;
import android.widget.RelativeLayout;
class UIHandler extends Handler
{
public static final int MSG_VIEW_CREATE_ROOT = 0x1001;
public static final int MSG_VIEW_CREATE = 0x1002;
public static final int MSG_VIEW_REMOVE = 0x1003;
public static final int MSG_VIEW_CONFIGURE = 0x1004;
public static final int MSG_VIEW_SET_VISIBLE = 0x1010;
public static final int MSG_VIEW_SET_BACKGROUND_COLOR = 0x1011;
public static final int MSG_VIEW_SET_BACKGROUND_BITMAP = 0x1012;
public static final int MSG_VIEW_INVALIDATE = 0x1020;
Context mContext;
ViewGroup mViewGroup;
UIHandler(Context ctx, ViewGroup vg) {
mContext = ctx;
mViewGroup = vg;
}
public void handleMessage(Message msg) {
X11Window w;
RelativeLayout.LayoutParams lp;
switch (msg.what) {
case MSG_VIEW_CREATE_ROOT:
w = (X11Window)msg.obj;
w.mView = new ClientView(mContext, w);
lp = new RelativeLayout.LayoutParams(w.mRect.x, w.mRect.y);
mViewGroup.addView(w.mView, lp);
w.mView.setFocusable(true);
w.mView.setFocusableInTouchMode(true);
w.mView.setVisibility(View.VISIBLE);
Log.d("X", "UI: w="+w.mId+": Attached ClientView to root window");
break;
case MSG_VIEW_CREATE:
w = (X11Window)msg.obj;
w.mView = new ClientView(mContext, w);
lp = new RelativeLayout.LayoutParams(w.mRect.w, w.mRect.h);
lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT, -1);
lp.addRule(RelativeLayout.ALIGN_PARENT_TOP, -1);
lp.setMargins(w.mRect.x, w.mRect.y, 0, 0);
mViewGroup.addView(w.mView, lp);
w.mView.setFocusable(true);
w.mView.setFocusableInTouchMode(true);
w.mView.setVisibility(View.INVISIBLE);
Log.d("X", "UI: w="+w.mId+": Attached ClientView to window at x="+w.mRect.x+", y=" + w.mRect.y);
break;
case MSG_VIEW_REMOVE:
w = (X11Window)msg.obj;
w.mRealized = false;
mViewGroup.removeView(w.mView);
w.mView.destroy();
w.mView = null;
break;
case MSG_VIEW_CONFIGURE:
w = (X11Window)msg.obj;
lp = new RelativeLayout.LayoutParams(w.mRect.w, w.mRect.h);
lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT, -1);
lp.addRule(RelativeLayout.ALIGN_PARENT_TOP, -1);
lp.setMargins(w.mRect.x, w.mRect.y, 0, 0);
//XXX: better way?
mViewGroup.removeView(w.mView);
mViewGroup.addView(w.mView, lp);
break;
case MSG_VIEW_SET_VISIBLE:
w = (X11Window)msg.obj;
w.mView.setVisibility(View.VISIBLE);
w.mRealized = true;
w.mView.requestFocus();
Log.d("X", "UI: w="+w.mId+": Set window visible");
break;
case MSG_VIEW_SET_BACKGROUND_COLOR:
w = (X11Window)msg.obj;
w.mView.setBackgroundColor(w.mBgPixel);
break;
case MSG_VIEW_SET_BACKGROUND_BITMAP:
w = (X11Window)msg.obj;
w.mView.setBackgroundDrawable(
new BitmapDrawable(mContext.getResources(),
w.mBgPixmap.mBitmap));
break;
case MSG_VIEW_INVALIDATE:
w = (X11Window)msg.obj;
w.mView.invalidate();
break;
}
}
}

View File

@ -0,0 +1,99 @@
package tdm.xserver;
class X11Atom
{
static final int NONE = 0;
static final int NR_PREDEFINED = 68;
static final String predefined_names[] = {
"(none)", // Invalid, placeholder
"PRIMARY", // 1
"SECONDARY",
"ARC",
"ATOM",
"BITMAP",
"CARDINAL",
"COLORMAP",
"CURSOR",
"CUT_BUFFER0",
"CUT_BUFFER1", // 10
"CUT_BUFFER2",
"CUT_BUFFER3",
"CUT_BUFFER4",
"CUT_BUFFER5",
"CUT_BUFFER6",
"CUT_BUFFER7",
"DRAWABLE",
"FONT",
"INTEGER",
"PIXMAP", // 20
"POINT",
"RECTANGLE",
"RESOURCE_MANAGER",
"RGB_COLOR_MAP",
"RGB_BEST_MAP",
"RGB_BLUE_MAP",
"RGB_DEFAULT_MAP",
"RGB_GRAY_MAP",
"RGB_GREEN_MAP",
"RGB_RED_MAP", // 30
"STRING",
"VISUALID",
"WINDOW",
"WM_COMMAND",
"WM_HINTS",
"WM_CLIENT_MACHINE",
"WM_ICON_NAME",
"WM_ICON_SIZE",
"WM_NAME",
"WM_NORMAL_HINTS", // 40
"WM_SIZE_HINTS",
"WM_ZOOM_HINTS",
"MIN_SPACE",
"NORM_SPACE",
"MAX_SPACE",
"END_SPACE",
"SUPERSCRIPT_X",
"SUPERSCRIPT_Y",
"SUBSCRIPT_X",
"SUBSCRIPT_Y", // 50
"UNDERLINE_POSITION",
"UNDERLINE_THICKNESS",
"STRIKEOUT_ASCENT",
"STRIKEOUT_DESCENT",
"ITALIC_ANGLE",
"X_HEIGHT",
"QUAD_WIDTH",
"WEIGHT",
"POINT_SIZE",
"RESOLUTION", // 60
"COPYRIGHT",
"NOTICE",
"FONT_NAME",
"FAMILY_NAME",
"FULL_NAME",
"CAP_HEIGHT",
"WM_CLASS",
"WM_TRANSIENT_FOR"
};
static final boolean predefined(int id) {
return (id >= 1 && id < NR_PREDEFINED);
}
static void globalInit(X11Server server) {
int i;
for (i = 1; i <= NR_PREDEFINED; ++i) {
server.doInternAtom(i, predefined_names[i]);
}
server.mLastAtomId = NR_PREDEFINED;
}
int mId;
String mName;
X11Atom(int id, String name) {
mId = id;
mName = name;
}
}

View File

@ -0,0 +1,11 @@
package tdm.xserver;
class X11CharInfo
{
short left_side_bearing;
short right_side_bearing;
short character_width;
short ascent;
short descent;
short attributes;
}

View File

@ -0,0 +1,320 @@
package tdm.xserver;
import android.util.Log;
import android.os.Message;
import java.io.IOException;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
class X11Client extends Thread implements X11ProtocolHandler
{
// NR_BITS_RESOURCES + NR_BITS_CLIENTS must be 29
static final int NR_BITS_RESOURCES = 22;
static final int NR_BITS_CLIENTS = (29-NR_BITS_RESOURCES);
static final int CLIENT_ID_SHIFT = NR_BITS_RESOURCES;
static final int MAX_CLIENTS = (1<<NR_BITS_CLIENTS);
X11Server mServer;
int mId;
X11Protocol mProt;
short mSeqNo;
Map<Integer,X11Resource> mResources;
X11Client(X11Server server) {
mServer = server;
mResources = new HashMap<Integer,X11Resource>();
}
X11Client(X11Server server, int id, Socket sock) throws Exception {
Log.e(XServer.TAG, "new client from " + sock.getRemoteSocketAddress().toString());
mServer = server;
mId = id;
mProt = new X11Protocol(this, sock);
mSeqNo = 0;
mResources = new HashMap<Integer,X11Resource>();
}
public void run() {
while (mProt != null) {
try {
mProt.read();
}
catch (Exception e) {
Log.e(XServer.TAG, "X11Client: terminating on exception: " + e.toString());
e.printStackTrace();
close();
}
}
//XXX: release grabs
//XXX: delete selection
//XXX: free resources
// ...
for (X11Resource r : mResources.values()) {
r.destroy();
}
mServer.clientClosed(this);
mServer = null;
}
private final void close() {
mProt = null;
}
void addResource(X11Resource r) throws X11Error {
if (mResources.containsKey(r.mId)) {
throw new X11Error(X11Error.IDCHOICE, r.mId);
}
mResources.put(r.mId, r);
}
void delResource(int id) {
mResources.remove(id);
}
X11Resource getResource(int id) throws X11Error {
X11Client c = mServer.mClients[id >> CLIENT_ID_SHIFT];
X11Resource r = c.mResources.get(id);
if (r == null) {
throw new X11Error(X11Error.MATCH, id);
}
return r;
}
X11Resource getResource(int id, int type) throws X11Error {
X11Resource r = getResource(id);
if (r.mType != type) {
throw new X11Error(X11Error.MATCH, id);
}
return r;
}
X11Pixmap getPixmap(int id) throws X11Error {
return (X11Pixmap)getResource(id, X11Resource.PIXMAP);
}
X11Window getWindow(int id) throws X11Error {
return (X11Window)getResource(id, X11Resource.WINDOW);
}
X11Colormap getColormap(int id) throws X11Error {
return (X11Colormap)getResource(id, X11Resource.COLORMAP);
}
X11Cursor getCursor(int id) throws X11Error {
return (X11Cursor)getResource(id, X11Resource.CURSOR);
}
X11Font getFont(int id) throws X11Error {
return (X11Font)getResource(id, X11Resource.FONT);
}
X11GContext getGContext(int id) throws X11Error {
return (X11GContext)getResource(id, X11Resource.GCONTEXT);
}
X11Drawable getDrawable(int id) throws X11Error {
X11Resource r = getResource(id);
if (r.mType != X11Resource.PIXMAP && r.mType != X11Resource.WINDOW) {
throw new X11Error(X11Error.MATCH, id);
}
return (X11Drawable)r;
}
X11Fontable getFontable(int id) throws X11Error {
X11Resource r = getResource(id);
if (r.mType != X11Resource.GCONTEXT && r.mType != X11Resource.FONT) {
throw new X11Error(X11Error.MATCH, id);
}
return (X11Fontable)r;
}
void send(X11ReplyMessage msg) {
msg.seqno(mSeqNo);
Log.d(XServer.TAG, "Send reply: seqno=" + mSeqNo);
try {
mProt.send(msg);
}
catch (IOException e) {
close();
}
}
void send(X11EventMessage msg) {
msg.seqno(mSeqNo);
Log.d(XServer.TAG, "Send event: seqno=" + mSeqNo + ", name=" + msg.name());
try {
mProt.send(msg);
}
catch (IOException e) {
close();
}
}
void send(X11ErrorMessage msg) {
msg.seqno(mSeqNo);
Log.d(XServer.TAG, "Send error: seqno=" + mSeqNo + ", name=" + msg.name());
try {
mProt.send(msg);
}
catch (IOException e) {
close();
}
}
X11Visual getVisual(int id) throws X11Error {
return mServer.getVisual(id);
}
public void onMessage(X11SetupRequest msg) {
//XXX: always allow for now
Log.e(XServer.TAG, "Got setup request");
String vendor = "My Vendor";
ArrayList<X11Format> pixmap_formats = mServer.getPixmapFormats();
X11SetupResponse r = new X11SetupResponse(msg);
r.mSuccess = 0x01; // Success
r.mProtoMajor = 11;
r.mProtoMinor = 0;
r.mData.enqInt((int)0); // release-number
r.mData.enqInt(mId << CLIENT_ID_SHIFT); // resource-id-base
r.mData.enqInt((1 << CLIENT_ID_SHIFT) - 1); // resource-id-mask
r.mData.enqInt((int)0); // motion-buffer-size
r.mData.enqShort((short)vendor.length());
r.mData.enqShort((short)0xffff); // maximum-request-length
r.mData.enqByte((byte)1); // number of SCREENs in roots
r.mData.enqByte((byte)pixmap_formats.size()); // number of FORMATs in pixmap-formats
r.mData.enqByte((byte)0); // image-byte-order = LSBFirst
r.mData.enqByte((byte)0); // bitmap-format-bit-order = LeastSignificant
r.mData.enqByte((byte)32); // bitmap-format-scanline-unit
r.mData.enqByte((byte)32); // bitmap-format-scanline-pad
r.mData.enqByte((byte)mServer.mKeyboard.minKeycode());
r.mData.enqByte((byte)mServer.mKeyboard.maxKeycode());
r.mData.enqSkip(4);
r.mData.enqString(vendor);
r.mData.enqAlign(4);
for (X11Format fmt : pixmap_formats) {
r.mData.enqByte(fmt.mDepth);
r.mData.enqByte(fmt.mBPP);
r.mData.enqByte(fmt.mPad);
r.mData.enqSkip(5);
}
mServer.mDefaultScreen.enqueue(r.mData);
Log.e(XServer.TAG, "Sending setup response");
try {
mProt.send(r);
}
catch (Exception e) {
Log.e(XServer.TAG, "Exception: ");
e.printStackTrace();
close();
}
}
public void onMessage(X11SetupResponse msg) { close(); }
public void onMessage(X11RequestMessage msg) {
while (mServer.mGrabClient != null && mServer.mGrabClient != this) {
Log.d(XServer.TAG, "Client#"+mId+" waiting on server grab");
try {
Thread.sleep(1);
}
catch (InterruptedException e) {
// Ignore
}
}
++mSeqNo;
Log.d(XServer.TAG, "Message: seqno=" + mSeqNo + ", name=" + msg.name());
try {
switch (msg.requestType()) {
case 1: X11Window.create(this, msg); break;
case 2: getWindow(msg.mData.deqInt()).handleChangeWindowAttributes(this, msg); break;
case 3: getWindow(msg.mData.deqInt()).handleGetWindowAttributes(this, msg); break;
case 4: getWindow(msg.mData.deqInt()).handleDestroyWindow(this, msg); break;
case 8: getWindow(msg.mData.deqInt()).handleMapWindow(this, msg); break;
case 9: getWindow(msg.mData.deqInt()).handleMapSubwindows(this, msg); break;
case 10: getWindow(msg.mData.deqInt()).handleUnmapWindow(this, msg); break;
case 11: getWindow(msg.mData.deqInt()).handleUnmapSubwindows(this, msg); break;
case 12: getWindow(msg.mData.deqInt()).handleConfigureWindow(this, msg); break;
case 14: getDrawable(msg.mData.deqInt()).handleGetGeometry(this, msg); break;
case 16: mServer.handleInternAtom(this, msg); break;
case 18: getWindow(msg.mData.deqInt()).handleChangeProperty(this, msg); break;
case 19: getWindow(msg.mData.deqInt()).handleDeleteProperty(this, msg); break;
case 20: getWindow(msg.mData.deqInt()).handleGetProperty(this, msg); break;
case 21: getWindow(msg.mData.deqInt()).handleListProperties(this, msg); break;
case 22: mServer.handleSetSelectionOwner(this, msg); break;
case 23: mServer.handleGetSelectionOwner(this, msg); break;
case 36: mServer.handleGrabServer(this, msg); break;
case 37: mServer.handleUngrabServer(this, msg); break;
case 38: mServer.mKeyboard.handleQueryPointer(this, msg); break;
case 40: getWindow(msg.mData.deqInt()).handleTranslateCoordinates(this, msg); break;
case 42: mServer.handleSetInputFocus(this, msg); break;
case 43: mServer.handleGetInputFocus(this, msg); break;
case 45: mServer.handleOpenFont(this, msg); break;
case 46: getFont(msg.mData.deqInt()).handleCloseFont(this, msg); break;
case 47: getFontable(msg.mData.deqInt()).handleQueryFont(this, msg); break;
case 49: mServer.handleListFonts(this, msg); break;
case 50: mServer.handleListFontsWithInfo(this, msg); break;
case 53: X11Pixmap.create(this, msg); break;
case 54: getPixmap(msg.mData.deqInt()).handleFreePixmap(this, msg); break;
case 55: X11GContext.create(this, msg); break;
case 56: getGContext(msg.mData.deqInt()).handleChangeGC(this, msg); break;
case 59: getGContext(msg.mData.deqInt()).handleSetClipRectangles(this, msg); break;
case 60: getGContext(msg.mData.deqInt()).handleFreeGC(this, msg); break;
case 61: getWindow(msg.mData.deqInt()).handleClearArea(this, msg); break;
case 62: getDrawable(msg.mData.deqInt()).handleCopyArea(this, msg); break;
case 64: getDrawable(msg.mData.deqInt()).handlePolyPoint(this, msg); break;
case 65: getDrawable(msg.mData.deqInt()).handlePolyLine(this, msg); break;
case 66: getDrawable(msg.mData.deqInt()).handlePolySegment(this, msg); break;
case 67: getDrawable(msg.mData.deqInt()).handlePolyRectangle(this, msg); break;
case 68: getDrawable(msg.mData.deqInt()).handlePolyArc(this, msg); break;
case 69: getDrawable(msg.mData.deqInt()).handleFillPoly(this, msg); break;
case 70: getDrawable(msg.mData.deqInt()).handlePolyFillRectangle(this, msg); break;
case 71: getDrawable(msg.mData.deqInt()).handlePolyFillArc(this, msg); break;
case 72: getDrawable(msg.mData.deqInt()).handlePutImage(this, msg); break;
case 73: getDrawable(msg.mData.deqInt()).handleGetImage(this, msg); break;
case 76: getDrawable(msg.mData.deqInt()).handleImageText8(this, msg); break;
case 78: X11Colormap.create(this, msg); break;
case 92: getColormap(msg.mData.deqInt()).handleLookupColor(this, msg); break;
case 93: X11PixmapCursor.create(this, msg); break;
case 94: X11GlyphCursor.create(this, msg); break;
case 95: getCursor(msg.mData.deqInt()).handleFreeCursor(this, msg); break;
case 96: getCursor(msg.mData.deqInt()).handleRecolorCursor(this, msg); break;
case 98: mServer.handleQueryExtension(this, msg); break;
case 101: mServer.mKeyboard.handleGetKeyboardMapping(this, msg); break;
case 104: mServer.mKeyboard.handleBell(this, msg); break;
case 119: mServer.mKeyboard.handleGetModifierMapping(this, msg); break;
default : throw new X11Error(X11Error.IMPLEMENTATION, msg.requestType());
}
}
catch (X11Error e) {
Log.e(XServer.TAG, "X11Error: " + e.name());
e.printStackTrace();
X11ErrorMessage err = new X11ErrorMessage(mProt.mEndian, e.mCode);
err.mData.enqInt(e.mVal);
err.mData.enqShort(msg.requestType() <= 127 ? 0 : msg.headerData());
err.mData.enqShort(msg.requestType());
send(err);
close(); //XXX: for debugging
}
catch (Exception e) {
Log.e(XServer.TAG, "Exception: ");
e.printStackTrace();
close();
}
}
public void onMessage(X11ReplyMessage msg) { close(); }
public void onMessage(X11EventMessage msg) { close(); }
public void onMessage(X11ErrorMessage msg) { close(); }
}

View File

@ -0,0 +1,19 @@
package tdm.xserver;
class X11Color
{
short r;
short g;
short b;
void set(byte _r, byte _g, byte _b) {
r = (short)(_r << 8 | _r);
g = (short)(_g << 8 | _g);
b = (short)(_b << 8 | _b);
}
void set(short _r, short _g, short _b) {
r = _r;
g = _g;
b = _b;
}
}

View File

@ -0,0 +1,124 @@
package tdm.xserver;
import android.content.res.AssetManager;
import android.util.Log;
import java.util.Map;
import java.util.HashMap;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
class X11Colormap extends X11Resource
{
static Map<String,X11Color> rgbmap;
static void globalInit(X11Server server) {
rgbmap = new HashMap<String,X11Color>();
BufferedReader br;
String s;
try {
AssetManager am = server.mContext.getAssets();
br = new BufferedReader(new InputStreamReader(am.open("rgb.txt")));
while ((s = br.readLine()) != null) {
if (s.length() == 0 || s.charAt(0) == '#' || s.charAt(0) == '!') {
continue;
}
String[] fields = s.trim().split("[ \t]{1,}", 4);
if (fields.length != 4) {
Log.d("X", "Bad line in rgb.txt: " + s);
continue;
}
X11Color color = new X11Color();
byte r = (byte)(Short.parseShort(fields[0]) & 0xff);
byte g = (byte)(Short.parseShort(fields[1]) & 0xff);
byte b = (byte)(Short.parseShort(fields[2]) & 0xff);
color.set(r, g, b);
String name = fields[3].toLowerCase();
rgbmap.put(name, color);
}
}
catch (Exception e) {
Log.e("X", "Failed to init RGB data from assets");
}
try {
br = new BufferedReader(new InputStreamReader(new FileInputStream("/data/local/rgb.txt")));
while ((s = br.readLine()) != null) {
if (s.length() == 0 || s.charAt(0) == '#' || s.charAt(0) == '!') {
continue;
}
String[] fields = s.trim().split("[ \t]{1,}", 4);
if (fields.length != 4) {
Log.d("X", "Bad line in rgb.txt: " + s);
continue;
}
X11Color color = new X11Color();
byte r = (byte)(Short.parseShort(fields[0]) & 0xff);
byte g = (byte)(Short.parseShort(fields[1]) & 0xff);
byte b = (byte)(Short.parseShort(fields[2]) & 0xff);
color.set(r, g, b);
String name = fields[3].toLowerCase();
rgbmap.put(name, color);
}
}
catch (Exception e) {
Log.e("X", "Failed to init RGB data from dir");
e.printStackTrace();
}
}
static void create(X11Client c, X11RequestMessage msg) throws X11Error {
int id = msg.mData.deqInt();
X11Colormap r = new X11Colormap(id);
r.handleCreate(c, msg);
c.addResource(r);
}
int mVisual;
byte mAlloc;
X11Colormap(int id) {
super(X11Resource.COLORMAP, id);
}
void destroy() {
super.destroy();
}
void initDefault() {
mVisual = X11Visual.NONE;
mAlloc = (byte)1; // All
}
void handleCreate(X11Client c, X11RequestMessage msg) {
//XXX: parse and handle window and visual
mVisual = X11Visual.NONE;
mAlloc = (byte)1; // All
}
void handleLookupColor(X11Client c, X11RequestMessage msg) throws X11Error {
short len = msg.mData.deqShort();
msg.mData.deqSkip(2);
String name = msg.mData.deqString(len);
Log.d("X", "LookupColor: name=<"+name+">");
X11Color color = rgbmap.get(name);
if (color == null) {
throw new X11Error(X11Error.NAME, 92 /* LookupColor */); //XXX???
}
X11ReplyMessage reply = new X11ReplyMessage(msg);
reply.mData.enqShort(color.r);
reply.mData.enqShort(color.g);
reply.mData.enqShort(color.b);
reply.mData.enqShort(color.r);
reply.mData.enqShort(color.g);
reply.mData.enqShort(color.b);
c.send(reply);
}
}

View File

@ -0,0 +1,103 @@
package tdm.xserver;
class X11Cursor extends X11Resource
{
X11Color mFgColor;
X11Color mBgColor;
X11Cursor(int id) {
super(X11Resource.CURSOR, id);
}
void destroy() {
mBgColor = null;
mFgColor = null;
super.destroy();
}
void handleFreeCursor(X11Client c, X11RequestMessage msg) {
c.delResource(mId);
}
void handleRecolorCursor(X11Client c, X11RequestMessage msg) {
doRecolor(msg.mData);
}
void doRecolor(ByteQueue q) {
mFgColor = new X11Color();
mFgColor.r = q.deqShort();
mFgColor.g = q.deqShort();
mFgColor.b = q.deqShort();
mBgColor = new X11Color();
mBgColor.r = q.deqShort();
mBgColor.g = q.deqShort();
mBgColor.b = q.deqShort();
}
}
class X11PixmapCursor extends X11Cursor
{
static void create(X11Client c, X11RequestMessage msg) throws X11Error {
int id = msg.mData.deqInt();
X11PixmapCursor r = new X11PixmapCursor(id);
r.handleCreate(c, msg);
c.addResource(r);
}
X11Pixmap mSource;
X11Pixmap mMask;
X11Point mHotSpot;
X11PixmapCursor(int id) {
super(id);
}
void destroy() {
mHotSpot = null;
mMask = null;
mSource = null;
super.destroy();
}
void handleCreate(X11Client c, X11RequestMessage msg) throws X11Error {
mSource = c.getPixmap(msg.mData.deqInt());
mMask = c.getPixmap(msg.mData.deqInt());
doRecolor(msg.mData);
mHotSpot = new X11Point();
mHotSpot.x = msg.mData.deqShort();
mHotSpot.y = msg.mData.deqShort();
}
}
class X11GlyphCursor extends X11Cursor
{
static void create(X11Client c, X11RequestMessage msg) throws X11Error {
int id = msg.mData.deqInt();
X11GlyphCursor r = new X11GlyphCursor(id);
r.handleCreate(c, msg);
c.addResource(r);
}
X11Font mSource;
short mSourceChar;
X11Font mMask;
short mMaskChar;
X11GlyphCursor(int id) {
super(id);
}
void destroy() {
mMask = null;
mSource = null;
super.destroy();
}
void handleCreate(X11Client c, X11RequestMessage msg) throws X11Error {
mSource = c.getFont(msg.mData.deqInt());
mMask = c.getFont(msg.mData.deqInt());
mSourceChar = msg.mData.deqShort();
mMaskChar = msg.mData.deqShort();
doRecolor(msg.mData);
}
}

View File

@ -0,0 +1,14 @@
package tdm.xserver;
import java.util.ArrayList;
class X11Depth
{
byte depth;
ArrayList<X11Visual> visuals;
X11Depth(byte d) {
depth = d;
visuals = new ArrayList<X11Visual>();
}
}

View File

@ -0,0 +1,314 @@
package tdm.xserver;
import android.util.Log;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.Paint;
import java.util.ArrayList;
abstract class X11Drawable extends X11Resource
{
byte mDepth;
byte mBPP;
X11Rect mRect;
short mBorderWidth; // This is here for GetGeometry
X11Visual mVisual; // This is here for GetImage
Bitmap mBitmap;
Canvas mCanvas;
X11Drawable(int type, int id) {
super(type, id);
}
void destroy() {
mCanvas = null;
mBitmap = null;
mVisual = null;
mRect = null;
super.destroy();
}
void handleGetGeometry(X11Client c, X11RequestMessage msg) {
X11ReplyMessage reply = new X11ReplyMessage(msg);
reply.headerData(mDepth);
reply.mData.enqInt(c.mServer.mDefaultScreen.mRoot.mId);
reply.mData.enqShort(mRect.x);
reply.mData.enqShort(mRect.y);
reply.mData.enqShort(mRect.w);
reply.mData.enqShort(mRect.h);
reply.mData.enqShort(mBorderWidth);
c.send(reply);
}
void handleCopyArea(X11Client c, X11RequestMessage msg) throws X11Error {
X11Drawable dst = c.getDrawable(msg.mData.deqInt());
X11GContext gc = c.getGContext(msg.mData.deqInt());
short src_x = msg.mData.deqShort();
short src_y = msg.mData.deqShort();
short dst_x = msg.mData.deqShort();
short dst_y = msg.mData.deqShort();
short w = msg.mData.deqShort();
short h = msg.mData.deqShort();
//XXX: Use Canvas.drawBitmap with a clip mask?
//XXX: handle window with tiled background
short x, y;
for (y = 0; y < h; ++y) {
for (x = 0; x < w; ++x) {
int pixel = mBitmap.getPixel(src_x + x, src_y + y);
dst.mBitmap.setPixel(dst_x + x, dst_y + y, pixel);
}
}
}
void handlePolyPoint(X11Client c, X11RequestMessage msg) throws X11Error {
X11GContext gc = c.getGContext(msg.mData.deqInt());
int count = msg.mData.remain()/4;
float[] points = new float[count*2];
for (int n = 0; n < count; ++n) {
points[n*2+0] = (float)msg.mData.deqShort();
points[n*2+1] = (float)msg.mData.deqShort();
}
mCanvas.drawPoints(points, gc.mPaint);
postRender();
}
void handlePolyLine(X11Client c, X11RequestMessage msg) throws X11Error {
byte coord_mode = msg.headerData();
X11GContext gc = c.getGContext(msg.mData.deqInt());
int count = msg.mData.remain()/4 - 1;
float[] points = new float[count*4];
float lastx = (float)msg.mData.deqShort();
float lasty = (float)msg.mData.deqShort();
for (int n = 0; n < count; ++n) {
points[n*4+0] = lastx;
points[n*4+1] = lasty;
points[n*4+2] = (float)msg.mData.deqShort();
points[n*4+3] = (float)msg.mData.deqShort();
if (coord_mode == 0 /* Origin */) {
lastx = points[n*4+2];
lasty = points[n*4+3];
}
else {
lastx += points[n*4+2];
lasty += points[n*4+3];
}
}
mCanvas.drawLines(points, gc.mPaint);
postRender();
}
void handlePolySegment(X11Client c, X11RequestMessage msg) throws X11Error {
X11GContext gc = c.getGContext(msg.mData.deqInt());
int count = msg.mData.remain()/8;
float[] points = new float[count*4];
for (int n = 0; n < count; ++n) {
points[n*4+0] = (float)msg.mData.deqShort();
points[n*4+1] = (float)msg.mData.deqShort();
points[n*4+2] = (float)msg.mData.deqShort();
points[n*4+3] = (float)msg.mData.deqShort();
}
mCanvas.drawLines(points, gc.mPaint);
postRender();
}
void handlePolyRectangle(X11Client c, X11RequestMessage msg) throws X11Error {
X11GContext gc = c.getGContext(msg.mData.deqInt());
while (msg.mData.remain() > 0) {
short x = msg.mData.deqShort();
short y = msg.mData.deqShort();
short w = msg.mData.deqShort();
short h = msg.mData.deqShort();
mCanvas.drawRect(x, y, x+w, y+h, gc.mPaint);
}
postRender();
}
void handlePolyArc(X11Client c, X11RequestMessage msg) throws X11Error {
X11GContext gc = c.getGContext(msg.mData.deqInt());
int count = msg.mData.remain()/12;
float[] points = new float[count*6];
for (int n = 0; n < count; ++n) {
points[n*4+0] = (float)msg.mData.deqShort();
points[n*4+1] = (float)msg.mData.deqShort();
points[n*4+2] = (float)msg.mData.deqShort();
points[n*4+3] = (float)msg.mData.deqShort();
points[n*4+4] = (float)msg.mData.deqShort();
points[n*4+5] = (float)msg.mData.deqShort();
}
throw new X11Error(X11Error.IMPLEMENTATION, 0);
}
void handleFillPoly(X11Client c, X11RequestMessage msg) throws X11Error {
X11GContext gc = c.getGContext(msg.mData.deqInt());
byte shape = msg.mData.deqByte();
byte coord_mode = msg.mData.deqByte();
msg.mData.deqSkip(2);
Path path = new Path();
path.setFillType(Path.FillType.WINDING); //XXX: ???
float x, y;
x = (float)msg.mData.deqShort();
y = (float)msg.mData.deqShort();
path.moveTo(x, y);
while (msg.mData.remain() > 0) {
x = (float)msg.mData.deqShort();
y = (float)msg.mData.deqShort();
path.lineTo(x, y);
}
mCanvas.drawPath(path, gc.mPaint);
postRender();
}
void handlePolyFillRectangle(X11Client c, X11RequestMessage msg) throws X11Error {
X11GContext gc = c.getGContext(msg.mData.deqInt());
Paint.Style oldstyle = gc.mPaint.getStyle();
gc.mPaint.setStyle(Paint.Style.FILL);
while (msg.mData.remain() > 0) {
short x = msg.mData.deqShort();
short y = msg.mData.deqShort();
short w = msg.mData.deqShort();
short h = msg.mData.deqShort();
mCanvas.drawRect(x, y, x+w, y+h, gc.mPaint);
}
gc.mPaint.setStyle(oldstyle);
postRender();
}
void handlePolyFillArc(X11Client c, X11RequestMessage msg) throws X11Error {
throw new X11Error(X11Error.IMPLEMENTATION, msg.requestType());
}
void handlePutImage(X11Client c, X11RequestMessage msg) throws X11Error {
byte fmt = msg.headerData();
X11GContext gc = c.getGContext(msg.mData.deqInt());
X11Rect rect = new X11Rect();
rect.w = msg.mData.deqShort();
rect.h = msg.mData.deqShort();
rect.x = msg.mData.deqShort();
rect.y = msg.mData.deqShort();
byte left_pad = msg.mData.deqByte();
byte depth = msg.mData.deqByte();
msg.mData.deqSkip(2);
if (fmt == 0 /* Bitmap */) {
if (depth != (byte)1) {
throw new X11Error(X11Error.VALUE, depth);
}
fmt = (byte)1 /* XYPixmap */;
}
ArrayList<X11Format> formats = c.mServer.getPixmapFormats();
X11Format f = null;
for (X11Format cur : formats) {
if (cur.mDepth == depth) {
f = cur;
break;
}
}
//XXX: handle not found
Bitmap bmp;
switch (fmt) {
case 1 /* XYPixmap */:
bmp = f.decodeImageXY(rect.w, rect.h, msg.mData);
break;
case 2 /* ZPixmap */ :
bmp = f.decodeImageZ(rect.w, rect.h, msg.mData);
break;
default:
throw new X11Error(X11Error.VALUE, fmt);
}
mCanvas.drawBitmap(bmp, rect.x, rect.y, null);
postRender(rect);
}
void handleGetImage(X11Client c, X11RequestMessage msg) throws X11Error {
byte fmt = msg.headerData();
X11Rect rect = new X11Rect();
rect.x = msg.mData.deqShort();
rect.y = msg.mData.deqShort();
rect.w = msg.mData.deqShort();
rect.h = msg.mData.deqShort();
int plane_mask = msg.mData.deqInt();
ArrayList<X11Format> formats = c.mServer.getPixmapFormats();
X11Format f = null;
for (X11Format cur : formats) {
if (cur.mDepth == mDepth) {
f = cur;
break;
}
}
//XXX: handle not found
byte[] data;
switch (fmt) {
case 1 /* XYPixmap */:
data = f.encodeImageXY(rect, plane_mask, mBitmap);
break;
case 2 /* ZPixmap */ :
data = f.encodeImageZ(rect, plane_mask, mBitmap);
break;
default:
throw new X11Error(X11Error.VALUE, fmt);
}
X11ReplyMessage reply = new X11ReplyMessage(msg);
reply.headerData(mDepth);
//XXX: This looks ugly, is it the best way?
reply.mData.enqInt( (mVisual == null ? 0 : mVisual.mId) );
reply.mData.enqSkip(20);
reply.mData.enqArray(data);
c.send(reply);
}
void handleImageText8(X11Client c, X11RequestMessage msg) throws X11Error {
byte len = msg.headerData();
X11GContext gc = c.getGContext(msg.mData.deqInt());
short x = msg.mData.deqShort();
short y = msg.mData.deqShort();
String text = msg.mData.deqString(len);
short x_min, x_max, y_min, y_max;
x_min = x;
x_max = x;
y_min = y;
y_max = y;
for (int idx = 0; idx < text.length(); ++idx) {
char ch = text.charAt(idx);
X11CharInfo info = gc.mFont.getCharInfo(ch);
Bitmap bmp = gc.mFont.getCharImage(ch, gc.mForePixel, gc.mBackPixel);
if (bmp != null) {
//XXX: This is probably not correct
mCanvas.drawBitmap(bmp, x, y-bmp.getHeight(), null);
}
x += info.character_width;
x_max += info.character_width;
y_min = (short)Math.min(y_min, y-bmp.getHeight());
//y_max = (short)Math.max(y_max, y+info.descent);
}
X11Rect r = new X11Rect();
r.x = x_min;
r.w = (short)(x_max - x_min);
r.y = y_min;
r.h = (short)(y_max - y_min);
postRender(r);
}
void postRender(X11Rect r) {}
void postRender() {}
}

View File

@ -0,0 +1,54 @@
package tdm.xserver;
class X11Error extends Throwable
{
static final byte NONE = (byte)0;
static final byte REQUEST = (byte)1;
static final byte VALUE = (byte)2;
static final byte WINDOW = (byte)3;
static final byte PIXMAP = (byte)4;
static final byte ATOM = (byte)5;
static final byte CURSOR = (byte)6;
static final byte FONT = (byte)7;
static final byte MATCH = (byte)8;
static final byte DRAWABLE = (byte)9;
static final byte ACCESS = (byte)10;
static final byte ALLOC = (byte)11;
static final byte COLORMAP = (byte)12;
static final byte GCONTEXT = (byte)13;
static final byte IDCHOICE = (byte)14;
static final byte NAME = (byte)15;
static final byte LENGTH = (byte)16;
static final byte IMPLEMENTATION = (byte)17;
static final String[] error_names = {
"NONE",
"Request",
"Value",
"Window",
"Pixmap",
"Atom",
"Cursor",
"Font",
"Match",
"Drawable",
"Access",
"Alloc",
"Colormap",
"GContext",
"IDChoice",
"Name",
"Length",
"Implementation"
};
String name() { return error_names[mCode]; }
byte mCode;
int mVal;
X11Error(byte code, int val) {
mCode = code;
mVal = val;
}
}

View File

@ -0,0 +1,57 @@
package tdm.xserver;
import java.nio.ByteOrder;
class X11ErrorMessage extends X11Message
{
static final String[] message_names = {
"NONE", // 0
"Request",
"Value",
"Window",
"Pixmap",
"Atom",
"Cursor",
"Font",
"Match",
"Drawable",
"Access", // 10
"Alloc",
"Colormap",
"GContext",
"IDChoice",
"Name",
"Length",
"Implementation"
};
String name() { return message_names[mHeaderData]; }
byte mHeaderData;
short mSeqNo;
X11ErrorMessage(ByteOrder endian, byte code) {
super(endian);
mHeaderData = code;
}
void read(ByteQueue q) {
byte event_type = q.deqByte(); // 0x00
mHeaderData = q.deqByte();
mSeqNo = q.deqShort();
mData = q.deqData(28);
}
void write(ByteQueue q) {
q.enqByte((byte)0x00);
q.enqByte(mHeaderData);
q.enqShort(mSeqNo);
q.enqData(mData);
q.enqSkip(28 - mData.pos());
}
void dispatch(X11ProtocolHandler h) { h.onMessage(this); }
void headerData(byte val) { mHeaderData = val; }
byte headerData() { return mHeaderData; }
void seqno(short val) { mSeqNo = val; }
short seqno() { return mSeqNo; }
}

View File

@ -0,0 +1,40 @@
package tdm.xserver;
class X11Event
{
static final byte ERROR = 0; // pseudo-event
static final byte REPLY = 1; // pseudo-event
static final byte KEY_PRESS = 2;
static final byte KEY_RELEASE = 3;
static final byte BUTTON_PRESS = 4;
static final byte BUTTON_RELEASE = 5;
static final byte MOTION_NOTIFY = 6;
static final byte ENTER_NOTIFY = 7;
static final byte LEAVE_NOTIFY = 8;
static final byte FOCUS_IN = 9;
static final byte FOCUS_OUT = 10;
static final byte KEYMAP_NOTIFY = 11;
static final byte EXPOSE = 12;
static final byte GRAPHICS_EXPOSE = 13;
static final byte NO_EXPOSE = 14;
static final byte VISIBILITY_NOTIFY = 15;
static final byte CREATE_NOTIFY = 16;
static final byte DESTROY_NOTIFY = 17;
static final byte UNMAP_NOTIFY = 18;
static final byte MAP_NOTIFY = 19;
static final byte MAP_REQUEST = 20;
static final byte REPARENT_NOTIFY = 21;
static final byte CONFIGURE_NOTIFY = 22;
static final byte CONFIGURE_REQUEST = 23;
static final byte GRAVITY_NOTIFY = 24;
static final byte RESIZE_REQUEST = 25;
static final byte CIRCULATE_NOTIFY = 26;
static final byte CIRCULATE_REQUEST = 27;
static final byte PROPERTY_NOTIFY = 28;
static final byte SELECTION_CLEAR = 29;
static final byte SELECTION_REQUEST = 30;
static final byte SELECTION_NOTIFY = 31;
static final byte COLORMAP_NOTIFY = 32;
static final byte CLIENT_MESSAGE = 33;
static final byte MAPPING_NOTIFY = 34;
}

View File

@ -0,0 +1,76 @@
package tdm.xserver;
import java.nio.ByteOrder;
class X11EventMessage extends X11Message
{
static final String[] message_names = {
"NONE",
"NONE",
"KeyPress",
"KeyRelease",
"ButtonPress",
"ButtonRelease",
"MotionNotify",
"EnterNotify",
"LeaveNotify",
"FocusIn",
"FocusOut", // 10
"KeymapNotify",
"Expose",
"GraphicsExposure",
"NoExposure",
"VisibilityNotify",
"CreateNotify",
"DestroyNotify",
"UnmapNotify",
"MapNotify",
"MapRequest", // 20
"ReparentNotify",
"ConfigureNotify",
"ConfigureRequest",
"GravityNotify",
"ResizeRequest",
"CirculateNotify",
"CirculateRequest",
"PropertyNotify",
"SelectionClear",
"SelectionRequest", // 30
"SelectionNotify",
"ColormapNotify",
"ClientMessage",
"MappingNotify"
};
String name() { return message_names[mEventType]; }
byte mEventType;
byte mHeaderData;
short mSeqNo;
X11EventMessage(ByteOrder endian, byte evtype) {
super(endian);
mEventType = evtype;
mData.resize(28);
}
void read(ByteQueue q) {
mEventType = q.deqByte();
mHeaderData = q.deqByte();
mSeqNo = q.deqShort();
mData = q.deqData(28);
}
void write(ByteQueue q) {
q.enqByte(mEventType);
q.enqByte(mHeaderData);
q.enqShort(mSeqNo);
q.enqData(mData);
q.enqSkip(28 - mData.pos());
}
void dispatch(X11ProtocolHandler h) { h.onMessage(this); }
void headerData(byte val) { mHeaderData = val; }
byte headerData() { return mHeaderData; }
void seqno(short val) { mSeqNo = val; }
short seqno() { return mSeqNo; }
}

View File

@ -0,0 +1,103 @@
package tdm.xserver;
import android.util.Log;
import android.graphics.Bitmap;
import android.graphics.Color;
import java.util.HashMap;
import java.util.Map;
class X11Font extends X11Fontable
{
class CacheKey {
char ch;
int fore;
int back;
CacheKey(char c, int f, int b) { ch = c; fore = f; back = b; }
public int hashCode() { return ((ch << 24) | (fore ^ back)); }
public boolean equals(Object obj) {
CacheKey other = (CacheKey)obj;
return (other.ch == ch && other.fore == fore && other.back == back);
}
}
FontData mData;
Map<CacheKey,Bitmap> mImageCache;
X11Font(int id, FontData data) {
super(X11Resource.FONT, id);
mData = data;
mImageCache = new HashMap<CacheKey,Bitmap>();
}
void destroy() {
mImageCache = null;
mData = null;
super.destroy();
}
void handleCloseFont(X11Client c, X11RequestMessage msg) {
c.delResource(mId);
}
void handleQueryFont(X11Client c, X11RequestMessage msg) {
X11ReplyMessage reply = new X11ReplyMessage(msg);
mData.enqueueInfo(reply.mData);
mData.enqueueCharInfoCount(reply.mData);
mData.enqueueProperties(reply.mData);
mData.enqueueCharInfo(reply.mData);
c.send(reply);
}
X11CharInfo getCharInfo(char ch) {
return mData.mCharInfos.get(ch);
}
Bitmap getCharImage(char ch, int fore, int back) {
CacheKey key = new CacheKey(ch, fore, back);
Bitmap bmp = mImageCache.get(key);
if (bmp == null) {
Bitmap glyph = mData.getBitmap(ch);
if (glyph == null) {
return null;
}
int w = glyph.getWidth();
int h = glyph.getHeight();
bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
int x, y;
for (y = 0; y < h; ++y) {
for (x = 0; x < w; ++x) {
int pixel = glyph.getPixel(x, y);
bmp.setPixel(x, y, (pixel == Color.BLACK ? back : fore));
}
}
mImageCache.put(key, bmp);
}
return bmp;
}
void showBitmap(char ch) {
Log.d("X", "Show bitmap for <"+ch+">...");
X11CharInfo info = getCharInfo(ch);
Bitmap bmp = mData.getBitmap(ch);
if (bmp == null) {
Log.d("X", " (null)");
return;
}
Log.d("X", " w="+bmp.getWidth()+", h="+bmp.getHeight()+", a="+info.ascent+", d="+info.descent+" ...");
for (int y = 0; y < bmp.getHeight(); ++y) {
StringBuffer buf = new StringBuffer();
for (int x = 0; x < bmp.getWidth(); ++x) {
if (bmp.getPixel(x, y) == Color.BLACK) {
buf.append(".");
}
else {
buf.append("X");
}
}
Log.d("X", " " + buf);
}
}
}

View File

@ -0,0 +1,7 @@
package tdm.xserver;
class X11FontProp
{
int name;
int value;
}

View File

@ -0,0 +1,14 @@
package tdm.xserver;
abstract class X11Fontable extends X11Resource
{
X11Fontable(int t, int n) {
super(t, n);
}
void destroy() {
super.destroy();
}
abstract void handleQueryFont(X11Client c, X11RequestMessage msg);
}

View File

@ -0,0 +1,332 @@
package tdm.xserver;
import android.graphics.Bitmap;
abstract class X11Format
{
byte mDepth;
byte mBPP;
byte mPad;
X11Format(byte d, byte b, byte p) {
mDepth = d;
mBPP = b;
mPad = p;
}
protected void decodePlane(short w, short h, byte[] buf, byte plane, Bitmap bmp) {
int plane_shift = (mDepth - plane);
int bytes_per_row = MathX.divceil(w, 8);
int bytes_per_plane = h * bytes_per_row;
short y, x;
int plane_off, y_off, x_off;
byte val;
plane_off = plane * bytes_per_plane;
for (y = 0; y < h; ++y) {
y_off = y * bytes_per_row;
for (x = 0; x < w; ++x) {
x_off = x/8;
val = buf[plane_off + y_off + x_off];
int shift = (7 - (x%8));
int tmp = bmp.getPixel(x, y);
tmp |= ((val >> shift) & 0x1) << plane_shift;
bmp.setPixel(x, y, tmp);
}
}
}
Bitmap decodeImageXY(short w, short h, ByteQueue q) {
byte[] buf = q.deqArray(w*h*mBPP/8); //XXX
Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
byte plane;
for (plane = 0; plane < mBPP; ++plane) {
decodePlane(w, h, buf, plane, bmp);
}
return bmp;
}
abstract Bitmap decodeImageZ(short w, short h, ByteQueue q);
protected void encodePlane(X11Rect r, Bitmap bmp, byte plane, byte[] buf) {
int plane_shift = (mDepth - plane);
int bytes_per_row = MathX.divceil(r.w, 8);
int bytes_per_plane = r.h * bytes_per_row;
short y, x;
int plane_off, y_off, x_off;
int val;
plane_off = plane * bytes_per_plane;
for (y = 0; y < r.h; ++y) {
y_off = y * bytes_per_row;
for (x = 0; x < r.w; ++x) {
x_off = x/8;
val = bmp.getPixel(r.x + x, r.y + y);
int shift = (7 - (x%8));
byte v;
v = buf[plane_off + y_off + x_off];
v |= ((val >> plane_shift) & 0x1) << shift;
buf[plane_off + y_off + x_off] = v;
}
}
}
byte[] encodeImageXY(X11Rect r, int plane_mask, Bitmap bmp) {
byte[] buf = new byte[r.w*r.h*mBPP/8]; //XXX
byte plane;
for (plane = 0; plane < mBPP; ++plane) {
if ((plane_mask & (1 << plane)) != 0) {
encodePlane(r, bmp, plane, buf);
}
}
return buf;
}
abstract byte[] encodeImageZ(X11Rect r, int plane_mask, Bitmap bmp);
}
class X11Format_1 extends X11Format
{
X11Format_1() { super((byte)1, (byte)1, (byte)8); }
Bitmap decodeImageZ(short w, short h, ByteQueue q) {
int bytes_per_row = MathX.divceil(w, 8);
byte[] buf = q.deqArray(bytes_per_row*h);
Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
decodePlane(w, h, buf, (byte)0, bmp);
return bmp;
}
byte[] encodeImageZ(X11Rect r, int plane_mask, Bitmap bmp) {
int bytes_per_row = MathX.divceil(r.w, 8);
byte[] buf = new byte[bytes_per_row*r.h];
if ((plane_mask & 1) != 0) {
encodePlane(r, bmp, (byte)0, buf);
}
return buf;
}
}
class X11Format_4 extends X11Format
{
X11Format_4() { super((byte)4, (byte)4, (byte)8); }
Bitmap decodeImageZ(short w, short h, ByteQueue q) {
int bytes_per_row = MathX.divceil(w, 2);
byte[] buf = q.deqArray(bytes_per_row*h);
Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
short y, x;
int y_off, x_off;
int val;
for (y = 0; y < h; ++y) {
y_off = y * bytes_per_row;
for (x = 0; x < w; ++x) {
x_off = x/2;
val = buf[y_off + x_off];
bmp.setPixel(x, y, (val >> (4*(1-x%2))) & 0xf);
}
}
return bmp;
}
byte[] encodeImageZ(X11Rect r, int plane_mask, Bitmap bmp) {
int bytes_per_row = MathX.divceil(r.w, 2);
byte[] buf = new byte[bytes_per_row*r.h];
short y, x;
int y_off, x_off;
int val;
for (y = 0; y < r.h; ++y) {
y_off = y * bytes_per_row;
for (x = 0; x < r.w; ++x) {
x_off = x/2;
val = bmp.getPixel(r.x + x, r.y + y) & 0xf;
val &= plane_mask;
buf[y_off + x_off] = (byte)(val << 4*(1-x%2));
}
}
return buf;
}
}
class X11Format_8 extends X11Format
{
X11Format_8() { super((byte)8, (byte)8, (byte)8); }
Bitmap decodeImageZ(short w, short h, ByteQueue q) {
byte[] buf = q.deqArray(w*h);
Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
short y, x;
int y_off, x_off;
int val;
for (y = 0; y < h; ++y) {
y_off = y * w;
for (x = 0; x < w; ++x) {
x_off = x;
val = buf[y_off + x_off];
bmp.setPixel(x, y, val);
}
}
return bmp;
}
byte[] encodeImageZ(X11Rect r, int plane_mask, Bitmap bmp) {
int bytes_per_row = r.w;
byte[] buf = new byte[bytes_per_row*r.h];
short y, x;
int y_off, x_off;
int val;
for (y = 0; y < r.h; ++y) {
y_off = y * bytes_per_row;
for (x = 0; x < r.w; ++x) {
x_off = x;
val = bmp.getPixel(r.x + x, r.y + y);
val &= plane_mask;
buf[y_off + x_off] = (byte)val;
}
}
return buf;
}
}
class X11Format_16 extends X11Format
{
X11Format_16() { super((byte)16, (byte)16, (byte)16); }
Bitmap decodeImageZ(short w, short h, ByteQueue q) {
int bytes_per_row = w*2;
byte[] buf = q.deqArray(bytes_per_row*h);
//XXX: This is wrong. Use RGB_565 and copyPixelsFromBuffer?
Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
short y, x;
int y_off, x_off;
int val;
for (y = 0; y < h; ++y) {
y_off = y * bytes_per_row;
for (x = 0; x < w; ++x) {
x_off = x*2;
val = (buf[y_off + x_off + 0] << 8) |
buf[y_off + x_off + 1];
bmp.setPixel(x, y, val);
}
}
return bmp;
}
byte[] encodeImageZ(X11Rect r, int plane_mask, Bitmap bmp) {
int bytes_per_row = r.w*2;
byte[] buf = new byte[bytes_per_row*r.h];
//XXX: This is wrong. Use RGB_565 and copyPixelsToBuffer?
short y, x;
int y_off, x_off;
int val;
for (y = 0; y < r.h; ++y) {
y_off = y * bytes_per_row;
for (x = 0; x < r.w; ++x) {
x_off = x*2;
val = bmp.getPixel(r.x + x, r.y + y);
val &= plane_mask;
buf[y_off + x_off + 0] = (byte)(val >> 8);
buf[y_off + x_off + 1] = (byte)val;
}
}
return buf;
}
}
class X11Format_24 extends X11Format
{
X11Format_24() { super((byte)24, (byte)32, (byte)32); }
Bitmap decodeImageZ(short w, short h, ByteQueue q) {
int bytes_per_row = w*4;
byte[] buf = q.deqArray(bytes_per_row*h);
//XXX: Use copyPixelsFromBuffer?
Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
short y, x;
int y_off, x_off;
int val;
for (y = 0; y < h; ++y) {
y_off = y * bytes_per_row;
for (x = 0; x < w; ++x) {
x_off = x*4;
val = (buf[y_off + x_off + 1] << 16) |
(buf[y_off + x_off + 2] << 8) |
buf[y_off + x_off + 3];
bmp.setPixel(x, y, 0xff000000 | val);
}
}
return bmp;
}
byte[] encodeImageZ(X11Rect r, int plane_mask, Bitmap bmp) {
int bytes_per_row = r.w*4;
byte[] buf = new byte[bytes_per_row*r.h];
//XXX: Use copyPixelsToBuffer?
short y, x;
int y_off, x_off;
int val;
for (y = 0; y < r.h; ++y) {
y_off = y * bytes_per_row;
for (x = 0; x < r.w; ++x) {
x_off = x*4;
val = bmp.getPixel(r.x + x, r.y + y);
val &= plane_mask;
buf[y_off + x_off + 0] = (byte)0;
buf[y_off + x_off + 1] = (byte)(val >> 16);
buf[y_off + x_off + 2] = (byte)(val >> 8);
buf[y_off + x_off + 3] = (byte)val;
}
}
return buf;
}
}
class X11Format_32 extends X11Format
{
X11Format_32() { super((byte)32, (byte)32, (byte)32); }
Bitmap decodeImageZ(short w, short h, ByteQueue q) {
int bytes_per_row = w*4;
byte[] buf = q.deqArray(bytes_per_row*h);
Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
short y, x;
int y_off, x_off;
int val;
for (y = 0; y < h; ++y) {
y_off = y * bytes_per_row;
for (x = 0; x < w; ++x) {
x_off = x*4;
//XXX: what about alpha?
val = (buf[y_off + x_off + 0] << 24) |
(buf[y_off + x_off + 1] << 16) |
(buf[y_off + x_off + 2] << 8) |
buf[y_off + x_off + 3];
bmp.setPixel(x, y, val);
}
}
return bmp;
}
byte[] encodeImageZ(X11Rect r, int plane_mask, Bitmap bmp) {
int bytes_per_row = r.w*4;
byte[] buf = new byte[bytes_per_row*r.h];
short y, x;
int y_off, x_off;
int val;
for (y = 0; y < r.h; ++y) {
y_off = y * bytes_per_row;
for (x = 0; x < r.w; ++x) {
x_off = x*4;
//XXX: what about alpha?
val = bmp.getPixel(r.x + x, r.y + y);
val &= plane_mask;
buf[y_off + x_off + 0] = (byte)(val >> 24);
buf[y_off + x_off + 1] = (byte)(val >> 16);
buf[y_off + x_off + 2] = (byte)(val >> 8);
buf[y_off + x_off + 3] = (byte)val;
}
}
return buf;
}
}

View File

@ -0,0 +1,179 @@
package tdm.xserver;
import android.util.Log;
import android.graphics.Paint;
import android.graphics.Path;
class X11GContext extends X11Fontable
{
static void create(X11Client c, X11RequestMessage msg) throws X11Error {
int id = msg.mData.deqInt();
X11GContext r = new X11GContext(id);
r.handleCreate(c, msg);
c.addResource(r);
}
Paint mPaint;
X11Drawable mDrawable;
byte mFunction;
int mPlaneMask;
int mForePixel;
int mBackPixel;
short mLineWidth;
byte mLineStyle;
byte mCapStyle;
byte mJoinStyle;
byte mFillStyle;
byte mFillRule;
X11Pixmap mTile;
X11Pixmap mStipple;
X11Point mTileStippleOrigin;
X11Font mFont;
byte mSubWindowMode;
byte mGraphicsExposures;
X11Point mClipOrigin;
X11Pixmap mClipMask;
Path mClipPath;
short mDashOffset;
byte mDashes;
byte mArcMode;
X11GContext(int n) {
super(X11Resource.GCONTEXT, n);
mPaint = new Paint();
mForePixel = 0xff000000;
mPaint.setColor(mForePixel);
mBackPixel = 0xffffffff;
mTileStippleOrigin = new X11Point();
}
void destroy() {
mClipPath = null;
mClipMask = null;
mClipOrigin = null;
mFont = null;
mTileStippleOrigin = null;
mStipple = null;
mTile = null;
mDrawable = null;
mPaint = null;
super.destroy();
}
void handleQueryFont(X11Client c, X11RequestMessage msg) {
mFont.handleQueryFont(c, msg);
}
void handleCreate(X11Client c, X11RequestMessage msg) throws X11Error {
int id = msg.mData.deqInt();
mDrawable = c.getDrawable(id);
doChangeAttributes(c, msg.mData);
}
void handleChangeGC(X11Client c, X11RequestMessage msg) throws X11Error {
doChangeAttributes(c, msg.mData);
}
void handleSetClipRectangles(X11Client c, X11RequestMessage msg) {
byte ordering = msg.headerData();
mClipOrigin = new X11Point();
mClipOrigin.x = msg.mData.deqShort();
mClipOrigin.y = msg.mData.deqShort();
mClipPath = new Path();
while (msg.mData.remain() > 0) {
short x = msg.mData.deqShort();
short y = msg.mData.deqShort();
short w = msg.mData.deqShort();
short h = msg.mData.deqShort();
mClipPath.addRect(x, y, x+w, y+h, Path.Direction.CW);
}
}
void handleFreeGC(X11Client c, X11RequestMessage msg) {
c.delResource(mId);
}
private void doChangeAttributes(X11Client c, ByteQueue q) throws X11Error {
int mask = q.deqInt();
int val;
if ((mask & 0x000001) != 0) { // function
mFunction = (byte)q.deqInt();
}
if ((mask & 0x000002) != 0) { // plane-mask
mPlaneMask = q.deqInt();
}
if ((mask & 0x000004) != 0) { // foreground
mForePixel = (q.deqInt() | 0xff000000);
mPaint.setColor(mForePixel);
}
if ((mask & 0x000008) != 0) { // background
mBackPixel = (q.deqInt() | 0xff000000);
}
if ((mask & 0x000010) != 0) { // line-width
mLineWidth = (short)q.deqInt();
}
if ((mask & 0x000020) != 0) { // line-style
mLineStyle = (byte)q.deqInt();
}
if ((mask & 0x000040) != 0) { // cap-style
mCapStyle = (byte)q.deqInt();
}
if ((mask & 0x000080) != 0) { // join-style
mJoinStyle = (byte)q.deqInt();
}
if ((mask & 0x000100) != 0) { // fill-style
mFillStyle = (byte)q.deqInt();
}
if ((mask & 0x000200) != 0) { // fill-rule
mFillRule = (byte)q.deqInt();
}
if ((mask & 0x000400) != 0) { // tile
mTile = c.getPixmap(q.deqInt());
}
if ((mask & 0x000800) != 0) { // stipple
mStipple = c.getPixmap(q.deqInt());
}
if ((mask & 0x001000) != 0) { // tile-stipple-x-origin
mTileStippleOrigin.x = (short)q.deqInt();
}
if ((mask & 0x002000) != 0) { // tile-stipple-y-origin
mTileStippleOrigin.y = (short)q.deqInt();
}
if ((mask & 0x004000) != 0) { // font
mFont = c.getFont(q.deqInt());
}
if ((mask & 0x008000) != 0) { // subwindow-mode
mSubWindowMode = (byte)q.deqInt();
}
if ((mask & 0x010000) != 0) { // graphics-exposures
mGraphicsExposures = (byte)q.deqInt();
}
if ((mask & 0x020000) != 0) { // clip-x-origin
mClipOrigin.x = (short)q.deqInt();
}
if ((mask & 0x040000) != 0) { // clip-y-origin
mClipOrigin.y = (short)q.deqInt();
}
if ((mask & 0x080000) != 0) { // clip-mask
val = q.deqInt();
if (val == X11Resource.NONE) {
mClipMask = null;
}
else {
mClipMask = c.getPixmap(val);
}
}
if ((mask & 0x100000) != 0) { // dash-offset
mDashOffset = (short)q.deqInt();
}
if ((mask & 0x200000) != 0) { // dashes
mDashes = (byte)q.deqInt();
}
if ((mask & 0x400000) != 0) { // arc-mode
mArcMode = (byte)q.deqInt();
}
}
}

View File

@ -0,0 +1,279 @@
package tdm.xserver;
import android.util.Log;
import android.media.AudioFormat;
import android.media.AudioTrack;
import android.media.AudioManager;
import android.view.KeyEvent;
import java.util.ArrayList;
class X11Keyboard
{
static final byte NO_SYMBOL = 0;
static final int X_MIN_KEYCODE = 8;
static final int NUM_KEYCODES = 100;
static final int NUM_MODIFIERS = 8;
/*
* NB: Many keycodes are undefined in v2.3.
* (ESCAPE, Fn keys, numpad, etc.)
*/
static final int[][] key_codes = {
{ 0, 0 }, // 0
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 }, // KEYCODE_CALL
{ 0, 0 }, // KEYCODE_ENDCALL
{ '0', ')' }, // KEYCODE_0
{ '1', '!' }, // KEYCODE_1
{ '2', '@' }, // KEYCODE_2
{ '3', '#' }, // KEYCODE_3 10
{ '4', '$' }, // KEYCODE_4
{ '5', '%' }, // KEYCODE_5
{ '6', '^' }, // KEYCODE_6
{ '7', '&' }, // KEYCODE_7
{ '8', '*' }, // KEYCODE_8
{ '9', '(' }, // KEYCODE_9
{ '*', '*' }, // KEYCODE_STAR (XXX: see KEYCODE_8)
{ '#', '#' }, // KEYCODE_POUND (XXX: see KEYCODE_3)
{ 0, 0 }, // KEYCODE_DPAD_UP
{ 0, 0 }, // KEYCODE_DPAD_DOWN 20
{ 0, 0 }, // KEYCODE_DPAD_LEFT
{ 0, 0 }, // KEYCODE_DPAD_RIGHT
{ 0xffe3, 0 }, // KEYCODE_DPAD_CENTER (=> L.CTRL)
{ 0, 0 }, // KEYCODE_VOLUME_UP
{ 0, 0 }, // KEYCODE_VOLUME_DOWN
{ 0, 0 }, // KEYCODE_POWER
{ 0, 0 }, // KEYCODE_CAMERA
{ 0, 0 }, // KEYCODE_CLEAR
{ 'a', 'A' }, // KEYCODE_A
{ 'b', 'B' }, // KEYCODE_B 30
{ 'c', 'C' }, // KEYCODE_C
{ 'd', 'D' }, // KEYCODE_D
{ 'e', 'E' }, // KEYCODE_E
{ 'f', 'F' }, // KEYCODE_F
{ 'g', 'G' }, // KEYCODE_G
{ 'h', 'H' }, // KEYCODE_H
{ 'i', 'I' }, // KEYCODE_I
{ 'j', 'J' }, // KEYCODE_J
{ 'k', 'K' }, // KEYCODE_K
{ 'l', 'L' }, // KEYCODE_L 40
{ 'm', 'M' }, // KEYCODE_M
{ 'n', 'N' }, // KEYCODE_N
{ 'o', 'O' }, // KEYCODE_O
{ 'p', 'P' }, // KEYCODE_P
{ 'q', 'Q' }, // KEYCODE_Q
{ 'r', 'R' }, // KEYCODE_R
{ 's', 'S' }, // KEYCODE_S
{ 't', 'T' }, // KEYCODE_T
{ 'u', 'U' }, // KEYCODE_U
{ 'v', 'V' }, // KEYCODE_V 50
{ 'w', 'W' }, // KEYCODE_W
{ 'x', 'X' }, // KEYCODE_X
{ 'y', 'Y' }, // KEYCODE_Y
{ 'z', 'Z' }, // KEYCODE_Z
{ ',', '<' }, // KEYCODE_COMMA
{ '.', '>' }, // KEYCODE_PERIOD
{ 0x0101, 0 }, // KEYCODE_ALT_LEFT
{ 0x0102, 0 }, // KEYCODE_ALT_RIGHT
{ 0x0113, 0 }, // KEYCODE_SHIFT_LEFT
{ 0x0114, 0 }, // KEYCODE_SHIFT_RIGHT 60
{ 0xff09, 0xff09 }, // KEYCODE_TAB
{ ' ', ' ' }, // KEYCODE_SPACE
{ 0, 0 }, // KEYCODE_SYM
{ 0, 0 },
{ 0, 0 }, // KEYCODE_ENVELOPE
{ 0xff0d, 0xff0d }, // KEYCODE_ENTER
{ 0xff08, 0xff08 }, // KEYCODE_DEL (backspace)
{ '`', '~' }, // KEYCODE_GRAVE
{ '-', '_' }, // KEYCODE_MINUS
{ '=', '+' }, // KEYCODE_EQUALS 70
{ '[', '{' }, // KEYCODE_LEFT_BRACKET
{ ']', '}' }, // KEYCODE_RIGHT_BRACKET
{ '\\', '|' }, // KEYCODE_BACKSLASH
{ ';', ':' }, // KEYCODE_SEMICOLON
{ '\'', '"' }, // KEYCODE_APOSTROPHE
{ '/', '?' }, // KEYCODE_SLASH
{ '@', '@' }, // KEYCODE_AT (XXX: see KEYCODE_2)
{ 0, 0 }, // KEYCODE_NUM (XXX: not numlock, see docs)
{ 0, 0 }, // KEYCODE_HEADSETHOOK
{ 0, 0 }, // KEYCODE_FOCUS 80
{ '+', '+' }, // KEYCODE_PLUS (XXX: see KEYCODE_EQUALS)
{ 0, 0 }, // KEYCODE_MENU
{ 0, 0 }, // KEYCODE_NOTIFICATION
{ 0, 0 }, // KEYCODE_SEARCH
{ 0, 0 }, // KEYCODE_MEDIA_PLAY_PAUSE
{ 0, 0 }, // KEYCODE_MEDIA_STOP
{ 0, 0 }, // KEYCODE_MEDIA_NEXT
{ 0, 0 }, // KEYCODE_MEDIA_PREVIOUS
{ 0, 0 }, // KEYCODE_MEDIA_REWIND
{ 0, 0 }, // KEYCODE_MEDIA_FAST_FORWARD 90
{ 0, 0 }, // KEYCODE_MUTE
{ 0x0111, 0 }, // KEYCODE_PAGE_UP
{ 0x0110, 0 }, // KEYCODE_PAGE_DOWN
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 } // 100
};
static final int[][] mod_map = {
{ 59, 60 }, // Shift = KEYCODE_SHIFT_LEFT, KEYCODE_SHIFT_RIGHT
{ 0, 0 }, // Lock = (none)
{ 23, 0 }, // Control = KEYCODE_DPAD_CENTER, (none)
{ 57, 58 }, // Mod1 = KEYCODE_ALT_LEFT, KEYCODE_ALT_RIGHT
{ 0, 0 }, // Mod2 = (none)
{ 0, 0 }, // Mod3 = (none)
{ 0, 0 }, // Mod4 = (none)
{ 0, 0 }, // Mod5 = (none)
};
byte mGlobalAutoRepeat;
byte mKeyClickPercent;
byte mBellVolume;
short mBellPitchHZ;
short mBellDurationMS;
int mBellSampleCount;
AudioTrack mBellAudioTrack;
int mLedMask;
byte mMinKeycode;
byte mMaxKeycode;
short mModState;
short mPointerX;
short mPointerY;
X11Keyboard() {
mGlobalAutoRepeat = 0;
mKeyClickPercent = 50;
mBellVolume = 50;
mBellPitchHZ = 2000;
mBellDurationMS = 100;
mLedMask = 0x00000000;
mMinKeycode = X_MIN_KEYCODE;
mMaxKeycode = X_MIN_KEYCODE + NUM_KEYCODES - 1;
createBellAudioTrack();
}
byte minKeycode() { return mMinKeycode; }
byte maxKeycode() { return mMaxKeycode; }
void handleQueryPointer(X11Client c, X11RequestMessage msg) throws X11Error {
X11Window w = c.getWindow(msg.mData.deqInt());
X11ReplyMessage reply = new X11ReplyMessage(msg);
reply.headerData((byte)1 /*True*/);
reply.mData.enqInt(c.mServer.mDefaultScreen.mRoot.mId);
reply.mData.enqInt(X11Resource.NONE); //XXX: child
reply.mData.enqShort(mPointerX);
reply.mData.enqShort(mPointerY);
reply.mData.enqShort((short)(mPointerX - w.mRect.x)); // win-x
reply.mData.enqShort((short)(mPointerY - w.mRect.y)); // win-y
reply.mData.enqShort(mModState);
c.send(reply);
}
void handleGetKeyboardMapping(X11Client c, X11RequestMessage msg) throws X11Error {
int first_keycode = msg.mData.deqByte();
int count = msg.mData.deqByte();
Log.d("X", "handleGetKeyboardMapping: fc="+first_keycode+", count="+count);
if (first_keycode < mMinKeycode) {
throw new X11Error(X11Error.VALUE, first_keycode);
}
if (first_keycode+count-1 > mMaxKeycode) {
throw new X11Error(X11Error.VALUE, count);
}
byte keysyms_per_keycode = 2;
X11ReplyMessage reply = new X11ReplyMessage(msg);
reply.headerData(keysyms_per_keycode);
reply.mData.enqSkip(24);
int kc;
for (kc = first_keycode; kc < first_keycode+count; ++kc) {
reply.mData.enqInt(key_codes[kc-X_MIN_KEYCODE][0]);
reply.mData.enqInt(key_codes[kc-X_MIN_KEYCODE][1]);
}
c.send(reply);
}
void handleBell(X11Client c, X11RequestMessage msg) {
byte volumePercent = msg.headerData();
int volume;
if (volumePercent >= 0) {
volume = mBellVolume - ((mBellVolume * volumePercent) / 100) + volumePercent;
}
else {
volume = mBellVolume + ((mBellVolume * volumePercent) / 100);
}
int frame_count = mBellSampleCount/2; //XXX ???
int loop_count = mBellDurationMS * mBellPitchHZ / 1000;
mBellAudioTrack.setStereoVolume(volume, volume);
mBellAudioTrack.setLoopPoints(0, frame_count, loop_count);
mBellAudioTrack.play();
}
void handleGetModifierMapping(X11Client c, X11RequestMessage msg) {
byte keycodes_per_modifier = 2;
X11ReplyMessage reply = new X11ReplyMessage(msg);
reply.headerData(keycodes_per_modifier);
reply.mData.enqSkip(24);
int m;
for (m = 0; m < NUM_MODIFIERS; ++m) {
reply.mData.enqInt(mod_map[m][0] + X_MIN_KEYCODE);
reply.mData.enqInt(mod_map[m][1] + X_MIN_KEYCODE);
}
c.send(reply);
}
void onKeyDown(int code) {
int m;
for (m = 0; m < NUM_MODIFIERS; ++m) {
if (mod_map[m][0] == code || mod_map[m][1] == code) {
mModState |= (1 << m);
}
}
}
void onKeyUp(int code) {
int m;
for (m = 0; m < NUM_MODIFIERS; ++m) {
if (mod_map[m][0] == code || mod_map[m][1] == code) {
mModState &= ~(1 << m);
}
}
}
void createBellAudioTrack() {
int sample_rate = AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_NOTIFICATION);
mBellSampleCount = (sample_rate / mBellPitchHZ);
byte[] pcm_data = new byte[2*mBellSampleCount];
for (int i = 0; i < mBellSampleCount; ++i) {
short val = (short)(32767 * Math.sin(2 * Math.PI * i / mBellSampleCount));
pcm_data[2*i+0] = (byte)(val & 0xff);
pcm_data[2*i+1] = (byte)(val >> 8);
}
mBellAudioTrack = new AudioTrack(
AudioManager.STREAM_NOTIFICATION,
sample_rate,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT,
mBellSampleCount,
AudioTrack.MODE_STATIC);
mBellAudioTrack.write(pcm_data, 0, mBellSampleCount);
}
}

View File

@ -0,0 +1,21 @@
package tdm.xserver;
import java.nio.ByteOrder;
abstract class X11Message
{
ByteQueue mData;
abstract void read(ByteQueue q) throws Exception;
abstract void write(ByteQueue q);
abstract void dispatch(X11ProtocolHandler h);
X11Message(ByteOrder endian) {
mData = new ByteQueue(32-4);
mData.endian(endian);
}
int QUADLEN(int val) {
return ((val+3)/4);
}
}

View File

@ -0,0 +1,51 @@
package tdm.xserver;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Path;
class X11Pixmap extends X11Drawable
{
static void create(X11Client c, X11RequestMessage msg) throws X11Error {
int id = msg.mData.deqInt();
X11Pixmap r = new X11Pixmap(id);
r.handleCreate(c, msg);
c.addResource(r);
}
X11Drawable mDrawable;
X11Pixmap(int id) {
super(X11Resource.PIXMAP, id);
mDrawable = null;
}
void destroy() {
mDrawable = null;
super.destroy();
}
void handleCreate(X11Client c, X11RequestMessage msg) throws X11Error {
byte depth = msg.headerData();
X11Drawable d = c.getDrawable(msg.mData.deqInt());
short w = msg.mData.deqShort();
short h = msg.mData.deqShort();
if (w == 0 || h == 0) {
throw new X11Error(X11Error.VALUE, 0);
}
doCreate(depth, w, h, d);
}
void handleFreePixmap(X11Client c, X11RequestMessage msg) {
c.delResource(mId);
}
void doCreate(byte depth, short w, short h, X11Drawable d) {
mDepth = depth;
mRect = new X11Rect((short)0, (short)0, w, h);
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mDrawable = d;
}
}

View File

@ -0,0 +1,7 @@
package tdm.xserver;
class X11Point
{
short x;
short y;
}

View File

@ -0,0 +1,15 @@
package tdm.xserver;
import android.util.Log;
import java.util.ArrayList;
class X11Pointer
{
int mButtonState;
X11Point mLocation;
X11Pointer() {
mLocation = new X11Point();
}
}

View File

@ -0,0 +1,30 @@
package tdm.xserver;
class X11Property
{
int mType;
byte mFormat;
byte[] mVal;
X11Property(int t, byte f) {
mType = t;
mFormat = f;
mVal = null;
}
void setValue(byte[] buf) {
mVal = buf;
}
void appendValue(byte[] buf) {
byte[] newval = new byte[mVal.length + buf.length];
System.arraycopy(mVal, 0, newval, 0, mVal.length);
System.arraycopy(buf, 0, newval, mVal.length, buf.length);
mVal = newval;
}
void prependValue(byte[] buf) {
byte[] newval = new byte[mVal.length + buf.length];
System.arraycopy(buf, 0, newval, 0, buf.length);
System.arraycopy(mVal, 0, newval, buf.length, mVal.length);
mVal = newval;
}
}

View File

@ -0,0 +1,103 @@
package tdm.xserver;
import java.net.Socket;
import java.io.InputStream;
import java.io.IOException;
import java.io.EOFException;
import java.net.SocketException;
import java.lang.Exception;
import java.nio.ByteOrder;
interface X11ProtocolHandler
{
void onMessage(X11SetupRequest msg);
void onMessage(X11SetupResponse msg);
void onMessage(X11RequestMessage msg);
void onMessage(X11ReplyMessage msg);
void onMessage(X11EventMessage msg);
void onMessage(X11ErrorMessage msg);
}
class X11Protocol
{
X11ProtocolHandler mHandler;
Socket mSock;
ByteOrder mEndian;
ByteQueue mQueue;
short mSendSeqNo;
short mRecvSeqNo;
boolean mBigReq;
X11Protocol(X11ProtocolHandler handler, Socket sock) {
mHandler = handler;
mSock = sock;
try {
sock.setTcpNoDelay(true);
}
catch (SocketException e) {
// Ignore
}
}
void close() {
try {
mSock.close();
}
catch (IOException e) {
// Ignore
}
}
void send(X11Message msg) throws IOException {
ByteQueue q = new ByteQueue(32);
q.endian(mEndian);
msg.write(q);
mSock.getOutputStream().write(q.getBytes());
}
void read() throws IOException {
byte[] buf = new byte[1500];
int len = mSock.getInputStream().read(buf, 0, buf.length);
if (len < 0) {
throw new EOFException();
}
if (mQueue == null) {
if (buf[0] == 0x42) {
mEndian = ByteOrder.BIG_ENDIAN;
}
else {
mEndian = ByteOrder.LITTLE_ENDIAN;
}
mQueue = new ByteQueue(32);
mQueue.endian(mEndian);
}
mQueue.pos(mQueue.length());
mQueue.enqArray(buf, 0, len);
mQueue.pos(0);
while (mQueue.remain() >= 4) {
X11Message msg = null;
if (mRecvSeqNo == 0) {
msg = new X11SetupRequest(mEndian);
}
else {
msg = new X11RequestMessage(mEndian, mBigReq);
}
int oldpos = mQueue.pos();
try {
msg.read(mQueue);
}
catch (Exception e) {
mQueue.pos(oldpos);
break;
}
msg.dispatch(mHandler);
++mRecvSeqNo;
}
mQueue.compact();
}
}

View File

@ -0,0 +1,22 @@
package tdm.xserver;
class X11Rect
{
short x;
short y;
short w;
short h;
X11Rect() {
x = 0;
y = 0;
w = 0;
h = 0;
}
X11Rect(short _x, short _y, short _w, short _h) {
x = _x;
y = _y;
w = _w;
h = _h;
}
}

View File

@ -0,0 +1,39 @@
package tdm.xserver;
import java.nio.ByteOrder;
class X11ReplyMessage extends X11Message
{
byte mHeaderData;
short mSeqNo;
X11ReplyMessage(X11RequestMessage msg) {
super(msg.mData.endian());
mData.resize(28);
}
void read(ByteQueue q) {
byte event_type = q.deqByte(); // 0x01
mHeaderData = q.deqByte();
mSeqNo = q.deqShort();
int exlen = q.deqInt();
mData = q.deqData(24 + exlen*4);
}
void write(ByteQueue q) {
q.enqByte((byte)0x01);
q.enqByte(mHeaderData);
q.enqShort(mSeqNo);
mData.enqAlign(4);
int datalen = Math.max(24, mData.pos());
q.enqInt((datalen-24)/4);
q.enqData(mData);
q.enqSkip(datalen - mData.pos());
}
void dispatch(X11ProtocolHandler h) { h.onMessage(this); }
void headerData(byte val) { mHeaderData = val; }
byte headerData() { return mHeaderData; }
void seqno(short val) { mSeqNo = val; }
short seqno() { return mSeqNo; }
}

View File

@ -0,0 +1,195 @@
package tdm.xserver;
import android.util.Log;
import java.nio.ByteOrder;
class X11RequestMessage extends X11Message
{
static final String[] message_names = {
"NONE", // 0
"CreateWindow",
"ChangeWindowAttributes",
"GetWindowAttributes",
"DestroyWindow",
"DestroySubwindows",
"ChangeSaveSet",
"ReparentWindow",
"MapWindow",
"MapSubwindows",
"UnmapWindow", // 10
"UnmapSubwindows",
"ConfigureWindow",
"CirculateWindow",
"GetGeometry",
"QueryTree",
"InternAtom",
"GetAtomName",
"ChangeProperty",
"DeleteProperty",
"GetProperty", // 20
"ListProperties",
"SetSelectionOwner",
"GetSelectionOwner",
"ConvertSelection",
"SendEvent",
"GrabPointer",
"UngrabPointer",
"GrabButton",
"UngrabButton",
"ChangeActivePointerGrab", // 30
"GrabKeyboard",
"UngrabKeyboard",
"GrabKey",
"UngrabKey",
"AllowEvents",
"GrabServer",
"UngrabServer",
"QueryPointer",
"GetMotionEvents",
"TranslateCoords", // 40
"WarpPointer",
"SetInputFocus",
"GetInputFocus",
"QueryKeymap",
"OpenFont",
"CloseFont",
"QueryFont",
"QueryTextExtents",
"ListFonts",
"ListFontsWithInfo", // 50
"SetFontPath",
"GetFontPath",
"CreatePixmap",
"FreePixmap",
"CreateGC",
"ChangeGC",
"CopyGC",
"SetDashes",
"SetClipRectangles",
"FreeGC", // 60
"ClearArea",
"CopyArea",
"CopyPlane",
"PolyPoint",
"PolyLine",
"PolySegment",
"PolyRectangle",
"PolyArc",
"FillPoly",
"PolyFillRectangle", // 70
"PolyFillArc",
"PutImage",
"GetImage",
"PolyText8",
"PolyText16",
"ImageText8",
"ImageText16",
"CreateColormap",
"FreeColormap",
"CopyColormapAndFree", // 80
"InstallColormap",
"UninstallColormap",
"ListInstalledColormaps",
"AllocColor",
"AllocNamedColor",
"AllocColorCells",
"AllocColorPlanes",
"FreeColors",
"StoreColors",
"StoreNamedColor", // 90
"QueryColors",
"LookupColor",
"CreateCursor",
"CreateGlyphCursor",
"FreeCursor",
"RecolorCursor",
"QueryBestSize",
"QueryExtension",
"ListExtensions",
"ChangeKeyboardMapping", // 100
"GetKeyboardMapping",
"ChangeKeyboardControl",
"GetKeyboardControl",
"Bell",
"ChangePointerControl",
"GetPointerControl",
"SetScreenSaver",
"GetScreenSaver",
"ChangeHosts",
"ListHosts", // 110
"SetAccessControl",
"SetCloseDownMode",
"KillClient",
"RotateProperties",
"ForceScreenSaver",
"SetPointerMapping",
"GetPointerMapping",
"SetModifierMapping",
"GetModifierMapping",
"120", // 120
"121",
"122",
"123",
"124",
"125",
"126",
"NoOperation"
};
String name() { return message_names[mRequestType]; }
byte mRequestType;
byte mHeaderData;
boolean mBigReq;
X11RequestMessage(ByteOrder endian, boolean bigreq) {
super(endian);
mBigReq = bigreq;
}
void read(ByteQueue q) throws Exception {
mRequestType = q.deqByte();
mHeaderData = q.deqByte();
short reqlen = q.deqShort();
if (reqlen == 0) {
if (!mBigReq) {
throw new Exception("X11 protocol error: invalid message length");
}
int bigreqlen = q.deqInt();
if (bigreqlen < 2) {
throw new Exception("X11 protocol error: invalid message length");
}
mData = q.deqData(bigreqlen*4-8);
}
else {
mData = q.deqData((int)reqlen*4-4); //XXX: cast needed and functional?
}
}
void write(ByteQueue q) {
q.enqByte(mRequestType);
q.enqByte(mHeaderData);
int reqlen = ((4+mData.length())+3)/4;
if (reqlen > 0xffff) {
if (!mBigReq) {
//XXX: throw new Exception("X11 protocol error: message too big");
System.exit(-1);
}
reqlen += 1;
q.enqShort((short)0);
q.enqInt(reqlen);
}
else {
q.enqShort((short)reqlen);
}
q.enqData(mData);
q.enqAlign(4);
}
void dispatch(X11ProtocolHandler h) { h.onMessage(this); }
void requestType(byte val) { mRequestType = val; }
byte requestType() { return mRequestType; }
void headerData(byte val) { mHeaderData = val; }
byte headerData() { return mHeaderData; }
}

View File

@ -0,0 +1,34 @@
package tdm.xserver;
class X11Resource
{
// Resource constants
static final int MIN_ID = 4;
static final int ID_DEF_COLORMAP = MIN_ID+0;
static final int ID_ROOT_WINDOW = MIN_ID+1;
static final int NONE = 0;
static final int NEVER = 0;
// Resource types
static final int WINDOW = 1;
static final int PIXMAP = 2;
static final int GCONTEXT = 3;
static final int FONT = 4;
static final int CURSOR = 5;
static final int COLORMAP = 6;
static final int CMAPENTRY = 7;
static final int OTHERCLIENT = 8;
static final int PASSIVEGRAB = 9;
int mType;
int mId;
X11Resource(int t, int n) {
mType = t;
mId = n;
}
void destroy() {}
}

View File

@ -0,0 +1,148 @@
package tdm.xserver;
import android.util.Log;
import java.util.ArrayList;
class X11Screen
{
X11Window mRoot;
X11Colormap mDefaultColormap;
int mWhitePixel;
int mBlackPixel;
int mCurrentInputMasks;
short mWidthPx;
short mHeightPx;
short mWidthMM;
short mHeightMM;
short mMinInstalledMaps;
short mMaxInstalledMaps;
int mRootVisual; //XXX: property of root?
byte mBackingStores;
byte mSaveUnders;
byte mRootDepth; //XXX: property of root?
ArrayList<X11Depth> mAllowedDepths;
// ...
X11Screen(X11Server server, X11Client client, short w, short h, short dpi) throws X11Error {
mAllowedDepths = new ArrayList();
mDefaultColormap = new X11Colormap(X11Resource.ID_DEF_COLORMAP);
client.addResource(mDefaultColormap);
mRoot = new X11Window(X11Resource.ID_ROOT_WINDOW);
client.addResource(mRoot);
mDefaultColormap.initDefault();
mRoot.mColormap = mDefaultColormap;
int vid = 0;
X11Depth d;
X11Visual v;
d = new X11Depth((byte)24);
v = new X11Visual(++vid, (byte)24);
v.mVisClass = X11Visual.DIRECT_COLOR;
v.mRedMask = (0xff << 16);
v.mGreenMask = (0xff << 8);
v.mBlueMask = (0xff);
v.mBitsPerRgbValue = 8;
v.mColormapEntries = (1 << 8);
server.addVisual(v);
d.visuals.add(v);
v = new X11Visual(++vid, (byte)24);
v.mVisClass = X11Visual.TRUE_COLOR;
v.mRedMask = (0xff << 16);
v.mGreenMask = (0xff << 8);
v.mBlueMask = (0xff);
v.mBitsPerRgbValue = 8;
v.mColormapEntries = (1 << 8);
server.addVisual(v);
d.visuals.add(v);
mAllowedDepths.add(d);
// root is depth 24, visual DirectColor
mRoot.createRoot(client, w, h, d.depth, v);
//XXX: depth 1, 4, 8, 15, 16?
d = new X11Depth((byte)32);
v = new X11Visual(++vid, (byte)32);
v.mVisClass = X11Visual.DIRECT_COLOR;
v.mRedMask = (0xff << 16);
v.mGreenMask = (0xff << 8);
v.mBlueMask = (0xff);
v.mBitsPerRgbValue = 8;
v.mColormapEntries = (1 << 8);
server.addVisual(v);
d.visuals.add(v);
v = new X11Visual(++vid, (byte)32);
v.mVisClass = X11Visual.TRUE_COLOR;
v.mRedMask = (0xff << 16);
v.mGreenMask = (0xff << 8);
v.mBlueMask = (0xff);
v.mBitsPerRgbValue = 8;
v.mColormapEntries = (1 << 8);
server.addVisual(v);
d.visuals.add(v);
mAllowedDepths.add(d);
mWhitePixel = 0x00ffffff;
mBlackPixel = 0;
mCurrentInputMasks = 0;
mWidthPx = w;
mHeightPx = h;
mWidthMM = (short)((254*w)/(10*dpi));
mHeightMM = (short)((254*h)/(10*dpi));
mMinInstalledMaps = 1;
mMaxInstalledMaps = 1;
mRootVisual = mRoot.mVisual.mId;
mBackingStores = X11Resource.NEVER;
mSaveUnders = 0;
mRootDepth = mRoot.mDepth;
}
void enqueue(ByteQueue q) {
q.enqInt(mRoot.mId);
q.enqInt(mDefaultColormap.mId);
q.enqInt(mWhitePixel);
q.enqInt(mBlackPixel);
q.enqInt(mCurrentInputMasks);
q.enqShort(mWidthPx);
q.enqShort(mHeightPx);
q.enqShort(mWidthMM);
q.enqShort(mHeightMM);
q.enqShort(mMinInstalledMaps);
q.enqShort(mMaxInstalledMaps);
q.enqInt(mRoot.mVisual.mId);
q.enqByte(mBackingStores);
q.enqByte(mSaveUnders);
q.enqByte(mRoot.mDepth);
q.enqByte((byte)mAllowedDepths.size());
for (X11Depth dep : mAllowedDepths) {
q.enqByte(dep.depth);
q.enqSkip(1);
q.enqShort((short)dep.visuals.size());
q.enqSkip(4);
for (X11Visual vis : dep.visuals) {
q.enqInt(vis.mId);
q.enqByte(vis.mVisClass);
q.enqByte(vis.mBitsPerRgbValue);
q.enqShort(vis.mColormapEntries);
q.enqInt(vis.mRedMask);
q.enqInt(vis.mGreenMask);
q.enqInt(vis.mBlueMask);
q.enqSkip(4);
}
}
}
}

View File

@ -0,0 +1,9 @@
package tdm.xserver;
class X11Selection
{
X11Atom mSelection;
X11Client mOwnerClient;
X11Window mOwnerWindow;
int mLastChangeTime;
}

View File

@ -0,0 +1,425 @@
package tdm.xserver;
import android.util.Log;
import android.content.Context;
import android.view.Display;
import android.view.WindowManager;
import android.util.DisplayMetrics;
import android.view.Display;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.net.ServerSocket;
class X11Server extends Thread
{
Context mContext;
UIHandler mHandler;
X11Client[] mClients;
Map<Integer,X11Visual> mVisuals;
ArrayList<X11Format> mPixmapFormats;
Map<Integer,X11Atom> mAtomsById;
Map<String,X11Atom> mAtomsByName;
int mLastAtomId;
Map<Integer,X11Selection> mSelections;
Map<String,FontData> mFonts;
Map<String,String> mFontAliases;
boolean mRunning;
long mStartTime;
ServerSocket mSock;
X11Keyboard mKeyboard;
X11Screen mDefaultScreen;
X11Window mInputFocus;
X11Client mGrabClient;
X11Server(Context ctx, UIHandler handler) {
mContext = ctx;
mHandler = handler;
}
public void run() {
mClients = new X11Client[X11Client.MAX_CLIENTS];
mClients[0] = new X11Client(this);
mVisuals = new HashMap<Integer,X11Visual>();
mPixmapFormats = new ArrayList();
mPixmapFormats.add(new X11Format_1()); //XXX
mPixmapFormats.add(new X11Format_4());
mPixmapFormats.add(new X11Format_8());
mPixmapFormats.add(new X11Format_16());
mPixmapFormats.add(new X11Format_24());
mPixmapFormats.add(new X11Format_32());
X11Colormap.globalInit(this);
mAtomsById = new HashMap<Integer,X11Atom>();
mAtomsByName = new HashMap<String,X11Atom>();
mLastAtomId = 0;
X11Atom.globalInit(this);
mSelections = new HashMap<Integer,X11Selection>();
mFonts = new HashMap<String,FontData>();
mFontAliases = new HashMap<String,String>();
FontData.globalInit(this);
// NB: PixelFormat (see dpy.getPixelFormat()) has bitsPerPixel/bytesPerPixel etc.
WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
Display dpy = wm.getDefaultDisplay();
DisplayMetrics dm = new DisplayMetrics();
dpy.getMetrics(dm);
mKeyboard = new X11Keyboard();
try {
mDefaultScreen = new X11Screen(this, mClients[0],
(short)dm.widthPixels,
(short)dm.heightPixels,
(short)dm.densityDpi);
}
catch (X11Error e) {
Log.e(XServer.TAG, "Cannot create screen");
return;
}
try {
mSock = new ServerSocket(6000);
}
catch (Exception e) {
Log.e(XServer.TAG, "Cannot create server socket");
return;
}
mStartTime = System.currentTimeMillis();
mRunning = true;
while (mRunning) {
try {
for (int i = 1; i < X11Client.MAX_CLIENTS; ++i) {
if (mClients[i] == null) {
X11Client c = new X11Client(this, i, mSock.accept());
mClients[i] = c;
c.start();
break;
}
}
}
catch (Exception e) {
Log.e(XServer.TAG, "Exception in run");
mRunning = false;
continue;
}
}
}
void onStop() {
mRunning = false;
}
int currentTime() {
return (int)(System.currentTimeMillis() - mStartTime);
}
void clientClosed(X11Client c) {
//XXX: locking
if (mGrabClient == c) {
mGrabClient = null;
}
//XXX: delete selections
//XXX: ...???
for (int i = 1; i < X11Client.MAX_CLIENTS; ++i) {
if (mClients[i] == c) {
c = null;
break;
}
}
}
void addVisual(X11Visual v) throws X11Error {
if (mVisuals.containsKey(v.mId)) {
throw new X11Error(X11Error.IDCHOICE, v.mId);
}
mVisuals.put(v.mId, v);
}
void delVisual(int id) {
mVisuals.remove(id);
}
X11Visual getVisual(int id) {
return mVisuals.get(id);
}
ArrayList<X11Format> getPixmapFormats() {
return mPixmapFormats;
}
void registerFont(FontData f) {
mFonts.put(f.mName, f);
}
void registerFontAlias(String alias, String name) {
mFontAliases.put(alias, name);
}
void handleInternAtom(X11Client c, X11RequestMessage msg) {
byte onlyifexist = msg.headerData();
short namelen = msg.mData.deqShort();
msg.mData.deqSkip(2);
String name = msg.mData.deqString(namelen);
int id = X11Atom.NONE;
boolean created = false;
if (!mAtomsByName.containsKey(name)) {
if (onlyifexist == 0 /* False */) {
id = doInternAtom(name);
created = true;
}
}
X11ReplyMessage reply = new X11ReplyMessage(msg);
reply.mData.enqInt(id);
c.send(reply);
}
void handleSetSelectionOwner(X11Client c, X11RequestMessage msg) throws X11Error {
X11Window owner = c.getWindow(msg.mData.deqInt());
X11Atom selection_name = mAtomsById.get(msg.mData.deqInt());
int time = msg.mData.deqInt();
X11Selection selection = mSelections.get(selection_name.mId);
if (selection == null) {
selection = new X11Selection();
selection.mSelection = selection_name;
selection.mOwnerClient = c;
selection.mOwnerWindow = owner;
selection.mLastChangeTime = currentTime();
mSelections.put(selection_name.mId, selection);
}
if (time < selection.mLastChangeTime) {
return;
}
if (selection.mOwnerClient != null && selection.mOwnerClient != c) {
X11EventMessage event = new X11EventMessage(selection.mOwnerClient.mProt.mEndian, X11Event.SELECTION_CLEAR);
event.mData.enqInt(time);
event.mData.enqInt(owner.mId);
event.mData.enqInt(selection_name.mId);
selection.mOwnerClient.send(event);
}
selection.mLastChangeTime = time;
if (owner == null) {
selection.mOwnerClient = null;
selection.mOwnerWindow = null;
}
else {
selection.mOwnerClient = c;
selection.mOwnerWindow = owner;
}
}
void handleGetSelectionOwner(X11Client c, X11RequestMessage msg) {
int id = X11Resource.NONE;
X11Selection selection = mSelections.get(msg.mData.deqInt());
if (selection != null) {
id = selection.mOwnerWindow.mId;
}
X11ReplyMessage reply = new X11ReplyMessage(msg);
reply.mData.enqInt(id);
c.send(reply);
}
void handleGrabServer(X11Client c, X11RequestMessage msg) throws X11Error {
if (mGrabClient != null) {
//XXX: handle recursion?
Log.e("X", "Error: GrabServer while server is grabbed");
throw new X11Error(X11Error.IMPLEMENTATION, 0);
}
mGrabClient = c;
}
void handleUngrabServer(X11Client c, X11RequestMessage msg) throws X11Error {
if (mGrabClient != c) {
//XXX: ignore this?
Log.e("X", "Error: UngrabServer while server is not grabbed");
throw new X11Error(X11Error.IMPLEMENTATION, 0);
}
mGrabClient = null;
}
void handleSetInputFocus(X11Client c, X11RequestMessage msg) throws X11Error {
//XXX
byte revert_to = msg.headerData();
X11Window w = c.getWindow(msg.mData.deqInt());
int timestamp = msg.mData.deqInt();
mInputFocus = w;
}
void handleGetInputFocus(X11Client c, X11RequestMessage msg) {
//XXX
X11ReplyMessage reply = new X11ReplyMessage(msg);
reply.headerData((byte)0); // revert-to = None
reply.mData.enqInt(mInputFocus.mId); // None
c.send(reply);
}
void handleOpenFont(X11Client c, X11RequestMessage msg) throws X11Error {
int fid = msg.mData.deqInt();
short len = msg.mData.deqShort();
msg.mData.deqSkip(2);
String name = msg.mData.deqString(len);
name = name.toLowerCase();
if (mFontAliases.containsKey(name)) {
Log.d("X", "OpenFont: alias: <" + name + "> => <" + mFontAliases.get(name) + ">");
name = mFontAliases.get(name);
}
FontData data = null;
for (FontData i : mFonts.values()) {
if (i.match(name)) {
data = i;
break;
}
}
if (data == null) {
Log.d("X", "OpenFont: cannot find font <" + name + ">: trying fixed instead");
data = mFonts.get(mFontAliases.get("fixed"));
}
if (data == null) {
Log.d("X", "OpenFont: cannot find font <" + name + ">");
throw new X11Error(X11Error.NAME, fid);
}
Log.d("X", "OpenFont: opening " + data.mName);
try {
data.loadMetadata(this);
data.loadGlyphs(this);
}
catch (Exception e) {
Log.e("X", "OpenFont: failed to load metadata");
e.printStackTrace();
throw new X11Error(X11Error.IMPLEMENTATION, 0);
}
X11Font font = new X11Font(fid, data);
c.addResource(font);
}
void handleListFonts(X11Client c, X11RequestMessage msg) {
short maxnames = msg.mData.deqShort();
short len = msg.mData.deqShort();
String name = msg.mData.deqString(len);
name = name.toLowerCase();
ArrayList<String> v = new ArrayList<String>();
for (FontData f : mFonts.values()) {
if (f.match(name)) {
v.add(f.mName);
if (v.size() == maxnames) {
break;
}
}
}
//XXX: how should this be done?
if (v.size() < maxnames) {
String realname = mFontAliases.get(name);
if (realname != null) {
v.add(realname); //XXX?
}
}
X11ReplyMessage reply = new X11ReplyMessage(msg);
reply.mData.enqShort((short)v.size());
reply.mData.enqSkip(22);
for (String s : v) {
reply.mData.enqByte((byte)s.length());
reply.mData.enqString(s);
}
reply.mData.enqAlign(4);
c.send(reply);
}
void handleListFontsWithInfo(X11Client c, X11RequestMessage msg) {
short maxnames = msg.mData.deqShort();
short len = msg.mData.deqShort();
String pattern = msg.mData.deqString(len);
pattern = pattern.toLowerCase();
ArrayList<FontData> v = new ArrayList<FontData>();
for (FontData f : mFonts.values()) {
if (f.match(pattern)) {
try {
f.loadMetadata(this);
v.add(f);
if (v.size() == maxnames) {
break;
}
}
catch (Exception e) {
Log.e("X", "Failed to read font " + f.mName);
}
}
}
X11ReplyMessage reply = new X11ReplyMessage(msg);
for (int n = 0; n < v.size(); ++n) {
FontData f = v.get(n);
reply.mData.clear();
f.enqueueInfo(reply.mData);
reply.mData.enqInt(v.size() - n);
f.enqueueProperties(reply.mData);
reply.mData.enqString(f.mName);
reply.headerData((byte)f.mName.length());
c.send(reply);
}
reply.mData.clear();
reply.mData.enqSkip(52);
reply.headerData((byte)0);
c.send(reply);
}
void handleQueryExtension(X11Client c, X11RequestMessage msg) {
X11ReplyMessage reply = new X11ReplyMessage(msg);
reply.mData.enqByte((byte)0); // present
reply.mData.enqByte((byte)0); // major-opcode
reply.mData.enqByte((byte)0); // first-event
reply.mData.enqByte((byte)0); // first-error
c.send(reply);
}
void doInternAtom(int id, String name) {
X11Atom atom = new X11Atom(id, name);
mAtomsById.put(id, atom);
mAtomsByName.put(name, atom);
}
int doInternAtom(String name) {
int id = ++mLastAtomId;
doInternAtom(id, name);
return id;
}
}

View File

@ -0,0 +1,47 @@
package tdm.xserver;
import java.nio.ByteOrder;
class X11SetupRequest extends X11Message //XXX: extends X11Consumer?
{
ByteOrder mByteOrder;
short mProtoMajor;
short mProtoMinor;
String mAuthProtoName;
byte[] mAuthProtoData;
X11SetupRequest(ByteOrder endian) {
super(endian);
}
void read(ByteQueue q) throws Exception {
short name_len, data_len;
mByteOrder = (q.deqByte() == (byte)0x42) ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
q.deqSkip(1);
mProtoMajor = q.deqShort();
mProtoMinor = q.deqShort();
name_len = q.deqShort();
data_len = q.deqShort();
q.deqSkip(2);
mAuthProtoName = q.deqString(name_len);
q.deqAlign(4);
mAuthProtoData = q.deqArray(data_len);
q.deqAlign(4);
}
void write(ByteQueue q) {
short name_len = (short)mAuthProtoName.length();
short data_len = (short)mAuthProtoData.length;
q.enqByte((mByteOrder == ByteOrder.BIG_ENDIAN) ? (byte)0x42 : (byte)0x6c);
q.enqSkip(1);
q.enqShort(mProtoMajor);
q.enqShort(mProtoMinor);
q.enqShort(name_len);
q.enqShort(data_len);
q.enqSkip(2);
q.enqString(mAuthProtoName);
q.enqArray(mAuthProtoData);
}
void dispatch(X11ProtocolHandler h) { h.onMessage(this); }
}

View File

@ -0,0 +1,72 @@
package tdm.xserver;
import java.nio.ByteOrder;
class X11SetupResponse extends X11Message //XXX: extends X11Consumer?
{
byte mSuccess;
short mProtoMajor;
short mProtoMinor;
String mReason;
X11SetupResponse(X11SetupRequest msg) {
super(msg.mData.endian());
}
void read(ByteQueue q) throws Exception {
mSuccess = q.deqByte();
if (mSuccess != 1 /* Success */) {
if (mSuccess == 2 /* Authenticate */) {
short add_len;
q.deqSkip(2);
add_len = q.deqShort();
mReason = q.deqString(add_len*4);
}
else { /* Failed */
short add_len;
byte reason_len;
reason_len = q.deqByte();
mProtoMajor = q.deqShort();
mProtoMinor = q.deqShort();
add_len = q.deqShort();
mReason = q.deqString(reason_len);
}
return;
}
q.deqSkip(1);
mProtoMajor = q.deqShort();
mProtoMinor = q.deqShort();
short add_len = q.deqShort();
mData = q.deqData(add_len*4);
}
void write(ByteQueue q) {
q.enqByte(mSuccess);
if (mSuccess != 1 /* Success */) {
if (mSuccess == 2 /* Authenticate */) {
short add_len = (short)MathX.divceil(mReason.length(), 4);
q.enqSkip(5);
q.enqShort(add_len);
q.enqString(mReason);
}
else { /* Failed */
short add_len = (short)MathX.divceil(mReason.length(), 4);
byte reason_len = (byte)mReason.length();
q.enqByte(reason_len);
q.enqShort(mProtoMajor);
q.enqShort(mProtoMinor);
q.enqShort(add_len);
q.enqString(mReason);
}
return;
}
q.enqSkip(1);
q.enqShort(mProtoMajor);
q.enqShort(mProtoMinor);
short add_len = (short)MathX.divceil(mData.pos(), 4);
q.enqShort(add_len);
q.enqData(mData);
}
void dispatch(X11ProtocolHandler h) { h.onMessage(this); }
}

View File

@ -0,0 +1,28 @@
package tdm.xserver;
class X11Visual
{
static final int NONE = 0;
static final byte STATIC_GRAY = 0;
static final byte GRAYSCALE = 1;
static final byte STATIC_COLOR = 2;
static final byte PSEUDO_COLOR = 3;
static final byte TRUE_COLOR = 4;
static final byte DIRECT_COLOR = 5;
int mId;
byte mDepth;
byte mVisClass;
byte mBitsPerRgbValue;
short mColormapEntries;
int mRedMask;
int mGreenMask;
int mBlueMask;
X11Visual(int n, byte d) {
mId = n;
mDepth = d;
}
}

View File

@ -0,0 +1,790 @@
package tdm.xserver;
import android.util.Log;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.Color;
import android.graphics.Canvas;
import android.os.Message;
import android.view.View;
import android.view.ViewGroup;
import android.content.Context;
import java.lang.Thread;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
class X11Window extends X11Drawable
{
static final int EVENT_EXPOSURE = 0x00008000;
static final short COPY_FROM_PARENT = 0;
static final short INPUT_OUTPUT = 1;
static final short INPUT_ONLY = 2;
static void create(X11Client c, X11RequestMessage msg) throws X11Error {
int id = msg.mData.deqInt();
X11Window r = new X11Window(id);
r.handleCreate(c, msg);
c.addResource(r);
}
X11Window mParent;
short mWndClass;
X11Pixmap mBgPixmap;
int mBgPixel;
X11Pixmap mBorderPixmap;
int mBorderPixel;
byte mBitGravity;
byte mWinGravity;
byte mBackingStore;
int mBackingPlanes;
int mBackingPixel;
byte mOverrideRedirect;
byte mSaveUnder;
int mEventMask;
short mDoNotPropagateMask;
X11Colormap mColormap;
X11Cursor mCursor;
Map<Integer,X11Property> mProperties;
// sibling position
ArrayList<X11Window> mChildren;
UIHandler mHandler;
ClientView mView;
boolean mMapped;
boolean mRealized;
X11Window(int id) {
super(X11Resource.WINDOW, id);
mProperties = new HashMap<Integer,X11Property>();
mChildren = new ArrayList<X11Window>();
}
void destroy() {
if (mView != null) {
sendViewMessage(UIHandler.MSG_VIEW_REMOVE);
while (mView != null) { Thread.yield(); }
}
mChildren = null;
mProperties = null;
mCursor = null;
mColormap = null;
mBorderPixmap = null;
mBgPixmap = null;
mParent = null;
super.destroy();
}
void createRoot(X11Client c, short w, short h, byte d, X11Visual v) {
mDepth = d;
mRect = new X11Rect((short)0, (short)0, w, h);
mBitmap = Bitmap.createBitmap(mRect.w, mRect.h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mBorderWidth = 0;
mWndClass = INPUT_OUTPUT;
mVisual = v;
mBgPixmap = new X11Pixmap(0); //XXX: should use a real (global) id
mBgPixmap.doCreate(mDepth, (short)3, (short)3, this);
mBgPixmap.mBitmap.setPixel((short)0, (short)0, Color.WHITE);
mBgPixmap.mBitmap.setPixel((short)1, (short)0, Color.BLACK);
mBgPixmap.mBitmap.setPixel((short)2, (short)0, Color.WHITE);
mBgPixmap.mBitmap.setPixel((short)0, (short)1, Color.BLACK);
mBgPixmap.mBitmap.setPixel((short)1, (short)1, Color.WHITE);
mBgPixmap.mBitmap.setPixel((short)2, (short)1, Color.BLACK);
mBgPixmap.mBitmap.setPixel((short)0, (short)2, Color.WHITE);
mBgPixmap.mBitmap.setPixel((short)1, (short)2, Color.BLACK);
mBgPixmap.mBitmap.setPixel((short)2, (short)2, Color.WHITE);
mHandler = c.mServer.mHandler;
mMapped = true;
mRealized = true;
paintBackgroundArea(mRect);
Log.d("X", "Creating root ClientView");
sendViewMessage(UIHandler.MSG_VIEW_CREATE_ROOT);
while (mView == null) { Thread.yield(); }
mView.mClient = c;
c.mServer.mInputFocus = this;
}
void handleCreate(X11Client c, X11RequestMessage msg) throws X11Error {
mDepth = msg.headerData();
mParent = c.getWindow(msg.mData.deqInt());
mParent.mChildren.add(this);
mRect = new X11Rect();
mRect.x = msg.mData.deqShort();
mRect.y = msg.mData.deqShort();
mRect.w = msg.mData.deqShort();
mRect.h = msg.mData.deqShort();
mBorderWidth = msg.mData.deqShort();
mBitmap = Bitmap.createBitmap(mRect.w, mRect.h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mWndClass = msg.mData.deqShort();
int vid = msg.mData.deqInt();
if (mWndClass == COPY_FROM_PARENT) {
mWndClass = mParent.mWndClass;
}
if (mWndClass == INPUT_ONLY) {
if (mDepth != 0) {
throw new X11Error(X11Error.MATCH, mDepth);
}
if (mBorderWidth != 0) {
throw new X11Error(X11Error.MATCH, mBorderWidth);
}
}
else if (mWndClass == INPUT_OUTPUT) {
if (mDepth == 0) {
mDepth = mParent.mDepth;
}
}
else {
throw new X11Error(X11Error.MATCH, mWndClass);
}
if (vid == 0 /* CopyFromParent */) {
mVisual = mParent.mVisual;
}
else {
mVisual = c.getVisual(vid);
}
if (mRect.w == 0 && mRect.h == 0) {
throw new X11Error(X11Error.VALUE, 0);
}
//XXX: Add checks per spec here?
mBorderPixmap = mParent.mBorderPixmap;
mColormap = mParent.mColormap;
doChangeAttributes(c, msg.mData);
mHandler = c.mServer.mHandler;
sendViewMessage(UIHandler.MSG_VIEW_CREATE);
while (mView == null) { Thread.yield(); }
mView.mClient = c;
paintBackgroundArea(mRect);
//XXX: xorg server does not seem to send a CreateNotify event?
}
void handleChangeWindowAttributes(X11Client c, X11RequestMessage msg) throws X11Error {
if (mParent == null) {
return;
}
doChangeAttributes(c, msg.mData);
}
void handleGetWindowAttributes(X11Client c, X11RequestMessage msg) {
byte map_state = (byte)0 /* Unmapped */;
if (mMapped) {
map_state = 1 /* Unviewable */;
if (mRealized) {
map_state = 2 /* Viewable */;
}
}
X11ReplyMessage reply = new X11ReplyMessage(msg);
reply.headerData(mBackingStore);
reply.mData.enqInt(mVisual.mId);
reply.mData.enqShort(mWndClass);
reply.mData.enqByte(mBitGravity);
reply.mData.enqByte(mWinGravity);
reply.mData.enqInt(mBackingPlanes);
reply.mData.enqInt(mBackingPixel);
reply.mData.enqByte(mSaveUnder);
reply.mData.enqByte((byte)1 /* True */); //XXX: map-is-installed
reply.mData.enqByte(map_state);
reply.mData.enqByte(mOverrideRedirect);
reply.mData.enqInt(mColormap.mId);
reply.mData.enqInt(mEventMask);
reply.mData.enqInt(mEventMask);
reply.mData.enqShort(mDoNotPropagateMask);
reply.mData.enqSkip(2);
c.send(reply);
}
void handleDestroyWindow(X11Client c, X11RequestMessage msg) {
doDestroy(c);
}
void handleDestroySubwindows(X11Client c, X11RequestMessage msg) {
doDestroySub(c);
}
void handleMapWindow(X11Client c, X11RequestMessage msg) throws X11Error {
doMap(c);
}
void handleMapSubwindows(X11Client c, X11RequestMessage msg) throws X11Error {
doMapSub(c);
}
void handleUnmapWindow(X11Client c, X11RequestMessage msg) throws X11Error {
doUnmap(c);
}
void handleUnmapSubwindows(X11Client c, X11RequestMessage msg) throws X11Error {
doUnmapSub(c);
}
void handleConfigureWindow(X11Client c, X11RequestMessage msg) throws X11Error {
boolean size_changed = false;
boolean position_changed = false;
short mask = msg.mData.deqShort();
if ((mask & 0x0001) != 0) { // x
mRect.x = (short)(msg.mData.deqInt() & 0xffff);
position_changed = true;
}
if ((mask & 0x0002) != 0) { // y
mRect.y = (short)(msg.mData.deqInt() & 0xffff);
position_changed = true;
}
if ((mask & 0x0004) != 0) { // width
mRect.w = (short)(msg.mData.deqInt() & 0xffff);
size_changed = true;
}
if ((mask & 0x0008) != 0) { // height
size_changed = true;
mRect.h = (short)(msg.mData.deqInt() & 0xffff);
}
if ((mask & 0x0010) != 0) { // border-width
mBorderWidth = (short)(msg.mData.deqInt() & 0xffff);
}
if ((mask & 0x0020) != 0) { // sibling
//XXX
c.getWindow(msg.mData.deqInt());
}
if ((mask & 0x0040) != 0) { // stack-mode
//XXX
msg.mData.deqInt();
}
if (size_changed) {
mBitmap = Bitmap.createBitmap(mRect.w, mRect.h, Bitmap.Config.ARGB_8888);
mCanvas.setBitmap(mBitmap);
paintBackgroundArea(mRect);
}
if (size_changed || position_changed) {
sendViewMessage(UIHandler.MSG_VIEW_CONFIGURE);
}
}
void handleChangeProperty(X11Client c, X11RequestMessage msg) throws X11Error {
byte mode = msg.headerData(); // Replace, Prepend, Append
int name = msg.mData.deqInt();
int type = msg.mData.deqInt();
byte fmt = msg.mData.deqByte();
msg.mData.deqSkip(3);
if (fmt != 8 && fmt != 16 && fmt != 32) {
throw new X11Error(X11Error.VALUE, fmt);
}
int datalen = msg.mData.deqInt();
ByteQueue data = new ByteQueue(); //XXX endian?
while (datalen-- != 0) {
switch (fmt) {
case 8: data.enqByte(msg.mData.deqByte()); break;
case 16: data.enqShort(msg.mData.deqShort()); break;
case 32: data.enqInt(msg.mData.deqInt()); break;
}
}
//XXX: lots of checks are missing here
X11Property prop = mProperties.get(name);
if (prop == null) {
prop = new X11Property(type, fmt);
mProperties.put(name, prop);
}
switch (mode) {
case 0: prop.setValue(data.getBytes()); break;
case 1: prop.appendValue(data.getBytes()); break;
case 2: prop.prependValue(data.getBytes()); break;
}
X11EventMessage evt = new X11EventMessage(c.mProt.mEndian, X11Event.PROPERTY_NOTIFY);
evt.mData.enqInt(mId);
evt.mData.enqInt(name);
evt.mData.enqInt(c.mServer.currentTime());
evt.mData.enqByte((byte)0); // NewValue
c.send(evt);
}
void handleDeleteProperty(X11Client c, X11RequestMessage msg) throws X11Error {
int name = msg.mData.deqInt();
if (mProperties.containsKey(name)) {
mProperties.remove(name);
X11EventMessage evt = new X11EventMessage(c.mProt.mEndian, X11Event.PROPERTY_NOTIFY);
evt.mData.enqInt(mId);
evt.mData.enqInt(name);
evt.mData.enqInt(c.mServer.currentTime());
evt.mData.enqByte((byte)1); // Deleted
c.send(evt);
}
}
void handleGetProperty(X11Client c, X11RequestMessage msg) throws X11Error {
byte del = msg.headerData();
int name = msg.mData.deqInt();
int type = msg.mData.deqInt();
int off = msg.mData.deqInt();
int len = msg.mData.deqInt();
X11ReplyMessage reply = new X11ReplyMessage(msg);
X11Property prop = mProperties.get(name);
if (prop != null) {
int rpos = off * 4;
if (rpos > prop.mVal.length) {
throw new X11Error(X11Error.VALUE, off);
}
int rlen = Math.min(prop.mVal.length - rpos, len*4);
byte[] rval = new byte[rlen];
System.arraycopy(prop.mVal, rpos, rval, 0, rlen); //XXX: endian?
if (type != 0 /* AnyPropertyType */ && type != prop.mType) {
rpos = 0;
rlen = 0;
del = 0 /* False */;
}
else {
if (prop.mVal.length < (rpos+rlen)) {
del = 0 /* False */;
}
}
reply.headerData(prop.mFormat);
reply.mData.enqInt(prop.mType);
reply.mData.enqInt(prop.mVal.length - (rpos + rlen));
reply.mData.enqInt(rval.length / (prop.mFormat/8));
reply.mData.enqSkip(12);
reply.mData.enqArray(rval);
if (del != 0 /* False */) {
mProperties.remove(name);
X11EventMessage evt = new X11EventMessage(c.mProt.mEndian, X11Event.PROPERTY_NOTIFY);
evt.mData.enqInt(mId);
evt.mData.enqInt(name);
evt.mData.enqInt(c.mServer.currentTime());
evt.mData.enqByte((byte)1); // Deleted
c.send(evt);
}
}
else {
reply.mData.enqInt(0); // None
reply.mData.enqInt(0);
reply.mData.enqInt(0);
}
c.send(reply);
}
void handleListProperties(X11Client c, X11RequestMessage msg) throws X11Error {
X11ReplyMessage reply = new X11ReplyMessage(msg);
reply.mData.enqShort((short)mProperties.size());
reply.mData.enqSkip(22);
for (Integer name : mProperties.keySet()) {
reply.mData.enqInt(name);
}
c.send(reply);
}
void handleTranslateCoordinates(X11Client c, X11RequestMessage msg) throws X11Error {
X11Window dst = c.getWindow(msg.mData.deqInt());
short src_x = msg.mData.deqShort();
short src_y = msg.mData.deqShort();
X11Point src_origin = absolutePosition(c);
X11Point dst_origin = dst.absolutePosition(c);
short dst_x = (short)(src_origin.x + src_x - dst_origin.x);
short dst_y = (short)(src_origin.y + src_y - dst_origin.y);
int childid = 0 /* None */;
for (X11Window child : mChildren) {
if (src_x >= child.mRect.x && src_y >= child.mRect.y &&
src_x < child.mRect.x + child.mRect.w &&
src_y < child.mRect.y + child.mRect.h) {
childid = child.mId;
break; //XXX: which child(ren) to return here?
}
}
X11ReplyMessage reply = new X11ReplyMessage(msg);
reply.headerData((byte)1 /* True */); // same-screen
reply.mData.enqInt(childid); // WINDOW or None
reply.mData.enqShort(dst_x);
reply.mData.enqShort(dst_y);
c.send(reply);
}
void handleClearArea(X11Client c, X11RequestMessage msg) throws X11Error {
if (mWndClass == INPUT_ONLY) {
throw new X11Error(X11Error.MATCH, mId);
}
byte exposures = msg.headerData();
X11Rect r = new X11Rect();
r.x = msg.mData.deqShort();
r.y = msg.mData.deqShort();
r.w = msg.mData.deqShort();
if (r.w == 0) {
r.w = (short)(mRect.x - r.x);
}
r.h = msg.mData.deqShort();
if (r.h == 0) {
r.h = (short)(mRect.y - r.y);
}
paintBackgroundArea(r);
mView.postInvalidate(r.x, r.y, r.x+r.w, r.y+r.h);
if (exposures != 0 /* False */) { //XXX: independent of EXPOSURES event mask?
X11EventMessage evt = new X11EventMessage(c.mProt.mEndian, X11Event.EXPOSE);
evt.mData.enqInt(mId);
evt.mData.enqShort((short)0); // x
evt.mData.enqShort((short)0); // y
evt.mData.enqShort(mRect.w); // width
evt.mData.enqShort(mRect.h); // height
evt.mData.enqShort((short)0); // count
c.send(evt); // XXX: thread sync?
}
}
void postRender(X11Rect r) {
mView.postInvalidate(r.x, r.y, r.x+r.w, r.y+r.h);
}
void postRender() {
mView.postInvalidate();
}
X11Point absolutePosition(X11Client c) throws X11Error {
X11Point pos = new X11Point();
if (mParent != null) {
pos = c.getWindow(mParent.mId).absolutePosition(c);
}
pos.x += mRect.x;
pos.y += mRect.y;
return pos;
}
private void doChangeAttributes(X11Client c, ByteQueue q) throws X11Error {
int mask = q.deqInt();
int val;
if (mWndClass == INPUT_ONLY) {
if ((mask & 0x25df) != 0) {
throw new X11Error(X11Error.MATCH, mask);
}
}
if ((mask & 0x0001) != 0) { // background-pixmap
val = q.deqInt();
if (val == 0 /* None */) {
mBgPixmap = null;
}
else if (val == 1 /* ParentRelative */) {
//XXX
mBgPixmap = null;
}
else {
mBgPixmap = c.getPixmap(val);
}
paintBackgroundArea(mRect);
}
if ((mask & 0x0002) != 0) { // background-pixel
mBgPixel = (q.deqInt() | 0xff000000);
paintBackgroundArea(mRect);
}
if ((mask & 0x0004) != 0) { // border-pixmap
mBorderPixmap = c.getPixmap(q.deqInt());
}
if ((mask & 0x0008) != 0) { // border-pixel
mBorderPixel = (q.deqInt() | 0xff000000);
}
if ((mask & 0x0010) != 0) { // bit-gravity
mBitGravity = (byte)q.deqInt();
}
if ((mask & 0x0020) != 0) { // win-gravity
mWinGravity = (byte)q.deqInt();
}
if ((mask & 0x0040) != 0) { // backing-store
mBackingStore = (byte)q.deqInt();
}
if ((mask & 0x0080) != 0) { // backing-planes
mBackingPlanes = q.deqInt();
}
if ((mask & 0x0100) != 0) { // backing-pixel
mBackingPixel = (q.deqInt() | 0xff000000);
}
if ((mask & 0x0200) != 0) { // override-redirect
mOverrideRedirect = (byte)q.deqInt();
}
if ((mask & 0x0400) != 0) { // save-under
mSaveUnder = (byte)q.deqInt();
}
if ((mask & 0x0800) != 0) { // event-mask
mEventMask = q.deqInt();
}
if ((mask & 0x1000) != 0) { // do-not-propagate-mask
mDoNotPropagateMask = (short)(q.deqInt() & ~0xffffc0b0);
}
if ((mask & 0x2000) != 0) { // colormap
//XXX: 0 = CopyFromParent
mColormap = c.getColormap(q.deqInt());
}
if ((mask & 0x4000) != 0) { // cursor
mCursor = c.getCursor(q.deqInt());
}
}
private void doDestroy(X11Client c) {
if (mParent == null) {
return;
}
for (X11Window child : mChildren) {
child.doDestroy(c);
}
doUnmap(c);
sendViewMessage(UIHandler.MSG_VIEW_REMOVE);
mCursor = null;
mColormap = null;
mBorderPixmap = null;
mBgPixmap = null;
mVisual = null;
mCanvas = null;
mBitmap = null;
mRect = null;
mParent.mChildren.remove(this);
mParent= null;
X11EventMessage evt = new X11EventMessage(c.mProt.mEndian, X11Event.DESTROY_NOTIFY);
evt.mData.enqInt(mId); // event
evt.mData.enqInt(mId); // window
c.send(evt);
}
private void doDestroySub(X11Client c) {
//XXX: bottom-to-top stacking order
for (X11Window child : mChildren) {
child.doDestroy(c);
}
}
private void doMap(X11Client c) {
if (mMapped) {
return;
}
//XXX: check override-redirect
mMapped = true;
if (mParent == null) {
mRealized = true;
}
else {
if (mParent.mRealized) {
doRealizeSub(c);
doRealize(c);
}
}
X11EventMessage evt = new X11EventMessage(c.mProt.mEndian, X11Event.MAP_NOTIFY);
evt.mData.enqInt(mId); // event
evt.mData.enqInt(mId); // window
evt.mData.enqByte(mOverrideRedirect);
c.send(evt);
}
private void doMapSub(X11Client c) {
for (X11Window child : mChildren) {
child.doMapSub(c);
child.doMap(c);
}
}
private void doUnmap(X11Client c) {
if (!mMapped) {
return;
}
if (mParent == null) {
return;
}
mMapped = false;
X11EventMessage evt = new X11EventMessage(c.mProt.mEndian, X11Event.UNMAP_NOTIFY);
evt.mData.enqInt(mId); // event
evt.mData.enqInt(mId); // window
evt.mData.enqByte((byte)0); // from-configure
c.send(evt);
}
private void doUnmapSub(X11Client c) {
for (X11Window child : mChildren) {
child.doUnmap(c);
child.doUnmapSub(c);
}
}
private void doRealizeSub(X11Client c) {
for (X11Window child : mChildren) {
child.doRealizeSub(c);
child.doRealize(c);
}
}
private void doRealize(X11Client c) {
Log.d("X", "Window#"+mId+".doRealize: set visible");
sendViewMessage(UIHandler.MSG_VIEW_SET_VISIBLE);
while (!mRealized) { Thread.yield(); }
X11EventMessage evt = new X11EventMessage(c.mProt.mEndian, X11Event.EXPOSE);
evt.mData.enqInt(mId);
evt.mData.enqShort((short)0); // x
evt.mData.enqShort((short)0); // y
evt.mData.enqShort(mRect.w); // width
evt.mData.enqShort(mRect.h); // height
evt.mData.enqShort((short)0); // count
c.send(evt); // XXX: thread sync?
}
void onKeyPress(X11Client c, int code) {
if ((mEventMask & X11Event.KEY_PRESS) == 0) {
return; //XXX ???
}
X11Window root = c.mServer.mDefaultScreen.mRoot;
X11EventMessage evt = new X11EventMessage(c.mProt.mEndian, X11Event.KEY_PRESS);
evt.headerData((byte)(X11Keyboard.X_MIN_KEYCODE+code));
evt.mData.enqInt(c.mServer.currentTime());
evt.mData.enqInt(root.mId);
evt.mData.enqInt(mId);
evt.mData.enqInt(0 /*None*/);
evt.mData.enqShort((short)0); // root-x
evt.mData.enqShort((short)0); // root-y
evt.mData.enqShort((short)0); // event-x
evt.mData.enqShort((short)0); // event-y
evt.mData.enqShort(c.mServer.mKeyboard.mModState);
evt.mData.enqByte((byte)1); // same-screen = True
evt.mData.enqSkip(1);
c.send(evt);
}
void onKeyRelease(X11Client c, int code) {
if ((mEventMask & X11Event.KEY_RELEASE) == 0) {
return; //XXX ???
}
X11Window root = c.mServer.mDefaultScreen.mRoot;
X11EventMessage evt = new X11EventMessage(c.mProt.mEndian, X11Event.KEY_RELEASE);
evt.headerData((byte)(X11Keyboard.X_MIN_KEYCODE+code));
evt.mData.enqInt(c.mServer.currentTime());
evt.mData.enqInt(root.mId);
evt.mData.enqInt(mId);
evt.mData.enqInt(0 /*None*/);
evt.mData.enqShort((short)0); // root-x
evt.mData.enqShort((short)0); // root-y
evt.mData.enqShort((short)0); // event-x
evt.mData.enqShort((short)0); // event-y
evt.mData.enqShort(c.mServer.mKeyboard.mModState); // state
evt.mData.enqByte((byte)1); // same-screen = True
evt.mData.enqSkip(1);
c.send(evt);
}
void onButtonPress(X11Client c, int x, int y, int num) {
if ((mEventMask & X11Event.BUTTON_PRESS) == 0) {
return; //XXX ???
}
X11Window root = c.mServer.mDefaultScreen.mRoot;
X11EventMessage evt = new X11EventMessage(c.mProt.mEndian, X11Event.BUTTON_PRESS);
evt.headerData((byte)num);
evt.mData.enqInt(c.mServer.currentTime());
evt.mData.enqInt(root.mId);
evt.mData.enqInt(mId);
evt.mData.enqInt(0 /*None*/);
evt.mData.enqShort((short)(mRect.x+x)); // root-x
evt.mData.enqShort((short)(mRect.y+y)); // root-y
evt.mData.enqShort((short)x); // event-x
evt.mData.enqShort((short)y); // event-y
evt.mData.enqShort((short)0); // state
evt.mData.enqByte((byte)1); // same-screen = True
evt.mData.enqSkip(1);
c.send(evt);
}
void onButtonRelease(X11Client c, int x, int y, int num) {
if ((mEventMask & X11Event.BUTTON_RELEASE) == 0) {
return; //XXX ???
}
X11Window root = c.mServer.mDefaultScreen.mRoot;
X11EventMessage evt = new X11EventMessage(c.mProt.mEndian, X11Event.BUTTON_RELEASE);
evt.headerData((byte)num);
evt.mData.enqInt(c.mServer.currentTime());
evt.mData.enqInt(root.mId);
evt.mData.enqInt(mId);
evt.mData.enqInt(0 /*None*/);
evt.mData.enqShort((short)(mRect.x+x)); // root-x
evt.mData.enqShort((short)(mRect.y+y)); // root-y
evt.mData.enqShort((short)x); // event-x
evt.mData.enqShort((short)y); // event-y
evt.mData.enqShort((short)0); // state
evt.mData.enqByte((byte)1); // same-screen = True
evt.mData.enqSkip(1);
c.send(evt);
}
void onMotion(X11Client c, int x, int y) {
if ((mEventMask & X11Event.MOTION_NOTIFY) == 0) {
return; //XXX ???
}
X11Window root = c.mServer.mDefaultScreen.mRoot;
X11EventMessage evt = new X11EventMessage(c.mProt.mEndian, X11Event.MOTION_NOTIFY);
evt.headerData((byte)0 /*Normal*/);
evt.mData.enqInt(c.mServer.currentTime());
evt.mData.enqInt(root.mId);
evt.mData.enqInt(mId);
evt.mData.enqInt(0 /*None*/);
evt.mData.enqShort((short)(mRect.x+x)); // root-x
evt.mData.enqShort((short)(mRect.y+y)); // root-y
evt.mData.enqShort((short)x); // event-x
evt.mData.enqShort((short)y); // event-y
evt.mData.enqShort((short)0); // state
evt.mData.enqByte((byte)1); // same-screen = True
evt.mData.enqSkip(1);
c.send(evt);
}
private void paintBackgroundArea(X11Rect r) {
mCanvas.save(Canvas.CLIP_SAVE_FLAG);
mCanvas.clipRect(r.x, r.y, r.x+r.w, r.y+r.h);
if (mBgPixel != 0) { //XXX: need a flag, 0 is a valid value
mCanvas.drawColor(0xff000000 | (mBgPixel & 0x00ffffff));
}
else if (mBgPixmap != null) {
for (int x = 0; x < mRect.w; x += mBgPixmap.mRect.w) {
for (int y = 0; y < mRect.h; y += mBgPixmap.mRect.h) {
mCanvas.drawBitmap(mBgPixmap.mBitmap, x, y, null);
}
}
}
mCanvas.restore();
}
private void sendViewMessage(int func) {
Message msg = Message.obtain(mHandler, func, this);
mHandler.sendMessage(msg);
}
}

View File

@ -0,0 +1,94 @@
package tdm.xserver;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuInflater;
import android.view.View;
import android.widget.RelativeLayout;
import java.net.ServerSocket;
public class XServer extends Activity
{
static final String TAG = "XServer";
static XServer mInstance;
RelativeLayout mLayout;
X11Server mServer;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mInstance = this;
}
protected void onResume() {
Log.e(TAG, "onResume");
super.onResume();
try {
mLayout = new RelativeLayout(this);
setContentView(mLayout);
UIHandler handler = new UIHandler(this, mLayout);
mServer = new X11Server(this, handler);
mServer.start();
}
catch (Exception e) {
Log.e(TAG, "Cannot create server", e);
finish();
return;
}
}
protected void onSaveInstanceState(Bundle outState) {
// ...?
}
protected void onPause() {
Log.e(TAG, "onPause");
super.onPause();
// ...?
}
protected void onStop() {
Log.e(TAG, "onStop");
super.onStop();
mServer.onStop();
// ...?
}
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.options_menu, menu);
// Other manipulations...
return super.onCreateOptionsMenu(menu);
}
public boolean onPrepareOptionsMenu(Menu menu) {
// Other manipulations...
return super.onPrepareOptionsMenu(menu);
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_save:
// ...
break;
case R.id.menu_delete:
// ...
break;
}
return super.onOptionsItemSelected(item);
}
}