4
.github/workflows/android.yml
vendored
|
|
@ -68,7 +68,7 @@ jobs:
|
|||
"-F chat_id=${{ secrets.TELEGRAM_CHAT_ID_VEC }}"; do
|
||||
|
||||
if curl -s -o /dev/null -F document=@"$file" $target \
|
||||
-F $'caption=Done! Note that this is a version that is automatically built when there are changes in the GitHub repository, not the official version. Please only install it if you really want to see what is new.\nRun ID: '${GITHUB_RUN_ID} \
|
||||
-F $'caption=Done! Note that this is a version that is automatically built when there are changes in the GitHub repository, not the official version. Please only install it if you really want to see what is new.\nExplore beta releases here: https://github.com/AnBui2004/Vectras-VM-Emu-Android/releases\nRun ID: '${GITHUB_RUN_ID} \
|
||||
https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendDocument; then
|
||||
success_targets=true
|
||||
else
|
||||
|
|
@ -96,7 +96,7 @@ jobs:
|
|||
"-d chat_id=${{ secrets.TELEGRAM_CHAT_ID_VEC }}"; do
|
||||
curl -s -o /dev/null -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
|
||||
$chat \
|
||||
-d $'text=Something went wrong and the APK file could not be uploaded.\nRun ID: '${GITHUB_RUN_ID}
|
||||
-d $'text=Something went wrong and the APK file could not be uploaded.\nExplore beta releases here: https://github.com/AnBui2004/Vectras-VM-Emu-Android/releases\nRun ID: '${GITHUB_RUN_ID}
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ android {
|
|||
applicationId "com.vectras.vm"
|
||||
minSdk minApi
|
||||
targetSdk targetApi
|
||||
versionCode 47
|
||||
versionName "3.4.3"
|
||||
versionCode 48
|
||||
versionName "3.4.4"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
multiDexEnabled true
|
||||
}
|
||||
|
|
@ -113,7 +113,6 @@ dependencies {
|
|||
implementation 'androidx.activity:activity-ktx:1.12.1'
|
||||
compileOnly project(':shell-loader:stub')
|
||||
implementation project(":terminal-view")
|
||||
implementation project(":library")
|
||||
|
||||
// Retrofit
|
||||
implementation 'com.squareup.retrofit2:retrofit:3.0.0'
|
||||
|
|
|
|||
|
|
@ -248,25 +248,6 @@
|
|||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name="au.com.darkside.xdemo.XServerService"
|
||||
android:stopWithTask="false" />
|
||||
|
||||
<activity
|
||||
android:name="au.com.darkside.xdemo.XServerActivity"
|
||||
android:configChanges="keyboardHidden"
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="sensorLandscape">
|
||||
<intent-filter>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:scheme="x11" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="au.com.darkside.xdemo.AccessControlEditor"
|
||||
android:label="Authorized IP addresses" />
|
||||
|
||||
<meta-data
|
||||
android:name="com.samsung.android.multidisplay.keep_process_alive"
|
||||
android:value="false" />
|
||||
|
|
|
|||
|
|
@ -1,205 +0,0 @@
|
|||
package au.com.darkside.xdemo;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.ListActivity;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.vectras.vm.R;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Editor for the list of hosts allowed to access the X server.
|
||||
*
|
||||
* Written by Matthew Kwan - March 2012
|
||||
*
|
||||
* @author mkwan
|
||||
*/
|
||||
public class AccessControlEditor extends ListActivity implements OnItemClickListener {
|
||||
private ArrayAdapter<String> _adapter;
|
||||
private EditText _hostField;
|
||||
private int _deletePosition = -1;
|
||||
|
||||
/**
|
||||
* Called when the activity is first created.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_access_control_editor_x_server);
|
||||
|
||||
_hostField = findViewById(R.id.host_field);
|
||||
|
||||
Button button;
|
||||
|
||||
button = findViewById(R.id.add_button);
|
||||
button.setOnClickListener(v -> addHost());
|
||||
|
||||
button = findViewById(R.id.cancel_button);
|
||||
button.setOnClickListener(v -> {
|
||||
setResult(RESULT_CANCELED, null);
|
||||
finish();
|
||||
});
|
||||
|
||||
button = findViewById(R.id.apply_button);
|
||||
button.setOnClickListener(v -> {
|
||||
saveAccessList();
|
||||
setResult(RESULT_OK, null);
|
||||
finish();
|
||||
});
|
||||
|
||||
getListView().setOnItemClickListener(this);
|
||||
loadAccessList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a list item is selected.
|
||||
*/
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
|
||||
_deletePosition = position;
|
||||
getDeleteHostDialog().show();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The Dialog to delete a host.
|
||||
*/
|
||||
private Dialog getDeleteHostDialog() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
|
||||
builder.setMessage("Delete the IP address?").setPositiveButton("OK", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
deleteSelectedHost();
|
||||
}
|
||||
}).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the host that was selected by the user.
|
||||
*/
|
||||
private void deleteSelectedHost() {
|
||||
if (_deletePosition < 0) return;
|
||||
|
||||
_adapter.remove(_adapter.getItem(_deletePosition));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a hexadecimal IP address into a human-readable dot-separated
|
||||
* format.
|
||||
*
|
||||
* @param host The host IP address, in hexadecimal format.
|
||||
* @return The host IP address in dot-separated format.
|
||||
*/
|
||||
private static String hostToString(String host) {
|
||||
int n;
|
||||
|
||||
try {
|
||||
n = (int) Long.parseLong(host, 16);
|
||||
} catch (Exception e) {
|
||||
return "Error";
|
||||
}
|
||||
|
||||
int b1 = (n >> 24) & 0xff;
|
||||
int b2 = (n >> 16) & 0xff;
|
||||
int b3 = (n >> 8) & 0xff;
|
||||
int b4 = n & 0xff;
|
||||
|
||||
return b1 + "." + b2 + "." + b3 + "." + b4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the list of hosts that are allowed to access the X server.
|
||||
*/
|
||||
private void loadAccessList() {
|
||||
SharedPreferences prefs = getSharedPreferences("AccessControlHosts", MODE_PRIVATE);
|
||||
Map<String, ?> map = prefs.getAll();
|
||||
Set<String> set = map.keySet();
|
||||
LinkedList<String> hosts = new LinkedList<String>();
|
||||
|
||||
for (String s : set)
|
||||
hosts.add(hostToString(s));
|
||||
|
||||
_adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, hosts);
|
||||
setListAdapter(_adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a human-readable dot-separated IP address into hexadecimal.
|
||||
* Return null if there's a parse error.
|
||||
*
|
||||
* @param s The host IP address, in dot-separated format.
|
||||
* @return The host IP address in hexadecimal format, or null.
|
||||
*/
|
||||
private static String stringToHost(String s) {
|
||||
String[] sa = s.split("\\.");
|
||||
|
||||
if (sa.length != 4) return null;
|
||||
|
||||
int n = 0;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int b;
|
||||
|
||||
try {
|
||||
b = Integer.parseInt(sa[i]);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (b < 0 || b > 255) return null;
|
||||
|
||||
n = (n << 8) | b;
|
||||
}
|
||||
|
||||
return Integer.toHexString(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the list of hosts that are allowed to access the X server.
|
||||
*/
|
||||
private void saveAccessList() {
|
||||
SharedPreferences prefs = getSharedPreferences("AccessControlHosts", MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
|
||||
editor.clear();
|
||||
|
||||
int n = _adapter.getCount();
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
String host = stringToHost(Objects.requireNonNull(_adapter.getItem(i)));
|
||||
|
||||
if (host != null) editor.putBoolean(host, true);
|
||||
}
|
||||
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the IP address in the host field and add it to the list.
|
||||
*/
|
||||
private void addHost() {
|
||||
String s = _hostField.getText().toString();
|
||||
|
||||
if (stringToHost(s) != null) _adapter.add(s);
|
||||
else Toast.makeText(this, "Bad IP address '" + s + "'", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,624 +0,0 @@
|
|||
package au.com.darkside.xdemo;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.Service;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import au.com.darkside.xserver.ScreenView;
|
||||
import au.com.darkside.xserver.XServer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.ProcessBuilder;
|
||||
import java.lang.Class;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import android.content.res.AssetManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.res.Configuration;
|
||||
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.vectras.qemu.Config;
|
||||
import com.vectras.vm.MainService;
|
||||
import com.vectras.vm.R;
|
||||
import com.vectras.vm.VMManager;
|
||||
import com.vectras.vm.databinding.ActivityMainXServerBinding;
|
||||
import com.vectras.vm.home.core.HomeStartVM;
|
||||
import com.vectras.vm.utils.DialogUtils;
|
||||
import com.vectras.vm.utils.FileUtils;
|
||||
import com.vectras.vm.utils.SimulateKeyEvent;
|
||||
|
||||
/**
|
||||
* This activity launches an X server and provides a screen for it.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
public class XServerActivity extends AppCompatActivity {
|
||||
private final String TAG = "XServerActivity";
|
||||
ActivityMainXServerBinding binding;
|
||||
private XServer _xServer;
|
||||
private ScreenView _screenView;
|
||||
private WakeLock _wakeLock;
|
||||
|
||||
private static final String NOTIFICATION_CHANNEL_DEFAULT = "default";
|
||||
|
||||
private static final int MENU_KEYBOARD = 1;
|
||||
private static final int MENU_IP_ADDRESS = 2;
|
||||
private static final int MENU_ACCESS_CONTROL = 3;
|
||||
private static final int MENU_REMOTE_LOGIN = 4;
|
||||
private static final int MENU_TOGGLE_ARROWS = 5;
|
||||
private static final int MENU_TOGGLE_BACKBUTTON = 6;
|
||||
private static final int MENU_TOGGLE_TOUCHCLICKS = 7;
|
||||
private static final int MENU_TOGGLE_WINDOWMANAGER = 8;
|
||||
private static final int MENU_TOGGLE_ORIENTATION = 9;
|
||||
private static final int MENU_TOGGLE_SHARED_CLIPBOARD = 10;
|
||||
private static final int ACTIVITY_ACCESS_CONTROL = 1;
|
||||
|
||||
private static final int DEFAULT_PORT = 6000;
|
||||
private static final String PORT_DESC_PRE = "Listening on port ";
|
||||
|
||||
private int _port = DEFAULT_PORT;
|
||||
private String _portDescription = PORT_DESC_PRE + DEFAULT_PORT;
|
||||
private Process _windowManager;
|
||||
|
||||
/**
|
||||
* Called when the activity is first created.
|
||||
*
|
||||
* @param savedInstanceState Saved state.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = ActivityMainXServerBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
// copyAssetData(""); // copy all assets to getApplicationInfo().dataDir directory
|
||||
|
||||
int port = DEFAULT_PORT;
|
||||
Intent intent = getIntent();
|
||||
|
||||
// If it was launched from an intent, get the port number.
|
||||
if (intent != null) {
|
||||
Uri uri = intent.getData();
|
||||
|
||||
if (uri != null) {
|
||||
int p = uri.getPort();
|
||||
|
||||
if (p >= 0) {
|
||||
if (p < 10) // Using ports 0-9 is bad juju.
|
||||
port = p + DEFAULT_PORT;
|
||||
else port = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_port = port;
|
||||
if (_port != DEFAULT_PORT) _portDescription = PORT_DESC_PRE + _port;
|
||||
|
||||
_xServer = new XServer(this, port, null);
|
||||
|
||||
// execute binary on start (if there was any packed into the assets folder)
|
||||
_xServer.setOnStartListener(new XServer.OnXSeverStartListener() {
|
||||
@Override
|
||||
public void onStart() {
|
||||
// execute our program
|
||||
try {
|
||||
File file = new File(getApplicationInfo().nativeLibraryDir + "/libbinary.so");
|
||||
if(file.exists()){
|
||||
if (!file.setExecutable(true)) Log.e(TAG, "Execution of libbinary.so failed."); // make program executable
|
||||
ProcessBuilder pb = new ProcessBuilder(file.getPath());
|
||||
Map<String, String> env = pb.environment();
|
||||
env.put("DISPLAY", "127.0.0.1:0");
|
||||
pb.directory(new File(getApplicationInfo().dataDir)); // execute within data-dir
|
||||
Process process = pb.start();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setAccessControl();
|
||||
_screenView = _xServer.getScreen();
|
||||
|
||||
if (_screenView != null) {
|
||||
// FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(1024, 768);
|
||||
// _screenView.setLayoutParams(params);
|
||||
binding.frame.addView(_screenView);
|
||||
}
|
||||
|
||||
PowerManager pm;
|
||||
|
||||
pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||
_wakeLock = pm.newWakeLock(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, getPackageName() + ":XServer");
|
||||
|
||||
// make window fullscreen
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
|
||||
startService(new Intent(this, XServerService.class));
|
||||
|
||||
/*
|
||||
* Create notification channel as it required for notifications on Android >= 8
|
||||
* Use reflection to stay backward compatible with sdk provided by debian
|
||||
*/
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
CharSequence name = "Default channel";
|
||||
String description = "Default notification channel of XServer demo";
|
||||
int importance = 3; // default importance
|
||||
|
||||
try{
|
||||
Class<?> nc = Class.forName("android.app.NotificationChannel");
|
||||
Object ncObj = nc.getConstructor(new Class[] {String.class, CharSequence.class, int.class}).newInstance(NOTIFICATION_CHANNEL_DEFAULT, name, importance);
|
||||
nc.getMethod("setDescription", String.class).invoke(ncObj, description);
|
||||
nc.getMethod("setVibrationPattern", long[].class).invoke(ncObj, (Object) new long[]{ 0 }); // enableVibration is bugged, use this as workaround
|
||||
nc.getMethod("enableVibration", boolean.class).invoke(ncObj, true);
|
||||
nc.getMethod("enableLights", boolean.class).invoke(ncObj, false);
|
||||
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
manager.getClass().getMethod("createNotificationChannel", nc).invoke(manager, ncObj);
|
||||
}
|
||||
catch(Exception e){
|
||||
Log.e("FATAL", "Could not reflect Android SDK >= 26", e);
|
||||
}
|
||||
}
|
||||
|
||||
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
|
||||
@Override
|
||||
public void handleOnBackPressed() {
|
||||
binding.lnTools.setVisibility(binding.lnTools.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
});
|
||||
|
||||
initializeToolBar();
|
||||
HomeStartVM.startPending(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity resumes.
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
manager.cancel(1);
|
||||
_wakeLock.acquire();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity pauses.
|
||||
*/
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, getIntent(), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
Notification.Builder nb = new Notification.Builder(this)
|
||||
.setSmallIcon(android.R.drawable.ic_menu_view)
|
||||
.setContentTitle("Running!")
|
||||
.setContentText("XServer running in background.")
|
||||
.setContentIntent(pendingIntent)
|
||||
.setOngoing(true);
|
||||
|
||||
/*
|
||||
* Set notification channel as it required for notifications on Android >= 8
|
||||
* Use reflection to stay backward compatible with sdk provided by debian
|
||||
*/
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
try{
|
||||
nb.getClass().getMethod("setChannelId", String.class).invoke(nb, NOTIFICATION_CHANNEL_DEFAULT);
|
||||
}
|
||||
catch(Exception e){
|
||||
Log.e("FATAL", "Could not reflect Android SDK >= 26", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
manager.notify(1, nb.build());
|
||||
|
||||
_wakeLock.release();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity is destroyed.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
_xServer.stop();
|
||||
super.onDestroy();
|
||||
|
||||
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
manager.cancel(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called the first time a menu is needed.
|
||||
*
|
||||
* @param menu The options menu in which you place your items.
|
||||
* @return True for the menu to be displayed.
|
||||
*/
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuItem item;
|
||||
|
||||
item = menu.add(0, MENU_KEYBOARD, 0, "Keyboard");
|
||||
item.setIcon(android.R.drawable.ic_menu_add);
|
||||
|
||||
item = menu.add(0, MENU_IP_ADDRESS, 0, "IP address");
|
||||
item.setIcon(android.R.drawable.ic_menu_info_details);
|
||||
|
||||
item = menu.add(0, MENU_ACCESS_CONTROL, 0, "Access control");
|
||||
item.setIcon(android.R.drawable.ic_menu_edit);
|
||||
|
||||
item = menu.add(0, MENU_REMOTE_LOGIN, 0, "Remote login");
|
||||
item.setIcon(android.R.drawable.ic_menu_upload);
|
||||
|
||||
item = menu.add(0, MENU_TOGGLE_ARROWS, 0, "Arrows as Mouseclicks (off)");
|
||||
item.setIcon(android.R.drawable.star_off);
|
||||
|
||||
item = menu.add(0, MENU_TOGGLE_BACKBUTTON, 0, "Inhibit back button (off)");
|
||||
item.setIcon(android.R.drawable.star_off);
|
||||
|
||||
item = menu.add(0, MENU_TOGGLE_TOUCHCLICKS, 0, "Touch Mouseclicks (on)");
|
||||
item.setIcon(android.R.drawable.star_on);
|
||||
|
||||
item = menu.add(0, MENU_TOGGLE_WINDOWMANAGER, 0, "Window Manager (off)");
|
||||
item.setIcon(android.R.drawable.star_on);
|
||||
|
||||
item = menu.add(0, MENU_TOGGLE_SHARED_CLIPBOARD, 0, "Shared Clipboard (on)");
|
||||
item.setIcon(android.R.drawable.star_on);
|
||||
|
||||
item = menu.add(0, MENU_TOGGLE_ORIENTATION, 0, "Screen Orientation (H)");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a menu selection has been made.
|
||||
*
|
||||
* @param item The menu item that was selected.
|
||||
* @return True if the menu selection has been handled.
|
||||
*/
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
super.onOptionsItemSelected(item);
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case MENU_KEYBOARD:
|
||||
InputMethodManager imm = (InputMethodManager) getSystemService(Service.INPUT_METHOD_SERVICE);
|
||||
|
||||
// If anyone knows a better way to bring up the soft
|
||||
// keyboard, I'd love to hear about it.
|
||||
_screenView.requestFocus();
|
||||
imm.hideSoftInputFromWindow(_screenView.getWindowToken(), 0);
|
||||
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
|
||||
return true;
|
||||
case MENU_IP_ADDRESS:
|
||||
getMenuIpAdressDialog().show();
|
||||
return true;
|
||||
case MENU_ACCESS_CONTROL:
|
||||
launchAccessControlEditor();
|
||||
return true;
|
||||
case MENU_REMOTE_LOGIN:
|
||||
launchSshApp();
|
||||
return true;
|
||||
case MENU_TOGGLE_ARROWS:
|
||||
if (_xServer.getScreen().toggleArrowsAsButtons()) {
|
||||
item.setIcon(android.R.drawable.star_on);
|
||||
item.setTitle("Arrows as Mouseclicks (on)");
|
||||
} else {
|
||||
item.setIcon(android.R.drawable.star_off);
|
||||
item.setTitle("Arrows as Mouseclicks (off)");
|
||||
}
|
||||
return true;
|
||||
case MENU_TOGGLE_BACKBUTTON:
|
||||
if (_xServer.getScreen().toggleInhibitBackButton()) {
|
||||
item.setIcon(android.R.drawable.star_on);
|
||||
item.setTitle("Inhibit back button (on)");
|
||||
} else {
|
||||
item.setIcon(android.R.drawable.star_off);
|
||||
item.setTitle("Inhibit back button (off)");
|
||||
}
|
||||
return true;
|
||||
case MENU_TOGGLE_TOUCHCLICKS:
|
||||
if (_xServer.getScreen().toggleEnableTouchClicks()) {
|
||||
item.setIcon(android.R.drawable.star_on);
|
||||
item.setTitle("Touch Mouseclicks (on)");
|
||||
} else {
|
||||
item.setIcon(android.R.drawable.star_off);
|
||||
item.setTitle("Touch Mouseclicks (off)");
|
||||
}
|
||||
return true;
|
||||
case MENU_TOGGLE_SHARED_CLIPBOARD:
|
||||
if (_xServer.getScreen().toggleSharedClipboard()) {
|
||||
item.setIcon(android.R.drawable.star_on);
|
||||
item.setTitle("Shared Clipboard (on)");
|
||||
} else {
|
||||
item.setIcon(android.R.drawable.star_off);
|
||||
item.setTitle("Shared Clipboard (off)");
|
||||
}
|
||||
return true;
|
||||
case MENU_TOGGLE_WINDOWMANAGER:
|
||||
if (_windowManager == null) {
|
||||
try {
|
||||
File file = new File(getApplicationInfo().nativeLibraryDir + "/libwm.so");
|
||||
if (!file.setExecutable(true)) Log.e(TAG, "Execution of libwm.so failed."); // make program executable
|
||||
ProcessBuilder pb = new ProcessBuilder(file.getPath());
|
||||
Map<String, String> env = pb.environment();
|
||||
env.put("DISPLAY", "127.0.0.1:0");
|
||||
pb.directory(new File(getApplicationInfo().dataDir)); // execute within dataDir
|
||||
_windowManager = pb.start();
|
||||
item.setIcon(android.R.drawable.star_on);
|
||||
item.setTitle("Window Manager (on)");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
_windowManager.destroy();
|
||||
_windowManager = null;
|
||||
item.setIcon(android.R.drawable.star_off);
|
||||
item.setTitle("Window Manager (off)");
|
||||
}
|
||||
return true;
|
||||
case MENU_TOGGLE_ORIENTATION:
|
||||
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
item.setTitle("Screen Orientation (V)");
|
||||
} else {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
||||
item.setTitle("Screen Orientation (H)");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string describing the IP address(es) of this device.
|
||||
*
|
||||
* @return A string describing the IP address(es) of this device.
|
||||
*/
|
||||
private String getAddressInfo() {
|
||||
String s = _portDescription;
|
||||
|
||||
try {
|
||||
for (Enumeration<NetworkInterface> nie = NetworkInterface.getNetworkInterfaces(); nie.hasMoreElements(); ) {
|
||||
NetworkInterface ni = nie.nextElement();
|
||||
|
||||
for (Enumeration<InetAddress> iae = ni.getInetAddresses(); iae.hasMoreElements(); ) {
|
||||
InetAddress ia = iae.nextElement();
|
||||
|
||||
if (ia.isLoopbackAddress()) continue;
|
||||
|
||||
s += "\n" + ni.getDisplayName() + ": " + ia.getHostAddress();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
s += "\nError: " + e.getMessage();
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The Dialog to enter the server IP Adress.
|
||||
*/
|
||||
private Dialog getMenuIpAdressDialog() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle("IP address").setMessage(getAddressInfo()).setPositiveButton("OK", (dialog, id) -> dialog.cancel());
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load the access control hosts from persistent storage.
|
||||
*/
|
||||
private void setAccessControl() {
|
||||
SharedPreferences prefs = getSharedPreferences("AccessControlHosts", MODE_PRIVATE);
|
||||
Map<String, ?> map = prefs.getAll();
|
||||
HashSet<Integer> hosts = _xServer.getAccessControlHosts();
|
||||
|
||||
hosts.clear();
|
||||
if (!map.isEmpty()) {
|
||||
Set<String> set = map.keySet();
|
||||
|
||||
for (String s : set) {
|
||||
try {
|
||||
int host = (int) Long.parseLong(s, 16);
|
||||
|
||||
hosts.add(host);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "setAccessControl: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_xServer.setAccessControl(!hosts.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an activity returns a result.
|
||||
*/
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (resultCode == RESULT_OK) {
|
||||
if (requestCode == ACTIVITY_ACCESS_CONTROL) {
|
||||
setAccessControl();
|
||||
} else {
|
||||
Uri content_describer = data.getData();
|
||||
File selectedFilePath = new File(getPath(content_describer));
|
||||
|
||||
switch (requestCode) {
|
||||
case 120:
|
||||
VMManager.changeCDROM(selectedFilePath.getAbsolutePath(), this);
|
||||
break;
|
||||
case 889:
|
||||
VMManager.changeFloppyDriveA(selectedFilePath.getAbsolutePath(), this);
|
||||
break;
|
||||
case 13335:
|
||||
VMManager.changeFloppyDriveB(selectedFilePath.getAbsolutePath(), this);
|
||||
break;
|
||||
case 32:
|
||||
VMManager.changeSDCard(selectedFilePath.getAbsolutePath(), this);
|
||||
break;
|
||||
case 1996:
|
||||
VMManager.changeRemovableDevice(VMManager.pendingDeviceID, selectedFilePath.getAbsolutePath(), this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch the access control list editor.
|
||||
*/
|
||||
private void launchAccessControlEditor() {
|
||||
Intent intent = new Intent(this, AccessControlEditor.class);
|
||||
|
||||
startActivityForResult(intent, ACTIVITY_ACCESS_CONTROL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch an application.
|
||||
*/
|
||||
private boolean launchApp(String pkg, String cls) {
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
|
||||
intent.setComponent(new ComponentName(pkg, cls));
|
||||
try {
|
||||
startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch an application that will allow an SSH login.
|
||||
*/
|
||||
private void launchSshApp() {
|
||||
if (launchApp("org.connectbot", "org.connectbot.HostListActivity")) return;
|
||||
if (launchApp("com.madgag.ssh.agent", "com.madgag.ssh.agent.HostListActivity")) return;
|
||||
if (launchApp("sk.vx.connectbot", "sk.vx.connectbot.HostListActivity")) return;
|
||||
|
||||
Toast.makeText(this, "The ConnectBot application needs to be installed", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
private void copyAssetData(String path) {
|
||||
AssetManager assetManager = this.getAssets();
|
||||
String[] assets;
|
||||
try {
|
||||
assets = assetManager.list(path);
|
||||
assert assets != null;
|
||||
if (assets.length == 0) {
|
||||
copyFile(path);
|
||||
} else {
|
||||
String fullPath = getApplicationInfo().dataDir + "/" + path;
|
||||
File dir = new File(fullPath);
|
||||
if (!dir.exists())
|
||||
if (!dir.mkdir()) Log.e(TAG, "Unable to copy asset: " + fullPath);
|
||||
for (String asset : assets) {
|
||||
Log.i(asset, "Info");
|
||||
if (path.isEmpty())
|
||||
copyAssetData(asset);
|
||||
else
|
||||
copyAssetData(path + "/" + asset);
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Log.e("tag", "I/O Exception", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyFile(String filename) {
|
||||
AssetManager assetManager = this.getAssets();
|
||||
InputStream in;
|
||||
OutputStream out;
|
||||
try {
|
||||
in = assetManager.open(filename);
|
||||
String newFileName = getApplicationInfo().dataDir + "/" + filename;
|
||||
out = new FileOutputStream(newFileName);
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
int read;
|
||||
while ((read = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, read);
|
||||
}
|
||||
in.close();
|
||||
out.flush();
|
||||
out.close();
|
||||
} catch (Exception e) {
|
||||
Log.e("tag", Objects.requireNonNull(e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeToolBar() {
|
||||
binding.btnPower.setOnClickListener(v -> DialogUtils.threeDialog(this, getString(R.string.power), getString(R.string.shutdown_or_reset_content), getString(R.string.shutdown), getString(R.string.reset), getString(R.string.close), true, R.drawable.power_settings_new_24px, true,
|
||||
this::shutdownthisvm, VMManager::resetCurrentVM, null, null));
|
||||
|
||||
binding.btnDevices.setOnClickListener(v -> VMManager.showChangeRemovableDevicesDialog(this, null));
|
||||
|
||||
binding.btnKeyboard.setOnClickListener(v -> {
|
||||
InputMethodManager imm = (InputMethodManager) getSystemService(Service.INPUT_METHOD_SERVICE);
|
||||
|
||||
// If anyone knows a better way to bring up the soft
|
||||
// keyboard, I'd love to hear about it.
|
||||
_screenView.requestFocus();
|
||||
imm.hideSoftInputFromWindow(_screenView.getWindowToken(), 0);
|
||||
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
|
||||
});
|
||||
|
||||
binding.btnForcustoqemu.setOnClickListener(v -> SimulateKeyEvent.qemuForcus(this));
|
||||
|
||||
binding.btnZoomin.setOnClickListener(v -> SimulateKeyEvent.qemuZoomIn(this));
|
||||
|
||||
binding.btnZoomout.setOnClickListener(v -> SimulateKeyEvent.qemuZoomOut(this));
|
||||
}
|
||||
|
||||
private void shutdownthisvm() {
|
||||
VMManager.shutdownCurrentVM();
|
||||
Config.setDefault();
|
||||
MainService.stopService();
|
||||
finish();
|
||||
}
|
||||
|
||||
public String getPath(Uri uri) {
|
||||
return FileUtils.getPath(this, uri);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
// only needed to capture kill of app, here used to get rid off all notifications
|
||||
package au.com.darkside.xdemo;
|
||||
|
||||
import android.app.Service;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public class XServerService extends Service {
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskRemoved(Intent rootIntent) {
|
||||
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
manager.cancelAll();
|
||||
}
|
||||
}
|
||||
|
|
@ -111,9 +111,6 @@ public class MainService extends Service {
|
|||
|
||||
public static void startCommand(String _env, Context _context) {
|
||||
Terminal vterm = new Terminal(_context);
|
||||
if (Build.VERSION.SDK_INT < 34) {
|
||||
vterm.executeShellCommand2("dwm", false, _context);
|
||||
}
|
||||
vterm.executeShellCommand2(_env, true, _context);
|
||||
}
|
||||
}
|
||||
|
|
@ -102,22 +102,18 @@ public class DisplaySystem {
|
|||
} else {
|
||||
if (SDK_INT >= 34) {
|
||||
Log.d(TAG, "launchX11: Opened: com.termux.x11.MainActivity.");
|
||||
// activity.startActivity(new Intent(activity, XServerActivity.class));
|
||||
Intent intent = new Intent();
|
||||
intent.setClassName("com.termux.x11", "com.termux.x11.MainActivity");
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||
context.startActivity(intent);
|
||||
try {
|
||||
TermuxX11.main(new String[]{":0"});
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "TermuxX11.main: ", e);
|
||||
}
|
||||
|
||||
startTermuxX11(context);
|
||||
} else {
|
||||
context.startActivity(new Intent(context, X11Activity.class));
|
||||
}
|
||||
if (isKill) {
|
||||
new Terminal(context).executeShellCommand2("killall fluxbox && " + (SDK_INT >= 34 ? "export DISPLAY=:0 && sleep 5 && " : "") + "fluxbox > /dev/null", false, context);
|
||||
new Terminal(context).executeShellCommand2((SDK_INT >= 34 ? "export DISPLAY=:0 && " : "killall fluxbox && ") + "fluxbox > /dev/null", false, context);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import com.vectras.vm.utils.FileUtils;
|
|||
import com.vectras.vm.utils.NetworkUtils;
|
||||
import com.vectras.vm.utils.PackageUtils;
|
||||
import com.vectras.vm.utils.ServiceUtils;
|
||||
import com.vectras.vterm.Terminal;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
|
@ -197,10 +198,13 @@ public class HomeStartVM {
|
|||
String finalCommand = VMManager.addAudioDevSdl(String.format(runCommandFormat, env));
|
||||
|
||||
if (MainSettingsManager.getVmUi(context).equals("X11") && SDK_INT >= 34) {
|
||||
finalCommand = "export DISPLAY=:0 && sleep 5\nfluxbox > /dev/null &\n" + finalCommand;
|
||||
finalCommand = "export DISPLAY=:0 &&" + finalCommand;
|
||||
}
|
||||
Log.i(TAG, finalCommand);
|
||||
|
||||
Terminal vterm = new Terminal(context);
|
||||
vterm.executeShellCommand2("export DISPLAY=:0 && fluxbox > /dev/null", false, context);
|
||||
|
||||
if (ServiceUtils.isServiceRunning(context, MainService.class)) {
|
||||
MainService.startCommand(finalCommand, context);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -27,16 +27,16 @@ public class VmsDiffUtil extends DiffUtil.Callback {
|
|||
|
||||
@Override
|
||||
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
|
||||
// So sánh bằng khóa duy nhất (vd: vmID)
|
||||
// Compare using a unique key (e.g., vmID)
|
||||
return oldList.get(oldItemPosition).vmID.equals(newList.get(newItemPosition).vmID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
|
||||
// So sánh nội dung, nếu khác thì update item đó
|
||||
// Compare the content, if it's different, update that item.
|
||||
DataMainRoms oldItem = oldList.get(oldItemPosition);
|
||||
DataMainRoms newItem = newList.get(newItemPosition);
|
||||
return oldItem.equals(newItem); // Nếu bạn override equals trong DataMainRoms thì dùng cách này
|
||||
return oldItem.equals(newItem); // If you override equals in DataMainRoms, use this method.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,9 +84,7 @@ public class VmsFragment extends Fragment implements CallbackInterface.HomeCallT
|
|||
vmsHomeAdapter = new VmsHomeAdapter(requireActivity(), data);
|
||||
binding.rvRomlist.setAdapter(vmsHomeAdapter);
|
||||
|
||||
binding.bnRomstore.setOnClickListener(v -> {
|
||||
vmsCallToHomeListener.openRomStore();
|
||||
});
|
||||
binding.bnRomstore.setOnClickListener(v -> vmsCallToHomeListener.openRomStore());
|
||||
|
||||
binding.bnRepair.setOnClickListener(V -> {
|
||||
VMManager.startFixRomsDataJson();
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 4.9 KiB |
|
|
@ -1,73 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="8sp"
|
||||
android:paddingRight="8sp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ListView
|
||||
android:id="@id/android:list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<TextView
|
||||
android:id="@id/android:empty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="Unrestricted access"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="5sp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/host_field"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:digits="0123456789."
|
||||
android:inputType="text" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/add_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Add IP address" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="5sp"
|
||||
android:paddingBottom="5sp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/cancel_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="10sp"
|
||||
android:text="Cancel" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/apply_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10sp"
|
||||
android:text="Apply changes" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/ln_tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center|end">
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_power"
|
||||
style="@style/Widget.Material3Expressive.Button.IconButton.Tonal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
app:icon="@drawable/power_settings_new_24px"
|
||||
android:tooltipText="@string/power" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_devices"
|
||||
style="@style/Widget.Material3Expressive.Button.IconButton.Tonal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
app:icon="@drawable/devices_other_24px"
|
||||
android:tooltipText="@string/devices" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_keyboard"
|
||||
style="@style/Widget.Material3Expressive.Button.IconButton.Tonal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
app:icon="@drawable/keyboard_24px"
|
||||
android:tooltipText="@string/keyboard" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_forcustoqemu"
|
||||
style="@style/Widget.Material3Expressive.Button.IconButton.Tonal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
app:icon="@drawable/left_click_24px"
|
||||
android:tooltipText="@string/focus_or_defocus_the_qemu_window" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_zoomin"
|
||||
style="@style/Widget.Material3Expressive.Button.IconButton.Tonal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
app:icon="@drawable/zoom_in_24px"
|
||||
android:tooltipText="@string/zoom_in" />
|
||||
<Button
|
||||
android:id="@+id/btn_zoomout"
|
||||
style="@style/Widget.Material3Expressive.Button.IconButton.Tonal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
app:icon="@drawable/zoom_out_24px"
|
||||
android:tooltipText="@string/zoom_out" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/update"
|
||||
android:icon="@drawable/syncglass96"
|
||||
android:icon="@drawable/round_file_download_24"
|
||||
android:title="@string/update"
|
||||
android:contentDescription="@string/update"
|
||||
app:showAsAction="always"/>
|
||||
|
|
|
|||
1
library/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
|||
/build
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
namespace "au.com.darkside.xserver"
|
||||
compileSdkVersion 29
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
<manifest package="au.com.darkside.xserver" />
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* This class implements an X atom.
|
||||
*
|
||||
* @author Matthew KWan
|
||||
*/
|
||||
public class Atom {
|
||||
private static final String[] _predefinedAtoms = {"PRIMARY", "SECONDARY", "CLIPBOARD", "ARC", "ATOM", "BITMAP", "CARDINAL", "COLORMAP", "CURSOR", "CUT_BUFFER0", "CUT_BUFFER1", "CUT_BUFFER2", "CUT_BUFFER3", "CUT_BUFFER4", "CUT_BUFFER5", "CUT_BUFFER6", "CUT_BUFFER7", "DRAWABLE", "FONT", "INTEGER", "PIXMAP", "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", "STRING", "VISUALID", "WINDOW", "WM_COMMAND", "WM_HINTS", "WM_CLIENT_MACHINE", "WM_ICON_NAME", "WM_ICON_SIZE", "WM_NAME", "WM_NORMAL_HINTS", "WM_SIZE_HINTS", "WM_ZOOM_HINTS", "MIN_SPACE", "NORM_SPACE", "MAX_SPACE", "END_SPACE", "SUPERSCRIPT_X", "SUPERSCRIPT_Y", "SUBSCRIPT_X", "SUBSCRIPT_Y", "UNDERLINE_POSITION", "UNDERLINE_THICKNESS", "STRIKEOUT_ASCENT", "STRIKEOUT_DESCENT", "ITALIC_ANGLE", "X_HEIGHT", "QUAD_WIDTH", "WEIGHT", "POINT_SIZE", "RESOLUTION", "COPYRIGHT", "NOTICE", "FONT_NAME", "FAMILY_NAME", "FULL_NAME", "CAP_HEIGHT", "WM_CLASS", "WM_TRANSIENT_FOR", "UTF8_STRING" };
|
||||
|
||||
private final int _id;
|
||||
private final String _name;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param id The atom's ID.
|
||||
*/
|
||||
public Atom(int id, String name) {
|
||||
_id = id;
|
||||
_name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the predefined atoms with the X server.
|
||||
*
|
||||
* @param xServer
|
||||
*/
|
||||
public static void registerPredefinedAtoms(XServer xServer) {
|
||||
for (int i = 0; i < _predefinedAtoms.length; i++)
|
||||
xServer.addAtom(new Atom(i + 1, _predefinedAtoms[i]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of predefined atoms.
|
||||
*
|
||||
* @return The number of predefined atoms.
|
||||
*/
|
||||
public static int numPredefinedAtoms() {
|
||||
return _predefinedAtoms.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the atom's ID.
|
||||
*
|
||||
* @return The atom's ID.
|
||||
*/
|
||||
public int getId() {
|
||||
return _id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the atom's name.
|
||||
*
|
||||
* @return The atom's name.
|
||||
*/
|
||||
public String getName() {
|
||||
return _name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a GetAtomName request.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The remote client.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void processGetAtomNameRequest(XServer xServer, Client client, int bytesRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
if (bytesRemaining != 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, RequestCode.GetAtomName, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
int id = io.readInt();
|
||||
Atom a = xServer.getAtom(id);
|
||||
|
||||
if (a == null) {
|
||||
ErrorCode.write(client, ErrorCode.Atom, RequestCode.GetAtomName, id);
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] bytes = a._name.getBytes();
|
||||
int length = bytes.length;
|
||||
int pad = -length & 3;
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, (byte) 0);
|
||||
io.writeInt((length + pad) / 4); // Reply length.
|
||||
io.writeShort((short) length); // Name length.
|
||||
io.writePadBytes(22); // Unused.
|
||||
io.writeBytes(bytes, 0, length); // Name.
|
||||
io.writePadBytes(pad); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an InternAtom request.
|
||||
* Return or create an atom with the specified name.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The remote client.
|
||||
* @param arg Optional first argument.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void processInternAtomRequest(XServer xServer, Client client, byte arg, int bytesRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
if (bytesRemaining < 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, RequestCode.InternAtom, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean onlyIfExists = (arg != 0);
|
||||
int n = io.readShort(); // Length of name.
|
||||
int pad = -n & 3;
|
||||
|
||||
io.readSkip(2); // Unused.
|
||||
bytesRemaining -= 4;
|
||||
|
||||
if (bytesRemaining != n + pad) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, RequestCode.InternAtom, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] name = new byte[n];
|
||||
|
||||
io.readBytes(name, 0, n); // The atom name.
|
||||
io.readSkip(pad); // Unused.
|
||||
|
||||
int id = 0;
|
||||
String s = new String(name);
|
||||
Atom a = xServer.findAtom(s);
|
||||
|
||||
if (a != null) {
|
||||
id = a.getId();
|
||||
} else if (!onlyIfExists) {
|
||||
a = new Atom(xServer.nextFreeAtomId(), s);
|
||||
xServer.addAtom(a);
|
||||
id = a.getId();
|
||||
}
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, (byte) 0);
|
||||
io.writeInt(0); // Reply length.
|
||||
io.writeInt(id); // The atom ID.
|
||||
io.writePadBytes(20); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,801 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.util.Vector;
|
||||
|
||||
import au.com.darkside.xserver.Xext.Extensions;
|
||||
|
||||
/**
|
||||
* This class handles communications with a client.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
public class Client extends Thread {
|
||||
public static final int Destroy = 0;
|
||||
public static final int RetainPermanent = 1;
|
||||
public static final int RetainTemporary = 2;
|
||||
|
||||
private final XServer _xServer;
|
||||
private final Socket _socket;
|
||||
private final InputOutput _inputOutput;
|
||||
private final int _resourceIdBase;
|
||||
private final int _resourceIdMask;
|
||||
private final Vector<Resource> _resources;
|
||||
private int _sequenceNumber = 0;
|
||||
private boolean _closeConnection = false;
|
||||
private boolean _isConnected = true;
|
||||
private int _closeDownMode = Destroy;
|
||||
private boolean _imperviousToServerGrabs = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param xserver The X Server.
|
||||
* @param socket The communications socket.
|
||||
* @param resourceIdBase The lowest resource ID the client can use.
|
||||
* @param resourceIdMask The range of resource IDs the client can use.
|
||||
* @throws IOException
|
||||
*/
|
||||
public Client(XServer xserver, Socket socket, int resourceIdBase, int resourceIdMask) throws IOException {
|
||||
_xServer = xserver;
|
||||
_socket = socket;
|
||||
_inputOutput = new InputOutput(socket);
|
||||
_resourceIdBase = resourceIdBase;
|
||||
_resourceIdMask = resourceIdMask;
|
||||
_resources = new Vector<Resource>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the client's close down mode.
|
||||
*
|
||||
* @return The client's close down mode.
|
||||
*/
|
||||
public int getCloseDownMode() {
|
||||
return _closeDownMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the input/output handle.
|
||||
*
|
||||
* @return The input/output handle.
|
||||
*/
|
||||
public InputOutput getInputOutput() {
|
||||
return _inputOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sequence number of the latest request sent by the client.
|
||||
*
|
||||
* @return The last-used sequence number.
|
||||
*/
|
||||
public int getSequenceNumber() {
|
||||
return _sequenceNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the client is connected.
|
||||
*
|
||||
* @return True if the client is connected.
|
||||
*/
|
||||
public boolean isConnected() {
|
||||
return _isConnected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the client is impervious to server grabs.
|
||||
*
|
||||
* @return True if impervious.
|
||||
*/
|
||||
public boolean getImperviousToServerGrabs() {
|
||||
return _imperviousToServerGrabs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the client is impervious to server grabs.
|
||||
*
|
||||
* @param impervious If true, the client is impervious.
|
||||
*/
|
||||
public void setImperviousToServerGrabs(boolean impervious) {
|
||||
_imperviousToServerGrabs = impervious;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to the client's list of resources.
|
||||
*
|
||||
* @param r The resource to add.
|
||||
*/
|
||||
public synchronized void addResource(Resource r) {
|
||||
_resources.add(r);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a resource from the client's list.
|
||||
*
|
||||
* @param id The resource ID.
|
||||
*/
|
||||
public synchronized void freeResource(Resource r) {
|
||||
_resources.remove(r);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the communications thread.
|
||||
*/
|
||||
public void run() {
|
||||
try {
|
||||
doComms();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
|
||||
synchronized (_xServer) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the communications thread.
|
||||
*/
|
||||
public void cancel() {
|
||||
_closeConnection = true;
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the communications thread and free resources.
|
||||
*/
|
||||
private void close() {
|
||||
if (!_isConnected) return;
|
||||
|
||||
_isConnected = false;
|
||||
|
||||
try {
|
||||
_inputOutput.close();
|
||||
_socket.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
|
||||
// Clear the resources associated with this client.
|
||||
if (_closeDownMode == Destroy) for (Resource r : _resources)
|
||||
r.delete();
|
||||
|
||||
_resources.clear();
|
||||
_xServer.removeClient(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle communications with the client.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private void doComms() throws IOException {
|
||||
// Read the connection setup.
|
||||
int byteOrder = _inputOutput.readByte();
|
||||
|
||||
if (byteOrder == 0x42) _inputOutput.setMSB(true);
|
||||
else if (byteOrder == 0x6c) _inputOutput.setMSB(false);
|
||||
else return;
|
||||
|
||||
_inputOutput.readByte(); // Unused.
|
||||
_inputOutput.readShort(); // Protocol major version.
|
||||
_inputOutput.readShort(); // Protocol minor version.
|
||||
|
||||
int nameLength = _inputOutput.readShort();
|
||||
int dataLength = _inputOutput.readShort();
|
||||
|
||||
_inputOutput.readShort(); // Unused.
|
||||
|
||||
if (nameLength > 0) {
|
||||
_inputOutput.readSkip(nameLength); // Authorization protocol name.
|
||||
_inputOutput.readSkip(-nameLength & 3); // Padding.
|
||||
}
|
||||
|
||||
if (dataLength > 0) {
|
||||
_inputOutput.readSkip(dataLength); // Authorization protocol data.
|
||||
_inputOutput.readSkip(-dataLength & 3); // Padding.
|
||||
}
|
||||
|
||||
// Complete the setup.
|
||||
final byte[] vendor = _xServer.vendor.getBytes();
|
||||
int pad = -vendor.length & 3;
|
||||
int extra = 26 + 2 * _xServer.getNumFormats() + (vendor.length + pad) / 4;
|
||||
Keyboard kb = _xServer.getKeyboard();
|
||||
|
||||
synchronized (_inputOutput) {
|
||||
_inputOutput.writeByte((byte) 1); // Success.
|
||||
_inputOutput.writeByte((byte) 0); // Unused.
|
||||
_inputOutput.writeShort(_xServer.ProtocolMajorVersion);
|
||||
_inputOutput.writeShort(_xServer.ProtocolMinorVersion);
|
||||
_inputOutput.writeShort((short) extra); // Length of data.
|
||||
_inputOutput.writeInt(_xServer.ReleaseNumber); // Release number.
|
||||
_inputOutput.writeInt(_resourceIdBase);
|
||||
_inputOutput.writeInt(_resourceIdMask);
|
||||
_inputOutput.writeInt(0); // Motion buffer size.
|
||||
_inputOutput.writeShort((short) vendor.length); // Vendor length.
|
||||
_inputOutput.writeShort((short) 0x7fff); // Max request length.
|
||||
_inputOutput.writeByte((byte) 1); // Number of screens.
|
||||
_inputOutput.writeByte((byte) _xServer.getNumFormats());
|
||||
_inputOutput.writeByte((byte) 0); // Image byte order (0=LSB, 1=MSB).
|
||||
_inputOutput.writeByte((byte) 1); // Bitmap bit order (0=LSB, 1=MSB).
|
||||
_inputOutput.writeByte((byte) 8); // Bitmap format scanline unit.
|
||||
_inputOutput.writeByte((byte) 8); // Bitmap format scanline pad.
|
||||
_inputOutput.writeByte((byte) kb.getMinimumKeycode());
|
||||
_inputOutput.writeByte((byte) kb.getMaximumKeycode());
|
||||
_inputOutput.writePadBytes(4); // Unused.
|
||||
|
||||
if (vendor.length > 0) { // Write padded vendor string.
|
||||
_inputOutput.writeBytes(vendor, 0, vendor.length);
|
||||
_inputOutput.writePadBytes(pad);
|
||||
}
|
||||
|
||||
_xServer.writeFormats(_inputOutput);
|
||||
_xServer.getScreen().write(_inputOutput);
|
||||
}
|
||||
_inputOutput.flush();
|
||||
|
||||
while (!_closeConnection) {
|
||||
byte opcode = (byte) _inputOutput.readByte();
|
||||
byte arg = (byte) _inputOutput.readByte();
|
||||
int requestLength = _inputOutput.readShort();
|
||||
int bytesRemaining;
|
||||
|
||||
if (requestLength == 0) { // Handle big requests.
|
||||
requestLength = _inputOutput.readInt();
|
||||
if (requestLength > 2) bytesRemaining = requestLength * 4 - 8;
|
||||
else bytesRemaining = 0;
|
||||
} else {
|
||||
bytesRemaining = requestLength * 4 - 4;
|
||||
}
|
||||
|
||||
// Deal with server grabs.
|
||||
while (!_xServer.processingAllowed(this)) {
|
||||
try {
|
||||
sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (_xServer) {
|
||||
processRequest(opcode, arg, bytesRemaining);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is it OK to create a resource with the specified ID?
|
||||
*
|
||||
* @param id The resource ID.
|
||||
* @return True if it is OK to create a resource with the ID.
|
||||
*/
|
||||
private boolean validResourceId(int id) {
|
||||
return ((id & ~_resourceIdMask) == _resourceIdBase && !_xServer.resourceExists(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single request from the client.
|
||||
*
|
||||
* @param opcode The request's opcode.
|
||||
* @param arg Optional first argument.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
private void processRequest(byte opcode, byte arg, int bytesRemaining) throws IOException {
|
||||
_sequenceNumber++;
|
||||
switch (opcode) {
|
||||
case RequestCode.CreateWindow:
|
||||
if (bytesRemaining < 28) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int id = _inputOutput.readInt(); // Window ID.
|
||||
int parent = _inputOutput.readInt(); // Parent.
|
||||
Resource r = _xServer.getResource(parent);
|
||||
|
||||
bytesRemaining -= 8;
|
||||
if (!validResourceId(id)) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.IDChoice, opcode, id);
|
||||
} else if (r == null || r.getType() != Resource.WINDOW) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Window, opcode, parent);
|
||||
} else {
|
||||
Window w = (Window) r;
|
||||
|
||||
w.processCreateWindowRequest(_inputOutput, this, _sequenceNumber, id, arg, bytesRemaining);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.ChangeWindowAttributes:
|
||||
case RequestCode.GetWindowAttributes:
|
||||
case RequestCode.DestroyWindow:
|
||||
case RequestCode.DestroySubwindows:
|
||||
case RequestCode.ChangeSaveSet:
|
||||
case RequestCode.ReparentWindow:
|
||||
case RequestCode.MapWindow:
|
||||
case RequestCode.MapSubwindows:
|
||||
case RequestCode.UnmapWindow:
|
||||
case RequestCode.UnmapSubwindows:
|
||||
case RequestCode.ConfigureWindow:
|
||||
case RequestCode.CirculateWindow:
|
||||
case RequestCode.QueryTree:
|
||||
case RequestCode.ChangeProperty:
|
||||
case RequestCode.DeleteProperty:
|
||||
case RequestCode.GetProperty:
|
||||
case RequestCode.ListProperties:
|
||||
case RequestCode.QueryPointer:
|
||||
case RequestCode.GetMotionEvents:
|
||||
case RequestCode.TranslateCoordinates:
|
||||
case RequestCode.ClearArea:
|
||||
case RequestCode.ListInstalledColormaps:
|
||||
case RequestCode.RotateProperties:
|
||||
if (bytesRemaining < 4) {
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int id = _inputOutput.readInt();
|
||||
Resource r = _xServer.getResource(id);
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if (r == null || r.getType() != Resource.WINDOW) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Window, opcode, id);
|
||||
} else {
|
||||
r.processRequest(this, opcode, arg, bytesRemaining);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.GetGeometry:
|
||||
case RequestCode.CopyArea:
|
||||
case RequestCode.CopyPlane:
|
||||
case RequestCode.PolyPoint:
|
||||
case RequestCode.PolyLine:
|
||||
case RequestCode.PolySegment:
|
||||
case RequestCode.PolyRectangle:
|
||||
case RequestCode.PolyArc:
|
||||
case RequestCode.FillPoly:
|
||||
case RequestCode.PolyFillRectangle:
|
||||
case RequestCode.PolyFillArc:
|
||||
case RequestCode.PutImage:
|
||||
case RequestCode.GetImage:
|
||||
case RequestCode.PolyText8:
|
||||
case RequestCode.PolyText16:
|
||||
case RequestCode.ImageText8:
|
||||
case RequestCode.ImageText16:
|
||||
case RequestCode.QueryBestSize:
|
||||
if (bytesRemaining < 4) {
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int id = _inputOutput.readInt();
|
||||
Resource r = _xServer.getResource(id);
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if (r == null || !r.isDrawable()) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Drawable, opcode, id);
|
||||
} else {
|
||||
r.processRequest(this, opcode, arg, bytesRemaining);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.InternAtom:
|
||||
Atom.processInternAtomRequest(_xServer, this, arg, bytesRemaining);
|
||||
break;
|
||||
case RequestCode.GetAtomName:
|
||||
Atom.processGetAtomNameRequest(_xServer, this, bytesRemaining);
|
||||
break;
|
||||
case RequestCode.GetSelectionOwner:
|
||||
case RequestCode.SetSelectionOwner:
|
||||
case RequestCode.ConvertSelection:
|
||||
Selection.processRequest(_xServer, this, opcode, bytesRemaining);
|
||||
break;
|
||||
case RequestCode.SendEvent:
|
||||
case RequestCode.GrabPointer:
|
||||
case RequestCode.UngrabPointer:
|
||||
case RequestCode.GrabButton:
|
||||
case RequestCode.UngrabButton:
|
||||
case RequestCode.ChangeActivePointerGrab:
|
||||
case RequestCode.GrabKeyboard:
|
||||
case RequestCode.UngrabKeyboard:
|
||||
case RequestCode.GrabKey:
|
||||
case RequestCode.UngrabKey:
|
||||
case RequestCode.AllowEvents:
|
||||
case RequestCode.SetInputFocus:
|
||||
case RequestCode.GetInputFocus:
|
||||
_xServer.getScreen().processRequest(_xServer, this, opcode, arg, bytesRemaining);
|
||||
break;
|
||||
case RequestCode.GrabServer:
|
||||
if (bytesRemaining != 0) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
_xServer.grabServer(this);
|
||||
}
|
||||
break;
|
||||
case RequestCode.UngrabServer:
|
||||
if (bytesRemaining != 0) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
_xServer.ungrabServer(this);
|
||||
}
|
||||
break;
|
||||
case RequestCode.WarpPointer:
|
||||
case RequestCode.ChangePointerControl:
|
||||
case RequestCode.GetPointerControl:
|
||||
case RequestCode.SetPointerMapping:
|
||||
case RequestCode.GetPointerMapping:
|
||||
_xServer.getPointer().processRequest(_xServer, this, opcode, arg, bytesRemaining);
|
||||
break;
|
||||
case RequestCode.OpenFont:
|
||||
if (bytesRemaining < 8) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int id = _inputOutput.readInt(); // Font ID.
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if (!validResourceId(id)) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.IDChoice, opcode, id);
|
||||
} else {
|
||||
Font.processOpenFontRequest(_xServer, this, id, bytesRemaining);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.CloseFont:
|
||||
if (bytesRemaining != 4) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int id = _inputOutput.readInt();
|
||||
Resource r = _xServer.getResource(id);
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if (r == null || r.getType() != Resource.FONT)
|
||||
ErrorCode.write(this, ErrorCode.Font, opcode, id);
|
||||
else r.processRequest(this, opcode, arg, bytesRemaining);
|
||||
}
|
||||
break;
|
||||
case RequestCode.QueryFont:
|
||||
case RequestCode.QueryTextExtents:
|
||||
if (bytesRemaining != 4) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int id = _inputOutput.readInt();
|
||||
Resource r = _xServer.getResource(id);
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if (r == null || !r.isFontable()) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Font, opcode, id);
|
||||
} else {
|
||||
r.processRequest(this, opcode, arg, bytesRemaining);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.ListFonts:
|
||||
case RequestCode.ListFontsWithInfo:
|
||||
Font.processListFonts(this, opcode, bytesRemaining);
|
||||
break;
|
||||
case RequestCode.SetFontPath:
|
||||
Font.processSetFontPath(_xServer, this, bytesRemaining);
|
||||
break;
|
||||
case RequestCode.GetFontPath:
|
||||
if (bytesRemaining != 0) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
Font.processGetFontPath(_xServer, this);
|
||||
}
|
||||
break;
|
||||
case RequestCode.CreatePixmap:
|
||||
if (bytesRemaining != 12) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int id = _inputOutput.readInt(); // Pixmap ID.
|
||||
int did = _inputOutput.readInt(); // Drawable ID.
|
||||
int width = _inputOutput.readShort(); // Width.
|
||||
int height = _inputOutput.readShort(); // Height.
|
||||
Resource r = _xServer.getResource(did);
|
||||
|
||||
if (!validResourceId(id)) {
|
||||
ErrorCode.write(this, ErrorCode.IDChoice, opcode, id);
|
||||
} else if (r == null || !r.isDrawable()) {
|
||||
ErrorCode.write(this, ErrorCode.Drawable, opcode, did);
|
||||
} else {
|
||||
try {
|
||||
Pixmap.processCreatePixmapRequest(_xServer, this, id, width, height, arg, r);
|
||||
} catch (OutOfMemoryError e) {
|
||||
ErrorCode.write(this, ErrorCode.Alloc, opcode, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.FreePixmap:
|
||||
if (bytesRemaining != 4) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int id = _inputOutput.readInt();
|
||||
Resource r = _xServer.getResource(id);
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if (r == null || r.getType() != Resource.PIXMAP)
|
||||
ErrorCode.write(this, ErrorCode.Pixmap, opcode, id);
|
||||
else r.processRequest(this, opcode, arg, bytesRemaining);
|
||||
}
|
||||
break;
|
||||
case RequestCode.CreateGC:
|
||||
if (bytesRemaining < 12) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int id = _inputOutput.readInt(); // GContext ID.
|
||||
int d = _inputOutput.readInt(); // Drawable ID.
|
||||
Resource r = _xServer.getResource(d);
|
||||
|
||||
bytesRemaining -= 8;
|
||||
if (!validResourceId(id)) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.IDChoice, opcode, id);
|
||||
} else if (r == null || !r.isDrawable()) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Drawable, opcode, d);
|
||||
} else {
|
||||
GContext.processCreateGCRequest(_xServer, this, id, bytesRemaining);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.ChangeGC:
|
||||
case RequestCode.CopyGC:
|
||||
case RequestCode.SetDashes:
|
||||
case RequestCode.SetClipRectangles:
|
||||
case RequestCode.FreeGC:
|
||||
if (bytesRemaining < 4) {
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int id = _inputOutput.readInt();
|
||||
Resource r = _xServer.getResource(id);
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if (r == null || r.getType() != Resource.GCONTEXT) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.GContext, opcode, id);
|
||||
} else {
|
||||
r.processRequest(this, opcode, arg, bytesRemaining);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.CreateColormap:
|
||||
if (bytesRemaining != 12) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int id = _inputOutput.readInt(); // Colormap ID.
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if (!validResourceId(id)) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.IDChoice, opcode, id);
|
||||
} else {
|
||||
Colormap.processCreateColormapRequest(_xServer, this, id, arg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.CopyColormapAndFree:
|
||||
if (bytesRemaining != 8) {
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int id1 = _inputOutput.readInt();
|
||||
int id2 = _inputOutput.readInt();
|
||||
Resource r = _xServer.getResource(id2);
|
||||
|
||||
if (r == null || r.getType() != Resource.COLORMAP)
|
||||
ErrorCode.write(this, ErrorCode.Colormap, opcode, id2);
|
||||
else if (!validResourceId(id1))
|
||||
ErrorCode.write(this, ErrorCode.IDChoice, opcode, id1);
|
||||
else ((Colormap) r).processCopyColormapAndFree(this, id1);
|
||||
}
|
||||
break;
|
||||
case RequestCode.FreeColormap:
|
||||
case RequestCode.InstallColormap:
|
||||
case RequestCode.UninstallColormap:
|
||||
case RequestCode.AllocColor:
|
||||
case RequestCode.AllocNamedColor:
|
||||
case RequestCode.AllocColorCells:
|
||||
case RequestCode.AllocColorPlanes:
|
||||
case RequestCode.FreeColors:
|
||||
case RequestCode.StoreColors:
|
||||
case RequestCode.StoreNamedColor:
|
||||
case RequestCode.QueryColors:
|
||||
case RequestCode.LookupColor:
|
||||
if (bytesRemaining < 4) {
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int id = _inputOutput.readInt();
|
||||
Resource r = _xServer.getResource(id);
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if (r == null || r.getType() != Resource.COLORMAP) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Colormap, opcode, id);
|
||||
} else {
|
||||
r.processRequest(this, opcode, arg, bytesRemaining);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.CreateCursor:
|
||||
case RequestCode.CreateGlyphCursor:
|
||||
if (bytesRemaining != 28) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int id = _inputOutput.readInt(); // Cursor ID.
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if (!validResourceId(id)) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.IDChoice, opcode, id);
|
||||
} else {
|
||||
Cursor.processCreateRequest(_xServer, this, opcode, id, bytesRemaining);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.FreeCursor:
|
||||
case RequestCode.RecolorCursor:
|
||||
if (bytesRemaining < 4) {
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int id = _inputOutput.readInt();
|
||||
Resource r = _xServer.getResource(id);
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if (r == null || r.getType() != Resource.CURSOR) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Colormap, opcode, id);
|
||||
} else {
|
||||
r.processRequest(this, opcode, arg, bytesRemaining);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.QueryExtension:
|
||||
_xServer.processQueryExtensionRequest(this, bytesRemaining);
|
||||
break;
|
||||
case RequestCode.ListExtensions:
|
||||
if (bytesRemaining != 0) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
_xServer.writeListExtensions(this);
|
||||
}
|
||||
break;
|
||||
case RequestCode.QueryKeymap:
|
||||
case RequestCode.ChangeKeyboardMapping:
|
||||
case RequestCode.GetKeyboardMapping:
|
||||
case RequestCode.ChangeKeyboardControl:
|
||||
case RequestCode.SetModifierMapping:
|
||||
case RequestCode.GetModifierMapping:
|
||||
case RequestCode.GetKeyboardControl:
|
||||
case RequestCode.Bell:
|
||||
_xServer.getKeyboard().processRequest(_xServer, this, opcode, arg, bytesRemaining);
|
||||
break;
|
||||
case RequestCode.SetScreenSaver:
|
||||
if (bytesRemaining != 8) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int timeout = _inputOutput.readShort(); // Timeout.
|
||||
int interval = _inputOutput.readShort(); // Interval
|
||||
int pb = _inputOutput.readByte(); // Prefer-blanking.
|
||||
int ae = _inputOutput.readByte(); // Allow-exposures.
|
||||
|
||||
_inputOutput.readSkip(2); // Unused.
|
||||
_xServer.setScreenSaver(timeout, interval, pb, ae);
|
||||
}
|
||||
break;
|
||||
case RequestCode.GetScreenSaver:
|
||||
if (bytesRemaining != 0) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
_xServer.writeScreenSaver(this);
|
||||
}
|
||||
break;
|
||||
case RequestCode.ChangeHosts:
|
||||
_xServer.processChangeHostsRequest(this, arg, bytesRemaining);
|
||||
break;
|
||||
case RequestCode.ListHosts:
|
||||
if (bytesRemaining != 0) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
_xServer.writeListHosts(this);
|
||||
}
|
||||
break;
|
||||
case RequestCode.SetAccessControl:
|
||||
if (bytesRemaining != 0) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
_xServer.setAccessControl(arg == 1);
|
||||
}
|
||||
break;
|
||||
case RequestCode.SetCloseDownMode:
|
||||
processSetCloseDownModeRequest(arg, bytesRemaining);
|
||||
break;
|
||||
case RequestCode.KillClient:
|
||||
processKillClientRequest(bytesRemaining);
|
||||
break;
|
||||
case RequestCode.ForceScreenSaver:
|
||||
if (bytesRemaining != 0) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
_xServer.getScreen().blank(arg == 1);
|
||||
}
|
||||
break;
|
||||
case RequestCode.NoOperation:
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
break;
|
||||
default: // Opcode not implemented.
|
||||
if (opcode < 0) {
|
||||
Extensions.processRequest(_xServer, this, opcode, arg, bytesRemaining);
|
||||
} else {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Implementation, opcode, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a SetCloseDownMode request.
|
||||
*
|
||||
* @param mode The close down mode.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void processSetCloseDownModeRequest(int mode, int bytesRemaining) throws IOException {
|
||||
if (bytesRemaining != 0) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Length, RequestCode.SetCloseDownMode, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
_closeDownMode = mode;
|
||||
for (Resource r : _resources)
|
||||
r.setCloseDownMode(mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a KillClient request.
|
||||
*
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void processKillClientRequest(int bytesRemaining) throws IOException {
|
||||
if (bytesRemaining != 4) {
|
||||
_inputOutput.readSkip(bytesRemaining);
|
||||
ErrorCode.write(this, ErrorCode.Length, RequestCode.KillClient, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
int id = _inputOutput.readInt();
|
||||
Client client = null;
|
||||
|
||||
if (id != 0) {
|
||||
Resource r = _xServer.getResource(id);
|
||||
|
||||
if (r == null) {
|
||||
ErrorCode.write(this, ErrorCode.Length, RequestCode.KillClient, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
client = r.getClient();
|
||||
}
|
||||
|
||||
if (client != null && client._isConnected) client._closeConnection = true;
|
||||
else if (client == null || client._closeDownMode != Destroy)
|
||||
_xServer.destroyClientResources(client);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,303 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* This class implements an X Windows cursor.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
public class Cursor extends Resource {
|
||||
private final int _hotspotX;
|
||||
private final int _hotspotY;
|
||||
private Bitmap _bitmap;
|
||||
private int _foregroundColor;
|
||||
private int _backgroundColor;
|
||||
|
||||
private static final int _glyphs[][] = {{R.drawable.xc_left_ptr, 7, 7}, {R.drawable.xc_arrow, 14, 1}, {R.drawable.xc_based_arrow_down, 4, 10}, {R.drawable.xc_based_arrow_up, 4, 10}, {R.drawable.xc_boat, 14, 4}, {R.drawable.xc_bogosity, 7, 7}, {R.drawable.xc_bottom_left_corner, 1, 14}, {R.drawable.xc_bottom_right_corner, 14, 14}, {R.drawable.xc_bottom_side, 7, 14}, {R.drawable.xc_bottom_tee, 8, 10}, {R.drawable.xc_box_spiral, 8, 8}, {R.drawable.xc_center_ptr, 5, 1}, {R.drawable.xc_circle, 8, 8}, {R.drawable.xc_clock, 6, 3}, {R.drawable.xc_coffee_mug, 7, 9}, {R.drawable.xc_cross, 7, 7}, {R.drawable.xc_cross_reverse, 7, 7}, {R.drawable.xc_crosshair, 7, 7}, {R.drawable.xc_diamond_cross, 7, 7}, {R.drawable.xc_dot, 6, 6}, {R.drawable.xc_dotbox, 7, 6}, {R.drawable.xc_double_arrow, 6, 8}, {R.drawable.xc_draft_large, 14, 0}, {R.drawable.xc_draft_small, 14, 0}, {R.drawable.xc_draped_box, 7, 6}, {R.drawable.xc_exchange, 7, 7}, {R.drawable.xc_fleur, 8, 8}, {R.drawable.xc_gobbler, 14, 3}, {R.drawable.xc_gumby, 2, 0}, {R.drawable.xc_hand1, 12, 0}, {R.drawable.xc_hand2, 0, 1}, {R.drawable.xc_heart, 6, 8}, {R.drawable.xc_icon, 8, 8}, {R.drawable.xc_iron_cross, 8, 7}, {R.drawable.xc_left_ptr, 1, 1}, {R.drawable.xc_left_side, 1, 7}, {R.drawable.xc_left_tee, 1, 8}, {R.drawable.xc_leftbutton, 8, 8}, {R.drawable.xc_ll_angle, 1, 10}, {R.drawable.xc_lr_angle, 10, 10}, {R.drawable.xc_man, 14, 5}, {R.drawable.xc_middlebutton, 8, 8}, {R.drawable.xc_mouse, 4, 1}, {R.drawable.xc_pencil, 11, 15}, {R.drawable.xc_pirate, 7, 12}, {R.drawable.xc_plus, 5, 6}, {R.drawable.xc_question_arrow, 5, 8}, {R.drawable.xc_right_ptr, 8, 1}, {R.drawable.xc_right_side, 14, 7}, {R.drawable.xc_right_tee, 10, 8}, {R.drawable.xc_rightbutton, 8, 8}, {R.drawable.xc_rtl_logo, 7, 7}, {R.drawable.xc_sailboat, 8, 0}, {R.drawable.xc_sb_down_arrow, 4, 15}, {R.drawable.xc_sb_h_double_arrow, 7, 4}, {R.drawable.xc_sb_left_arrow, 0, 4}, {R.drawable.xc_sb_right_arrow, 15, 4}, {R.drawable.xc_sb_up_arrow, 4, 0}, {R.drawable.xc_sb_v_double_arrow, 4, 7}, {R.drawable.xc_shuttle, 11, 0}, {R.drawable.xc_sizing, 8, 8}, {R.drawable.xc_spider, 6, 7}, {R.drawable.xc_spraycan, 10, 2}, {R.drawable.xc_star, 7, 7}, {R.drawable.xc_target, 7, 7}, {R.drawable.xc_tcross, 7, 7}, {R.drawable.xc_top_left_arrow, 1, 1}, {R.drawable.xc_top_left_corner, 1, 1}, {R.drawable.xc_top_right_corner, 14, 1}, {R.drawable.xc_top_side, 7, 1}, {R.drawable.xc_top_tee, 8, 1}, {R.drawable.xc_trek, 4, 0}, {R.drawable.xc_ul_angle, 1, 1}, {R.drawable.xc_umbrella, 8, 2}, {R.drawable.xc_ur_angle, 10, 1}, {R.drawable.xc_watch, 15, 9}, {R.drawable.xc_xterm, 4, 8}};
|
||||
|
||||
/**
|
||||
* Constructor for a pixmap cursor.
|
||||
*
|
||||
* @param id The server cursor ID.
|
||||
* @param xServer The X server.
|
||||
* @param client The client issuing the request.
|
||||
* @param p Cursor pixmap.
|
||||
* @param mp Mask pixmap. May be null.
|
||||
* @param x Hotspot X coordinate.
|
||||
* @param y Hotspot Y coordinate.
|
||||
* @param foregroundColor Foreground color of the cursor.
|
||||
* @param backgroundColor Foreground color of the cursor.
|
||||
*/
|
||||
public Cursor(int id, XServer xServer, Client client, Pixmap p, Pixmap mp, int x, int y, int foregroundColor, int backgroundColor) {
|
||||
super(CURSOR, id, xServer, client);
|
||||
|
||||
_hotspotX = x;
|
||||
_hotspotY = y;
|
||||
_foregroundColor = foregroundColor;
|
||||
_backgroundColor = backgroundColor;
|
||||
|
||||
Bitmap bm = p.getDrawable().getBitmap();
|
||||
int width = bm.getWidth();
|
||||
int height = bm.getHeight();
|
||||
int[] pixels = new int[width * height];
|
||||
|
||||
bm.getPixels(pixels, 0, width, 0, 0, width, height);
|
||||
if (mp == null) {
|
||||
for (int i = 0; i < pixels.length; i++) {
|
||||
if (pixels[i] == 0xffffffff) pixels[i] = foregroundColor;
|
||||
else pixels[i] = backgroundColor;
|
||||
}
|
||||
} else {
|
||||
Bitmap mbm = mp.getDrawable().getBitmap();
|
||||
int[] mask = new int[width * height];
|
||||
|
||||
mbm.getPixels(mask, 0, width, 0, 0, width, height);
|
||||
for (int i = 0; i < pixels.length; i++) {
|
||||
if (mask[i] != 0xffffffff) pixels[i] = 0;
|
||||
else if (pixels[i] == 0xffffffff) pixels[i] = foregroundColor;
|
||||
else pixels[i] = backgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
_bitmap = Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a glyph cursor.
|
||||
* This functions just assumes the caller wants one of the 77 predefined
|
||||
* cursors from the "cursor" font, so the sourceFont, maskFont, and
|
||||
* maskChar are all ignored.
|
||||
*
|
||||
* @param id The server cursor ID.
|
||||
* @param xServer The X server.
|
||||
* @param client The client issuing the request.
|
||||
* @param sourceFont Font to use for the cursor character.
|
||||
* @param maskFont Font for the mask character. May be null.
|
||||
* @param sourceChar Character to use as the cursor.
|
||||
* @param maskChar Character to use as the mask.
|
||||
* @param foregroundColor Foreground color of the cursor.
|
||||
* @param backgroundColor Foreground color of the cursor.
|
||||
*/
|
||||
public Cursor(int id, XServer xServer, Client client, Font sourceFont, Font maskFont, int sourceChar, int maskChar, int foregroundColor, int backgroundColor) {
|
||||
super(CURSOR, id, xServer, client);
|
||||
|
||||
sourceChar /= 2;
|
||||
if (sourceChar < 0 || sourceChar >= _glyphs.length) sourceChar = 0;
|
||||
|
||||
if (maskChar == 32) {
|
||||
_bitmap = Bitmap.createBitmap(16, 16, Bitmap.Config.ARGB_8888);
|
||||
_bitmap.eraseColor(0);
|
||||
} else {
|
||||
_bitmap = BitmapFactory.decodeResource(xServer.getContext().getResources(), _glyphs[sourceChar][0]);
|
||||
}
|
||||
|
||||
_foregroundColor = 0xff000000;
|
||||
_backgroundColor = 0xffffffff;
|
||||
setColor(foregroundColor, backgroundColor);
|
||||
|
||||
_hotspotX = _glyphs[sourceChar][1];
|
||||
_hotspotY = _glyphs[sourceChar][2];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cursor's bitmap.
|
||||
*
|
||||
* @return The cursor's bitmap.
|
||||
*/
|
||||
public Bitmap getBitmap() {
|
||||
return _bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the X coordinate of the cursor's hotspot.
|
||||
*
|
||||
* @return The X coordinate of the cursor's hotspot.
|
||||
*/
|
||||
public int getHotspotX() {
|
||||
return _hotspotX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Y coordinate of the cursor's hotspot.
|
||||
*
|
||||
* @return The Y coordinate of the cursor's hotspot.
|
||||
*/
|
||||
public int getHotspotY() {
|
||||
return _hotspotY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the foreground and background colors of the cursor.
|
||||
*
|
||||
* @param fg Foreground color.
|
||||
* @param bg Background color.
|
||||
*/
|
||||
private void setColor(int fg, int bg) {
|
||||
if (fg == _foregroundColor && bg == _backgroundColor) return;
|
||||
|
||||
int width = _bitmap.getWidth();
|
||||
int height = _bitmap.getHeight();
|
||||
int[] pixels = new int[width * height];
|
||||
|
||||
_bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
|
||||
for (int i = 0; i < pixels.length; i++) {
|
||||
int pix = pixels[i];
|
||||
|
||||
if (pix == _foregroundColor) pixels[i] = fg;
|
||||
else if (pix == _backgroundColor) pixels[i] = bg;
|
||||
}
|
||||
|
||||
_bitmap = Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
|
||||
_foregroundColor = fg;
|
||||
_backgroundColor = bg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an X request relating to this cursor.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @param opcode The request's opcode.
|
||||
* @param arg Optional first argument.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public void processRequest(Client client, byte opcode, byte arg, int bytesRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
switch (opcode) {
|
||||
case RequestCode.FreeCursor:
|
||||
if (bytesRemaining != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
_xServer.freeResource(_id);
|
||||
if (_client != null) _client.freeResource(this);
|
||||
}
|
||||
break;
|
||||
case RequestCode.RecolorCursor:
|
||||
if (bytesRemaining != 12) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int fgRed = io.readShort();
|
||||
int fgGreen = io.readShort();
|
||||
int fgBlue = io.readShort();
|
||||
int bgRed = io.readShort();
|
||||
int bgGreen = io.readShort();
|
||||
int bgBlue = io.readShort();
|
||||
|
||||
setColor(Colormap.fromParts16(fgRed, fgGreen, fgBlue), Colormap.fromParts16(bgRed, bgGreen, bgBlue));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Implementation, opcode, _id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a create request.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The client issuing the request.
|
||||
* @param opcode The request opcode.
|
||||
* @param id The ID of the cursor to create.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void processCreateRequest(XServer xServer, Client client, byte opcode, int id, int bytesRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
if (opcode == RequestCode.CreateCursor) {
|
||||
int sid = io.readInt(); // Source pixmap ID.
|
||||
int mid = io.readInt(); // Mask pixmap ID.
|
||||
int fgRed = io.readShort();
|
||||
int fgGreen = io.readShort();
|
||||
int fgBlue = io.readShort();
|
||||
int bgRed = io.readShort();
|
||||
int bgGreen = io.readShort();
|
||||
int bgBlue = io.readShort();
|
||||
short x = (short) io.readShort();
|
||||
short y = (short) io.readShort();
|
||||
Resource r = xServer.getResource(sid);
|
||||
Resource mr = null;
|
||||
|
||||
if (r == null || r.getType() != PIXMAP) {
|
||||
ErrorCode.write(client, ErrorCode.Pixmap, opcode, sid);
|
||||
return;
|
||||
} else if (mid != 0) {
|
||||
mr = xServer.getResource(mid);
|
||||
if (mr == null || mr.getType() != PIXMAP) {
|
||||
ErrorCode.write(client, ErrorCode.Pixmap, opcode, mid);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Pixmap p = (Pixmap) r;
|
||||
Pixmap mp = (Pixmap) mr;
|
||||
|
||||
if (p.getDepth() != 1) {
|
||||
ErrorCode.write(client, ErrorCode.Match, opcode, sid);
|
||||
return;
|
||||
} else if (mp != null) {
|
||||
if (mp.getDepth() != 1) {
|
||||
ErrorCode.write(client, ErrorCode.Match, opcode, mid);
|
||||
return;
|
||||
}
|
||||
|
||||
Bitmap bm1 = p.getDrawable().getBitmap();
|
||||
Bitmap bm2 = mp.getDrawable().getBitmap();
|
||||
|
||||
if (bm1.getWidth() != bm2.getWidth() || bm1.getHeight() != bm2.getHeight()) {
|
||||
ErrorCode.write(client, ErrorCode.Match, opcode, mid);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int fg = Colormap.fromParts16(fgRed, fgGreen, fgBlue);
|
||||
int bg = Colormap.fromParts16(bgRed, bgGreen, bgBlue);
|
||||
Cursor c = new Cursor(id, xServer, client, p, mp, x, y, fg, bg);
|
||||
|
||||
xServer.addResource(c);
|
||||
client.addResource(c);
|
||||
} else if (opcode == RequestCode.CreateGlyphCursor) {
|
||||
int sid = io.readInt(); // Source font ID.
|
||||
int mid = io.readInt(); // Mask font ID.
|
||||
int sourceChar = io.readShort(); // Source char.
|
||||
int maskChar = io.readShort(); // Mask char.
|
||||
int fgRed = io.readShort();
|
||||
int fgGreen = io.readShort();
|
||||
int fgBlue = io.readShort();
|
||||
int bgRed = io.readShort();
|
||||
int bgGreen = io.readShort();
|
||||
int bgBlue = io.readShort();
|
||||
Resource r = xServer.getResource(sid);
|
||||
Resource mr = null;
|
||||
|
||||
if (r == null || r.getType() != FONT) {
|
||||
ErrorCode.write(client, ErrorCode.Font, opcode, sid);
|
||||
return;
|
||||
} else if (mid != 0) {
|
||||
mr = xServer.getResource(mid);
|
||||
if (mr == null || mr.getType() != FONT) {
|
||||
ErrorCode.write(client, ErrorCode.Font, opcode, mid);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int fg = Colormap.fromParts16(fgRed, fgGreen, fgBlue);
|
||||
int bg = Colormap.fromParts16(bgRed, bgGreen, bgBlue);
|
||||
Cursor c = new Cursor(id, xServer, client, (Font) r, (Font) mr, sourceChar, maskChar, fg, bg);
|
||||
|
||||
xServer.addResource(c);
|
||||
client.addResource(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,991 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* This class implements an X drawable.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
public class Drawable {
|
||||
private final Bitmap _bitmap;
|
||||
private final Canvas _canvas;
|
||||
private final int _depth;
|
||||
private Bitmap _backgroundBitmap;
|
||||
private int _backgroundColor;
|
||||
private boolean[] _shapeMask = null;
|
||||
|
||||
private static final byte BITMAP_FORMAT = 0;
|
||||
private static final byte XY_PIXMAP_FORMAT = 1;
|
||||
private static final byte Z_PIXMAP_FORMAT = 2;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param width The drawable width.
|
||||
* @param height The drawable height.
|
||||
* @param depth The drawable depth.
|
||||
* @param bgbitmap Background bitmap. Can be null.
|
||||
* @param bgcolor Background color.
|
||||
*/
|
||||
public Drawable(int width, int height, int depth, Bitmap bgbitmap, int bgcolor) {
|
||||
_bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
_canvas = new Canvas(_bitmap);
|
||||
_depth = depth;
|
||||
_backgroundBitmap = bgbitmap;
|
||||
_backgroundColor = bgcolor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the drawable's width.
|
||||
*
|
||||
* @return The drawable's width.
|
||||
*/
|
||||
public int getWidth() {
|
||||
return _bitmap.getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the drawable's height.
|
||||
*
|
||||
* @return The drawable's height.
|
||||
*/
|
||||
public int getHeight() {
|
||||
return _bitmap.getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the drawable's depth.
|
||||
*
|
||||
* @return The drawable's depth.
|
||||
*/
|
||||
public int getDepth() {
|
||||
return _depth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the drawable's bitmap.
|
||||
*
|
||||
* @return The drawable's bitmap.
|
||||
*/
|
||||
public Bitmap getBitmap() {
|
||||
return _bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the drawable's background color.
|
||||
*
|
||||
* @param color The background color.
|
||||
*/
|
||||
public void setBackgroundColor(int color) {
|
||||
_backgroundColor = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the drawable's background bitmap.
|
||||
*
|
||||
* @param bitmap The background bitmap.
|
||||
*/
|
||||
public void setBackgroundBitmap(Bitmap bitmap) {
|
||||
_backgroundBitmap = bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an X request relating to this drawable.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The remote client.
|
||||
* @param id The ID of the pixmap or window using this drawable.
|
||||
* @param opcode The request's opcode.
|
||||
* @param arg Optional first argument.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @return True if the drawable has been changed.
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean processRequest(XServer xServer, Client client, int id, byte opcode, byte arg, int bytesRemaining) throws IOException {
|
||||
boolean changed = false;
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
switch (opcode) {
|
||||
case RequestCode.CopyArea:
|
||||
if (bytesRemaining != 20) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int did = io.readInt(); // Dest drawable.
|
||||
int gcid = io.readInt(); // GC.
|
||||
short sx = (short) io.readShort(); // Src X.
|
||||
short sy = (short) io.readShort(); // Src Y.
|
||||
short dx = (short) io.readShort(); // Dst X.
|
||||
short dy = (short) io.readShort(); // Dst Y.
|
||||
int width = io.readShort(); // Width.
|
||||
int height = io.readShort(); // Height.
|
||||
Resource r1 = xServer.getResource(did);
|
||||
Resource r2 = xServer.getResource(gcid);
|
||||
|
||||
if (r1 == null || !r1.isDrawable()) {
|
||||
ErrorCode.write(client, ErrorCode.Drawable, opcode, did);
|
||||
} else if (r2 == null || r2.getType() != Resource.GCONTEXT) {
|
||||
ErrorCode.write(client, ErrorCode.GContext, opcode, gcid);
|
||||
} else if (width > 0 && height > 0) {
|
||||
copyArea(sx, sy, width, height, r1, dx, dy, (GContext) r2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.CopyPlane:
|
||||
if (bytesRemaining != 24) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, id);
|
||||
} else {
|
||||
int did = io.readInt(); // Dest drawable.
|
||||
int gcid = io.readInt(); // GC.
|
||||
short sx = (short) io.readShort(); // Src X.
|
||||
short sy = (short) io.readShort(); // Src Y.
|
||||
short dx = (short) io.readShort(); // Dst X.
|
||||
short dy = (short) io.readShort(); // Dst Y.
|
||||
int width = io.readShort(); // Width.
|
||||
int height = io.readShort(); // Height.
|
||||
int bitPlane = io.readInt(); // Bit plane.
|
||||
Resource r1 = xServer.getResource(did);
|
||||
Resource r2 = xServer.getResource(gcid);
|
||||
|
||||
if (r1 == null || !r1.isDrawable()) {
|
||||
ErrorCode.write(client, ErrorCode.Drawable, opcode, did);
|
||||
} else if (r2 == null || r2.getType() != Resource.GCONTEXT) {
|
||||
ErrorCode.write(client, ErrorCode.GContext, opcode, gcid);
|
||||
} else {
|
||||
if (_depth != 32)
|
||||
copyPlane(sx, sy, width, height, bitPlane, r1, dx, dy, (GContext) r2);
|
||||
else copyArea(sx, sy, width, height, r1, dx, dy, (GContext) r2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.GetImage:
|
||||
if (bytesRemaining != 12) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
processGetImageRequest(client, arg);
|
||||
}
|
||||
break;
|
||||
case RequestCode.QueryBestSize:
|
||||
if (bytesRemaining != 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int width = io.readShort(); // Width.
|
||||
int height = io.readShort(); // Height.
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, (byte) 0);
|
||||
io.writeInt(0); // Reply length.
|
||||
io.writeShort((short) width); // Width.
|
||||
io.writeShort((short) height); // Height.
|
||||
io.writePadBytes(20); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
break;
|
||||
case RequestCode.PolyPoint:
|
||||
case RequestCode.PolyLine:
|
||||
case RequestCode.PolySegment:
|
||||
case RequestCode.PolyRectangle:
|
||||
case RequestCode.PolyArc:
|
||||
case RequestCode.FillPoly:
|
||||
case RequestCode.PolyFillRectangle:
|
||||
case RequestCode.PolyFillArc:
|
||||
case RequestCode.PutImage:
|
||||
case RequestCode.PolyText8:
|
||||
case RequestCode.PolyText16:
|
||||
case RequestCode.ImageText8:
|
||||
case RequestCode.ImageText16:
|
||||
if (bytesRemaining < 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int gcid = io.readInt(); // GContext.
|
||||
Resource r = xServer.getResource(gcid);
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if (r == null || r.getType() != Resource.GCONTEXT) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.GContext, opcode, 0);
|
||||
|
||||
} else {
|
||||
changed = processGCRequest(xServer, client, id, (GContext) r, opcode, arg, bytesRemaining);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Implementation, opcode, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a GetImage request.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @param format 1=XYPixmap, 2=ZPixmap.
|
||||
* @throws IOException
|
||||
*/
|
||||
private void processGetImageRequest(Client client, byte format) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
short x = (short) io.readShort(); // X.
|
||||
short y = (short) io.readShort(); // Y.
|
||||
int width = io.readShort(); // Width.
|
||||
int height = io.readShort(); // Height.
|
||||
int planeMask = io.readInt(); // Plane mask.
|
||||
int wh = width * height;
|
||||
int n, pad;
|
||||
int[] pixels;
|
||||
byte[] bytes = null;
|
||||
|
||||
if (x < 0 || y < 0 || x + width > _bitmap.getWidth() || y + height > _bitmap.getHeight()) {
|
||||
ErrorCode.write(client, ErrorCode.Match, RequestCode.GetImage, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
pixels = new int[wh];
|
||||
} catch (OutOfMemoryError e) {
|
||||
ErrorCode.write(client, ErrorCode.Alloc, RequestCode.GetImage, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
_bitmap.getPixels(pixels, 0, width, x, y, width, height);
|
||||
|
||||
if (format == Z_PIXMAP_FORMAT) {
|
||||
n = wh * 3;
|
||||
} else { // XY_PIXMAP_FORMAT is the only other valid value.
|
||||
int planes = Util.bitcount(planeMask);
|
||||
int rightPad = -width & 7;
|
||||
int xmax = width + rightPad;
|
||||
int offset = 0;
|
||||
|
||||
n = planes * height * (width + rightPad) / 8;
|
||||
|
||||
try {
|
||||
bytes = new byte[n];
|
||||
} catch (OutOfMemoryError e) {
|
||||
ErrorCode.write(client, ErrorCode.Alloc, RequestCode.GetImage, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int plane = 31; plane >= 0; plane--) {
|
||||
int bit = 1 << plane;
|
||||
|
||||
if ((planeMask & bit) == 0) continue;
|
||||
|
||||
byte b = 0;
|
||||
|
||||
for (int yi = 0; yi < height; yi++) {
|
||||
for (int xi = 0; xi < xmax; xi++) {
|
||||
b <<= 1;
|
||||
if (xi < width && (pixels[yi * width + xi] & bit) != 0) b |= 1;
|
||||
|
||||
if ((xi & 7) == 7) {
|
||||
bytes[offset++] = b;
|
||||
b = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pad = -n & 3;
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, (byte) 32);
|
||||
io.writeInt((n + pad) / 4); // Reply length.
|
||||
io.writeInt(0); // Visual ID.
|
||||
io.writePadBytes(20); // Unused.
|
||||
|
||||
if (format == 2) {
|
||||
for (int i = 0; i < wh; i++) {
|
||||
n = pixels[i] & planeMask;
|
||||
io.writeByte((byte) (n & 0xff));
|
||||
io.writeByte((byte) ((n >> 8) & 0xff));
|
||||
io.writeByte((byte) ((n >> 16) & 0xff));
|
||||
}
|
||||
} else {
|
||||
io.writeBytes(bytes, 0, n);
|
||||
}
|
||||
|
||||
io.writePadBytes(pad); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the entire drawable.
|
||||
*/
|
||||
public void clear() {
|
||||
if (_backgroundBitmap == null || _backgroundBitmap.isRecycled()) {
|
||||
_bitmap.eraseColor(_backgroundColor);
|
||||
} else {
|
||||
int width = _bitmap.getWidth();
|
||||
int height = _bitmap.getHeight();
|
||||
int dx = _backgroundBitmap.getWidth();
|
||||
int dy = _backgroundBitmap.getHeight();
|
||||
|
||||
for (int y = 0; y < height; y += dy)
|
||||
for (int x = 0; x < width; x += dx)
|
||||
_canvas.drawBitmap(_backgroundBitmap, x, y, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a rectangular region of the drawable.
|
||||
*
|
||||
* @param x X coordinate of the rectangle.
|
||||
* @param y Y coordinate of the rectangle.
|
||||
* @param width Width of the rectangle.
|
||||
* @param height Height of the rectangle.
|
||||
*/
|
||||
public void clearArea(int x, int y, int width, int height) {
|
||||
Rect r = new Rect(x, y, x + width, y + height);
|
||||
Paint paint = new Paint();
|
||||
|
||||
if (_backgroundBitmap == null || _backgroundBitmap.isRecycled()) {
|
||||
paint.setColor(_backgroundColor);
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
_canvas.drawRect(r, paint);
|
||||
} else {
|
||||
int bw = _bitmap.getWidth();
|
||||
int bh = _bitmap.getHeight();
|
||||
int dx = _backgroundBitmap.getWidth();
|
||||
int dy = _backgroundBitmap.getHeight();
|
||||
|
||||
_canvas.save();
|
||||
_canvas.clipRect(r);
|
||||
|
||||
for (int iy = 0; iy < bh; iy += dy) {
|
||||
if (iy >= r.bottom) break;
|
||||
if (iy + dy < r.top) continue;
|
||||
|
||||
for (int ix = 0; ix < bw; ix += dx) {
|
||||
if (ix >= r.right) break;
|
||||
if (iy + dy < r.left) continue;
|
||||
|
||||
_canvas.drawBitmap(_backgroundBitmap, ix, iy, null);
|
||||
}
|
||||
}
|
||||
|
||||
_canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a rectangle from this drawable to another.
|
||||
*
|
||||
* @param sx X coordinate of this rectangle.
|
||||
* @param sy Y coordinate of this rectangle.
|
||||
* @param width Width of the rectangle.
|
||||
* @param height Height of the rectangle.
|
||||
* @param dr The pixmap or window to draw the rectangle in.
|
||||
* @param dx The destination X coordinate.
|
||||
* @param dy The destination Y coordinate.
|
||||
* @param gc The GContext.
|
||||
* @throws IOException
|
||||
*/
|
||||
private void copyArea(int sx, int sy, int width, int height, Resource dr, int dx, int dy, GContext gc) throws IOException {
|
||||
Drawable dst;
|
||||
|
||||
if (dr.getType() == Resource.PIXMAP) dst = ((Pixmap) dr).getDrawable();
|
||||
else dst = ((Window) dr).getDrawable();
|
||||
|
||||
if (sx < 0) {
|
||||
width += sx;
|
||||
dx -= sx;
|
||||
sx = 0;
|
||||
}
|
||||
|
||||
if (sy < 0) {
|
||||
height += sy;
|
||||
dy -= sy;
|
||||
sy = 0;
|
||||
}
|
||||
|
||||
if (sx + width > _bitmap.getWidth()) width = _bitmap.getWidth() - sx;
|
||||
|
||||
if (sy + height > _bitmap.getHeight()) height = _bitmap.getHeight() - sy;
|
||||
|
||||
if (width <= 0 || height <= 0) return;
|
||||
|
||||
Bitmap bm = Bitmap.createBitmap(_bitmap, sx, sy, width, height);
|
||||
|
||||
dst._canvas.drawBitmap(bm, dx, dy, gc.getPaint());
|
||||
|
||||
if (dr.getType() == Resource.WINDOW) ((Window) dr).invalidate(dx, dy, width, height);
|
||||
|
||||
if (gc.getGraphicsExposure())
|
||||
EventCode.sendNoExposure(gc.getClient(), dr, RequestCode.CopyArea);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a rectangle from a plane of this drawable to another rectangle.
|
||||
*
|
||||
* @param sx X coordinate of this rectangle.
|
||||
* @param sy Y coordinate of this rectangle.
|
||||
* @param width Width of the rectangle.
|
||||
* @param height Height of the rectangle.
|
||||
* @param bitPlane The bit plane being copied.
|
||||
* @param dr The pixmap or window to draw the rectangle in.
|
||||
* @param dx The destination X coordinate.
|
||||
* @param dy The destination Y coordinate.
|
||||
* @param gc The GContext.
|
||||
* @throws IOException
|
||||
*/
|
||||
private void copyPlane(int sx, int sy, int width, int height, int bitPlane, Resource dr, int dx, int dy, GContext gc) throws IOException {
|
||||
Drawable dst;
|
||||
|
||||
if (dr.getType() == Resource.PIXMAP) dst = ((Pixmap) dr).getDrawable();
|
||||
else dst = ((Window) dr).getDrawable();
|
||||
|
||||
int fg = (_depth == 1) ? 0xffffffff : gc.getForegroundColor();
|
||||
int bg = (_depth == 1) ? 0 : gc.getBackgroundColor();
|
||||
int[] pixels = new int[width * height];
|
||||
|
||||
_bitmap.getPixels(pixels, 0, width, sx, sy, width, height);
|
||||
for (int i = 0; i < pixels.length; i++)
|
||||
pixels[i] = ((pixels[i] & bitPlane) != 0) ? fg : bg;
|
||||
|
||||
Bitmap pixelsBmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
pixelsBmp.setPixels(pixels, 0, width, 0, 0, width, height);
|
||||
dst._canvas.drawBitmap(pixelsBmp, dx, dy, gc.getPaint());
|
||||
|
||||
if (dr.getType() == Resource.WINDOW) ((Window) dr).invalidate(dx, dy, width, height);
|
||||
|
||||
if (gc.getGraphicsExposure())
|
||||
EventCode.sendNoExposure(gc.getClient(), dr, RequestCode.CopyPlane);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw text at the specified location, on top of a bounding rectangle
|
||||
* drawn in the background color.
|
||||
*
|
||||
* @param s The string to write.
|
||||
* @param x X coordinate.
|
||||
* @param y Y coordinate.
|
||||
* @param gc Graphics context for drawing the text.
|
||||
*/
|
||||
private void drawImageText(String s, int x, int y, GContext gc) {
|
||||
Paint paint = gc.getPaint();
|
||||
Font font = gc.getFont();
|
||||
Rect rect = new Rect();
|
||||
|
||||
font.getTextBounds(s, x, y, rect);
|
||||
paint.setColor(gc.getBackgroundColor());
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
_canvas.drawRect(rect, paint);
|
||||
|
||||
paint.setColor(gc.getForegroundColor());
|
||||
_canvas.drawText(s, x, y, paint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an X request relating to this drawable using the
|
||||
* GContext provided.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The remote client.
|
||||
* @param id The ID of the pixmap or window using this drawable.
|
||||
* @param gc The GContext to use for drawing.
|
||||
* @param opcode The request's opcode.
|
||||
* @param arg Optional first argument.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @return True if the drawable is modified.
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean processGCRequest(XServer xServer, Client client, int id, GContext gc, byte opcode, byte arg, int bytesRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
Paint paint = gc.getPaint();
|
||||
boolean changed = false;
|
||||
int originalColor = paint.getColor();
|
||||
|
||||
_canvas.save();
|
||||
gc.applyClipRectangles(_canvas);
|
||||
|
||||
switch (opcode) {
|
||||
case RequestCode.PolyPoint:
|
||||
if ((bytesRemaining & 3) != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
float[] points = new float[bytesRemaining / 2];
|
||||
int i = 0;
|
||||
|
||||
while (bytesRemaining > 0) {
|
||||
float p = (short) io.readShort();
|
||||
|
||||
bytesRemaining -= 2;
|
||||
if (arg == 0 || i < 2) // Relative to origin.
|
||||
points[i] = p;
|
||||
else points[i] = points[i - 2] + p; // Rel to previous.
|
||||
i++;
|
||||
}
|
||||
|
||||
try {
|
||||
_canvas.drawPoints(points, paint);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
for (i = 0; i < points.length; i += 2)
|
||||
_canvas.drawPoint(points[i], points[i + 1], paint);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
case RequestCode.PolyLine:
|
||||
if ((bytesRemaining & 3) != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
Path path = new Path();
|
||||
int i = 0;
|
||||
|
||||
while (bytesRemaining > 0) {
|
||||
float x = (short) io.readShort();
|
||||
float y = (short) io.readShort();
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if (i == 0) path.moveTo(x, y);
|
||||
else if (arg == 0) // Relative to origin.
|
||||
path.lineTo(x, y);
|
||||
else // Relative to previous.
|
||||
path.rLineTo(x, y);
|
||||
i++;
|
||||
}
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
_canvas.drawPath(path, paint);
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
case RequestCode.PolySegment:
|
||||
if ((bytesRemaining & 7) != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
float[] points = new float[bytesRemaining / 2];
|
||||
int i = 0;
|
||||
|
||||
while (bytesRemaining > 0) {
|
||||
points[i++] = (short) io.readShort();
|
||||
bytesRemaining -= 2;
|
||||
}
|
||||
|
||||
_canvas.drawLines(points, paint);
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
case RequestCode.PolyRectangle:
|
||||
case RequestCode.PolyFillRectangle:
|
||||
if ((bytesRemaining & 7) != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
if (opcode == RequestCode.PolyRectangle) paint.setStyle(Paint.Style.STROKE);
|
||||
else paint.setStyle(Paint.Style.FILL);
|
||||
|
||||
while (bytesRemaining > 0) {
|
||||
float x = (short) io.readShort();
|
||||
float y = (short) io.readShort();
|
||||
float width = io.readShort();
|
||||
float height = io.readShort();
|
||||
|
||||
bytesRemaining -= 8;
|
||||
_canvas.drawRect(x, y, x + width, y + height, paint);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.FillPoly:
|
||||
if (bytesRemaining < 4 || (bytesRemaining & 3) != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
io.readByte(); // Shape.
|
||||
|
||||
int mode = io.readByte(); // Coordinate mode.
|
||||
Path path = new Path();
|
||||
int i = 0;
|
||||
|
||||
io.readSkip(2); // Unused.
|
||||
bytesRemaining -= 4;
|
||||
|
||||
while (bytesRemaining > 0) {
|
||||
float x = (short) io.readShort();
|
||||
float y = (short) io.readShort();
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if (i == 0) path.moveTo(x, y);
|
||||
else if (mode == 0) // Relative to origin.
|
||||
path.lineTo(x, y);
|
||||
else // Relative to previous.
|
||||
path.rLineTo(x, y);
|
||||
i++;
|
||||
}
|
||||
|
||||
path.close();
|
||||
path.setFillType(gc.getFillType());
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
_canvas.drawPath(path, paint);
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
case RequestCode.PolyArc:
|
||||
case RequestCode.PolyFillArc:
|
||||
if ((bytesRemaining % 12) != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
boolean useCenter = false;
|
||||
|
||||
if (opcode == RequestCode.PolyArc) {
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
} else {
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
if (gc.getArcMode() == 1) // Pie slice.
|
||||
useCenter = true;
|
||||
}
|
||||
|
||||
while (bytesRemaining > 0) {
|
||||
float x = (short) io.readShort();
|
||||
float y = (short) io.readShort();
|
||||
float width = io.readShort();
|
||||
float height = io.readShort();
|
||||
float angle1 = (short) io.readShort();
|
||||
float angle2 = (short) io.readShort();
|
||||
RectF r = new RectF(x, y, x + width, y + height);
|
||||
|
||||
bytesRemaining -= 12;
|
||||
_canvas.drawArc(r, angle1 / -64.0f, angle2 / -64.0f, useCenter, paint);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.PutImage:
|
||||
changed = processPutImage(client, gc, arg, bytesRemaining);
|
||||
break;
|
||||
case RequestCode.PolyText8:
|
||||
case RequestCode.PolyText16:
|
||||
changed = processPolyText(client, gc, opcode, bytesRemaining);
|
||||
break;
|
||||
case RequestCode.ImageText8:
|
||||
if (bytesRemaining != 4 + arg + (-arg & 3)) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int x = (short) io.readShort();
|
||||
int y = (short) io.readShort();
|
||||
int pad = -arg & 3;
|
||||
byte[] bytes = new byte[arg];
|
||||
|
||||
io.readBytes(bytes, 0, arg);
|
||||
io.readSkip(pad);
|
||||
drawImageText(new String(bytes), x, y, gc);
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
case RequestCode.ImageText16:
|
||||
if (bytesRemaining != 4 + 2 * arg + (-(2 * arg) & 3)) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int x = (short) io.readShort();
|
||||
int y = (short) io.readShort();
|
||||
int pad = (-2 * arg) & 3;
|
||||
char[] chars = new char[arg];
|
||||
|
||||
for (int i = 0; i < arg; i++) {
|
||||
int b1 = io.readByte();
|
||||
int b2 = io.readByte();
|
||||
|
||||
chars[i] = (char) ((b1 << 8) | b2);
|
||||
}
|
||||
|
||||
io.readSkip(pad);
|
||||
drawImageText(new String(chars), x, y, gc);
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Implementation, opcode, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (_depth == 1) paint.setColor(originalColor);
|
||||
|
||||
_canvas.restore(); // Undo any clip rectangles.
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a PutImage request.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @param gc The GContext to use for drawing.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @return True if the drawable is modified.
|
||||
* @throws IOException
|
||||
*/
|
||||
private boolean processPutImage(Client client, GContext gc, byte format, int bytesRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
if (bytesRemaining < 12) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, RequestCode.PutImage, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
int width = io.readShort();
|
||||
int height = io.readShort();
|
||||
float dstX = (short) io.readShort();
|
||||
float dstY = (short) io.readShort();
|
||||
int leftPad = io.readByte();
|
||||
int depth = io.readByte();
|
||||
int n, pad, rightPad;
|
||||
|
||||
io.readSkip(2); // Unused.
|
||||
bytesRemaining -= 12;
|
||||
|
||||
boolean badMatch = false;
|
||||
|
||||
if (format == BITMAP_FORMAT) {
|
||||
if (depth != 1) badMatch = true;
|
||||
} else if (format == XY_PIXMAP_FORMAT) {
|
||||
if (depth != _depth) badMatch = true;
|
||||
} else if (format == Z_PIXMAP_FORMAT) {
|
||||
if (depth != _depth || leftPad != 0) badMatch = true;
|
||||
} else { // Invalid format.
|
||||
badMatch = true;
|
||||
}
|
||||
|
||||
if (badMatch) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Match, RequestCode.PutImage, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isShapeMask = false;
|
||||
|
||||
if (format == Z_PIXMAP_FORMAT) {
|
||||
rightPad = 0;
|
||||
if (depth == 32) {
|
||||
n = 3 * width * height;
|
||||
} else {
|
||||
n = (width * height + 7) / 8;
|
||||
if (bytesRemaining != n + (-n & 3)) {
|
||||
isShapeMask = true;
|
||||
n = (width + 1) / 2 * height;
|
||||
}
|
||||
}
|
||||
} else { // XYPixmap or Bitmap.
|
||||
rightPad = -(width + leftPad) & 7;
|
||||
n = ((width + leftPad + rightPad) * height * depth + 7) / 8;
|
||||
}
|
||||
pad = -n & 3;
|
||||
|
||||
if (bytesRemaining != n + pad) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, RequestCode.PutImage, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
int[] colors;
|
||||
|
||||
try {
|
||||
colors = new int[width * height];
|
||||
} catch (OutOfMemoryError e) {
|
||||
ErrorCode.write(client, ErrorCode.Alloc, RequestCode.PutImage, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (format == BITMAP_FORMAT) {
|
||||
int fg = gc.getForegroundColor();
|
||||
int bg = gc.getBackgroundColor();
|
||||
int offset = 0;
|
||||
int count = 0;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int mask = 128;
|
||||
int val = 0;
|
||||
|
||||
for (; ; ) {
|
||||
if ((count++ & 7) == 0) {
|
||||
val = io.readByte();
|
||||
mask = 128;
|
||||
}
|
||||
|
||||
if (x >= leftPad && x < leftPad + width)
|
||||
colors[offset++] = ((val & mask) == 0) ? bg : fg;
|
||||
|
||||
mask >>= 1;
|
||||
if (++x == leftPad + width + rightPad) {
|
||||
x = 0;
|
||||
if (++y == height) break;
|
||||
}
|
||||
}
|
||||
} else if (format == XY_PIXMAP_FORMAT) {
|
||||
int planeBit = 1 << (depth - 1);
|
||||
|
||||
for (int i = 0; i < depth; i++) {
|
||||
int offset = 0;
|
||||
int count = 0;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int mask = 128;
|
||||
int val = 0;
|
||||
|
||||
for (; ; ) {
|
||||
if ((count++ & 7) == 0) {
|
||||
val = io.readByte();
|
||||
mask = 128;
|
||||
}
|
||||
|
||||
if (x >= leftPad && x < leftPad + width)
|
||||
colors[offset++] |= ((val & mask) == 0) ? 0 : planeBit;
|
||||
|
||||
mask >>= 1;
|
||||
if (++x == leftPad + width + rightPad) {
|
||||
x = 0;
|
||||
if (++y == height) break;
|
||||
}
|
||||
}
|
||||
|
||||
planeBit >>= 1;
|
||||
}
|
||||
} else if (depth == 32) { // 32-bit ZPixmap.
|
||||
boolean useShapeMask = (_shapeMask != null && colors.length == _shapeMask.length);
|
||||
|
||||
for (int i = 0; i < colors.length; i++) {
|
||||
int b = io.readByte();
|
||||
int g = io.readByte();
|
||||
int r = io.readByte();
|
||||
int alpha = (useShapeMask && !_shapeMask[i]) ? 0 : 0xff000000;
|
||||
|
||||
colors[i] = alpha | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
|
||||
if (useShapeMask) _shapeMask = null;
|
||||
} else if (isShapeMask) { // ZPixmap, depth = 1, shape mask.
|
||||
_shapeMask = new boolean[colors.length];
|
||||
io.readShapeMask(_shapeMask, width, height);
|
||||
io.readSkip(pad);
|
||||
|
||||
return false; // Don't redraw.
|
||||
} else { // ZPixmap with depth = 1.
|
||||
int fg = gc.getForegroundColor();
|
||||
int bg = gc.getBackgroundColor();
|
||||
boolean[] bits = new boolean[colors.length];
|
||||
|
||||
io.readBits(bits, 0, colors.length);
|
||||
|
||||
for (int i = 0; i < colors.length; i++)
|
||||
colors[i] = bits[i] ? fg : bg;
|
||||
}
|
||||
|
||||
io.readSkip(pad);
|
||||
Bitmap colorsBmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
colorsBmp.setPixels(colors, 0, width, 0, 0, width, height);
|
||||
_canvas.drawBitmap(colorsBmp, dstX, dstY, gc.getPaint());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a PolyText8 or PolyText16 request.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @param gc The GContext to use for drawing.
|
||||
* @param opcode The request's opcode.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @return True if the drawable is modified.
|
||||
* @throws IOException
|
||||
*/
|
||||
private boolean processPolyText(Client client, GContext gc, byte opcode, int bytesRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
if (bytesRemaining < 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
float x = (short) io.readShort();
|
||||
float y = (short) io.readShort();
|
||||
|
||||
bytesRemaining -= 4;
|
||||
while (bytesRemaining > 1) {
|
||||
int length = io.readByte();
|
||||
int minBytes;
|
||||
|
||||
bytesRemaining--;
|
||||
if (length == 255) // Font change indicator.
|
||||
minBytes = 4;
|
||||
else if (opcode == RequestCode.PolyText8) minBytes = 1 + length;
|
||||
else minBytes = 1 + length * 2;
|
||||
|
||||
if (bytesRemaining < minBytes) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (length == 255) { // Font change indicator.
|
||||
int fid = 0;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
fid = (fid << 8) | io.readByte();
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if (!gc.setFont(fid)) ErrorCode.write(client, ErrorCode.Font, opcode, fid);
|
||||
} else { // It's a string.
|
||||
int delta = io.readByte();
|
||||
String s;
|
||||
|
||||
bytesRemaining--;
|
||||
if (opcode == RequestCode.PolyText8) {
|
||||
byte[] bytes = new byte[length];
|
||||
|
||||
io.readBytes(bytes, 0, length);
|
||||
bytesRemaining -= length;
|
||||
s = new String(bytes);
|
||||
} else {
|
||||
char[] chars = new char[length];
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
int b1 = io.readByte();
|
||||
int b2 = io.readByte();
|
||||
|
||||
chars[i] = (char) ((b1 << 8) | b2);
|
||||
}
|
||||
|
||||
bytesRemaining -= length * 2;
|
||||
s = new String(chars);
|
||||
}
|
||||
|
||||
Paint paint = gc.getPaint();
|
||||
|
||||
x += delta;
|
||||
_canvas.drawText(s, x, y, paint);
|
||||
x += paint.measureText(s);
|
||||
}
|
||||
}
|
||||
io.readSkip(bytesRemaining);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* All the X error codes.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
public class ErrorCode {
|
||||
public static final byte None = 0;
|
||||
public static final byte Request = 1;
|
||||
public static final byte Value = 2;
|
||||
public static final byte Window = 3;
|
||||
public static final byte Pixmap = 4;
|
||||
public static final byte Atom = 5;
|
||||
public static final byte Cursor = 6;
|
||||
public static final byte Font = 7;
|
||||
public static final byte Match = 8;
|
||||
public static final byte Drawable = 9;
|
||||
public static final byte Access = 10;
|
||||
public static final byte Alloc = 11;
|
||||
public static final byte Colormap = 12;
|
||||
public static final byte GContext = 13;
|
||||
public static final byte IDChoice = 14;
|
||||
public static final byte Name = 15;
|
||||
public static final byte Length = 16;
|
||||
public static final byte Implementation = 17;
|
||||
|
||||
/**
|
||||
* Write an X error.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @param error The error code.
|
||||
* @param opcode The opcode of the error request.
|
||||
* @param resourceId The (optional) resource ID that caused the error.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void write(Client client, byte error, byte opcode, int resourceId) throws IOException {
|
||||
writeWithMinorOpcode(client, error, (short) 0, opcode, resourceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an X error with a minor opcode specified.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @param error The error code.
|
||||
* @param minorOpcode The minor opcode of the error request.
|
||||
* @param opcode The major opcode of the error request.
|
||||
* @param resourceId The (optional) resource ID that caused the error.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void writeWithMinorOpcode(Client client, byte error, short minorOpcode, byte opcode, int resourceId) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
short sn = (short) (client.getSequenceNumber() & 0xffff);
|
||||
|
||||
synchronized (io) {
|
||||
io.writeByte((byte) 0); // Indicates an error.
|
||||
io.writeByte(error); // Error code.
|
||||
io.writeShort(sn); // Sequence number.
|
||||
io.writeInt(resourceId); // Bad resource ID.
|
||||
io.writeShort(minorOpcode); // Minor opcode.
|
||||
io.writeByte(opcode); // Major opcode.
|
||||
io.writePadBytes(21); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,961 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Handle X events.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
public class EventCode {
|
||||
public static final byte KeyPress = 2;
|
||||
public static final byte KeyRelease = 3;
|
||||
public static final byte ButtonPress = 4;
|
||||
public static final byte ButtonRelease = 5;
|
||||
public static final byte MotionNotify = 6;
|
||||
public static final byte EnterNotify = 7;
|
||||
public static final byte LeaveNotify = 8;
|
||||
public static final byte FocusIn = 9;
|
||||
public static final byte FocusOut = 10;
|
||||
public static final byte KeymapNotify = 11;
|
||||
public static final byte Expose = 12;
|
||||
public static final byte GraphicsExposure = 13;
|
||||
public static final byte NoExposure = 14;
|
||||
public static final byte VisibilityNotify = 15;
|
||||
public static final byte CreateNotify = 16;
|
||||
public static final byte DestroyNotify = 17;
|
||||
public static final byte UnmapNotify = 18;
|
||||
public static final byte MapNotify = 19;
|
||||
public static final byte MapRequest = 20;
|
||||
public static final byte ReparentNotify = 21;
|
||||
public static final byte ConfigureNotify = 22;
|
||||
public static final byte ConfigureRequest = 23;
|
||||
public static final byte GravityNotify = 24;
|
||||
public static final byte ResizeRequest = 25;
|
||||
public static final byte CirculateNotify = 26;
|
||||
public static final byte CirculateRequest = 27;
|
||||
public static final byte PropertyNotify = 28;
|
||||
public static final byte SelectionClear = 29;
|
||||
public static final byte SelectionRequest = 30;
|
||||
public static final byte SelectionNotify = 31;
|
||||
public static final byte ColormapNotify = 32;
|
||||
public static final byte ClientMessage = 33;
|
||||
public static final byte MappingNotify = 34;
|
||||
|
||||
public static final int MaskKeyPress = 0x00000001;
|
||||
public static final int MaskKeyRelease = 0x00000002;
|
||||
public static final int MaskButtonPress = 0x00000004;
|
||||
public static final int MaskButtonRelease = 0x00000008;
|
||||
public static final int MaskEnterWindow = 0x00000010;
|
||||
public static final int MaskLeaveWindow = 0x00000020;
|
||||
public static final int MaskPointerMotion = 0x00000040;
|
||||
public static final int MaskPointerMotionHint = 0x00000080;
|
||||
public static final int MaskButton1Motion = 0x00000100;
|
||||
public static final int MaskButton2Motion = 0x00000200;
|
||||
public static final int MaskButton3Motion = 0x00000400;
|
||||
public static final int MaskButton4Motion = 0x00000800;
|
||||
public static final int MaskButton5Motion = 0x00001000;
|
||||
public static final int MaskButtonMotion = 0x00002000;
|
||||
public static final int MaskKeymapState = 0x00004000;
|
||||
public static final int MaskExposure = 0x00008000;
|
||||
public static final int MaskVisibilityChange = 0x00010000;
|
||||
public static final int MaskStructureNotify = 0x00020000;
|
||||
public static final int MaskResizeRedirect = 0x00040000;
|
||||
public static final int MaskSubstructureNotify = 0x00080000;
|
||||
public static final int MaskSubstructureRedirect = 0x00100000;
|
||||
public static final int MaskFocusChange = 0x00200000;
|
||||
public static final int MaskPropertyChange = 0x00400000;
|
||||
public static final int MaskColormapChange = 0x00800000;
|
||||
public static final int MaskOwnerGrabButton = 0x01000000;
|
||||
|
||||
public static final int MaskAllPointer = MaskButtonPress | MaskButtonRelease | MaskPointerMotion | MaskPointerMotionHint | MaskButton1Motion | MaskButton2Motion | MaskButton3Motion | MaskButton4Motion | MaskButton5Motion | MaskButtonMotion;
|
||||
|
||||
/**
|
||||
* Write an event header.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param code The event code.
|
||||
* @param arg Optional first argument.
|
||||
* @throws IOException
|
||||
*/
|
||||
private static void writeHeader(Client client, byte code, int arg) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
io.writeByte((byte) code);
|
||||
io.writeByte((byte) arg);
|
||||
io.writeShort((short) (client.getSequenceNumber() & 0xffff));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a key press event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param timestamp Time in milliseconds since last server reset.
|
||||
* @param keycode The code of the key that was pressed.
|
||||
* @param root The root window of the event window.
|
||||
* @param eventWindow The window interested in the event.
|
||||
* @param child Child of event window, ancestor of source. Can be null.
|
||||
* @param rootX Pointer root X coordinate at the time of the event.
|
||||
* @param rootY Pointer root Y coordinate at the time of the event.
|
||||
* @param eventX Pointer X coordinate relative to event window.
|
||||
* @param eventY Pointer Y coordinate relative to event window.
|
||||
* @param state Bitmask of the buttons and modifier keys.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendKeyPress(Client client, int timestamp, int keycode, Window root, Window eventWindow, Window child, int rootX, int rootY, int eventX, int eventY, int state) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, KeyPress, keycode);
|
||||
io.writeInt(timestamp); // Time.
|
||||
io.writeInt(root.getId()); // Root.
|
||||
io.writeInt(eventWindow.getId()); // Event.
|
||||
io.writeInt(child == null ? 0 : child.getId()); // Child.
|
||||
io.writeShort((short) rootX); // Root X.
|
||||
io.writeShort((short) rootY); // Root Y.
|
||||
io.writeShort((short) eventX); // Event X.
|
||||
io.writeShort((short) eventY); // Event Y.
|
||||
io.writeShort((short) state); // State.
|
||||
io.writeByte((byte) 1); // Same screen.
|
||||
io.writePadBytes(1); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a key release event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param timestamp Time in milliseconds since last server reset.
|
||||
* @param keycode The code of the key that was released.
|
||||
* @param root The root window of the event window.
|
||||
* @param eventWindow The window interested in the event.
|
||||
* @param child Child of event window, ancestor of source. Can be null.
|
||||
* @param rootX Pointer root X coordinate at the time of the event.
|
||||
* @param rootY Pointer root Y coordinate at the time of the event.
|
||||
* @param eventX Pointer X coordinate relative to event window.
|
||||
* @param eventY Pointer Y coordinate relative to event window.
|
||||
* @param state Bitmask of the buttons and modifier keys.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendKeyRelease(Client client, int timestamp, int keycode, Window root, Window eventWindow, Window child, int rootX, int rootY, int eventX, int eventY, int state) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, KeyRelease, keycode);
|
||||
io.writeInt(timestamp); // Time.
|
||||
io.writeInt(root.getId()); // Root.
|
||||
io.writeInt(eventWindow.getId()); // Event.
|
||||
io.writeInt(child == null ? 0 : child.getId()); // Child.
|
||||
io.writeShort((short) rootX); // Root X.
|
||||
io.writeShort((short) rootY); // Root Y.
|
||||
io.writeShort((short) eventX); // Event X.
|
||||
io.writeShort((short) eventY); // Event Y.
|
||||
io.writeShort((short) state); // State.
|
||||
io.writeByte((byte) 1); // Same screen.
|
||||
io.writePadBytes(1); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a button press event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param timestamp Time in milliseconds since last server reset.
|
||||
* @param button The button that was pressed.
|
||||
* @param root The root window of the event window.
|
||||
* @param eventWindow The window interested in the event.
|
||||
* @param child Child of event window, ancestor of source. Can be null.
|
||||
* @param rootX Pointer root X coordinate at the time of the event.
|
||||
* @param rootY Pointer root Y coordinate at the time of the event.
|
||||
* @param eventX Pointer X coordinate relative to event window.
|
||||
* @param eventY Pointer Y coordinate relative to event window.
|
||||
* @param state Bitmask of the buttons and modifier keys.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendButtonPress(Client client, int timestamp, int button, Window root, Window eventWindow, Window child, int rootX, int rootY, int eventX, int eventY, int state) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, ButtonPress, button);
|
||||
io.writeInt(timestamp); // Time.
|
||||
io.writeInt(root.getId()); // Root.
|
||||
io.writeInt(eventWindow.getId()); // Event.
|
||||
io.writeInt(child == null ? 0 : child.getId()); // Child.
|
||||
io.writeShort((short) rootX); // Root X.
|
||||
io.writeShort((short) rootY); // Root Y.
|
||||
io.writeShort((short) eventX); // Event X.
|
||||
io.writeShort((short) eventY); // Event Y.
|
||||
io.writeShort((short) state); // State.
|
||||
io.writeByte((byte) 1); // Same screen.
|
||||
io.writePadBytes(1); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a button release event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param timestamp Time in milliseconds since last server reset.
|
||||
* @param button The button that was released.
|
||||
* @param root The root window of the event window.
|
||||
* @param eventWindow The window interested in the event.
|
||||
* @param child Child of event window, ancestor of source. Can be null.
|
||||
* @param rootX Pointer root X coordinate at the time of the event.
|
||||
* @param rootY Pointer root Y coordinate at the time of the event.
|
||||
* @param eventX Pointer X coordinate relative to event window.
|
||||
* @param eventY Pointer Y coordinate relative to event window.
|
||||
* @param state Bitmask of the buttons and modifier keys.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendButtonRelease(Client client, int timestamp, int button, Window root, Window eventWindow, Window child, int rootX, int rootY, int eventX, int eventY, int state) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, ButtonRelease, button);
|
||||
io.writeInt(timestamp); // Time.
|
||||
io.writeInt(root.getId()); // Root.
|
||||
io.writeInt(eventWindow.getId()); // Event.
|
||||
io.writeInt(child == null ? 0 : child.getId()); // Child.
|
||||
io.writeShort((short) rootX); // Root X.
|
||||
io.writeShort((short) rootY); // Root Y.
|
||||
io.writeShort((short) eventX); // Event X.
|
||||
io.writeShort((short) eventY); // Event Y.
|
||||
io.writeShort((short) state); // State.
|
||||
io.writeByte((byte) 1); // Same screen.
|
||||
io.writePadBytes(1); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a motion notify event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param timestamp Time in milliseconds since last server reset.
|
||||
* @param detail 0=Normal, 1=Hint.
|
||||
* @param root The root window of the event window.
|
||||
* @param eventWindow The window interested in the event.
|
||||
* @param child Child of event window, ancestor of source. Can be null.
|
||||
* @param rootX Pointer root X coordinate at the time of the event.
|
||||
* @param rootY Pointer root Y coordinate at the time of the event.
|
||||
* @param eventX Pointer X coordinate relative to event window.
|
||||
* @param eventY Pointer Y coordinate relative to event window.
|
||||
* @param state Bitmask of the buttons and modifier keys.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendMotionNotify(Client client, int timestamp, int detail, Window root, Window eventWindow, Window child, int rootX, int rootY, int eventX, int eventY, int state) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, MotionNotify, detail);
|
||||
io.writeInt(timestamp); // Time.
|
||||
io.writeInt(root.getId()); // Root.
|
||||
io.writeInt(eventWindow.getId()); // Event.
|
||||
io.writeInt(child == null ? 0 : child.getId()); // Child.
|
||||
io.writeShort((short) rootX); // Root X.
|
||||
io.writeShort((short) rootY); // Root Y.
|
||||
io.writeShort((short) eventX); // Event X.
|
||||
io.writeShort((short) eventY); // Event Y.
|
||||
io.writeShort((short) state); // State.
|
||||
io.writeByte((byte) 1); // Same screen.
|
||||
io.writePadBytes(1); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an enter notify event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param timestamp Time in milliseconds since last server reset.
|
||||
* @param detail 0=Ancestor, 1=Virtual, 2=Inferior, 3=Nonlinear, 4=NonlinearVirtual.
|
||||
* @param root The root window of the event window.
|
||||
* @param eventWindow The window interested in the event.
|
||||
* @param child Child of event window, ancestor of source. Can be null.
|
||||
* @param rootX Pointer root X coordinate at the time of the event.
|
||||
* @param rootY Pointer root Y coordinate at the time of the event.
|
||||
* @param eventX Pointer X coordinate relative to event window.
|
||||
* @param eventY Pointer Y coordinate relative to event window.
|
||||
* @param state Bitmask of the buttons and modifier keys.
|
||||
* @param mode 0=Normal, 1=Grab, 2=Ungrab.
|
||||
* @param focus Is the event window the focus window or an inferior of it?
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendEnterNotify(Client client, int timestamp, int detail, Window root, Window eventWindow, Window child, int rootX, int rootY, int eventX, int eventY, int state, int mode, boolean focus) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, EnterNotify, detail);
|
||||
io.writeInt(timestamp); // Time.
|
||||
io.writeInt(root.getId()); // Root.
|
||||
io.writeInt(eventWindow.getId()); // Event.
|
||||
io.writeInt(child == null ? 0 : child.getId()); // Child.
|
||||
io.writeShort((short) rootX); // Root X.
|
||||
io.writeShort((short) rootY); // Root Y.
|
||||
io.writeShort((short) eventX); // Event X.
|
||||
io.writeShort((short) eventY); // Event Y.
|
||||
io.writeShort((short) state); // State.
|
||||
io.writeByte((byte) mode); // Mode.
|
||||
io.writeByte((byte) (focus ? 3 : 2)); // Same screen, focus.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a leave notify event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param timestamp Time in milliseconds since last server reset.
|
||||
* @param detail 0=Ancestor, 1=Virtual, 2=Inferior, 3=Nonlinear,
|
||||
* 4=NonlinearVirtual.
|
||||
* @param root The root window of the event window.
|
||||
* @param eventWindow The window interested in the event.
|
||||
* @param child Child of event window, ancestor of source. Can be null.
|
||||
* @param rootX Pointer root X coordinate at the time of the event.
|
||||
* @param rootY Pointer root Y coordinate at the time of the event.
|
||||
* @param eventX Pointer X coordinate relative to event window.
|
||||
* @param eventY Pointer Y coordinate relative to event window.
|
||||
* @param state Bitmask of the buttons and modifier keys.
|
||||
* @param mode 0=Normal, 1=Grab, 2=Ungrab.
|
||||
* @param focus Is the event window the focus window or an inferior of it?
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendLeaveNotify(Client client, int timestamp, int detail, Window root, Window eventWindow, Window child, int rootX, int rootY, int eventX, int eventY, int state, int mode, boolean focus) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, LeaveNotify, detail);
|
||||
io.writeInt(timestamp); // Time.
|
||||
io.writeInt(root.getId()); // Root.
|
||||
io.writeInt(eventWindow.getId()); // Event.
|
||||
io.writeInt(child == null ? 0 : child.getId()); // Child.
|
||||
io.writeShort((short) rootX); // Root X.
|
||||
io.writeShort((short) rootY); // Root Y.
|
||||
io.writeShort((short) eventX); // Event X.
|
||||
io.writeShort((short) eventY); // Event Y.
|
||||
io.writeShort((short) state); // State.
|
||||
io.writeByte((byte) mode); // Mode.
|
||||
io.writeByte((byte) (focus ? 3 : 2)); // Same screen, focus.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a focus in event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param timestamp Time in milliseconds since last server reset.
|
||||
* @param detail 0=Ancestor, 1=Virtual, 2=Inferior, 3=Nonlinear,
|
||||
* 4=NonlinearVirtual, 5=Pointer, 6=PinterRoot, 7=None.
|
||||
* @param eventWindow The window interested in the event.
|
||||
* @param mode 0=Normal, 1=Grab, 2=Ungrab, 3=WhileGrabbed.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendFocusIn(Client client, int timestamp, int detail, Window eventWindow, int mode) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, FocusIn, detail);
|
||||
io.writeInt(eventWindow.getId()); // Event.
|
||||
io.writeByte((byte) mode); // Mode.
|
||||
io.writePadBytes(23); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a focus out event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param timestamp Time in milliseconds since last server reset.
|
||||
* @param detail 0=Ancestor, 1=Virtual, 2=Inferior, 3=Nonlinear,
|
||||
* 4=NonlinearVirtual, 5=Pointer, 6=PinterRoot, 7=None.
|
||||
* @param eventWindow The window interested in the event.
|
||||
* @param mode 0=Normal, 1=Grab, 2=Ungrab, 3=WhileGrabbed.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendFocusOut(Client client, int timestamp, int detail, Window eventWindow, int mode) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, FocusOut, detail);
|
||||
io.writeInt(eventWindow.getId()); // Event.
|
||||
io.writeByte((byte) mode); // Mode.
|
||||
io.writePadBytes(23); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a keymap notify event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param keys A bit vector of the logical state of the keyboard.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendKeymapNotify(Client client, byte[] keys) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
io.writeByte(KeymapNotify);
|
||||
io.writeBytes(keys, 0, 31); // Keys.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an expose event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param window The window where the exposure occurred.
|
||||
* @param x Left of the exposed rectangle.
|
||||
* @param y Top of the exposed rectangle.
|
||||
* @param width Width of the exposed rectangle.
|
||||
* @param height Height of the exposed rectangle.
|
||||
* @param count Number of rectangles remaining in the exposed region.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendExpose(Client client, Window window, int x, int y, int width, int height, int count) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, Expose, 0);
|
||||
io.writeInt(window.getId()); // Event.
|
||||
io.writeShort((short) x); // X.
|
||||
io.writeShort((short) y); // Y.
|
||||
io.writeShort((short) width); // Width.
|
||||
io.writeShort((short) height); // Height.
|
||||
io.writeShort((short) count); // Count.
|
||||
io.writePadBytes(14); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a graphics exposure event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param drawable The drawable where the exposure occurred.
|
||||
* @param majorOpcode The graphics request that caused the event.
|
||||
* Either CopyArea or CopyPlane.
|
||||
* @param x Left of the exposed rectangle.
|
||||
* @param y Top of the exposed rectangle.
|
||||
* @param width Width of the exposed rectangle.
|
||||
* @param height Height of the exposed rectangle.
|
||||
* @param count Number of rectangles remaining in the exposed region.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendGraphicsExposure(Client client, Resource drawable, byte majorOpcode, int x, int y, int width, int height, int count) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, GraphicsExposure, 0);
|
||||
io.writeInt(drawable.getId()); // Drawable.
|
||||
io.writeShort((short) x); // X.
|
||||
io.writeShort((short) y); // Y.
|
||||
io.writeShort((short) width); // Width.
|
||||
io.writeShort((short) height); // Height.
|
||||
io.writeShort((short) 0); // Minor opcode.
|
||||
io.writeShort((short) count); // Count.
|
||||
io.writeByte((byte) majorOpcode); // Major opcode.
|
||||
io.writePadBytes(11); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a no exposure event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param drawable The drawable where no exposure exposure occurred.
|
||||
* @param majorOpcode The graphics request that caused the event.
|
||||
* Either CopyArea or CopyPlane.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendNoExposure(Client client, Resource drawable, byte majorOpcode) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, NoExposure, 0);
|
||||
io.writeInt(drawable.getId()); // Drawable.
|
||||
io.writeShort((short) 0); // Minor opcode.
|
||||
io.writeByte((byte) majorOpcode); // Major opcode.
|
||||
io.writePadBytes(21); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a visibility notify event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param window The window where the exposure occurred.
|
||||
* @param state 0=Unobscured, 1=PartiallyObscured, 2=FullyObscured.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendVisibilityNotify(Client client, Window window, int state) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, VisibilityNotify, 0);
|
||||
io.writeInt(window.getId()); // Event.
|
||||
io.writeByte((byte) state); // State.
|
||||
io.writePadBytes(23); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a create notify event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param parent The parent of the window that was created.
|
||||
* @param window The window that was created.
|
||||
* @param x X position of the created window.
|
||||
* @param y Y position of the created window.
|
||||
* @param width Width of the created window.
|
||||
* @param height Height of the created window.
|
||||
* @param borderWidth Border width of the created window.
|
||||
* @param overrideRedirect Does the created window use override redirect?
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendCreateNotify(Client client, Window parent, Window window, int x, int y, int width, int height, int borderWidth, boolean overrideRedirect) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, CreateNotify, 0);
|
||||
io.writeInt(parent.getId()); // Parent.
|
||||
io.writeInt(window.getId()); // Window.
|
||||
io.writeShort((short) x); // X.
|
||||
io.writeShort((short) y); // Y.
|
||||
io.writeShort((short) width); // Width.
|
||||
io.writeShort((short) height); // Height.
|
||||
io.writeShort((short) borderWidth); // Border width.
|
||||
io.writeByte((byte) (overrideRedirect ? 1 : 0));
|
||||
io.writePadBytes(9); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a destroy notify event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param eventWindow The window where the event was generated.
|
||||
* @param window The window that was destroyed.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendDestroyNotify(Client client, Window eventWindow, Window window) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, DestroyNotify, 0);
|
||||
io.writeInt(eventWindow.getId()); // Event.
|
||||
io.writeInt(window.getId()); // Window.
|
||||
io.writePadBytes(20); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an unmap notify event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param eventWindow The window where the event was generated.
|
||||
* @param window The window that was unmapped.
|
||||
* @param fromConfigure True if event was caused by parent being resized.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendUnmapNotify(Client client, Window eventWindow, Window window, boolean fromConfigure) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, UnmapNotify, 0);
|
||||
io.writeInt(eventWindow.getId()); // Event.
|
||||
io.writeInt(window.getId()); // Window.
|
||||
io.writeByte((byte) (fromConfigure ? 1 : 0)); // From configure.
|
||||
io.writePadBytes(19); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a map notify event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param eventWindow The window where the event was generated.
|
||||
* @param window The window that was mapped.
|
||||
* @param overrideRedirect True if the window uses override redirect.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendMapNotify(Client client, Window eventWindow, Window window, boolean overrideRedirect) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, MapNotify, 0);
|
||||
io.writeInt(eventWindow.getId()); // Event.
|
||||
io.writeInt(window.getId()); // Window.
|
||||
io.writeByte((byte) (overrideRedirect ? 1 : 0));
|
||||
io.writePadBytes(19); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a map request event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param eventWindow The window where the event was generated.
|
||||
* @param window The window that was mapped.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendMapRequest(Client client, Window eventWindow, Window window) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, MapRequest, 0);
|
||||
io.writeInt(eventWindow.getId()); // Event.
|
||||
io.writeInt(window.getId()); // Window.
|
||||
io.writePadBytes(20); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a reparent notify event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param eventWindow The window where the event was generated.
|
||||
* @param window The window that has been rerooted.
|
||||
* @param parent The window's new parent.
|
||||
* @param x X position of the window relative to the new parent.
|
||||
* @param y Y position of the window relative to the new parent.
|
||||
* @param overrideRedirect Does the window use override redirect?
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendReparentNotify(Client client, Window eventWindow, Window window, Window parent, int x, int y, boolean overrideRedirect) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, ReparentNotify, 0);
|
||||
io.writeInt(eventWindow.getId()); // Event.
|
||||
io.writeInt(window.getId()); // Window.
|
||||
io.writeInt(parent.getId()); // Parent.
|
||||
io.writeShort((short) x); // X.
|
||||
io.writeShort((short) y); // Y.
|
||||
io.writeByte((byte) (overrideRedirect ? 1 : 0));
|
||||
io.writePadBytes(11); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a configure notify event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param eventWindow The window where the event was generated.
|
||||
* @param window The window that was changed.
|
||||
* @param aboveSibling The sibling window beneath it. May be null.
|
||||
* @param x X position of the window relative to its parent.
|
||||
* @param y Y position of the window relative to its parent.
|
||||
* @param width Width of the window.
|
||||
* @param height Height of the window.
|
||||
* @param borderWidth Border width of the window.
|
||||
* @param overrideRedirect Does the window use override redirect?
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendConfigureNotify(Client client, Window eventWindow, Window window, Window aboveSibling, int x, int y, int width, int height, int borderWidth, boolean overrideRedirect) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, ConfigureNotify, 0);
|
||||
io.writeInt(eventWindow.getId()); // Event.
|
||||
io.writeInt(window.getId()); // Window.
|
||||
io.writeInt(aboveSibling == null ? 0 : aboveSibling.getId());
|
||||
io.writeShort((short) x); // X.
|
||||
io.writeShort((short) y); // Y.
|
||||
io.writeShort((short) width); // Width.
|
||||
io.writeShort((short) height); // Height.
|
||||
io.writeShort((short) borderWidth); // Border width.
|
||||
io.writeByte((byte) (overrideRedirect ? 1 : 0));
|
||||
io.writePadBytes(5); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a configure request event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param stackMode 0=Above, 1=Below, 2=TopIf, 3=BottomIf, 4=Opposite
|
||||
* @param parent The parent of the window.
|
||||
* @param window The window that the ConfigureWindow was issued to.
|
||||
* @param sibling The sibling window beneath it. May be null.
|
||||
* @param x X position of the window.
|
||||
* @param y Y position of the window.
|
||||
* @param width Width of the window.
|
||||
* @param height Height of the window.
|
||||
* @param borderWidth Border width of the window.
|
||||
* @param valueMask Components specified in the ConfigureWindow request.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendConfigureRequest(Client client, int stackMode, Window parent, Window window, Window sibling, int x, int y, int width, int height, int borderWidth, int valueMask) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, ConfigureRequest, stackMode);
|
||||
io.writeInt(parent.getId()); // Parent.
|
||||
io.writeInt(window.getId()); // Window.
|
||||
io.writeInt(sibling == null ? 0 : sibling.getId()); // Sibling.
|
||||
io.writeShort((short) x); // X.
|
||||
io.writeShort((short) y); // Y.
|
||||
io.writeShort((short) width); // Width.
|
||||
io.writeShort((short) height); // Height.
|
||||
io.writeShort((short) borderWidth); // Border width.
|
||||
io.writeShort((short) valueMask); // Value mask.
|
||||
io.writePadBytes(4); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a gravity notify event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param eventWindow The window where the event was generated.
|
||||
* @param window The window that was moved because parent changed size.
|
||||
* @param x X position of the window relative to the parent.
|
||||
* @param y Y position of the window relative to the parent.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendGravityNotify(Client client, Window eventWindow, Window window, int x, int y) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, GravityNotify, 0);
|
||||
io.writeInt(eventWindow.getId()); // Event.
|
||||
io.writeInt(window.getId()); // Window.
|
||||
io.writeShort((short) x); // X.
|
||||
io.writeShort((short) y); // Y.
|
||||
io.writePadBytes(16); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a resize request event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param window A client is attempting to resize this window.
|
||||
* @param width Requested width of the window.
|
||||
* @param height Requested height of the window.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendResizeRequest(Client client, Window window, int width, int height) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, ResizeRequest, 0);
|
||||
io.writeInt(window.getId()); // Window.
|
||||
io.writeShort((short) width); // Width.
|
||||
io.writeShort((short) height); // Height.
|
||||
io.writePadBytes(20); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a circulate notify event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param eventWindow The window where the event was generated.
|
||||
* @param window The window that was restacked.
|
||||
* @param place 0=Top, 1=Bottom.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendCirculateNotify(Client client, Window eventWindow, Window window, int place) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, CirculateNotify, 0);
|
||||
io.writeInt(eventWindow.getId()); // Event.
|
||||
io.writeInt(window.getId()); // Window.
|
||||
io.writePadBytes(4); // Unused.
|
||||
io.writeByte((byte) place); // Place.
|
||||
io.writePadBytes(15); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a circulate request event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param parent The parent of the window.
|
||||
* @param window The window that needs to be restacked.
|
||||
* @param place 0=Top, 1=Bottom.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendCirculateRequest(Client client, Window parent, Window window, int place) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, CirculateRequest, 0);
|
||||
io.writeInt(parent.getId()); // Parent.
|
||||
io.writeInt(window.getId()); // Window.
|
||||
io.writePadBytes(4); // Unused.
|
||||
io.writeByte((byte) place); // Place.
|
||||
io.writePadBytes(15); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a property notify event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param window The window being changed
|
||||
* @param atom The property being changed.
|
||||
* @param timestamp Time in milliseconds when the property was changed.
|
||||
* @param state 0=NewValue, 1=Deleted.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendPropertyNotify(Client client, Window window, Atom atom, int timestamp, int state) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, PropertyNotify, 0);
|
||||
io.writeInt(window.getId()); // Window.
|
||||
io.writeInt(atom.getId()); // Atom.
|
||||
io.writeInt(timestamp); // Time.
|
||||
io.writeByte((byte) state); // State.
|
||||
io.writePadBytes(15); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a selection clear event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param timestamp Last-change time for the selection.
|
||||
* @param window Previous owner of the selection.
|
||||
* @param atom The selection.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendSelectionClear(Client client, int timestamp, Window window, Atom atom) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, SelectionClear, 0);
|
||||
io.writeInt(timestamp); // Time.
|
||||
io.writeInt(window.getId()); // Owner.
|
||||
io.writeInt(atom.getId()); // Selection.
|
||||
io.writePadBytes(16); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a selection request event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param timestamp Last-change time for the selection.
|
||||
* @param owner Current owner of the selection.
|
||||
* @param requestor Window requesting change of selection.
|
||||
* @param selection The selection whose owner changed.
|
||||
* @param target Target atom.
|
||||
* @param property Property atom. May be null.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendSelectionRequest(Client client, int timestamp, Window owner, Window requestor, Atom selection, Atom target, Atom property) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, SelectionRequest, 0);
|
||||
io.writeInt(timestamp); // Time.
|
||||
io.writeInt(owner.getId()); // Owner.
|
||||
io.writeInt(requestor.getId()); // Requestor.
|
||||
io.writeInt(selection.getId()); // Selection.
|
||||
io.writeInt(target.getId()); // Target.
|
||||
io.writeInt(property == null ? 0 : property.getId());
|
||||
io.writePadBytes(4); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a selection notify event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param timestamp Last-change time for the selection.
|
||||
* @param requestor Window requesting change of selection.
|
||||
* @param selection The selection whose owner changed.
|
||||
* @param target Target atom.
|
||||
* @param property Property atom. May be null.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendSelectionNotify(Client client, int timestamp, Window requestor, Atom selection, Atom target, Atom property) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, SelectionNotify, 0);
|
||||
io.writeInt(timestamp); // Time.
|
||||
io.writeInt(requestor.getId()); // Requestor.
|
||||
io.writeInt(selection.getId()); // Selection.
|
||||
io.writeInt(target.getId()); // Target.
|
||||
io.writeInt(property == null ? 0 : property.getId());
|
||||
io.writePadBytes(8); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a colormap notify event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param window The window the colormap belongs to.
|
||||
* @param cmap The colormap. May be null.
|
||||
* @param isNew True=colormap changed, False=colormap un/installed.
|
||||
* @param state 0=Uninstalled, 1=Installed.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendColormapNotify(Client client, Window window, Colormap cmap, boolean isNew, int state) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, ColormapNotify, 0);
|
||||
io.writeInt(window.getId()); // Window.
|
||||
io.writeInt(cmap == null ? 0 : cmap.getId()); // Colormap.
|
||||
io.writeByte((byte) (isNew ? 1 : 0)); // New.
|
||||
io.writeByte((byte) state); // State.
|
||||
io.writePadBytes(18); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a mapping notify event.
|
||||
*
|
||||
* @param client The client to write to.
|
||||
* @param request 0=Modifier, 1=Keyboard, 2=Pointer.
|
||||
* @param firstKeycode Start of altered keycodes if request=Keyboard.
|
||||
* @param count Size of altered keycode range if request=Keyboard.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void sendMappingNotify(Client client, int request, int firstKeycode, int count) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
writeHeader(client, MappingNotify, 0);
|
||||
io.writeByte((byte) request); // Request.
|
||||
io.writeByte((byte) firstKeycode); // First keycode.
|
||||
io.writeByte((byte) count); // Count.
|
||||
io.writePadBytes(25); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,661 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Typeface;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Vector;
|
||||
|
||||
|
||||
/**
|
||||
* This class implements an X font.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
public class Font extends Resource {
|
||||
private static int _dpi = 250;
|
||||
|
||||
private final Paint _paint;
|
||||
private final float _minWidth;
|
||||
private final float _maxWidth;
|
||||
private final short _ascent;
|
||||
private final short _descent;
|
||||
private final short _maxAscent;
|
||||
private final short _maxDescent;
|
||||
private final char _maxChar;
|
||||
private Atom _nameAtom = null;
|
||||
|
||||
private static final String[] _allFonts = {"-android-default-medium-r-normal--0-0-0-0-p-0-iso8859-1", "-android-default-bold-r-normal--0-0-0-0-p-0-iso8859-1", "-android-default-medium-i-normal--0-0-0-0-p-0-iso8859-1", "-android-default-bold-i-normal--0-0-0-0-p-0-iso8859-1", "-android-default-medium-r-normal--0-0-0-0-p-0-iso10646-1", "-android-default-bold-r-normal--0-0-0-0-p-0-iso10646-1", "-android-default-medium-i-normal--0-0-0-0-p-0-iso10646-1", "-android-default-bold-i-normal--0-0-0-0-p-0-iso10646-1", "-android-monospace-medium-r-normal--0-0-0-0-m-0-iso8859-1", "-android-monospace-bold-r-normal--0-0-0-0-m-0-iso8859-1", "-android-monospace-medium-i-normal--0-0-0-0-m-0-iso8859-1", "-android-monospace-bold-i-normal--0-0-0-0-m-0-iso8859-1", "-android-monospace-medium-r-normal--0-0-0-0-m-0-iso10646-1", "-android-monospace-bold-r-normal--0-0-0-0-m-0-iso10646-1", "-android-monospace-medium-i-normal--0-0-0-0-m-0-iso10646-1", "-android-monospace-bold-i-normal--0-0-0-0-m-0-iso10646-1", "-android-serif-medium-r-normal--0-0-0-0-p-0-iso8859-1", "-android-serif-bold-r-normal--0-0-0-0-p-0-iso8859-1", "-android-serif-medium-i-normal--0-0-0-0-p-0-iso8859-1", "-android-serif-bold-i-normal--0-0-0-0-p-0-iso8859-1", "-android-serif-medium-r-normal--0-0-0-0-p-0-iso10646-1", "-android-serif-bold-r-normal--0-0-0-0-p-0-iso10646-1", "-android-serif-medium-i-normal--0-0-0-0-p-0-iso10646-1", "-android-serif-bold-i-normal--0-0-0-0-p-0-iso10646-1", "-android-sans serif-medium-r-normal--0-0-0-0-p-0-iso8859-1", "-android-sans serif-bold-r-normal--0-0-0-0-p-0-iso8859-1", "-android-sans serif-medium-i-normal--0-0-0-0-p-0-iso8859-1", "-android-sans serif-bold-i-normal--0-0-0-0-p-0-iso8859-1", "-android-sans serif-medium-r-normal--0-0-0-0-p-0-iso10646-1", "-android-sans serif-bold-r-normal--0-0-0-0-p-0-iso10646-1", "-android-sans serif-medium-i-normal--0-0-0-0-p-0-iso10646-1", "-android-sans serif-bold-i-normal--0-0-0-0-p-0-iso10646-1", "fixed", "cursor"};
|
||||
|
||||
private static String[][] _allFontFields = null;
|
||||
|
||||
/**
|
||||
* Set the dots-per-inch resolution at which fonts will be displayed.
|
||||
*
|
||||
* @param dpi The dots-per-inch resolution.
|
||||
*/
|
||||
public static void setDpi(int dpi) {
|
||||
_dpi = dpi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param id The server font ID.
|
||||
* @param xserver The X server.
|
||||
* @param client The client issuing the request.
|
||||
* @param name The name of the font. May be null.
|
||||
*/
|
||||
public Font(int id, XServer xServer, Client client, String name) {
|
||||
super(FONT, id, xServer, client);
|
||||
|
||||
char maxChar = 255;
|
||||
|
||||
_paint = new Paint();
|
||||
if (name == null || name.equalsIgnoreCase("cursor")) {
|
||||
_paint.setTypeface(Typeface.DEFAULT);
|
||||
} else if (name.equalsIgnoreCase("fixed")) {
|
||||
_paint.setTypeface(Typeface.MONOSPACE);
|
||||
} else {
|
||||
String[] fields = name.split("-");
|
||||
Typeface base = Typeface.DEFAULT;
|
||||
int style = Typeface.NORMAL;
|
||||
|
||||
if (fields.length == 15) {
|
||||
if (fields[3].equalsIgnoreCase("bold")) style |= Typeface.BOLD;
|
||||
if (fields[4].equalsIgnoreCase("i")) style |= Typeface.ITALIC;
|
||||
|
||||
try {
|
||||
int n = Integer.valueOf(fields[7]);
|
||||
|
||||
if (n > 0) _paint.setTextSize(n);
|
||||
} catch (java.lang.NumberFormatException e) {
|
||||
}
|
||||
|
||||
if (!fields[11].equalsIgnoreCase("p")) base = Typeface.MONOSPACE;
|
||||
else if (fields[2].equalsIgnoreCase("default")) base = Typeface.DEFAULT;
|
||||
else if (fields[2].equalsIgnoreCase("serif")) base = Typeface.SERIF;
|
||||
else if (fields[2].equalsIgnoreCase("sans serif")) base = Typeface.SANS_SERIF;
|
||||
else base = Typeface.create(fields[2], style);
|
||||
|
||||
if (fields[13].equalsIgnoreCase("iso10646")) maxChar = 65534;
|
||||
}
|
||||
|
||||
_paint.setTypeface(Typeface.create(base, style));
|
||||
}
|
||||
|
||||
_maxChar = maxChar;
|
||||
|
||||
// Calculate the minimum and maximum widths.
|
||||
byte[] bytes = new byte[126 - 32 + 1];
|
||||
float[] widths = new float[bytes.length];
|
||||
|
||||
for (int i = 0; i < bytes.length; i++)
|
||||
bytes[i] = (byte) (i + 32);
|
||||
|
||||
_paint.getTextWidths(new String(bytes), widths);
|
||||
|
||||
float minw = widths[0];
|
||||
float maxw = widths[0];
|
||||
|
||||
for (float width : widths) {
|
||||
if (width < minw) minw = width;
|
||||
if (width > maxw) maxw = width;
|
||||
}
|
||||
|
||||
_minWidth = minw;
|
||||
_maxWidth = maxw;
|
||||
|
||||
Paint.FontMetricsInt metrics = _paint.getFontMetricsInt();
|
||||
|
||||
_ascent = (short) -metrics.ascent;
|
||||
_descent = (short) metrics.descent;
|
||||
_maxAscent = (short) -metrics.top;
|
||||
_maxDescent = (short) metrics.bottom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the font's typeface.
|
||||
*
|
||||
* @return The font's typeface.
|
||||
*/
|
||||
public Typeface getTypeface() {
|
||||
return _paint.getTypeface();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the font's size.
|
||||
*
|
||||
* @return The font's size.
|
||||
*/
|
||||
public int getSize() {
|
||||
return (int) _paint.getTextSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the bounding rectangle for text drawn at a location.
|
||||
*
|
||||
* @param s The text.
|
||||
* @param x X coordinate.
|
||||
* @param y Y coordinate.
|
||||
* @param rect Return value. The bounding rectangle.
|
||||
*/
|
||||
public void getTextBounds(String s, int x, int y, Rect rect) {
|
||||
rect.left = x;
|
||||
rect.right = x + (int) _paint.measureText(s);
|
||||
rect.top = y - _ascent;
|
||||
rect.bottom = y + _descent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an X request relating to this font.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @param opcode The request's opcode.
|
||||
* @param arg Optional first argument.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public void processRequest(Client client, byte opcode, byte arg, int bytesRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
switch (opcode) {
|
||||
case RequestCode.CloseFont:
|
||||
if (bytesRemaining != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
_xServer.freeResource(_id);
|
||||
if (_client != null) _client.freeResource(this);
|
||||
}
|
||||
break;
|
||||
case RequestCode.QueryFont:
|
||||
if (bytesRemaining != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
processQueryFontRequest(client);
|
||||
}
|
||||
break;
|
||||
case RequestCode.QueryTextExtents:
|
||||
if (bytesRemaining < 4 || (bytesRemaining & 3) != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int pad = (arg == 0) ? 0 : 2;
|
||||
int length = (bytesRemaining - pad) / 2;
|
||||
char[] chars = new char[length];
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
int b1 = io.readByte();
|
||||
int b2 = io.readByte();
|
||||
|
||||
chars[i] = (char) ((b1 << 8) | b2);
|
||||
}
|
||||
|
||||
io.readSkip(pad);
|
||||
processQueryTextExtentsRequest(client, new String(chars));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Implementation, opcode, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an OpenFont request.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The client issuing the request.
|
||||
* @param id The ID of the font to create.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void processOpenFontRequest(XServer xServer, Client client, int id, int bytesRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
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.OpenFont, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] nameBytes = new byte[length];
|
||||
|
||||
io.readBytes(nameBytes, 0, length);
|
||||
io.readSkip(pad);
|
||||
|
||||
String name = new String(nameBytes);
|
||||
Font f = new Font(id, xServer, client, name);
|
||||
|
||||
xServer.addResource(f);
|
||||
client.addResource(f);
|
||||
|
||||
// Create an atom containing the font name.
|
||||
Atom a = xServer.findAtom(name);
|
||||
|
||||
if (a == null) {
|
||||
a = new Atom(xServer.nextFreeAtomId(), name);
|
||||
xServer.addAtom(a);
|
||||
}
|
||||
|
||||
f._nameAtom = a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a QueryFont request.
|
||||
*
|
||||
* @param client The client issuing the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
private void processQueryFontRequest(Client client) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
int numFontProperties = (_nameAtom == null) ? 0 : 1;
|
||||
int numCharInfos = _maxChar - 31;
|
||||
char[] chars = new char[numCharInfos];
|
||||
|
||||
for (char c = 32; c <= _maxChar; c++)
|
||||
chars[c - 32] = c;
|
||||
|
||||
String s = new String(chars);
|
||||
Rect bounds = new Rect();
|
||||
float[] widths = new float[numCharInfos];
|
||||
|
||||
_paint.getTextWidths(s, widths);
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, (byte) 0);
|
||||
// Reply length.
|
||||
io.writeInt(7 + numFontProperties * 2 + numCharInfos * 3);
|
||||
|
||||
// Min bounds.
|
||||
io.writeShort((short) 0); // Left side bearing.
|
||||
io.writeShort((short) 0); // Right side bearing.
|
||||
io.writeShort((short) _minWidth); // Character width.
|
||||
io.writeShort((short) 0); // Ascent.
|
||||
io.writeShort((short) 0); // Descent.
|
||||
io.writeShort((short) 0); // Attributes.
|
||||
io.writePadBytes(4); // Unused.
|
||||
|
||||
// Max bounds.
|
||||
io.writeShort((short) 0); // Left side bearing.
|
||||
io.writeShort((short) _maxWidth); // Right side bearing.
|
||||
io.writeShort((short) _maxWidth); // Character width.
|
||||
io.writeShort(_maxAscent); // Ascent.
|
||||
io.writeShort(_maxDescent); // Descent.
|
||||
io.writeShort((short) 0); // Attributes.
|
||||
io.writePadBytes(4); // Unused.
|
||||
|
||||
io.writeShort((short) 32); // Min char or byte2.
|
||||
io.writeShort((short) _maxChar); // Max char or byte2.
|
||||
io.writeShort((short) 32); // Default char.
|
||||
io.writeShort((short) numFontProperties);
|
||||
io.writeByte((byte) 0); // Draw direction = left-to-right.
|
||||
io.writeByte((byte) 0); // Min byte 1.
|
||||
io.writeByte((byte) 0); // Max byte 1.
|
||||
io.writeByte((byte) 0); // All chars exist = false.
|
||||
io.writeShort(_ascent); // Font ascent.
|
||||
io.writeShort(_descent); // Font descent.
|
||||
io.writeInt(numCharInfos);
|
||||
|
||||
// If name atom is specified, write the FONT property.
|
||||
if (_nameAtom != null) {
|
||||
Atom a = _xServer.findAtom("FONT");
|
||||
|
||||
io.writeInt(a.getId()); // Name.
|
||||
io.writeInt(_nameAtom.getId()); // Value.
|
||||
}
|
||||
|
||||
for (int i = 0; i < numCharInfos; i++) {
|
||||
_paint.getTextBounds(s, i, i + 1, bounds);
|
||||
io.writeShort((short) bounds.left); // Left side bearing.
|
||||
io.writeShort((short) bounds.right); // Right side bearing.
|
||||
io.writeShort((short) widths[i]); // Character width.
|
||||
io.writeShort((short) -bounds.top); // Ascent.
|
||||
io.writeShort((short) bounds.bottom); // Descent.
|
||||
io.writeShort((short) 0); // Attributes.
|
||||
}
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a QueryTextExtents request.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @param s The string whose extents are being queried.
|
||||
* @throws IOException
|
||||
*/
|
||||
private void processQueryTextExtentsRequest(Client client, String s) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
int width = (int) _paint.measureText(s);
|
||||
Rect bounds = new Rect();
|
||||
|
||||
_paint.getTextBounds(s, 0, s.length(), bounds);
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, (byte) 0);
|
||||
io.writeInt(0); // Reply length.
|
||||
io.writeShort(_ascent); // Font ascent.
|
||||
io.writeShort(_descent); // Font descent.
|
||||
io.writeShort((short) -bounds.top); // Overall ascent.
|
||||
io.writeShort((short) bounds.bottom); // Overall descent.
|
||||
io.writeInt(width); // Overall width.
|
||||
io.writeInt(bounds.left); // Overall left.
|
||||
io.writeInt(bounds.right); // Overall right.
|
||||
io.writePadBytes(4); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a GetFontPath request.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The remote client.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void processGetFontPath(XServer xServer, Client client) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
String[] fontPaths = xServer.getFontPath();
|
||||
int numPaths = 0;
|
||||
int length = 0;
|
||||
|
||||
if (fontPaths != null) numPaths = fontPaths.length;
|
||||
|
||||
for (int i = 0; i < numPaths; i++)
|
||||
length += fontPaths[i].length() + 1;
|
||||
|
||||
int pad = -length & 3;
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, (byte) 0);
|
||||
io.writeInt((length + pad) / 4); // Reply length.
|
||||
io.writeShort((short) numPaths); // Number of STRs in path.
|
||||
io.writePadBytes(22); // Unused.
|
||||
|
||||
for (int i = 0; i < numPaths; i++) {
|
||||
byte[] ba = fontPaths[i].getBytes();
|
||||
|
||||
io.writeByte((byte) ba.length);
|
||||
io.writeBytes(ba, 0, ba.length);
|
||||
}
|
||||
io.writePadBytes(pad); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a SetFontPath request.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The remote client.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void processSetFontPath(XServer xServer, Client client, int bytesRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
if (bytesRemaining < 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, RequestCode.SetFontPath, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
int numPaths = io.readShort(); // Number of STRs in path.
|
||||
String[] fontPaths = (numPaths > 0) ? new String[numPaths] : null;
|
||||
boolean lengthError = false;
|
||||
|
||||
io.readSkip(2); // Unused.
|
||||
bytesRemaining -= 4;
|
||||
|
||||
for (int i = 0; i < numPaths; i++) {
|
||||
if (bytesRemaining < 1) {
|
||||
lengthError = true;
|
||||
break;
|
||||
}
|
||||
|
||||
int length = io.readByte();
|
||||
byte[] ba = new byte[length];
|
||||
|
||||
bytesRemaining--;
|
||||
if (bytesRemaining < length) {
|
||||
lengthError = true;
|
||||
break;
|
||||
}
|
||||
|
||||
io.readBytes(ba, 0, length);
|
||||
bytesRemaining -= length + 1;
|
||||
fontPaths[i] = new String(ba);
|
||||
}
|
||||
|
||||
if (bytesRemaining >= 4) lengthError = true;
|
||||
|
||||
io.readSkip(bytesRemaining);
|
||||
if (lengthError) ErrorCode.write(client, ErrorCode.Length, RequestCode.SetFontPath, 0);
|
||||
else xServer.setFontPath(fontPaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the font name match the pattern?
|
||||
*
|
||||
* @param idx The index of the font being matched.
|
||||
* @param pattern The pattern being matched.
|
||||
* @param pfields The pattern, broken into its components.
|
||||
* @return The name of the matching font, or null if it doesn't match.
|
||||
*/
|
||||
private static String fontMatchesPattern(int idx, String pattern, String[] pfields) {
|
||||
String font = _allFonts[idx];
|
||||
|
||||
if (pattern.equals("*")) return font;
|
||||
|
||||
String[] fields;
|
||||
|
||||
if (_allFontFields == null) _allFontFields = new String[_allFonts.length][];
|
||||
|
||||
if (_allFontFields[idx] == null) fields = _allFontFields[idx] = font.split("-");
|
||||
else fields = _allFontFields[idx];
|
||||
|
||||
if (fields.length < pfields.length) return null;
|
||||
|
||||
if (fields.length == 1) return pattern.equalsIgnoreCase(font) ? font : null;
|
||||
|
||||
int offset = 0;
|
||||
boolean rescale = false;
|
||||
|
||||
if (pfields[0].equals("*")) offset = fields.length - pfields.length;
|
||||
|
||||
for (int i = 0; i < pfields.length; i++) {
|
||||
if (pfields[i].equals("*")) continue;
|
||||
|
||||
int foff = offset + i;
|
||||
|
||||
if (foff == 0 || foff == 9 || foff == 10)
|
||||
continue; // First field not used. And ignore resolution.
|
||||
else if (fields[foff].equalsIgnoreCase(pfields[i])) continue;
|
||||
// else if (fields[foff].matches (pfields[i]))
|
||||
// continue; // Pattern matching.
|
||||
else if (foff >= 7 && foff <= 8) // Pixel and point size.
|
||||
rescale = true;
|
||||
else return null;
|
||||
}
|
||||
|
||||
if (rescale) {
|
||||
int pixels = 0;
|
||||
int points = 0;
|
||||
|
||||
if (offset <= 7) {
|
||||
try {
|
||||
pixels = Integer.parseInt(pfields[7 - offset]);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
if (offset <= 8) {
|
||||
try {
|
||||
points = Integer.parseInt(pfields[8 - offset]);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
if (pixels == 0 && points == 0) return font;
|
||||
else if (pixels == 0 && points != 0) pixels = (int) Math.round(points * _dpi / 722.7);
|
||||
else if (pixels != 0 && points == 0) points = (int) Math.round(pixels * 722.7 / _dpi);
|
||||
|
||||
return "-" + fields[1] + "-" + fields[2] + "-" + fields[3] + "-" + fields[4] + "-" + fields[5] + "-" + fields[6] + "-" + pixels + "-" + points + "-" + _dpi + "-" + _dpi + "-" + fields[11] + "-" + fields[12] + "-" + fields[13] + "-" + fields[14];
|
||||
}
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a ListFonts or ListFontsWithInfo request.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @param opcode The request's opcode.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void processListFonts(Client client, byte opcode, int bytesRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
if (bytesRemaining < 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
int maxNames = io.readShort(); // Max names.
|
||||
int length = io.readShort(); // Length of pattern.
|
||||
int pad = -length & 3;
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if (bytesRemaining != length + pad) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[length];
|
||||
|
||||
io.readBytes(bytes, 0, length); // Pattern.
|
||||
io.readSkip(pad); // Unused.
|
||||
|
||||
String pattern = new String(bytes);
|
||||
String[] pfields = pattern.split("-");
|
||||
Vector<String> fonts = new Vector<String>();
|
||||
|
||||
for (int i = 0; i < _allFonts.length; i++) {
|
||||
String f = fontMatchesPattern(i, pattern, pfields);
|
||||
|
||||
if (f != null) {
|
||||
fonts.add(f);
|
||||
if (fonts.size() >= maxNames) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (opcode == RequestCode.ListFonts) {
|
||||
length = 0;
|
||||
for (String s : fonts)
|
||||
length += s.length() + 1;
|
||||
|
||||
pad = -length & 3;
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, (byte) 0);
|
||||
io.writeInt((length + pad) / 4); // Reply length.
|
||||
io.writeShort((short) fonts.size()); // Number of names.
|
||||
io.writePadBytes(22); // Unused.
|
||||
|
||||
for (String s : fonts) {
|
||||
byte[] ba = s.getBytes();
|
||||
|
||||
io.writeByte((byte) ba.length);
|
||||
io.writeBytes(ba, 0, ba.length);
|
||||
}
|
||||
|
||||
io.writePadBytes(pad); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
} else {
|
||||
int remaining = fonts.size();
|
||||
|
||||
for (String s : fonts)
|
||||
writeFontWithInfo(client, s, remaining--);
|
||||
|
||||
// Last in series indicator.
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, (byte) 0);
|
||||
io.writeInt(7); // Reply length.
|
||||
io.writePadBytes(52); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write information about a named font.
|
||||
* This is one of multiple replies to a ListFontsWithInfo request.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @param name The name of the font.
|
||||
* @param fontsRemaining Number of replies before request is complete.
|
||||
* @throws IOException
|
||||
*/
|
||||
private static void writeFontWithInfo(Client client, String name, int fontsRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
Font font = new Font(0, null, null, name);
|
||||
int numFontProperties = 0;
|
||||
byte nameLength = (byte) name.length();
|
||||
int pad = -nameLength & 3;
|
||||
Paint.FontMetricsInt metrics = font._paint.getFontMetricsInt();
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, nameLength);
|
||||
// Reply length.
|
||||
io.writeInt(7 + numFontProperties * 2 + (nameLength + pad) / 4);
|
||||
|
||||
// Min bounds.
|
||||
io.writeShort((short) 0); // Left side bearing.
|
||||
io.writeShort((short) 0); // Right side bearing.
|
||||
io.writeShort((short) font._minWidth); // Character width.
|
||||
io.writeShort((short) 0); // Ascent.
|
||||
io.writeShort((short) 0); // Descent.
|
||||
io.writeShort((short) 0); // Attributes.
|
||||
io.writePadBytes(4); // Unused.
|
||||
|
||||
// Max bounds.
|
||||
io.writeShort((short) 0); // Left side bearing.
|
||||
io.writeShort((short) font._maxWidth); // Right side bearing.
|
||||
io.writeShort((short) font._maxWidth); // Character width.
|
||||
io.writeShort((short) -metrics.top); // Ascent.
|
||||
io.writeShort((short) metrics.bottom); // Descent.
|
||||
io.writeShort((short) 0); // Attributes.
|
||||
io.writePadBytes(4); // Unused.
|
||||
|
||||
io.writeShort((short) 32); // Min char or byte2.
|
||||
io.writeShort((short) font._maxChar); // Max char or byte2.
|
||||
io.writeShort((short) 32); // Default char.
|
||||
io.writeShort((short) numFontProperties);
|
||||
io.writeByte((byte) 0); // Draw direction = left-to-right.
|
||||
io.writeByte((byte) 0); // Min byte 1.
|
||||
io.writeByte((byte) 0); // Max byte 1.
|
||||
io.writeByte((byte) 0); // All chars exist = false.
|
||||
io.writeShort((short) -metrics.ascent); // Font ascent.
|
||||
io.writeShort((short) metrics.descent); // Font descent.
|
||||
io.writeInt(fontsRemaining); // Replies hint.
|
||||
// No font properties.
|
||||
io.writeBytes(name.getBytes(), 0, nameLength); // Name.
|
||||
io.writePadBytes(pad); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* This class stores details of a pixmap format.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
public class Format {
|
||||
private final byte _depth;
|
||||
private final byte _bitsPerPixel;
|
||||
private final byte _scanlinePad;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param depth The depth in bits.
|
||||
* @param bitsPerPixel Number of bits per pixel.
|
||||
* @param scanlinePad Number of bits to pad each scan line.
|
||||
*/
|
||||
public Format(byte depth, byte bitsPerPixel, byte scanlinePad) {
|
||||
_depth = depth;
|
||||
_bitsPerPixel = bitsPerPixel;
|
||||
_scanlinePad = scanlinePad;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write details of the format.
|
||||
*
|
||||
* @param io The input/output stream.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void write(InputOutput io) throws IOException {
|
||||
io.writeByte(_depth); // Depth.
|
||||
io.writeByte(_bitsPerPixel); // Bits per pixel.
|
||||
io.writeByte(_scanlinePad); // Scanline pad.
|
||||
io.writePadBytes(5); // Unused.
|
||||
}
|
||||
}
|
||||
|
|
@ -1,450 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Region;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* This class implements an X graphics context.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
public class GContext extends Resource {
|
||||
private Paint _paint;
|
||||
private Font _font = null;
|
||||
private Path.FillType _fillType;
|
||||
private int[] _attributes;
|
||||
private Rect[] _clipRectangles = null;
|
||||
private int _foregroundColor = 0xff000000;
|
||||
private int _backgroundColor = 0xffffffff;
|
||||
|
||||
private static final int AttrFunction = 0;
|
||||
private static final int AttrPlaneMask = 1;
|
||||
private static final int AttrForeground = 2;
|
||||
private static final int AttrBackground = 3;
|
||||
private static final int AttrLineWidth = 4;
|
||||
private static final int AttrLineStyle = 5;
|
||||
private static final int AttrCapStyle = 6;
|
||||
private static final int AttrJoinStyle = 7;
|
||||
private static final int AttrFillStyle = 8;
|
||||
private static final int AttrFillRule = 9;
|
||||
private static final int AttrTile = 10;
|
||||
private static final int AttrStipple = 11;
|
||||
private static final int AttrTileStippleXOrigin = 12;
|
||||
private static final int AttrTileStippleYOrigin = 13;
|
||||
private static final int AttrFont = 14;
|
||||
private static final int AttrSubwindowMode = 15;
|
||||
private static final int AttrGraphicsExposures = 16;
|
||||
private static final int AttrClipXOrigin = 17;
|
||||
private static final int AttrClipYOrigin = 18;
|
||||
private static final int AttrClipMask = 19;
|
||||
private static final int AttrDashOffset = 20;
|
||||
private static final int AttrDashes = 21;
|
||||
private static final int AttrArcMode = 22;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param id The graphics context's ID.
|
||||
* @param xServer The X server.
|
||||
* @param client The client issuing the request.
|
||||
*/
|
||||
public GContext(int id, XServer xServer, Client client) {
|
||||
super(GCONTEXT, id, xServer, client);
|
||||
|
||||
_paint = new Paint();
|
||||
_attributes = new int[]{3, // function = Copy
|
||||
0xffffffff, // plane-mask = all ones
|
||||
0, // foreground = 0
|
||||
1, // background = 1
|
||||
0, // line-width = 0
|
||||
0, // line-style = Solid
|
||||
1, // cap-style = Butt
|
||||
0, // join-style = Miter
|
||||
0, // fill-style = Solid
|
||||
0, // fill-rule = EvenOdd
|
||||
0, // tile = foreground-filled pixmap
|
||||
0, // stipple = pixmap filled with ones
|
||||
0, // tile-stipple-x-origin = 0
|
||||
0, // tile-stipple-y-origin = 0
|
||||
0, // font = server-dependent
|
||||
0, // subwindow-mode = ClipByChildren
|
||||
1, // graphics-exposures = True
|
||||
0, // clip-x-origin = 0
|
||||
0, // clip-y-origin = 0
|
||||
0, // clip-mask = None
|
||||
0, // dash-offset = 0
|
||||
4, // dashes = 4 (i.e. the list [4,4])
|
||||
1 // arc-mode = PieSlice
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the GContext's Paint handle.
|
||||
*
|
||||
* @return The GContext's Paint handle.
|
||||
*/
|
||||
public Paint getPaint() {
|
||||
return _paint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the GContext's background color.
|
||||
*
|
||||
* @return The GContext's background color.
|
||||
*/
|
||||
public int getBackgroundColor() {
|
||||
return _backgroundColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the GContext's foreground color.
|
||||
*
|
||||
* @return The GContext's foreground color.
|
||||
*/
|
||||
public int getForegroundColor() {
|
||||
return _foregroundColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fill type.
|
||||
*
|
||||
* @return The fill type.
|
||||
*/
|
||||
public Path.FillType getFillType() {
|
||||
return _fillType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the arc mode.
|
||||
* 0 = chord, 1 = pie slice.
|
||||
*
|
||||
* @return The arc mode.
|
||||
*/
|
||||
public int getArcMode() {
|
||||
return _attributes[AttrArcMode];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to generate graphics exposure events.
|
||||
*
|
||||
* @return Whether to generate graphics exposure events.
|
||||
*/
|
||||
public boolean getGraphicsExposure() {
|
||||
return (_attributes[AttrGraphicsExposures] != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the GContext's font.
|
||||
*
|
||||
* @return The GContext's font.
|
||||
*/
|
||||
public Font getFont() {
|
||||
return _font;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the GContext's font.
|
||||
*
|
||||
* @param id The ID of the font.
|
||||
* @return True if the ID refers to a valid font.
|
||||
*/
|
||||
public boolean setFont(int id) {
|
||||
Resource r = _xServer.getResource(id);
|
||||
|
||||
if (r == null || r.getType() != Resource.FONT) return false;
|
||||
|
||||
_font = (Font) r;
|
||||
_paint.setTypeface(_font.getTypeface());
|
||||
_paint.setTextSize(_font.getSize());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the GContext's clip rectangles to the canvas.
|
||||
*
|
||||
* @param canvas The canvas to apply the rectangles to.
|
||||
*/
|
||||
public void applyClipRectangles(Canvas canvas) {
|
||||
if (_clipRectangles == null) return;
|
||||
|
||||
if (_clipRectangles.length == 0) canvas.clipRect(0, 0, 0, 0);
|
||||
else for (Rect r : _clipRectangles){
|
||||
canvas.save();
|
||||
canvas.clipRect(r);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an X request relating to this graphics context.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @param opcode The request's opcode.
|
||||
* @param arg Optional first argument.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public void processRequest(Client client, byte opcode, byte arg, int bytesRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
switch (opcode) {
|
||||
case RequestCode.QueryFont:
|
||||
case RequestCode.QueryTextExtents:
|
||||
_font.processRequest(client, opcode, arg, bytesRemaining);
|
||||
return;
|
||||
case RequestCode.ChangeGC:
|
||||
processValues(client, opcode, bytesRemaining);
|
||||
break;
|
||||
case RequestCode.CopyGC:
|
||||
if (bytesRemaining != 8) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int id = io.readInt(); // Destination GContext.
|
||||
int mask = io.readInt(); // Value mask.
|
||||
Resource r = _xServer.getResource(id);
|
||||
|
||||
if (r == null || r.getType() != Resource.GCONTEXT) {
|
||||
ErrorCode.write(client, ErrorCode.GContext, opcode, id);
|
||||
} else {
|
||||
GContext gc = (GContext) r;
|
||||
|
||||
for (int i = 0; i < 23; i++)
|
||||
if ((mask & (1 << i)) != 0) gc._attributes[i] = _attributes[i];
|
||||
|
||||
gc.applyValues(null, opcode);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.SetDashes:
|
||||
if (bytesRemaining < 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
io.readShort(); // Dash offset.
|
||||
|
||||
int n = io.readShort(); // Length of dashes.
|
||||
int pad = -n & 3;
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if (bytesRemaining != n + pad)
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
|
||||
io.readSkip(n + pad); // Ignore the dash information.
|
||||
}
|
||||
break;
|
||||
case RequestCode.SetClipRectangles:
|
||||
if (bytesRemaining < 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int clipXOrigin = (short) io.readShort();
|
||||
int clipYOrigin = (short) io.readShort();
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if ((bytesRemaining & 7) != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int i = 0;
|
||||
|
||||
_clipRectangles = new Rect[bytesRemaining / 8];
|
||||
while (bytesRemaining > 0) {
|
||||
int x = (short) io.readShort();
|
||||
int y = (short) io.readShort();
|
||||
int width = io.readShort();
|
||||
int height = io.readShort();
|
||||
|
||||
bytesRemaining -= 8;
|
||||
_clipRectangles[i++] = new Rect(x + clipXOrigin, y + clipYOrigin, x + clipXOrigin + width, y + clipYOrigin + height);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.FreeGC:
|
||||
if (bytesRemaining != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
_xServer.freeResource(_id);
|
||||
if (_client != null) _client.freeResource(this);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Implementation, opcode, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a CreateGC request.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The client issuing the request.
|
||||
* @param id The ID of the GContext to create.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void processCreateGCRequest(XServer xServer, Client client, int id, int bytesRemaining) throws IOException {
|
||||
GContext gc = new GContext(id, xServer, client);
|
||||
|
||||
if (gc.processValues(client, RequestCode.CreateGC, bytesRemaining)) {
|
||||
xServer.addResource(gc);
|
||||
client.addResource(gc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a list of GContext attribute values.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @param opcode The opcode being processed.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @return True if the values are all valid.
|
||||
* @throws IOException
|
||||
*/
|
||||
private boolean processValues(Client client, byte opcode, int bytesRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
if (bytesRemaining < 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
int valueMask = io.readInt(); // Value mask.
|
||||
int n = Util.bitcount(valueMask);
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if (bytesRemaining != n * 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 23; i++)
|
||||
if ((valueMask & (1 << i)) != 0) processValue(io, i);
|
||||
|
||||
return applyValues(client, opcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single GContext attribute value.
|
||||
*
|
||||
* @param io The input/output stream.
|
||||
* @param maskBit The mask bit of the attribute.
|
||||
* @throws IOException
|
||||
*/
|
||||
private void processValue(InputOutput io, int maskBit) throws IOException {
|
||||
switch (maskBit) {
|
||||
case AttrFunction:
|
||||
case AttrLineStyle:
|
||||
case AttrCapStyle:
|
||||
case AttrJoinStyle:
|
||||
case AttrFillStyle:
|
||||
case AttrFillRule:
|
||||
case AttrSubwindowMode:
|
||||
case AttrGraphicsExposures:
|
||||
case AttrDashes:
|
||||
case AttrArcMode:
|
||||
_attributes[maskBit] = io.readByte();
|
||||
io.readSkip(3);
|
||||
break;
|
||||
case AttrPlaneMask:
|
||||
case AttrForeground:
|
||||
case AttrBackground:
|
||||
case AttrTile:
|
||||
case AttrStipple:
|
||||
case AttrFont:
|
||||
case AttrClipMask:
|
||||
_attributes[maskBit] = io.readInt();
|
||||
break;
|
||||
case AttrLineWidth:
|
||||
case AttrDashOffset:
|
||||
_attributes[maskBit] = io.readShort();
|
||||
io.readSkip(2);
|
||||
break;
|
||||
case AttrTileStippleXOrigin:
|
||||
case AttrTileStippleYOrigin:
|
||||
case AttrClipXOrigin:
|
||||
case AttrClipYOrigin:
|
||||
_attributes[maskBit] = (short) io.readShort();
|
||||
io.readSkip(2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the attribute values to the Paint.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @param opcode The opcode being processed.
|
||||
* @return True if the values are all valid.
|
||||
* @throws IOException
|
||||
*/
|
||||
private boolean applyValues(Client client, byte opcode) throws IOException {
|
||||
boolean ok = true;
|
||||
|
||||
_foregroundColor = _attributes[AttrForeground] | 0xff000000;
|
||||
_backgroundColor = _attributes[AttrBackground] | 0xff000000;
|
||||
|
||||
_paint.setColor(_foregroundColor);
|
||||
_paint.setStrokeWidth(_attributes[AttrLineWidth]);
|
||||
|
||||
if (_attributes[AttrFunction] == 6) // XOR.
|
||||
_paint.setXfermode(new PorterDuffXfermode(Mode.XOR));
|
||||
else _paint.setXfermode(null);
|
||||
|
||||
switch (_attributes[AttrCapStyle]) {
|
||||
case 0: // NotLast
|
||||
case 1: // Butt
|
||||
_paint.setStrokeCap(Paint.Cap.BUTT);
|
||||
break;
|
||||
case 2: // Round
|
||||
_paint.setStrokeCap(Paint.Cap.ROUND);
|
||||
break;
|
||||
case 3: // Projecting
|
||||
_paint.setStrokeCap(Paint.Cap.SQUARE);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (_attributes[AttrJoinStyle]) {
|
||||
case 0: // Miter
|
||||
_paint.setStrokeJoin(Paint.Join.MITER);
|
||||
break;
|
||||
case 1: // Round
|
||||
_paint.setStrokeJoin(Paint.Join.ROUND);
|
||||
break;
|
||||
case 2: // Bevel
|
||||
_paint.setStrokeJoin(Paint.Join.BEVEL);
|
||||
break;
|
||||
}
|
||||
|
||||
if (_attributes[AttrFillRule] == 1) // Winding.
|
||||
_fillType = Path.FillType.WINDING;
|
||||
else // Defaults to even-odd.
|
||||
_fillType = Path.FillType.EVEN_ODD;
|
||||
|
||||
int fid = _attributes[AttrFont];
|
||||
|
||||
if (_font == null || fid == 0) _font = _xServer.getDefaultFont();
|
||||
|
||||
if (fid != 0 && !setFont(fid)) {
|
||||
ok = false;
|
||||
ErrorCode.write(client, ErrorCode.Font, opcode, fid);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,350 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This class handles buffered bi-directional communications.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
public class InputOutput {
|
||||
private final BufferedInputStream _inStream;
|
||||
private final BufferedOutputStream _outStream;
|
||||
private boolean _msb = true;
|
||||
private static final byte[] PadBytes = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param socket Communicate via this socket.
|
||||
* @throws IOException
|
||||
*/
|
||||
public InputOutput(Socket socket) throws IOException {
|
||||
_inStream = new BufferedInputStream(socket.getInputStream(), 16384);
|
||||
_outStream = new BufferedOutputStream(socket.getOutputStream(), 16384);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the most significant byte comes first.
|
||||
*
|
||||
* @param msb
|
||||
*/
|
||||
public void setMSB(boolean msb) {
|
||||
_msb = msb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an 8-bit integer from the input stream.
|
||||
*
|
||||
* @return An 8-bit integer in the range 0 to 255.
|
||||
* @throws IOException
|
||||
*/
|
||||
public int readByte() throws IOException {
|
||||
int n = _inStream.read();
|
||||
|
||||
if (n < 0) throw new IOException();
|
||||
else return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read bytes from the input stream.
|
||||
*
|
||||
* @param ba The array to store the bytes to.
|
||||
* @param offset The start position in the array to store the bytes.
|
||||
* @param length The maximum number of bytes to store.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void readBytes(byte[] ba, int offset, int length) throws IOException {
|
||||
while (length > 0) {
|
||||
int n = _inStream.read(ba, offset, length);
|
||||
|
||||
if (n < 0) {
|
||||
throw new IOException();
|
||||
} else {
|
||||
length -= n;
|
||||
offset += n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read bits from the input stream as an array of booleans.
|
||||
*
|
||||
* @param bits The array to store the bits to.
|
||||
* @param offset The start position in the array to store the bits.
|
||||
* @param length The maximum number of bits to store.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void readBits(boolean[] bits, int offset, int length) throws IOException {
|
||||
for (int i = 0; i < length; i += 8) {
|
||||
int x = readByte();
|
||||
int n = length - i;
|
||||
|
||||
if (n > 8) n = 8;
|
||||
|
||||
for (int j = 0; j < n; j++)
|
||||
bits[offset + i + j] = ((x & (1 << j)) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a shape mask from the input stream as an array of booleans.
|
||||
*
|
||||
* @param bits The array to store the mask to.
|
||||
* @param width Width of the pixmap.
|
||||
* @param height Height of the pixmap.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void readShapeMask(boolean[] bits, int width, int height) throws IOException {
|
||||
int count = 0;
|
||||
int bytesPerRow = (width + 1) / 2;
|
||||
|
||||
for (int row = 0; row < height; row++) {
|
||||
int col = 0;
|
||||
|
||||
for (int i = 0; i < bytesPerRow; i++) {
|
||||
int b = readByte();
|
||||
int mask = 0x80;
|
||||
|
||||
for (int j = 0; j < 8; j++) {
|
||||
bits[count++] = ((b & mask) != 0);
|
||||
mask >>= 1;
|
||||
|
||||
if (++col == width) break;
|
||||
}
|
||||
|
||||
if (col == width) {
|
||||
readSkip(bytesPerRow - i - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a 16-bit integer from the output stream.
|
||||
*
|
||||
* @return A 16-bit integer in the range 0 to 65535.
|
||||
* @throws IOException
|
||||
*/
|
||||
public int readShort() throws IOException {
|
||||
if (_msb) {
|
||||
int n = readByte();
|
||||
|
||||
return (n << 8) | readByte();
|
||||
} else {
|
||||
int n = readByte();
|
||||
|
||||
return n | (readByte() << 8);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a 32-bit integer from the input stream.
|
||||
*
|
||||
* @return A 32-bit signed integer.
|
||||
* @throws IOException
|
||||
*/
|
||||
public int readInt() throws IOException {
|
||||
int n = readByte();
|
||||
|
||||
if (_msb) {
|
||||
n = (n << 8) | readByte();
|
||||
n = (n << 8) | readByte();
|
||||
n = (n << 8) | readByte();
|
||||
} else {
|
||||
n |= readByte() << 8;
|
||||
n |= readByte() << 16;
|
||||
n |= readByte() << 24;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a 64-bit integer from the input stream.
|
||||
*
|
||||
* @return A 64-bit signed integer.
|
||||
* @throws IOException
|
||||
*/
|
||||
public long readLong() throws IOException {
|
||||
long n = readByte();
|
||||
|
||||
if (_msb) {
|
||||
n = (n << 8) | (long)readByte();
|
||||
n = (n << 8) | (long)readByte();
|
||||
n = (n << 8) | (long)readByte();
|
||||
n = (n << 8) | (long)readByte();
|
||||
n = (n << 8) | (long)readByte();
|
||||
n = (n << 8) | (long)readByte();
|
||||
n = (n << 8) | (long)readByte();
|
||||
} else {
|
||||
n |= (long)readByte() << 8;
|
||||
n |= (long)readByte() << 16;
|
||||
n |= (long)readByte() << 24;
|
||||
n |= (long)readByte() << 32;
|
||||
n |= (long)readByte() << 40;
|
||||
n |= (long)readByte() << 48;
|
||||
n |= (long)readByte() << 56;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip bytes from the input stream.
|
||||
*
|
||||
* @param n The number of bytes to skip.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void readSkip(int n) throws IOException {
|
||||
int avaiable = _inStream.available();
|
||||
if(n > avaiable) // to avoid blocking
|
||||
n = avaiable;
|
||||
while (n > 0)
|
||||
n -= _inStream.skip(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an 8-bit integer to the output stream.
|
||||
*
|
||||
* @param n The byte to write.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void writeByte(byte n) throws IOException {
|
||||
_outStream.write(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write bytes to the output stream.
|
||||
*
|
||||
* @param ba The array to be written.
|
||||
* @param offset The start position in the array to write from.
|
||||
* @param length The number of bytes to write.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void writeBytes(byte[] ba, int offset, int length) throws IOException {
|
||||
_outStream.write(ba, offset, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a 16-bit integer to the output stream.
|
||||
*
|
||||
* @param n The short to write.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void writeShort(short n) throws IOException {
|
||||
if (_msb) {
|
||||
_outStream.write((byte) ((n >> 8) & 0xff));
|
||||
_outStream.write((byte) (n & 0xff));
|
||||
} else {
|
||||
_outStream.write((byte) (n & 0xff));
|
||||
_outStream.write((byte) ((n >> 8) & 0xff));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a 32-bit integer to the output stream.
|
||||
*
|
||||
* @param n The integer to write.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void writeInt(int n) throws IOException {
|
||||
if (_msb) {
|
||||
_outStream.write((byte) ((n >> 24) & 0xff));
|
||||
_outStream.write((byte) ((n >> 16) & 0xff));
|
||||
_outStream.write((byte) ((n >> 8) & 0xff));
|
||||
_outStream.write((byte) (n & 0xff));
|
||||
} else {
|
||||
_outStream.write((byte) ((n) & 0xff));
|
||||
_outStream.write((byte) ((n >> 8) & 0xff));
|
||||
_outStream.write((byte) ((n >> 16) & 0xff));
|
||||
_outStream.write((byte) ((n >> 24) & 0xff));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a 64-bit integer to the output stream.
|
||||
*
|
||||
* @param n The integer to write.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void writeLong(long n) throws IOException {
|
||||
if (_msb) {
|
||||
_outStream.write((byte) ((n >> 56) & 0xff));
|
||||
_outStream.write((byte) ((n >> 48) & 0xff));
|
||||
_outStream.write((byte) ((n >> 40) & 0xff));
|
||||
_outStream.write((byte) ((n >> 32) & 0xff));
|
||||
_outStream.write((byte) ((n >> 24) & 0xff));
|
||||
_outStream.write((byte) ((n >> 16) & 0xff));
|
||||
_outStream.write((byte) ((n >> 8) & 0xff));
|
||||
_outStream.write((byte) (n & 0xff));
|
||||
} else {
|
||||
_outStream.write((byte) ((n) & 0xff));
|
||||
_outStream.write((byte) ((n >> 8) & 0xff));
|
||||
_outStream.write((byte) ((n >> 16) & 0xff));
|
||||
_outStream.write((byte) ((n >> 24) & 0xff));
|
||||
_outStream.write((byte) ((n >> 32) & 0xff));
|
||||
_outStream.write((byte) ((n >> 40) & 0xff));
|
||||
_outStream.write((byte) ((n >> 48) & 0xff));
|
||||
_outStream.write((byte) ((n >> 56) & 0xff));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write padding byte 0 to the output stream multiple times.
|
||||
*
|
||||
* @param n The number of bytes to write.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void writePadBytes(int n) throws IOException {
|
||||
final int max = PadBytes.length;
|
||||
|
||||
while (n > max) {
|
||||
_outStream.write(PadBytes, 0, max);
|
||||
n -= max;
|
||||
}
|
||||
|
||||
if (n > 0) _outStream.write(PadBytes, 0, n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush all unwritten output bytes in own background thread.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public synchronized void flush() throws IOException {
|
||||
Thread t = new Thread(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
synchronized(this){
|
||||
_outStream.flush();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e("FATAL", e.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
try{
|
||||
t.join();
|
||||
}catch (InterruptedException e) {
|
||||
Log.e("FATAL", e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the input and output streams.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
_inStream.close();
|
||||
_outStream.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,543 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AudioTrack;
|
||||
import android.view.KeyCharacterMap;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This class handles an X keyboard.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
public class Keyboard {
|
||||
private int _minimumKeycode;
|
||||
private int _numKeycodes;
|
||||
private byte _keysymsPerKeycode = 3;
|
||||
private int[] _keyboardMapping = null;
|
||||
private byte _keycodesPerModifier = 8;
|
||||
private byte[] _keymap = new byte[32];
|
||||
private byte[] _modifierMapping = new byte[] {
|
||||
KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_SHIFT_RIGHT, 0, 0, 0, 0, 0, 0, // 1
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // 2
|
||||
KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.KEYCODE_CTRL_RIGHT, 0, 0, 0, 0, 0, 0, // 4
|
||||
KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_ALT_RIGHT, 0, 0, 0, 0, 0, 0, // 8
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, // 20
|
||||
KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_META_RIGHT, 0, 0, 0, 0, 0, 0, // 40
|
||||
0, 0, 0, 0, 0, 0, 0, 0 // 80
|
||||
};
|
||||
|
||||
private static final int DefaultBellPercent = 50;
|
||||
private int _bellPercent = DefaultBellPercent;
|
||||
private static final int DefaultBellPitch = 400;
|
||||
private int _bellPitch = DefaultBellPitch;
|
||||
private static final int DefaultBellDuration = 100;
|
||||
private int _bellDuration = DefaultBellDuration;
|
||||
private short[] _bellBuffer = null;
|
||||
private boolean _bellBufferFilled = false;
|
||||
private AudioTrack _audioTrack = null;
|
||||
|
||||
private static final int SAMPLE_RATE = 11025;
|
||||
private static final int AttrKeyClickPercent = 0;
|
||||
private static final int AttrBellPercent = 1;
|
||||
private static final int AttrBellPitch = 2;
|
||||
private static final int AttrBellDuration = 3;
|
||||
private static final int AttrLed = 4;
|
||||
private static final int AttrLedMode = 5;
|
||||
private static final int AttrKey = 6;
|
||||
private static final int AttrAutoRepeatMode = 7;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
Keyboard() {
|
||||
final byte kpk = _keysymsPerKeycode;
|
||||
int min = 255;
|
||||
int max = 0;
|
||||
int idx = 0;
|
||||
int[] map = new int[256 * kpk];
|
||||
KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.FULL);
|
||||
// Some devices don't have keyboard with full type, and the Android OS uses
|
||||
// the KeyCharacterMap.BUILT_IN_KEYBOARD (value is 0) as the fallback keyboard
|
||||
// type. If the phone/tablet doesn't have built in keyboard, the returned
|
||||
// KeyCharacterMap is empty, and it will causes the ArrayOutOfBounds exception
|
||||
// when initializing keyboard mapping.
|
||||
if (kcm.getKeyboardType() != KeyCharacterMap.FULL) {
|
||||
kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
int c1 = kcm.get(i, 0);
|
||||
int c2 = kcm.get(i, KeyEvent.META_SHIFT_ON);
|
||||
int c3 = kcm.get(i, KeyEvent.META_ALT_ON);
|
||||
|
||||
map[idx++] = c1;
|
||||
map[idx++] = c2;
|
||||
map[idx++] = c3;
|
||||
|
||||
if (c1 != 0 || c2 != 0 || c3 != 0) {
|
||||
if (i < min) min = i;
|
||||
if (i > max) max = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (max == 0) min = 0;
|
||||
|
||||
if (max < KeyEvent.KEYCODE_DEL) max = KeyEvent.KEYCODE_DEL;
|
||||
|
||||
_minimumKeycode = min;
|
||||
_numKeycodes = max - min + 1;
|
||||
if (_numKeycodes > 248) _numKeycodes = 248;
|
||||
|
||||
_keyboardMapping = new int[kpk * _numKeycodes];
|
||||
System.arraycopy(map, min * kpk, _keyboardMapping, 0, _keyboardMapping.length);
|
||||
|
||||
// Keycode translation table
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_FORWARD_DEL - min) * kpk] = 0xffff; // del
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_DEL - min) * kpk] = 0xff08; // backspace
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_ALT_LEFT - min) * kpk] = 0xffe9;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_ALT_RIGHT - min) * kpk] = 0xfe03;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_CTRL_LEFT - min) * kpk] = 0xffe3;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_CTRL_RIGHT - min) * kpk] = 0xffe4;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_ENTER - min) * kpk] = 0xff0d; // enter/return
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_DPAD_UP - min) * kpk] = 0xff52;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_DPAD_DOWN - min) * kpk] = 0xff54;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_DPAD_LEFT - min) * kpk] = 0xff51;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_DPAD_RIGHT - min) * kpk] = 0xff53;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_PAGE_UP - min) * kpk] = 0xff55;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_PAGE_DOWN - min) * kpk] = 0xff56;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_MOVE_HOME - min) * kpk] = 0xff50;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_MOVE_END - min) * kpk] = 0xff57;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_ESCAPE - min) * kpk] = 0xff1b;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_F1 - min) * kpk] = 0xffbe;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_F2 - min) * kpk] = 0xffbf;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_F3 - min) * kpk] = 0xffc0;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_F4 - min) * kpk] = 0xffc1;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_F5 - min) * kpk] = 0xffc2;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_F6 - min) * kpk] = 0xffc3;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_F7 - min) * kpk] = 0xffc4;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_F8 - min) * kpk] = 0xffc5;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_F9 - min) * kpk] = 0xffc6;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_F10 - min) * kpk] = 0xffc7;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_F11 - min) * kpk] = 0xffc8;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_F12 - min) * kpk] = 0xffc9;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_INSERT - min) * kpk] = 0xff63;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_NUMPAD_0 - min) * kpk] = 0xffb0;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_NUMPAD_1 - min) * kpk] = 0xffb1;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_NUMPAD_2 - min) * kpk] = 0xffb2;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_NUMPAD_3 - min) * kpk] = 0xffb3;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_NUMPAD_4 - min) * kpk] = 0xffb4;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_NUMPAD_5 - min) * kpk] = 0xffb5;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_NUMPAD_6 - min) * kpk] = 0xffb6;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_NUMPAD_7 - min) * kpk] = 0xffb7;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_NUMPAD_8 - min) * kpk] = 0xffb8;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_NUMPAD_9 - min) * kpk] = 0xffb9;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_NUMPAD_ADD - min) * kpk] = 0xffab;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_NUMPAD_COMMA - min) * kpk] = 0xffac;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_NUMPAD_DIVIDE - min) * kpk] = 0xffaf;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_NUMPAD_DOT - min) * kpk] = 0xffae;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_NUMPAD_ENTER - min) * kpk] = 0xff8d;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_NUMPAD_EQUALS - min) * kpk] = 0xffbd;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_NUMPAD_MULTIPLY - min) * kpk] = 0xffaa;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_NUMPAD_SUBTRACT - min) * kpk] = 0xffad;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_NUM_LOCK - min) * kpk] = 0xff7f;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_META_LEFT - min) * kpk] = 0xffeb; // Windows / Super key
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_META_RIGHT - min) * kpk] = 0xffec; // Windows / Super key
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_TAB - min) * kpk] = 0xff09;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_BREAK - min) * kpk] = 0xff13;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_SCROLL_LOCK - min) * kpk] = 0xff14;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_SYSRQ - min) * kpk] = 0xff61; // print screen
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_SHIFT_LEFT - min) * kpk] = 0xffe1;
|
||||
_keyboardMapping[(KeyEvent.KEYCODE_SHIFT_RIGHT - min) * kpk] = 0xffe2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate an Android keycode to an X keycode.
|
||||
*
|
||||
* @param keycode The Android keycode.
|
||||
* @return The corresponding X keycode.
|
||||
*/
|
||||
public int translateToXKeycode(int keycode) {
|
||||
if (_minimumKeycode < 8) return keycode + 8 - _minimumKeycode;
|
||||
else return keycode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum keycode.
|
||||
*
|
||||
* @return The minimum keycode.
|
||||
*/
|
||||
public int getMinimumKeycode() {
|
||||
if (_minimumKeycode < 8) return 8;
|
||||
else return _minimumKeycode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum keycode diff.
|
||||
*
|
||||
* @return The minimum keycode.
|
||||
*/
|
||||
public int getMinimumKeycodeDiff() {
|
||||
if (_minimumKeycode < 8) return 8 - _minimumKeycode;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum keycode.
|
||||
*
|
||||
* @return The maximum keycode.
|
||||
*/
|
||||
public int getMaximumKeycode() {
|
||||
return getMinimumKeycode() + _numKeycodes - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the keymap for keycodes 8-255.
|
||||
*
|
||||
* @return The keymap for keycodes 8-255.
|
||||
*/
|
||||
public byte[] getKeymap() {
|
||||
byte[] keymap = new byte[31];
|
||||
|
||||
System.arraycopy(_keymap, 1, keymap, 0, 31);
|
||||
|
||||
return keymap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the keymap when a key is pressed or released.
|
||||
*
|
||||
* @param keycode The keycode of the key.
|
||||
* @param pressed True if pressed, false if released.
|
||||
*/
|
||||
public void updateKeymap(int keycode, boolean pressed) {
|
||||
if (keycode < 0 || keycode > 255) return;
|
||||
|
||||
int offset = keycode / 8;
|
||||
byte mask = (byte) (1 << (keycode & 7));
|
||||
|
||||
if (pressed) _keymap[offset] |= mask;
|
||||
else _keymap[offset] &= ~mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an X request relating to this keyboard.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The remote client.
|
||||
* @param opcode The request's opcode.
|
||||
* @param arg Optional first argument.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void processRequest(XServer xServer, Client client, byte opcode, byte arg, int bytesRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
switch (opcode) {
|
||||
case RequestCode.QueryKeymap:
|
||||
if (bytesRemaining != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, (byte) 0);
|
||||
io.writeInt(2); // Reply length.
|
||||
io.writeBytes(_keymap, 0, 32); // Keys.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
break;
|
||||
case RequestCode.ChangeKeyboardMapping:
|
||||
if (bytesRemaining < 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
byte keycodeCount = arg;
|
||||
byte keycode = (byte) io.readByte(); // First code.
|
||||
byte kspkc = (byte) io.readByte(); // Keysyms per code.
|
||||
|
||||
io.readSkip(2); // Unused.
|
||||
bytesRemaining -= 4;
|
||||
|
||||
if (bytesRemaining != keycodeCount * kspkc * 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else if (kspkc > _keysymsPerKeycode) {
|
||||
io.readSkip (bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Value, opcode, 0);
|
||||
} else {
|
||||
int numKeycodes = keycode + keycodeCount;
|
||||
if (numKeycodes > _numKeycodes ) {
|
||||
int [] newKeyboardMapping = new int[(numKeycodes - getMinimumKeycode ()) * _keysymsPerKeycode];
|
||||
System.arraycopy(_keyboardMapping, 0, newKeyboardMapping, 0, (_numKeycodes - getMinimumKeycode ()) * _keysymsPerKeycode);
|
||||
_keyboardMapping = newKeyboardMapping;
|
||||
_numKeycodes = numKeycodes;
|
||||
}
|
||||
|
||||
for (int key = 0; key < keycodeCount; key++)
|
||||
for (int sym = 0; sym < kspkc; sym++)
|
||||
_keyboardMapping[(keycode - getMinimumKeycode () + key) * _keysymsPerKeycode + sym] = io.readInt (); // Keysyms.
|
||||
|
||||
xServer.sendMappingNotify(1, keycode, keycodeCount);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.GetKeyboardMapping:
|
||||
if (bytesRemaining != 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int keycode = io.readByte(); // First code.
|
||||
int count = io.readByte(); // Count.
|
||||
int length = count * _keysymsPerKeycode;
|
||||
int offset = (keycode - getMinimumKeycode()) * _keysymsPerKeycode;
|
||||
|
||||
io.readSkip(2); // Unused.
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, _keysymsPerKeycode);
|
||||
io.writeInt(length); // Reply length.
|
||||
io.writePadBytes(24); // Unused.
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
int n = i + offset;
|
||||
|
||||
if (n < 0 || n >= _keyboardMapping.length)
|
||||
io.writeInt(0); // No symbol.
|
||||
else io.writeInt(_keyboardMapping[n]);
|
||||
}
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
break;
|
||||
case RequestCode.ChangeKeyboardControl:
|
||||
if (bytesRemaining < 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int valueMask = io.readInt(); // Value mask.
|
||||
int nbits = Util.bitcount(valueMask);
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if (bytesRemaining != nbits * 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
for (int i = 0; i < 23; i++)
|
||||
if ((valueMask & (1 << i)) != 0) processValue(io, i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.GetKeyboardControl:
|
||||
if (bytesRemaining != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, _keysymsPerKeycode);
|
||||
io.writeInt(5); // Reply length.
|
||||
io.writeInt(0); // LED mask.
|
||||
io.writeByte((byte) 0); // Key click percent.
|
||||
io.writeByte((byte) _bellPercent); // Bell volume.
|
||||
io.writeShort((short) _bellPitch); // Bell pitch Hz.
|
||||
io.writeShort((short) _bellDuration);
|
||||
io.writePadBytes(2); // Unused.
|
||||
io.writePadBytes(32); // Auto repeats. Ignored.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
break;
|
||||
case RequestCode.SetModifierMapping:
|
||||
if (bytesRemaining != 8 * arg) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else { // Not supported. Always fails.
|
||||
int diff = getMinimumKeycodeDiff();
|
||||
int status = 0;
|
||||
|
||||
byte[] modifierMapping = new byte[bytesRemaining];
|
||||
for (int i = 0; i < bytesRemaining ; i++) {
|
||||
int keycode = io.readByte();
|
||||
int offset = keycode / 8;
|
||||
byte mask = (byte) (1 << (keycode & 7));
|
||||
if ((_keymap[offset] & mask) == mask)
|
||||
status = 2; // If a modifier is pressed
|
||||
modifierMapping[i] = (byte) (keycode - diff);
|
||||
}
|
||||
|
||||
if (status == 0) {
|
||||
_modifierMapping = modifierMapping;
|
||||
xServer.sendMappingNotify (0, 0, 0);
|
||||
}
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, (byte) status);
|
||||
io.writeInt(0); // Reply length.
|
||||
io.writePadBytes(24); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
break;
|
||||
case RequestCode.GetModifierMapping:
|
||||
if (bytesRemaining != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
final byte kpm = _keycodesPerModifier;
|
||||
byte[] map = null;
|
||||
|
||||
if (kpm > 0) {
|
||||
int diff = getMinimumKeycodeDiff();
|
||||
|
||||
map = new byte[kpm * 8];
|
||||
for (int i = 0; i < map.length; i++)
|
||||
if (_modifierMapping[i] == 0) map[i] = 0;
|
||||
else map[i] = (byte) (_modifierMapping[i] + diff);
|
||||
}
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, kpm);
|
||||
io.writeInt(kpm * 2); // Reply length.
|
||||
io.writePadBytes(24); // Unused.
|
||||
|
||||
if (map != null) io.writeBytes(map, 0, map.length);
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
break;
|
||||
case RequestCode.Bell:
|
||||
if (bytesRemaining != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
playBell((byte) arg);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Implementation, opcode, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get modifier state.
|
||||
*
|
||||
* @return Modifier state According to pressed keys (keymap) and modifiermapping.
|
||||
*/
|
||||
public int getState() {
|
||||
int diff = getMinimumKeycodeDiff();
|
||||
int state = 0;
|
||||
|
||||
for (int m = 0 ; m < 8; m++) {
|
||||
for (int i = 0 ; i < 8 && _modifierMapping[m * 8 + i] != 0xff ; i++) {
|
||||
byte keycode = (byte) (_modifierMapping[m * 8 + i] + diff);
|
||||
int offset = keycode / 8;
|
||||
byte mask = (byte) (1 << (keycode & 7));
|
||||
if ((_keymap[offset] & mask) == mask)
|
||||
state |= 1 << m;
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Play a beep.
|
||||
*
|
||||
* @param percent Volume relative to base volume, [-100, 100]
|
||||
*/
|
||||
private void playBell(int percent) {
|
||||
int volume;
|
||||
|
||||
if (percent < 0) {
|
||||
volume = _bellPercent + _bellPercent * percent / 100;
|
||||
_bellBufferFilled = false;
|
||||
} else if (percent > 0) {
|
||||
volume = _bellPercent - _bellPercent * percent / 100 + percent;
|
||||
_bellBufferFilled = false;
|
||||
} else {
|
||||
volume = _bellPercent;
|
||||
}
|
||||
|
||||
if (_bellBuffer == null) {
|
||||
_bellBuffer = new short[SAMPLE_RATE * _bellDuration / 1000];
|
||||
_bellBufferFilled = false;
|
||||
|
||||
}
|
||||
|
||||
if (!_bellBufferFilled) {
|
||||
double vol = 32767.0 * (double) volume / 100.0;
|
||||
double dt = _bellPitch * 2.0 * Math.PI / SAMPLE_RATE;
|
||||
|
||||
for (int i = 0; i < _bellBuffer.length; i++)
|
||||
_bellBuffer[i] = (short) (vol * Math.sin((double) i * dt));
|
||||
|
||||
_bellBufferFilled = true;
|
||||
}
|
||||
|
||||
if (_audioTrack != null) {
|
||||
_audioTrack.stop();
|
||||
_audioTrack.release();
|
||||
}
|
||||
|
||||
_audioTrack = new AudioTrack(AudioManager.STREAM_SYSTEM, SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, 2 * _bellBuffer.length, AudioTrack.MODE_STATIC);
|
||||
|
||||
_audioTrack.write(_bellBuffer, 0, _bellBuffer.length);
|
||||
_audioTrack.play();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single keyboard attribute value.
|
||||
*
|
||||
* @param io The input/output stream.
|
||||
* @param maskBit The mask bit of the attribute.
|
||||
* @throws IOException
|
||||
*/
|
||||
private void processValue(InputOutput io, int maskBit) throws IOException {
|
||||
switch (maskBit) {
|
||||
case AttrKeyClickPercent:
|
||||
io.readByte(); // Not implemented.
|
||||
io.readSkip(3);
|
||||
break;
|
||||
case AttrBellPercent:
|
||||
_bellPercent = (byte) io.readByte();
|
||||
if (_bellPercent < 0) _bellPercent = DefaultBellPercent;
|
||||
io.readSkip(3);
|
||||
_bellBufferFilled = false;
|
||||
break;
|
||||
case AttrBellPitch:
|
||||
_bellPitch = (short) io.readShort();
|
||||
if (_bellPitch < 0) _bellPitch = DefaultBellPitch;
|
||||
io.readSkip(2);
|
||||
_bellBufferFilled = false;
|
||||
break;
|
||||
case AttrBellDuration:
|
||||
_bellDuration = (short) io.readShort();
|
||||
if (_bellDuration < 0) _bellDuration = DefaultBellDuration;
|
||||
io.readSkip(2);
|
||||
_bellBuffer = null;
|
||||
break;
|
||||
case AttrLed:
|
||||
io.readByte(); // Not implemented.
|
||||
io.readSkip(3);
|
||||
break;
|
||||
case AttrLedMode:
|
||||
io.readByte(); // Not implemented.
|
||||
io.readSkip(3);
|
||||
break;
|
||||
case AttrKey:
|
||||
io.readByte(); // Not implemented.
|
||||
io.readSkip(3);
|
||||
break;
|
||||
case AttrAutoRepeatMode:
|
||||
io.readByte(); // Not implemented.
|
||||
io.readSkip(3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
/**
|
||||
* This class records details of a passive button grab.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
public class PassiveButtonGrab {
|
||||
private final Client _grabClient;
|
||||
private final Window _grabWindow;
|
||||
private final byte _button;
|
||||
private final int _modifiers;
|
||||
private final boolean _ownerEvents;
|
||||
private final int _eventMask;
|
||||
private final boolean _pointerSynchronous;
|
||||
private final boolean _keyboardSynchronous;
|
||||
private final Window _confineWindow;
|
||||
private final Cursor _cursor;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param grabClient The grabbing client.
|
||||
* @param grabWindow The grab window.
|
||||
* @param button The button being grabbed, or 0 for any.
|
||||
* @param modifiers The modifier mask, or 0x8000 for any.
|
||||
* @param ownerEvents Owner-events flag.
|
||||
* @param eventMask Selected pointer events.
|
||||
* @param pointerSynchronous Are pointer events synchronous?
|
||||
* @param keyboardSynchronous Are keyboard events synchronous?
|
||||
* @param confineWindow Confine the cursor to this window. Can be null.
|
||||
* @param cursor The cursor to use during the grab. Can be null.
|
||||
*/
|
||||
public PassiveButtonGrab(Client grabClient, Window grabWindow, byte button, int modifiers, boolean ownerEvents, int eventMask, boolean pointerSynchronous, boolean keyboardSynchronous, Window confineWindow, Cursor cursor) {
|
||||
_grabClient = grabClient;
|
||||
_grabWindow = grabWindow;
|
||||
_button = button;
|
||||
_modifiers = modifiers;
|
||||
_ownerEvents = ownerEvents;
|
||||
_eventMask = eventMask;
|
||||
_pointerSynchronous = pointerSynchronous;
|
||||
_keyboardSynchronous = keyboardSynchronous;
|
||||
_confineWindow = confineWindow;
|
||||
_cursor = cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the event trigger the passive grab?
|
||||
*
|
||||
* @param button Currently-pressed buttons and modifiers.
|
||||
* @return True if the event matches.
|
||||
*/
|
||||
public boolean matchesEvent(int buttons) {
|
||||
if (_button != 0 && (buttons & 0xff00) != (0x80 << _button)) return false;
|
||||
|
||||
if (_modifiers != 0x8000 && (buttons & 0xff) != _modifiers) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this match the parameters of the grab?
|
||||
*
|
||||
* @param button The button being grabbed, or 0 for any.
|
||||
* @param modifiers The modifier mask, or 0x8000 for any.
|
||||
* @return True if it matches the parameters.
|
||||
*/
|
||||
public boolean matchesGrab(int button, int modifiers) {
|
||||
if (button != 0 && _button != 0 && button != _button) return false;
|
||||
|
||||
if (modifiers != 0x8000 && _modifiers != 0x8000 && modifiers != _modifiers) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the button.
|
||||
*
|
||||
* @return The button.
|
||||
*/
|
||||
public byte getButton() {
|
||||
return _button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the modifier mask.
|
||||
*
|
||||
* @return The modifier mask.
|
||||
*/
|
||||
public int getModifiers() {
|
||||
return _modifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the grab client.
|
||||
*
|
||||
* @return The grab client.
|
||||
*/
|
||||
public Client getGrabClient() {
|
||||
return _grabClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the grab window.
|
||||
*
|
||||
* @return The grab window.
|
||||
*/
|
||||
public Window getGrabWindow() {
|
||||
return _grabWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the owner-events flag.
|
||||
*
|
||||
* @return The owner-events flag.
|
||||
*/
|
||||
public boolean getOwnerEvents() {
|
||||
return _ownerEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the pointer events mask.
|
||||
*
|
||||
* @return The pointer events mask.
|
||||
*/
|
||||
public int getEventMask() {
|
||||
return _eventMask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether pointer events are synchronous.
|
||||
*
|
||||
* @return Whether pointer events are synchronous.
|
||||
*/
|
||||
public boolean getPointerSynchronous() {
|
||||
return _pointerSynchronous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether pointer events are synchronous.
|
||||
*
|
||||
* @return Whether pointer events are synchronous.
|
||||
*/
|
||||
public boolean getKeyboardSynchronous() {
|
||||
return _keyboardSynchronous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the confine window.
|
||||
*
|
||||
* @return The confine window.
|
||||
*/
|
||||
public Window getConfineWindow() {
|
||||
return _confineWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cursor.
|
||||
*
|
||||
* @return The cursor.
|
||||
*/
|
||||
public Cursor getCursor() {
|
||||
return _cursor;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
/**
|
||||
* This class records details of a passive key grab.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
public class PassiveKeyGrab {
|
||||
private final Client _grabClient;
|
||||
private final Window _grabWindow;
|
||||
private final byte _key;
|
||||
private final int _modifiers;
|
||||
private final boolean _ownerEvents;
|
||||
private final boolean _pointerSynchronous;
|
||||
private final boolean _keyboardSynchronous;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param grabClient The grabbing client.
|
||||
* @param grabWindow The grab window.
|
||||
* @param key The key being grabbed, or 0 for any.
|
||||
* @param modifiers The modifier mask, or 0x8000 for any.
|
||||
* @param ownerEvents Owner-events flag.
|
||||
* @param pointerSynchronous Are pointer events synchronous?
|
||||
* @param keyboardSynchronous Are keyboard events synchronous?
|
||||
*/
|
||||
public PassiveKeyGrab(Client grabClient, Window grabWindow, byte key, int modifiers, boolean ownerEvents, boolean pointerSynchronous, boolean keyboardSynchronous) {
|
||||
_grabClient = grabClient;
|
||||
_grabWindow = grabWindow;
|
||||
_key = key;
|
||||
_modifiers = modifiers;
|
||||
_ownerEvents = ownerEvents;
|
||||
_pointerSynchronous = pointerSynchronous;
|
||||
_keyboardSynchronous = keyboardSynchronous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the event trigger the passive grab?
|
||||
*
|
||||
* @param key The key that was pressed.
|
||||
* @param modifiers The current state of the modifiers.
|
||||
* @return True if the event matches.
|
||||
*/
|
||||
public boolean matchesEvent(int key, int modifiers) {
|
||||
if (_key != 0 && _key != key) return false;
|
||||
|
||||
if (_modifiers != 0x8000 && _modifiers != modifiers) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this match the parameters of the grab?
|
||||
*
|
||||
* @param key The key being grabbed, or 0 for any.
|
||||
* @param modifiers The modifier mask, or 0x8000 for any.
|
||||
* @return True if it matches the parameters.
|
||||
*/
|
||||
public boolean matchesGrab(int key, int modifiers) {
|
||||
if (key != 0 && _key != 0 && key != _key) return false;
|
||||
|
||||
if (modifiers != 0x8000 && _modifiers != 0x8000 && modifiers != _modifiers) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key code.
|
||||
*
|
||||
* @return The key code.
|
||||
*/
|
||||
public byte getKey() {
|
||||
return _key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the modifier mask.
|
||||
*
|
||||
* @return The modifier mask.
|
||||
*/
|
||||
public int getModifiers() {
|
||||
return _modifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the grab client.
|
||||
*
|
||||
* @return The grab client.
|
||||
*/
|
||||
public Client getGrabClient() {
|
||||
return _grabClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the grab window.
|
||||
*
|
||||
* @return The grab window.
|
||||
*/
|
||||
public Window getGrabWindow() {
|
||||
return _grabWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the owner-events flag.
|
||||
*
|
||||
* @return The owner-events flag.
|
||||
*/
|
||||
public boolean getOwnerEvents() {
|
||||
return _ownerEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether pointer events are synchronous.
|
||||
*
|
||||
* @return Whether pointer events are synchronous.
|
||||
*/
|
||||
public boolean getPointerSynchronous() {
|
||||
return _pointerSynchronous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether pointer events are synchronous.
|
||||
*
|
||||
* @return Whether pointer events are synchronous.
|
||||
*/
|
||||
public boolean getKeyboardSynchronous() {
|
||||
return _keyboardSynchronous;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,170 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* This class implements an X pixmap.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
public class Pixmap extends Resource {
|
||||
private final Drawable _drawable;
|
||||
public final ScreenView _screen;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param id The pixmap's ID.
|
||||
* @param xServer The X server.
|
||||
* @param client The client issuing the request.
|
||||
* @param screen The screen.
|
||||
* @param width The pixmap width.
|
||||
* @param height The pixmap height.
|
||||
* @param depth The pixmap depth.
|
||||
*/
|
||||
public Pixmap(int id, XServer xServer, Client client, ScreenView screen, int width, int height, int depth) {
|
||||
super(PIXMAP, id, xServer, client);
|
||||
|
||||
_drawable = new Drawable(width, height, depth, null, 0xff000000);
|
||||
_screen = screen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the pixmap's screen.
|
||||
*
|
||||
* @return The pixmap's screen.
|
||||
*/
|
||||
public ScreenView getScreen() {
|
||||
return _screen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the pixmap's drawable.
|
||||
*
|
||||
* @return The pixmap's drawable.
|
||||
*/
|
||||
public Drawable getDrawable() {
|
||||
return _drawable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the pixmap's depth.
|
||||
*
|
||||
* @return The pixmap's depth.
|
||||
*/
|
||||
public int getDepth() {
|
||||
return _drawable.getDepth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an X request relating to this pixmap.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @param opcode The request's opcode.
|
||||
* @param arg Optional first argument.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public void processRequest(Client client, byte opcode, byte arg, int bytesRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
switch (opcode) {
|
||||
case RequestCode.FreePixmap:
|
||||
if (bytesRemaining != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
_xServer.freeResource(_id);
|
||||
if (_client != null) _client.freeResource(this);
|
||||
_drawable.getBitmap().recycle();
|
||||
}
|
||||
break;
|
||||
case RequestCode.GetGeometry:
|
||||
if (bytesRemaining != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
writeGeometry(client);
|
||||
}
|
||||
break;
|
||||
case RequestCode.CopyArea:
|
||||
case RequestCode.CopyPlane:
|
||||
case RequestCode.PolyPoint:
|
||||
case RequestCode.PolyLine:
|
||||
case RequestCode.PolySegment:
|
||||
case RequestCode.PolyRectangle:
|
||||
case RequestCode.PolyArc:
|
||||
case RequestCode.FillPoly:
|
||||
case RequestCode.PolyFillRectangle:
|
||||
case RequestCode.PolyFillArc:
|
||||
case RequestCode.PutImage:
|
||||
case RequestCode.GetImage:
|
||||
case RequestCode.PolyText8:
|
||||
case RequestCode.PolyText16:
|
||||
case RequestCode.ImageText8:
|
||||
case RequestCode.ImageText16:
|
||||
case RequestCode.QueryBestSize:
|
||||
_drawable.processRequest(_xServer, client, _id, opcode, arg, bytesRemaining);
|
||||
return;
|
||||
default:
|
||||
io.readSkip(bytesRemaining);
|
||||
bytesRemaining = 0;
|
||||
ErrorCode.write(client, ErrorCode.Implementation, opcode, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write details of the pixmap's geometry in response to a GetGeometry
|
||||
* request.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @throws IOException
|
||||
*/
|
||||
private void writeGeometry(Client client) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, (byte) 32);
|
||||
io.writeInt(0); // Reply length.
|
||||
io.writeInt(_screen.getRootWindow().getId()); // Root window.
|
||||
io.writeShort((short) 0); // X.
|
||||
io.writeShort((short) 0); // Y.
|
||||
io.writeShort((short) _drawable.getWidth()); // Width.
|
||||
io.writeShort((short) _drawable.getHeight()); // Height.
|
||||
io.writeShort((short) 0); // Border width.
|
||||
io.writePadBytes(10); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a CreatePixmap request.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The client issuing the request.
|
||||
* @param id The ID of the pixmap to create.
|
||||
* @param depth The depth of the pixmap.
|
||||
* @param drawable The drawable whose depth it must match.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void processCreatePixmapRequest(XServer xServer, Client client, int id, int width, int height, int depth, Resource drawable) throws IOException {
|
||||
ScreenView screen;
|
||||
Pixmap p;
|
||||
|
||||
if (drawable.getType() == Resource.PIXMAP) screen = ((Pixmap) drawable).getScreen();
|
||||
else screen = ((Window) drawable).getScreen();
|
||||
|
||||
try {
|
||||
p = new Pixmap(id, xServer, client, screen, width, height, depth);
|
||||
} catch (OutOfMemoryError e) {
|
||||
ErrorCode.write(client, ErrorCode.Alloc, RequestCode.CreatePixmap, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
xServer.addResource(p);
|
||||
client.addResource(p);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,177 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
import android.graphics.Rect;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* This class handles an X pointer.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
public class Pointer {
|
||||
private byte[] _buttonMap = {1, 2, 3};
|
||||
|
||||
/**
|
||||
* Return the virtual button that a physical button has been mapped to.
|
||||
* Zero indicates the button has been disabled.
|
||||
*
|
||||
* @param button The physical button: 1, 2, or 3.
|
||||
* @return The virtual button, or 0 for disabled.
|
||||
*/
|
||||
public int mapButton(int button) {
|
||||
if (button < 1 || button > _buttonMap.length) return 0;
|
||||
|
||||
return _buttonMap[button - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a WarpPointer request.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The remote client.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void processWarpPointer(XServer xServer, Client client) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
int swin = io.readInt(); // Source window.
|
||||
int dwin = io.readInt(); // Destination window.
|
||||
int sx = io.readShort(); // Source X.
|
||||
int sy = io.readShort(); // Source Y.
|
||||
int width = io.readShort(); // Source width.
|
||||
int height = io.readShort(); // Source height.
|
||||
int dx = io.readShort(); // Destination X.
|
||||
int dy = io.readShort(); // Destination Y.
|
||||
ScreenView screen = xServer.getScreen();
|
||||
boolean ok = true;
|
||||
int x, y;
|
||||
|
||||
if (dwin == 0) {
|
||||
x = screen.getPointerX() + dx;
|
||||
y = screen.getPointerY() + dy;
|
||||
} else {
|
||||
Resource r = xServer.getResource(dwin);
|
||||
|
||||
if (r == null || r.getType() != Resource.WINDOW) {
|
||||
ErrorCode.write(client, ErrorCode.Window, RequestCode.WarpPointer, dwin);
|
||||
ok = false;
|
||||
}
|
||||
|
||||
Rect rect = ((Window) r).getIRect();
|
||||
|
||||
x = rect.left + dx;
|
||||
y = rect.top + dy;
|
||||
}
|
||||
|
||||
if (swin != 0) {
|
||||
Resource r = xServer.getResource(swin);
|
||||
|
||||
if (r == null || r.getType() != Resource.WINDOW) {
|
||||
ErrorCode.write(client, ErrorCode.Window, RequestCode.WarpPointer, swin);
|
||||
ok = false;
|
||||
} else {
|
||||
Window w = (Window) r;
|
||||
Rect rect = w.getIRect();
|
||||
|
||||
sx += rect.left;
|
||||
sy += rect.top;
|
||||
|
||||
if (width == 0) width = rect.right - sx;
|
||||
if (height == 0) height = rect.bottom - sy;
|
||||
|
||||
if (x < sx || x >= sx + width || y < sy || y >= sy + height) ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) screen.updatePointerPosition(x, y, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an X request relating to the pointers.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The remote client.
|
||||
* @param opcode The request's opcode.
|
||||
* @param arg Optional first argument.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void processRequest(XServer xServer, Client client, byte opcode, byte arg, int bytesRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
switch (opcode) {
|
||||
case RequestCode.WarpPointer:
|
||||
if (bytesRemaining != 20) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
processWarpPointer(xServer, client);
|
||||
}
|
||||
break;
|
||||
case RequestCode.ChangePointerControl:
|
||||
if (bytesRemaining != 8) ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
io.readSkip(bytesRemaining);
|
||||
break; // Do nothing.
|
||||
case RequestCode.GetPointerControl:
|
||||
if (bytesRemaining != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, (byte) 0);
|
||||
io.writeInt(0); // Reply length.
|
||||
io.writeShort((short) 1); // Acceleration numerator.
|
||||
io.writeShort((short) 1); // Acceleration denom.
|
||||
io.writeShort((short) 1); // Threshold.
|
||||
io.writePadBytes(18); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
break;
|
||||
case RequestCode.SetPointerMapping:
|
||||
if (bytesRemaining != arg + (-arg & 3)) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else if (arg != _buttonMap.length) {
|
||||
ErrorCode.write(client, ErrorCode.Value, opcode, 0);
|
||||
} else {
|
||||
io.readBytes(_buttonMap, 0, arg);
|
||||
io.readSkip(-arg & 3); // Unused.
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, (byte) 0);
|
||||
io.writeInt(0); // Reply length.
|
||||
io.writePadBytes(24); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
|
||||
xServer.sendMappingNotify(2, 0, 0);
|
||||
}
|
||||
break;
|
||||
case RequestCode.GetPointerMapping:
|
||||
if (bytesRemaining != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
byte n = (byte) _buttonMap.length;
|
||||
int pad = -n & 3;
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, n);
|
||||
io.writeInt((n + pad) / 4); // Reply length.
|
||||
io.writePadBytes(24); // Unused.
|
||||
|
||||
io.writeBytes(_buttonMap, 0, n); // Map.
|
||||
io.writePadBytes(pad); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Implementation, opcode, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,435 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* This class implements a property.
|
||||
*
|
||||
* @author Matthew KWan
|
||||
*/
|
||||
public class Property {
|
||||
|
||||
/**
|
||||
* Listener Object which allows listening on property change events.
|
||||
*/
|
||||
static public abstract class OnPropertyChangedListener {
|
||||
/**
|
||||
* @param data New data after the ChangeProperty request
|
||||
* @param type Atom describing the property data (i.e. "UTF8_STRING")
|
||||
*/
|
||||
public abstract void onPropertyChanged(byte[] data, Atom type);
|
||||
}
|
||||
|
||||
private final int _id;
|
||||
private int _type;
|
||||
private byte _format;
|
||||
private byte[] _data = null;
|
||||
private OnPropertyChangedListener _onPropertyChange = null;
|
||||
|
||||
/**
|
||||
* Allows setting a callback object to listen on property change events.
|
||||
* @param l Callback object to listen on property change events
|
||||
*/
|
||||
public void setOnPropertyChangedListener(OnPropertyChangedListener l){
|
||||
_onPropertyChange = l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param id The property's ID.
|
||||
* @param type The ID of the property's type atom.
|
||||
* @param format Data format = 8, 16, or 32.
|
||||
*/
|
||||
public Property(int id, int type, byte format) {
|
||||
_id = id;
|
||||
_type = type;
|
||||
_format = format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param p The property to copy.
|
||||
*/
|
||||
private Property(final Property p) {
|
||||
_id = p._id;
|
||||
_type = p._type;
|
||||
_format = p._format;
|
||||
_data = p._data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the property's atom ID.
|
||||
*
|
||||
* @return The property's atom ID.
|
||||
*/
|
||||
public int getId() {
|
||||
return _id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows setting the data held by this property.
|
||||
* @param d Data to set.
|
||||
*/
|
||||
public void setData(byte[] d) {
|
||||
_data = d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Data held by this property
|
||||
*/
|
||||
public byte[] getData() {
|
||||
return _data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows setting the type of this property.
|
||||
* @param id Atom ID of atom describing this property type.
|
||||
*/
|
||||
public void setType(int id) {
|
||||
_type = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows setting the data held by this property.
|
||||
* @param d Data to set as string, will be converted to bytes.
|
||||
*/
|
||||
public void setData(String d) {
|
||||
_data = d.getBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an X request relating to properties.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The remote client.
|
||||
* @param arg Optional first argument.
|
||||
* @param opcode The request's opcode.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @param w The window containing the properties.
|
||||
* @param properties Hash table of the window's properties.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void processRequest(XServer xServer, Client client, byte arg, byte opcode, int bytesRemaining, Window w, Hashtable<Integer, Property> properties) throws IOException {
|
||||
switch (opcode) {
|
||||
case RequestCode.ChangeProperty:
|
||||
processChangePropertyRequest(xServer, client, arg, bytesRemaining, w, properties);
|
||||
break;
|
||||
case RequestCode.GetProperty:
|
||||
processGetPropertyRequest(xServer, client, arg == 1, bytesRemaining, w, properties);
|
||||
break;
|
||||
case RequestCode.RotateProperties:
|
||||
processRotatePropertiesRequest(xServer, client, bytesRemaining, w, properties);
|
||||
break;
|
||||
default:
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Implementation, opcode, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a ChangeProperty request.
|
||||
* Change the owner of the specified selection.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The remote client.
|
||||
* @param mode 0=Replace 1=Prepend 2=Append.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @param w The window containing the properties.
|
||||
* @param properties Hash table of the window's properties.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void processChangePropertyRequest(XServer xServer, Client client, byte mode, int bytesRemaining, Window w, Hashtable<Integer, Property> properties) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
if (bytesRemaining < 16) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, RequestCode.ChangeProperty, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
int pid = io.readInt(); // Property atom.
|
||||
int tid = io.readInt(); // Type atom.
|
||||
byte format = (byte) io.readByte(); // Format.
|
||||
|
||||
io.readSkip(3); // Unused.
|
||||
|
||||
int length = io.readInt(); // Length of data.
|
||||
int n, pad;
|
||||
|
||||
if (format == 8) n = length;
|
||||
else if (format == 16) n = length * 2;
|
||||
else n = length * 4;
|
||||
|
||||
pad = -n & 3;
|
||||
|
||||
bytesRemaining -= 16;
|
||||
if (bytesRemaining != n + pad) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, RequestCode.ChangeProperty, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] data = new byte[n];
|
||||
|
||||
io.readBytes(data, 0, n);
|
||||
io.readSkip(pad); // Unused.
|
||||
|
||||
Atom property = xServer.getAtom(pid);
|
||||
|
||||
if (property == null) {
|
||||
ErrorCode.write(client, ErrorCode.Atom, RequestCode.ChangeProperty, pid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!xServer.atomExists(tid)) {
|
||||
ErrorCode.write(client, ErrorCode.Atom, RequestCode.ChangeProperty, tid);
|
||||
return;
|
||||
}
|
||||
|
||||
Property p;
|
||||
|
||||
if (properties.containsKey(pid)) {
|
||||
p = properties.get(pid);
|
||||
} else {
|
||||
p = new Property(pid, tid, format);
|
||||
properties.put(pid, p);
|
||||
}
|
||||
|
||||
if (mode == 0) { // Replace.
|
||||
p._type = tid;
|
||||
p._format = format;
|
||||
p._data = data;
|
||||
} else {
|
||||
if (tid != p._type || format != p._format) {
|
||||
ErrorCode.write(client, ErrorCode.Match, RequestCode.ChangeProperty, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (p._data == null) {
|
||||
p._data = data;
|
||||
} else {
|
||||
byte[] d1, d2;
|
||||
|
||||
if (mode == 1) { // Prepend.
|
||||
d1 = data;
|
||||
d2 = p._data;
|
||||
} else { // Append.
|
||||
d1 = p._data;
|
||||
d2 = data;
|
||||
}
|
||||
|
||||
p._data = new byte[d1.length + d2.length];
|
||||
System.arraycopy(d1, 0, p._data, 0, d1.length);
|
||||
System.arraycopy(d2, 0, p._data, d1.length, d2.length);
|
||||
}
|
||||
}
|
||||
|
||||
Vector<Client> sc;
|
||||
|
||||
if ((sc = w.getSelectingClients(EventCode.MaskPropertyChange)) != null) {
|
||||
for (Client c : sc) {
|
||||
if (c == null) continue;
|
||||
EventCode.sendPropertyNotify(c, w, property, xServer.getTimestamp(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// trigger callback for event change if existent
|
||||
if(p._onPropertyChange != null) p._onPropertyChange.onPropertyChanged(p._data, xServer.getAtom(tid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a GetProperty request.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The remote client.
|
||||
* @param delete Delete flag.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @param w The window containing the properties.
|
||||
* @param properties Hash table of the window's properties.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void processGetPropertyRequest(XServer xServer, Client client, boolean delete, int bytesRemaining, Window w, Hashtable<Integer, Property> properties) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
if (bytesRemaining != 16) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, RequestCode.GetProperty, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
int pid = io.readInt(); // Property.
|
||||
int tid = io.readInt(); // Type.
|
||||
int longOffset = io.readInt(); // Long offset.
|
||||
int longLength = io.readInt(); // Long length.
|
||||
Atom property = xServer.getAtom(pid);
|
||||
|
||||
if (property == null) {
|
||||
ErrorCode.write(client, ErrorCode.Atom, RequestCode.GetProperty, pid);
|
||||
return;
|
||||
} else if (tid != 0 && !xServer.atomExists(tid)) {
|
||||
ErrorCode.write(client, ErrorCode.Atom, RequestCode.GetProperty, tid);
|
||||
return;
|
||||
}
|
||||
|
||||
byte format = 0;
|
||||
int bytesAfter = 0;
|
||||
byte[] value = null;
|
||||
boolean generateNotify = false;
|
||||
|
||||
if (properties.containsKey(pid)) {
|
||||
Property p = properties.get(pid);
|
||||
|
||||
tid = p._type;
|
||||
format = p._format;
|
||||
|
||||
if (tid != 0 && tid != p._type) {
|
||||
bytesAfter = (p._data == null) ? 0 : p._data.length;
|
||||
} else {
|
||||
int n, i, t, l;
|
||||
|
||||
n = (p._data == null) ? 0 : p._data.length;
|
||||
i = 4 * longOffset;
|
||||
t = n - i;
|
||||
|
||||
if (longLength < 0 || longLength > 536870911)
|
||||
longLength = 536870911; // Prevent overflow.
|
||||
|
||||
if (t < longLength * 4) l = t;
|
||||
else l = longLength * 4;
|
||||
|
||||
bytesAfter = n - (i + l);
|
||||
|
||||
if (l < 0) {
|
||||
ErrorCode.write(client, ErrorCode.Value, RequestCode.GetProperty, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (l > 0) {
|
||||
value = new byte[l];
|
||||
System.arraycopy(p._data, i, value, 0, l);
|
||||
}
|
||||
|
||||
if (delete && bytesAfter == 0) {
|
||||
properties.remove(pid);
|
||||
generateNotify = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tid = 0;
|
||||
}
|
||||
|
||||
int length = (value == null) ? 0 : value.length;
|
||||
int pad = -length & 3;
|
||||
int valueLength;
|
||||
|
||||
if (format == 8) valueLength = length;
|
||||
else if (format == 16) valueLength = length / 2;
|
||||
else if (format == 32) valueLength = length / 4;
|
||||
else valueLength = 0;
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, format);
|
||||
io.writeInt((length + pad) / 4); // Reply length.
|
||||
io.writeInt(tid); // Type.
|
||||
io.writeInt(bytesAfter); // Bytes after.
|
||||
io.writeInt(valueLength); // Value length.
|
||||
io.writePadBytes(12); // Unused.
|
||||
|
||||
if (value != null) {
|
||||
io.writeBytes(value, 0, value.length); // Value.
|
||||
io.writePadBytes(pad); // Unused.
|
||||
}
|
||||
}
|
||||
io.flush();
|
||||
|
||||
if (generateNotify) {
|
||||
Vector<Client> sc;
|
||||
|
||||
if ((sc = w.getSelectingClients(EventCode.MaskPropertyChange)) != null) {
|
||||
for (Client c : sc) {
|
||||
if (c == null) continue;
|
||||
EventCode.sendPropertyNotify(c, w, property, xServer.getTimestamp(), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a RotateProperties request.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The remote client.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @param w The window containing the properties.
|
||||
* @param properties Hash table of the window's properties.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void processRotatePropertiesRequest(XServer xServer, Client client, int bytesRemaining, Window w, Hashtable<Integer, Property> properties) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
if (bytesRemaining < 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, RequestCode.RotateProperties, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
int n = io.readShort(); // Num properties.
|
||||
int delta = io.readShort(); // Delta.
|
||||
|
||||
bytesRemaining -= 4;
|
||||
if (bytesRemaining != n * 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, RequestCode.RotateProperties, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (n == 0 || (delta % n) == 0) return;
|
||||
|
||||
int[] aids = new int[n];
|
||||
Property[] props = new Property[n];
|
||||
Property[] pcopy = new Property[n];
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
aids[i] = io.readInt();
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (!xServer.atomExists(aids[i])) {
|
||||
ErrorCode.write(client, ErrorCode.Atom, RequestCode.RotateProperties, aids[i]);
|
||||
return;
|
||||
} else if (!properties.containsKey(aids[i])) {
|
||||
ErrorCode.write(client, ErrorCode.Match, RequestCode.RotateProperties, aids[i]);
|
||||
return;
|
||||
} else {
|
||||
props[i] = properties.get(aids[i]);
|
||||
pcopy[i] = new Property(props[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
Property p = props[i];
|
||||
Property pc = pcopy[(i + delta) % n];
|
||||
|
||||
p._type = pc._type;
|
||||
p._format = pc._format;
|
||||
p._data = pc._data;
|
||||
}
|
||||
|
||||
Vector<Client> sc;
|
||||
|
||||
if ((sc = w.getSelectingClients(EventCode.MaskPropertyChange)) != null) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (Client c : sc) {
|
||||
if (c == null) continue;
|
||||
EventCode.sendPropertyNotify(c, w, xServer.getAtom(aids[i]), xServer.getTimestamp(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,168 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
import android.util.SparseArray;
|
||||
|
||||
/**
|
||||
* All the X request codes.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
public class RequestCode {
|
||||
|
||||
public static class AllowEventsMode {
|
||||
|
||||
public static final int AsyncPointer = 0;
|
||||
public static final int SyncPointer = 1;
|
||||
public static final int ReplayPointer = 2;
|
||||
public static final int AsyncKeyboard = 3;
|
||||
public static final int SyncKeyboard = 4;
|
||||
public static final int ReplayKeyboard = 5;
|
||||
public static final int AsyncBoth = 6;
|
||||
public static final int SyncBoth = 7;
|
||||
|
||||
private static SparseArray<String> mNames = new SparseArray<String>();
|
||||
|
||||
public static String toString(int mode) {
|
||||
String name = mNames.get(mode);
|
||||
return name != null ? name : "unknown mode";
|
||||
}
|
||||
|
||||
private static void initialize() {
|
||||
mNames.put(AsyncPointer, "AsyncPointer");
|
||||
mNames.put(SyncPointer, "SyncPointer");
|
||||
mNames.put(ReplayPointer, "ReplayPointer");
|
||||
mNames.put(AsyncKeyboard, "AsyncKeyboard");
|
||||
mNames.put(SyncKeyboard, "SyncKeyboard");
|
||||
mNames.put(ReplayKeyboard, "ReplayKeyboard");
|
||||
mNames.put(AsyncBoth, "AsyncBoth");
|
||||
mNames.put(SyncBoth, "SyncBoth");
|
||||
}
|
||||
}
|
||||
|
||||
public static final byte CreateWindow = 1;
|
||||
public static final byte ChangeWindowAttributes = 2;
|
||||
public static final byte GetWindowAttributes = 3;
|
||||
public static final byte DestroyWindow = 4;
|
||||
public static final byte DestroySubwindows = 5;
|
||||
public static final byte ChangeSaveSet = 6;
|
||||
public static final byte ReparentWindow = 7;
|
||||
public static final byte MapWindow = 8;
|
||||
public static final byte MapSubwindows = 9;
|
||||
public static final byte UnmapWindow = 10;
|
||||
public static final byte UnmapSubwindows = 11;
|
||||
public static final byte ConfigureWindow = 12;
|
||||
public static final byte CirculateWindow = 13;
|
||||
public static final byte GetGeometry = 14;
|
||||
public static final byte QueryTree = 15;
|
||||
public static final byte InternAtom = 16;
|
||||
public static final byte GetAtomName = 17;
|
||||
public static final byte ChangeProperty = 18;
|
||||
public static final byte DeleteProperty = 19;
|
||||
public static final byte GetProperty = 20;
|
||||
public static final byte ListProperties = 21;
|
||||
public static final byte SetSelectionOwner = 22;
|
||||
public static final byte GetSelectionOwner = 23;
|
||||
public static final byte ConvertSelection = 24;
|
||||
public static final byte SendEvent = 25;
|
||||
public static final byte GrabPointer = 26;
|
||||
public static final byte UngrabPointer = 27;
|
||||
public static final byte GrabButton = 28;
|
||||
public static final byte UngrabButton = 29;
|
||||
public static final byte ChangeActivePointerGrab = 30;
|
||||
public static final byte GrabKeyboard = 31;
|
||||
public static final byte UngrabKeyboard = 32;
|
||||
public static final byte GrabKey = 33;
|
||||
public static final byte UngrabKey = 34;
|
||||
public static final byte AllowEvents = 35;
|
||||
public static final byte GrabServer = 36;
|
||||
public static final byte UngrabServer = 37;
|
||||
public static final byte QueryPointer = 38;
|
||||
public static final byte GetMotionEvents = 39;
|
||||
public static final byte TranslateCoordinates = 40;
|
||||
public static final byte WarpPointer = 41;
|
||||
public static final byte SetInputFocus = 42;
|
||||
public static final byte GetInputFocus = 43;
|
||||
public static final byte QueryKeymap = 44;
|
||||
public static final byte OpenFont = 45;
|
||||
public static final byte CloseFont = 46;
|
||||
public static final byte QueryFont = 47;
|
||||
public static final byte QueryTextExtents = 48;
|
||||
public static final byte ListFonts = 49;
|
||||
public static final byte ListFontsWithInfo = 50;
|
||||
public static final byte SetFontPath = 51;
|
||||
public static final byte GetFontPath = 52;
|
||||
public static final byte CreatePixmap = 53;
|
||||
public static final byte FreePixmap = 54;
|
||||
public static final byte CreateGC = 55;
|
||||
public static final byte ChangeGC = 56;
|
||||
public static final byte CopyGC = 57;
|
||||
public static final byte SetDashes = 58;
|
||||
public static final byte SetClipRectangles = 59;
|
||||
public static final byte FreeGC = 60;
|
||||
public static final byte ClearArea = 61;
|
||||
public static final byte CopyArea = 62;
|
||||
public static final byte CopyPlane = 63;
|
||||
public static final byte PolyPoint = 64;
|
||||
public static final byte PolyLine = 65;
|
||||
public static final byte PolySegment = 66;
|
||||
public static final byte PolyRectangle = 67;
|
||||
public static final byte PolyArc = 68;
|
||||
public static final byte FillPoly = 69;
|
||||
public static final byte PolyFillRectangle = 70;
|
||||
public static final byte PolyFillArc = 71;
|
||||
public static final byte PutImage = 72;
|
||||
public static final byte GetImage = 73;
|
||||
public static final byte PolyText8 = 74;
|
||||
public static final byte PolyText16 = 75;
|
||||
public static final byte ImageText8 = 76;
|
||||
public static final byte ImageText16 = 77;
|
||||
public static final byte CreateColormap = 78;
|
||||
public static final byte FreeColormap = 79;
|
||||
public static final byte CopyColormapAndFree = 80;
|
||||
public static final byte InstallColormap = 81;
|
||||
public static final byte UninstallColormap = 82;
|
||||
public static final byte ListInstalledColormaps = 83;
|
||||
public static final byte AllocColor = 84;
|
||||
public static final byte AllocNamedColor = 85;
|
||||
public static final byte AllocColorCells = 86;
|
||||
public static final byte AllocColorPlanes = 87;
|
||||
public static final byte FreeColors = 88;
|
||||
public static final byte StoreColors = 89;
|
||||
public static final byte StoreNamedColor = 90;
|
||||
public static final byte QueryColors = 91;
|
||||
public static final byte LookupColor = 92;
|
||||
public static final byte CreateCursor = 93;
|
||||
public static final byte CreateGlyphCursor = 94;
|
||||
public static final byte FreeCursor = 95;
|
||||
public static final byte RecolorCursor = 96;
|
||||
public static final byte QueryBestSize = 97;
|
||||
public static final byte QueryExtension = 98;
|
||||
public static final byte ListExtensions = 99;
|
||||
public static final byte ChangeKeyboardMapping = 100;
|
||||
public static final byte GetKeyboardMapping = 101;
|
||||
public static final byte ChangeKeyboardControl = 102;
|
||||
public static final byte GetKeyboardControl = 103;
|
||||
public static final byte Bell = 104;
|
||||
public static final byte ChangePointerControl = 105;
|
||||
public static final byte GetPointerControl = 106;
|
||||
public static final byte SetScreenSaver = 107;
|
||||
public static final byte GetScreenSaver = 108;
|
||||
public static final byte ChangeHosts = 109;
|
||||
public static final byte ListHosts = 110;
|
||||
public static final byte SetAccessControl = 111;
|
||||
public static final byte SetCloseDownMode = 112;
|
||||
public static final byte KillClient = 113;
|
||||
public static final byte RotateProperties = 114;
|
||||
public static final byte ForceScreenSaver = 115;
|
||||
public static final byte SetPointerMapping = 116;
|
||||
public static final byte GetPointerMapping = 117;
|
||||
public static final byte SetModifierMapping = 118;
|
||||
public static final byte GetModifierMapping = 119;
|
||||
public static final byte NoOperation = 127;
|
||||
public static final byte ExtensionStart = -128;
|
||||
public static final byte ExtensionEnd = -1;
|
||||
|
||||
static {
|
||||
AllowEventsMode.initialize();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This class handles details of a server resource.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
public class Resource {
|
||||
public static final int WINDOW = 1;
|
||||
public static final int PIXMAP = 2;
|
||||
public static final int CURSOR = 3;
|
||||
public static final int FONT = 4;
|
||||
public static final int GCONTEXT = 5;
|
||||
public static final int COLORMAP = 6;
|
||||
|
||||
private final int _type;
|
||||
protected final int _id;
|
||||
protected final XServer _xServer;
|
||||
protected Client _client;
|
||||
private int _closeDownMode = Client.Destroy;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param type The resource type.
|
||||
* @param id The resource ID.
|
||||
* @param xServer The X server.
|
||||
* @param client The client issuing the request.
|
||||
*/
|
||||
protected Resource(int type, int id, XServer xServer, Client client) {
|
||||
_type = type;
|
||||
_id = id;
|
||||
_xServer = xServer;
|
||||
_client = client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the resource type.
|
||||
*
|
||||
* @return The resource type.
|
||||
*/
|
||||
public int getType() {
|
||||
return _type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the resource ID.
|
||||
*
|
||||
* @return The resource ID.
|
||||
*/
|
||||
public int getId() {
|
||||
return _id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the client that created the resource.
|
||||
*
|
||||
* @return The client that created the resource.
|
||||
*/
|
||||
public Client getClient() {
|
||||
return _client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the resource's close down mode.
|
||||
*
|
||||
* @return The resource's close down mode.
|
||||
*/
|
||||
public int getCloseDownMode() {
|
||||
return _closeDownMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the close down mode of the resource.
|
||||
*
|
||||
* @param mode The mode used to destroy the resource.
|
||||
*/
|
||||
public void setCloseDownMode(int mode) {
|
||||
_closeDownMode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the resource a drawable? (Window or Pixmap)
|
||||
*
|
||||
* @return Whether the resource is a drawable.
|
||||
*/
|
||||
public boolean isDrawable() {
|
||||
return (_type == WINDOW || _type == PIXMAP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the resource a fontable? (Font or GContext)
|
||||
*
|
||||
* @return Whether the resource is a fontable.
|
||||
*/
|
||||
public boolean isFontable() {
|
||||
return (_type == FONT || _type == GCONTEXT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the resource.
|
||||
* Remove it from the X server's resources, and override this function
|
||||
* to handle object-specific removals.
|
||||
*/
|
||||
public void delete() {
|
||||
_xServer.freeResource(_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an X request relating to this resource.
|
||||
* This is a fallback function that does nothing.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @param opcode The request's opcode.
|
||||
* @param arg Optional first argument.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void processRequest(Client client, byte opcode, byte arg, int bytesRemaining) throws IOException {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,297 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* This class implements a selection.
|
||||
*
|
||||
* @author Matthew KWan
|
||||
*/
|
||||
public class Selection {
|
||||
private final int _id;
|
||||
private Client _owner = null;
|
||||
private Window _ownerWindow = null;
|
||||
private int _lastChangeTime = 0;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param id The selection's ID.
|
||||
*/
|
||||
public Selection(int id) {
|
||||
_id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the selection's atom ID.
|
||||
*
|
||||
* @return The selection's atom ID.
|
||||
*/
|
||||
public int getId() {
|
||||
return _id;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the selection is owned by the client, clear it.
|
||||
* This occurs when a client disconnects.
|
||||
*
|
||||
* @param client The disconnecting client.
|
||||
*/
|
||||
public void clearClient(Client client) {
|
||||
if (_owner == client) {
|
||||
_owner = null;
|
||||
_ownerWindow = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows transfering a selection to a (window-)property.
|
||||
* @param xServer xServer to perform this action on.
|
||||
* @param fromSelectionAtom Atom associated with the source selection. (i.e. "CLIPBOARD")
|
||||
* @param toPropertyAtom Atom associated with the target property (i.e. "CLIPBOARD" of target window).
|
||||
* @param toWindow Window holding toPropertyAtom. (the propertyAtom can be reused, so we need to specify the window which holds the property this atom is associated with)
|
||||
* @param transferTypeAtom Atom describing the clipboard type (i.e. "UTF8_STRING").
|
||||
*/
|
||||
public static void transferSelectionRequest(XServer xServer, Atom fromSelectionAtom, Atom toPropertyAtom, Window toWindow, Atom transferTypeAtom){
|
||||
Selection srcSelection = xServer.getSelection(fromSelectionAtom.getId());
|
||||
if(srcSelection == null)
|
||||
return; // selection not owned by any client
|
||||
try{
|
||||
if(srcSelection._owner != null) {
|
||||
EventCode.sendSelectionRequest(srcSelection._owner, xServer.getTimestamp(), srcSelection._ownerWindow, toWindow, fromSelectionAtom, transferTypeAtom, toPropertyAtom);
|
||||
}
|
||||
}
|
||||
catch (IOException e){
|
||||
// silently fail
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows changing/setting the owner of a selection.
|
||||
* @param xServer xServer to perform this action on.
|
||||
* @param selectionAtom Atom associated with the selection. (i.e. "CLIPBOARD")
|
||||
* @param owner New owner window.
|
||||
*/
|
||||
public static void setSelectionOwner(XServer xServer, Atom selectionAtom, Window owner) {
|
||||
Selection sel = xServer.getSelection(selectionAtom.getId());
|
||||
|
||||
if (sel == null) {
|
||||
sel = new Selection(selectionAtom.getId());
|
||||
xServer.addSelection(sel);
|
||||
}
|
||||
|
||||
// remove old owner
|
||||
if (sel._owner != null && sel._owner != owner.getClient()){
|
||||
try{
|
||||
EventCode.sendSelectionClear(sel._owner, sel._lastChangeTime, owner, selectionAtom);
|
||||
}
|
||||
catch (IOException e){
|
||||
// silently fail
|
||||
}
|
||||
}
|
||||
sel._lastChangeTime = xServer.getTimestamp();
|
||||
sel._ownerWindow = owner;
|
||||
sel._owner = owner.getClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an X request relating to selections.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The client issuing the request.
|
||||
* @param opcode The request's opcode.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void processRequest(XServer xServer, Client client, byte opcode, int bytesRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
switch (opcode) {
|
||||
case RequestCode.SetSelectionOwner:
|
||||
processSetSelectionOwnerRequest(xServer, client, bytesRemaining);
|
||||
break;
|
||||
case RequestCode.GetSelectionOwner:
|
||||
if (bytesRemaining != 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int aid = io.readInt(); // Selection atom.
|
||||
|
||||
if (!xServer.atomExists(aid)) {
|
||||
ErrorCode.write(client, ErrorCode.Atom, RequestCode.SetSelectionOwner, aid);
|
||||
} else {
|
||||
int wid = 0;
|
||||
Selection sel = xServer.getSelection(aid);
|
||||
|
||||
if (sel != null && sel._ownerWindow != null) wid = sel._ownerWindow.getId();
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, (byte) 0);
|
||||
io.writeInt(0); // Reply length.
|
||||
io.writeInt(wid); // Owner.
|
||||
io.writePadBytes(20); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RequestCode.ConvertSelection:
|
||||
processConvertSelectionRequest(xServer, client, bytesRemaining);
|
||||
break;
|
||||
default:
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Implementation, opcode, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a SetSelectionOwner request.
|
||||
* Change the owner of the specified selection.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The client issuing the request.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void processSetSelectionOwnerRequest(XServer xServer, Client client, int bytesRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
if (bytesRemaining != 12) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, RequestCode.SetSelectionOwner, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
int wid = io.readInt(); // Owner window.
|
||||
int aid = io.readInt(); // Selection atom.
|
||||
int time = io.readInt(); // Timestamp.
|
||||
Window w = null;
|
||||
|
||||
if (wid != 0) {
|
||||
Resource r = xServer.getResource(wid);
|
||||
|
||||
if (r == null || r.getType() != Resource.WINDOW) {
|
||||
ErrorCode.write(client, ErrorCode.Window, RequestCode.SetSelectionOwner, wid);
|
||||
return;
|
||||
}
|
||||
|
||||
w = (Window) r;
|
||||
}
|
||||
|
||||
Atom a = xServer.getAtom(aid);
|
||||
|
||||
if (a == null) {
|
||||
ErrorCode.write(client, ErrorCode.Atom, RequestCode.SetSelectionOwner, aid);
|
||||
return;
|
||||
}
|
||||
|
||||
Selection sel = xServer.getSelection(aid);
|
||||
|
||||
if (sel == null) {
|
||||
sel = new Selection(aid);
|
||||
xServer.addSelection(sel);
|
||||
}
|
||||
|
||||
int now = xServer.getTimestamp();
|
||||
|
||||
if (time != 0) {
|
||||
if (time < sel._lastChangeTime || time >= now) return;
|
||||
} else {
|
||||
time = now;
|
||||
}
|
||||
|
||||
sel._lastChangeTime = time;
|
||||
sel._ownerWindow = w;
|
||||
|
||||
if (sel._owner != null && sel._owner != client)
|
||||
EventCode.sendSelectionClear(sel._owner, time, w, a);
|
||||
|
||||
sel._owner = (w != null) ? client : null;
|
||||
if (xServer.getScreen().hasSharedClipboard() && aid == xServer.findAtom("CLIPBOARD").getId())
|
||||
transferSelectionRequest(xServer, xServer.findAtom("CLIPBOARD"), xServer.findAtom("CLIPBOARD"), xServer.getScreen().getSharedClipboardWindow() , xServer.findAtom("UTF8_STRING"));
|
||||
else if (xServer.getScreen().hasSharedClipboard() && aid == xServer.findAtom("PRIMARY").getId())
|
||||
transferSelectionRequest(xServer, xServer.findAtom("PRIMARY"), xServer.findAtom("PRIMARY"), xServer.getScreen().getSharedClipboardWindow() , xServer.findAtom("UTF8_STRING"));
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a ConvertSelection request.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The client issuing the request.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void processConvertSelectionRequest(XServer xServer, Client client, int bytesRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
if (bytesRemaining != 20) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, RequestCode.ConvertSelection, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
int wid = io.readInt(); // Requestor.
|
||||
int sid = io.readInt(); // Selection.
|
||||
int tid = io.readInt(); // Target.
|
||||
int pid = io.readInt(); // Property.
|
||||
int time = io.readInt(); // Time.
|
||||
Resource r = xServer.getResource(wid);
|
||||
Window w;
|
||||
Atom selectionAtom, targetAtom, propertyAtom;
|
||||
|
||||
if (r == null || r.getType() != Resource.WINDOW) {
|
||||
ErrorCode.write(client, ErrorCode.Window, RequestCode.ConvertSelection, wid);
|
||||
return;
|
||||
} else {
|
||||
w = (Window) r;
|
||||
}
|
||||
|
||||
selectionAtom = xServer.getAtom(sid);
|
||||
if (selectionAtom == null) {
|
||||
ErrorCode.write(client, ErrorCode.Atom, RequestCode.ConvertSelection, sid);
|
||||
return;
|
||||
}
|
||||
|
||||
targetAtom = xServer.getAtom(tid);
|
||||
if (targetAtom == null) {
|
||||
ErrorCode.write(client, ErrorCode.Atom, RequestCode.ConvertSelection, tid);
|
||||
return;
|
||||
}
|
||||
|
||||
propertyAtom = null;
|
||||
if (pid != 0 && (propertyAtom = xServer.getAtom(pid)) == null) {
|
||||
ErrorCode.write(client, ErrorCode.Atom, RequestCode.ConvertSelection, pid);
|
||||
return;
|
||||
}
|
||||
|
||||
Client owner = null;
|
||||
Selection sel = xServer.getSelection(sid);
|
||||
|
||||
if (sel != null) owner = sel._owner;
|
||||
|
||||
Window ownerWindow = sel != null ? sel._ownerWindow : null;
|
||||
// move the property to the client the dirty way,... some interfaces would make sense here
|
||||
if(ownerWindow != null && ownerWindow.isServerWindow() && owner == null){
|
||||
Property target = w.getProperty(pid);
|
||||
if(target == null){
|
||||
target = new Property(pid, tid, (byte)8);
|
||||
w.addProperty(target);
|
||||
}
|
||||
target.setData(sel._ownerWindow.getProperty(sid).getData());
|
||||
target.setType(tid);
|
||||
}
|
||||
|
||||
if (owner != null) {
|
||||
try {
|
||||
EventCode.sendSelectionRequest(owner, time, sel._ownerWindow, w, selectionAtom, targetAtom, propertyAtom);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
} else {
|
||||
EventCode.sendSelectionNotify(client, time, w, selectionAtom, targetAtom, propertyAtom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Utility functions.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
public class Util {
|
||||
/**
|
||||
* Count the number of bits in an integer.
|
||||
*
|
||||
* @param n The integer containing the bits.
|
||||
* @return The number of bits in the integer.
|
||||
*/
|
||||
public static int bitcount(int n) {
|
||||
int c = 0;
|
||||
|
||||
while (n != 0) {
|
||||
c += n & 1;
|
||||
n >>= 1;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the header of a reply.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @param arg Optional argument.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void writeReplyHeader(Client client, byte arg) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
short sn = (short) (client.getSequenceNumber() & 0xffff);
|
||||
|
||||
io.writeByte((byte) 1); // Reply.
|
||||
io.writeByte(arg);
|
||||
io.writeShort(sn);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* An X visual. This is always 32-bit TrueColor.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
public class Visual {
|
||||
public final static byte BackingStoreNever = 0;
|
||||
public final static byte BackingStoreWhenMapped = 1;
|
||||
public final static byte BackingStoreAlways = 2;
|
||||
|
||||
public final static byte StaticGray = 0;
|
||||
public final static byte GrayScale = 1;
|
||||
public final static byte StaticColor = 2;
|
||||
public final static byte PseudoColor = 3;
|
||||
public final static byte TrueColor = 4;
|
||||
public final static byte DirectColor = 5;
|
||||
|
||||
private final int _id;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param id The visual ID.
|
||||
*/
|
||||
public Visual(int id) {
|
||||
_id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the visual's ID.
|
||||
*
|
||||
* @return The visual's ID.
|
||||
*/
|
||||
public int getId() {
|
||||
return _id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the visual supports a backing store.
|
||||
*
|
||||
* @return Whether a backing store is supported.
|
||||
*/
|
||||
public byte getBackingStoreInfo() {
|
||||
return BackingStoreAlways;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the visual supports save-under.
|
||||
*
|
||||
* @return Whether save-under is supported.
|
||||
*/
|
||||
public boolean getSaveUnder() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the depth of the visual.
|
||||
* Under Android this is always 32.
|
||||
*
|
||||
* @return The depth of the visual, in bits.
|
||||
*/
|
||||
public byte getDepth() {
|
||||
return 32;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write details of the visual.
|
||||
*
|
||||
* @param io The input/output stream.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void write(InputOutput io) throws IOException {
|
||||
io.writeInt(_id); // Visual ID.
|
||||
io.writeByte(TrueColor); // Class.
|
||||
io.writeByte((byte) 8); // Bits per RGB value.
|
||||
io.writeShort((short) (1 << 8)); // Colormap entries.
|
||||
io.writeInt(0x00ff0000); // Red mask.
|
||||
io.writeInt(0x0000ff00); // Green mask.
|
||||
io.writeInt(0x000000ff); // Blue mask.
|
||||
io.writePadBytes(4); // Unused.
|
||||
}
|
||||
}
|
||||
|
|
@ -1,963 +0,0 @@
|
|||
package au.com.darkside.xserver;
|
||||
|
||||
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 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 au.com.darkside.xserver.Xext.Extensions;
|
||||
import au.com.darkside.xserver.Xext.XShape;
|
||||
import au.com.darkside.xserver.Xext.XSync;
|
||||
|
||||
/**
|
||||
* This class implements an X Windows server.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
public class XServer {
|
||||
|
||||
static public abstract class OnXSeverStartListener {
|
||||
public abstract void onStart();
|
||||
}
|
||||
|
||||
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<Format> _formats;
|
||||
private final Hashtable<Integer, Resource> _resources;
|
||||
|
||||
private final Vector<Client> _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<Integer> _accessControlHosts;
|
||||
|
||||
private final Hashtable<String, Extension> _extensions;
|
||||
private OnXSeverStartListener _onStartListener = null;
|
||||
|
||||
/**
|
||||
* 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<Format>();
|
||||
_resources = new Hashtable<Integer, Resource>();
|
||||
_clients = new Vector<Client>();
|
||||
_atoms = new Hashtable<Integer, Atom>();
|
||||
_atomNames = new Hashtable<String, Atom>();
|
||||
_selections = new Hashtable<Integer, Selection>();
|
||||
_accessControlHosts = new HashSet<Integer>();
|
||||
|
||||
Extensions.Initialize();
|
||||
_extensions = new Hashtable<String, Extension>();
|
||||
_extensions.put("Generic Event Extension", new Extension(Extensions.XGE, (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));
|
||||
//_extensions.put("SYNC", new Extension(Extensions.Sync, XSync.EventBase, XSync.ErrorBase));
|
||||
_extensions.put("XTEST", new Extension(Extensions.XTEST, (byte) 0, (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();
|
||||
}
|
||||
|
||||
public void setOnStartListener(OnXSeverStartListener l){
|
||||
_onStartListener = l;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
||||
try {
|
||||
_acceptThread = new AcceptThread(_port);
|
||||
_acceptThread.start();
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
if(_onStartListener != null) _onStartListener.onStart();
|
||||
|
||||
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<Integer> it = _resources.keySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Integer entry = it.next();
|
||||
if (entry > _clientIdStep) _resources.remove(entry);
|
||||
}
|
||||
|
||||
_screen.removeNonDefaultColormaps();
|
||||
|
||||
if (_atoms.size() != Atom.numPredefinedAtoms()) {
|
||||
_atoms.clear();
|
||||
_atomNames.clear();
|
||||
Atom.registerPredefinedAtoms(this);
|
||||
}
|
||||
|
||||
_selections.clear();
|
||||
_timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the server's application context.
|
||||
*
|
||||
* @return The server's application context.
|
||||
*/
|
||||
public Context getContext() {
|
||||
return _context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the internet address the server is listening on.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a client from the list of active clients.
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable all clients except this one.
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next free resource id.
|
||||
*
|
||||
* @param id The resource ID.
|
||||
*/
|
||||
public int nextFreeResourceId() {
|
||||
int maxKey = 0;
|
||||
for (int cur : _resources.keySet())
|
||||
if (cur > maxKey)
|
||||
maxKey = cur;
|
||||
return maxKey++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<Resource> rc = _resources.values();
|
||||
Vector<Resource> dl = new Vector<Resource>();
|
||||
|
||||
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) {
|
||||
if (c == null) continue;
|
||||
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();
|
||||
|
||||
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.
|
||||
|
||||
if (e == null) {
|
||||
io.writeByte((byte) 0); // Present. 0 = false.
|
||||
io.writeByte((byte) 0); // Major opcode.
|
||||
io.writeByte((byte) 0); // First event.
|
||||
io.writeByte((byte) 0); // First error.
|
||||
} else {
|
||||
io.writeByte((byte) 1); // Present. 1 = true.
|
||||
io.writeByte(e.majorOpcode); // Major opcode.
|
||||
io.writeByte(e.firstEvent); // First event.
|
||||
io.writeByte(e.firstError); // First error.
|
||||
}
|
||||
|
||||
io.writePadBytes(20); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the list of extensions supported by the server.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @throws IOException
|
||||
*/
|
||||
public void writeListExtensions(Client client) throws IOException {
|
||||
Set<String> 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.
|
||||
|
||||
for (String s : ss) {
|
||||
byte[] ba = s.getBytes();
|
||||
|
||||
io.writeByte((byte) ba.length);
|
||||
io.writeBytes(ba, 0, ba.length);
|
||||
}
|
||||
|
||||
io.writePadBytes(pad); // Unused.
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a ChangeHosts request.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @param mode Change mode. 0=Insert, 1=Delete.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reply to a ListHosts request.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @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.
|
||||
|
||||
for (int addr : _accessControlHosts) {
|
||||
io.writeByte((byte) 0); // Family = Internet.
|
||||
io.writePadBytes(1); // Unused.
|
||||
io.writeShort((short) 4); // Length of address.
|
||||
io.writeByte((byte) ((addr >> 24) & 0xff));
|
||||
io.writeByte((byte) ((addr >> 16) & 0xff));
|
||||
io.writeByte((byte) ((addr >> 8) & 0xff));
|
||||
io.writeByte((byte) (addr & 0xff));
|
||||
}
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable access control.
|
||||
*
|
||||
* @param enabled If true, enable access control.
|
||||
*/
|
||||
public void setAccessControl(boolean enabled) {
|
||||
_accessControlEnabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of hosts that are allowed to connect.
|
||||
* This can be modified.
|
||||
*
|
||||
* @return The set of addresses that are allowed to connect.
|
||||
*/
|
||||
public HashSet<Integer> getAccessControlHosts() {
|
||||
return _accessControlHosts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is a client from the specified address allowed to connect?
|
||||
*
|
||||
* @param address The client's IP address, MSB format.
|
||||
* @return True if the client is allowed to exist.
|
||||
*/
|
||||
private boolean isAccessAllowed(int address) {
|
||||
if (!_accessControlEnabled) return true;
|
||||
|
||||
return _accessControlHosts.contains(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the screen saver parameters.
|
||||
*
|
||||
* @param timeout Timeout period, in seconds. 0=disabled, -1=default.
|
||||
* @param interval Interval in seconds. 0=disabled, -1=default.
|
||||
* @param preferBlanking 0=No, 1=Yes, 2=Default.
|
||||
* @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 - System.currentTimeMillis() / 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reply to GetScreenSaver request.
|
||||
*
|
||||
* @param client The remote client.
|
||||
* @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();
|
||||
}
|
||||
|
||||
/**
|
||||
* This class holds details of an extension.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
private class Extension {
|
||||
private final byte majorOpcode;
|
||||
private final byte firstEvent;
|
||||
private final byte firstError;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param pmajorOpcode Major opcode of the extension, or zero.
|
||||
* @param pfirstEvent Base event type code, or zero.
|
||||
* @param pfirstError Base error code, or zero.
|
||||
*/
|
||||
public Extension(byte pmajorOpcode, byte pfirstEvent, byte pfirstError) {
|
||||
majorOpcode = pmajorOpcode;
|
||||
firstEvent = pfirstEvent;
|
||||
firstError = pfirstError;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This thread runs while listening for incoming connections.
|
||||
* It runs until it is cancelled.
|
||||
*
|
||||
* @author Matthew Kwan
|
||||
*/
|
||||
private class AcceptThread extends Thread {
|
||||
private final ServerSocket _serverSocket;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param port The port to listen on.
|
||||
* @throws IOException
|
||||
*/
|
||||
AcceptThread(int port) throws IOException {
|
||||
_serverSocket = new ServerSocket(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the internet address that is accepting connections.
|
||||
* May be null.
|
||||
*
|
||||
* @return The internet address that is accepting connections.
|
||||
*/
|
||||
public InetAddress getInetAddress() {
|
||||
return _serverSocket.getInetAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the thread.
|
||||
*/
|
||||
public void run() {
|
||||
while (true) {
|
||||
Socket socket;
|
||||
|
||||
try {
|
||||
// This is a blocking call and will only return on a
|
||||
// successful connection or an exception.
|
||||
socket = _serverSocket.accept();
|
||||
} catch (IOException e) {
|
||||
break;
|
||||
}
|
||||
|
||||
int addr = 0;
|
||||
InetSocketAddress isa;
|
||||
|
||||
isa = (InetSocketAddress) socket.getRemoteSocketAddress();
|
||||
if (isa != null) {
|
||||
InetAddress ia = isa.getAddress();
|
||||
byte[] ba = ia.getAddress();
|
||||
|
||||
addr = ((ba[0] & 0xff) << 24) | ((ba[1] & 0xff) << 16) | ((ba[2] & 0xff) << 8) | (ba[3] & 0xff);
|
||||
}
|
||||
|
||||
if (addr != 0 && !isAccessAllowed(addr)) {
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
Client c;
|
||||
|
||||
try {
|
||||
c = new Client(XServer.this, socket, _clientIdBase, _clientIdStep - 1);
|
||||
_clients.add(c);
|
||||
c.start();
|
||||
_clientIdBase += _clientIdStep;
|
||||
} catch (IOException e) {
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException e2) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the thread.
|
||||
*/
|
||||
public void cancel() {
|
||||
try {
|
||||
_serverSocket.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
package au.com.darkside.xserver.Xext;
|
||||
|
||||
public class Counter {
|
||||
public int COUNTER;
|
||||
public long value;
|
||||
public long firstTimestamp;
|
||||
public long firstTimestampNano;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param pmajorOpcode Major opcode of the extension, or zero.
|
||||
* @param pfirstEvent Base event type code, or zero.
|
||||
* @param pfirstError Base error code, or zero.
|
||||
*/
|
||||
public Counter (int ID, long initVal) {
|
||||
COUNTER=ID;
|
||||
value=initVal;
|
||||
firstTimestamp=System.currentTimeMillis();
|
||||
firstTimestampNano=System.nanoTime();
|
||||
}
|
||||
|
||||
public long getValue() {
|
||||
return System.currentTimeMillis()-firstTimestamp;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
package au.com.darkside.xserver.Xext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import au.com.darkside.xserver.Client;
|
||||
import au.com.darkside.xserver.ErrorCode;
|
||||
import au.com.darkside.xserver.InputOutput;
|
||||
import au.com.darkside.xserver.Util;
|
||||
import au.com.darkside.xserver.XServer;
|
||||
|
||||
/**
|
||||
* This class handles requests relating to extensions.
|
||||
*
|
||||
* @author mkwan
|
||||
*/
|
||||
public class Extensions {
|
||||
public static final byte XGE = -128;
|
||||
public static final byte XTEST = -124;
|
||||
public static final byte Sync = -127;
|
||||
public static final byte BigRequests = -126;
|
||||
public static final byte Shape = -125;
|
||||
|
||||
static public void Initialize(){
|
||||
XSync.Initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a request relating to an X extension.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The remote client.
|
||||
* @param opcode The request's opcode.
|
||||
* @param arg Optional first argument.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void processRequest(XServer xServer, Client client, byte opcode, byte arg, int bytesRemaining) throws IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
switch (opcode) {
|
||||
case XGE:
|
||||
if (bytesRemaining != 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else { // Assume arg == 0 (GEQueryVersion).
|
||||
short xgeMajor = (short) io.readShort();
|
||||
short xgeMinor = (short) io.readShort();
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, arg);
|
||||
io.writeInt(0); // Reply length.
|
||||
io.writeShort(xgeMajor);
|
||||
io.writeShort(xgeMinor);
|
||||
io.writePadBytes(20);
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
break;
|
||||
case BigRequests:
|
||||
if (bytesRemaining != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else { // Assume arg == 0 (BigReqEnable).
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, arg);
|
||||
io.writeInt(0);
|
||||
io.writeInt(Integer.MAX_VALUE);
|
||||
io.writePadBytes(20);
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
break;
|
||||
case Shape:
|
||||
XShape.processRequest(xServer, client, opcode, arg, bytesRemaining);
|
||||
break;
|
||||
case XTEST:
|
||||
XTest.processRequest(xServer, client, opcode, arg, bytesRemaining);
|
||||
break;
|
||||
case Sync:
|
||||
// XSync.processRequest(xServer, client, opcode, arg, bytesRemaining);
|
||||
// break;
|
||||
default:
|
||||
io.readSkip(bytesRemaining); // Not implemented.
|
||||
ErrorCode.write(client, ErrorCode.Implementation, opcode, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
package au.com.darkside.xserver.Xext;
|
||||
|
||||
public class SystemCounter {
|
||||
public final byte resHi;
|
||||
public final byte resLo;
|
||||
public int COUNTER;
|
||||
public short nameLength;
|
||||
public byte[] name;
|
||||
public int nameLengthInt;
|
||||
public int padLen;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param pmajorOpcode Major opcode of the extension, or zero.
|
||||
* @param pfirstEvent Base event type code, or zero.
|
||||
* @param pfirstError Base error code, or zero.
|
||||
*/
|
||||
public SystemCounter (int ID, int presHi, int presLo, String pname) {
|
||||
COUNTER=ID;
|
||||
resHi = (byte) presHi;
|
||||
resLo = (byte) presLo;
|
||||
name=pname.getBytes();
|
||||
nameLengthInt=name.length;
|
||||
nameLength=(short)nameLengthInt;
|
||||
padLen=2;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,506 +0,0 @@
|
|||
package au.com.darkside.xserver.Xext;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Region;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import au.com.darkside.xserver.Client;
|
||||
import au.com.darkside.xserver.Drawable;
|
||||
import au.com.darkside.xserver.ErrorCode;
|
||||
import au.com.darkside.xserver.InputOutput;
|
||||
import au.com.darkside.xserver.Pixmap;
|
||||
import au.com.darkside.xserver.Util;
|
||||
import au.com.darkside.xserver.Window;
|
||||
import au.com.darkside.xserver.XServer;
|
||||
|
||||
/**
|
||||
* Handles requests related to the X SHAPE extension.
|
||||
*
|
||||
* @author mkwan
|
||||
*/
|
||||
public class XShape {
|
||||
public static final byte EventBase = 76;
|
||||
public static final byte KindBounding = 0;
|
||||
public static final byte KindClip = 1;
|
||||
public static final byte KindInput = 2;
|
||||
|
||||
private static final byte ShapeQueryVersion = 0;
|
||||
private static final byte ShapeRectangles = 1;
|
||||
private static final byte ShapeMask = 2;
|
||||
private static final byte ShapeCombine = 3;
|
||||
private static final byte ShapeOffset = 4;
|
||||
private static final byte ShapeQueryExtents = 5;
|
||||
private static final byte ShapeSelectInput = 6;
|
||||
private static final byte ShapeInputSelected = 7;
|
||||
private static final byte ShapeGetRectangles = 8;
|
||||
|
||||
private static final byte OpSet = 0;
|
||||
private static final byte OpUnion = 1;
|
||||
private static final byte OpIntersect = 2;
|
||||
private static final byte OpSubtract = 3;
|
||||
private static final byte OpInvert = 4;
|
||||
|
||||
/**
|
||||
* Process a request relating to the X SHAPE extension.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The remote client.
|
||||
* @param opcode The request's opcode.
|
||||
* @param arg Optional first argument.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void processRequest(XServer xServer, Client client, byte opcode, byte arg, int bytesRemaining) throws java.io.IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
switch (arg) {
|
||||
case ShapeQueryVersion:
|
||||
if (bytesRemaining != 0) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.writeWithMinorOpcode(client, ErrorCode.Length, arg, opcode, 0);
|
||||
} else {
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, arg);
|
||||
io.writeInt(0); // Reply length.
|
||||
io.writeShort((short) 1); // Shape major.
|
||||
io.writeShort((short) 1); // Shape minor.
|
||||
io.writePadBytes(20);
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
break;
|
||||
case ShapeRectangles:
|
||||
if (bytesRemaining < 12) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.writeWithMinorOpcode(client, ErrorCode.Length, arg, opcode, 0);
|
||||
} else {
|
||||
byte shapeOp = (byte) io.readByte();
|
||||
byte shapeKind = (byte) io.readByte();
|
||||
|
||||
io.readByte(); // Ordering.
|
||||
io.readSkip(1);
|
||||
|
||||
int wid = io.readInt();
|
||||
int x = io.readShort();
|
||||
int y = io.readShort();
|
||||
Window w = (Window) xServer.getResource(wid);
|
||||
|
||||
bytesRemaining -= 12;
|
||||
|
||||
int nr = bytesRemaining / 8;
|
||||
Region r = (nr == 0) ? null : new Region();
|
||||
|
||||
for (int i = 0; i < nr; i++) {
|
||||
int rx = io.readShort();
|
||||
int ry = io.readShort();
|
||||
int rw = io.readShort();
|
||||
int rh = io.readShort();
|
||||
|
||||
r.op(new Rect(rx, ry, rx + rw, ry + rh), Region.Op.UNION);
|
||||
bytesRemaining -= 8;
|
||||
}
|
||||
|
||||
if (bytesRemaining != 0) // Oops!
|
||||
io.readSkip(bytesRemaining);
|
||||
|
||||
regionOperate(w, shapeKind, r, shapeOp, x, y);
|
||||
if (shapeKind != KindInput && w.isViewable()) w.invalidate();
|
||||
}
|
||||
break;
|
||||
case ShapeMask:
|
||||
if (bytesRemaining != 16) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.writeWithMinorOpcode(client, ErrorCode.Length, arg, opcode, 0);
|
||||
} else {
|
||||
byte shapeOp = (byte) io.readByte();
|
||||
byte shapeKind = (byte) io.readByte();
|
||||
|
||||
io.readSkip(2);
|
||||
|
||||
int wid = io.readInt();
|
||||
int x = io.readShort();
|
||||
int y = io.readShort();
|
||||
int pid = io.readInt(); // Pixmap ID.
|
||||
Window w = (Window) xServer.getResource(wid);
|
||||
Pixmap p = (pid == 0) ? null : (Pixmap) xServer.getResource(pid);
|
||||
Region r = (p == null) ? null : createRegion(p);
|
||||
|
||||
regionOperate(w, shapeKind, r, shapeOp, x, y);
|
||||
if (shapeKind != KindInput && w.isViewable()) w.invalidate();
|
||||
}
|
||||
break;
|
||||
case ShapeCombine:
|
||||
if (bytesRemaining != 16) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.writeWithMinorOpcode(client, ErrorCode.Length, arg, opcode, 0);
|
||||
} else {
|
||||
byte shapeOp = (byte) io.readByte();
|
||||
byte dstKind = (byte) io.readByte();
|
||||
byte srcKind = (byte) io.readByte();
|
||||
|
||||
io.readSkip(1);
|
||||
|
||||
int dwid = io.readInt();
|
||||
int x = io.readShort();
|
||||
int y = io.readShort();
|
||||
int swid = io.readInt();
|
||||
Window sw = (Window) xServer.getResource(swid);
|
||||
Window dw = (Window) xServer.getResource(dwid);
|
||||
Region sr = sw.getShapeRegion(srcKind);
|
||||
Rect irect = sw.getIRect();
|
||||
|
||||
x -= irect.left; // Make region coordinates relative.
|
||||
y -= irect.top;
|
||||
|
||||
regionOperate(dw, dstKind, sr, shapeOp, x, y);
|
||||
if (dstKind != KindInput && dw.isViewable()) dw.invalidate();
|
||||
}
|
||||
break;
|
||||
case ShapeOffset:
|
||||
if (bytesRemaining != 12) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.writeWithMinorOpcode(client, ErrorCode.Length, arg, opcode, 0);
|
||||
} else {
|
||||
byte shapeKind = (byte) io.readByte();
|
||||
|
||||
io.readSkip(3);
|
||||
|
||||
int wid = io.readInt();
|
||||
int x = io.readShort();
|
||||
int y = io.readShort();
|
||||
Window w = (Window) xServer.getResource(wid);
|
||||
Region r = w.getShapeRegion(shapeKind);
|
||||
|
||||
if (r != null && (x != 0 || y != 0)) {
|
||||
r.translate(x, y);
|
||||
w.sendShapeNotify(shapeKind);
|
||||
if (shapeKind != KindInput && w.isViewable()) w.invalidate();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ShapeQueryExtents:
|
||||
if (bytesRemaining != 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.writeWithMinorOpcode(client, ErrorCode.Length, arg, opcode, 0);
|
||||
} else {
|
||||
int wid = io.readInt();
|
||||
Window w = (Window) xServer.getResource(wid);
|
||||
boolean bs = w.isBoundingShaped();
|
||||
boolean cs = w.isClipShaped();
|
||||
Rect orect;
|
||||
Rect irect;
|
||||
|
||||
if (bs) orect = w.getShapeRegion(KindBounding).getBounds();
|
||||
else orect = w.getORect();
|
||||
|
||||
if (cs) irect = w.getShapeRegion(KindClip).getBounds();
|
||||
else irect = w.getIRect();
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, arg);
|
||||
io.writeInt(0);
|
||||
io.writeByte((byte) (bs ? 1 : 0)); // Bounding shaped?
|
||||
io.writeByte((byte) (cs ? 1 : 0)); // Clip shaped?
|
||||
io.writePadBytes(2);
|
||||
io.writeShort((short) orect.left);
|
||||
io.writeShort((short) orect.top);
|
||||
io.writeShort((short) orect.width());
|
||||
io.writeShort((short) orect.height());
|
||||
io.writeShort((short) irect.left);
|
||||
io.writeShort((short) irect.top);
|
||||
io.writeShort((short) irect.width());
|
||||
io.writeShort((short) irect.height());
|
||||
io.writePadBytes(4);
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
break;
|
||||
case ShapeSelectInput:
|
||||
if (bytesRemaining != 8) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.writeWithMinorOpcode(client, ErrorCode.Length, arg, opcode, 0);
|
||||
} else {
|
||||
int wid = io.readInt();
|
||||
boolean enable = (io.readByte() == 1);
|
||||
|
||||
io.readSkip(3);
|
||||
|
||||
Window w = (Window) xServer.getResource(wid);
|
||||
|
||||
if (enable) w.addShapeSelectInput(client);
|
||||
else w.removeShapeSelectInput(client);
|
||||
}
|
||||
break;
|
||||
case ShapeInputSelected:
|
||||
if (bytesRemaining != 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.writeWithMinorOpcode(client, ErrorCode.Length, arg, opcode, 0);
|
||||
} else {
|
||||
int wid = io.readInt();
|
||||
Window w = (Window) xServer.getResource(wid);
|
||||
boolean enabled = w.shapeSelectInputEnabled(client);
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, (byte) (enabled ? 1 : 0));
|
||||
io.writeInt(0); // Reply length.
|
||||
io.writePadBytes(24);
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
break;
|
||||
case ShapeGetRectangles:
|
||||
if (bytesRemaining != 8) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.writeWithMinorOpcode(client, ErrorCode.Length, arg, opcode, 0);
|
||||
} else {
|
||||
int wid = io.readInt();
|
||||
byte shapeKind = (byte) io.readByte();
|
||||
|
||||
io.readSkip(3);
|
||||
|
||||
Window w = (Window) xServer.getResource(wid);
|
||||
Region r = w.getShapeRegion(shapeKind);
|
||||
Rect irect = w.getIRect();
|
||||
byte ordering = 0; // Unsorted.
|
||||
List<Rect> rectangles = rectanglesFromRegion(r);
|
||||
int nr = rectangles.size();
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, ordering);
|
||||
io.writeInt(2 * nr); // Reply length.
|
||||
io.writeInt(nr);
|
||||
io.writePadBytes(20);
|
||||
|
||||
for (Rect rect : rectangles) {
|
||||
io.writeShort((short) (rect.left - irect.left));
|
||||
io.writeShort((short) (rect.top - irect.top));
|
||||
io.writeShort((short) rect.width());
|
||||
io.writeShort((short) rect.height());
|
||||
}
|
||||
}
|
||||
|
||||
io.flush();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Implementation, opcode, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Carry out a shape operation on a region.
|
||||
*
|
||||
* @param w The destination window to operate on.
|
||||
* @param shapeKind The type of shape in the destination window.
|
||||
* @param sr Source region.
|
||||
* @param shapeOp Operation to carry out on the regions.
|
||||
* @param x X offset to apply to the source region.
|
||||
* @param y Y offset to apply to the source region.
|
||||
*/
|
||||
private static void regionOperate(Window w, byte shapeKind, Region sr, byte shapeOp, int x, int y) {
|
||||
if (sr != null) { // Apply (x, y) offset.
|
||||
Region r = new Region();
|
||||
Rect irect = w.getIRect();
|
||||
|
||||
sr.translate(x + irect.left, y + irect.top, r);
|
||||
sr = r;
|
||||
}
|
||||
|
||||
Region dr = w.getShapeRegion(shapeKind);
|
||||
|
||||
switch (shapeOp) {
|
||||
case OpSet:
|
||||
break;
|
||||
case OpUnion:
|
||||
if (sr == null || dr == null) sr = null;
|
||||
else sr.op(dr, Region.Op.UNION);
|
||||
break;
|
||||
case OpIntersect:
|
||||
if (sr == null) sr = dr;
|
||||
else if (dr != null) sr.op(dr, Region.Op.INTERSECT);
|
||||
break;
|
||||
case OpSubtract: // Subtract source region from dest region.
|
||||
if (sr == null) sr = new Region(); // Empty region.
|
||||
else if (dr == null) sr.op(w.getORect(), Region.Op.DIFFERENCE);
|
||||
else sr.op(dr, Region.Op.DIFFERENCE);
|
||||
break;
|
||||
case OpInvert: // Subtract dest region from source region.
|
||||
if (dr == null) {
|
||||
sr = new Region(); // Empty region.
|
||||
} else if (sr == null) {
|
||||
sr = new Region(w.getORect());
|
||||
sr.op(dr, Region.Op.REVERSE_DIFFERENCE);
|
||||
} else {
|
||||
sr.op(dr, Region.Op.REVERSE_DIFFERENCE);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
w.setShapeRegion(shapeKind, sr);
|
||||
w.sendShapeNotify(shapeKind);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of rectangles that when combined make up the region.
|
||||
*
|
||||
* @param r The region.
|
||||
* @return
|
||||
*/
|
||||
private static List<Rect> rectanglesFromRegion(Region r) {
|
||||
ArrayList<Rect> rl = new ArrayList<Rect>();
|
||||
|
||||
if (r != null && !r.isEmpty()) {
|
||||
if (r.isRect()) rl.add(r.getBounds());
|
||||
else extractRectangles(r, r.getBounds(), rl);
|
||||
}
|
||||
|
||||
return rl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively break the region contained in the rectangle into
|
||||
* rectangles entirely contained in the rectangle.
|
||||
*
|
||||
* @param r Region being broken into rectangles.
|
||||
* @param rect Part of the region being analyzed..
|
||||
* @param rl Return list of rectangles.
|
||||
*/
|
||||
private static void extractRectangles(Region r, Rect rect, ArrayList<Rect> rl) {
|
||||
int rs = regionRectIntersection(r, rect);
|
||||
|
||||
if (rs == 0) // No intersection with rect.
|
||||
return;
|
||||
|
||||
if (rs == 1) { // Full intersection with rect.
|
||||
rl.add(rect);
|
||||
return;
|
||||
}
|
||||
|
||||
int rw = rect.width();
|
||||
int rh = rect.height();
|
||||
|
||||
if (rw > rh) { // Split the rectangle horizontally.
|
||||
int cx = rect.left + rw / 2;
|
||||
|
||||
extractRectangles(r, new Rect(rect.left, rect.top, cx, rect.bottom), rl);
|
||||
extractRectangles(r, new Rect(cx, rect.top, rect.right, rect.bottom), rl);
|
||||
} else { // Split it vertically.
|
||||
int cy = rect.top + rh / 2;
|
||||
|
||||
extractRectangles(r, new Rect(rect.left, rect.top, rect.right, cy), rl);
|
||||
extractRectangles(r, new Rect(rect.left, cy, rect.right, rect.bottom), rl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check how a region intersects with a rectangle.
|
||||
*
|
||||
* @param r The region.
|
||||
* @param rect The rectangle.
|
||||
* @return 0 = no overlap; 1 = full overlap; -1 = partial overlap.
|
||||
*/
|
||||
private static int regionRectIntersection(final Region r, final Rect rect) {
|
||||
if (r.quickReject(rect)) return 0;
|
||||
|
||||
int icount = 0;
|
||||
int ocount = 0;
|
||||
|
||||
for (int y = rect.top; y < rect.bottom; y++) {
|
||||
for (int x = rect.left; x < rect.right; x++)
|
||||
if (r.contains(x, y)) icount++;
|
||||
else ocount++;
|
||||
|
||||
if (icount > 0 && ocount > 0) return -1;
|
||||
}
|
||||
|
||||
if (icount == 0) return 0;
|
||||
else if (ocount == 0) return 1;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a region using the non-zero pixels in the pixmap.
|
||||
*
|
||||
* @param p The pixmap.
|
||||
* @return A region equivalent to the non-zero pixels.
|
||||
*/
|
||||
private static Region createRegion(Pixmap p) {
|
||||
Drawable d = p.getDrawable();
|
||||
Region r = new Region();
|
||||
|
||||
extractRegion(r, d.getBitmap(), new Rect(0, 0, d.getWidth(), d.getHeight()));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively break the image contained in the rectangle into
|
||||
* rectangles containing non-zero pixels.
|
||||
*
|
||||
* @param region Returned region.
|
||||
* @param bitmap Bitmap where the pixels appear.
|
||||
* @param rect Rectangle containing the pixels.
|
||||
*/
|
||||
private static void extractRegion(Region region, Bitmap bitmap, Rect rect) {
|
||||
int nzp = checkNonZeroPixels(bitmap, rect);
|
||||
|
||||
if (nzp == 1) // Empty.
|
||||
return;
|
||||
|
||||
int rw = rect.width();
|
||||
int rh = rect.height();
|
||||
|
||||
if (nzp == 2) { // All non-zero. We have a rectangle.
|
||||
region.op(rect, Region.Op.UNION);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rw > rh) { // Split the rectangle horizontally.
|
||||
int cx = rect.left + rw / 2;
|
||||
|
||||
extractRegion(region, bitmap, new Rect(rect.left, rect.top, cx, rect.bottom));
|
||||
extractRegion(region, bitmap, new Rect(cx, rect.top, rect.right, rect.bottom));
|
||||
} else { // Split it vertically.
|
||||
int cy = rect.top + rh / 2;
|
||||
|
||||
extractRegion(region, bitmap, new Rect(rect.left, rect.top, rect.right, cy));
|
||||
extractRegion(region, bitmap, new Rect(rect.left, cy, rect.right, rect.bottom));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the number of non-zero pixels contained in the rectangle.
|
||||
* Return a bit mask indicating whether all the pixels are non-zero,
|
||||
* none of them, or a mix.
|
||||
*
|
||||
* @param bitmap The bitmap containing the pixels.
|
||||
* @param rect The rectangle.
|
||||
* @return 1 = no pixels set; 2 = all pixels set; 0 = some pixels set
|
||||
*/
|
||||
private static int checkNonZeroPixels(Bitmap bitmap, Rect rect) {
|
||||
final int width = rect.width();
|
||||
final int height = rect.height();
|
||||
int[] pixels = new int[width];
|
||||
int mask = 3;
|
||||
|
||||
for (int i = 0; i < height; i++) {
|
||||
bitmap.getPixels(pixels, 0, width, rect.left, rect.top + i, width, 1);
|
||||
|
||||
for (int p : pixels) {
|
||||
mask &= (p != 0xff000000) ? 2 : 1;
|
||||
if (mask == 0) return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
package au.com.darkside.xserver.Xext;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
import au.com.darkside.xserver.XServer;
|
||||
import au.com.darkside.xserver.Client;
|
||||
import au.com.darkside.xserver.InputOutput;
|
||||
import au.com.darkside.xserver.Util;
|
||||
import au.com.darkside.xserver.ErrorCode;
|
||||
|
||||
/**
|
||||
* Handles requests related to the X SYNC extension.
|
||||
*/
|
||||
public class XSync {
|
||||
private static int syncMajor = -1;
|
||||
private static int syncMinor;
|
||||
private static final Hashtable<String, SystemCounter> _systemCounters = new Hashtable<String, SystemCounter>();
|
||||
private static final Hashtable<Integer, Counter> _counters = new Hashtable<Integer, Counter>();
|
||||
|
||||
public static final byte SYNC_INIT = 0;
|
||||
public static final byte SYNC_LIST = 1;
|
||||
public static final byte SYNC_CREATE = 2;
|
||||
public static final byte SYNC_DESTROY = 3;
|
||||
public static final byte EventBase = 95;
|
||||
public static final byte ErrorBase = (byte)154;
|
||||
|
||||
static public void Initialize() {
|
||||
_systemCounters.put("dummy", new SystemCounter (1,4, 0,"dummy"));
|
||||
_systemCounters.put("SERVERTIME", new SystemCounter (3,6,7,"SERVERTIME"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a request relating to the X SYNC extension.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The remote client.
|
||||
* @param opcode The request's opcode.
|
||||
* @param arg Optional first argument.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
static public void processRequest(XServer xServer, Client client, byte opcode, byte arg, int bytesRemaining) throws java.io.IOException {
|
||||
InputOutput io=client.getInputOutput();
|
||||
switch(arg) {
|
||||
case SYNC_INIT:
|
||||
if (bytesRemaining < 2) {
|
||||
io.readSkip (bytesRemaining);
|
||||
ErrorCode.write (client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
syncMajor=io.readByte();
|
||||
syncMinor=io.readByte();
|
||||
bytesRemaining-=2;
|
||||
io.readSkip (bytesRemaining);
|
||||
synchronized(io) {
|
||||
Util.writeReplyHeader(client, arg);
|
||||
io.writeInt (0); // Reply length.
|
||||
io.writeByte((byte)syncMajor);
|
||||
io.writeByte((byte)syncMinor);
|
||||
io.writePadBytes(22);
|
||||
}
|
||||
io.flush ();
|
||||
}
|
||||
break;
|
||||
case SYNC_LIST:
|
||||
int n = _systemCounters.size();//number of counters
|
||||
int length = 0;
|
||||
for(String key : _systemCounters.keySet()) {
|
||||
int temp=(key.length()+2)%4;
|
||||
if(temp==0)
|
||||
temp=4;
|
||||
length+=14+key.length()+(4-temp);
|
||||
}
|
||||
int len=length%32;
|
||||
length/=32;
|
||||
if(len>0)
|
||||
length++;
|
||||
else
|
||||
len=32;
|
||||
length*=8;
|
||||
synchronized(io) {
|
||||
Util.writeReplyHeader(client, arg);
|
||||
io.writeInt (length); // Reply length.
|
||||
io.writeInt (n); // List length.
|
||||
io.writePadBytes(20); //unused
|
||||
for(SystemCounter count : _systemCounters.values()) {
|
||||
/* 4 COUNTER counter
|
||||
8 INT64 resolution
|
||||
2 n length of name in bytes
|
||||
n STRING8 name
|
||||
p pad,p=pad(n+2) */
|
||||
io.writeInt(count.COUNTER);
|
||||
io.writeInt(count.resLo);
|
||||
io.writeInt(count.resHi);
|
||||
io.writeShort(count.nameLength);
|
||||
io.writeBytes(count.name,0,count.name.length);
|
||||
int pad=(count.name.length+2)%4;
|
||||
if(pad==0)
|
||||
pad=4;
|
||||
io.writePadBytes(4-pad);
|
||||
}
|
||||
io.writePadBytes(32-(len));
|
||||
}
|
||||
io.flush();
|
||||
break;
|
||||
case SYNC_CREATE:
|
||||
if (bytesRemaining != 12) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int ID=io.readInt();
|
||||
long initVal=io.readLong();
|
||||
bytesRemaining-=12;
|
||||
_counters.put(ID, new Counter(ID,initVal));
|
||||
}
|
||||
break;
|
||||
case SYNC_DESTROY:
|
||||
if (bytesRemaining != 4) {
|
||||
io.readSkip (bytesRemaining);
|
||||
ErrorCode.write (client, ErrorCode.Length, opcode, 0);
|
||||
} else {
|
||||
int ID=io.readInt();
|
||||
bytesRemaining-=4;
|
||||
Counter count=_counters.remove(ID);
|
||||
if(count!=null) {
|
||||
synchronized(io) {
|
||||
Util.writeReplyHeader(client, arg);
|
||||
io.writeInt(0);
|
||||
io.writeLong(count.getValue());
|
||||
io.writePadBytes(16);
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
else {
|
||||
ErrorCode.writeWithMinorOpcode(client, ErrorBase, arg, opcode, ID);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
io.readSkip(bytesRemaining); // Not implemented.
|
||||
ErrorCode.write(client, ErrorCode.Implementation, opcode, 0);
|
||||
break;
|
||||
}
|
||||
}}
|
||||
|
|
@ -1,211 +0,0 @@
|
|||
package au.com.darkside.xserver.Xext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import au.com.darkside.xserver.Client;
|
||||
import au.com.darkside.xserver.Cursor;
|
||||
import au.com.darkside.xserver.ErrorCode;
|
||||
import au.com.darkside.xserver.InputOutput;
|
||||
import au.com.darkside.xserver.Resource;
|
||||
import au.com.darkside.xserver.ScreenView;
|
||||
import au.com.darkside.xserver.Util;
|
||||
import au.com.darkside.xserver.Window;
|
||||
import au.com.darkside.xserver.XServer;
|
||||
|
||||
/**
|
||||
* Handles requests related to the XTEST extension.
|
||||
*
|
||||
* @author mkwan
|
||||
*/
|
||||
public class XTest {
|
||||
private static final byte XTestGetVersion = 0;
|
||||
private static final byte XTestCompareCursor = 1;
|
||||
private static final byte XTestFakeInput = 2;
|
||||
private static final byte XTestGrabControl = 3;
|
||||
|
||||
private static final byte KeyPress = 2;
|
||||
private static final byte KeyRelease = 3;
|
||||
private static final byte ButtonPress = 4;
|
||||
private static final byte ButtonRelease = 5;
|
||||
private static final byte MotionNotify = 6;
|
||||
|
||||
/**
|
||||
* Process a request relating to the X XTEST extension.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The remote client.
|
||||
* @param opcode The request's opcode.
|
||||
* @param arg Optional first argument.
|
||||
* @param bytesRemaining Bytes yet to be read in the request.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void processRequest(XServer xServer, Client client, byte opcode, byte arg, int bytesRemaining) throws java.io.IOException {
|
||||
InputOutput io = client.getInputOutput();
|
||||
|
||||
switch (arg) {
|
||||
case XTestGetVersion:
|
||||
if (bytesRemaining != 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.writeWithMinorOpcode(client, ErrorCode.Length, arg, opcode, 0);
|
||||
} else {
|
||||
io.readSkip(4); // Skip client major/minor version.
|
||||
|
||||
final byte serverMajorVersion = 2;
|
||||
final int serverMinorVersion = 1;
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, serverMajorVersion);
|
||||
io.writeInt(0); // Reply length.
|
||||
io.writeShort((short) serverMinorVersion);
|
||||
io.writePadBytes(22);
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
break;
|
||||
case XTestCompareCursor:
|
||||
if (bytesRemaining != 8) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.writeWithMinorOpcode(client, ErrorCode.Length, arg, opcode, 0);
|
||||
} else {
|
||||
int wid = io.readInt();
|
||||
int cid = io.readInt();
|
||||
Resource r = xServer.getResource(wid);
|
||||
|
||||
if (r == null || r.getType() != Resource.WINDOW) {
|
||||
ErrorCode.writeWithMinorOpcode(client, ErrorCode.Window, arg, opcode, wid);
|
||||
return;
|
||||
}
|
||||
|
||||
Window w = (Window) r;
|
||||
Cursor c = w.getCursor();
|
||||
boolean same;
|
||||
|
||||
if (cid == 0) // No cursor.
|
||||
same = (c == null);
|
||||
else if (cid == 1) // CurrentCursor.
|
||||
same = (c == xServer.getScreen().getCurrentCursor());
|
||||
else same = (cid == c.getId());
|
||||
|
||||
synchronized (io) {
|
||||
Util.writeReplyHeader(client, (byte) (same ? 1 : 0));
|
||||
io.writeInt(0); // Reply length.
|
||||
io.writePadBytes(24);
|
||||
}
|
||||
io.flush();
|
||||
}
|
||||
break;
|
||||
case XTestFakeInput:
|
||||
if (bytesRemaining != 32) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.writeWithMinorOpcode(client, ErrorCode.Length, arg, opcode, 0);
|
||||
} else {
|
||||
byte type = (byte) io.readByte();
|
||||
int detail = io.readByte();
|
||||
|
||||
io.readSkip(2);
|
||||
|
||||
int delay = io.readInt();
|
||||
int wid = io.readInt();
|
||||
|
||||
io.readSkip(8);
|
||||
|
||||
int x = io.readShort();
|
||||
int y = io.readShort();
|
||||
|
||||
io.readSkip(8);
|
||||
|
||||
Window root;
|
||||
|
||||
if (wid == 0) {
|
||||
root = xServer.getScreen().getRootWindow();
|
||||
} else {
|
||||
Resource r = (Window) xServer.getResource(wid);
|
||||
|
||||
if (r == null || r.getType() != Resource.WINDOW) {
|
||||
ErrorCode.writeWithMinorOpcode(client, ErrorCode.Window, arg, opcode, wid);
|
||||
return;
|
||||
} else {
|
||||
root = (Window) r;
|
||||
}
|
||||
}
|
||||
testFakeInput(xServer, client, opcode, arg, type, detail, delay, root, x, y);
|
||||
}
|
||||
break;
|
||||
case XTestGrabControl:
|
||||
if (bytesRemaining != 4) {
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.writeWithMinorOpcode(client, ErrorCode.Length, arg, opcode, 0);
|
||||
} else {
|
||||
boolean impervious = (io.readByte() != 0);
|
||||
|
||||
io.readSkip(3);
|
||||
client.setImperviousToServerGrabs(impervious);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
io.readSkip(bytesRemaining);
|
||||
ErrorCode.write(client, ErrorCode.Implementation, opcode, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject a fake event.
|
||||
*
|
||||
* @param xServer The X server.
|
||||
* @param client The remote client.
|
||||
* @param opcode The request opcode, used for error reporting.
|
||||
* @param minorOpcode The minor opcode, used for error reporting.
|
||||
* @param type The event type.
|
||||
* @param detail Meaning depends on event type.
|
||||
* @param delay Millisecond delay.
|
||||
* @param root The root window in which motion takes place.
|
||||
* @param x The X coordinate of a motion event.
|
||||
* @param y The Y coordinate of a motion event.
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
private static void testFakeInput(XServer xServer, Client client, byte opcode, byte minorOpcode, byte type, int detail, int delay, Window root, int x, int y) throws java.io.IOException {
|
||||
if (delay != 0) {
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
ScreenView sv = xServer.getScreen();
|
||||
|
||||
switch (type) {
|
||||
case KeyPress:
|
||||
sv.notifyKeyPressedReleased(detail, true);
|
||||
break;
|
||||
case KeyRelease:
|
||||
sv.notifyKeyPressedReleased(detail, false);
|
||||
break;
|
||||
case ButtonPress:
|
||||
sv.updatePointerButtons(detail, true);
|
||||
break;
|
||||
case ButtonRelease:
|
||||
sv.updatePointerButtons(detail, false);
|
||||
break;
|
||||
case MotionNotify:
|
||||
if (root != null) sv = root.getScreen();
|
||||
|
||||
if (detail != 0) { // Relative position.
|
||||
x += sv.getPointerX();
|
||||
y += sv.getPointerY();
|
||||
}
|
||||
|
||||
if (x < 0) x = 0;
|
||||
else if (x >= sv.getWidth()) x = sv.getWidth() - 1;
|
||||
|
||||
if (y < 0) y = 0;
|
||||
else if (y >= sv.getHeight()) y = sv.getHeight() - 1;
|
||||
|
||||
sv.updatePointerPosition(x, y, 0);
|
||||
break;
|
||||
default:
|
||||
ErrorCode.writeWithMinorOpcode(client, ErrorCode.Value, minorOpcode, opcode, type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 162 B |
|
Before Width: | Height: | Size: 135 B |
|
Before Width: | Height: | Size: 134 B |
|
Before Width: | Height: | Size: 138 B |
|
Before Width: | Height: | Size: 153 B |
|
Before Width: | Height: | Size: 154 B |
|
Before Width: | Height: | Size: 158 B |
|
Before Width: | Height: | Size: 144 B |
|
Before Width: | Height: | Size: 123 B |
|
Before Width: | Height: | Size: 141 B |
|
Before Width: | Height: | Size: 154 B |
|
Before Width: | Height: | Size: 148 B |
|
Before Width: | Height: | Size: 163 B |
|
Before Width: | Height: | Size: 159 B |
|
Before Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 153 B |
|
Before Width: | Height: | Size: 124 B |
|
Before Width: | Height: | Size: 156 B |
|
Before Width: | Height: | Size: 129 B |
|
Before Width: | Height: | Size: 136 B |
|
Before Width: | Height: | Size: 146 B |
|
Before Width: | Height: | Size: 163 B |
|
Before Width: | Height: | Size: 151 B |
|
Before Width: | Height: | Size: 148 B |
|
Before Width: | Height: | Size: 166 B |
|
Before Width: | Height: | Size: 154 B |
|
Before Width: | Height: | Size: 161 B |
|
Before Width: | Height: | Size: 168 B |
|
Before Width: | Height: | Size: 160 B |
|
Before Width: | Height: | Size: 163 B |
|
Before Width: | Height: | Size: 159 B |
|
Before Width: | Height: | Size: 120 B |
|
Before Width: | Height: | Size: 153 B |
|
Before Width: | Height: | Size: 145 B |
|
Before Width: | Height: | Size: 141 B |
|
Before Width: | Height: | Size: 121 B |
|
Before Width: | Height: | Size: 130 B |
|
Before Width: | Height: | Size: 118 B |
|
Before Width: | Height: | Size: 121 B |
|
Before Width: | Height: | Size: 175 B |
|
Before Width: | Height: | Size: 117 B |
|
Before Width: | Height: | Size: 157 B |
|
Before Width: | Height: | Size: 156 B |
|
Before Width: | Height: | Size: 169 B |
|
Before Width: | Height: | Size: 122 B |
|
Before Width: | Height: | Size: 157 B |
|
Before Width: | Height: | Size: 145 B |