fixed half screen update, server scale support

This commit is contained in:
Jose Pereira 2010-08-25 12:16:17 +01:00
parent 2acc4b6cca
commit f58601cc1b
53 changed files with 2460 additions and 84 deletions

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionName="0.7" android:versionCode="29" package="org.onaips.vnc_donate">
package="org.onaips.vnc" android:versionCode="31" android:versionName="0.72">
<uses-sdk android:minSdkVersion="4" />
<application android:debuggable="false" android:icon="@drawable/icon" android:label="droid VNC server">
<activity android:name=".MainActivity" android:label="@string/app_name" android:icon="@drawable/icon" android:launchMode="singleInstance" android:screenOrientation="portrait">

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -90,6 +90,7 @@
/home/onaips/Desktop/android_dev/android-ndk-r4/build/platforms/android-5/arch-arm/usr/include/asm/signal.h \
/home/onaips/Desktop/android_dev/android-ndk-r4/build/platforms/android-5/arch-arm/usr/include/asm-generic/signal.h \
/home/onaips/Desktop/android_dev/android-ndk-r4/build/platforms/android-5/arch-arm/usr/include/sched.h \
/home/onaips/Dropbox/projectos/workspace/avnc/jni/vnc/LibVNCServer-0.9.7/libvncserver/scale.h \
/home/onaips/Dropbox/projectos/workspace/avnc/jni/vnc/LibVNCServer-0.9.7/rfb/keysym.h \
/home/onaips/Dropbox/projectos/workspace/avnc/jni/vnc/suinput.h \
/home/onaips/Desktop/android_dev/android-ndk-r4/build/platforms/android-5/arch-arm/usr/include/linux/uinput.h \
@ -275,6 +276,8 @@
/home/onaips/Desktop/android_dev/android-ndk-r4/build/platforms/android-5/arch-arm/usr/include/sched.h:
/home/onaips/Dropbox/projectos/workspace/avnc/jni/vnc/LibVNCServer-0.9.7/libvncserver/scale.h:
/home/onaips/Dropbox/projectos/workspace/avnc/jni/vnc/LibVNCServer-0.9.7/rfb/keysym.h:
/home/onaips/Dropbox/projectos/workspace/avnc/jni/vnc/suinput.h:

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
bin/org/onaips/vnc/R.class Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

197
gen/org/onaips/vnc/R.java Normal file
View File

@ -0,0 +1,197 @@
/* AUTO-GENERATED FILE. DO NOT MODIFY.
*
* This class was automatically generated by the
* aapt tool from the resource data it found. It
* should not be modified by hand.
*/
package org.onaips.vnc;
public final class R {
public static final class array {
public static final int rotation_values=0x7f070000;
public static final int scale_strings=0x7f070002;
public static final int scale_values=0x7f070001;
}
public static final class attr {
/** <p>Must be a color value, in the form of "<code>#<i>rgb</i></code>", "<code>#<i>argb</i></code>",
"<code>#<i>rrggbb</i></code>", or "<code>#<i>aarrggbb</i></code>".
<p>This may also be a reference to a resource (in the form
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
theme attribute (in the form
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
containing a value of this type.
*/
public static final int backgroundColor=0x7f010000;
/** <p>Must be a string value, using '\\;' to escape characters such as '\\n' or '\\uxxxx' for a unicode character.
<p>This may also be a reference to a resource (in the form
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
theme attribute (in the form
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
containing a value of this type.
*/
public static final int keywords=0x7f010003;
/** <p>Must be a color value, in the form of "<code>#<i>rgb</i></code>", "<code>#<i>argb</i></code>",
"<code>#<i>rrggbb</i></code>", or "<code>#<i>aarrggbb</i></code>".
<p>This may also be a reference to a resource (in the form
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
theme attribute (in the form
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
containing a value of this type.
*/
public static final int primaryTextColor=0x7f010001;
/** <p>Must be an integer value, such as "<code>100</code>".
<p>This may also be a reference to a resource (in the form
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
theme attribute (in the form
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
containing a value of this type.
*/
public static final int refreshInterval=0x7f010004;
/** <p>Must be a color value, in the form of "<code>#<i>rgb</i></code>", "<code>#<i>argb</i></code>",
"<code>#<i>rrggbb</i></code>", or "<code>#<i>aarrggbb</i></code>".
<p>This may also be a reference to a resource (in the form
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
theme attribute (in the form
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
containing a value of this type.
*/
public static final int secondaryTextColor=0x7f010002;
}
public static final class drawable {
public static final int bg=0x7f020000;
public static final int btnstart=0x7f020001;
public static final int btnstart_normal=0x7f020002;
public static final int btnstart_pressed=0x7f020003;
public static final int btnstop=0x7f020004;
public static final int btnstop_normal=0x7f020005;
public static final int btnstop_pressed=0x7f020006;
public static final int droidvnclogo=0x7f020007;
public static final int icon=0x7f020008;
}
public static final class id {
public static final int Button01=0x7f090000;
public static final int Button02=0x7f090001;
public static final int TextView01=0x7f090002;
public static final int TextView02=0x7f090004;
public static final int banner_adview=0x7f090005;
public static final int preferences=0x7f090006;
public static final int stateLabel=0x7f090003;
}
public static final class layout {
public static final int main=0x7f030000;
}
public static final class menu {
public static final int menu=0x7f080000;
}
public static final class raw {
public static final int androidvncserver=0x7f050000;
public static final int indexvnc=0x7f050001;
public static final int vncviewer=0x7f050002;
}
public static final class string {
public static final int app_name=0x7f060000;
public static final int device_info_fmt=0x7f060002;
public static final int password=0x7f060001;
}
public static final class xml {
public static final int preferences=0x7f040000;
}
public static final class styleable {
/** Attributes that can be used with a com_admob_android_ads_AdView.
<p>Includes the following attributes:</p>
<table>
<colgroup align="left" />
<colgroup align="left" />
<tr><th>Attribute</th><th>Description</th></tr>
<tr><td><code>{@link #com_admob_android_ads_AdView_backgroundColor org.onaips.vnc:backgroundColor}</code></td><td></td></tr>
<tr><td><code>{@link #com_admob_android_ads_AdView_keywords org.onaips.vnc:keywords}</code></td><td></td></tr>
<tr><td><code>{@link #com_admob_android_ads_AdView_primaryTextColor org.onaips.vnc:primaryTextColor}</code></td><td></td></tr>
<tr><td><code>{@link #com_admob_android_ads_AdView_refreshInterval org.onaips.vnc:refreshInterval}</code></td><td></td></tr>
<tr><td><code>{@link #com_admob_android_ads_AdView_secondaryTextColor org.onaips.vnc:secondaryTextColor}</code></td><td></td></tr>
</table>
@see #com_admob_android_ads_AdView_backgroundColor
@see #com_admob_android_ads_AdView_keywords
@see #com_admob_android_ads_AdView_primaryTextColor
@see #com_admob_android_ads_AdView_refreshInterval
@see #com_admob_android_ads_AdView_secondaryTextColor
*/
public static final int[] com_admob_android_ads_AdView = {
0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003,
0x7f010004
};
/**
<p>This symbol is the offset where the {@link org.onaips.vnc.R.attr#backgroundColor}
attribute's value can be found in the {@link #com_admob_android_ads_AdView} array.
<p>Must be a color value, in the form of "<code>#<i>rgb</i></code>", "<code>#<i>argb</i></code>",
"<code>#<i>rrggbb</i></code>", or "<code>#<i>aarrggbb</i></code>".
<p>This may also be a reference to a resource (in the form
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
theme attribute (in the form
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
containing a value of this type.
@attr name android:backgroundColor
*/
public static final int com_admob_android_ads_AdView_backgroundColor = 0;
/**
<p>This symbol is the offset where the {@link org.onaips.vnc.R.attr#keywords}
attribute's value can be found in the {@link #com_admob_android_ads_AdView} array.
<p>Must be a string value, using '\\;' to escape characters such as '\\n' or '\\uxxxx' for a unicode character.
<p>This may also be a reference to a resource (in the form
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
theme attribute (in the form
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
containing a value of this type.
@attr name android:keywords
*/
public static final int com_admob_android_ads_AdView_keywords = 3;
/**
<p>This symbol is the offset where the {@link org.onaips.vnc.R.attr#primaryTextColor}
attribute's value can be found in the {@link #com_admob_android_ads_AdView} array.
<p>Must be a color value, in the form of "<code>#<i>rgb</i></code>", "<code>#<i>argb</i></code>",
"<code>#<i>rrggbb</i></code>", or "<code>#<i>aarrggbb</i></code>".
<p>This may also be a reference to a resource (in the form
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
theme attribute (in the form
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
containing a value of this type.
@attr name android:primaryTextColor
*/
public static final int com_admob_android_ads_AdView_primaryTextColor = 1;
/**
<p>This symbol is the offset where the {@link org.onaips.vnc.R.attr#refreshInterval}
attribute's value can be found in the {@link #com_admob_android_ads_AdView} array.
<p>Must be an integer value, such as "<code>100</code>".
<p>This may also be a reference to a resource (in the form
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
theme attribute (in the form
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
containing a value of this type.
@attr name android:refreshInterval
*/
public static final int com_admob_android_ads_AdView_refreshInterval = 4;
/**
<p>This symbol is the offset where the {@link org.onaips.vnc.R.attr#secondaryTextColor}
attribute's value can be found in the {@link #com_admob_android_ads_AdView} array.
<p>Must be a color value, in the form of "<code>#<i>rgb</i></code>", "<code>#<i>argb</i></code>",
"<code>#<i>rrggbb</i></code>", or "<code>#<i>aarrggbb</i></code>".
<p>This may also be a reference to a resource (in the form
"<code>@[<i>package</i>:]<i>type</i>:<i>name</i></code>") or
theme attribute (in the form
"<code>?[<i>package</i>:][<i>type</i>:]<i>name</i></code>")
containing a value of this type.
@attr name android:secondaryTextColor
*/
public static final int com_admob_android_ads_AdView_secondaryTextColor = 2;
};
}

View File

@ -1,8 +1,8 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CFLAGS := -DDONATE_VERSION
#LOCAL_CFLAGS += -DDONATE_VERSION
LOCAL_CFLAGS := -O3 -Wall
LOCAL_SRC_FILES:= \

54
jni/vnc/Android.mk~ Executable file
View File

