This class implements an X Windows server.
*/
public class XServer {
public final short ProtocolMajorVersion = 11;
public final short ProtocolMinorVersion = 0;
public final String vendor = "Open source";
public final int ReleaseNumber = 0;
private final int _port;
private final Context _context;
private final String _windowManagerClass;
private final Vector _formats;
private final Hashtable<Integer, Resource> _resources;
private final Vector _clients;
private final int _clientIdBits = 20;
private final int _clientIdStep = (1 << _clientIdBits);
private int _clientIdBase = _clientIdStep;
private final Hashtable<Integer, Atom> _atoms;
private final Hashtable<String, Atom> _atomNames;
private int _maxAtomId = 0;
private final Hashtable<Integer, Selection> _selections;
private final Keyboard _keyboard;
private final Pointer _pointer;
private final Font _defaultFont;
private final Visual _rootVisual;
private ScreenView _screen = null;
private String[] _fontPath = null;
private AcceptThread _acceptThread = null;
private long _timestamp;
private Client _grabClient;
private int _screenSaverTimeout = 0;
private int _screenSaverInterval = 0;
private int _preferBlanking = 1;
private int _allowExposures = 0;
private long _screenSaverTime = 0;
private CountDownTimer _screenSaverCountDownTimer = null;
private boolean _accessControlEnabled = false;
private final HashSet _accessControlHosts;
private final Hashtable<String, Extension> _extensions;
/**
Constructor.
@param c The application context.
@param port The port to listen on. Usually 6000.
@param windowManagerClass Window manager class name. Can be null.
*/
public XServer (
Context c,
int port,
String windowManagerClass
) {
_context = c;
_port = port;
_windowManagerClass = windowManagerClass;
_formats = new Vector();
_resources = new Hashtable<Integer, Resource>();
_clients = new Vector();
_atoms = new Hashtable<Integer, Atom>();
_atomNames = new Hashtable<String, Atom>();
_selections = new Hashtable<Integer, Selection>();
_accessControlHosts = new HashSet();
_extensions = new Hashtable<String, Extension>();
_extensions.put ("Generic Event Extension",
new Extension (Extensions.XGE, (byte) 0, (byte) 0));
_extensions.put ("XTEST",
new Extension (Extensions.XTEST, (byte) 0, (byte) 0));
_extensions.put ("BIG-REQUESTS",
new Extension (Extensions.BigRequests, (byte) 0, (byte) 0));
_extensions.put ("SHAPE", new Extension (Extensions.Shape,
XShape.EventBase, (byte) 0));
_formats.add (new Format ((byte) 32, (byte) 24, (byte) 8));
_keyboard = new Keyboard ();
_pointer = new Pointer ();
_defaultFont = new Font (1, this, null, null);
addResource (_defaultFont);
addResource (new Cursor (2, this, null, (Font) null, (Font) null,
0, 1, 0xff000000, 0xffffffff));
_screen = new ScreenView (_context, this, 3, pixelsPerMillimeter ());
Colormap cmap = new Colormap (4, this, null, _screen);
cmap.setInstalled (true);
addResource (cmap);
_rootVisual = new Visual (1);
Atom.registerPredefinedAtoms (this);
_timestamp = System.currentTimeMillis ();
}
/**
Start the thread that listens on the socket.
Also start the window manager if one is specified.
@return True if the thread is started successfully.
*/
public synchronized boolean
start () {
if (_acceptThread != null)
return true; // Already running.
if (_windowManagerClass != null) {
int idx = _windowManagerClass.lastIndexOf ('.');
if (idx > 0) {
String pkg = _windowManagerClass.substring (0, idx);
Intent intent = new Intent (Intent.ACTION_MAIN);
intent.setComponent (new ComponentName (pkg,
_windowManagerClass));
try {
if (_context.startService (intent) == null)
Log.e ("XServer",
"Could not start " + _windowManagerClass);
} catch (SecurityException e) {
Log.e ("XServer", "Could not start " + _windowManagerClass
+ ": " + e.getMessage ());
}
}
}
resetScreenSaver ();
return true;
}
/**
Stop listening on the socket and terminate all clients.
*/
public synchronized void
stop () {
if (_acceptThread != null) {
_acceptThread.cancel ();
_acceptThread = null;
}
_grabClient = null;
while (!_clients.isEmpty ())
_clients.get(0).cancel ();
}
/**
Reset the server.
This should be called when the last client disconnects with a
close-down mode of Destroy.
*/
private void
reset () {
Iterator<Hashtable.Entry<Integer, Resource>>
it = _resources.entrySet().iterator ();
// Remove all client-allocated resources.
while (it.hasNext ()) {
Hashtable.Entry<Integer, Resource> entry = it.next ();
if (entry.getKey () > _clientIdStep)
it.remove ();
@param client The client issuing the grab.
*/
public void
grabServer (
Client client
) {
_grabClient = client;
}
/**
End the server grab.
@param client The client issuing the grab.
*/
public void
ungrabServer (
Client client
) {
if (_grabClient == client)
_grabClient = null;
}
/**
Return true if processing is allowed. This is only false if the
server has been grabbed by another client and the checking client
is not impervious to server grabs.
@param client The client checking if processing is allowed.
@return True if processing is allowed for the client.
*/
public boolean
processingAllowed (
Client client
) {
if (_grabClient == null || client.getImperviousToServerGrabs ())
return true;
return _grabClient == client;
}
/**
Get the X server's keyboard.
@return The keyboard used by the X server.
*/
public Keyboard
getKeyboard () {
return _keyboard;
}
/**
Get the X server's pointer.
@return The pointer used by the X server.
*/
public Pointer
getPointer () {
return _pointer;
}
/**
Get the server's font path.
@return The server's font path.
*/
public String[]
getFontPath () {
return _fontPath;
}
/**
Set the server's font path.
@param path The new font path.
*/
public void
setFontPath (
String[] path
) {
_fontPath = path;
}
/**
Return the screen attached to the display.
@return The screen attached to the display.
*/
public ScreenView
getScreen () {
return _screen;
}
/**
Return the number of pixels per millimeter on the display.
@return The number of pixels per millimeter on the display.
*/
private float
pixelsPerMillimeter () {
DisplayMetrics metrics = new DisplayMetrics();
WindowManager wm = (WindowManager) _context.getSystemService
(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics (metrics);
Font.setDpi ((int) metrics.ydpi); // Use the value since we have it.
return metrics.xdpi / 25.4f;
}
/**
Get the number of pixmap formats.
@return The number of pixmap formats.
*/
public int
getNumFormats () {
return _formats.size ();
}
/**
Write details of all the pixmap formats.
@param io The input/output stream.
@throws IOException
*/
public void
writeFormats (
InputOutput io
) throws IOException {
for (Format f: _formats)
f.write (io);
}
/**
Return the default font.
@return The default font.
*/
public Font
getDefaultFont () {
return _defaultFont;
}
/**
Return the root visual.
@return The root visual.
*/
public Visual
getRootVisual () {
return _rootVisual;
}
/**
Add an atom.
@param a The atom to add.
*/
public void
addAtom (
Atom a
) {
_atoms.put (a.getId (), a);
_atomNames.put (a.getName (), a);
if (a.getId () > _maxAtomId)
_maxAtomId = a.getId ();
}
/**
Return the atom with the specified ID.
@param id The atom ID.
@return The specified atom, or null if it doesn't exist.
*/
public Atom
getAtom (
int id
) {
if (!_atoms.containsKey (id)) // No such atom.
return null;
return _atoms.get (id);
}
/**
Return the atom with the specified name.
@param name The atom's name.
@return The specified atom, or null if it doesn't exist.
*/
public Atom
findAtom (
final String name
) {
if (!_atomNames.containsKey (name))
return null;
return _atomNames.get (name);
}
/**
Does the atom with specified ID exist?
@param id The atom ID.
@return True if an atom with the ID exists.
*/
public boolean
atomExists (
int id
) {
return _atoms.containsKey (id);
}
/**
Get the ID of the next free atom.
@return The ID of the next free atom.
*/
public int
nextFreeAtomId () {
return ++_maxAtomId;
}
/**
Return the selection with the specified ID.
@param id The selection ID.
@return The specified selection, or null if it doesn't exist.
*/
public Selection
getSelection (
int id
) {
if (!_selections.containsKey (id)) // No such selection.
return null;
return _selections.get (id);
}
/**
Add a selection.
@param sel The selection to add.
*/
public void
addSelection (
Selection sel
) {
_selections.put (sel.getId (), sel);
}
/**
Add a resource.
@param r The resource to add.
*/
public void
addResource (
Resource r
) {
_resources.put (r.getId (), r);
}
/**
Return the resource with the specified ID.
@param id The resource ID.
@return The specified resource, or null if it doesn't exist.
*/
public Resource
getResource (
int id
) {
if (!resourceExists (id))
return null;
return _resources.get (id);
}
/**
Does the resource with specified ID exist?
@param id The resource ID.
@return True if a resource with the ID exists.
*/
public boolean
resourceExists (
int id
) {
return _resources.containsKey (id);
}
/**
Free the resource with the specified ID.
@param id The resource ID.
*/
public void
freeResource (
int id
) {
_resources.remove (id);
}
/**
If client is null, destroy the resources of all clients that have
terminated in RetainTemporary mode. Otherwise destroy all resources
associated with the client, which has terminated with mode
RetainPermanant or RetainTemporary.
@param client The terminated client, or null.
*/
public synchronized void
destroyClientResources (
Client client
) {
Collection rc = _resources.values ();
Vector dl = new Vector ();
if (client == null) {
for (Resource r: rc) {
Client c = r.getClient ();
boolean disconnected = (c == null || !c.isConnected ());
if (disconnected && r.getCloseDownMode ()
== Client.RetainTemporary)
dl.add (r);
}
} else {
for (Resource r: rc)
if (r.getClient () == client)
dl.add (r);
}
for (Resource r: dl)
r.delete ();
}
/**
Send a MappingNotify to all clients.
@param request 0=Modifier, 1=Keyboard, 2=Pointer
@param firstKeycode First keycode in new keyboard map.
@param keycodeCount Number of keycodes in new keyboard map.
*/
public void
sendMappingNotify (
int request,
int firstKeycode,
int keycodeCount
) {
for (Client c: _clients) {
try {
EventCode.sendMappingNotify (c, request, firstKeycode,
keycodeCount);
} catch (IOException e) {
}
}
}
/**
Process a QueryExtension request.
@param client The remote client.
@param bytesRemaining Bytes yet to be read in the request.
@throws IOException
*/
public void
processQueryExtensionRequest (
Client client,
int bytesRemaining
) throws IOException {
InputOutput io = client.getInputOutput ();
/**
import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.Collection; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.Set; import java.util.Vector;
import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.os.CountDownTimer; import android.util.DisplayMetrics; import android.util.Log; import android.view.WindowManager; import au.com.darkside.XServer.Xext.Extensions; import au.com.darkside.XServer.Xext.XShape;
/**
This class implements an X Windows server. */ public class XServer { public final short ProtocolMajorVersion = 11; public final short ProtocolMinorVersion = 0; public final String vendor = "Open source"; public final int ReleaseNumber = 0;
private final int _port; private final Context _context; private final String _windowManagerClass; private final Vector _formats;
private final Hashtable<Integer, Resource> _resources;
private final Vector _clients;
private final int _clientIdBits = 20;
private final int _clientIdStep = (1 << _clientIdBits);
private int _clientIdBase = _clientIdStep;
private final Hashtable<Integer, Atom> _atoms; private final Hashtable<String, Atom> _atomNames; private int _maxAtomId = 0; private final Hashtable<Integer, Selection> _selections;
private final Keyboard _keyboard; private final Pointer _pointer; private final Font _defaultFont; private final Visual _rootVisual; private ScreenView _screen = null; private String[] _fontPath = null; private AcceptThread _acceptThread = null; private long _timestamp; private Client _grabClient;
private int _screenSaverTimeout = 0; private int _screenSaverInterval = 0; private int _preferBlanking = 1; private int _allowExposures = 0; private long _screenSaverTime = 0; private CountDownTimer _screenSaverCountDownTimer = null;
private boolean _accessControlEnabled = false; private final HashSet _accessControlHosts;
private final Hashtable<String, Extension> _extensions;
/**
@param windowManagerClass Window manager class name. Can be null. */ public XServer ( Context c, int port, String windowManagerClass ) { _context = c; _port = port; _windowManagerClass = windowManagerClass; _formats = new Vector();
_resources = new Hashtable<Integer, Resource>();
_clients = new Vector();
_atoms = new Hashtable<Integer, Atom>();
_atomNames = new Hashtable<String, Atom>();
_selections = new Hashtable<Integer, Selection>();
_accessControlHosts = new HashSet();
_extensions = new Hashtable<String, Extension>(); _extensions.put ("Generic Event Extension", new Extension (Extensions.XGE, (byte) 0, (byte) 0)); _extensions.put ("XTEST", new Extension (Extensions.XTEST, (byte) 0, (byte) 0)); _extensions.put ("BIG-REQUESTS", new Extension (Extensions.BigRequests, (byte) 0, (byte) 0)); _extensions.put ("SHAPE", new Extension (Extensions.Shape, XShape.EventBase, (byte) 0));
_formats.add (new Format ((byte) 32, (byte) 24, (byte) 8));
_keyboard = new Keyboard (); _pointer = new Pointer ();
_defaultFont = new Font (1, this, null, null); addResource (_defaultFont); addResource (new Cursor (2, this, null, (Font) null, (Font) null, 0, 1, 0xff000000, 0xffffffff));
_screen = new ScreenView (_context, this, 3, pixelsPerMillimeter ());
Colormap cmap = new Colormap (4, this, null, _screen);
cmap.setInstalled (true); addResource (cmap);
_rootVisual = new Visual (1); Atom.registerPredefinedAtoms (this);
_timestamp = System.currentTimeMillis (); }
/**
@return True if the thread is started successfully. */ public synchronized boolean start () { if (_acceptThread != null) return true; // Already running.
try { _acceptThread = new AcceptThread (_port); _acceptThread.start (); } catch (IOException e) { return false; }
if (_windowManagerClass != null) { int idx = _windowManagerClass.lastIndexOf ('.');
}
resetScreenSaver ();
return true; }
/**
Stop listening on the socket and terminate all clients. */ public synchronized void stop () { if (_acceptThread != null) { _acceptThread.cancel (); _acceptThread = null; }
_grabClient = null; while (!_clients.isEmpty ()) _clients.get(0).cancel (); }
/**
close-down mode of Destroy. */ private void reset () { Iterator<Hashtable.Entry<Integer, Resource>> it = _resources.entrySet().iterator ();
while (it.hasNext ()) { Hashtable.Entry<Integer, Resource> entry = it.next ();
}
_screen.removeNonDefaultColormaps ();
if (_atoms.size () != Atom.numPredefinedAtoms ()) { _atoms.clear (); _atomNames.clear (); Atom.registerPredefinedAtoms (this); }
_selections.clear (); _timestamp = System.currentTimeMillis (); }
/**
/**
@return The internet address the server is listening on. */ public InetAddress getInetAddress () { if (_acceptThread == null) return null;
return _acceptThread.getInetAddress (); }
/**
@return The number of milliseconds since the last reset. */ public int getTimestamp () { long diff = System.currentTimeMillis () - _timestamp;
if (diff <= 0) return 1;
return (int) diff; }
/**
@param client The client to remove. */ public void removeClient ( Client client ) { for (Selection sel: _selections.values ()) sel.clearClient (client);
_clients.remove (client); if (_grabClient == client) _grabClient = null;
if (client.getCloseDownMode () == Client.Destroy && _clients.size () == 0) reset (); }
/**
/**
/**
@return True if processing is allowed for the client. */ public boolean processingAllowed ( Client client ) { if (_grabClient == null || client.getImperviousToServerGrabs ()) return true;
return _grabClient == client; }
/**
/**
/**
/**
/**
/**
@return The number of pixels per millimeter on the display. */ private float pixelsPerMillimeter () { DisplayMetrics metrics = new DisplayMetrics(); WindowManager wm = (WindowManager) _context.getSystemService (Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics (metrics); Font.setDpi ((int) metrics.ydpi); // Use the value since we have it.
return metrics.xdpi / 25.4f; }
/**
/**
/**
/**
/**
@param a The atom to add. */ public void addAtom ( Atom a ) { _atoms.put (a.getId (), a); _atomNames.put (a.getName (), a);
if (a.getId () > _maxAtomId) _maxAtomId = a.getId (); }
/**
@return The specified atom, or null if it doesn't exist. */ public Atom getAtom ( int id ) { if (!_atoms.containsKey (id)) // No such atom. return null;
return _atoms.get (id); }
/**
@return The specified atom, or null if it doesn't exist. */ public Atom findAtom ( final String name ) { if (!_atomNames.containsKey (name)) return null;
return _atomNames.get (name); }
/**
/**
/**
@return The specified selection, or null if it doesn't exist. */ public Selection getSelection ( int id ) { if (!_selections.containsKey (id)) // No such selection. return null;
return _selections.get (id); }
/**
/**
/**
@return The specified resource, or null if it doesn't exist. */ public Resource getResource ( int id ) { if (!resourceExists (id)) return null;
return _resources.get (id); }
/**
/**
/**
@param client The terminated client, or null. */ public synchronized void destroyClientResources ( Client client ) { Collection rc = _resources.values ();
Vector dl = new Vector ();
if (client == null) { for (Resource r: rc) { Client c = r.getClient (); boolean disconnected = (c == null || !c.isConnected ());
} else { for (Resource r: rc) if (r.getClient () == client) dl.add (r); }
for (Resource r: dl) r.delete (); }
/**
/**
@throws IOException */ public void processQueryExtensionRequest ( Client client, int bytesRemaining ) throws IOException { InputOutput io = client.getInputOutput ();
if (bytesRemaining < 4) { io.readSkip (bytesRemaining); ErrorCode.write (client, ErrorCode.Length, RequestCode.QueryExtension, 0); return; }
int length = io.readShort (); // Length of name. int pad = -length & 3;
io.readSkip (2); // Unused. bytesRemaining -= 4;
if (bytesRemaining != length + pad) { io.readSkip (bytesRemaining); ErrorCode.write (client, ErrorCode.Length, RequestCode.QueryExtension, 0); return; }
byte[] bytes = new byte[length];
io.readBytes(bytes, 0, length); io.readSkip (pad); // Unused.
String s = new String (bytes); Extension e;
if (_extensions.containsKey (s)) e = _extensions.get (s); else e = null;
synchronized (io) { Util.writeReplyHeader (client, (byte) 0); io.writeInt (0); // Reply length.
} io.flush (); }
/**
@throws IOException */ public void writeListExtensions ( Client client ) throws IOException { Set ss = _extensions.keySet ();
int length = 0;
for (String s: ss) length += s.length () + 1;
int pad = -length & 3; InputOutput io = client.getInputOutput ();
synchronized (io) { Util.writeReplyHeader (client, (byte) ss.size ()); io.writeInt ((length + pad) / 4); // Reply length. io.writePadBytes (24); // Unused.
} io.flush (); }
/**
@throws IOException */ public void processChangeHostsRequest ( Client client, int mode, int bytesRemaining ) throws IOException { InputOutput io = client.getInputOutput ();
if (bytesRemaining < 4) { io.readSkip (bytesRemaining); ErrorCode.write (client, ErrorCode.Length, RequestCode.ChangeHosts, 0); return; }
int family = io.readByte (); // 0=Inet, 1=DECnet, 2=Chaos.
io.readSkip (1); // Unused.
int length = io.readShort (); // Length of address. int pad = -length & 3;
bytesRemaining -= 4; if (bytesRemaining != length + pad) { io.readSkip (bytesRemaining); ErrorCode.write (client, ErrorCode.Length, RequestCode.ChangeHosts, 0); return; }
if (family != 0 || length != 4) { io.readSkip (bytesRemaining); ErrorCode.write (client, ErrorCode.Value, RequestCode.ChangeHosts, 0); return; }
int address = 0;
for (int i = 0; i < length; i++) address = (address << 8) | io.readByte ();
io.readSkip (pad); // Unused.
if (mode == 0) _accessControlHosts.add (address); else _accessControlHosts.remove (address); }
/**
@throws IOException */ public void writeListHosts ( Client client ) throws IOException { InputOutput io = client.getInputOutput (); int n = _accessControlHosts.size ();
synchronized (io) { Util.writeReplyHeader (client, (byte) (_accessControlEnabled ? 1 : 0)); io.writeInt (n * 2); // Reply length. io.writeShort ((short) n); // Number of hosts. io.writePadBytes (22); // Unused.
} io.flush (); }
/**
/**
/**
@return True if the client is allowed to exist. */ private boolean isAccessAllowed ( int address ) { if (!_accessControlEnabled) return true;
return _accessControlHosts.contains (address); }
/**
@param allowExposures 0=No, 1=Yes, 2=Default. */ public void setScreenSaver ( int timeout, int interval, int preferBlanking, int allowExposures ) { if (timeout == -1) _screenSaverTimeout = 0; // Default timeout. else _screenSaverTimeout = timeout;
if (interval == -1) _screenSaverInterval = 0; // Default interval. else _screenSaverInterval = interval;
_preferBlanking = preferBlanking; _allowExposures = allowExposures;
resetScreenSaver (); }
/**
Called when we'd potentially want to blank the screen. */ private void checkScreenBlank () { if (_screenSaverTimeout == 0) // Disabled. return;
long offset = (_screenSaverTime + _screenSaverTimeout) * 1000
if (offset < 1000) { _screen.blank (true); return; }
_screenSaverCountDownTimer = new CountDownTimer (offset, offset + 1) { public void onTick (long millis) {} public void onFinish () { _screenSaverCountDownTimer = null; checkScreenBlank (); } }; _screenSaverCountDownTimer.start (); }
/**
Reset the screen saver timer. */ public void resetScreenSaver () { long now = System.currentTimeMillis () / 1000;
if (now == _screenSaverTime) return;
_screenSaverTime = now; if (_screenSaverCountDownTimer == null) checkScreenBlank (); }
/**
@throws IOException */ public void writeScreenSaver ( Client client ) throws IOException { InputOutput io = client.getInputOutput ();
synchronized (io) { Util.writeReplyHeader (client, (byte) 0); io.writeInt (0); // Reply length. io.writeShort ((short) _screenSaverTimeout); // Timeout. io.writeShort ((short) _screenSaverInterval); // Interval. io.writeByte ((byte) _preferBlanking); // Prefer blanking. io.writeByte ((byte) _allowExposures); // Allow exposures. io.writePadBytes (18); // Unused. } io.flush (); }
/**
@author Matthew Kwan */ private class Extension { private final byte majorOpcode; private final byte firstEvent; private final byte firstError;
/**
/**
@author Matthew Kwan */ private class AcceptThread extends Thread { private final ServerSocket _serverSocket;
/**
/**
/**
Run the thread. */ public void run () { while (true) { Socket socket;
} }
/**