Skip to content

Commit

Permalink
Init commit
Browse files Browse the repository at this point in the history
  • Loading branch information
thegoldgoat committed Sep 27, 2021
0 parents commit 85add1f
Show file tree
Hide file tree
Showing 8 changed files with 492 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
build
.cache
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Toccami Linux

Desktop version of the Virtual Multitouch Touchpad Mobile Application, for Linux.

## Architecture

1. Create a virtual kernel device using libevdev
2. Start a TCP server waiting for events from the remote mobile application
3. Start a UDP server to automatically show the server on the mobile application

## Build and Run

```bash

# Install build dependencies (Ubuntu)
sudo apt install ninja-build meson libevdev-dev

# Build
meson setup build
cd build
ninja

# Run with a custom name, 7777 port and resolution (sensitivity) of 5
sudo ./toccami_linux -n "Desktop Name" -p 7777 -r 5

```
16 changes: 16 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
project('toccami_linux', 'c')

executable('toccami_linux',
'src/main.c',
'src/utils/utils.c',
'src/udp/udp.c',
install: true,
c_args : [
'-std=c11',
'-I/usr/include/libevdev-1.0'
],
dependencies: [
dependency('libevdev'),
dependency('threads')
]
)
203 changes: 203 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
#include <libevdev/libevdev-uinput.h>
#include <libevdev/libevdev.h>
#include <stdbool.h>

#include <getopt.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>

#include "udp/udp.h"
#include "utils/utils.h"

#define TOCCAMI_EVENT_RELEASED 0
#define TOCCAMI_EVENT_DOWN 1
#define TOCCAMI_EVENT_CHANGE_RESOLUTION 2

void sig_handler(int signo);

bool canContinue = true;

int gethostname();

int main(int argc, char **argv) {

// Catch SIGINT
if (signal(SIGINT, sig_handler) == SIG_ERR) {
perror("Couldn't catch SIGINT signals (?)\n");
return -1;
}

// Parse arguments from CLI
int port = 7777;
int resolution = 5;
int c;
char serverName[1024];
gethostname(serverName);

while ((c = getopt(argc, argv, "p:r:n:")) != -1) {
switch (c) {
case 'p':
port = atoi(optarg);
break;
case 'r':
resolution = atoi(optarg);
break;
case 'n':
// Leave some space for the port
strncpy(serverName, optarg, sizeof(serverName) - 10);
break;
case '?':
fprintf(stderr, "Invalid arguments.\n");
return -1;
}
}

pthread_t tid;
struct UDPDaemonData udpData;
udpData.canContinue = &canContinue;
udpData.port = port;
udpData.serverName = serverName;

pthread_create(&tid, NULL, startUDPDaemon, &udpData);

struct libevdev_uinput *uidev = openDevice(resolution);

if (!uidev) {
perror("Error while creating device");

return -1;
}

int serverSocket, clientSocketFd;

serverSocket = openServer(port);

if (serverSocket < 0) {
perror("Error while opening socket");
return -1;
}

struct sockaddr_in clientAddress;
socklen_t clientAddressLen = sizeof(clientAddress);

char buffer[256];
int bytesRead;
uint16_t x, y, eventType, pointerIndex;

while (canContinue) {
printf("Listening on port %d\n", port);
clientSocketFd = accept(serverSocket, (struct sockaddr *)&clientAddress,
&clientAddressLen);

int fingersDownCount = 0;
bool wasFingerDown[MAX_TRACKING_ID];

if (clientSocketFd < 0) {
// This happens when we send SIGTERM and the accept syscall gets
// interrupted.
continue;
}

printf("Client accepted!\n");

while ((bytesRead = read(clientSocketFd, buffer, sizeof(buffer))) > 0) {
if (bytesRead % 8 != 0) {
printf("⚠️: Received payload with invalid lenght\n");
continue;
}

char *offsetBuffer;

int lastX = -1, lastY = -1;
for (int i = 0; i < bytesRead / 8; i++) {

offsetBuffer = buffer + i * 8;

x = *((uint16_t *)offsetBuffer);
y = *(uint16_t *)(offsetBuffer + 2);
pointerIndex = *(uint16_t *)(offsetBuffer + 4);
eventType = *(uint16_t *)(offsetBuffer + 6);

switch (eventType) {

case TOCCAMI_EVENT_DOWN:

libevdev_uinput_write_event(uidev, EV_ABS, ABS_MT_SLOT,
pointerIndex % MAX_TOUCHES);

if (!wasFingerDown[pointerIndex]) {
// Finger just pressed!
fingersDownCount++;
wasFingerDown[pointerIndex] = true;

// When the finger is just pressed, register tracking ID
libevdev_uinput_write_event(uidev, EV_ABS, ABS_MT_TRACKING_ID,
pointerIndex);
}

libevdev_uinput_write_event(uidev, EV_ABS, ABS_MT_POSITION_X, x);
libevdev_uinput_write_event(uidev, EV_ABS, ABS_MT_POSITION_Y, y);

lastX = x;
lastY = y;

break;
case TOCCAMI_EVENT_RELEASED:
if (wasFingerDown[pointerIndex]) {
fingersDownCount--;
wasFingerDown[pointerIndex] = false;

// Release Tracking ID after specifying SLOT
libevdev_uinput_write_event(uidev, EV_ABS, ABS_MT_SLOT,
pointerIndex % MAX_TOUCHES);
libevdev_uinput_write_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, -1);
}
break;
case TOCCAMI_EVENT_CHANGE_RESOLUTION:
// TODO: Do something!
break;
default:
fprintf(stderr, "Warning: invalid eventType: %d\n", eventType);
break;
}

// printf("fingCount = %d, x=%u, y=%u, pointer=%u, evType=%u\n",
// fingersDownCount, x, y, pointerIndex, eventType);
}

libevdev_uinput_write_event(uidev, EV_KEY, BTN_TOOL_FINGER,
fingersDownCount == 1);

libevdev_uinput_write_event(uidev, EV_KEY, BTN_TOUCH,
fingersDownCount >= 1);
libevdev_uinput_write_event(uidev, EV_KEY, BTN_TOOL_DOUBLETAP,
fingersDownCount == 2);
libevdev_uinput_write_event(uidev, EV_KEY, BTN_TOOL_TRIPLETAP,
fingersDownCount == 3);
libevdev_uinput_write_event(uidev, EV_KEY, BTN_TOOL_QUADTAP,
fingersDownCount == 4);
libevdev_uinput_write_event(uidev, EV_KEY, BTN_TOOL_QUINTTAP,
fingersDownCount == 5);

if (lastX != -1) {
libevdev_uinput_write_event(uidev, EV_ABS, ABS_X, lastX);
libevdev_uinput_write_event(uidev, EV_ABS, ABS_Y, lastY);
}

libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0);
}
printf("Client disconnected.\n");
close(clientSocketFd);
}