@ -0,0 +1,54 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
#LOCAL_CFLAGS += -DDONATE_VERSION
LOCAL_CFLAGS := -O3
LOCAL_SRC_FILES:= \
fbvncserver.c \
suinput.c \
LibVNCServer-0.9.7/libvncserver/main.c \
LibVNCServer-0.9.7/libvncserver/rfbserver.c \
LibVNCServer-0.9.7/libvncserver/rfbregion.c \
LibVNCServer-0.9.7/libvncserver/auth.c \
LibVNCServer-0.9.7/libvncserver/sockets.c \
LibVNCServer-0.9.7/libvncserver/stats.c \
LibVNCServer-0.9.7/libvncserver/corre.c \
LibVNCServer-0.9.7/libvncserver/hextile.c \
LibVNCServer-0.9.7/libvncserver/rre.c \
LibVNCServer-0.9.7/libvncserver/translate.c \
LibVNCServer-0.9.7/libvncserver/cutpaste.c \
LibVNCServer-0.9.7/libvncserver/httpd.c \
LibVNCServer-0.9.7/libvncserver/cursor.c \
LibVNCServer-0.9.7/libvncserver/font.c \
LibVNCServer-0.9.7/libvncserver/draw.c \
LibVNCServer-0.9.7/libvncserver/selbox.c \
LibVNCServer-0.9.7/libvncserver/d3des.c \
LibVNCServer-0.9.7/libvncserver/vncauth.c \
LibVNCServer-0.9.7/libvncserver/cargs.c \
LibVNCServer-0.9.7/libvncserver/minilzo.c \
LibVNCServer-0.9.7/libvncserver/ultra.c \
LibVNCServer-0.9.7/libvncserver/scale.c \
LibVNCServer-0.9.7/libvncserver/zlib.c \
LibVNCServer-0.9.7/libvncserver/zrle.c \
LibVNCServer-0.9.7/libvncserver/zrleoutstream.c \
LibVNCServer-0.9.7/libvncserver/zrlepalettehelper.c \
LibVNCServer-0.9.7/libvncserver/zywrletemplate.c \
LibVNCServer-0.9.7/libvncserver/tight.c
LOCAL_C_INCLUDES := \
$(LOCAL_PATH) \
$(LOCAL_PATH)/LibVNCServer-0.9.7/libvncserver \
$(LOCAL_PATH)/LibVNCServer-0.9.7 \
$(LOCAL_PATH)/../jpeg
LOCAL_STATIC_LIBRARIES := jpeg
LOCAL_MODULE:= androidvncserver
LOCAL_LDLIBS := -lz -llog
include $(BUILD_EXECUTABLE)

View File

@ -351,12 +351,12 @@ void rfbScalingSetup(rfbClientPtr cl, int width, int height)
/* Update it! */
if (ptr->scaledScreenRefCount<1)
rfbScaledScreenUpdateRect(cl->screen, ptr, 0, 0, cl->screen->width, cl->screen->height);
/*
* rfbLog("Taking one from %dx%d-%d and adding it to %dx%d-%d\n",
* cl->scaledScreen->width, cl->scaledScreen->height,
* cl->scaledScreen->scaledScreenRefCount,
* ptr->width, ptr->height, ptr->scaledScreenRefCount);
*/
rfbLog("Taking one from %dx%d-%d and adding it to %dx%d-%d\n",
cl->scaledScreen->width, cl->scaledScreen->height,
cl->scaledScreen->scaledScreenRefCount,
ptr->width, ptr->height, ptr->scaledScreenRefCount);
LOCK(cl->updateMutex);
cl->scaledScreen->scaledScreenRefCount--;

View File

@ -0,0 +1,420 @@
/*
* scale.c - deal with server-side scaling.
*/
/*
* Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin
* Copyright (C) 2002 RealVNC Ltd.
* OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
* Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
* All Rights Reserved.
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#ifdef __STRICT_ANSI__
#define _BSD_SOURCE
#endif
#include <string.h>
#include <rfb/rfb.h>
#include <rfb/rfbregion.h>
#include "private.h"
#ifdef LIBVNCSERVER_HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef WIN32
#define write(sock,buf,len) send(sock,buf,len,0)
#else
#ifdef LIBVNCSERVER_HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <pwd.h>
#ifdef LIBVNCSERVER_HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef LIBVNCSERVER_HAVE_NETINET_IN_H
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#endif
#endif
#ifdef CORBA
#include <vncserverctrl.h>
#endif
#ifdef DEBUGPROTO
#undef DEBUGPROTO
#define DEBUGPROTO(x) x
#else
#define DEBUGPROTO(x)
#endif
/****************************/
#define CEIL(x) ( (double) ((int) (x)) == (x) ? \
(double) ((int) (x)) : (double) ((int) (x) + 1) )
#define FLOOR(x) ( (double) ((int) (x)) )
int ScaleX(rfbScreenInfoPtr from, rfbScreenInfoPtr to, int x)
{
if ((from==to) || (from==NULL) || (to==NULL)) return x;
return ((int)(((double) x / (double)from->width) * (double)to->width ));
}
int ScaleY(rfbScreenInfoPtr from, rfbScreenInfoPtr to, int y)
{
if ((from==to) || (from==NULL) || (to==NULL)) return y;
return ((int)(((double) y / (double)from->height) * (double)to->height ));
}
/* So, all of the encodings point to the ->screen->frameBuffer,
* We need to change this!
*/
void rfbScaledCorrection(rfbScreenInfoPtr from, rfbScreenInfoPtr to, int *x, int *y, int *w, int *h, char *function)
{
double x1,y1,w1,h1, x2, y2, w2, h2;
double scaleW = ((double) to->width) / ((double) from->width);
double scaleH = ((double) to->height) / ((double) from->height);
/*
* rfbLog("rfbScaledCorrection(%p -> %p, %dx%d->%dx%d (%dXx%dY-%dWx%dH)\n",
* from, to, from->width, from->height, to->width, to->height, *x, *y, *w, *h);
*/
/* If it's the original framebuffer... */
if (from==to) return;
x1 = ((double) *x) * scaleW;
y1 = ((double) *y) * scaleH;
w1 = ((double) *w) * scaleW;
h1 = ((double) *h) * scaleH;
/*cast from double to int is same as "*x = floor(x1);" */
x2 = FLOOR(x1);
y2 = FLOOR(y1);
/* include into W and H the jitter of scaling X and Y */
w2 = CEIL(w1 + ( x1 - x2 ));
h2 = CEIL(h1 + ( y1 - y2 ));
/*
* rfbLog("%s (%dXx%dY-%dWx%dH -> %fXx%fY-%fWx%fH) {%dWx%dH -> %dWx%dH}\n",
* function, *x, *y, *w, *h, x2, y2, w2, h2,
* from->width, from->height, to->width, to->height);
*/
/* simulate ceil() without math library */
*x = (int)x2;
*y = (int)y2;
*w = (int)w2;
*h = (int)h2;
/* Small changes for a thumbnail may be scaled to zero */
if (*w==0) (*w)++;
if (*h==0) (*h)++;
/* scaling from small to big may overstep the size a bit */
if (*x+*w > to->width) *w=to->width - *x;
if (*y+*h > to->height) *h=to->height - *y;
}
void rfbScaledScreenUpdateRect(rfbScreenInfoPtr screen, rfbScreenInfoPtr ptr, int x0, int y0, int w0, int h0)
{
int x,y,w,v,z;
int x1, y1, w1, h1;
int bitsPerPixel, bytesPerPixel, bytesPerLine, areaX, areaY, area2;
unsigned char *srcptr, *dstptr;
/* Nothing to do!!! */
if (screen==ptr) return;
x1 = x0;
y1 = y0;
w1 = w0;
h1 = h0;
rfbScaledCorrection(screen, ptr, &x1, &y1, &w1, &h1, "rfbScaledScreenUpdateRect");
x0 = ScaleX(ptr, screen, x1);
y0 = ScaleY(ptr, screen, y1);
w0 = ScaleX(ptr, screen, w1);
h0 = ScaleY(ptr, screen, h1);
bitsPerPixel = screen->bitsPerPixel;
bytesPerPixel = bitsPerPixel / 8;
bytesPerLine = w1 * bytesPerPixel;
srcptr = (unsigned char *)(screen->frameBuffer +
(y0 * screen->paddedWidthInBytes + x0 * bytesPerPixel));
dstptr = (unsigned char *)(ptr->frameBuffer +
( y1 * ptr->paddedWidthInBytes + x1 * bytesPerPixel));
/* The area of the source framebuffer for each destination pixel */
areaX = ScaleX(ptr,screen,1);
areaY = ScaleY(ptr,screen,1);
area2 = areaX*areaY;
/* Ensure that we do not go out of bounds */
if ((x1+w1) > (ptr->width))
{
if (x1==0) w1=ptr->width; else x1 = ptr->width - w1;
}
if ((y1+h1) > (ptr->height))
{
if (y1==0) h1=ptr->height; else y1 = ptr->height - h1;
}
/*
* rfbLog("rfbScaledScreenUpdateRect(%dXx%dY-%dWx%dH -> %dXx%dY-%dWx%dH <%dx%d>) {%dWx%dH -> %dWx%dH} 0x%p\n",
* x0, y0, w0, h0, x1, y1, w1, h1, areaX, areaY,
* screen->width, screen->height, ptr->width, ptr->height, ptr->frameBuffer);
*/
if (screen->serverFormat.trueColour) { /* Blend neighbouring pixels together */
unsigned char *srcptr2;
unsigned long pixel_value, red, green, blue;
unsigned int redShift = screen->serverFormat.redShift;
unsigned int greenShift = screen->serverFormat.greenShift;
unsigned int blueShift = screen->serverFormat.blueShift;
unsigned long redMax = screen->serverFormat.redMax;
unsigned long greenMax = screen->serverFormat.greenMax;
unsigned long blueMax = screen->serverFormat.blueMax;
/* for each *destination* pixel... */
for (y = 0; y < h1; y++) {
for (x = 0; x < w1; x++) {
red = green = blue = 0;
/* Get the totals for rgb from the source grid... */
for (w = 0; w < areaX; w++) {
for (v = 0; v < areaY; v++) {
srcptr2 = &srcptr[(((x * areaX) + w) * bytesPerPixel) +
(v * screen->paddedWidthInBytes)];
pixel_value = 0;
switch (bytesPerPixel) {
case 4: pixel_value = *((unsigned int *)srcptr2); break;
case 2: pixel_value = *((unsigned short *)srcptr2); break;
case 1: pixel_value = *((unsigned char *)srcptr2); break;
default:
/* fixme: endianess problem? */
for (z = 0; z < bytesPerPixel; z++)
pixel_value += (srcptr2[z] << (8 * z));
break;
}
/*
srcptr2 += bytesPerPixel;
*/
red += ((pixel_value >> redShift) & redMax);
green += ((pixel_value >> greenShift) & greenMax);
blue += ((pixel_value >> blueShift) & blueMax);
}
}
/* We now have a total for all of the colors, find the average! */
red /= area2;
green /= area2;
blue /= area2;
/* Stuff the new value back into memory */
pixel_value = ((red & redMax) << redShift) | ((green & greenMax) << greenShift) | ((blue & blueMax) << blueShift);
switch (bytesPerPixel) {
case 4: *((unsigned int *)dstptr) = (unsigned int) pixel_value; break;
case 2: *((unsigned short *)dstptr) = (unsigned short) pixel_value; break;
case 1: *((unsigned char *)dstptr) = (unsigned char) pixel_value; break;
default:
/* fixme: endianess problem? */
for (z = 0; z < bytesPerPixel; z++)
dstptr[z]=(pixel_value >> (8 * z)) & 0xff;
break;
}
dstptr += bytesPerPixel;
}
srcptr += (screen->paddedWidthInBytes * areaY);
dstptr += (ptr->paddedWidthInBytes - bytesPerLine);
}
} else
{ /* Not truecolour, so we can't blend. Just use the top-left pixel instead */
for (y = y1; y < (y1+h1); y++) {
for (x = x1; x < (x1+w1); x++)
memcpy (&ptr->frameBuffer[(y *ptr->paddedWidthInBytes) + (x * bytesPerPixel)],
&screen->frameBuffer[(y * areaY * screen->paddedWidthInBytes) + (x *areaX * bytesPerPixel)], bytesPerPixel);
}
}
}
void rfbScaledScreenUpdate(rfbScreenInfoPtr screen, int x1, int y1, int x2, int y2)
{
/* ok, now the task is to update each and every scaled version of the framebuffer
* and we only have to do this for this specific changed rectangle!
*/
rfbScreenInfoPtr ptr;
int count=0;
/* We don't point to cl->screen as it is the original */
for (ptr=screen->scaledScreenNext;ptr!=NULL;ptr=ptr->scaledScreenNext)
{
/* Only update if it has active clients... */
if (ptr->scaledScreenRefCount>0)
{
rfbScaledScreenUpdateRect(screen, ptr, x1, y1, x2-x1, y2-y1);
count++;
}
}
}
/* Create a new scaled version of the framebuffer */
rfbScreenInfoPtr rfbScaledScreenAllocate(rfbClientPtr cl, int width, int height)
{
rfbScreenInfoPtr ptr;
ptr = malloc(sizeof(rfbScreenInfo));
if (ptr!=NULL)
{
/* copy *everything* (we don't use most of it, but just in case) */
memcpy(ptr, cl->screen, sizeof(rfbScreenInfo));
ptr->width = width;
ptr->height = height;
ptr->paddedWidthInBytes = (ptr->bitsPerPixel/8)*ptr->width;
/* Need to by multiples of 4 for Sparc systems */
ptr->paddedWidthInBytes += (ptr->paddedWidthInBytes % 4);
/* Reset the reference count to 0! */
ptr->scaledScreenRefCount = 0;
ptr->sizeInBytes = ptr->paddedWidthInBytes * ptr->height;
ptr->serverFormat = cl->screen->serverFormat;
ptr->frameBuffer = malloc(ptr->sizeInBytes);
if (ptr->frameBuffer!=NULL)
{
/* Reset to a known condition: scale the entire framebuffer */
rfbScaledScreenUpdateRect(cl->screen, ptr, 0, 0, cl->screen->width, cl->screen->height);
/* Now, insert into the chain */
LOCK(cl->updateMutex);
ptr->scaledScreenNext = cl->screen->scaledScreenNext;
cl->screen->scaledScreenNext = ptr;
UNLOCK(cl->updateMutex);
}
else
{
/* Failed to malloc the new frameBuffer, cleanup */
free(ptr);
ptr=NULL;
}
}
return ptr;
}
/* Find an active scaled version of the framebuffer
* TODO: implement a refcount per scaled screen to prevent
* unreferenced scaled screens from hanging around
*/
rfbScreenInfoPtr rfbScalingFind(rfbClientPtr cl, int width, int height)
{
rfbScreenInfoPtr ptr;
/* include the original in the search (ie: fine 1:1 scaled version of the frameBuffer) */
for (ptr=cl->screen; ptr!=NULL; ptr=ptr->scaledScreenNext)
{
if ((ptr->width==width) && (ptr->height==height))
return ptr;
}
return NULL;
}
/* Future needs "scale to 320x240, as that's the client's screen size */
void rfbScalingSetup(rfbClientPtr cl, int width, int height)
{
rfbScreenInfoPtr ptr;
ptr = rfbScalingFind(cl,width,height);
if (ptr==NULL)
ptr = rfbScaledScreenAllocate(cl,width,height);
/* Now, there is a new screen available (if ptr is not NULL) */
if (ptr!=NULL)
{
/* Update it! */
if (ptr->scaledScreenRefCount<1)
rfbScaledScreenUpdateRect(cl->screen, ptr, 0, 0, cl->screen->width, cl->screen->height);
/*
* rfbLog("Taking one from %dx%d-%d and adding it to %dx%d-%d\n",
* cl->scaledScreen->width, cl->scaledScreen->height,
* cl->scaledScreen->scaledScreenRefCount,
* ptr->width, ptr->height, ptr->scaledScreenRefCount);
*/
LOCK(cl->updateMutex);
cl->scaledScreen->scaledScreenRefCount--;
ptr->scaledScreenRefCount++;
cl->scaledScreen=ptr;
cl->newFBSizePending = TRUE;
UNLOCK(cl->updateMutex);
rfbLog("Scaling to %dx%d (refcount=%d)\n",width,height,ptr->scaledScreenRefCount);
}
else
rfbLog("Scaling to %dx%d failed, leaving things alone\n",width,height);
}
int rfbSendNewScaleSize(rfbClientPtr cl)
{
/* if the client supports newFBsize Encoding, use it */
if (cl->useNewFBSize && cl->newFBSizePending)
return FALSE;
LOCK(cl->updateMutex);
cl->newFBSizePending = FALSE;
UNLOCK(cl->updateMutex);
if (cl->PalmVNC==TRUE)
{
rfbPalmVNCReSizeFrameBufferMsg pmsg;
pmsg.type = rfbPalmVNCReSizeFrameBuffer;
pmsg.pad1 = 0;
pmsg.desktop_w = Swap16IfLE(cl->screen->width);
pmsg.desktop_h = Swap16IfLE(cl->screen->height);
pmsg.buffer_w = Swap16IfLE(cl->scaledScreen->width);
pmsg.buffer_h = Swap16IfLE(cl->scaledScreen->height);
pmsg.pad2 = 0;
rfbLog("Sending a response to a PalmVNC style frameuffer resize event (%dx%d)\n", cl->scaledScreen->width, cl->scaledScreen->height);
if (rfbWriteExact(cl, (char *)&pmsg, sz_rfbPalmVNCReSizeFrameBufferMsg) < 0) {
rfbLogPerror("rfbNewClient: write");
rfbCloseClient(cl);
rfbClientConnectionGone(cl);
return FALSE;
}
}
else
{
rfbResizeFrameBufferMsg rmsg;
rmsg.type = rfbResizeFrameBuffer;
rmsg.pad1=0;
rmsg.framebufferWidth = Swap16IfLE(cl->scaledScreen->width);
rmsg.framebufferHeigth = Swap16IfLE(cl->scaledScreen->height);
rfbLog("Sending a response to a UltraVNC style frameuffer resize event (%dx%d)\n", cl->scaledScreen->width, cl->scaledScreen->height);
if (rfbWriteExact(cl, (char *)&rmsg, sz_rfbResizeFrameBufferMsg) < 0) {
rfbLogPerror("rfbNewClient: write");
rfbCloseClient(cl);
rfbClientConnectionGone(cl);
return FALSE;
}
}
return TRUE;
}
/****************************/

