| /* |
| * Copyright 2007 The Android Open Source Project |
| * |
| * Simulator interactions. |
| * |
| * TODO: for multi-process we probably need to switch to a new process |
| * group if we are the first process (could be runtime, could be gdb), |
| * rather than wait for the simulator to tell us to switch. |
| */ |
| #include "Common.h" |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <stdint.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <sys/ipc.h> |
| #include <sys/shm.h> |
| #include <sys/sem.h> |
| #include <sys/un.h> |
| #include <signal.h> |
| #include <assert.h> |
| |
| // fwd |
| static int connectToSim(void); |
| static void listenToSim(void); |
| |
| /* |
| * Env var to restrict who tries to talk to the front end. |
| */ |
| #define kWrapSimConnectedEnv "WRAP_SIM_CONNECTED" |
| |
| |
| /* |
| * Signal the main thread that we're ready to continue. |
| */ |
| static void signalMainThread(void) |
| { |
| int cc; |
| |
| cc = pthread_mutex_lock(&gWrapSim.startLock); |
| assert(cc == 0); |
| |
| gWrapSim.startReady = 1; |
| |
| cc = pthread_cond_signal(&gWrapSim.startCond); |
| assert(cc == 0); |
| |
| cc = pthread_mutex_unlock(&gWrapSim.startLock); |
| assert(cc == 0); |
| } |
| |
| |
| /* |
| * Entry point for the sim management thread. |
| * |
| * Once we have established a connection to the simulator and are ready |
| * for other threads to send messages, we signal the main thread. |
| */ |
| static void* simThreadEntry(void* arg) |
| { |
| wsLog("--- sim manager thread started\n"); |
| |
| /* |
| * Establish a connection to the simulator front-end. If we can't do |
| * that, we have no access to input or output devices, and we might |
| * as well give up. |
| */ |
| if (connectToSim() != 0) { |
| signalMainThread(); |
| return NULL; |
| } |
| |
| /* success! */ |
| wsLog("--- sim manager thread ready\n"); |
| gWrapSim.simulatorInitFailed = 0; |
| signalMainThread(); |
| |
| listenToSim(); |
| |
| wsLog("--- sim manager thread exiting\n"); |
| |
| return NULL; |
| } |
| |
| /* |
| * If we think we're not yet connected to the sim, do so now. We only |
| * want to do this once per process *group*, so we control access with |
| * an environment variable. |
| */ |
| int wsSimConnect(void) |
| { |
| /* |
| * If the environment variable hasn't been set, assume we're the first |
| * to get here, and should attach to the simulator. We set the env |
| * var here whether or not we succeed in connecting to the sim. |
| * |
| * (For correctness we should wrap the getenv/setenv in a semaphore.) |
| */ |
| if (getenv(kWrapSimConnectedEnv) == NULL) { |
| pthread_attr_t threadAttr; |
| pthread_t threadHandle; |
| int cc; |
| |
| gWrapSim.simulatorInitFailed = 1; |
| setenv(kWrapSimConnectedEnv, "1", 1); |
| |
| cc = pthread_mutex_lock(&gWrapSim.startLock); |
| assert(cc == 0); |
| |
| pthread_attr_init(&threadAttr); |
| pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED); |
| cc = pthread_create(&threadHandle, &threadAttr, simThreadEntry, NULL); |
| if (cc != 0) { |
| wsLog("Unable to create new thread: %s\n", strerror(errno)); |
| abort(); |
| } |
| |
| while (!gWrapSim.startReady) { |
| cc = pthread_cond_wait(&gWrapSim.startCond, &gWrapSim.startLock); |
| assert(cc == 0); |
| } |
| |
| cc = pthread_mutex_unlock(&gWrapSim.startLock); |
| assert(cc == 0); |
| |
| if (gWrapSim.simulatorInitFailed) { |
| wsLog("Simulator initialization failed, bailing\n"); |
| |
| /* this *should* be okay to do */ |
| fprintf(stderr, "Fatal error:" |
| " unable to connect to sim front-end (not running?)\n"); |
| abort(); |
| } |
| } |
| |
| wsLog("+++ continuing\n"); |
| return 0; |
| } |
| |
| |
| /* |
| * =========================================================================== |
| * Message / MessageStream |
| * =========================================================================== |
| */ |
| |
| /* |
| * This is a quick & dirty rewrite of the C++ Message and MessageStream |
| * classes, ported to C, reduced in generality, with syscalls stubbed |
| * where necessary. I didn't fix the API to make it more sensible in C |
| * (which lacks destructors), so some of this is a little fragile or |
| * awkward. |
| */ |
| |
| /* values for message type byte; must match android::Message constants */ |
| typedef enum MessageType { |
| kTypeUnknown = 0, |
| kTypeRaw, // chunk of raw data |
| kTypeConfig, // send a name=value pair to peer |
| kTypeCommand, // simple command w/arg |
| kTypeCommandExt, // slightly more complicated command |
| kTypeLogBundle, // multi-part log message |
| } MessageType; |
| |
| /* |
| * Reusable message object. |
| */ |
| typedef struct Message { |
| MessageType mType; |
| unsigned char* mData; |
| int mLength; |
| } Message; |
| |
| /* magic init messages; must match android::MessageStream constants */ |
| enum { |
| kHelloMsg = 0x4e303047, // 'N00G' |
| kHelloAckMsg = 0x31455221, // '1ER!' |
| }; |
| |
| |
| /* |
| * Clear out a Message. |
| */ |
| static void Message_clear(Message* msg) |
| { |
| memset(msg, 0, sizeof(Message)); |
| } |
| |
| /* |
| * Keep reading until we get all bytes or hit EOF/error. "fd" is expected |
| * to be in blocking mode. |
| * |
| * Returns 0 on success. |
| */ |
| static int readAll(int fd, void* buf, size_t count) |
| { |
| ssize_t actual; |
| ssize_t have; |
| |
| have = 0; |
| while (have != (ssize_t) count) { |
| actual = _ws_read(fd, ((char*) buf) + have, count - have); |
| if (actual < 0) { |
| if (errno == EINTR) |
| continue; |
| wsLog("read %d failed: %s\n", fd, strerror(errno)); |
| } else if (actual == 0) { |
| wsLog("early EOF on %d\n", fd); |
| return -1; |
| } else { |
| have += actual; |
| } |
| |
| assert(have <= (ssize_t)count); |
| } |
| |
| return 0; |
| } |
| |
| #if 0 |
| /* |
| * Keep writing until we put all bytes or hit an error. "fd" is expected |
| * to be in blocking mode. |
| * |
| * Returns 0 on success. |
| */ |
| static int writeAll(int fd, const void* buf, size_t count) |
| { |
| ssize_t actual; |
| ssize_t have; |
| |
| have = 0; |
| while (have != count) { |
| actual = _ws_write(fd, ((const char*) buf) + have, count - have); |
| if (actual < 0) { |
| if (errno == EINTR) |
| continue; |
| wsLog("write %d failed: %s\n", fd, strerror(errno)); |
| } else if (actual == 0) { |
| wsLog("wrote zero on %d\n", fd); |
| return -1; |
| } else { |
| have += actual; |
| } |
| |
| assert(have <= count); |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| /* |
| * Read a message from the specified file descriptor. |
| * |
| * The caller must Message_release(&msg). |
| * |
| * We guarantee 32-bit alignment for msg->mData. |
| */ |
| static int Message_read(Message* msg, int fd) |
| { |
| unsigned char header[4]; |
| |
| readAll(fd, header, 4); |
| |
| msg->mType = (MessageType) header[2]; |
| msg->mLength = header[0] | header[1] << 8; |
| msg->mLength -= 2; // we already read two of them in the header |
| |
| if (msg->mLength > 0) { |
| int actual; |
| |
| /* Linux malloc guarantees at least 32-bit alignment */ |
| msg->mData = (unsigned char*) malloc(msg->mLength); |
| if (msg->mData == NULL) { |
| wsLog("alloc %d failed\n", msg->mLength); |
| return -1; |
| } |
| |
| if (readAll(fd, msg->mData, msg->mLength) != 0) { |
| wsLog("failed reading message body (wanted %d)\n", msg->mLength); |
| return -1; |
| } |
| } else { |
| msg->mData = NULL; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Write a message to the specified file descriptor. |
| * |
| * The caller must Message_release(&msg). |
| */ |
| static int Message_write(Message* msg, int fd) |
| { |
| struct iovec writeVec[2]; |
| unsigned char header[4]; |
| int len, numVecs; |
| ssize_t actual; |
| |
| len = msg->mLength + 2; |
| header[0] = len & 0xff; |
| header[1] = (len >> 8) & 0xff; |
| header[2] = msg->mType; |
| header[3] = 0; |
| writeVec[0].iov_base = header; |
| writeVec[0].iov_len = 4; |
| numVecs = 1; |
| |
| if (msg->mLength > 0) { |
| assert(msg->mData != NULL); |
| writeVec[1].iov_base = msg->mData; |
| writeVec[1].iov_len = msg->mLength; |
| numVecs++; |
| } |
| |
| /* write it all in one shot; not worrying about partial writes for now */ |
| actual = _ws_writev(fd, writeVec, numVecs); |
| if (actual != len+2) { |
| wsLog("failed writing message to fd %d: %d of %d %s\n", |
| fd, actual, len+2, strerror(errno)); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Release storage associated with a Message. |
| */ |
| static void Message_release(Message* msg) |
| { |
| free(msg->mData); |
| msg->mData = NULL; |
| } |
| |
| /* |
| * Extract a name/value pair from a message. |
| */ |
| static int getConfig(const Message* msg, const char** name, const char** val) |
| { |
| if (msg->mLength < 2) { |
| wsLog("message len (%d) is too short\n", msg->mLength); |
| return -1; |
| } |
| const char* ptr = (const char*) msg->mData; |
| |
| *name = (const char*) ptr; |
| *val = (const char*) (ptr + strlen((char*)ptr) +1); |
| return 0; |
| } |
| |
| /* |
| * Extract a command from a message. |
| */ |
| static int getCommand(const Message* msg, int* pCmd, int* pArg) |
| { |
| if (msg->mLength != sizeof(int) * 2) { |
| wsLog("message len (%d) is wrong for cmd (%d)\n", |
| msg->mLength, sizeof(int) * 2); |
| return -1; |
| } |
| |
| /* this assumes 32-bit alignment on mData */ |
| const int* ptr = (const int*) msg->mData; |
| |
| *pCmd = ptr[0]; |
| *pArg = ptr[1]; |
| |
| return 0; |
| } |
| |
| /* |
| * Extract an extended command from a message. |
| */ |
| static int getCommandExt(const Message* msg, int* pCmd, int* pArg0, |
| int* pArg1, int* pArg2) |
| { |
| if (msg->mLength != sizeof(int) * 4) { |
| wsLog("message len (%d) is wrong for cmd (%d)\n", |
| msg->mLength, sizeof(int) * 4); |
| return -1; |
| } |
| |
| /* this assumes 32-bit alignment on mData */ |
| const int* ptr = (const int*) msg->mData; |
| |
| *pCmd = ptr[0]; |
| *pArg0 = ptr[1]; |
| *pArg1 = ptr[2]; |
| *pArg2 = ptr[3]; |
| |
| return 0; |
| } |
| |
| /* |
| * Attach 8 bytes of data with "cmd" and "arg" to "msg". |
| * |
| * "msg->mData" will need to be freed by the caller. (This approach made |
| * more sense when C++ destructors were available, but it's just not worth |
| * reworking it.) |
| */ |
| static int setCommand(Message* msg, int cmd, int arg) |
| { |
| Message_clear(msg); |
| |
| msg->mLength = 8; |
| msg->mData = malloc(msg->mLength); |
| msg->mType = kTypeCommand; |
| |
| /* assumes 32-bit alignment on malloc blocks */ |
| int* pInt = (int*) msg->mData; |
| pInt[0] = cmd; |
| pInt[1] = arg; |
| |
| return 0; |
| } |
| |
| /* |
| * Construct the full path. The caller must free() the return value. |
| */ |
| static char* makeFilename(const char* name) |
| { |
| static const char* kBasePath = "/tmp/android-"; |
| char* fileName; |
| |
| assert(name != NULL && name[0] != '\0'); |
| |
| fileName = (char*) malloc(strlen(kBasePath) + strlen(name) + 1); |
| strcpy(fileName, kBasePath); |
| strcat(fileName, name); |
| |
| return fileName; |
| } |
| |
| /* |
| * Attach to a SysV shared memory segment. |
| */ |
| static int attachToShmem(int key, int* pShmid, void** pAddr, long* pLength) |
| { |
| int shmid; |
| |
| shmid = shmget(key, 0, 0); |
| if (shmid == -1) { |
| wsLog("ERROR: failed to find shmem key=%d\n", key); |
| return -1; |
| } |
| |
| void* addr = shmat(shmid, NULL, 0); |
| if (addr == (void*) -1) { |
| wsLog("ERROR: could not attach to key=%d shmid=%d\n", key, shmid); |
| return -1; |
| } |
| |
| struct shmid_ds shmids; |
| int cc; |
| |
| cc = shmctl(shmid, IPC_STAT, &shmids); |
| if (cc != 0) { |
| wsLog("ERROR: could not IPC_STAT shmid=%d\n", shmid); |
| return -1; |
| } |
| *pLength = shmids.shm_segsz; |
| |
| *pAddr = addr; |
| *pShmid = shmid; |
| return 0; |
| } |
| |
| /* |
| * Attach to a SysV semaphore. |
| */ |
| static int attachToSem(int key, int* pSemid) |
| { |
| int semid; |
| |
| semid = semget(key, 0, 0); |
| if (semid == -1) { |
| wsLog("ERROR: failed to attach to semaphore key=%d\n", key); |
| return -1; |
| } |
| |
| *pSemid = semid; |
| return 0; |
| } |
| |
| /* |
| * "Adjust" a semaphore. |
| */ |
| static int adjustSem(int semid, int adj) |
| { |
| const int wait = 1; |
| struct sembuf op; |
| int cc; |
| |
| op.sem_num = 0; |
| op.sem_op = adj; |
| op.sem_flg = SEM_UNDO; |
| if (!wait) |
| op.sem_flg |= IPC_NOWAIT; |
| |
| cc = semop(semid, &op, 1); |
| if (cc != 0) { |
| if (wait || errno != EAGAIN) { |
| wsLog("Warning:" |
| " semaphore adjust by %d failed for semid=%d (errno=%d)\n", |
| adj, semid, errno); |
| } |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Acquire the semaphore associated with a display. |
| */ |
| void wsLockDisplay(int displayIdx) |
| { |
| assert(displayIdx >= 0 && displayIdx < gWrapSim.numDisplays); |
| int semid = gWrapSim.display[displayIdx].semid; |
| |
| (void) adjustSem(semid, -1); |
| } |
| |
| /* |
| * Acquire the semaphore associated with a display. |
| */ |
| void wsUnlockDisplay(int displayIdx) |
| { |
| assert(displayIdx >= 0 && displayIdx < gWrapSim.numDisplays); |
| int semid = gWrapSim.display[displayIdx].semid; |
| |
| (void) adjustSem(semid, 1); |
| } |
| |
| /* |
| * Process the display config from the simulator |
| * |
| * Right now this is a blob of raw data that looks like this: |
| * +00 magic number |
| * +04 #of displays |
| * +08 display 0: |
| * +00 width |
| * +04 height |
| * +08 format |
| * +0c refresh rate |
| * +10 shmem key |
| * +1c display 1... |
| */ |
| static int handleDisplayConfig(const int* pData, int length) |
| { |
| int numDisplays; |
| |
| if (length < 8) { |
| wsLog("Bad display config: length is %d\n", length); |
| return -1; |
| } |
| assert(*pData == kDisplayConfigMagic); |
| |
| /* |
| * Pull out the #of displays. If it looks valid, configure the runtime. |
| */ |
| pData++; // skip over magic |
| numDisplays = *pData++; |
| |
| if (numDisplays <= 0 || numDisplays > kMaxDisplays) { |
| wsLog("Bizarre display count %d\n", numDisplays); |
| return -1; |
| } |
| if (length != 8 + numDisplays * kValuesPerDisplay * (int)sizeof(int)) { |
| wsLog("Bad display config: length is %d (expected %d)\n", |
| length, 8 + numDisplays * kValuesPerDisplay * (int)sizeof(int)); |
| return -1; |
| } |
| |
| /* |
| * Extract the config values. |
| * |
| * The runtime doesn't support multiple devices, so we don't either. |
| */ |
| int i; |
| for (i = 0; i < numDisplays; i++) { |
| gWrapSim.display[i].width = pData[0]; |
| gWrapSim.display[i].height = pData[1]; |
| gWrapSim.display[i].shmemKey = pData[4]; |
| /* format/refresh no longer needed */ |
| |
| void* addr; |
| int shmid, semid; |
| long length; |
| if (attachToShmem(gWrapSim.display[i].shmemKey, &shmid, &addr, |
| &length) != 0) |
| { |
| wsLog("Unable to connect to shared memory\n"); |
| return -1; |
| } |
| |
| if (attachToSem(gWrapSim.display[i].shmemKey, &semid) != 0) { |
| wsLog("Unable to attach to sempahore\n"); |
| return -1; |
| } |
| |
| gWrapSim.display[i].shmid = shmid; |
| gWrapSim.display[i].addr = addr; |
| gWrapSim.display[i].length = length; |
| gWrapSim.display[i].semid = semid; |
| |
| wsLog("Display %d: width=%d height=%d\n", |
| i, |
| gWrapSim.display[i].width, |
| gWrapSim.display[i].height); |
| wsLog(" shmem=0x%08x addr=%p len=%ld semid=%d\n", |
| gWrapSim.display[i].shmemKey, |
| gWrapSim.display[i].addr, |
| gWrapSim.display[i].length, |
| gWrapSim.display[i].semid); |
| |
| pData += kValuesPerDisplay; |
| } |
| gWrapSim.numDisplays = numDisplays; |
| |
| return 0; |
| } |
| |
| |
| /* |
| * Initialize our connection to the simulator, which will be listening on |
| * a UNIX domain socket. |
| * |
| * On success, this configures gWrapSim.simulatorFd and returns 0. |
| */ |
| static int openSimConnection(const char* name) |
| { |
| int result = -1; |
| char* fileName = NULL; |
| int sock = -1; |
| int cc; |
| |
| assert(gWrapSim.simulatorFd == -1); |
| |
| fileName = makeFilename(name); |
| |
| struct sockaddr_un addr; |
| |
| sock = socket(AF_UNIX, SOCK_STREAM, 0); |
| if (sock < 0) { |
| wsLog("UNIX domain socket create failed (errno=%d)\n", errno); |
| goto bail; |
| } |
| |
| /* connect to socket; fails if file doesn't exist */ |
| strcpy(addr.sun_path, fileName); // max 108 bytes |
| addr.sun_family = AF_UNIX; |
| cc = connect(sock, (struct sockaddr*) &addr, SUN_LEN(&addr)); |
| if (cc < 0) { |
| // ENOENT means socket file doesn't exist |
| // ECONNREFUSED means socket exists but nobody is listening |
| wsLog("AF_UNIX connect failed for '%s': %s\n", |
| fileName, strerror(errno)); |
| goto bail; |
| } |
| |
| gWrapSim.simulatorFd = sock; |
| sock = -1; |
| |
| result = 0; |
| wsLog("+++ connected to '%s'\n", fileName); |
| |
| bail: |
| if (sock >= 0) |
| _ws_close(sock); |
| free(fileName); |
| return result; |
| } |
| |
| /* |
| * Prepare communication with the front end. We wait for a "hello" from |
| * the other side, and respond in kind. |
| */ |
| static int prepSimConnection(void) |
| { |
| /* NOTE: this is endian-specific; we're x86 Linux only, so no problem */ |
| static const unsigned int hello = kHelloMsg; |
| static const unsigned int helloAck = kHelloAckMsg; |
| Message msg; |
| |
| if (Message_read(&msg, gWrapSim.simulatorFd) != 0) { |
| wsLog("hello read failed\n"); |
| return -1; |
| } |
| |
| if (memcmp(msg.mData, &hello, 4) != 0) { |
| wsLog("Got bad hello from peer\n"); |
| return -1; |
| } |
| |
| Message_release(&msg); |
| |
| msg.mType = kTypeRaw; |
| msg.mData = (unsigned char*) &helloAck; |
| msg.mLength = 4; |
| |
| if (Message_write(&msg, gWrapSim.simulatorFd) != 0) { |
| wsLog("hello ack write failed\n"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Get the sim front-end configuration. We loop here until the sim claims |
| * to be done with us. |
| */ |
| static int getSimConfig(void) |
| { |
| Message msg; |
| int joinNewGroup, grabTerminal, done; |
| int result = -1; |
| |
| joinNewGroup = grabTerminal = done = 0; |
| Message_clear(&msg); // clear out msg->mData |
| |
| wsLog("+++ awaiting hardware configuration\n"); |
| while (!done) { |
| if (Message_read(&msg, gWrapSim.simulatorFd) != 0) { |
| wsLog("failed receiving config from parent\n"); |
| goto bail; |
| } |
| |
| if (msg.mType == kTypeCommand) { |
| int cmd, arg; |
| |
| if (getCommand(&msg, &cmd, &arg) != 0) |
| goto bail; |
| |
| switch (cmd) { |
| case kCommandGoAway: |
| wsLog("Simulator front-end is busy\n"); |
| goto bail; |
| case kCommandNewPGroup: |
| joinNewGroup = 1; |
| grabTerminal = (arg != 0); |
| wsLog("Simulator wants us to be in a new pgrp (term=%d)\n", |
| grabTerminal); |
| break; |
| case kCommandConfigDone: |
| done = 1; |
| break; |
| default: |
| wsLog("Got unexpected command %d/%d\n", cmd, arg); |
| break; |
| } |
| } else if (msg.mType == kTypeRaw) { |
| /* assumes 32-bit alignment and identical byte ordering */ |
| int* pData = (int*) msg.mData; |
| if (msg.mLength >= 4 && *pData == kDisplayConfigMagic) { |
| if (handleDisplayConfig(pData, msg.mLength) != 0) |
| goto bail; |
| } |
| } else if (msg.mType == kTypeConfig) { |
| const char *name; |
| const char *val; |
| getConfig(&msg, &name, &val); |
| if(strcmp(name, "keycharmap") == 0) { |
| free((void*)gWrapSim.keyMap); |
| gWrapSim.keyMap = strdup(val); |
| } |
| } else { |
| wsLog("Unexpected msg type %d during startup\n", msg.mType); |
| goto bail; |
| } |
| |
| /* clear out the data field if necessary */ |
| Message_release(&msg); |
| } |
| |
| wsLog("Configuration received from simulator\n"); |
| |
| if (joinNewGroup) { |
| /* set pgid to pid */ |
| pid_t pgid = getpid(); |
| setpgid(0, pgid); |
| |
| /* |
| * Put our pgrp in the foreground. |
| * tcsetpgrp() from background process causes us to get a SIGTTOU, |
| * which is mostly harmless but makes tcsetpgrp() fail with EINTR. |
| */ |
| signal(SIGTTOU, SIG_IGN); |
| if (grabTerminal) { |
| if (tcsetpgrp(fileno(stdin), getpgrp()) != 0) { |
| wsLog("tcsetpgrp(%d, %d) failed (errno=%d)\n", |
| fileno(stdin), getpgrp(), errno); |
| } |
| wsLog("Set pgrp %d as foreground\n", (int) getpgrp()); |
| } |
| |
| /* tell the sim where we're at */ |
| Message msg; |
| setCommand(&msg, kCommandNewPGroupCreated, pgid); |
| Message_write(&msg, gWrapSim.simulatorFd); |
| Message_release(&msg); |
| } |
| |
| result = 0; |
| |
| bail: |
| /* make sure the data was freed */ |
| Message_release(&msg); |
| //wsLog("bailing, result=%d\n", result); |
| return result; |
| } |
| |
| /* |
| * Connect to the simulator and exchange pleasantries. |
| * |
| * Returns 0 on success. |
| */ |
| static int connectToSim(void) |
| { |
| if (openSimConnection(kAndroidPipeName) != 0) |
| return -1; |
| |
| if (prepSimConnection() != 0) |
| return -1; |
| |
| if (getSimConfig() != 0) |
| return -1; |
| |
| wsLog("+++ sim is ready to go\n"); |
| |
| return 0; |
| } |
| |
| /* |
| * Listen to the sim forever or until the front end shuts down, whichever |
| * comes first. |
| * |
| * All we're really getting here are key events. |
| */ |
| static void listenToSim(void) |
| { |
| wsLog("--- listening for input events from front end\n"); |
| |
| while (1) { |
| Message msg; |
| |
| Message_clear(&msg); |
| if (Message_read(&msg, gWrapSim.simulatorFd) != 0) { |
| wsLog("--- sim message read failed\n"); |
| return; |
| } |
| |
| if (msg.mType == kTypeCommand) { |
| int cmd, arg; |
| |
| if (getCommand(&msg, &cmd, &arg) != 0) { |
| wsLog("bad command from sim?\n"); |
| continue; |
| } |
| |
| switch (cmd) { |
| case kCommandQuit: |
| wsLog("--- sim sent us a QUIT message\n"); |
| return; |
| case kCommandKeyDown: |
| wsLog("KEY DOWN: %d\n", arg); |
| wsSendSimKeyEvent(arg, 1); |
| break; |
| case kCommandKeyUp: |
| wsLog("KEY UP: %d\n", arg); |
| wsSendSimKeyEvent(arg, 0); |
| break; |
| default: |
| wsLog("--- sim sent unrecognized command %d\n", cmd); |
| break; |
| } |
| |
| Message_release(&msg); |
| } else if (msg.mType == kTypeCommandExt) { |
| int cmd, arg0, arg1, arg2; |
| |
| if (getCommandExt(&msg, &cmd, &arg0, &arg1, &arg2) != 0) { |
| wsLog("bad ext-command from sim?\n"); |
| continue; |
| } |
| |
| switch (cmd) { |
| case kCommandTouch: |
| wsSendSimTouchEvent(arg0, arg1, arg2); |
| break; |
| } |
| |
| Message_release(&msg); |
| } else { |
| wsLog("--- sim sent non-command message, type=%d\n", msg.mType); |
| } |
| } |
| |
| assert(0); // not reached |
| } |
| |
| |
| /* |
| * Tell the simulator front-end that the display has been updated. |
| */ |
| void wsPostDisplayUpdate(int displayIdx) |
| { |
| if (gWrapSim.simulatorFd < 0) { |
| wsLog("Not posting display update -- sim not ready\n"); |
| return; |
| } |
| |
| Message msg; |
| |
| setCommand(&msg, kCommandUpdateDisplay, displayIdx); |
| Message_write(&msg, gWrapSim.simulatorFd); |
| Message_release(&msg); |
| } |
| |
| /* |
| * Send a log message to the front-end. |
| */ |
| void wsPostLogMessage(int logPrio, const char* tag, const char* message) |
| { |
| if (gWrapSim.simulatorFd < 0) { |
| wsLog("Not posting log message -- sim not ready\n"); |
| return; |
| } |
| |
| time_t when = time(NULL); |
| int pid = (int) getpid(); |
| int tagLen, messageLen, totalLen; |
| |
| tagLen = strlen(tag) +1; |
| messageLen = strlen(message) +1; |
| totalLen = sizeof(int) * 3 + tagLen + messageLen; |
| unsigned char outBuf[totalLen]; |
| unsigned char* cp = outBuf; |
| |
| /* See Message::set/getLogBundle() in simulator/MessageStream.cpp. */ |
| memcpy(cp, &when, sizeof(int)); |
| cp += sizeof(int); |
| memcpy(cp, &logPrio, sizeof(int)); |
| cp += sizeof(int); |
| memcpy(cp, &pid, sizeof(int)); |
| cp += sizeof(int); |
| memcpy(cp, tag, tagLen); |
| cp += tagLen; |
| memcpy(cp, message, messageLen); |
| cp += messageLen; |
| |
| assert(cp - outBuf == totalLen); |
| |
| Message msg; |
| msg.mType = kTypeLogBundle; |
| msg.mData = outBuf; |
| msg.mLength = totalLen; |
| Message_write(&msg, gWrapSim.simulatorFd); |
| |
| msg.mData = NULL; // don't free |
| Message_release(&msg); |
| } |
| |
| /* |
| * Turn the vibrating notification device on or off. |
| */ |
| void wsEnableVibration(int vibrateOn) |
| { |
| if (gWrapSim.simulatorFd < 0) { |
| wsLog("Not posting vibrator update -- sim not ready\n"); |
| return; |
| } |
| |
| Message msg; |
| |
| //wsLog("+++ sending vibrate:%d\n", vibrateOn); |
| |
| setCommand(&msg, kCommandVibrate, vibrateOn); |
| Message_write(&msg, gWrapSim.simulatorFd); |
| Message_release(&msg); |
| } |
| |