blob: dc0c30997434a9a9e93a060b882e9b77eedb8e07 [file] [log] [blame]
/*
* Copyright (C) 2019 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 <source/TouchSink.h>
#include <https/SafeCallbackable.h>
#include <https/Support.h>
#include <android-base/logging.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <sys/socket.h>
#include <unistd.h>
namespace android {
namespace {
// TODO de-dup this from vnc server and here
struct virtio_input_event {
uint16_t type;
uint16_t code;
int32_t value;
};
template <typename T>
void SendEvent(int32_t x, int32_t y, int32_t down,
std::function<void(const void*, size_t)> sender) {
std::vector<T> events = {{.type = EV_ABS, .code = ABS_X, .value = x},
{.type = EV_ABS, .code = ABS_Y, .value = y},
{.type = EV_KEY, .code = BTN_TOUCH, .value = down},
{.type = EV_SYN, .code = 0, .value = 0}};
sender(events.data(), events.size() * sizeof(T));
}
template <typename T>
void SendMTEvent(int32_t /* id */, int32_t x, int32_t y, int32_t initial_down,
int32_t /* slot */,
std::function<void(const void*, size_t)> sender) {
// TODO(b/124121375): multitouch
SendEvent<T>(x, y, initial_down, sender);
}
}
TouchSink::TouchSink(std::shared_ptr<RunLoop> runLoop, int serverFd,
bool write_virtio_input)
: mRunLoop(runLoop),
mServerFd(serverFd),
mClientFd(-1),
mSendPending(false) {
if (mServerFd >= 0) {
makeFdNonblocking(mServerFd);
}
if (write_virtio_input) {
send_event_ = [this](int32_t x, int32_t y, bool down) {
SendEvent<virtio_input_event>(
x, y, down, [this](const void* b, size_t l) { sendRawEvents(b, l); });
};
send_mt_event_ = [this](int32_t id, int32_t x, int32_t y, bool initialDown,
int32_t slot) {
SendMTEvent<virtio_input_event>(
id, x, y, initialDown, slot,
[this](const void* b, size_t l) { sendRawEvents(b, l); });
};
} else {
send_event_ = [this](int32_t x, int32_t y, bool down) {
SendEvent<input_event>(
x, y, down, [this](const void* b, size_t l) { sendRawEvents(b, l); });
};
send_mt_event_ = [this](int32_t id, int32_t x, int32_t y, bool initialDown,
int32_t slot) {
SendMTEvent<input_event>(
id, x, y, initialDown, slot,
[this](const void* b, size_t l) { sendRawEvents(b, l); });
};
}
}
TouchSink::~TouchSink() {
if (mClientFd >= 0) {
mRunLoop->cancelSocket(mClientFd);
close(mClientFd);
mClientFd = -1;
}
if (mServerFd >= 0) {
mRunLoop->cancelSocket(mServerFd);
close(mServerFd);
mServerFd = -1;
}
}
void TouchSink::start() {
if (mServerFd < 0) {
return;
}
mRunLoop->postSocketRecv(
mServerFd,
makeSafeCallback(this, &TouchSink::onServerConnection));
}
void TouchSink::onServerConnection() {
int s = accept(mServerFd, nullptr, nullptr);
if (s >= 0) {
if (mClientFd >= 0) {
LOG(INFO) << "Rejecting client, we already have one.";
// We already have a client.
close(s);
s = -1;
} else {
LOG(INFO) << "Accepted client socket " << s << ".";
makeFdNonblocking(s);
mClientFd = s;
}
}
mRunLoop->postSocketRecv(
mServerFd,
makeSafeCallback(this, &TouchSink::onServerConnection));
}
void TouchSink::onAccessUnit(const std::shared_ptr<InputEvent> &accessUnit) {
bool down = accessUnit->down != 0;
int x = accessUnit->x;
int y = accessUnit->y;
LOG(VERBOSE)
<< "Received touch (down="
<< down
<< ", x="
<< x
<< ", y="
<< y;
send_event_(x, y, down);
}
void TouchSink::sendRawEvents(const void* evt_buffer, size_t size) {
if (size <= 0) return;
std::lock_guard autoLock(mLock);
if (mClientFd < 0) {
return;
}
size_t offset = mOutBuffer.size();
mOutBuffer.resize(offset + size);
memcpy(mOutBuffer.data() + offset, evt_buffer, size);
if (!mSendPending) {
mSendPending = true;
mRunLoop->postSocketSend(
mClientFd,
makeSafeCallback(this, &TouchSink::onSocketSend));
}
}
void TouchSink::onSocketSend() {
std::lock_guard autoLock(mLock);
CHECK(mSendPending);
mSendPending = false;
if (mClientFd < 0) {
return;
}
ssize_t n;
while (!mOutBuffer.empty()) {
do {
n = ::send(mClientFd, mOutBuffer.data(), mOutBuffer.size(), 0);
} while (n < 0 && errno == EINTR);
if (n <= 0) {
break;
}
mOutBuffer.erase(mOutBuffer.begin(), mOutBuffer.begin() + n);
}
if ((n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) || n == 0) {
LOG(ERROR) << "Client is gone.";
// Client is gone.
mRunLoop->cancelSocket(mClientFd);
close(mClientFd);
mClientFd = -1;
return;
}
if (!mOutBuffer.empty()) {
mSendPending = true;
mRunLoop->postSocketSend(
mClientFd,
makeSafeCallback(this, &TouchSink::onSocketSend));
}
}
} // namespace android