View File

@ -21,7 +21,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
@ -36,10 +36,11 @@
#include <assert.h>
#include <errno.h>
//android log
#include <android/log.h>
#include <android/log.h>
/* libvncserver */
#include "rfb/rfb.h"
#include "libvncserver/scale.h"
#include "rfb/keysym.h"
#include "suinput.h"
@ -58,47 +59,44 @@
static char VNC_PASSWORD[256] = "";
static struct fb_var_screeninfo scrinfo;
static struct fb_fix_screeninfo fscrinfo;
static int fbfd = -1;
static int inputfd = -1;
static unsigned int *fbmmap = MAP_FAILED;
static unsigned int *vncbuf;
static unsigned int *cmpbuf;
static unsigned int *cmpbuf_rot;
/* Android already has 5900 bound natively. */
#define VNC_PORT 5901
static rfbScreenInfoPtr vncscr;
int xmin, xmax;
int ymin, ymax;
int idle=0,standby=0;
int change=0;
static int rotation=0;
int idle=0,standby=0,change=0;
static int rotation=0,scaling=0;
/*****************************************************************************/
static void (*update_screen)(void)=NULL;
static void keyevent(rfbBool down, rfbKeySym key, rfbClientPtr cl);
static void ptrevent(int buttonMask, int x, int y, rfbClientPtr cl);
static void rotate();
/*****************************************************************************/
void update_fb_info()
{
void update_fb_info()
{
if (ioctl(fbfd, FBIOGET_VSCREENINFO, &scrinfo) != 0)
{
__android_log_print(ANDROID_LOG_INFO,"VNC","ioctl error\n");
exit(EXIT_FAILURE);
}
}
}
}
#define OUT 32
#include "update_screen.c"
#define OUT 8
#include "update_screen.c"
#define OUT 8
#include "update_screen.c"
#define OUT 16
#include "update_screen.c"
@ -116,6 +114,12 @@ static void init_fb(void)
update_fb_info();
if (ioctl(fbfd, FBIOGET_FSCREENINFO, &fscrinfo) != 0)
{
__android_log_print(ANDROID_LOG_INFO,"VNC","ioctl error\n");
exit(EXIT_FAILURE);
}
pixels = scrinfo.xres * scrinfo.yres;
bytespp = scrinfo.bits_per_pixel /CHAR_BIT;
@ -125,8 +129,9 @@ static void init_fb(void)
(int)scrinfo.xoffset, (int)scrinfo.yoffset,
(int)scrinfo.bits_per_pixel);
off_t o=0;
// off_t o=0;
//now handled with double buffer detection
// if (scrinfo.xres==480 && scrinfo.yres==854 && scrinfo.xres_virtual==480
// && scrinfo.yres_virtual==854 && scrinfo.xoffset==448 && scrinfo.yoffset==1710)
/*if (scrinfo.bits_per_pixel==32)
@ -141,11 +146,11 @@ static void init_fb(void)
} */
__android_log_print(ANDROID_LOG_INFO,"VNC","buffer offset=%d * %d\n",o,sysconf(_SC_PAGE_SIZE));
fbmmap = mmap(NULL, (scrinfo.xres_virtual*scrinfo.yres_virtual)* bytespp , PROT_READ|PROT_READ , MAP_PRIVATE , fbfd, o*sysconf(_SC_PAGE_SIZE));
printf("aki %d\n",o*sysconf(_SC_PAGE_SIZE));
// __android_log_print(ANDROID_LOG_INFO,"VNC","buffer offset=%d * %d\n",o,(int)sysconf(_SC_PAGE_SIZE));
//
// fbmmap = mmap(NULL, (scrinfo.xres_virtual*scrinfo.yres_virtual)* bytespp , PROT_READ|PROT_READ , MAP_PRIVATE , fbfd, o*sysconf(_SC_PAGE_SIZE));
fbmmap = mmap(NULL, (scrinfo.xres_virtual*scrinfo.yres_virtual)* bytespp , PROT_READ|PROT_READ , MAP_PRIVATE , fbfd, 0);
if (fbmmap == MAP_FAILED)
{
@ -187,32 +192,37 @@ static void cleanup_kbd()
}
/*****************************************************************************/
/* response is cl->authChallenge vncEncrypted with passwd */
static rfbBool rfbDefaultPasswordCheck(rfbClientPtr cl,const char* response,int len)
static rfbNewClientHookPtr clientHook(rfbClientPtr cl)
{
if (strcmp(response,VNC_PASSWORD)==0)
return (TRUE);
return(FALSE);
if (scaling!=0 && scaling!=100)
{
rfbScalingSetup(cl, vncscr->width*scaling/100.0, vncscr->height*scaling/100.0);
}
return RFB_CLIENT_ACCEPT;
}
static void init_fb_server(int argc, char **argv)
{
{
__android_log_print(ANDROID_LOG_INFO,"VNC","Initializing server...\n");
vncbuf = calloc(scrinfo.xres * scrinfo.yres, scrinfo.bits_per_pixel/CHAR_BIT);
cmpbuf = calloc(scrinfo.xres * scrinfo.yres, scrinfo.bits_per_pixel/CHAR_BIT);
cmpbuf_rot = calloc(scrinfo.xres * scrinfo.yres, scrinfo.bits_per_pixel/CHAR_BIT);
assert(vncbuf != NULL);
assert(cmpbuf != NULL);
if (rotation==0 || rotation==180)
vncscr = rfbGetScreen(&argc, argv, scrinfo.xres, scrinfo.yres, 0 /* not used */ , 3, scrinfo.bits_per_pixel/CHAR_BIT);
else
vncscr = rfbGetScreen(&argc, argv, scrinfo.yres, scrinfo.xres, 0 /* not used */ , 3, scrinfo.bits_per_pixel/CHAR_BIT);
assert(vncscr != NULL);
vncscr->desktopName = "Android";
@ -221,6 +231,7 @@ static void init_fb_server(int argc, char **argv)
vncscr->kbdAddEvent = keyevent;
vncscr->ptrAddEvent = ptrevent;
vncscr->newClientHook = clientHook;
if (strcmp(VNC_PASSWORD,"")!=0)
{
@ -232,9 +243,9 @@ static void init_fb_server(int argc, char **argv)
}
#ifdef DONATE_VERSION
vncscr->httpDir="/data/data/org.onaips.vnc/";
#else
vncscr->httpDir="/data/data/org.onaips.vnc_donate/";
#else
vncscr->httpDir="/data/data/org.onaips.vnc/";
#endif
vncscr->serverFormat.redShift=scrinfo.red.offset;
vncscr->serverFormat.greenShift=scrinfo.green.offset;
@ -244,7 +255,7 @@ static void init_fb_server(int argc, char **argv)
vncscr->serverFormat.greenMax=((1<<scrinfo.green.length)-1);
vncscr->serverFormat.blueMax=((1<<scrinfo.blue.length)-1);
vncscr->alwaysShared = TRUE;
vncscr->alwaysShared = TRUE;
vncscr->handleEventsEagerly = TRUE;
vncscr->deferUpdateTime = 5;
@ -408,7 +419,7 @@ static int keysym2scancode(rfbBool down, rfbKeySym c, rfbClientPtr cl, int *sh,
return 30; //A with acute -> a with ring above
case 50089:
case 233: (*alt)=1; return 18; //e with acute
case 50057:
case 50057:
case 201:(*sh)=1; (*alt)=1; return 18; //E with acute
case 50093:
case 0xffbf: (*alt)=1;
@ -566,9 +577,6 @@ inline void transform_touch_coordinates(int *x, int *y)
static void ptrevent(int buttonMask, int x, int y, rfbClientPtr cl)
{
struct input_event ev;
static int leftClicked=0,rightClicked=0,middleClicked=0;
transform_touch_coordinates(&x,&y);
@ -635,7 +643,7 @@ void sigproc()
exit(0); /* normal exit status */
}
static void rotate()
{
@ -657,19 +665,19 @@ static void rotate()
vncscr->serverFormat.blueMax=((1<<scrinfo.blue.length)-1);
rfbMarkRectAsModified(vncscr, 0, 0, vncscr->width, vncscr->height);
}
void print_usage(char **argv)
{
__android_log_print(ANDROID_LOG_INFO,"VNC","%s [-p password] [-h]\n"
__android_log_print(ANDROID_LOG_INFO,"VNC","androidvncserver [-p password] [-h]\n"
"-p password: Password to access server\n"
"-r rotation: Screen rotation (degrees) (0,90,180,270)\n"
"-s screen scale: percentage (20,30,50,100,150)\n"
"-h : print this help\n");
}
int main(int argc, char **argv)
{
signal(SIGINT, sigproc);//pipe signals
@ -688,18 +696,28 @@ int main(int argc, char **argv)
{
case 'h':
print_usage(argv);
exit(0);
exit(0);
break;
case 'p':
i++;
i++;
strcpy(VNC_PASSWORD,argv[i]);
break;
case 'r':
i++;
i++;
r=atoi(argv[i]);
if (r==0 || r==90 || r==180 || r==270)
rotation=r;
__android_log_print(ANDROID_LOG_INFO,"VNC","rotating to %d degrees\n",rotation);
break;
case 's':
i++;
r=atoi(argv[i]);
if (r>=1 && r <= 150)
scaling=r;
else
scaling=100;
__android_log_print(ANDROID_LOG_INFO,"VNC","scaling to %d percent\n",scaling);
break;
}
}
i++;

752
jni/vnc/fbvncserver.c~ Executable file
View File

@ -0,0 +1,752 @@
/*
* $Id$
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.*
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* This project is an adaptation of the original fbvncserver for the iPAQ
* and Zaurus.
*
* This is a modification from letsgoustc source, to handle uinput events and more
*
*/
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/sysmacros.h> /* For makedev() */
#include <fcntl.h>
#include <linux/fb.h>
#include <linux/input.h>
#include <assert.h>
#include <errno.h>
//android log
#include <android/log.h>
/* libvncserver */
#include "rfb/rfb.h"
#include "libvncserver/scale.h"
#include "rfb/keysym.h"
#include "suinput.h"
#define CONCAT2(a,b) a##b
#define CONCAT2E(a,b) CONCAT2(a,b)
#define CONCAT3(a,b,c) a##b##c
#define CONCAT3E(a,b,c) CONCAT3(a,b,c)
#define BUS_VIRTUAL 0x06
/*****************************************************************************/
/* Android does not use /dev/fb0. */
#define FB_DEVICE "/dev/graphics/fb0"
static char VNC_PASSWORD[256] = "";
static struct fb_var_screeninfo scrinfo;
static struct fb_fix_screeninfo fscrinfo;
static int fbfd = -1;
static int inputfd = -1;
static unsigned int *fbmmap = MAP_FAILED;
static unsigned int *vncbuf;
static unsigned int *cmpbuf;
/* Android already has 5900 bound natively. */
#define VNC_PORT 5901
static rfbScreenInfoPtr vncscr;
int idle=0,standby=0,change=0;
static int rotation=0,scaling=0;
/*****************************************************************************/
static void (*update_screen)(void)=NULL;
static void keyevent(rfbBool down, rfbKeySym key, rfbClientPtr cl);
static void ptrevent(int buttonMask, int x, int y, rfbClientPtr cl);
static void rotate();
/*****************************************************************************/
void update_fb_info()
{
if (ioctl(fbfd, FBIOGET_VSCREENINFO, &scrinfo) != 0)
{
__android_log_print(ANDROID_LOG_INFO,"VNC","ioctl error\n");
exit(EXIT_FAILURE);
}
}
#define OUT 32
#include "update_screen.c"
#define OUT 8
#include "update_screen.c"
#define OUT 16
#include "update_screen.c"
static void init_fb(void)
{
size_t pixels;
size_t bytespp;
if ((fbfd = open(FB_DEVICE, O_RDWR)) == -1)
{
__android_log_print(ANDROID_LOG_INFO,"VNC","cannot open fb device %s\n", FB_DEVICE);
return;
// exit(EXIT_FAILURE);
}
update_fb_info();
if (ioctl(fbfd, FBIOGET_FSCREENINFO, &fscrinfo) != 0)
{
__android_log_print(ANDROID_LOG_INFO,"VNC","ioctl error\n");
exit(EXIT_FAILURE);
}
pixels = scrinfo.xres * scrinfo.yres;
bytespp = scrinfo.bits_per_pixel /CHAR_BIT;
__android_log_print(ANDROID_LOG_INFO,"VNC", "xres=%d, yres=%d, xresv=%d, yresv=%d, xoffs=%d, yoffs=%d, bpp=%d\n",
(int)scrinfo.xres, (int)scrinfo.yres,
(int)scrinfo.xres_virtual, (int)scrinfo.yres_virtual,
(int)scrinfo.xoffset, (int)scrinfo.yoffset,
(int)scrinfo.bits_per_pixel);
// off_t o=0;
//now handled with double buffer detection
// if (scrinfo.xres==480 && scrinfo.yres==854 && scrinfo.xres_virtual==480
// && scrinfo.yres_virtual==854 && scrinfo.xoffset==448 && scrinfo.yoffset==1710)
/*if (scrinfo.bits_per_pixel==32)
// tweak for motorola droid
{
int off=scrinfo.xres*scrinfo.yres*scrinfo.bits_per_pixel/CHAR_BIT;
int resto= off % sysconf(_SC_PAGE_SIZE);
o= off / sysconf(_SC_PAGE_SIZE);
if (resto)
o+=1;
} */
// __android_log_print(ANDROID_LOG_INFO,"VNC","buffer offset=%d * %d\n",o,(int)sysconf(_SC_PAGE_SIZE));
//
// fbmmap = mmap(NULL, (scrinfo.xres_virtual*scrinfo.yres_virtual)* bytespp , PROT_READ|PROT_READ , MAP_PRIVATE , fbfd, o*sysconf(_SC_PAGE_SIZE));
fbmmap = mmap(NULL, (scrinfo.xres_virtual*scrinfo.yres_virtual)* bytespp , PROT_READ|PROT_READ , MAP_PRIVATE , fbfd, 0);
if (fbmmap == MAP_FAILED)
{
__android_log_print(ANDROID_LOG_INFO,"VNC","mmap failed\n");
exit(EXIT_FAILURE);
}
}
static void cleanup_fb(void)
{
if(fbfd != -1)
{
close(fbfd);
}
}
static void init_input()
{
struct input_id id = {
BUS_VIRTUAL, /* Bus type. */
1, /* Vendor id. */
1, /* Product id. */
1 /* Version id. */
};
if((inputfd = suinput_open("qwerty", &id)) == -1)
{
__android_log_print(ANDROID_LOG_INFO,"VNC","cannot create virtual kbd device.\n");
// exit(EXIT_FAILURE); do not exit, so we still can see the framebuffer
}
}
static void cleanup_kbd()
{
if(inputfd != -1)
{
suinput_close(inputfd);
}
}
/*****************************************************************************/
static rfbNewClientHookPtr clientHook(rfbClientPtr cl)
{
if (scaling!=0 && scaling!=100)
{
rfbScalingSetup(cl, vncscr->width*scaling/100.0, vncscr->height*scaling/100.0);
rfbSendNewScaleSize(cl);
}
return RFB_CLIENT_ACCEPT;
}
static void init_fb_server(int argc, char **argv)
{
__android_log_print(ANDROID_LOG_INFO,"VNC","Initializing server...\n");
vncbuf = calloc(scrinfo.xres * scrinfo.yres, scrinfo.bits_per_pixel/CHAR_BIT);
cmpbuf = calloc(scrinfo.xres * scrinfo.yres, scrinfo.bits_per_pixel/CHAR_BIT);
assert(vncbuf != NULL);
assert(cmpbuf != NULL);
if (rotation==0 || rotation==180)
vncscr = rfbGetScreen(&argc, argv, scrinfo.xres, scrinfo.yres, 0 /* not used */ , 3, scrinfo.bits_per_pixel/CHAR_BIT);
else
vncscr = rfbGetScreen(&argc, argv, scrinfo.yres, scrinfo.xres, 0 /* not used */ , 3, scrinfo.bits_per_pixel/CHAR_BIT);
assert(vncscr != NULL);
vncscr->desktopName = "Android";
vncscr->frameBuffer =(char *)vncbuf;
vncscr->port = VNC_PORT;
vncscr->kbdAddEvent = keyevent;
vncscr->ptrAddEvent = ptrevent;
vncscr->newClientHook = clientHook;
if (strcmp(VNC_PASSWORD,"")!=0)
{
char **passwords = (char **)malloc(2 * sizeof(char **));
passwords[0] = VNC_PASSWORD;
passwords[1] = NULL;
vncscr->authPasswdData = passwords;
vncscr->passwordCheck = rfbCheckPasswordByList;
}
#ifdef DONATE_VERSION
vncscr->httpDir="/data/data/org.onaips.vnc_donate/";
#else
vncscr->httpDir="/data/data/org.onaips.vnc/";
#endif
vncscr->serverFormat.redShift=scrinfo.red.offset;
vncscr->serverFormat.greenShift=scrinfo.green.offset;
vncscr->serverFormat.blueShift=scrinfo.blue.offset;
vncscr->serverFormat.redMax=((1<<scrinfo.red.length)-1);
vncscr->serverFormat.greenMax=((1<<scrinfo.green.length)-1);
vncscr->serverFormat.blueMax=((1<<scrinfo.blue.length)-1);
vncscr->alwaysShared = TRUE;
vncscr->handleEventsEagerly = TRUE;
vncscr->deferUpdateTime = 5;
rfbInitServer(vncscr);
//assign update_screen depending on bpp
if (vncscr->serverFormat.bitsPerPixel == 32)
{
__android_log_print(ANDROID_LOG_INFO,"VNC","I'm on update_screen_32");
update_screen=&CONCAT2E(update_screen_,32);
}
else if (vncscr->serverFormat.bitsPerPixel == 16)
{
__android_log_print(ANDROID_LOG_INFO,"VNC","I'm on update_screen_16");
update_screen=&CONCAT2E(update_screen_,16);
}
else if (vncscr->serverFormat.bitsPerPixel == 8)
{
__android_log_print(ANDROID_LOG_INFO,"VNC","I'm on update_screen_8");
update_screen=&CONCAT2E(update_screen_,8);
}
else {
rfbErr("Unsupported pixel depth: %d\n",
vncscr->serverFormat.bitsPerPixel);
return;
}
/* Mark as dirty since we haven't sent any updates at all yet. */
rfbMarkRectAsModified(vncscr, 0, 0, scrinfo.xres, scrinfo.yres);
rfbProcessEvents(vncscr, 100000);
}
static int keysym2scancode(rfbBool down, rfbKeySym c, rfbClientPtr cl, int *sh, int *alt);
static void keyevent(rfbBool down, rfbKeySym key, rfbClientPtr cl)
{
int code;
__android_log_print(ANDROID_LOG_INFO,"VNC","Got keysym: %04x (down=%d)\n", (unsigned int)key, (int)down);
int sh = 0;
int alt = 0;
if ((code = keysym2scancode(down, key, cl,&sh,&alt)))
{
int ret;
if (key && down)
{
if (sh) suinput_press(inputfd, 42); //left shift
if (alt) suinput_press(inputfd, 56); //left alt
ret=suinput_press(inputfd,code);
ret=suinput_release(inputfd,code);
if (alt) suinput_release(inputfd, 56); //left alt
if (sh) suinput_release(inputfd, 42); //left shift
}
else
;//ret=suinput_release(inputfd,code);
//__android_log_print(ANDROID_LOG_INFO,"VNC","injectKey (%d, %d) ret=%d\n", code , down,ret);
}
}
// keyboard code modified from remote input by http://www.math.bme.hu/~morap/RemoteInput/
// q,w,e,r,t,y,u,i,o,p,a,s,d,f,g,h,j,k,l,z,x,c,v,b,n,m
int qwerty[] = {30,48,46,32,18,33,34,35,23,36,37,38,50,49,24,25,16,19,31,20,22,47,17,45,21,44};
// ,!,",#,$,%,&,',(,),*,+,,,-,.,/
int spec1[] = {57,2,40,4,5,6,8,40,10,11,9,13,51,12,52,52};
int spec1sh[] = {0,1,1,1,1,1,1,0,1,1,1,1,0,0,0,1};
// :,;,<,=,>,?,@
int spec2[] = {39,39,227,13,228,53,215};
int spec2sh[] = {1,0,1,1,1,1,0};
// [,\,],^,_,`
int spec3[] = {26,43,27,7,12,399};
int spec3sh[] = {0,0,0,1,1,0};
// {,|,},~
int spec4[] = {26,43,27,215,14};
int spec4sh[] = {1,1,1,1,0};
static int keysym2scancode(rfbBool down, rfbKeySym c, rfbClientPtr cl, int *sh, int *alt)
{
int real=1;
if ('a' <= c && c <= 'z')
return qwerty[c-'a'];
if ('A' <= c && c <= 'Z')
{
(*sh)=1;
return qwerty[c-'A'];
}
if ('1' <= c && c <= '9')
return c-'1'+2;
if (c == '0')
return 11;
if (32 <= c && c <= 47)
{
(*sh) = spec1sh[c-32];
return spec1[c-32];
}
if (58 <= c && c <= 64)
{
(*sh) = spec2sh[c-58];
return spec2[c-58];
}
if (91 <= c && c <= 96)
{
(*sh) = spec3sh[c-91];
return spec3[c-91];
}
if (123 <= c && c <= 127)
{
(*sh) = spec4sh[c-123];
return spec4[c-123];
}
switch(c)
{
case 0xff08: return 14;// backspace
case 0xff09: return 15;// tab
case 1: (*alt)=1; return 34;// ctrl+a
case 3: (*alt)=1; return 46;// ctrl+c
case 4: (*alt)=1; return 32;// ctrl+d
case 18: (*alt)=1; return 31;// ctrl+r
case 0xff0D: return 28;// enter
case 0xff1B: return 158;// esc -> back
case 0xFF51: return 105;// left -> DPAD_LEFT
case 0xFF53: return 106;// right -> DPAD_RIGHT
case 0xFF54: return 108;// down -> DPAD_DOWN
case 0xFF52: return 103;// up -> DPAD_UP
// case 360: return 232;// end -> DPAD_CENTER (ball click)
case 0xff50: return KEY_HOME;// home
case 0xFFC8: rfbShutdownServer(cl->screen,TRUE); return 0; //F11 disconnect
case 0xFFC9:
__android_log_print(ANDROID_LOG_INFO,"VNC","F12 closing...");
exit(0); //F10 closes daemon
break;
case 0xffc1: down?rotate():0; return 0; // F4 rotate
case 0xffff: return 158;// del -> back
case 0xff55: return 229;// PgUp -> menu
case 0xffcf: return 127;// F2 -> search
case 0xff56: return 61;// PgUp -> call
case 0xff57: return 107;// End -> endcall
case 0xffc2: return 211;// F5 -> focus
case 0xffc3: return 212;// F6 -> camera
case 0xffc4: return 150;// F7 -> explorer
case 0xffc5: return 155;// F8 -> envelope
case 50081:
case 225: (*alt)=1;
if (real) return 48; //a with acute
return 30; //a with acute -> a with ring above
case 50049:
case 193:(*sh)=1; (*alt)=1;
if (real) return 48; //A with acute
return 30; //A with acute -> a with ring above
case 50089:
case 233: (*alt)=1; return 18; //e with acute
case 50057:
case 201:(*sh)=1; (*alt)=1; return 18; //E with acute
case 50093:
case 0xffbf: (*alt)=1;
if (real) return 36; //i with acute
return 23; //i with acute -> i with grave
case 50061:
case 205: (*sh)=1; (*alt)=1;
if (real) return 36; //I with acute
return 23; //I with acute -> i with grave
case 50099:
case 243:(*alt)=1;
if (real) return 16; //o with acute
return 24; //o with acute -> o with grave
case 50067:
case 211:(*sh)=1; (*alt)=1;
if (real) return 16; //O with acute
return 24; //O with acute -> o with grave
case 50102:
case 246: (*alt)=1; return 25; //o with diaeresis
case 50070:
case 214: (*sh)=1; (*alt)=1; return 25; //O with diaeresis
case 50577:
case 245:(*alt)=1;
if (real) return 19; //Hungarian o
return 25; //Hungarian o -> o with diaeresis
case 50576:
case 213: (*sh)=1; (*alt)=1;
if (real) return 19; //Hungarian O
return 25; //Hungarian O -> O with diaeresis
case 50106:
// case 0xffbe: (*alt)=1;
// if (real) return 17; //u with acute
// return 22; //u with acute -> u with grave
case 50074:
case 218: (*sh)=1; (*alt)=1;
if (real) return 17; //U with acute
return 22; //U with acute -> u with grave
case 50108:
case 252: (*alt)=1; return 47; //u with diaeresis
case 50076:
case 220:(*sh)=1; (*alt)=1; return 47; //U with diaeresis
case 50609:
case 251: (*alt)=1;
if (real) return 45; //Hungarian u
return 47; //Hungarian u -> u with diaeresis
case 50608:
case 219: (*sh)=1; (*alt)=1;
if (real) return 45; //Hungarian U
return 47; //Hungarian U -> U with diaeresis
}
return 0;
}
// old key code, i'll need it for some missing things
/*
static int _keysym2scancode(rfbBool down, rfbKeySym key, rfbClientPtr cl)
{
int scancode = 0;
int code = (int)key;
if (code>='0' && code<='9') {
scancode = (code & 0xF) - 1;
if (scancode<0) scancode += 10;
scancode += KEY_1;
} else if (code>=0xFF50 && code<=0xFF58) {
static const uint16_t map[] =
{ KEY_HOME, KEY_LEFT, KEY_UP, KEY_RIGHT, KEY_DOWN,
KEY_SOFT1, KEY_SOFT2, KEY_END, 0 };
scancode = map[code & 0xF];
} else if (code>=0xFFE1 && code<=0xFFEE) {
static const uint16_t map[] =
{ KEY_LEFTSHIFT, KEY_LEFTSHIFT,
KEY_COMPOSE, KEY_COMPOSE,
KEY_LEFTSHIFT, KEY_LEFTSHIFT,
0,0,
KEY_LEFTALT, KEY_RIGHTALT,
0, 0, 0, 0 };
scancode = map[code & 0xF];
} else if ((code>='A' && code<='Z') || (code>='a' && code<='z')) {
static const uint16_t map[] = {
KEY_A, KEY_B, KEY_C, KEY_D, KEY_E,
KEY_F, KEY_G, KEY_H, KEY_I, KEY_J,
KEY_K, KEY_L, KEY_M, KEY_N, KEY_O,
KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T,
KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z };
scancode = map[(code & 0x5F) - 'A'];
} else {
switch (code) {
case 0x0003: scancode = KEY_CENTER; break;
case 0x0020: scancode = KEY_SPACE; break;
case 0x0023: scancode = KEY_SHARP; break;
case 0x0033: scancode = KEY_SHARP; break;
case 0x002C: scancode = KEY_COMMA; break;
case 0x003C: scancode = KEY_COMMA; break;
case 0x002E: scancode = KEY_DOT; break;
case 0x003E: scancode = KEY_DOT; break;
case 0x002F: scancode = KEY_SLASH; break;
case 0x003F: scancode = KEY_SLASH; break;
case 0x0032: scancode = KEY_EMAIL; break;
case 0x0040: scancode = KEY_EMAIL; break;
case 0xFF08: scancode = KEY_BACKSPACE; break;
case 0xFF1B: scancode = KEY_BACK; break;
case 0xFF09: scancode = KEY_TAB; break;
case 0xFF0D: scancode = KEY_ENTER; break;
case 0xFE51: scancode = 399; break;
case 0x002A: scancode = KEY_STAR; break;
case 0xFFBE: scancode = KEY_F1; break; // F1
case 0xFFBF: scancode = KEY_F2; break; // F2
case 0xFFC0: scancode = KEY_F3; break; // F3
case 0xFFC1: down?rotate():0; break; // F4 rotate
case 0xFFC5: scancode = KEY_F4; break; // F8
case 0xFFC8: rfbShutdownServer(cl->screen,TRUE); break; // F11
}
}
return scancode;
}
*/
inline void transform_touch_coordinates(int *x, int *y)
{
int scale=4096.0;
int old_x=*x,old_y=*y;
if (rotation==0)
{
*x = old_x*scale/scrinfo.xres-scale/2.0;
*y = old_y*scale/scrinfo.yres-scale/2.0;
}
else if (rotation==90)
{
*x =old_y*scale/scrinfo.xres-scale/2.0;
*y = (scrinfo.yres - old_x)*scale/scrinfo.yres-scale/2.0;
}
else if (rotation==180)
{
*x =(scrinfo.xres - old_x)*scale/scrinfo.xres-scale/2.0;
*y =(scrinfo.yres - old_y)*scale/scrinfo.yres-scale/2.0;
}
else if (rotation==270)
{
*y =old_x*scale/scrinfo.yres-scale/2.0;
*x =(scrinfo.xres - old_y)*scale/scrinfo.xres-scale/2.0;
}
}
static void ptrevent(int buttonMask, int x, int y, rfbClientPtr cl)
{
static int leftClicked=0,rightClicked=0,middleClicked=0;
transform_touch_coordinates(&x,&y);
if((buttonMask & 1)&& leftClicked) {//left btn clicked and moving
static int i=0;
i=i+1;
if (i%10==1)//some tweak to not report every move event
{
suinput_write(inputfd, EV_ABS, ABS_X, x);
suinput_write(inputfd, EV_ABS, ABS_Y, y);
suinput_write(inputfd, EV_SYN, SYN_REPORT, 0);
}
}
else if (buttonMask & 1)//left btn clicked
{
leftClicked=1;
suinput_write(inputfd, EV_ABS, ABS_X, x);
suinput_write(inputfd, EV_ABS, ABS_Y, y);
suinput_write(inputfd,EV_KEY,BTN_TOUCH,1);
suinput_write(inputfd, EV_SYN, SYN_REPORT, 0);
}
else if (leftClicked)//left btn released
{
leftClicked=0;
suinput_write(inputfd, EV_ABS, ABS_X, x);
suinput_write(inputfd, EV_ABS, ABS_Y, y);
suinput_write(inputfd,EV_KEY,BTN_TOUCH,0);
suinput_write(inputfd, EV_SYN, SYN_REPORT, 0);
}
if (buttonMask & 4)//right btn clicked
{
rightClicked=1;
suinput_press(inputfd,158); //back key
}
else if (rightClicked)//right button released
{
rightClicked=0;
suinput_release(inputfd,158);
}
if (buttonMask & 2)//mid btn clicked
{
middleClicked=1;
suinput_press( inputfd,KEY_END);
}
else if (middleClicked)// mid btn released
{
middleClicked=0;
suinput_release( inputfd,KEY_END);
}
}
void sigproc()
{
__android_log_print(ANDROID_LOG_INFO,"VNC","Cleaning up...\n");
cleanup_fb();
cleanup_kbd();
exit(0); /* normal exit status */
}
static void rotate()
{
rotation+=90;
rotation=rotation%360;
if (rotation==90 || rotation==270)
rfbNewFramebuffer(vncscr,(char*)vncbuf, scrinfo.yres, scrinfo.xres,0 /* not used */ , 3, scrinfo.bits_per_pixel/CHAR_BIT);
else
rfbNewFramebuffer(vncscr,(char*)vncbuf, scrinfo.xres, scrinfo.yres,0 /* not used */ , 3, scrinfo.bits_per_pixel/CHAR_BIT);
vncscr->serverFormat.redShift=scrinfo.red.offset;
vncscr->serverFormat.greenShift=scrinfo.green.offset;
vncscr->serverFormat.blueShift=scrinfo.blue.offset;
vncscr->serverFormat.redMax=((1<<scrinfo.red.length)-1);
vncscr->serverFormat.greenMax=((1<<scrinfo.green.length)-1);
vncscr->serverFormat.blueMax=((1<<scrinfo.blue.length)-1);
rfbMarkRectAsModified(vncscr, 0, 0, vncscr->width, vncscr->height);
}
void print_usage(char **argv)
{
__android_log_print(ANDROID_LOG_INFO,"VNC","androidvncserver [-p password] [-h]\n"
"-p password: Password to access server\n"
"-r rotation: Screen rotation (degrees) (0,90,180,270)\n"
"-s screen scale: percentage (20,30,50,100,150)\n"
"-h : print this help\n");
}
int main(int argc, char **argv)
{
signal(SIGINT, sigproc);//pipe signals
signal(SIGKILL, sigproc);
signal(SIGILL, sigproc);
if(argc > 1)
{
int i=1;
int r;
while(i < argc)
{
if(*argv[i] == '-')
{
switch(*(argv[i] + 1))
{
case 'h':
print_usage(argv);
exit(0);
break;
case 'p':
i++;
strcpy(VNC_PASSWORD,argv[i]);
break;
case 'r':
i++;
r=atoi(argv[i]);
if (r==0 || r==90 || r==180 || r==270)
rotation=r;
__android_log_print(ANDROID_LOG_INFO,"VNC","rotating to %d degrees\n",rotation);
break;
case 's':
i++;
r=atoi(argv[i]);
if (r>=1 && r <= 150)
scaling=r;
else
scaling=100;
__android_log_print(ANDROID_LOG_INFO,"VNC","scaling to %d percent\n",scaling);
break;
}
}
i++;
}
}
__android_log_print(ANDROID_LOG_INFO,"VNC","Initializing framebuffer device " FB_DEVICE "...\n");
init_fb();
__android_log_print(ANDROID_LOG_INFO,"VNC","Initializing virtual keyboard and touch device...\n");
init_input();
__android_log_print(ANDROID_LOG_INFO,"VNC","Initializing VNC server:\n");
__android_log_print(ANDROID_LOG_INFO,"VNC"," width: %d\n", (int)scrinfo.xres);
__android_log_print(ANDROID_LOG_INFO,"VNC"," height: %d\n", (int)scrinfo.yres);
__android_log_print(ANDROID_LOG_INFO,"VNC"," bpp: %d\n", (int)scrinfo.bits_per_pixel);
__android_log_print(ANDROID_LOG_INFO,"VNC"," port: %d\n", (int)VNC_PORT);
init_fb_server(argc, argv);
while (1)
{
while (vncscr->clientHead == NULL)
rfbProcessEvents(vncscr, 100000);
update_screen();
}
__android_log_print(ANDROID_LOG_INFO,"VNC","Cleaning up...\n");
cleanup_fb();
cleanup_kbd();
}

View File

@ -5,14 +5,18 @@ void FUNCTION(void)
{
static int i,j,offset;
update_fb_info();
update_fb_info();
//detect active buffer
if (scrinfo.yoffset)
offset=scrinfo.xres*scrinfo.yres;
else
offset=0;
// // detect active buffer
// offset= (scrinfo.xoffset) * (scrinfo.bits_per_pixel/8) +
// (scrinfo.yoffset) * scrinfo.xres;
// if (scrinfo.yoffset)
// offset=scrinfo.xres*scrinfo.yoffset;
// else
// offset=0;
offset = scrinfo.xres * scrinfo.yoffset + scrinfo.xoffset * scrinfo.bits_per_pixel / CHAR_BIT;
OUT_T* a = (OUT_T*)cmpbuf;
OUT_T* b = (OUT_T*)fbmmap;
@ -61,10 +65,12 @@ void FUNCTION(void)
if (i<min_y)
min_y=i;
if (j>max_x)
max_x=j;
if (j<min_x)
min_x=j;
int h=scrinfo.yres-j;
if (h < min_x)
min_x=scrinfo.yres-j;
if (h > max_x)
max_x=scrinfo.yres-j;
if (idle)
idle=0;
@ -113,35 +119,39 @@ void FUNCTION(void)
if (i<min_y)
min_y=i;
if (j>max_x)
max_x=j;
if (j<min_x)
min_x=j;
int h=scrinfo.yres-j;
if (h < min_x)
min_x=scrinfo.yres-j;
if (h > max_x)
max_x=scrinfo.yres-j;
if (idle)
idle=0;
}
}
}
}
}
memcpy(vncbuf,a,vncscr->width*vncscr->height*scrinfo.bits_per_pixel/CHAR_BIT);
if (min_x!=9999 && min_y!=9999 && max_x!=-1 && max_y!=-1)
{
min_x--;
max_x++;
min_y--;
max_y++;
// min_x--;
max_x++;
// min_y--;
max_y++;
rfbMarkRectAsModified(vncscr, min_x, min_y, max_x, max_y);
rfbProcessEvents(vncscr, 10000);
}
}
// rfbMarkRectAsModified(vncscr, 0, 0, scrinfo.yres,scrinfo.xres);
// rfbProcessEvents(vncscr, 10000);
// rfbMarkRectAsModified(vncscr, 0, 0, scrinfo.yres,scrinfo.xres);
// rfbProcessEvents(vncscr, 10000);
if (idle)
{
@ -151,8 +161,8 @@ memcpy(vncbuf,a,vncscr->width*vncscr->height*scrinfo.bits_per_pixel/CHAR_BIT);
rfbProcessEvents(vncscr, 1000000);
else
rfbProcessEvents(vncscr, 100000);
// printf("standby %d xoff=%d yoff=%d\n",standby,scrinfo.xres,scrinfo.yres);
// __android_log_print(ANDROID_LOG_INFO,"VNC","standby %d xoff=%d yoff=%d\n",standby,scrinfo.xoffset,scrinfo.yoffset);
change=0;
}
else
@ -160,6 +170,6 @@ memcpy(vncbuf,a,vncscr->width*vncscr->height*scrinfo.bits_per_pixel/CHAR_BIT);
change=change+1;
standby=0;
rfbProcessEvents(vncscr, 100000);
// printf("change %d\tmin_x=%d max_x=%d min_y=%d max_y=%d xoff=%d yoff=%d\n",change,min_x,max_x,min_y,max_y,scrinfo.xoffset,scrinfo.yoffset);
// __android_log_print(ANDROID_LOG_INFO,"VNC","change %d\tmin_x=%d max_x=%d min_y=%d max_y=%d xoff=%d yoff=%d\n",change,min_x,max_x,min_y,max_y,scrinfo.xoffset,scrinfo.yoffset);
}
}