close(clientSocketFd);
close(serverSocket);
libevdev_uinput_destroy(uidev);

pthread_kill(tid, SIGINT);

return 0;
}

void sig_handler(int signo) { canContinue = false; }
63 changes: 63 additions & 0 deletions src/udp/udp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#include "udp.h"

void *startUDPDaemon(void *data) {

bool *canContinue = (bool *)((struct UDPDaemonData *)data)->canContinue;

char udpResponse[1024];
int udpResponseLen = snprintf(udpResponse, sizeof(udpResponse), "%s:%d",
((struct UDPDaemonData *)data)->serverName,
((struct UDPDaemonData *)data)->port);

struct sockaddr_in serverSocketAddress, clientSocketAddress;

int serverSocket, recv_len;
socklen_t slen = sizeof(clientSocketAddress);
char buf[BUFLEN];

// create a UDP socket
if ((serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
UDPdie("socket");
}

// zero out the structure
memset((char *)&serverSocketAddress, 0, sizeof(serverSocketAddress));

serverSocketAddress.sin_family = AF_INET;
serverSocketAddress.sin_port = htons(PORT);
serverSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);

// bind socket to port
if (bind(serverSocket, (struct sockaddr *)&serverSocketAddress,
sizeof(serverSocketAddress)) == -1) {
UDPdie("bind");
}

// keep listening for data
while (*canContinue) {
// try to receive some data, this is a blocking call
if ((recv_len = recvfrom(serverSocket, buf, BUFLEN, 0,
(struct sockaddr *)&clientSocketAddress, &slen)) ==
-1) {
perror("recvfrom()");
continue;
}

// print details of the client/peer and the data received

if (buf[0] != 127) {
printf("Weird data received!\n");
continue;
}

// now reply the client with the same data
if (sendto(serverSocket, udpResponse, udpResponseLen, 0,
(struct sockaddr *)&clientSocketAddress, slen) == -1) {
printf("sendto()");
continue;
}
}

close(serverSocket);
return NULL;
}
29 changes: 29 additions & 0 deletions src/udp/udp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include "../utils/utils.h"

#define BUFLEN 512 // Max length of buffer
#define PORT 3011 // The port on which to listen for incoming data

#define UDPdie(s) \
perror(s); \
return NULL;

struct UDPDaemonData {
bool *canContinue;
char *serverName;
int port;
};

void *startUDPDaemon(void *data);
Loading

0 comments on commit 85add1f

Please sign in to comment.