Merge "emulator:opengl: input event redirection"

This commit is contained in:
David Turner
2011-07-11 21:41:14 -07:00
committed by Android Code Review
12 changed files with 2975 additions and 5 deletions

View File

@@ -85,6 +85,7 @@ include $(EMUGL_PATH)/host/renderer/Android.mk
# Host unit-test for the renderer. this one uses its own small
# EGL host wrapper.
include $(EMUGL_PATH)/tests/event_injector/Android.mk
include $(EMUGL_PATH)/tests/EGL_host_wrapper/Android.mk
include $(EMUGL_PATH)/tests/emulator_test_renderer/Android.mk
include $(EMUGL_PATH)/tests/ut_renderer/Android.mk

View File

@@ -1,7 +1,7 @@
LOCAL_PATH:=$(call my-dir)
$(call emugl-begin-host-executable,emulator_test_renderer)
$(call emugl-import,libOpenglRender)
$(call emugl-import,libOpenglRender event_injector)
LOCAL_SRC_FILES := main.cpp

View File

@@ -19,6 +19,9 @@
#include <stdio.h>
#include <string.h>
#include "libOpenglRender/render_api.h"
#include <EventInjector.h>
static int convert_keysym(int sym); // forward
#ifdef _WIN32
#include <winsock2.h>
@@ -31,8 +34,11 @@ int main(int argc, char *argv[])
int winWidth = 320;
int winHeight = 480;
int width, height;
int mouseDown = 0;
const char* env = getenv("ANDROID_WINDOW_SIZE");
FBNativeWindowType windowId = NULL;
EventInjector* injector;
int consolePort = 5554;
if (env && sscanf(env, "%dx%d", &width, &height) == 2) {
winWidth = width;
@@ -81,14 +87,53 @@ int main(int argc, char *argv[])
}
printf("renderer process started\n");
injector = new EventInjector(consolePort);
// Just wait until the window is closed
SDL_Event ev;
while( SDL_WaitEvent(&ev) ) {
if (ev.type == SDL_QUIT) {
break;
for (;;) {
injector->wait(1000/15);
injector->poll();
while (SDL_PollEvent(&ev)) {
switch (ev.type) {
case SDL_MOUSEBUTTONDOWN:
if (!mouseDown) {
injector->sendMouseDown(ev.button.x, ev.button.y);
mouseDown = 1;
}
break;
case SDL_MOUSEBUTTONUP:
if (mouseDown) {
injector->sendMouseUp(ev.button.x,ev.button.y);
mouseDown = 0;
}
break;
case SDL_MOUSEMOTION:
if (mouseDown)
injector->sendMouseMotion(ev.button.x,ev.button.y);
break;
case SDL_KEYDOWN:
#ifdef __APPLE__
/* special code to deal with Command-Q properly */
if (ev.key.keysym.sym == SDLK_q &&
ev.key.keysym.mod & KMOD_META) {
goto EXIT;
}
#endif
injector->sendKeyDown(convert_keysym(ev.key.keysym.sym));
break;
case SDL_KEYUP:
injector->sendKeyUp(convert_keysym(ev.key.keysym.sym));
break;
case SDL_QUIT:
goto EXIT;
}
}
}
EXIT:
//
// stop the renderer
//
@@ -97,3 +142,26 @@ int main(int argc, char *argv[])
return 0;
}
static int convert_keysym(int sym)
{
#define EE(x,y) SDLK_##x, EventInjector::KEY_##y,
static const int keymap[] = {
EE(LEFT,LEFT)
EE(RIGHT,RIGHT)
EE(DOWN,DOWN)
EE(UP,UP)
EE(RETURN,ENTER)
EE(F1,SOFT1)
EE(ESCAPE,BACK)
EE(HOME,HOME)
-1
};
int nn;
for (nn = 0; keymap[nn] >= 0; nn += 2) {
if (keymap[nn] == sym)
return keymap[nn+1];
}
return sym;
}

View File

@@ -0,0 +1,13 @@
LOCAL_PATH := $(call my-dir)
$(call emugl-begin-host-static-library,event_injector)
LOCAL_SRC_FILES := \
EventInjector.cpp \
sockets.c \
emulator-console.c \
iolooper-select.c
$(call emugl-export,C_INCLUDES,$(LOCAL_PATH))
$(call emugl-end-module)

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2011 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.
*/
#include "EventInjector.h"
#include "emulator-console.h"
#define PRIVATE EventInjectorPrivate
class PRIVATE
{
public:
IoLooper* mLooper;
EmulatorConsole* mConsole;
EventInjectorPrivate(int port) {
mLooper = iolooper_new();
mConsole = emulatorConsole_new(port, mLooper);
}
};
EventInjector::EventInjector(int consolePort)
{
mPrivate = new PRIVATE(consolePort);
}
EventInjector::~EventInjector()
{
delete mPrivate;
}
void EventInjector::wait(int timeout_ms)
{
iolooper_wait(mPrivate->mLooper, timeout_ms);
}
void EventInjector::poll(void)
{
emulatorConsole_poll(mPrivate->mConsole);
}
void EventInjector::sendMouseDown( int x, int y )
{
emulatorConsole_sendMouseDown(mPrivate->mConsole, x, y);
}
void EventInjector::sendMouseUp( int x, int y )
{
emulatorConsole_sendMouseUp(mPrivate->mConsole, x, y);
}
void EventInjector::sendMouseMotion( int x, int y )
{
emulatorConsole_sendMouseMotion(mPrivate->mConsole, x, y);
}
void EventInjector::sendKeyDown( int keycode )
{
emulatorConsole_sendKey(mPrivate->mConsole, keycode, 1);
}
void EventInjector::sendKeyUp( int keycode )
{
emulatorConsole_sendKey(mPrivate->mConsole, keycode, 0);
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2011 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.
*/
/* Event redirector is used to inject user events from a GL window
* into the emulated program.
*/
#ifndef EVENT_INJECTOR_H
#define EVENT_INJECTOR_H
class EventInjectorPrivate;
class EventInjector
{
public:
EventInjector(int consolePort);
virtual ~EventInjector();
void wait( int timeout_ms );
void poll( void );
void sendMouseDown( int x, int y );
void sendMouseUp( int x, int y );
void sendMouseMotion( int x, int y );
void sendKeyDown( int keycode );
void sendKeyUp( int keycode );
/* Keycode values expected by the Linux kernel, and the emulator */
enum {
KEY_BACK = 158,
KEY_HOME = 102,
KEY_SOFT1 = 229,
KEY_LEFT = 105,
KEY_UP = 103,
KEY_DOWN = 108,
KEY_RIGHT = 106,
KEY_VOLUMEUP = 115,
KEY_VOLUMEDOWN = 114,
KEY_SEND = 231,
KEY_END = 107,
KEY_ENTER = 28,
};
private:
EventInjectorPrivate* mPrivate;
};
#endif /* EVENT_INJECTOR_H */

View File

@@ -0,0 +1,345 @@
/*
* Copyright (C) 2011 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.
*/
#include "emulator-console.h"
#include "sockets.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define DEBUG 0
#if DEBUG >= 1
# define D(...) printf(__VA_ARGS__), printf("\n")
#else
# define D(...) ((void)0)
#endif
#if DEBUG >= 2
# define DD(...) printf(__VA_ARGS__), printf("\n")
#else
# define DD(...) ((void)0)
#endif
#define ANEW0(p) (p) = calloc(sizeof(*(p)), 1)
enum {
STATE_CONNECTING = 0,
STATE_CONNECTED,
STATE_WAITING,
STATE_ERROR = 2
};
typedef struct Msg {
const char* data; // pointer to data
int size; // size of data
int sent; // already sent (so sent..size remain in buffer).
struct Msg* next; // next message in queue.
} Msg;
static Msg*
msg_alloc( const char* data, int datalen )
{
Msg* msg;
msg = malloc(sizeof(*msg) + datalen);
msg->data = (const char*)(msg + 1);
msg->size = datalen;
msg->sent = 0;
memcpy((char*)msg->data, data, datalen);
msg->next = NULL;
return msg;
}
static void
msg_free( Msg* msg )
{
free(msg);
}
struct EmulatorConsole {
int fd;
IoLooper* looper;
int state;
Msg* out_msg;
SockAddress address;
int64_t waitUntil;
};
/* Read as much from the input as possible, ignoring it.
*/
static int
emulatorConsole_eatInput( EmulatorConsole* con )
{
for (;;) {
char temp[64];
int ret = socket_recv(con->fd, temp, sizeof temp);
if (ret < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return 0;
}
return -1;
}
if (ret == 0) {
return -1;
}
DD("Console received: '%.*s'", ret, temp);
}
}
static int
emulatorConsole_sendOutput( EmulatorConsole* con )
{
if (con->state != STATE_CONNECTED) {
errno = EINVAL;
return -1;
}
while (con->out_msg != NULL) {
Msg* msg = con->out_msg;
int ret;
ret = socket_send(con->fd,
msg->data + msg->sent,
msg->size - msg->sent);
if (ret > 0) {
DD("Console sent: '%.*s'", ret, msg->data + msg->sent);
msg->sent += ret;
if (msg->sent == msg->size) {
con->out_msg = msg->next;
msg_free(msg);
}
continue;
}
if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
return 0;
}
con->state = STATE_ERROR;
D("Console error when sending: %s", strerror(errno));
return -1;
}
iolooper_del_write(con->looper, con->fd);
return 0;
}
static void
emulatorConsole_completeConnect(EmulatorConsole* con)
{
D("Console connected!");
iolooper_add_read(con->looper, con->fd);
iolooper_del_write(con->looper, con->fd);
con->state = STATE_CONNECTED;
if (con->out_msg != NULL) {
iolooper_add_write(con->looper, con->fd);
emulatorConsole_sendOutput(con);
}
}
static void
emulatorConsole_retry(EmulatorConsole* con)
{
/* Not possible yet, wait one second */
D("Could not connect to emulator, waiting 1 second: %s", errno_str);
con->state = STATE_WAITING;
con->waitUntil = iolooper_now() + 5000;
}
static void
emulatorConsole_connect(EmulatorConsole* con)
{
D("Trying to connect!");
if (con->fd < 0) {
con->fd = socket_create_inet( SOCKET_STREAM );
if (con->fd < 0) {
D("ERROR: Could not create socket: %s", errno_str);
con->state = STATE_ERROR;
return;
}
socket_set_nonblock(con->fd);
}
con->state = STATE_CONNECTING;
if (socket_connect(con->fd, &con->address) < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) {
iolooper_add_write(con->looper, con->fd);
} else {
emulatorConsole_retry(con);
}
return;
}
emulatorConsole_completeConnect(con);
}
static void
emulatorConsole_reset( EmulatorConsole* con )
{
D("Resetting console connection");
while (con->out_msg) {
Msg* msg = con->out_msg;
con->out_msg = msg->next;
msg_free(msg);
}
iolooper_del_read(con->looper, con->fd);
iolooper_del_write(con->looper, con->fd);
socket_close(con->fd);
con->fd = -1;
emulatorConsole_connect(con);
}
/* Create a new EmulatorConsole object to connect asynchronously to
* a given emulator port. Note that this should always succeeds since
* the connection is asynchronous.
*/
EmulatorConsole*
emulatorConsole_new(int port, IoLooper* looper)
{
EmulatorConsole* con;
SockAddress addr;
ANEW0(con);
con->looper = looper;
con->fd = -1;
sock_address_init_inet(&con->address, SOCK_ADDRESS_INET_LOOPBACK, port);
emulatorConsole_connect(con);
return con;
}
int
emulatorConsole_poll( EmulatorConsole* con )
{
int ret;
if (con->state == STATE_WAITING) {
if (iolooper_now() >= con->waitUntil)
emulatorConsole_connect(con);
return 0;
}
if (!iolooper_is_read(con->looper, con->fd) &&
!iolooper_is_write(con->looper, con->fd))
{
return 0;
}
LOOP:
switch (con->state) {
case STATE_ERROR:
return -1;
case STATE_CONNECTING:
// read socket error to determine success / error.
if (socket_get_error(con->fd) != 0) {
emulatorConsole_retry(con);
} else {
emulatorConsole_completeConnect(con);
}
return 0;
case STATE_CONNECTED:
/* ignore input, if any */
if (iolooper_is_read(con->looper, con->fd)) {
if (emulatorConsole_eatInput(con) < 0) {
goto SET_ERROR;
}
}
/* send outgoing data, if any */
if (iolooper_is_write(con->looper, con->fd)) {
if (emulatorConsole_sendOutput(con) < 0) {
goto SET_ERROR;
}
}
return 0;
default:
D("UNSUPPORTED STATE!");
break;
}
SET_ERROR:
D("Console ERROR!: %s\n", errno_str);
con->state = STATE_ERROR;
emulatorConsole_reset(con);
return -1;
}
/* Send a message to the console asynchronously. Any answer will be
* ignored. */
void
emulatorConsole_send( EmulatorConsole* con, const char* command )
{
int cmdlen = strlen(command);
Msg* msg;
Msg** plast;
if (cmdlen == 0)
return;
/* Append new message at end of outgoing list */
msg = msg_alloc(command, cmdlen);
plast = &con->out_msg;
while (*plast) {
plast = &(*plast)->next;
}
*plast = msg;
if (con->out_msg == msg) {
iolooper_add_write(con->looper, con->fd);
}
emulatorConsole_sendOutput(con);
}
void
emulatorConsole_sendMouseDown( EmulatorConsole* con, int x, int y )
{
char temp[128];
D("sendMouseDown(%d,%d)", x, y);
snprintf(temp, sizeof temp,
"event send 3:0:%d 3:1:%d 1:330:1 0:0:0\r\n",
x, y);
emulatorConsole_send(con, temp);
}
void
emulatorConsole_sendMouseMotion( EmulatorConsole* con, int x, int y )
{
/* Same as mouse down */
emulatorConsole_sendMouseDown(con, x, y);
}
void
emulatorConsole_sendMouseUp( EmulatorConsole* con, int x, int y )
{
char temp[128];
D("sendMouseUp(%d,%d)", x, y);
snprintf(temp, sizeof temp,
"event send 3:0:%d 3:1:%d 1:330:0 0:0:0\r\n",
x, y);
emulatorConsole_send(con, temp);
}
#define EE(x,y) if (keycode == x) return y;
void
emulatorConsole_sendKey( EmulatorConsole* con, int keycode, int down )
{
char temp[128];
snprintf(temp, sizeof temp,
"event send EV_KEY:%d:%d 0:0:0\r\n", keycode, down);
emulatorConsole_send(con, temp);
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2011 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.
*/
#ifndef ANDROID_EMULATOR_CONSOLE_H
#define ANDROID_EMULATOR_CONSOLE_H
#include "iolooper.h"
#include "sockets.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct EmulatorConsole EmulatorConsole;
/* Create a new EmulatorConsole object to connect asynchronously to
* a given emulator port. Note that this always succeeds since the
* connection is asynchronous.
*/
EmulatorConsole* emulatorConsole_new(int port, IoLooper* looper);
/* Call this after an iolooper_poll() or iolooper_wait() to check
* the status of the console's socket and act upon it.
*
* Returns 0 on success, or -1 on error (which indicates disconnection!)
*/
int emulatorConsole_poll( EmulatorConsole* console );
/* Send a message to the console asynchronously. Any answer will be
* ignored. */
void emulatorConsole_send( EmulatorConsole* console, const char* command );
void emulatorConsole_sendMouseDown( EmulatorConsole* con, int x, int y );
void emulatorConsole_sendMouseMotion( EmulatorConsole* con, int x, int y );
void emulatorConsole_sendMouseUp( EmulatorConsole* con, int x, int y );
void emulatorConsole_sendKey( EmulatorConsole* con, int keycode, int down );
#ifdef __cplusplus
}
#endif
#endif /* ANDROID_EMULATOR_CONSOLE_H */