175
jni/vnc/update_screen.c~ Normal file
View File

@ -0,0 +1,175 @@
#define OUT_T CONCAT3E(uint,OUT,_t)
#define FUNCTION CONCAT2E(update_screen_,OUT)
void FUNCTION(void)
{
static int i,j,offset;
update_fb_info();
// // detect active buffer
// offset= (scrinfo.xoffset) * (scrinfo.bits_per_pixel/8) +
// (scrinfo.yoffset) * scrinfo.xres;
// if (scrinfo.yoffset)
// offset=scrinfo.xres*scrinfo.yoffset;
// else
// offset=0;
offset = scrinfo.xres * scrinfo.yoffset + scrinfo.xoffset * scrinfo.bits_per_pixel / CHAR_BIT;
OUT_T* a = (OUT_T*)cmpbuf;
OUT_T* b = (OUT_T*)fbmmap;
int max_x=-1,max_y=-1, min_x=9999, min_y=9999;
idle=1;
if (rotation==0)
{
//memcpy(vncbuf,fbmmap,vncscr->width*vncscr->height*scrinfo.bits_per_pixel/CHAR_BIT);
for (j = 0; j < scrinfo.yres; j++)
{
for (i = 0; i < scrinfo.xres; i++)
{
if (a[i + j * scrinfo.xres]!=b[i + j * scrinfo.xres + offset ])
{
a[i + j * scrinfo.xres]=b[i + j * scrinfo.xres + offset];
if (i>max_x)
max_x=i;
if (i<min_x)
min_x=i;
if (j>max_y)
max_y=j;
if (j<min_y)
min_y=j;
if (idle)
idle=0;
}
}
}
}
else if (rotation==90)
{
for (j = 0; j < scrinfo.yres; j++)
{
for (i = 0; i < scrinfo.xres; i++)
{
if (a[(scrinfo.yres - 1 - j + i * scrinfo.yres)] != b[i + j * scrinfo.xres + offset])
{
a[(scrinfo.yres - 1 - j + i * scrinfo.yres)] = b[i + j * scrinfo.xres + offset ];
if (i>max_y)
max_y=i;
if (i<min_y)
min_y=i;
int h=scrinfo.yres-j;
if (h < min_x)
min_x=scrinfo.yres-j;
if (h > max_x)
max_x=scrinfo.yres-j;
if (idle)
idle=0;
}
}
}
}
else if (rotation==180)
{
for (j = 0; j < scrinfo.yres; j++)
{
for (i = 0; i < scrinfo.xres; i++)
{
if (a[i + j * scrinfo.xres]!=b[scrinfo.yres*scrinfo.xres - (i + j * scrinfo.xres ) + offset ])
{
a[i + j * scrinfo.xres]=b[scrinfo.yres*scrinfo.xres - (i + j * scrinfo.xres) + offset ];
if (i>max_x)
max_x=i;
if (i<min_x)
min_x=i;
if (j>max_y)
max_y=j;
if (j<min_y)
min_y=j;
if (idle)
idle=0;
}
}
}
}
else if (rotation==270)
{
for (j = 0; j < scrinfo.yres; j++)
{
for (i = 0; i < scrinfo.xres; i++)
{
if(a[(scrinfo.yres - 1 - j + i * scrinfo.yres)] != b[ scrinfo.yres * scrinfo.xres - (i + j * scrinfo.xres) + offset ])
{
a[(scrinfo.yres - 1 - j + i * scrinfo.yres)] = b[ scrinfo.yres * scrinfo.xres - (i + j * scrinfo.xres) + offset ];
if (i>max_y)
max_y=i;
if (i<min_y)
min_y=i;
int h=scrinfo.yres-j;
if (h < min_x)
min_x=scrinfo.yres-j;
if (h > max_x)
max_x=scrinfo.yres-j;
if (idle)
idle=0;
}
}
}
}
memcpy(vncbuf,a,vncscr->width*vncscr->height*scrinfo.bits_per_pixel/CHAR_BIT);
if (min_x!=9999 && min_y!=9999 && max_x!=-1 && max_y!=-1)
{
// min_x--;
max_x++;
// min_y--;
max_y++;
rfbMarkRectAsModified(vncscr, min_x, min_y, max_x, max_y);
rfbProcessEvents(vncscr, 10000);
}
// rfbMarkRectAsModified(vncscr, 0, 0, scrinfo.yres,scrinfo.xres);
// rfbProcessEvents(vncscr, 10000);
if (idle)
{
standby=standby+1;
if (standby>30)
rfbProcessEvents(vncscr, 1000000);
else
rfbProcessEvents(vncscr, 100000);
__android_log_print(ANDROID_LOG_INFO,"VNC","standby %d xoff=%d yoff=%d\n",standby,scrinfo.xoffset,scrinfo.yoffset);
change=0;
}
else
{
change=change+1;
standby=0;
rfbProcessEvents(vncscr, 100000);
__android_log_print(ANDROID_LOG_INFO,"VNC","change %d\tmin_x=%d max_x=%d min_y=%d max_y=%d xoff=%d yoff=%d\n",change,min_x,max_x,min_y,max_y,scrinfo.xoffset,scrinfo.yoffset);
}
}

