diff --git a/.gitignore b/.gitignore deleted file mode 100644 index dbeaf58..0000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -bin/ -ve/ diff --git a/Makefile b/Makefile deleted file mode 100644 index dc994e9..0000000 --- a/Makefile +++ /dev/null @@ -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 diff --git a/README.md b/README.md index c5bedbe..b7395ba 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ # symkey -This is a quick-and-dirty project for programmatically controlling keyboard and mouse. -I use it primarily to autoplay Minecraft on Hypixel Skyblock. -Shhh, don't tell. +A simple play tool for capturing keyboard and mouse and replaying them. Useful for Hypixel Skyblock \ No newline at end of file diff --git a/capnproto-example/Makefile b/capnproto-example/Makefile deleted file mode 100644 index f520545..0000000 --- a/capnproto-example/Makefile +++ /dev/null @@ -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 diff --git a/capnproto-example/calculator-client.c++ b/capnproto-example/calculator-client.c++ deleted file mode 100644 index 5d84529..0000000 --- a/capnproto-example/calculator-client.c++ +++ /dev/null @@ -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 -#include -#include -#include - -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 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(); - - // 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()); - 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; -} diff --git a/capnproto-example/calculator-server.c++ b/capnproto-example/calculator-server.c++ deleted file mode 100644 index c2593be..0000000 --- a/capnproto-example/calculator-server.c++ +++ /dev/null @@ -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 -#include -#include -#include - -typedef unsigned int uint; - -kj::Promise 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 result) { - return result.getValue(); - }); -} - -kj::Promise evaluateImpl( - Calculator::Expression::Reader expression, - capnp::List::Reader params = capnp::List::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> paramPromises = - KJ_MAP(param, call.getParams()) { - return evaluateImpl(param, params); - }; - - // Join the array of promises into a promise for an array. - kj::Promise> joinedParams = - kj::joinPromises(kj::mv(paramPromises)); - - // When the parameters are complete, call the function. - return joinedParams.then([KJ_CPCAP(func)](kj::Array&& paramValues) mutable { - auto request = func.callRequest(); - request.setParams(paramValues); - return request.send().then( - [](capnp::Response&& 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 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 call(CallContext context) { - auto params = context.getParams().getParams(); - KJ_REQUIRE(params.size() == paramCount, "Wrong number of parameters."); - - return evaluateImpl(body.getRoot(), 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 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 evaluate(EvaluateContext context) override { - return evaluateImpl(context.getParams().getExpression()) - .then([KJ_CPCAP(context)](double value) mutable { - context.getResults().setValue(kj::heap(value)); - }); - } - - kj::Promise defFunction(DefFunctionContext context) override { - auto params = context.getParams(); - context.getResults().setFunc(kj::heap( - params.getParamCount(), params.getBody())); - return kj::READY_NOW; - } - - kj::Promise getOperator(GetOperatorContext context) override { - context.getResults().setFunc(kj::heap( - 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(), 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); -} diff --git a/capnproto-example/calculator.capnp b/capnproto-example/calculator.capnp deleted file mode 100644 index adc8294..0000000 --- a/capnproto-example/calculator.capnp +++ /dev/null @@ -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; - } -} diff --git a/capture.cpp b/capture.cpp deleted file mode 100644 index ef04ad7..0000000 --- a/capture.cpp +++ /dev/null @@ -1,220 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#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; -} diff --git a/capture.py b/capture.py deleted file mode 100644 index 05f23fd..0000000 --- a/capture.py +++ /dev/null @@ -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() diff --git a/playback.cpp b/playback.cpp deleted file mode 100644 index 4021053..0000000 --- a/playback.cpp +++ /dev/null @@ -1,261 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -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); -} diff --git a/playback.py b/playback.py deleted file mode 100644 index 26c6867..0000000 --- a/playback.py +++ /dev/null @@ -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() diff --git a/show-keys b/show-keys deleted file mode 100755 index 84e846a..0000000 --- a/show-keys +++ /dev/null @@ -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() diff --git a/show-mouse b/show-mouse deleted file mode 100755 index 63713a9..0000000 --- a/show-mouse +++ /dev/null @@ -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() diff --git a/show-mouse.c b/show-mouse.c deleted file mode 100644 index 87a944c..0000000 --- a/show-mouse.c +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include -#include -#include -#include - -#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