View File

@@ -0,0 +1,257 @@
/*
* Copyright (C) 2011 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.
*/
#include <errno.h>
#include <stdlib.h>
#include "iolooper.h"
/* An implementation of iolooper.h based on Unix select() */
#ifdef _WIN32
# include <winsock2.h>
# include <time.h>
#else
# include <sys/types.h>
# include <sys/select.h>
# include <sys/time.h>
#endif
struct IoLooper {
fd_set reads[1];
fd_set writes[1];
fd_set reads_result[1];
fd_set writes_result[1];
int max_fd;
int max_fd_valid;
};
IoLooper*
iolooper_new(void)
{
IoLooper* iol = malloc(sizeof(*iol));
iolooper_reset(iol);
return iol;
}
void
iolooper_free( IoLooper* iol )
{
free(iol);
}
void
iolooper_reset( IoLooper* iol )
{
FD_ZERO(iol->reads);
FD_ZERO(iol->writes);
iol->max_fd = -1;
iol->max_fd_valid = 1;
}
static void
iolooper_add_fd( IoLooper* iol, int fd )
{
if (iol->max_fd_valid && fd > iol->max_fd) {
iol->max_fd = fd;
}
}
static void
iolooper_del_fd( IoLooper* iol, int fd )
{
if (iol->max_fd_valid && fd == iol->max_fd)
iol->max_fd_valid = 0;
}
void
iolooper_modify( IoLooper* iol, int fd, int oldflags, int newflags )
{
if (fd < 0)
return;
int changed = oldflags ^ newflags;
if ((changed & IOLOOPER_READ) != 0) {
if ((newflags & IOLOOPER_READ) != 0)
iolooper_add_read(iol, fd);
else
iolooper_del_read(iol, fd);
}
if ((changed & IOLOOPER_WRITE) != 0) {
if ((newflags & IOLOOPER_WRITE) != 0)
iolooper_add_write(iol, fd);
else
iolooper_del_write(iol, fd);
}
}
static int
iolooper_fd_count( IoLooper* iol )
{
int max_fd = iol->max_fd;
int fd;
if (iol->max_fd_valid)
return max_fd + 1;
/* recompute max fd */
for (fd = 0; fd < FD_SETSIZE; fd++) {
if (!FD_ISSET(fd, iol->reads) && !FD_ISSET(fd, iol->writes))
continue;
max_fd = fd;
}
iol->max_fd = max_fd;
iol->max_fd_valid = 1;
return max_fd + 1;
}
void
iolooper_add_read( IoLooper* iol, int fd )
{
if (fd >= 0) {
iolooper_add_fd(iol, fd);
FD_SET(fd, iol->reads);
}
}
void
iolooper_add_write( IoLooper* iol, int fd )
{
if (fd >= 0) {
iolooper_add_fd(iol, fd);
FD_SET(fd, iol->writes);
}
}
void
iolooper_del_read( IoLooper* iol, int fd )
{
if (fd >= 0) {
iolooper_del_fd(iol, fd);
FD_CLR(fd, iol->reads);
}
}
void
iolooper_del_write( IoLooper* iol, int fd )
{
if (fd >= 0) {
iolooper_del_fd(iol, fd);
FD_CLR(fd, iol->writes);
}
}
int
iolooper_poll( IoLooper* iol )
{
int count = iolooper_fd_count(iol);
int ret;
fd_set errs;
if (count == 0)
return 0;
FD_ZERO(&errs);
do {
struct timeval tv;
tv.tv_sec = tv.tv_usec = 0;
iol->reads_result[0] = iol->reads[0];
iol->writes_result[0] = iol->writes[0];
ret = select( count, iol->reads_result, iol->writes_result, &errs, &tv);
} while (ret < 0 && errno == EINTR);
return ret;
}
int
iolooper_wait( IoLooper* iol, int64_t duration )
{
int count = iolooper_fd_count(iol);
int ret;
fd_set errs;
struct timeval tm0, *tm = NULL;
if (count == 0)
return 0;
if (duration < 0)
tm = NULL;
else {
tm = &tm0;
tm->tv_sec = duration / 1000;
tm->tv_usec = (duration - 1000*tm->tv_sec) * 1000;
}
FD_ZERO(&errs);
do {
iol->reads_result[0] = iol->reads[0];
iol->writes_result[0] = iol->writes[0];
ret = select( count, iol->reads_result, iol->writes_result, &errs, tm);
if (ret == 0) {
// Indicates timeout
errno = ETIMEDOUT;
}
} while (ret < 0 && errno == EINTR);
return ret;
}
int
iolooper_is_read( IoLooper* iol, int fd )
{
return FD_ISSET(fd, iol->reads_result);
}
int
iolooper_is_write( IoLooper* iol, int fd )
{
return FD_ISSET(fd, iol->writes_result);
}
int
iolooper_has_operations( IoLooper* iol )
{
return iolooper_fd_count(iol) > 0;
}
int64_t
iolooper_now(void)
{
struct timeval time_now;
return gettimeofday(&time_now, NULL) ? -1 : (int64_t)time_now.tv_sec * 1000LL +
time_now.tv_usec / 1000;
}
int
iolooper_wait_absolute(IoLooper* iol, int64_t deadline)
{
int64_t timeout = deadline - iolooper_now();
/* If the deadline has passed, set the timeout to 0, this allows us
* to poll the file descriptor nonetheless */
if (timeout < 0)
timeout = 0;
return iolooper_wait(iol, timeout);
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2011 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.
*/
#ifndef IOLOOPER_H
#define IOLOOPER_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* An IOLooper is an abstraction for select() */
typedef struct IoLooper IoLooper;
IoLooper* iolooper_new(void);
void iolooper_free( IoLooper* iol );
void iolooper_reset( IoLooper* iol );
void iolooper_add_read( IoLooper* iol, int fd );
void iolooper_add_write( IoLooper* iol, int fd );
void iolooper_del_read( IoLooper* iol, int fd );
void iolooper_del_write( IoLooper* iol, int fd );
enum {
IOLOOPER_READ = (1<<0),
IOLOOPER_WRITE = (1<<1),
};
void iolooper_modify( IoLooper* iol, int fd, int oldflags, int newflags);
int iolooper_poll( IoLooper* iol );
/* Wrapper around select()
* Return:
* > 0 in case an I/O has occurred, or < 0 on error, or 0 on timeout with
* errno set to ETIMEDOUT.
*/
int iolooper_wait( IoLooper* iol, int64_t duration );
int iolooper_is_read( IoLooper* iol, int fd );
int iolooper_is_write( IoLooper* iol, int fd );
/* Returns 1 if this IoLooper has one or more file descriptor to interact with */
int iolooper_has_operations( IoLooper* iol );
/* Gets current time in milliseconds.
* Return:
* Number of milliseconds corresponded to the current time on success, or -1
* on failure.
*/
int64_t iolooper_now(void);
/* Waits for an I/O to occur before specific absolute time.
* This routine should be used (instead of iolooper_wait) in cases when multiple
* sequential I/O should be completed within given time interval. For instance,
* consider the scenario, when "server" does two sequential writes, and "client"
* now has to read data transferred with these two distinct writes. It might be
* wasteful to do two reads, each with the same (large) timeout. Instead, it
* would be better to assign a deadline for both reads before the first read,
* and call iolooper_wait_absoulte with the same deadline value:
* int64_t deadline = iolooper_now() + TIMEOUT;
* if (iolooper_wait_absoulte(iol, deadline)) {
* // Process first buffer.
* (iolooper_wait_absoulte(iol, deadline)) {
* // Process second read
* }
* }
* Param:
* iol IoLooper instance for an I/O.
* deadline Deadline (absoulte time in milliseconds) before which an I/O should
* occur.
* Return:
* Number of I/O descriptors set in iol, if an I/O has occurred, 0 if no I/O
* occurred before the deadline, or -1 on error.
*/
int iolooper_wait_absolute(IoLooper* iol, int64_t deadline);
#ifdef __cplusplus
}
#endif
#endif /* IOLOOPER_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,432 @@
/*
* Copyright (C) 2011 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.
*/
/* headers to use the BSD sockets */
#ifndef ANDROID_SOCKET_H
#define ANDROID_SOCKET_H
#include <stddef.h>
#include <stdint.h>
#include <errno.h>
#ifdef __cplusplus
extern "C" {
#endif
/* we're going to hide the implementation details of sockets behind
* a simple wrapper interface declared here.
*
* all socket operations set the global 'errno' variable on error.
* this is unlike Winsock which instead modifies another internal
* variable accessed through WSAGetLastError() and WSASetLastError()
*/
/* the wrapper will convert any Winsock error message into an errno
* code for you. There are however a few standard Unix error codes
* that are not defined by the MS C library headers, so we add them
* here. We use the official Winsock error codes, which are documented
* even though we don't want to include the Winsock headers
*/
#ifdef _WIN32
# ifndef EINTR
# define EINTR 10004
# endif
# ifndef EAGAIN
# define EAGAIN 10035
# endif
# ifndef EWOULDBLOCK
# define EWOULDBLOCK EAGAIN
# endif
# ifndef EINPROGRESS
# define EINPROGRESS 10036
# endif
# ifndef EALREADY
# define EALREADY 10037
# endif
# ifndef EDESTADDRREQ
# define EDESTADDRREQ 10039
# endif
# ifndef EMSGSIZE
# define EMSGSIZE 10040
# endif
# ifndef EPROTOTYPE
# define EPROTOTYPE 10041
# endif
# ifndef ENOPROTOOPT
# define ENOPROTOOPT 10042
# endif
# ifndef EAFNOSUPPORT
# define EAFNOSUPPORT 10047
# endif
# ifndef EADDRINUSE
# define EADDRINUSE 10048
# endif
# ifndef EADDRNOTAVAIL
# define EADDRNOTAVAIL 10049
# endif
# ifndef ENETDOWN
# define ENETDOWN 10050
# endif
# ifndef ENETUNREACH
# define ENETUNREACH 10051
# endif
# ifndef ENETRESET
# define ENETRESET 10052
# endif
# ifndef ECONNABORTED
# define ECONNABORTED 10053
# endif
# ifndef ECONNRESET
# define ECONNRESET 10054
# endif
# ifndef ENOBUFS
# define ENOBUFS 10055
# endif
# ifndef EISCONN
# define EISCONN 10056
# endif
# ifndef ENOTCONN
# define ENOTCONN 10057
# endif
# ifndef ESHUTDOWN
# define ESHUTDOWN 10058
# endif
# ifndef ETOOMANYREFS
# define ETOOMANYREFS 10059
# endif
# ifndef ETIMEDOUT
# define ETIMEDOUT 10060
# endif
# ifndef ECONNREFUSED
# define ECONNREFUSED 10061
# endif
# ifndef ELOOP
# define ELOOP 10062
# endif
# ifndef EHOSTDOWN
# define EHOSTDOWN 10064
# endif
# ifndef EHOSTUNREACH
# define EHOSTUNREACH 10065
# endif
#endif /* _WIN32 */
/* Define 'errno_str' as a handy macro to return the string
* corresponding to a given errno code. On Unix, this is
* equivalent to strerror(errno), but on Windows, this will
* take care of Winsock-originated errors as well.
*/
#ifdef _WIN32
extern const char* _errno_str(void);
# define errno_str _errno_str()
#else
# define errno_str strerror(errno)
#endif
/* always enable IPv6 sockets for now.
* the QEMU internal router is not capable of
* supporting them, but we plan to replace it
* with something better in the future.
*/
#define HAVE_IN6_SOCKETS 1
/* Unix sockets are not available on Win32 */
#ifndef _WIN32
# define HAVE_UNIX_SOCKETS 1
#endif
/* initialize the socket sub-system. this must be called before
* using any of the declarations below.
*/
int socket_init( void );
/* return the name of the current host */
char* host_name( void );
/* supported socket types */
typedef enum {
SOCKET_DGRAM = 0,
SOCKET_STREAM
} SocketType;
/* supported socket families */
typedef enum {
SOCKET_UNSPEC,
SOCKET_INET,
SOCKET_IN6,
SOCKET_UNIX
} SocketFamily;
/* Generic socket address structure. Note that for Unix
* sockets, the path is stored in a heap-allocated block,
* unless the 'owner' field is cleared. If this is the case,
*/
typedef struct {
SocketFamily family;
union {
struct {
uint16_t port;
uint32_t address;
} inet;
struct {
uint16_t port;
uint8_t address[16];
} in6;
struct {
int owner;
const char* path;
} _unix;
} u;
} SockAddress;
#define SOCK_ADDRESS_INET_ANY 0x00000000
#define SOCK_ADDRESS_INET_LOOPBACK 0x7f000001
/* initialize a new IPv4 socket address, the IP address and port are
* in host endianess.
*/
void sock_address_init_inet( SockAddress* a, uint32_t ip, uint16_t port );
/* Initialize an IPv6 socket address, the address is in network order
* and the port in host endianess.
*/
#if HAVE_IN6_SOCKETS
void sock_address_init_in6 ( SockAddress* a, const uint8_t* ip6[16], uint16_t port );
#endif
/* Intialize a Unix socket address, this will copy the 'path' string into the
* heap. You need to call sock_address_done() to release the copy
*/
#if HAVE_UNIX_SOCKETS
void sock_address_init_unix( SockAddress* a, const char* path );
#endif
/* Finalize a socket address, only needed for now for Unix addresses */
void sock_address_done( SockAddress* a );
int sock_address_equal( const SockAddress* a, const SockAddress* b );
/* return a static string describing the address */
const char* sock_address_to_string( const SockAddress* a );
static __inline__
SocketFamily sock_address_get_family( const SockAddress* a )
{
return a->family;
}
/* return the port number of a given socket address, or -1 if it's a Unix one */
int sock_address_get_port( const SockAddress* a );
/* set the port number of a given socket address, don't do anything for Unix ones */
void sock_address_set_port( SockAddress* a, uint16_t port );
/* return the path of a given Unix socket, returns NULL for non-Unix ones */
const char* sock_address_get_path( const SockAddress* a );
/* return the inet address, or -1 if it's not SOCKET_INET */
int sock_address_get_ip( const SockAddress* a );
/* bufprint a socket address into a human-readable string */
char* bufprint_sock_address( char* p, char* end, const SockAddress* a );
/* resolve a hostname or decimal IPv4/IPv6 address into a socket address.
* returns 0 on success, or -1 on failure. Note that the values or errno
* set by this function are the following:
*
* EINVAL : invalid argument
* EHOSTDOWN : could not reach DNS server
* ENOENT : no host with this name, or host doesn't have any IP address
* ENOMEM : not enough memory to perform request
*/
int sock_address_init_resolve( SockAddress* a,
const char* hostname,
uint16_t port,
int preferIn6 );
int sock_address_get_numeric_info( SockAddress* a,
char* host,
size_t hostlen,
char* serv,
size_t servlen );
/* Support for listing all socket addresses of a given host */
enum {
SOCKET_LIST_PASSIVE = (1 << 0),
SOCKET_LIST_FORCE_INET = (1 << 1),
SOCKET_LIST_FORCE_IN6 = (1 << 2),
SOCKET_LIST_DGRAM = (1 << 3),
};
/* resolve a host and service/port name into a list of SockAddress objects.
* returns a NULL-terminated array of SockAddress pointers on success,
* or NULL in case of failure, with the value of errno set to one of the
* following:
*
* EINVAL : invalid argument
* EHOSTDOWN : could not reach DNS server
* ENOENT : no host with this name, or host doesn't have IP address
* ENOMEM : not enough memory to perform request
*
* other system-level errors can also be set depending on the host sockets
* implementation.
*
* This function loops on EINTR so the caller shouldn't have to check for it.
*/
SockAddress** sock_address_list_create( const char* hostname,
const char* port,
unsigned flags );
/* resolve a string containing host and port name into a list of SockAddress
* objects. Parameter host_and_port should be in format [host:]port, where
* 'host' addresses the machine and must be resolvable into an IP address, and
* 'port' is a decimal numeric value for the port. 'host' is optional, and if
* ommited, localhost will be used.
* returns a NULL-terminated array of SockAddress pointers on success,
* or NULL in case of failure, with the value of errno set to one of the
* following:
*
* EINVAL : invalid argument
* EHOSTDOWN : could not reach DNS server
* ENOENT : no host with this name, or host doesn't have IP address
* ENOMEM : not enough memory to perform request
*
* other system-level errors can also be set depending on the host sockets
* implementation.
*
* This function loops on EINTR so the caller shouldn't have to check for it.
*/
SockAddress** sock_address_list_create2(const char* host_and_port,
unsigned flags );
void sock_address_list_free( SockAddress** list );
/* create a new socket, return the socket number of -1 on failure */
int socket_create( SocketFamily family, SocketType type );
/* create a new socket intended for IPv4 communication. returns the socket number,
* or -1 on failure.
*/
int socket_create_inet( SocketType type );
/* create a new socket intended for IPv6 communication. returns the socket number,
* or -1 on failure.
*/
#if HAVE_IN6_SOCKETS
int socket_create_in6 ( SocketType type );
#endif
/* create a unix/local domain socket. returns the socket number,
* or -1 on failure.
*/
#if HAVE_UNIX_SOCKETS
int socket_create_unix( SocketType type );
#endif
/* return the type of a given socket */
SocketType socket_get_type(int fd);
/* set SO_REUSEADDR on Unix, SO_EXCLUSIVEADDR on Windows */
int socket_set_xreuseaddr(int fd);
/* set socket in non-blocking mode */
int socket_set_nonblock(int fd);
/* set socket in blocking mode */
int socket_set_blocking(int fd);
/* disable the TCP Nagle algorithm for lower latency */
int socket_set_nodelay(int fd);
/* send OOB data inline for this socket */
int socket_set_oobinline(int fd);
/* force listening to IPv6 interfaces only */
int socket_set_ipv6only(int fd);
/* retrieve last socket error code */
int socket_get_error(int fd);
/* close an opened socket. Note that this is unlike the Unix 'close' because:
* - it will properly shutdown the socket in the background
* - it does not modify errno
*/
void socket_close( int fd );
/* the following functions are equivalent to the BSD sockets ones
*/
int socket_recv ( int fd, void* buf, int buflen );
int socket_recvfrom( int fd, void* buf, int buflen, SockAddress* from );
int socket_send ( int fd, const void* buf, int buflen );
int socket_send_oob( int fd, const void* buf, int buflen );
int socket_sendto( int fd, const void* buf, int buflen, const SockAddress* to );
int socket_connect( int fd, const SockAddress* address );
int socket_bind( int fd, const SockAddress* address );
int socket_get_address( int fd, SockAddress* address );
int socket_get_peer_address( int fd, SockAddress* address );
int socket_listen( int fd, int backlog );
int socket_accept( int fd, SockAddress* address );
/* returns the number of bytes that can be read from a socket */
int socket_can_read( int fd );
/* this call creates a pair of non-blocking sockets connected
* to each other. this is equivalent to calling the Unix function:
* socketpair(AF_LOCAL,SOCK_STREAM,0,&fds)
*
* on Windows, this will use a pair of TCP loopback sockets instead
* returns 0 on success, -1 on error.
*/
int socket_pair(int *fd1, int *fd2);
/* create a server socket listening on the host's loopback interface */
int socket_loopback_server( int port, SocketType type );
/* connect to a port on the host's loopback interface */
int socket_loopback_client( int port, SocketType type );
/* create a server socket listening to a Unix domain path */
#if HAVE_UNIX_SOCKETS
int socket_unix_server( const char* name, SocketType type );
#endif
/* create a Unix sockets and connects it to a Unix server */
#if HAVE_UNIX_SOCKETS
int socket_unix_client( const char* name, SocketType type );
#endif
/* create an IPv4 client socket and connect it to a given host */
int socket_network_client( const char* host, int port, SocketType type );
/* create an IPv4 socket and binds it to a given port of the host's interface */
int socket_anyaddr_server( int port, SocketType type );
/* accept a connection from the host's any interface, return the new socket
* descriptor or -1 */
int socket_accept_any( int server_fd );
int socket_mcast_inet_add_membership( int s, uint32_t ip );
int socket_mcast_inet_drop_membership( int s, uint32_t ip );
int socket_mcast_inet_set_loop( int s, int enabled );
int socket_mcast_inet_set_ttl( int s, int ttl );
#ifdef __cplusplus
}
#endif
#endif /* ANDROID_SOCKET_H */