Binary file not shown.

View File

@ -53,10 +53,10 @@
</TextView>
</LinearLayout>
<!--
<TextView
android:textSize="15sp"
android:text="Click on ad to support project"
android:text="Click on ad to support project"
android:id="@+id/stateLabel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
@ -71,5 +71,5 @@ android:layout_height="50dip"
android:layout_width="320dip"
android:layout_gravity="bottom"
android:layout_alignParentBottom="true"
/> -->
/>
</RelativeLayout>

Binary file not shown.

View File

@ -7,6 +7,21 @@
<item>180</item>
<item>270</item>
</string-array>
<string-array name="scale_values">
<item>150</item>
<item>100</item>
<item>50</item>
<item>30</item>
<item>20</item>
</string-array>
<string-array name="scale_strings">
<item>150 %</item>
<item>100 %</item>
<item>50 %</item>
<item>30 %</item>
<item>20 %</item>
</string-array>
<string name="device_info_fmt">
droid VNC version: <xliff:g id="application_version">%s</xliff:g>\n
Device model: <xliff:g id="device_model">%s</xliff:g>\n

View File

@ -3,6 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android">
<EditTextPreference android:key="password" android:title="VNC password"></EditTextPreference>
<ListPreference android:key="rotation" android:title="Rotation" android:entries="@array/rotation_values" android:entryValues="@array/rotation_values"></ListPreference>
<ListPreference android:key="scale" android:title="Scale Screen" android:entries="@array/scale_strings" android:entryValues="@array/scale_values"></ListPreference>

