1
0
Fork 0
mirror of https://github.com/rfc2822/GfxTablet synced 2025-10-03 09:39:16 +02:00

* app refactoring

* uinput driver
This commit is contained in:
Richard Hirner 2013-01-26 21:24:36 +01:00
parent 1b93a87ed0
commit cef17da6d2
34 changed files with 251 additions and 103 deletions

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.gimpusers.xorgtablet"
package="at.bitfire.gfxtablet"
android:versionCode="1"
android:versionName="1.0" >
@ -15,7 +15,7 @@
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name="com.gimpusers.xorgtablet.CanvasActivity"
android:name="at.bitfire.gfxtablet.CanvasActivity"
android:label="@string/app_name"
android:screenOrientation="landscape" >
<intent-filter>
@ -25,11 +25,11 @@
</intent-filter>
</activity>
<activity
android:name="com.gimpusers.xorgtablet.SettingsActivity"
android:name="at.bitfire.gfxtablet.SettingsActivity"
android:label="@string/title_activity_settings" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.gimpusers.xorgtablet.CanvasActivity" />
android:value="at.bitfire.gfxtablet.CanvasActivity" />
</activity>
</application>

View file

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before After
Before After

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">GfxTablet</string>
<string name="menu_settings">Settings</string>
<string name="preferences_host_title">Network host</string>
<string name="stylus_preference">Sense stylus only</string>
<string name="menu_about">About / Help</string>
</resources>

View file

@ -0,0 +1,9 @@
<preference-headers
xmlns:android="http://schemas.android.com/apk/res/android">
<header android:fragment="at.bitfire.gfxtablet.SettingsActivity$NetworkPrefsFragment"
android:title="Networking" />
<header android:fragment="at.bitfire.gfxtablet.SettingsActivity$DrawingPrefsFragment"
android:title="Drawing" />
</preference-headers>

View file

