Compare commits
No commits in common. "0d4574f205e7b64d78e2b3341caf471b438c035b" and "f55fa715f2667e5d478e79162210019b2c7cc16a" have entirely different histories.
0d4574f205
...
f55fa715f2
|
@ -1,2 +0,0 @@
|
||||||
bin/
|
|
||||||
ve/
|
|
13
Makefile
13
Makefile
|
@ -1,13 +0,0 @@
|
||||||
all: capture playback
|
|
||||||
|
|
||||||
bin:
|
|
||||||
mkdir -p bin
|
|
||||||
|
|
||||||
capture: bin capture.cpp
|
|
||||||
g++ capture.c -o bin/capture
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -Rf bin
|
|
||||||
|
|
||||||
playback: bin playback.cpp
|
|
||||||
g++ playback.c -o bin/playback
|
|
|
@ -1,5 +1,3 @@
|
||||||
# symkey
|
# symkey
|
||||||
|
|
||||||
This is a quick-and-dirty project for programmatically controlling keyboard and mouse.
|
A simple play tool for capturing keyboard and mouse and replaying them. Useful for Hypixel Skyblock
|
||||||
I use it primarily to autoplay Minecraft on Hypixel Skyblock.
|
|
||||||
Shhh, don't tell.
|
|
|
@ -1,20 +0,0 @@
|
||||||
all: client
|
|
||||||
|
|
||||||
CAPNPROTO=/home/eliribble/src/capnproto
|
|
||||||
CXX=g++
|
|
||||||
LIBS=\
|
|
||||||
kj \
|
|
||||||
kj-async \
|
|
||||||
capnp \
|
|
||||||
capnp-rpc
|
|
||||||
|
|
||||||
calculator.capnp.h: calculator.capnp
|
|
||||||
capnp compile -oc++ calculator.capnp
|
|
||||||
|
|
||||||
|
|
||||||
CLIENT_SRCS=calculator-client.c++ calculator.capnp.c++
|
|
||||||
client: $(CLIENT_SRCS) calculator.capnp.h
|
|
||||||
$(CXX) $(CLIENT_SRCS) -L $(CAPNPROTO)/c++/.libs $(addprefix -l,$(LIBS)) -o client
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm client
|
|
|
@ -1,367 +0,0 @@
|
||||||
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
|
|
||||||
// Licensed under the MIT License:
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
|
|
||||||
#include "calculator.capnp.h"
|
|
||||||
#include <capnp/ez-rpc.h>
|
|
||||||
#include <kj/debug.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
class PowerFunction final: public Calculator::Function::Server {
|
|
||||||
// An implementation of the Function interface wrapping pow(). Note that
|
|
||||||
// we're implementing this on the client side and will pass a reference to
|
|
||||||
// the server. The server will then be able to make calls back to the client.
|
|
||||||
|
|
||||||
public:
|
|
||||||
kj::Promise<void> call(CallContext context) {
|
|
||||||
auto params = context.getParams().getParams();
|
|
||||||
KJ_REQUIRE(params.size() == 2, "Wrong number of parameters.");
|
|
||||||
context.getResults().setValue(pow(params[0], params[1]));
|
|
||||||
return kj::READY_NOW;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, const char* argv[]) {
|
|
||||||
if (argc != 2) {
|
|
||||||
std::cerr << "usage: " << argv[0] << " HOST:PORT\n"
|
|
||||||
"Connects to the Calculator server at the given address and "
|
|
||||||
"does some RPCs." << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
capnp::EzRpcClient client(argv[1]);
|
|
||||||
Calculator::Client calculator = client.getMain<Calculator>();
|
|
||||||
|
|
||||||
// Keep an eye on `waitScope`. Whenever you see it used is a place where we
|
|
||||||
// stop and wait for the server to respond. If a line of code does not use
|
|
||||||
// `waitScope`, then it does not block!
|
|
||||||
auto& waitScope = client.getWaitScope();
|
|
||||||
|
|
||||||
{
|
|
||||||
// Make a request that just evaluates the literal value 123.
|
|
||||||
//
|
|
||||||
// What's interesting here is that evaluate() returns a "Value", which is
|
|
||||||
// another interface and therefore points back to an object living on the
|
|
||||||
// server. We then have to call read() on that object to read it.
|
|
||||||
// However, even though we are making two RPC's, this block executes in
|
|
||||||
// *one* network round trip because of promise pipelining: we do not wait
|
|
||||||
// for the first call to complete before we send the second call to the
|
|
||||||
// server.
|
|
||||||
|
|
||||||
std::cout << "Evaluating a literal... ";
|
|
||||||
std::cout.flush();
|
|
||||||
|
|
||||||
// Set up the request.
|
|
||||||
auto request = calculator.evaluateRequest();
|
|
||||||
request.getExpression().setLiteral(123);
|
|
||||||
|
|
||||||
// Send it, which returns a promise for the result (without blocking).
|
|
||||||
auto evalPromise = request.send();
|
|
||||||
|
|
||||||
// Using the promise, create a pipelined request to call read() on the
|
|
||||||
// returned object, and then send that.
|
|
||||||
auto readPromise = evalPromise.getValue().readRequest().send();
|
|
||||||
|
|
||||||
// Now that we've sent all the requests, wait for the response. Until this
|
|
||||||
// point, we haven't waited at all!
|
|
||||||
auto response = readPromise.wait(waitScope);
|
|
||||||
KJ_ASSERT(response.getValue() == 123);
|
|
||||||
|
|
||||||
std::cout << "PASS" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// Make a request to evaluate 123 + 45 - 67.
|
|
||||||
//
|
|
||||||
// The Calculator interface requires that we first call getOperator() to
|
|
||||||
// get the addition and subtraction functions, then call evaluate() to use
|
|
||||||
// them. But, once again, we can get both functions, call evaluate(), and
|
|
||||||
// then read() the result -- four RPCs -- in the time of *one* network
|
|
||||||
// round trip, because of promise pipelining.
|
|
||||||
|
|
||||||
std::cout << "Using add and subtract... ";
|
|
||||||
std::cout.flush();
|
|
||||||
|
|
||||||
Calculator::Function::Client add = nullptr;
|
|
||||||
Calculator::Function::Client subtract = nullptr;
|
|
||||||
|
|
||||||
{
|
|
||||||
// Get the "add" function from the server.
|
|
||||||
auto request = calculator.getOperatorRequest();
|
|
||||||
request.setOp(Calculator::Operator::ADD);
|
|
||||||
add = request.send().getFunc();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// Get the "subtract" function from the server.
|
|
||||||
auto request = calculator.getOperatorRequest();
|
|
||||||
request.setOp(Calculator::Operator::SUBTRACT);
|
|
||||||
subtract = request.send().getFunc();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the request to evaluate 123 + 45 - 67.
|
|
||||||
auto request = calculator.evaluateRequest();
|
|
||||||
|
|
||||||
auto subtractCall = request.getExpression().initCall();
|
|
||||||
subtractCall.setFunction(subtract);
|
|
||||||
auto subtractParams = subtractCall.initParams(2);
|
|
||||||
subtractParams[1].setLiteral(67);
|
|
||||||
|
|
||||||
auto addCall = subtractParams[0].initCall();
|
|
||||||
addCall.setFunction(add);
|
|
||||||
auto addParams = addCall.initParams(2);
|
|
||||||
addParams[0].setLiteral(123);
|
|
||||||
addParams[1].setLiteral(45);
|
|
||||||
|
|
||||||
// Send the evaluate() request, read() the result, and wait for read() to
|
|
||||||
// finish.
|
|
||||||
auto evalPromise = request.send();
|
|
||||||
auto readPromise = evalPromise.getValue().readRequest().send();
|
|
||||||
|
|
||||||
auto response = readPromise.wait(waitScope);
|
|
||||||
KJ_ASSERT(response.getValue() == 101);
|
|
||||||
|
|
||||||
std::cout << "PASS" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// Make a request to evaluate 4 * 6, then use the result in two more
|
|
||||||
// requests that add 3 and 5.
|
|
||||||
//
|
|
||||||
// Since evaluate() returns its result wrapped in a `Value`, we can pass
|
|
||||||
// that `Value` back to the server in subsequent requests before the first
|
|
||||||
// `evaluate()` has actually returned. Thus, this example again does only
|
|
||||||
// one network round trip.
|
|
||||||
|
|
||||||
std::cout << "Pipelining eval() calls... ";
|
|
||||||
std::cout.flush();
|
|
||||||
|
|
||||||
Calculator::Function::Client add = nullptr;
|
|
||||||
Calculator::Function::Client multiply = nullptr;
|
|
||||||
|
|
||||||
{
|
|
||||||
// Get the "add" function from the server.
|
|
||||||
auto request = calculator.getOperatorRequest();
|
|
||||||
request.setOp(Calculator::Operator::ADD);
|
|
||||||
add = request.send().getFunc();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// Get the "multiply" function from the server.
|
|
||||||
auto request = calculator.getOperatorRequest();
|
|
||||||
request.setOp(Calculator::Operator::MULTIPLY);
|
|
||||||
multiply = request.send().getFunc();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the request to evaluate 4 * 6
|
|
||||||
auto request = calculator.evaluateRequest();
|
|
||||||
|
|
||||||
auto multiplyCall = request.getExpression().initCall();
|
|
||||||
multiplyCall.setFunction(multiply);
|
|
||||||
auto multiplyParams = multiplyCall.initParams(2);
|
|
||||||
multiplyParams[0].setLiteral(4);
|
|
||||||
multiplyParams[1].setLiteral(6);
|
|
||||||
|
|
||||||
auto multiplyResult = request.send().getValue();
|
|
||||||
|
|
||||||
// Use the result in two calls that add 3 and add 5.
|
|
||||||
|
|
||||||
auto add3Request = calculator.evaluateRequest();
|
|
||||||
auto add3Call = add3Request.getExpression().initCall();
|
|
||||||
add3Call.setFunction(add);
|
|
||||||
auto add3Params = add3Call.initParams(2);
|
|
||||||
add3Params[0].setPreviousResult(multiplyResult);
|
|
||||||
add3Params[1].setLiteral(3);
|
|
||||||
auto add3Promise = add3Request.send().getValue().readRequest().send();
|
|
||||||
|
|
||||||
auto add5Request = calculator.evaluateRequest();
|
|
||||||
auto add5Call = add5Request.getExpression().initCall();
|
|
||||||
add5Call.setFunction(add);
|
|
||||||
auto add5Params = add5Call.initParams(2);
|
|
||||||
add5Params[0].setPreviousResult(multiplyResult);
|
|
||||||
add5Params[1].setLiteral(5);
|
|
||||||
auto add5Promise = add5Request.send().getValue().readRequest().send();
|
|
||||||
|
|
||||||
// Now wait for the results.
|
|
||||||
KJ_ASSERT(add3Promise.wait(waitScope).getValue() == 27);
|
|
||||||
KJ_ASSERT(add5Promise.wait(waitScope).getValue() == 29);
|
|
||||||
|
|
||||||
std::cout << "PASS" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// Our calculator interface supports defining functions. Here we use it
|
|
||||||
// to define two functions and then make calls to them as follows:
|
|
||||||
//
|
|
||||||
// f(x, y) = x * 100 + y
|
|
||||||
// g(x) = f(x, x + 1) * 2;
|
|
||||||
// f(12, 34)
|
|
||||||
// g(21)
|
|
||||||
//
|
|
||||||
// Once again, the whole thing takes only one network round trip.
|
|
||||||
|
|
||||||
std::cout << "Defining functions... ";
|
|
||||||
std::cout.flush();
|
|
||||||
|
|
||||||
Calculator::Function::Client add = nullptr;
|
|
||||||
Calculator::Function::Client multiply = nullptr;
|
|
||||||
Calculator::Function::Client f = nullptr;
|
|
||||||
Calculator::Function::Client g = nullptr;
|
|
||||||
|
|
||||||
{
|
|
||||||
// Get the "add" function from the server.
|
|
||||||
auto request = calculator.getOperatorRequest();
|
|
||||||
request.setOp(Calculator::Operator::ADD);
|
|
||||||
add = request.send().getFunc();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// Get the "multiply" function from the server.
|
|
||||||
auto request = calculator.getOperatorRequest();
|
|
||||||
request.setOp(Calculator::Operator::MULTIPLY);
|
|
||||||
multiply = request.send().getFunc();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// Define f.
|
|
||||||
auto request = calculator.defFunctionRequest();
|
|
||||||
request.setParamCount(2);
|
|
||||||
|
|
||||||
{
|
|
||||||
// Build the function body.
|
|
||||||
auto addCall = request.getBody().initCall();
|
|
||||||
addCall.setFunction(add);
|
|
||||||
auto addParams = addCall.initParams(2);
|
|
||||||
addParams[1].setParameter(1); // y
|
|
||||||
|
|
||||||
auto multiplyCall = addParams[0].initCall();
|
|
||||||
multiplyCall.setFunction(multiply);
|
|
||||||
auto multiplyParams = multiplyCall.initParams(2);
|
|
||||||
multiplyParams[0].setParameter(0); // x
|
|
||||||
multiplyParams[1].setLiteral(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
f = request.send().getFunc();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// Define g.
|
|
||||||
auto request = calculator.defFunctionRequest();
|
|
||||||
request.setParamCount(1);
|
|
||||||
|
|
||||||
{
|
|
||||||
// Build the function body.
|
|
||||||
auto multiplyCall = request.getBody().initCall();
|
|
||||||
multiplyCall.setFunction(multiply);
|
|
||||||
auto multiplyParams = multiplyCall.initParams(2);
|
|
||||||
multiplyParams[1].setLiteral(2);
|
|
||||||
|
|
||||||
auto fCall = multiplyParams[0].initCall();
|
|
||||||
fCall.setFunction(f);
|
|
||||||
auto fParams = fCall.initParams(2);
|
|
||||||
fParams[0].setParameter(0);
|
|
||||||
|
|
||||||
auto addCall = fParams[1].initCall();
|
|
||||||
addCall.setFunction(add);
|
|
||||||
auto addParams = addCall.initParams(2);
|
|
||||||
addParams[0].setParameter(0);
|
|
||||||
addParams[1].setLiteral(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
g = request.send().getFunc();
|
|
||||||
}
|
|
||||||
|
|
||||||
// OK, we've defined all our functions. Now create our eval requests.
|
|
||||||
|
|
||||||
// f(12, 34)
|
|
||||||
auto fEvalRequest = calculator.evaluateRequest();
|
|
||||||
auto fCall = fEvalRequest.initExpression().initCall();
|
|
||||||
fCall.setFunction(f);
|
|
||||||
auto fParams = fCall.initParams(2);
|
|
||||||
fParams[0].setLiteral(12);
|
|
||||||
fParams[1].setLiteral(34);
|
|
||||||
auto fEvalPromise = fEvalRequest.send().getValue().readRequest().send();
|
|
||||||
|
|
||||||
// g(21)
|
|
||||||
auto gEvalRequest = calculator.evaluateRequest();
|
|
||||||
auto gCall = gEvalRequest.initExpression().initCall();
|
|
||||||
gCall.setFunction(g);
|
|
||||||
gCall.initParams(1)[0].setLiteral(21);
|
|
||||||
auto gEvalPromise = gEvalRequest.send().getValue().readRequest().send();
|
|
||||||
|
|
||||||
// Wait for the results.
|
|
||||||
KJ_ASSERT(fEvalPromise.wait(waitScope).getValue() == 1234);
|
|
||||||
KJ_ASSERT(gEvalPromise.wait(waitScope).getValue() == 4244);
|
|
||||||
|
|
||||||
std::cout << "PASS" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// Make a request that will call back to a function defined locally.
|
|
||||||
//
|
|
||||||
// Specifically, we will compute 2^(4 + 5). However, exponent is not
|
|
||||||
// defined by the Calculator server. So, we'll implement the Function
|
|
||||||
// interface locally and pass it to the server for it to use when
|
|
||||||
// evaluating the expression.
|
|
||||||
//
|
|
||||||
// This example requires two network round trips to complete, because the
|
|
||||||
// server calls back to the client once before finishing. In this
|
|
||||||
// particular case, this could potentially be optimized by using a tail
|
|
||||||
// call on the server side -- see CallContext::tailCall(). However, to
|
|
||||||
// keep the example simpler, we haven't implemented this optimization in
|
|
||||||
// the sample server.
|
|
||||||
|
|
||||||
std::cout << "Using a callback... ";
|
|
||||||
std::cout.flush();
|
|
||||||
|
|
||||||
Calculator::Function::Client add = nullptr;
|
|
||||||
|
|
||||||
{
|
|
||||||
// Get the "add" function from the server.
|
|
||||||
auto request = calculator.getOperatorRequest();
|
|
||||||
request.setOp(Calculator::Operator::ADD);
|
|
||||||
add = request.send().getFunc();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the eval request for 2^(4+5).
|
|
||||||
auto request = calculator.evaluateRequest();
|
|
||||||
|
|
||||||
auto powCall = request.getExpression().initCall();
|
|
||||||
powCall.setFunction(kj::heap<PowerFunction>());
|
|
||||||
auto powParams = powCall.initParams(2);
|
|
||||||
powParams[0].setLiteral(2);
|
|
||||||
|
|
||||||
auto addCall = powParams[1].initCall();
|
|
||||||
addCall.setFunction(add);
|
|
||||||
auto addParams = addCall.initParams(2);
|
|
||||||
addParams[0].setLiteral(4);
|
|
||||||
addParams[1].setLiteral(5);
|
|
||||||
|
|
||||||
// Send the request and wait.
|
|
||||||
auto response = request.send().getValue().readRequest()
|
|
||||||
.send().wait(waitScope);
|
|
||||||
KJ_ASSERT(response.getValue() == 512);
|
|
||||||
|
|
||||||
std::cout << "PASS" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,215 +0,0 @@
|
||||||
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
|
|
||||||
// Licensed under the MIT License:
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
// THE SOFTWARE.
|
|
||||||
|
|
||||||
#include "calculator.capnp.h"
|
|
||||||
#include <kj/debug.h>
|
|
||||||
#include <capnp/ez-rpc.h>
|
|
||||||
#include <capnp/message.h>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
typedef unsigned int uint;
|
|
||||||
|
|
||||||
kj::Promise<double> readValue(Calculator::Value::Client value) {
|
|
||||||
// Helper function to asynchronously call read() on a Calculator::Value and
|
|
||||||
// return a promise for the result. (In the future, the generated code might
|
|
||||||
// include something like this automatically.)
|
|
||||||
|
|
||||||
return value.readRequest().send()
|
|
||||||
.then([](capnp::Response<Calculator::Value::ReadResults> result) {
|
|
||||||
return result.getValue();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
kj::Promise<double> evaluateImpl(
|
|
||||||
Calculator::Expression::Reader expression,
|
|
||||||
capnp::List<double>::Reader params = capnp::List<double>::Reader()) {
|
|
||||||
// Implementation of CalculatorImpl::evaluate(), also shared by
|
|
||||||
// FunctionImpl::call(). In the latter case, `params` are the parameter
|
|
||||||
// values passed to the function; in the former case, `params` is just an
|
|
||||||
// empty list.
|
|
||||||
|
|
||||||
switch (expression.which()) {
|
|
||||||
case Calculator::Expression::LITERAL:
|
|
||||||
return expression.getLiteral();
|
|
||||||
|
|
||||||
case Calculator::Expression::PREVIOUS_RESULT:
|
|
||||||
return readValue(expression.getPreviousResult());
|
|
||||||
|
|
||||||
case Calculator::Expression::PARAMETER: {
|
|
||||||
KJ_REQUIRE(expression.getParameter() < params.size(),
|
|
||||||
"Parameter index out-of-range.");
|
|
||||||
return params[expression.getParameter()];
|
|
||||||
}
|
|
||||||
|
|
||||||
case Calculator::Expression::CALL: {
|
|
||||||
auto call = expression.getCall();
|
|
||||||
auto func = call.getFunction();
|
|
||||||
|
|
||||||
// Evaluate each parameter.
|
|
||||||
kj::Array<kj::Promise<double>> paramPromises =
|
|
||||||
KJ_MAP(param, call.getParams()) {
|
|
||||||
return evaluateImpl(param, params);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Join the array of promises into a promise for an array.
|
|
||||||
kj::Promise<kj::Array<double>> joinedParams =
|
|
||||||
kj::joinPromises(kj::mv(paramPromises));
|
|
||||||
|
|
||||||
// When the parameters are complete, call the function.
|
|
||||||
return joinedParams.then([KJ_CPCAP(func)](kj::Array<double>&& paramValues) mutable {
|
|
||||||
auto request = func.callRequest();
|
|
||||||
request.setParams(paramValues);
|
|
||||||
return request.send().then(
|
|
||||||
[](capnp::Response<Calculator::Function::CallResults>&& result) {
|
|
||||||
return result.getValue();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Throw an exception.
|
|
||||||
KJ_FAIL_REQUIRE("Unknown expression type.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ValueImpl final: public Calculator::Value::Server {
|
|
||||||
// Simple implementation of the Calculator.Value Cap'n Proto interface.
|
|
||||||
|
|
||||||
public:
|
|
||||||
ValueImpl(double value): value(value) {}
|
|
||||||
|
|
||||||
kj::Promise<void> read(ReadContext context) {
|
|
||||||
context.getResults().setValue(value);
|
|
||||||
return kj::READY_NOW;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
double value;
|
|
||||||
};
|
|
||||||
|
|
||||||
class FunctionImpl final: public Calculator::Function::Server {
|
|
||||||
// Implementation of the Calculator.Function Cap'n Proto interface, where the
|
|
||||||
// function is defined by a Calculator.Expression.
|
|
||||||
|
|
||||||
public:
|
|
||||||
FunctionImpl(uint paramCount, Calculator::Expression::Reader body)
|
|
||||||
: paramCount(paramCount) {
|
|
||||||
this->body.setRoot(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
kj::Promise<void> call(CallContext context) {
|
|
||||||
auto params = context.getParams().getParams();
|
|
||||||
KJ_REQUIRE(params.size() == paramCount, "Wrong number of parameters.");
|
|
||||||
|
|
||||||
return evaluateImpl(body.getRoot<Calculator::Expression>(), params)
|
|
||||||
.then([KJ_CPCAP(context)](double value) mutable {
|
|
||||||
context.getResults().setValue(value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint paramCount;
|
|
||||||
// The function's arity.
|
|
||||||
|
|
||||||
capnp::MallocMessageBuilder body;
|
|
||||||
// Stores a permanent copy of the function body.
|
|
||||||
};
|
|
||||||
|
|
||||||
class OperatorImpl final: public Calculator::Function::Server {
|
|
||||||
// Implementation of the Calculator.Function Cap'n Proto interface, wrapping
|
|
||||||
// basic binary arithmetic operators.
|
|
||||||
|
|
||||||
public:
|
|
||||||
OperatorImpl(Calculator::Operator op): op(op) {}
|
|
||||||
|
|
||||||
kj::Promise<void> call(CallContext context) {
|
|
||||||
auto params = context.getParams().getParams();
|
|
||||||
KJ_REQUIRE(params.size() == 2, "Wrong number of parameters.");
|
|
||||||
|
|
||||||
double result;
|
|
||||||
switch (op) {
|
|
||||||
case Calculator::Operator::ADD: result = params[0] + params[1]; break;
|
|
||||||
case Calculator::Operator::SUBTRACT:result = params[0] - params[1]; break;
|
|
||||||
case Calculator::Operator::MULTIPLY:result = params[0] * params[1]; break;
|
|
||||||
case Calculator::Operator::DIVIDE: result = params[0] / params[1]; break;
|
|
||||||
default:
|
|
||||||
KJ_FAIL_REQUIRE("Unknown operator.");
|
|
||||||
}
|
|
||||||
|
|
||||||
context.getResults().setValue(result);
|
|
||||||
return kj::READY_NOW;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Calculator::Operator op;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CalculatorImpl final: public Calculator::Server {
|
|
||||||
// Implementation of the Calculator Cap'n Proto interface.
|
|
||||||
|
|
||||||
public:
|
|
||||||
kj::Promise<void> evaluate(EvaluateContext context) override {
|
|
||||||
return evaluateImpl(context.getParams().getExpression())
|
|
||||||
.then([KJ_CPCAP(context)](double value) mutable {
|
|
||||||
context.getResults().setValue(kj::heap<ValueImpl>(value));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
kj::Promise<void> defFunction(DefFunctionContext context) override {
|
|
||||||
auto params = context.getParams();
|
|
||||||
context.getResults().setFunc(kj::heap<FunctionImpl>(
|
|
||||||
params.getParamCount(), params.getBody()));
|
|
||||||
return kj::READY_NOW;
|
|
||||||
}
|
|
||||||
|
|
||||||
kj::Promise<void> getOperator(GetOperatorContext context) override {
|
|
||||||
context.getResults().setFunc(kj::heap<OperatorImpl>(
|
|
||||||
context.getParams().getOp()));
|
|
||||||
return kj::READY_NOW;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, const char* argv[]) {
|
|
||||||
if (argc != 2) {
|
|
||||||
std::cerr << "usage: " << argv[0] << " ADDRESS[:PORT]\n"
|
|
||||||
"Runs the server bound to the given address/port.\n"
|
|
||||||
"ADDRESS may be '*' to bind to all local addresses.\n"
|
|
||||||
":PORT may be omitted to choose a port automatically." << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up a server.
|
|
||||||
capnp::EzRpcServer server(kj::heap<CalculatorImpl>(), argv[1]);
|
|
||||||
|
|
||||||
// Write the port number to stdout, in case it was chosen automatically.
|
|
||||||
auto& waitScope = server.getWaitScope();
|
|
||||||
uint port = server.getPort().wait(waitScope);
|
|
||||||
if (port == 0) {
|
|
||||||
// The address format "unix:/path/to/socket" opens a unix domain socket,
|
|
||||||
// in which case the port will be zero.
|
|
||||||
std::cout << "Listening on Unix socket..." << std::endl;
|
|
||||||
} else {
|
|
||||||
std::cout << "Listening on port " << port << "..." << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run forever, accepting connections and handling requests.
|
|
||||||
kj::NEVER_DONE.wait(waitScope);
|
|
||||||
}
|
|
|
@ -1,118 +0,0 @@
|
||||||
# Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
|
|
||||||
# Licensed under the MIT License:
|
|
||||||
#
|
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
|
||||||
# in the Software without restriction, including without limitation the rights
|
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
|
||||||
# furnished to do so, subject to the following conditions:
|
|
||||||
#
|
|
||||||
# The above copyright notice and this permission notice shall be included in
|
|
||||||
# all copies or substantial portions of the Software.
|
|
||||||
#
|
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
# THE SOFTWARE.
|
|
||||||
|
|
||||||
@0x85150b117366d14b;
|
|
||||||
|
|
||||||
interface Calculator {
|
|
||||||
# A "simple" mathematical calculator, callable via RPC.
|
|
||||||
#
|
|
||||||
# But, to show off Cap'n Proto, we add some twists:
|
|
||||||
#
|
|
||||||
# - You can use the result from one call as the input to the next
|
|
||||||
# without a network round trip. To accomplish this, evaluate()
|
|
||||||
# returns a `Value` object wrapping the actual numeric value.
|
|
||||||
# This object may be used in a subsequent expression. With
|
|
||||||
# promise pipelining, the Value can actually be used before
|
|
||||||
# the evaluate() call that creates it returns!
|
|
||||||
#
|
|
||||||
# - You can define new functions, and then call them. This again
|
|
||||||
# shows off pipelining, but it also gives the client the
|
|
||||||
# opportunity to define a function on the client side and have
|
|
||||||
# the server call back to it.
|
|
||||||
#
|
|
||||||
# - The basic arithmetic operators are exposed as Functions, and
|
|
||||||
# you have to call getOperator() to obtain them from the server.
|
|
||||||
# This again demonstrates pipelining -- using getOperator() to
|
|
||||||
# get each operator and then using them in evaluate() still
|
|
||||||
# only takes one network round trip.
|
|
||||||
|
|
||||||
evaluate @0 (expression :Expression) -> (value :Value);
|
|
||||||
# Evaluate the given expression and return the result. The
|
|
||||||
# result is returned wrapped in a Value interface so that you
|
|
||||||
# may pass it back to the server in a pipelined request. To
|
|
||||||
# actually get the numeric value, you must call read() on the
|
|
||||||
# Value -- but again, this can be pipelined so that it incurs
|
|
||||||
# no additional latency.
|
|
||||||
|
|
||||||
struct Expression {
|
|
||||||
# A numeric expression.
|
|
||||||
|
|
||||||
union {
|
|
||||||
literal @0 :Float64;
|
|
||||||
# A literal numeric value.
|
|
||||||
|
|
||||||
previousResult @1 :Value;
|
|
||||||
# A value that was (or, will be) returned by a previous
|
|
||||||
# evaluate().
|
|
||||||
|
|
||||||
parameter @2 :UInt32;
|
|
||||||
# A parameter to the function (only valid in function bodies;
|
|
||||||
# see defFunction).
|
|
||||||
|
|
||||||
call :group {
|
|
||||||
# Call a function on a list of parameters.
|
|
||||||
function @3 :Function;
|
|
||||||
params @4 :List(Expression);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Value {
|
|
||||||
# Wraps a numeric value in an RPC object. This allows the value
|
|
||||||
# to be used in subsequent evaluate() requests without the client
|
|
||||||
# waiting for the evaluate() that returns the Value to finish.
|
|
||||||
|
|
||||||
read @0 () -> (value :Float64);
|
|
||||||
# Read back the raw numeric value.
|
|
||||||
}
|
|
||||||
|
|
||||||
defFunction @1 (paramCount :Int32, body :Expression)
|
|
||||||
-> (func :Function);
|
|
||||||
# Define a function that takes `paramCount` parameters and returns the
|
|
||||||
# evaluation of `body` after substituting these parameters.
|
|
||||||
|
|
||||||
interface Function {
|
|
||||||
# An algebraic function. Can be called directly, or can be used inside
|
|
||||||
# an Expression.
|
|
||||||
#
|
|
||||||
# A client can create a Function that runs on the server side using
|
|
||||||
# `defFunction()` or `getOperator()`. Alternatively, a client can
|
|
||||||
# implement a Function on the client side and the server will call back
|
|
||||||
# to it. However, a function defined on the client side will require a
|
|
||||||
# network round trip whenever the server needs to call it, whereas
|
|
||||||
# functions defined on the server and then passed back to it are called
|
|
||||||
# locally.
|
|
||||||
|
|
||||||
call @0 (params :List(Float64)) -> (value :Float64);
|
|
||||||
# Call the function on the given parameters.
|
|
||||||
}
|
|
||||||
|
|
||||||
getOperator @2 (op :Operator) -> (func :Function);
|
|
||||||
# Get a Function representing an arithmetic operator, which can then be
|
|
||||||
# used in Expressions.
|
|
||||||
|
|
||||||
enum Operator {
|
|
||||||
add @0;
|
|
||||||
subtract @1;
|
|
||||||
multiply @2;
|
|
||||||
divide @3;
|
|
||||||
}
|
|
||||||
}
|
|
220
capture.cpp
220
capture.cpp
|
@ -1,220 +0,0 @@
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string>
|
|
||||||
#include <time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <linux/input.h>
|
|
||||||
#include <sys/epoll.h>
|
|
||||||
|
|
||||||
#define MAX_EVENTS 16
|
|
||||||
#define CONTENT_BUFFER_SIZE 32
|
|
||||||
|
|
||||||
constexpr std::string_view KEYBOARD = "k";
|
|
||||||
constexpr std::string_view MOUSE = "m";
|
|
||||||
|
|
||||||
int dump_event(struct timespec* start, struct input_event* event, const std::string_view& type);
|
|
||||||
|
|
||||||
int event_content_keyboard(char* buffer, int buffer_size, struct input_event* event);
|
|
||||||
int event_content_mouse(char* buffer, int buffer_size, struct input_event* event);
|
|
||||||
|
|
||||||
int stream_events(char* mouse_path, char* keyboard_path, int hotkey_scancode);
|
|
||||||
|
|
||||||
static inline void timespec_diff(struct timespec* a, struct timespec* b, struct timespec* result) {
|
|
||||||
result->tv_sec = a->tv_sec - b->tv_sec;
|
|
||||||
result->tv_nsec = a->tv_nsec - b->tv_nsec;
|
|
||||||
if (result->tv_nsec < 0) {
|
|
||||||
--result->tv_sec;
|
|
||||||
result->tv_nsec += 1000000000L;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void timeval_diff(struct timeval* a, struct timeval* b, struct timeval* result) {
|
|
||||||
result->tv_sec = a->tv_sec - b->tv_sec;
|
|
||||||
result->tv_usec = a->tv_usec - b->tv_usec;
|
|
||||||
if (result->tv_usec < 0) {
|
|
||||||
--result->tv_sec;
|
|
||||||
result->tv_usec += 1000000L;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void time_diff(struct timeval* a, struct timespec* b, struct timeval* result) {
|
|
||||||
result->tv_sec = a->tv_sec - b->tv_sec;
|
|
||||||
result->tv_usec = a->tv_usec - (b->tv_nsec / 1000);
|
|
||||||
if (result->tv_usec < 0) {
|
|
||||||
--result->tv_sec;
|
|
||||||
result->tv_usec += 1000000L;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int dump_event(struct timespec* start, struct input_event* event, const std::string_view& type) {
|
|
||||||
struct timespec now;
|
|
||||||
struct timespec diff;
|
|
||||||
char content_buffer[CONTENT_BUFFER_SIZE];
|
|
||||||
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
||||||
timespec_diff(&now, start, &diff);
|
|
||||||
// time_diff(&(event.time), start, &diff);
|
|
||||||
|
|
||||||
if(type == MOUSE) {
|
|
||||||
if(event_content_mouse(content_buffer, CONTENT_BUFFER_SIZE, event)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
} else if(type == KEYBOARD) {
|
|
||||||
// Ignore all but EV_KEY events on keyboard, they have no useful content.
|
|
||||||
if(event->type != EV_KEY) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if(event_content_keyboard(content_buffer, CONTENT_BUFFER_SIZE, event)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Unknown event type.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
start->tv_sec = now.tv_sec;
|
|
||||||
start->tv_nsec = now.tv_nsec;
|
|
||||||
|
|
||||||
std::cout <<
|
|
||||||
diff.tv_sec << " " <<
|
|
||||||
diff.tv_nsec << "," <<
|
|
||||||
type << "," <<
|
|
||||||
content_buffer << std::endl;
|
|
||||||
|
|
||||||
fflush(stdout);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int event_content_keyboard(char* buffer, int buffer_size, struct input_event* event) {
|
|
||||||
sprintf(buffer, "%d,%d,%d",
|
|
||||||
event->type,
|
|
||||||
event->code,
|
|
||||||
event->value);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int event_content_mouse(char* buffer, int buffer_size, struct input_event* event) {
|
|
||||||
unsigned char button, bLeft, bMiddle, bRight;
|
|
||||||
unsigned char *ptr = (unsigned char*)event;
|
|
||||||
int i;
|
|
||||||
char x, y;
|
|
||||||
button = ptr[0];
|
|
||||||
bLeft = button & 0x1;
|
|
||||||
bMiddle = ( button & 0x4 ) > 0;
|
|
||||||
bRight = ( button & 0x2 ) > 0;
|
|
||||||
x=(char) ptr[1];
|
|
||||||
y=(char) ptr[2];
|
|
||||||
|
|
||||||
int chars = sprintf(buffer, "l%d,m%d,r%d,x%d,y%d",
|
|
||||||
bLeft, bMiddle, bRight, x, y);
|
|
||||||
return chars < 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
int hotkey_scancode;
|
|
||||||
if(argc < 2) {
|
|
||||||
fprintf(stderr, "You must specify a mouse input to track like /dev/input/mouse1.");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
if(argc < 3) {
|
|
||||||
fprintf(stderr, "You must specify a keyboard input to track like /dev/input/event3. If you're not sure which to use read through /proc/bus/input/devices and look for 'Handlers=eventX'");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
if(argc < 4) {
|
|
||||||
fprintf(stderr, "You must specify a character to indicate when to start and stop capture. 53 for 'z'.\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
int matched = sscanf(argv[3], "%d", &hotkey_scancode);
|
|
||||||
if(matched != 1) {
|
|
||||||
fprintf(stderr, "Failed to read hotkey scancode.\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
int result = stream_events(argv[1], argv[2], hotkey_scancode);
|
|
||||||
exit(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stream_events(char* mouse_path, char* keyboard_path, int hotkey_scancode) {
|
|
||||||
int keyboard_fd, mouse_fd;
|
|
||||||
struct timespec start;
|
|
||||||
struct epoll_event e_event, events[MAX_EVENTS];
|
|
||||||
|
|
||||||
int has_seen_hotkey = 0;
|
|
||||||
int running = 1;
|
|
||||||
int epoll_fd = epoll_create1(0);
|
|
||||||
if(epoll_fd < 0) {
|
|
||||||
fprintf(stderr, "Failed to initialize epoll fd: %d", errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if((mouse_fd = open(mouse_path, O_RDONLY)) == -1) {
|
|
||||||
fprintf(stderr, "Failed to open %s. Are you running as root?\n", mouse_path);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
fprintf(stderr, "%s open OK\n", mouse_path);
|
|
||||||
}
|
|
||||||
if((keyboard_fd = open(keyboard_path, O_RDONLY)) == -1) {
|
|
||||||
fprintf(stderr, "Failed to open %s. Are you running as root?\n", keyboard_path);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
fprintf(stderr, "%s open OK\n", keyboard_path);
|
|
||||||
}
|
|
||||||
e_event.events = EPOLLIN;
|
|
||||||
|
|
||||||
e_event.data.fd = keyboard_fd;
|
|
||||||
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, keyboard_fd, &e_event)) {
|
|
||||||
fprintf(stderr, "Failed to add keyboard file descriptor\n");
|
|
||||||
close(epoll_fd);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
e_event.data.fd = mouse_fd;
|
|
||||||
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, mouse_fd, &e_event)) {
|
|
||||||
fprintf(stderr, "Failed to add mouse file descriptor\n");
|
|
||||||
close(epoll_fd);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "Waiting for hotkey\n");
|
|
||||||
struct input_event i_event;
|
|
||||||
while(running) {
|
|
||||||
int event_count = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
|
|
||||||
std::string_view type;
|
|
||||||
for(int i = 0; i < event_count; i++) {
|
|
||||||
int result = read(events[i].data.fd, &i_event, sizeof(struct input_event));
|
|
||||||
if(result < 0) {
|
|
||||||
perror("Failed to read an event");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(events[i].data.fd == keyboard_fd) {
|
|
||||||
type = KEYBOARD;
|
|
||||||
if(i_event.type == EV_KEY && i_event.code == hotkey_scancode && i_event.value == 1) {
|
|
||||||
if(has_seen_hotkey) {
|
|
||||||
fprintf(stderr, "Stop capture\n");
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
has_seen_hotkey = 1;
|
|
||||||
fprintf(stderr, "Start capture\n");
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else if(i_event.value == 2) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else if (events[i].data.fd == mouse_fd) {
|
|
||||||
type = MOUSE;
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Unknown fd");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
// Wait for the hotkey to start capture
|
|
||||||
if(has_seen_hotkey && dump_event(&start, &i_event, type)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
102
capture.py
102
capture.py
|
@ -1,102 +0,0 @@
|
||||||
import argparse
|
|
||||||
import functools
|
|
||||||
import keyboard
|
|
||||||
import logging
|
|
||||||
import mouse
|
|
||||||
import pickle
|
|
||||||
import queue
|
|
||||||
import sys
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
|
|
||||||
LOGGER = logging.getLogger("capture")
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument(
|
|
||||||
"-d", "--delay",
|
|
||||||
default=0,
|
|
||||||
type=int,
|
|
||||||
help="Seconds to wait before capture."
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-t", "--trigger",
|
|
||||||
default=None,
|
|
||||||
help="The key to use to trigger start and stop of capture."
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-o", "--output",
|
|
||||||
default="dump.capture",
|
|
||||||
help="Name of the file to capture to."
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--verbose",
|
|
||||||
action="store_true",
|
|
||||||
help="Show verbose logging.",
|
|
||||||
)
|
|
||||||
args = parser.parse_args()
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.DEBUG if args.verbose else logging.INFO)
|
|
||||||
if args.delay and args.trigger:
|
|
||||||
print("You cannot specify 'delay' and 'trigger'")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
_do_delay(args.delay)
|
|
||||||
_do_trigger(args.trigger)
|
|
||||||
LOGGER.info("Capturing...")
|
|
||||||
now = time.time()
|
|
||||||
# Add dummy events to lock in the time start
|
|
||||||
with open(args.output, "w") as output:
|
|
||||||
hook = functools.partial(_on_hook, now, output)
|
|
||||||
keyhook = keyboard.hook(hook)
|
|
||||||
mousehook = mouse.hook(hook)
|
|
||||||
try:
|
|
||||||
keyboard.wait(args.trigger)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
keyboard.unhook(keyhook)
|
|
||||||
mouse.unhook(mousehook)
|
|
||||||
LOGGER.info("Wrote %s", args.output)
|
|
||||||
|
|
||||||
def _on_hook(start, output, event):
|
|
||||||
LOGGER.debug(str(event))
|
|
||||||
relative_time = event.time - start
|
|
||||||
if isinstance(event, keyboard.KeyboardEvent):
|
|
||||||
output.write(
|
|
||||||
f"{relative_time},k,{event.event_type},{event.scan_code},{event.name}\n")
|
|
||||||
elif isinstance(event, mouse.ButtonEvent):
|
|
||||||
output.write(
|
|
||||||
f"{relative_time},mb,{event.event_type},{event.button}\n")
|
|
||||||
elif isinstance(event, mouse.MoveEvent):
|
|
||||||
output.write(
|
|
||||||
f"{relative_time},mm,{event.x},{event.y}\n")
|
|
||||||
elif isinstance(event, mouse.WheelEvent):
|
|
||||||
output.write(
|
|
||||||
f"{relative_time},mw,{event.delta}\n")
|
|
||||||
else:
|
|
||||||
raise ValueError(f"{event} is not recognized")
|
|
||||||
|
|
||||||
def _do_delay(delay: int) -> None:
|
|
||||||
if not delay:
|
|
||||||
return
|
|
||||||
print("\n")
|
|
||||||
for i in range(delay):
|
|
||||||
print(f"\rStarting in {delay-i} seconds")
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
def _do_trigger(trigger: str) -> None:
|
|
||||||
if not trigger:
|
|
||||||
return
|
|
||||||
print(f"Waiting for '{trigger}'")
|
|
||||||
keyboard.wait(trigger)
|
|
||||||
|
|
||||||
def _save_events(event_q: queue.Queue, filename: str) -> None:
|
|
||||||
events = []
|
|
||||||
while not event_q.empty():
|
|
||||||
event = event_q.get(block=False)
|
|
||||||
events.append(event)
|
|
||||||
with open(filename, "wb") as output:
|
|
||||||
pickle.dump(events, output)
|
|
||||||
print(f"Wrote to {filename}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
261
playback.cpp
261
playback.cpp
|
@ -1,261 +0,0 @@
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <linux/uinput.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
static volatile int is_running = 1;
|
|
||||||
|
|
||||||
int handle_udevice(char* details, int udevice_fd);
|
|
||||||
int handle_line(char* line, int udevice_fd);
|
|
||||||
int handle_mouse(char* details, int udevice_fd);
|
|
||||||
int read_file(char* filename, int udevice_fd);
|
|
||||||
int setup_udevice();
|
|
||||||
void sigint_handler(int dummy);
|
|
||||||
void teardown_udevice(int fd);
|
|
||||||
|
|
||||||
void emit(int fd, int type, int code, int val) {
|
|
||||||
struct input_event ie;
|
|
||||||
ie.type = type;
|
|
||||||
ie.code = code;
|
|
||||||
ie.value = val;
|
|
||||||
ie.time.tv_sec = 0;
|
|
||||||
ie.time.tv_usec = 0;
|
|
||||||
|
|
||||||
write(fd, &ie, sizeof(ie));
|
|
||||||
}
|
|
||||||
|
|
||||||
int handle_keyboard(char* details, int udevice_fd) {
|
|
||||||
int code, event_type, value;
|
|
||||||
int matched = sscanf(details, "%d,%d,%d", &event_type, &code, &value);
|
|
||||||
if(matched != 3) {
|
|
||||||
printf("Didn't match enough values for a keyboard event.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
// printf("Event type: %d, Code: %d, Value: %d\n",
|
|
||||||
// event_type, code, value);
|
|
||||||
if(event_type != 1) {
|
|
||||||
printf("Not sure what to do with event type %d", event_type);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
emit(udevice_fd, EV_KEY, code, value);
|
|
||||||
emit(udevice_fd, EV_SYN, SYN_REPORT, 0);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int handle_line(char* line, int udevice_fd) {
|
|
||||||
static time_t total_seconds = 0;
|
|
||||||
static long total_nanos = 0;
|
|
||||||
|
|
||||||
time_t seconds;
|
|
||||||
long nanos;
|
|
||||||
char type;
|
|
||||||
char details[32];
|
|
||||||
struct timespec to_sleep;
|
|
||||||
struct timespec remaining;
|
|
||||||
|
|
||||||
int matched = sscanf(line, "%ld %ld,%c,%s\n", &seconds, &nanos, &type, details);
|
|
||||||
if(matched == 0) {
|
|
||||||
printf("Line '%s' appears incorrect. Exiting", line);
|
|
||||||
return 1;
|
|
||||||
} else if(matched < 4) {
|
|
||||||
printf("Only matched %d", matched);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
remaining.tv_sec = 0;
|
|
||||||
remaining.tv_nsec = 0;
|
|
||||||
to_sleep.tv_sec = seconds;
|
|
||||||
to_sleep.tv_nsec = nanos;
|
|
||||||
if(to_sleep.tv_nsec < 0) {
|
|
||||||
--to_sleep.tv_sec;
|
|
||||||
to_sleep.tv_nsec += 1000000000L;
|
|
||||||
}
|
|
||||||
// printf("Timer %ld %ld\n", timer_seconds, timer_nanos);
|
|
||||||
// printf("Read %ld %ld\n", seconds, nanos);
|
|
||||||
// printf("Sleep %ld %ld\n", to_sleep.tv_sec, to_sleep.tv_nsec);
|
|
||||||
while(nanosleep(&to_sleep, &remaining) == -1) {
|
|
||||||
if(!is_running) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
perror("nanosleep error");
|
|
||||||
printf("Attempted %ld.%ld sleep\n", to_sleep.tv_sec, to_sleep.tv_nsec);
|
|
||||||
printf("Need %ld.%ld more seconds for total sleep\n", remaining.tv_sec, remaining.tv_nsec);
|
|
||||||
to_sleep.tv_sec = remaining.tv_sec;
|
|
||||||
to_sleep.tv_nsec = remaining.tv_nsec;
|
|
||||||
}
|
|
||||||
if(remaining.tv_sec != 0 || remaining.tv_nsec != 0) {
|
|
||||||
printf("oops, remaining.\n");
|
|
||||||
}
|
|
||||||
total_seconds += to_sleep.tv_sec;
|
|
||||||
total_nanos += to_sleep.tv_nsec;
|
|
||||||
if(total_nanos > 1000000000L) {
|
|
||||||
total_nanos -= 1000000000L;
|
|
||||||
total_seconds += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// printf("%ld %ld\tslept %ld %ld\n",
|
|
||||||
// total_seconds, total_nanos, to_sleep.tv_sec, to_sleep.tv_nsec);
|
|
||||||
if(type == 'k') {
|
|
||||||
return handle_keyboard(details, udevice_fd);
|
|
||||||
} else if(type == 'm') {
|
|
||||||
return handle_mouse(details, udevice_fd);
|
|
||||||
} else {
|
|
||||||
printf("Unexpected type %c/n", type);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int handle_mouse(char* details, int udevice_fd) {
|
|
||||||
static int current_left = 0;
|
|
||||||
static int current_middle = 0;
|
|
||||||
static int current_right = 0;
|
|
||||||
|
|
||||||
int left, middle, right, x, y;
|
|
||||||
int matched = sscanf(details, "l%d,m%d,r%d,x%d,y%d",
|
|
||||||
&left, &middle, &right, &x, &y);
|
|
||||||
if(matched != 5) {
|
|
||||||
printf("Failed to match enough data for a mouse event.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
// printf("L: %d M: %d, R: %d, X: %d, Y: %d\n",
|
|
||||||
// left, middle, right, x, y);
|
|
||||||
|
|
||||||
/* Move the mouse diagonally, 5 units per axis */
|
|
||||||
if(x != 0) {
|
|
||||||
emit(udevice_fd, EV_REL, REL_X, x);
|
|
||||||
}
|
|
||||||
if(y != 0) {
|
|
||||||
emit(udevice_fd, EV_REL, REL_Y, -1 * y);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(left != current_left) {
|
|
||||||
emit(udevice_fd, EV_KEY, BTN_LEFT, left);
|
|
||||||
current_left = left;
|
|
||||||
}
|
|
||||||
if(middle != current_middle) {
|
|
||||||
emit(udevice_fd, EV_KEY, BTN_MIDDLE, middle);
|
|
||||||
current_middle = middle;
|
|
||||||
}
|
|
||||||
if(right != current_right) {
|
|
||||||
emit(udevice_fd, EV_KEY, BTN_RIGHT, right);
|
|
||||||
current_right = right;
|
|
||||||
}
|
|
||||||
emit(udevice_fd, EV_SYN, SYN_REPORT, 0);
|
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
int repeat = 1;
|
|
||||||
if(argc < 2) {
|
|
||||||
printf("Please provide a capture file.");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
if(argc == 3) {
|
|
||||||
int matched = sscanf(argv[2], "%d", &repeat);
|
|
||||||
if(matched != 1) {
|
|
||||||
fprintf(stderr, "Failed to read repeat value.\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
printf("Repeating %d times\n", repeat);
|
|
||||||
}
|
|
||||||
|
|
||||||
signal(SIGINT, sigint_handler);
|
|
||||||
|
|
||||||
for(int i = 3; i > 0; i--) {
|
|
||||||
fprintf(stderr, "Playing back in %d seconds\n", i);
|
|
||||||
sleep(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int result = 0;
|
|
||||||
int udevice_fd = setup_udevice();
|
|
||||||
for(int i = 0; is_running && i < repeat; i++) {
|
|
||||||
fprintf(stderr, "Repeat %d/%d\n", i+1, repeat);
|
|
||||||
if(read_file(argv[1], udevice_fd)) {
|
|
||||||
result = EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
teardown_udevice(udevice_fd);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int read_file(char* filename, int udevice_fd) {
|
|
||||||
FILE* fp;
|
|
||||||
char* line = NULL;
|
|
||||||
size_t len = 0;
|
|
||||||
ssize_t read;
|
|
||||||
|
|
||||||
fp = fopen(filename, "r");
|
|
||||||
if (fp == NULL) {
|
|
||||||
printf("Failed to open file %s: %d\n", filename, errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while(is_running && (read = getline(&line, &len, fp)) != -1) {
|
|
||||||
if(handle_line(line, udevice_fd)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int setup_udevice() {
|
|
||||||
struct uinput_setup usetup;
|
|
||||||
int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
|
|
||||||
|
|
||||||
/* enable mouse button left and relative events */
|
|
||||||
|
|
||||||
ioctl(fd, UI_SET_EVBIT, EV_KEY);
|
|
||||||
|
|
||||||
// Add keyboard keys. We could do this individually but we're super
|
|
||||||
// lazy and it appears a loop should work fine based on the linux/input-event-codes.h header
|
|
||||||
for(int i = KEY_ESC; i <= KEY_MICMUTE; i++) {
|
|
||||||
ioctl(fd, UI_SET_KEYBIT, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add mouse buttons
|
|
||||||
ioctl(fd, UI_SET_KEYBIT, BTN_LEFT);
|
|
||||||
ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT);
|
|
||||||
|
|
||||||
ioctl(fd, UI_SET_EVBIT, EV_REL);
|
|
||||||
ioctl(fd, UI_SET_RELBIT, REL_X);
|
|
||||||
ioctl(fd, UI_SET_RELBIT, REL_Y);
|
|
||||||
|
|
||||||
memset(&usetup, 0, sizeof(usetup));
|
|
||||||
usetup.id.bustype = BUS_USB;
|
|
||||||
usetup.id.vendor = 0x1234; /* sample vendor */
|
|
||||||
usetup.id.product = 0x5678; /* sample product */
|
|
||||||
strcpy(usetup.name, "Playback mouses");
|
|
||||||
|
|
||||||
ioctl(fd, UI_DEV_SETUP, &usetup);
|
|
||||||
ioctl(fd, UI_DEV_CREATE);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* On UI_DEV_CREATE the kernel will create the device node for this
|
|
||||||
* device. We are inserting a pause here so that userspace has time
|
|
||||||
* to detect, initialize the new device, and can start listening to
|
|
||||||
* the event, otherwise it will not notice the event we are about
|
|
||||||
* to send. This pause is only needed in our example code!
|
|
||||||
*/
|
|
||||||
// sleep(1);
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sigint_handler(int dummy) {
|
|
||||||
is_running = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void teardown_udevice(int fd) {
|
|
||||||
ioctl(fd, UI_DEV_DESTROY);
|
|
||||||
close(fd);
|
|
||||||
}
|
|
96
playback.py
96
playback.py
|
@ -1,96 +0,0 @@
|
||||||
import argparse
|
|
||||||
import keyboard
|
|
||||||
import logging
|
|
||||||
import mouse
|
|
||||||
import pickle
|
|
||||||
import threading
|
|
||||||
import time
|
|
||||||
|
|
||||||
LOGGER = logging.getLogger("playback")
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument(
|
|
||||||
"-d", "--delay",
|
|
||||||
default=5,
|
|
||||||
type=int,
|
|
||||||
help="Seconds to wait before replay."
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-i", "--input",
|
|
||||||
default="dump.capture",
|
|
||||||
help="Name of the file to replay from."
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--verbose",
|
|
||||||
action="store_true",
|
|
||||||
help="Show verbose messages."
|
|
||||||
)
|
|
||||||
args = parser.parse_args()
|
|
||||||
logging.basicConfig(
|
|
||||||
level = logging.DEBUG if args.verbose else logging.INFO
|
|
||||||
)
|
|
||||||
_do_delay(args.delay)
|
|
||||||
|
|
||||||
events = _load_events(args.input)
|
|
||||||
_play_events(events)
|
|
||||||
|
|
||||||
def _play_events(events) -> None:
|
|
||||||
LOGGER.info("Playback started.")
|
|
||||||
key_state = keyboard.stash_state()
|
|
||||||
last_time = None
|
|
||||||
for event in events:
|
|
||||||
if last_time is not None:
|
|
||||||
to_sleep = event.time - last_time
|
|
||||||
if to_sleep > 0:
|
|
||||||
time.sleep(to_sleep)
|
|
||||||
last_time = event.time
|
|
||||||
if isinstance(event, keyboard.KeyboardEvent):
|
|
||||||
_play_event_keyboard(event)
|
|
||||||
elif any([
|
|
||||||
isinstance(event, mouse.ButtonEvent),
|
|
||||||
isinstance(event, mouse.MoveEvent),
|
|
||||||
isinstance(event, mouse.WheelEvent),
|
|
||||||
]):
|
|
||||||
_play_event_mouse(event)
|
|
||||||
else:
|
|
||||||
raise ValueError(f"Not a recognized event {event}")
|
|
||||||
|
|
||||||
keyboard.restore_modifiers(key_state)
|
|
||||||
LOGGER.info("Done.")
|
|
||||||
|
|
||||||
def _play_event_keyboard(event) -> None:
|
|
||||||
LOGGER.debug("Key %s", event)
|
|
||||||
key = event.scan_code or event.name
|
|
||||||
keyboard.press(key) if event.event_type == keyboard.KEY_DOWN else keyboard.release(key)
|
|
||||||
|
|
||||||
def _play_event_mouse(event) -> None:
|
|
||||||
LOGGER.debug("Mouse %s", event)
|
|
||||||
if isinstance(event, mouse.ButtonEvent):
|
|
||||||
if event.event_type == mouse.UP:
|
|
||||||
mouse.release(event.button)
|
|
||||||
else:
|
|
||||||
mouse.press(event.button)
|
|
||||||
elif isinstance(event, mouse.MoveEvent):
|
|
||||||
mouse.move(event.x, event.y, absolute=True)
|
|
||||||
# mouse.move(event.x, event.y)
|
|
||||||
elif isinstance(event, mouse.WheelEvent):
|
|
||||||
mouse.wheel(event.delta)
|
|
||||||
|
|
||||||
def _do_delay(delay: int) -> None:
|
|
||||||
if not delay:
|
|
||||||
return
|
|
||||||
print("\n")
|
|
||||||
for i in range(delay):
|
|
||||||
print(f"\rStarting in {delay-i} seconds")
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
def _load_events(filename: str):
|
|
||||||
with open(filename, "rb") as input_:
|
|
||||||
events = pickle.load(input_)
|
|
||||||
LOGGER.debug("Loaded %s", filename)
|
|
||||||
return events
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
15
show-keys
15
show-keys
|
@ -1,15 +0,0 @@
|
||||||
#!env python3
|
|
||||||
import keyboard
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
keyboard.hook(_on_event)
|
|
||||||
try:
|
|
||||||
keyboard.wait()
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
print("End.")
|
|
||||||
|
|
||||||
def _on_event(event: keyboard.KeyboardEvent) -> None:
|
|
||||||
print(event.to_json())
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
19
show-mouse
19
show-mouse
|
@ -1,19 +0,0 @@
|
||||||
#!env python3
|
|
||||||
import keyboard
|
|
||||||
import logging
|
|
||||||
import mouse
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
|
||||||
mouse.hook(_on_event)
|
|
||||||
try:
|
|
||||||
mouse.wait()
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
pass
|
|
||||||
print("End.")
|
|
||||||
|
|
||||||
def _on_event(event: keyboard.KeyboardEvent) -> None:
|
|
||||||
print(event)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
61
show-mouse.c
61
show-mouse.c
|
@ -1,61 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <linux/input.h>
|
|
||||||
|
|
||||||
#define MOUSEFILE "/dev/input/mouse1"
|
|
||||||
//
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
int fd;
|
|
||||||
struct input_event ie;
|
|
||||||
//
|
|
||||||
unsigned char button,bLeft,bMiddle,bRight;
|
|
||||||
char x,y;
|
|
||||||
int absolute_x,absolute_y;
|
|
||||||
|
|
||||||
if((fd = open(MOUSEFILE, O_RDONLY)) == -1) {
|
|
||||||
printf("Device open ERROR\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("Device open OK\n");
|
|
||||||
}
|
|
||||||
//
|
|
||||||
printf("right-click to set absolute x,y coordinates origin (0,0)\n");
|
|
||||||
while(read(fd, &ie, sizeof(struct input_event)))
|
|
||||||
{
|
|
||||||
unsigned char *ptr = (unsigned char*)&ie;
|
|
||||||
int i;
|
|
||||||
//
|
|
||||||
button=ptr[0];
|
|
||||||
bLeft = button & 0x1;
|
|
||||||
bMiddle = ( button & 0x4 ) > 0;
|
|
||||||
bRight = ( button & 0x2 ) > 0;
|
|
||||||
x=(char) ptr[1];y=(char) ptr[2];
|
|
||||||
// printf("bLEFT:%d, bMIDDLE: %d, bRIGHT: %d, rx: %d ry=%d\n",bLeft,bMiddle,bRight, x,y);
|
|
||||||
//
|
|
||||||
absolute_x+=x;
|
|
||||||
absolute_y-=y;
|
|
||||||
if (bRight==1)
|
|
||||||
{
|
|
||||||
absolute_x=0;
|
|
||||||
absolute_y=0;
|
|
||||||
printf("Absolute x,y coords origin recorded\n");
|
|
||||||
}
|
|
||||||
//
|
|
||||||
printf("Absolute coords from TOP_LEFT= %i %i\n",absolute_x,absolute_y);
|
|
||||||
//
|
|
||||||
// comment to disable the display of raw event structure datas
|
|
||||||
//
|
|
||||||
for(i=0; i<sizeof(ie); i++)
|
|
||||||
{
|
|
||||||
printf("%02X ", *ptr++);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
Loading…
Reference in New Issue