View File

@ -0,0 +1,713 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onaips.vnc;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Color;
import android.net.DhcpInfo;
import android.net.Uri;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.Html;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity
{
private static final int MENU_QUIT = 0;
private static final int MENU_HELP = 1;
private static final int MENU_ONAIPS = 2;
private static final int MENU_SENDLOG = 3;
private static final String changelog="-PLEASE REPORT FEEDBACK! (send debug info in menu)<br>- [Add] Server-side screen scaling (change it in preferences - BETA)<br>- [Fix] Half-screen bug<br>New icon<br>Courtesy of Sandro Forbice (@sandroforbice)<br>- [Fix] Double buffering now supported<br>- [Add] Low CPU on idle screen while connected<br>- [Fix] Update while rotated";
SharedPreferences preferences;
ProgressDialog dialog=null;
AlertDialog startDialog;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Initialize preferences
preferences = PreferenceManager.getDefaultSharedPreferences(this);
if (!hasRootPermission())
{
Log.v("VNC","You don't have root permissions...!!!");
showTextOnScreen("You don't have root permissions...Please ROOT your phone first!!!");
//System.exit(-1);
}
showInitialScreen();
createBinary();
boolean serverRunning=isAndroidServerRunning();
setStateLabel(serverRunning);
TextView t=(TextView)findViewById(R.id.TextView01);
if (serverRunning)
t.setText("Connect to:\n" + getIpAddress()+":5901" + "\nor\nhttp://" + getIpAddress() + ":5801");
else
t.setText("");
findViewById(R.id.Button01).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Thread t=new Thread(){
public void run()
{
startServerButtonClicked();
}
};
t.start();
try {
Thread.sleep(1000);
for (int i=0;i<5;i++)
{
if (!isAndroidServerRunning())
Thread.sleep(1000);
else
break;
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
Log.v("VNC","Button01_click:"+ e.getMessage());
}
boolean s=isAndroidServerRunning();
if (!s)
showTextOnScreen("Couldn't start VNC Server");
//setStateLabel(s);
return;
}
}) ;
findViewById(R.id.Button02).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Thread t=new Thread(){
public void run()
{
stopServer();
}
};
t.start();
try {
Thread.sleep(1000);
for (int i=0;i<5;i++)
{
if (isAndroidServerRunning())
Thread.sleep(1000);
else
break;
}
} catch (InterruptedException e) {
Log.v("VNC","Button02_click:"+ e.getMessage());
}
boolean s=isAndroidServerRunning();
if (s)
showTextOnScreen("Couldn't Stop VNC Server\nTry hitting F12 on a connected client");
//setStateLabel(s);
return;
}
});
}
public String packageVersion()
{
String version = "";
try {
PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0);
version = pi.versionName;
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
Log.v("VNC","onOptionsItemSelected: "+ e.getMessage());
};
return version;
}
public boolean free_version()
{
return getPackageName().equals("org.onaips.vnc");
}
public void showInitialScreen()
{
// Initialize preferences
preferences = PreferenceManager.getDefaultSharedPreferences(this );
SharedPreferences.Editor editor = preferences.edit();
String message="Changelog:<br><br>" + changelog;
String version=packageVersion();
if (!free_version())
{
if (version.equals(preferences.getString("version", "")))
return;
editor.putString("version", version);
editor.commit();
}
startDialog = new AlertDialog.Builder(this).create();
startDialog.setTitle("droid VNC Server " + version);
startDialog.setMessage(Html.fromHtml(message));
startDialog.setIcon(R.drawable.icon);
if (free_version())
{
startDialog.setButton(AlertDialog.BUTTON1,"Wait...", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
startDialog.dismiss();
}
});
startDialog.setButton2("Donate Version", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=org.onaips.vnc_donate"));
startActivity(myIntent);
}
});
startDialog.setCancelable(false);
startDialog.show();
Button b=(Button)startDialog.getButton(AlertDialog.BUTTON1);
b.setEnabled(false);
Timer t1=new Timer();
t1.schedule(new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable(){
public void run() {
// ((DialogInterface) startDialog).dismiss();
Button b=(Button)startDialog.getButton(AlertDialog.BUTTON1);
b.setEnabled(true);
b.setText("OK");
}
});
}
}, (long)10000);
}
else
startDialog.show();
}
static void writeCommand(OutputStream os, String command) throws Exception
{
os.write((command + "\n").getBytes("ASCII"));
}
public void showTextOnScreen(final String t)
{
runOnUiThread(new Runnable(){
public void run() {
Toast.makeText(MainActivity.this,t,Toast.LENGTH_LONG).show();
}
});
}
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
menu.add(0,MENU_SENDLOG,0,"Send debug info");
menu.add(0,MENU_ONAIPS,0,"About");
menu.add(0, MENU_HELP, 0, "Help");
menu.add(0, MENU_QUIT, 0, "Close");
return true;
}
public void setStateLabel(boolean state)
{
TextView stateLabel=(TextView)findViewById(R.id.stateLabel);
stateLabel.setText(state?"Running":"Stopped");
stateLabel.setTextColor(state?Color.GREEN:Color.RED);
}
public String getIpAddress()
{
WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
DhcpInfo dhcpInfo = wifiManager.getDhcpInfo();
return intToIp(dhcpInfo.ipAddress);
}
boolean wifiConnected()
{
WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
WifiInfo info = wifi.getConnectionInfo();
return info.getBSSID()!=null;
}
private String intToIp(int i) {
return ( i & 0xFF) + "." + ((i >> 8 ) & 0xFF) + "." + ((i >> 16 ) & 0xFF) + "." + ((i >> 24 ) & 0xFF);
}
public void stopServer()
{
try{
Process sh;
sh = Runtime.getRuntime().exec("su");
OutputStream os = sh.getOutputStream();
writeCommand(os, "killall androidvncserver");
writeCommand(os, "killall -KILL androidvncserver");
writeCommand(os, "exit");
os.flush();
os.close();
} catch (IOException e) {
showTextOnScreen("stopServer()" + e.getMessage());
Log.v("VNC","stopServer()" + e.getMessage());
} catch (Exception e) {
Log.v("VNC","stopServer()" + e.getMessage());
}
}
public void startServerButtonClicked()
{
if (isAndroidServerRunning())
{
new AlertDialog.Builder(this)
.setTitle("Alert")
.setMessage(Html.fromHtml("There is another daemon already running, do you want to kill it?"))
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
stopServer();
startServer();
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
return;
}
})
.show();
}
else
startServer();
}
public void startServer()
{
try{
Process sh;
String password=preferences.getString("password", "");
String password_check="";
if (!password.equals(""))
password_check="-p " + password;
String rotation=preferences.getString("rotation", "0");
rotation="-r " + rotation;
String scaling=preferences.getString("scale", "100");
String scaling_string="";
if (!scaling.equals("0"))
scaling_string="-s " + scaling;
sh = Runtime.getRuntime().exec("su");
OutputStream os = sh.getOutputStream();
writeCommand(os, "chmod 777 /data/data/"+getPackageName()+"/androidvncserver");
writeCommand(os, "/data/data/"+getPackageName()+"/androidvncserver "+ password_check + " " + rotation + " " + scaling_string);
} catch (IOException e) {
Log.v("VNC","startServer():" + e.getMessage());
showTextOnScreen("startServer():" + e.getMessage());
} catch (Exception e) {
Log.v("VNC","startServer():" + e.getMessage());
showTextOnScreen("startServer():" + e.getMessage());
}
}
public void copyBinary(int id,String path)
{
try {
InputStream ins = getResources().openRawResource(id);
int size = ins.available();
// Read the entire resource into a local byte buffer.
byte[] buffer = new byte[size];
ins.read(buffer);
ins.close();
FileOutputStream fos = new FileOutputStream(path);
fos.write(buffer);
fos.close();
}
catch (Exception e)
{
Log.v("VNC","public void createBinary(): " + e.getMessage());
}
}
public void createBinary()
{
copyBinary(R.raw.androidvncserver, "/data/data/" + getPackageName() + "/androidvncserver");
copyBinary(R.raw.vncviewer, "/data/data/"+getPackageName()+"/VncViewer.jar");
copyBinary(R.raw.indexvnc, "/data/data/"+getPackageName()+"/index.vnc");
}
public void showHelp()
{
new AlertDialog.Builder(this)
.setTitle("Help")
.setMessage(Html.fromHtml("Mouse Mappings:<br><br>Right Click -> Back<br>Middle Click -> End Call<br>Left Click -> Touch<br><br>Keyboard Mappings<br><br>" +
"Home Key -> Home<br>Escape -> Back<br>Page Up ->Menu<br>Left Ctrl -> Search<br>PgDown -> Start Call<br>" +
"End Key -> End Call<br>F4 -> Rotate<br>F11 -> Disconnect Server<br>F12 -> Stop Server Daemon"))
.setPositiveButton("Fechar", null)
.setNegativeButton("Open Website", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://onaips.blogspot.com"));
startActivity(myIntent);
}
})
.show();
}
// This method is called once the menu is selected
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// We have only one menu option
case R.id.preferences:
// Launch Preference activity
Intent i = new Intent(MainActivity.this, preferences.class);
startActivity(i);
showTextOnScreen("Don't forget to stop/start the server after changes");
break;
case MENU_QUIT:
System.exit(1);
break;
case MENU_HELP:
showHelp();
break;
case MENU_SENDLOG:
collectAndSendLog();
break;
case MENU_ONAIPS:
new AlertDialog.Builder(this)
.setTitle("About")
.setMessage(Html.fromHtml("version " + packageVersion() + "<br><br>developed by oNaiPs<br><br>Graphics: Sandro Forbice (@sandroforbice)<br><br>Open-Source Software"))
.setPositiveButton("Close", null)
.setNegativeButton("Open Website", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://onaips.blogspot.com"));
startActivity(myIntent);
}
})
.show();
}
return true;
}
public boolean isAndroidServerRunning()
{
String result="";
Process sh;
try {
sh = Runtime.getRuntime().exec("ps");
InputStream is=sh.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
result+=line;
if (result.indexOf("androidvncserver")>0)
return true;
}
OutputStream os = sh.getOutputStream();
writeCommand(os, "exit");
os.flush();
os.close();
} catch (IOException e) {
Log.v("VNC"," isAndroidServerRunning():" + e.getMessage());
} catch (Exception e) {
Log.v("VNC"," isAndroidServerRunning():" + e.getMessage());
}
//Log.v("VNC",result);
return false;
}
public boolean hasRootPermission() {
boolean rooted = true;
try {
File su = new File("/system/bin/su");
if (su.exists() == false) {
su = new File("/system/xbin/su");
if (su.exists() == false) {
rooted = false;
}
}
} catch (Exception e) {
Log.v("VNC", "Can't obtain root - Here is what I know: "+e.getMessage());
rooted = false;
}
Process sh;
try {
String result="";
sh = Runtime.getRuntime().exec("whoami");
InputStream is=sh.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
result+=line;
if (result.indexOf("androidvncserver")>0)
return true;
}
Process sh1=Runtime.getRuntime().exec("su");
//Log.v("aaaaaa","chgrp -R " + result + " /data/data/org.onaips.vnc/");
OutputStream os = sh1.getOutputStream();
writeCommand(os, "chown -R " + result + " /data/data/" + getPackageName() + "/");
writeCommand(os, "chgrp -R " + result + " /data/data/" + getPackageName() + "/");
os.flush();
os.close();
isr.close();
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return rooted;
}
public static final String LOG_COLLECTOR_PACKAGE_NAME = "com.xtralogic.android.logcollector";//$NON-NLS-1$
public static final String ACTION_SEND_LOG = "com.xtralogic.logcollector.intent.action.SEND_LOG";//$NON-NLS-1$
public static final String EXTRA_SEND_INTENT_ACTION = "com.xtralogic.logcollector.intent.extra.SEND_INTENT_ACTION";//$NON-NLS-1$
public static final String EXTRA_DATA = "com.xtralogic.logcollector.intent.extra.DATA";//$NON-NLS-1$
public static final String EXTRA_ADDITIONAL_INFO = "com.xtralogic.logcollector.intent.extra.ADDITIONAL_INFO";//$NON-NLS-1$
public static final String EXTRA_SHOW_UI = "com.xtralogic.logcollector.intent.extra.SHOW_UI";//$NON-NLS-1$
public static final String EXTRA_FILTER_SPECS = "com.xtralogic.logcollector.intent.extra.FILTER_SPECS";//$NON-NLS-1$
public static final String EXTRA_FORMAT = "com.xtralogic.logcollector.intent.extra.FORMAT";//$NON-NLS-1$
public static final String EXTRA_BUFFER = "com.xtralogic.logcollector.intent.extra.BUFFER";//$NON-NLS-1$
void collectAndSendLog(){
final PackageManager packageManager = getPackageManager();
final Intent intent = new Intent(ACTION_SEND_LOG);
List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
final boolean isInstalled = list.size() > 0;
if (!isInstalled){
new AlertDialog.Builder(this)
.setTitle(getString(R.string.app_name))
.setIcon(android.R.drawable.ic_dialog_info)
.setMessage("Please install Log Collector application to collect the device log and send it to dev.")
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int whichButton){
Intent marketIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:" + LOG_COLLECTOR_PACKAGE_NAME));
marketIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(marketIntent);
}
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
else{
new AlertDialog.Builder(this)
.setTitle(getString(R.string.app_name))
.setIcon(android.R.drawable.ic_dialog_info)
.setMessage("Do you want to send debug info to the dev? Please specify what problem is ocurring.\n\nMake sure you started/stopped the server before submitting")
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int whichButton){
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(EXTRA_SEND_INTENT_ACTION, Intent.ACTION_SENDTO);
final String email = "onaips@gmail.com";
intent.putExtra(EXTRA_DATA, Uri.parse("mailto:" + email));
intent.putExtra(EXTRA_ADDITIONAL_INFO,"Problem Description: \n\n\n\n---------DEBUG--------\n" + getString(R.string.device_info_fmt,getVersionNumber(getApplicationContext()),Build.MODEL,Build.VERSION.RELEASE, getFormattedKernelVersion(), Build.DISPLAY));
intent.putExtra(Intent.EXTRA_SUBJECT, "droid VNC server: Debug Info");
intent.putExtra(EXTRA_FORMAT, "time");
//The log can be filtered to contain data relevant only to your app
String[] filterSpecs = new String[4];
filterSpecs[0] = "VNC:I";
filterSpecs[1] = "VNC:D";
filterSpecs[2] = "VNC:V";
filterSpecs[3] = "*:S";
intent.putExtra(EXTRA_FILTER_SPECS, filterSpecs);
startActivity(intent);
}
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
}
private String getFormattedKernelVersion()
{
String procVersionStr;
try {
BufferedReader reader = new BufferedReader(new FileReader("/proc/version"), 256);
try {
procVersionStr = reader.readLine();
} finally {
reader.close();
}
final String PROC_VERSION_REGEX =
"\\w+\\s+" + /* ignore: Linux */
"\\w+\\s+" + /* ignore: version */
"([^\\s]+)\\s+" + /* group 1: 2.6.22-omap1 */
"\\(([^\\s@]+(?:@[^\\s.]+)?)[^)]*\\)\\s+" + /* group 2: (xxxxxx@xxxxx.constant) */
"\\([^)]+\\)\\s+" + /* ignore: (gcc ..) */
"([^\\s]+)\\s+" + /* group 3: #26 */
"(?:PREEMPT\\s+)?" + /* ignore: PREEMPT (optional) */
"(.+)"; /* group 4: date */
Pattern p = Pattern.compile(PROC_VERSION_REGEX);
Matcher m = p.matcher(procVersionStr);
if (!m.matches()) {
Log.e("VNC", "Regex did not match on /proc/version: " + procVersionStr);
return "Unavailable";
} else if (m.groupCount() < 4) {
Log.e("VNC", "Regex match on /proc/version only returned " + m.groupCount()
+ " groups");
return "Unavailable";
} else {
return (new StringBuilder(m.group(1)).append("\n").append(
m.group(2)).append(" ").append(m.group(3)).append("\n")
.append(m.group(4))).toString();
}
} catch (IOException e) {
Log.e("VNC", "IO Exception when getting kernel version for Device Info screen", e);
return "Unavailable";
}
}
private static String getVersionNumber(Context context)
{
String version = "?";
try
{
PackageInfo packagInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
version = packagInfo.versionName;
}
catch (PackageManager.NameNotFoundException e){};
return version;
}
}

View File

@ -0,0 +1,18 @@
package org.onaips.vnc;
import android.os.Bundle;
import android.preference.PreferenceActivity;
public class preferences extends PreferenceActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}