@ -1,4 +1,6 @@
package com.gimpusers.xorgtablet;
package at.bitfire.gfxtablet;
import at.bitfire.gfxtablet.R;
import android.net.Uri;
import android.os.Bundle;
@ -6,16 +8,14 @@ import android.preference.PreferenceManager;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.LinearLayout;
import android.widget.Toast;
public class CanvasActivity extends Activity {
CanvasView canvas;
SharedPreferences prefs;
XorgClient xorgClient;
NetworkClient netClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -27,15 +27,15 @@ public class CanvasActivity extends Activity {
setContentView(R.layout.activity_canvas);
LinearLayout layout = (LinearLayout)findViewById(R.id.canvas_layout);
new Thread(xorgClient = new XorgClient(PreferenceManager.getDefaultSharedPreferences(this))).start();
new Thread(netClient = new NetworkClient(PreferenceManager.getDefaultSharedPreferences(this))).start();
canvas = new CanvasView(this, xorgClient);
canvas = new CanvasView(this, netClient);
layout.addView(canvas);
}
@Override
protected void onDestroy() {
xorgClient.getQueue().add(new XDisconnectEvent());
netClient.getQueue().add(new NetDisconnectEvent());
super.onDestroy();
}
@ -46,7 +46,7 @@ public class CanvasActivity extends Activity {
}
public void showAbout(MenuItem item) {
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(("https://github.com/rfc2822/XorgTablet"))));
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(("http://rfc2822.github.com/GfxTablet"))));
}
public void showSettings(MenuItem item) {

View file

@ -1,6 +1,4 @@
package com.gimpusers.xorgtablet;
import java.util.concurrent.ExecutionException;
package at.bitfire.gfxtablet;
import android.annotation.SuppressLint;
import android.content.Context;
@ -8,7 +6,6 @@ import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
@ -17,11 +14,11 @@ import android.widget.Toast;
public class CanvasView extends View implements OnSharedPreferenceChangeListener {
final static int PRESSURE_RESOLUTION = 10000;
XorgClient xorgClient;
NetworkClient netClient;
SharedPreferences settings;
boolean acceptStylusOnly;
public CanvasView(Context context, XorgClient xorgClient) {
public CanvasView(Context context, NetworkClient netClient) {
super(context);
// disable until networking has been configured
@ -32,7 +29,7 @@ public class CanvasView extends View implements OnSharedPreferenceChangeListener
settings.registerOnSharedPreferenceChangeListener(this);
reconfigureAcceptedInputDevices();
this.xorgClient = xorgClient;
this.netClient = netClient;
new ConfigureNetworkingTask().execute();
}
@ -51,7 +48,7 @@ public class CanvasView extends View implements OnSharedPreferenceChangeListener
@Override
protected void onSizeChanged (int w, int h, int oldw, int oldh) {
Toast.makeText(getContext(), String.format("%dx%d", w, h), Toast.LENGTH_SHORT).show();
xorgClient.getQueue().add(new XConfigurationEvent(w, h, PRESSURE_RESOLUTION));
netClient.getQueue().add(new NetConfigurationEvent(w, h, PRESSURE_RESOLUTION));
}
@ -62,7 +59,7 @@ public class CanvasView extends View implements OnSharedPreferenceChangeListener
if (!acceptStylusOnly || (event.getToolType(ptr) == MotionEvent.TOOL_TYPE_STYLUS)) {
//Log.i("XorgTablet", String.format("Generic motion event logged: %f|%f, pressure %f", event.getX(ptr), event.getY(ptr), event.getPressure(ptr)));
if (event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE)
xorgClient.getQueue().add(new XMotionEvent((int)event.getX(ptr), (int)event.getY(ptr), (int)(event.getPressure(ptr)*PRESSURE_RESOLUTION)));
netClient.getQueue().add(new NetMotionEvent((int)event.getX(ptr), (int)event.getY(ptr), (int)(event.getPressure(ptr)*PRESSURE_RESOLUTION)));
}
return true;
}
@ -77,14 +74,14 @@ public class CanvasView extends View implements OnSharedPreferenceChangeListener
//Log.i("XorgTablet", String.format("Touch event logged: %f|%f, pressure %f", event.getX(ptr), event.getY(ptr), event.getPressure(ptr)));
switch (event.getActionMasked()) {
case MotionEvent.ACTION_MOVE:
xorgClient.getQueue().add(new XMotionEvent((int)event.getX(ptr), (int)event.getY(ptr), (int)(event.getPressure(ptr)*PRESSURE_RESOLUTION)));
netClient.getQueue().add(new NetMotionEvent((int)event.getX(ptr), (int)event.getY(ptr), (int)(event.getPressure(ptr)*PRESSURE_RESOLUTION)));
break;
case MotionEvent.ACTION_DOWN:
xorgClient.getQueue().add(new XButtonEvent((int)event.getX(ptr), (int)event.getY(ptr), (int)(event.getPressure(ptr)*PRESSURE_RESOLUTION), true));
netClient.getQueue().add(new NetButtonEvent((int)event.getX(ptr), (int)event.getY(ptr), (int)(event.getPressure(ptr)*PRESSURE_RESOLUTION), true));
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
xorgClient.getQueue().add(new XButtonEvent((int)event.getX(ptr), (int)event.getY(ptr), (int)(event.getPressure(ptr)*PRESSURE_RESOLUTION), false));
netClient.getQueue().add(new NetButtonEvent((int)event.getX(ptr), (int)event.getY(ptr), (int)(event.getPressure(ptr)*PRESSURE_RESOLUTION), false));
break;
}
@ -98,7 +95,7 @@ public class CanvasView extends View implements OnSharedPreferenceChangeListener
private class ConfigureNetworkingTask extends AsyncTask<Void, Void, Boolean> {
@Override
protected Boolean doInBackground(Void... params) {
return xorgClient.configureNetworking();
return netClient.configureNetworking();
}
protected void onPostExecute(Boolean success) {

View file

@ -1,13 +1,13 @@
package com.gimpusers.xorgtablet;
package at.bitfire.gfxtablet;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class XButtonEvent extends XEvent {
public class NetButtonEvent extends NetEvent {
boolean down;
public XButtonEvent(int x, int y, int pressure, boolean down) {
public NetButtonEvent(int x, int y, int pressure, boolean down) {
super(x, y, pressure);
this.down = down;
}

View file

@ -1,11 +1,11 @@
package com.gimpusers.xorgtablet;
package at.bitfire.gfxtablet;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class XConfigurationEvent extends XEvent {
public XConfigurationEvent(int x, int y, int pressure) {
public class NetConfigurationEvent extends NetEvent {
public NetConfigurationEvent(int x, int y, int pressure) {
super(x, y, pressure);
}

View file

@ -0,0 +1,12 @@
package at.bitfire.gfxtablet;
public class NetDisconnectEvent extends NetEvent {
public NetDisconnectEvent() {
super(0, 0, 0);
}
@Override
public byte[] toByteArray() {
return null;
}
}

View file

@ -1,13 +1,13 @@
package com.gimpusers.xorgtablet;
package at.bitfire.gfxtablet;
public abstract class XEvent {
public abstract class NetEvent {
int x, y, pressure;
public int getX() { return x; }
public int getY() { return y; }
public int getPressure() { return pressure; }
public XEvent(int x, int y, int pressure) {
public NetEvent(int x, int y, int pressure) {
this.x = Math.max(x, 0);
this.y = Math.max(y, 0);
this.pressure = pressure;

View file

@ -1,11 +1,11 @@
package com.gimpusers.xorgtablet;
package at.bitfire.gfxtablet;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class XMotionEvent extends XEvent {
public XMotionEvent(int x, int y, int pressure) {
public class NetMotionEvent extends NetEvent {
public NetMotionEvent(int x, int y, int pressure) {
super(x, y, pressure);
}

View file

@ -1,7 +1,5 @@
package com.gimpusers.xorgtablet;
package at.bitfire.gfxtablet;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
@ -11,18 +9,18 @@ import java.util.concurrent.LinkedBlockingQueue;
import android.content.SharedPreferences;
import android.util.Log;
// see xf86-networktablet on Github for details about the protocol
// see "drivers" directory in Github repository for details about the protocol
public class XorgClient implements Runnable {
LinkedBlockingQueue<XEvent> motionQueue = new LinkedBlockingQueue<XEvent>();
LinkedBlockingQueue<XEvent> getQueue() { return motionQueue; }
public class NetworkClient implements Runnable {
LinkedBlockingQueue<NetEvent> motionQueue = new LinkedBlockingQueue<NetEvent>();
LinkedBlockingQueue<NetEvent> getQueue() { return motionQueue; }
InetAddress destAddress;
SharedPreferences preferences;
XConfigurationEvent lastConfiguration = null;
NetConfigurationEvent lastConfiguration = null;
XorgClient(SharedPreferences preferences) {
NetworkClient(SharedPreferences preferences) {
this.preferences = preferences;
}
@ -46,17 +44,14 @@ public class XorgClient implements Runnable {
try {
DatagramSocket socket = new DatagramSocket();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
while (true) {
XEvent event = motionQueue.take();
NetEvent event = motionQueue.take();
// save resolution, even if not sending it
if (event.getClass() == XConfigurationEvent.class)
lastConfiguration = (XConfigurationEvent)event;
if (event.getClass() == NetConfigurationEvent.class)
lastConfiguration = (NetConfigurationEvent)event;
// graceful shutdown
else if (event.getClass() == XDisconnectEvent.class)
else if (event.getClass() == NetDisconnectEvent.class)
break;
if (destAddress == null) // no valid destination host
@ -65,11 +60,9 @@ public class XorgClient implements Runnable {
byte[] data = event.toByteArray();
DatagramPacket pkt = new DatagramPacket(data, data.length, destAddress, 40117);
socket.send(pkt);
baos.reset();
}
} catch (Exception e) {
Log.e("XorgTablet", "motionQueue failed: " + e.getMessage());
Log.e("GfxTablet", "motionQueue failed: " + e.getMessage());
}
}
}

View file

@ -1,26 +1,14 @@
package com.gimpusers.xorgtablet;
package at.bitfire.gfxtablet;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Configuration;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.preference.RingtonePreference;
import android.text.TextUtils;
import android.view.MenuItem;
import android.support.v4.app.NavUtils;
import java.util.List;
import at.bitfire.gfxtablet.R;
public class SettingsActivity extends PreferenceActivity {
public static final String
KEY_PREF_HOST = "host_preference",

1
driver-uinput/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
networktablet

2
driver-uinput/Makefile Normal file
View file

@ -0,0 +1,2 @@
networktablet : networktablet.c

View file

@ -0,0 +1,142 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#include <arpa/inet.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include "protocol.h"
#define die(str, args...) { \
perror(str); \
exit(EXIT_FAILURE); \
}
void init_device(int fd)
{
struct uinput_user_dev uidev;
// 1 button
if (ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0)
die("error: ioctl UI_SET_EVBIT EV_KEY");
if (ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH) < 0)
die("error: ioctl UI_SET_KEYBIT");
// 2 main axes + pressure (absolute positioning)
if (ioctl(fd, UI_SET_EVBIT, EV_ABS) < 0)
die("error: ioctl UI_SET_EVBIT EV_ABS");
if (ioctl(fd, UI_SET_ABSBIT, ABS_X) < 0)
die("error: ioctl UI_SETEVBIT ABS_X");
if (ioctl(fd, UI_SET_ABSBIT, ABS_Y) < 0)
die("error: ioctl UI_SETEVBIT ABS_Y");
if (ioctl(fd, UI_SET_ABSBIT, ABS_PRESSURE) < 0)
die("error: ioctl UI_SETEVBIT ABS_PRESSURE");
memset(&uidev, 0, sizeof(uidev));
snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "Network Tablet");
uidev.id.bustype = BUS_VIRTUAL;
uidev.id.vendor = 0x1;
uidev.id.product = 0x1;
uidev.id.version = 1;
uidev.absmin[ABS_X] = 0;
uidev.absmax[ABS_X] = INT_MAX;
uidev.absmin[ABS_Y] = 0;
uidev.absmax[ABS_Y] = INT_MAX;
uidev.absmin[ABS_PRESSURE] = 0;
uidev.absmax[ABS_PRESSURE] = 10000; // 10,000 instead of 32,767 because sometimes there is pressure >1.0
if (write(fd, &uidev, sizeof(uidev)) < 0)
die("error: write");
if (ioctl(fd, UI_DEV_CREATE) < 0)
die("error: ioctl");
}
int prepare_socket()
{
int s;
struct sockaddr_in addr;
if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
die("error: prepare_socket()");
bzero(&addr, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(NETWORKTABLET_PORT);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
die("error: prepare_socket()");
return s;
}
void send_event(int device, int type, int code, int value)
{
struct input_event ev;
ev.type = type;
ev.code = code;
ev.value = value;
if (write(device, &ev, sizeof(ev)) < 0)
error("error: write()");
}
int main(void)
{
int device, socket;
struct event_packet ev_pkt;
int x, y, btn_down = 0;
int max_x = INT_MAX, max_y = INT_MAX;
if ((device = open("/dev/uinput", O_WRONLY | O_NONBLOCK)) < 0)
die("error: open");
init_device(device);
socket = prepare_socket();
while (recv(socket, &ev_pkt, sizeof(ev_pkt), 0) >= 7) { // every packet has at least 7 bytes
printf("."); fflush(0);
ev_pkt.x = ntohs(ev_pkt.x);
ev_pkt.y = ntohs(ev_pkt.y);
ev_pkt.pressure = ntohs(ev_pkt.pressure);
//printf("x: %hi, y: %hi, pressure: %hi\n", ev_pkt.x, ev_pkt.y, ev_pkt.pressure);
x = (long)ev_pkt.x * INT_MAX/max_x;
y = (long)ev_pkt.y * INT_MAX/max_y;
send_event(device, EV_ABS, ABS_X, x);
send_event(device, EV_ABS, ABS_Y, y);
send_event(device, EV_ABS, ABS_PRESSURE, ev_pkt.pressure);
switch (ev_pkt.type) {
case EVENT_TYPE_SET_RESOLUTION: {
struct input_absinfo absinfo;
max_x = ev_pkt.x;
max_y = ev_pkt.y;
printf("Set resolution to %hix%hi\n", max_x+1, max_y+1);
break;
}
case EVENT_TYPE_MOTION:
send_event(device, EV_SYN, SYN_REPORT, 1);
break;
case EVENT_TYPE_BUTTON:
if (ev_pkt.button == 1)
send_event(device, EV_KEY, BTN_TOUCH, ev_pkt.down);
send_event(device, EV_SYN, SYN_REPORT, 1);
break;
}
}
close(socket);
ioctl(device, UI_DEV_DESTROY);
close(device);
return 0;
}

26
driver-uinput/protocol.h Normal file
View file

@ -0,0 +1,26 @@
#define NETWORKTABLET_PORT 40117
#pragma pack(push)
#pragma pack(1)
#define EVENT_TYPE_MOTION 0
#define EVENT_TYPE_BUTTON 1
#define EVENT_TYPE_SET_RESOLUTION 2
struct event_packet
{
char type; /* EVENT_TYPE_... */
struct { /* required */
short x, y;
short pressure;
};
struct { /* only required for EVENT_TYPE_BUTTON */
char button; /* number of button, beginning with 1 */
char down; /* 1 = button down, 0 = button up */
};
};
#pragma pack(pop)

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">XorgTablet</string>
<string name="menu_settings">Settings</string>
<string name="preferences_host_title">X.org host</string>
<string name="stylus_preference">Sense stylus only</string>
<string name="menu_about">About</string>
</resources>

View file

@ -1,10 +0,0 @@
<preference-headers
xmlns:android="http://schemas.android.com/apk/res/android">
<header android:fragment="com.gimpusers.xorgtablet.SettingsActivity$NetworkPrefsFragment"
android:title="Networking" />
<header android:fragment="com.gimpusers.xorgtablet.SettingsActivity$DrawingPrefsFragment"
android:title="Drawing" />
</preference-headers>

View file

@ -1,12 +0,0 @@
package com.gimpusers.xorgtablet;
public class XDisconnectEvent extends XEvent {
public XDisconnectEvent() {
super(0, 0, 0);
}
@Override
public byte[] toByteArray() {
return null;
}
}