Репозиторий Sisyphus
Последнее обновление: 1 октября 2023 | Пакетов: 18631 | Посещений: 37913393
en ru br
Репозитории ALT

Группа :: Система/X11
Пакет: twofing

 Главная   Изменения   Спек   Патчи   Исходники   Загрузить   Gear   Bugs and FR  Repocop 

pax_global_header00006660000000000000000000000064117313352070014514gustar00rootroot0000000000000052 comment=e84108b26e593ad267e7e42fddbdb4fbf6393fb4
twofing-0.0.9/000075500000000000000000000000001173133520700131755ustar00rootroot00000000000000twofing-0.0.9/70-touchscreen-egalax.rules000064400000000000000000000002131173133520700202520ustar00rootroot00000000000000SUBSYSTEMS=="usb",ACTION=="add",KERNEL=="event*",ATTRS{idProduct}=="480d",SYMLINK+="twofingtouch",RUN+="/bin/chmod a+r /dev/twofingtouch"

twofing-0.0.9/Makefile000064400000000000000000000006451173133520700146420ustar00rootroot00000000000000CC = gcc
OBJECTS = twofingemu.o gestures.o easing.o
LIBS = -lm -lpthread -lXtst -lXrandr -lX11 -lXi
CFLAGS = -Wall -O2
BINDIR = $(DESTDIR)/usr/bin
NAME = twofing

twofing: $(OBJECTS)
$(CC) -o $(NAME) $(OBJECTS) $(LIBS)

%.o: %.c
$(CC) -c $(CFLAGS) $<

install:
install --mode=755 $(NAME) $(BINDIR)/
cp 70-touchscreen-egalax.rules $(DESTDIR)/etc/udev/rules.d/

clean:
rm *.o $(NAME)

uninstall:
rm $(BINDIR)/$(NAME)
twofing-0.0.9/easing.c000064400000000000000000000066631173133520700146220ustar00rootroot00000000000000/*
Copyright (C) 2010, 2011 Philipp Merkel <linux@philmerk.de>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <math.h>
#include <X11/Xlib.h>
#include "twofingemu.h"
#include "easing.h"
#include "gestures.h"
#include <unistd.h>


/* Variables for easing: */
/* 1 if there is currently easing going on */
int easingActive = 0;

/* Start Interval between two easing steps */
int easingInterval = 0;
/* 1 if easing shall move to the right, -1 if it shall move to the left. */
int easingDirectionX = 0;
/* 1 if easing shall move down, -1 if it shall move up. */
int easingDirectionY = 0;
/* The profile for the easing */
Profile* easingProfile;

TimeVal easingStepTimeVal;

/* Starts the easing; profile, interval and directions have to be set before. */
void startEasing(Profile * profile, int directionX, int directionY, int interval) {
easingDirectionX = directionX;
easingDirectionY = directionY;
easingProfile = profile;
easingInterval = interval;
easingStepTimeVal.tv_sec = interval / 1000;
easingStepTimeVal.tv_usec = (interval % 1000) * 1000;
easingActive = 1;

}

/* Stops the easing. */
void stopEasing() {
if(easingActive) {
easingActive = 0;
}
}

void checkEasingStep()
{
if(easingActive && easingStepTimeVal.tv_sec <= 0 && easingStepTimeVal.tv_usec <= 0)
{

if(inDebugMode()) printf("Easing step\n");
if (easingProfile->scrollInherit) {
if(easingDirectionY == -1) {
executeAction(&(getDefaultProfile()->scrollUpAction),
EXECUTEACTION_BOTH);
}
if(easingDirectionY == 1) {
executeAction(&(getDefaultProfile()->scrollDownAction),
EXECUTEACTION_BOTH);
}
if(easingDirectionX == -1) {
executeAction(&(getDefaultProfile()->scrollLeftAction),
EXECUTEACTION_BOTH);
}
if(easingDirectionX == 1) {
executeAction(&(getDefaultProfile()->scrollRightAction),
EXECUTEACTION_BOTH);
}
} else {
if(easingDirectionY == -1) {
executeAction(&(easingProfile->scrollUpAction),
EXECUTEACTION_BOTH);
}
if(easingDirectionY == 1) {
executeAction(&(easingProfile->scrollDownAction),
EXECUTEACTION_BOTH);
}
if(easingDirectionX == -1) {
executeAction(&(easingProfile->scrollLeftAction),
EXECUTEACTION_BOTH);
}
if(easingDirectionX == 1) {
executeAction(&(easingProfile->scrollRightAction),
EXECUTEACTION_BOTH);
}
}


easingInterval = (int) (((float) easingInterval) * 1.15);

easingStepTimeVal.tv_sec = easingInterval / 1000;
easingStepTimeVal.tv_usec = (easingInterval % 1000) * 1000;

if(easingInterval > MAX_EASING_INTERVAL) {
easingActive = 0;
}

}
}

TimeVal* getEasingStepTimeVal()
{
if(easingActive) {
return &easingStepTimeVal;
} else {
return NULL;
}

}

int isEasingActive()
{
return easingActive;
}
twofing-0.0.9/easing.h000064400000000000000000000023151173133520700146150ustar00rootroot00000000000000/*
Copyright (C) 2010, Philipp Merkel <linux@philmerk.de>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef EASING_H_
#define EASING_H_


/* Maximum amount of milliseconds between two scrolling steps before easing stops. */
#define MAX_EASING_INTERVAL 200
/* Maximum amount of milliseconds between the last two scrolling steps for easing to start. */
#define MAX_EASING_START_INTERVAL 200


void startEasing(Profile *, int, int, int);
void stopEasing();
int isEasingActive();
TimeVal* getEasingStepTimeVal();
void checkEasingStep();

#endif /* EASING_H_ */
twofing-0.0.9/gestures.c000064400000000000000000000460631173133520700152130ustar00rootroot00000000000000/*
Copyright (C) 2010, 2011 Philipp Merkel <linux@philmerk.de>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <math.h>
#include <X11/Xlib.h>
#include "profiles.h"
#include "twofingemu.h"
#include "gestures.h"
#include "easing.h"
#include <unistd.h>


/* clickMode: 0 - first finger; 1 - second finger; 2 - center */
int clickMode;


/* Number of milliseconds before a single click is registered, to give the user time to put down
second finger for two-finger gestures. */
#define CLICK_DELAY 100

/* Continuation mode -- when 1, two finger gesture is continued when one finger is released. */
#define CONTINUATION 1

#if CONTINUATION
#define TWO_FINGERS_DOWN fingersDown == 2 && hadTwoFingersOn == 0
#define TWO_FINGERS_ON fingersDown > 0 && hadTwoFingersOn == 1
#define TWO_FINGERS_UP fingersDown == 0 && hadTwoFingersOn == 1
#else
#define TWO_FINGERS_DOWN fingersDown == 2 && fingersWereDown < 2
#define TWO_FINGERS_ON fingersDown == 2
#define TWO_FINGERS_UP fingersDown < 2 && fingersWereDown == 2
#endif


/* Maximum distance that two fingers have been moved while they were on */
double maxDist;
/* The time when the first finger touched */
TimeVal fingerDownTime;
/* Were there once two fingers on during the current touch phase? */
int hadTwoFingersOn = 0;

/* Profile of current window, if activated */
Profile* currentProfile = NULL;

/* Gesture information */
int amPerformingGesture = 0;
/* Current profile has scrollBraceAction, thus mouse pointer has to be moved during scrolling */
int dragScrolling = 0;

/* position of center between fingers at start of gesture */
int gestureStartCenterX, gestureStartCenterY;
/* distance between two fingers at start of gesture */
double gestureStartDist;
/* angle of two fingers at start of gesture */
double gestureStartAngle;
/* current position of center between fingers */
int currentCenterX, currentCenterY;

#define PI 3.141592654

/* 1 if the last X scroll command was to the right, -1 if it was to the left. */
int lastScrollDirectionX = 0;
/* 1 if the last Y scroll command was down, -1 if it was up. */
int lastScrollDirectionY = 0;
/* Time last scroll command in X/Y action was sent. */
TimeVal lastScrollXTime;
TimeVal lastScrollYTime;
/* Interval between the last two X/Y scroll commands. */
int lastScrollXIntv;
int lastScrollYIntv;
/* Last values of lastScrollXIntv/lastScrollYIntv. */
int lastLastScrollXIntv;
int lastLastScrollYIntv;



void initGestures(int theClickMode) {
clickMode = theClickMode;
}

/* All the gesture-related code.
* Returns 1 if the method should be called again, 0 otherwise.
*/
int checkGesture(FingerInfo* fingerInfos, int fingersDown) {

/* Calculate difference between two touch points and angle */
int xdiff = fingerInfos[1].x - fingerInfos[0].x;
int ydiff = fingerInfos[1].y - fingerInfos[0].y;
double currentDist = sqrt(xdiff * xdiff + ydiff * ydiff);
double currentAngle = atan2(ydiff, xdiff) * 180 / PI;

/* Check distance the fingers (more exactly: the point between them)
* has been moved since start of the gesture. */
int xdist = currentCenterX - gestureStartCenterX;
int ydist = currentCenterY - gestureStartCenterY;
double moveDist = sqrt(xdist * xdist + ydist * ydist);
if (moveDist > maxDist && fingersDown == 2) {
/* Set maxDist (but only if we are not in continuation) */
maxDist = moveDist;
}

/* Prevent division by zero */
if(gestureStartDist < 1) gestureStartDist = 0;

/* We don't know yet what to do, so look if we can decide now (only do this if there
are still two fingers down, otherwise we are in continuation and can't decide). */
if (amPerformingGesture == GESTURE_UNDECIDED && fingersDown == 2) {
int scrollMinDist = currentProfile->scrollMinDistance;
if (currentProfile->scrollInherit)
scrollMinDist = defaultProfile.scrollMinDistance;
if ((int) moveDist > scrollMinDist) {
amPerformingGesture = GESTURE_SCROLL;
if(inDebugMode()) printf("Start scrolling gesture\n");

if (currentProfile->scrollInherit) {
executeAction(&(defaultProfile.scrollBraceAction),
EXECUTEACTION_PRESS);
if (defaultProfile.scrollBraceAction.actionType
== ACTIONTYPE_NONE) {
dragScrolling = 0;
} else {
dragScrolling = 1;
}
} else {
executeAction(&(currentProfile->scrollBraceAction),
EXECUTEACTION_PRESS);
if (currentProfile->scrollBraceAction.actionType
== ACTIONTYPE_NONE) {
dragScrolling = 0;
} else {
dragScrolling = 1;
}
}
return 1;
}

int zoomMinDist = currentProfile->zoomMinDistance;
double zoomMinFactor = currentProfile->zoomMinFactor;
if (currentProfile->zoomInherit) {
zoomMinDist = defaultProfile.zoomMinDistance;
zoomMinFactor = defaultProfile.zoomMinFactor;
}
if (abs((int) currentDist - gestureStartDist) > zoomMinDist && (currentDist / gestureStartDist > zoomMinFactor || currentDist / gestureStartDist < 1/zoomMinFactor)) {
amPerformingGesture = GESTURE_ZOOM;
if(inDebugMode()) printf("Start zoom gesture\n");
return 1;
}

int rotateMinDist = currentProfile->rotateMinDistance;
double rotateMinAngle = currentProfile->rotateMinAngle;
if (currentProfile->rotateInherit) {
rotateMinDist = defaultProfile.rotateMinDistance;
rotateMinAngle = defaultProfile.rotateMinAngle;
}
double rotatedBy = currentAngle - gestureStartAngle;
if (rotatedBy < -180)
rotatedBy += 360;
if (rotatedBy > 180)
rotatedBy -= 360;
//printf("Rotated by: %f; min. angle: %f\n", rotatedBy, rotateMinAngle);
if (abs(rotatedBy) > rotateMinAngle && (int) currentDist
> rotateMinDist) {
amPerformingGesture = GESTURE_ROTATE;
if(inDebugMode()) printf("Start rotation gesture\n");
return 1;
}
}

/* If we know what gesture to perform, look if there is something to do */
switch (amPerformingGesture) {
case GESTURE_SCROLL:
;
int hscrolledBy = currentCenterX - gestureStartCenterX;
int vscrolledBy = currentCenterY - gestureStartCenterY;
int hscrollStep = currentProfile->hscrollStep;
int vscrollStep = currentProfile->vscrollStep;
if (currentProfile->scrollInherit) {
hscrollStep = defaultProfile.hscrollStep;
vscrollStep = defaultProfile.vscrollStep;
}
if (hscrollStep == 0 || vscrollStep == 0)
return 0;

TimeVal currentTime = getCurrentTime();
if (hscrolledBy > hscrollStep) {
lastScrollDirectionX = 1;
lastLastScrollXIntv = lastScrollXIntv;
lastScrollXIntv = timeDiff(lastScrollXTime, currentTime);
lastScrollXTime = currentTime;
if (currentProfile->scrollInherit) {
executeAction(&(defaultProfile.scrollRightAction),
EXECUTEACTION_BOTH);
} else {
executeAction(&(currentProfile->scrollRightAction),
EXECUTEACTION_BOTH);
}

gestureStartCenterX = gestureStartCenterX + hscrollStep;
return 1;
} else if (hscrolledBy < -hscrollStep) {
lastLastScrollXIntv = lastScrollXIntv;
lastScrollXIntv = timeDiff(lastScrollXTime, currentTime);
lastScrollXTime = currentTime;
lastScrollDirectionX = -1;
if (currentProfile->scrollInherit) {
executeAction(&(defaultProfile.scrollLeftAction),
EXECUTEACTION_BOTH);
} else {
executeAction(&(currentProfile->scrollLeftAction),
EXECUTEACTION_BOTH);
}

gestureStartCenterX = gestureStartCenterX - hscrollStep;
return 1;
}
if (vscrolledBy > vscrollStep) {
lastLastScrollYIntv = lastScrollYIntv;
lastScrollYIntv = timeDiff(lastScrollYTime, currentTime);
lastScrollYTime = currentTime;
lastScrollDirectionY = 1;
if (currentProfile->scrollInherit) {
executeAction(&(defaultProfile.scrollDownAction),
EXECUTEACTION_BOTH);
} else {
executeAction(&(currentProfile->scrollDownAction),
EXECUTEACTION_BOTH);
}

gestureStartCenterY = gestureStartCenterY + vscrollStep;
return 1;
} else if (vscrolledBy < -vscrollStep) {
lastLastScrollYIntv = lastScrollYIntv;
lastScrollYIntv = timeDiff(lastScrollYTime, currentTime);
lastScrollYTime = currentTime;
lastScrollDirectionY = -1;
if (currentProfile->scrollInherit) {
executeAction(&(defaultProfile.scrollUpAction),
EXECUTEACTION_BOTH);
} else {
executeAction(&(currentProfile->scrollUpAction),
EXECUTEACTION_BOTH);
}

gestureStartCenterY = gestureStartCenterY - vscrollStep;
return 1;
}

return 0;
case GESTURE_ZOOM:
;
double zoomedBy = currentDist / gestureStartDist;
double zoomStep = currentProfile->zoomStep;
if (currentProfile->zoomInherit)
zoomStep = defaultProfile.zoomStep;
if (zoomedBy > zoomStep) {
if(inDebugMode()) printf("Zoom in step\n");
if (currentProfile->zoomInherit) {
executeAction(&(defaultProfile.zoomInAction),
EXECUTEACTION_BOTH);
} else {
executeAction(&(currentProfile->zoomInAction),
EXECUTEACTION_BOTH);
}
/* Reset distance */
gestureStartDist = gestureStartDist * zoomStep;
return 1;
} else if (zoomedBy < 1 / zoomStep) {
if(inDebugMode()) printf("Zoom out step\n");
if (currentProfile->zoomInherit) {
executeAction(&(defaultProfile.zoomOutAction),
EXECUTEACTION_BOTH);
} else {
executeAction(&(currentProfile->zoomOutAction),
EXECUTEACTION_BOTH);
}
/* Reset distance */
gestureStartDist = gestureStartDist / zoomStep;
return 1;
}
return 0;
case GESTURE_ROTATE:
;
double rotatedBy = currentAngle - gestureStartAngle;
if (rotatedBy < -180)
rotatedBy += 360;
if (rotatedBy > 180)
rotatedBy -= 360;
double rotateStep = currentProfile->rotateStep;
if (currentProfile->rotateInherit)
rotateStep = defaultProfile.rotateStep;
if (rotatedBy > rotateStep) {
if(inDebugMode()) printf("Rotate right\n");
if (currentProfile->rotateInherit) {
executeAction(&(defaultProfile.rotateRightAction),
EXECUTEACTION_BOTH);
} else {
executeAction(&(currentProfile->rotateRightAction),
EXECUTEACTION_BOTH);
}

gestureStartAngle = gestureStartAngle + rotateStep;
} else if (rotatedBy < -rotateStep) {
if(inDebugMode()) printf("Rotate left\n");
if (currentProfile->rotateInherit) {
executeAction(&(defaultProfile.rotateLeftAction),
EXECUTEACTION_BOTH);
} else {
executeAction(&(currentProfile->rotateLeftAction),
EXECUTEACTION_BOTH);
}

gestureStartAngle = gestureStartAngle - rotateStep;
}

return 0;
}

return 0;

}

void processFingerGesture(FingerInfo* fingerInfos, int fingersDown, int fingersWereDown) {

if(fingersDown != 0 && fingersWereDown == 0) {
stopEasing();
}

TimeVal currentTime = getCurrentTime();
if (TWO_FINGERS_DOWN) {
/* Second finger touched (and maybe first too) */

lastScrollXTime = currentTime;
lastScrollYTime = currentTime;
lastScrollXIntv = 0; lastScrollYIntv = 0;
lastLastScrollXIntv = 0; lastLastScrollYIntv = 0;

maxDist = 0;

/* Memorize that there were two fingers on during touch */
hadTwoFingersOn = 1;

/* Get current profile */
currentProfile = getWindowProfile(getActiveWindow());
if(inDebugMode()) {
if(currentProfile->windowClass != NULL) {
printf("Use profile '%s'\n", currentProfile->windowClass);
} else {
printf("Use default profile.\n");
}
}

/* If there had already been a single-touch event raised because the
* user was too slow, stop it now. */
releaseButton();

/* Calculate center position and distance between touch points */
gestureStartCenterX = (fingerInfos[0].x + fingerInfos[1].x) / 2;
gestureStartCenterY = (fingerInfos[0].y + fingerInfos[1].y) / 2;

int xdiff = fingerInfos[1].x - fingerInfos[0].x;
int ydiff = fingerInfos[1].y - fingerInfos[0].y;
gestureStartDist = sqrt(xdiff * xdiff + ydiff * ydiff);
gestureStartAngle = atan2(ydiff, xdiff) * 180 / PI;

/* We have not decided on a gesture yet. */
amPerformingGesture = GESTURE_UNDECIDED;

movePointer(gestureStartCenterX, gestureStartCenterY, 0);
} else if (TWO_FINGERS_ON) {

/* Moved with two fingers */

if(fingersDown == 2) {
/* Calculate new center between fingers */
currentCenterX = (fingerInfos[0].x + fingerInfos[1].x) / 2;
currentCenterY = (fingerInfos[0].y + fingerInfos[1].y) / 2;
} else {
int i;
for(i = 0; i <= 1; i++) {
if(fingerInfos[i].slotUsed) {
currentCenterX = fingerInfos[i].x;
currentCenterY = fingerInfos[i].y;
}
}
}

/* If we are dragScrolling (we are scrolling and there is a brace action,
* we need to move the pointer */
if (amPerformingGesture == GESTURE_SCROLL && dragScrolling) {
/* Move pointer to center between touch points */
movePointer(currentCenterX, currentCenterY, 0);
}

/* Perform gestures as long as there are some. */
while (checkGesture(fingerInfos, fingersDown));
} else if (TWO_FINGERS_UP) {
/* Second finger (and maybe also first) released */

if (amPerformingGesture == GESTURE_SCROLL) {
/* If there was a scroll gesture and we have a brace action, perform release. */
if (currentProfile->scrollInherit) {
executeAction(&(defaultProfile.scrollBraceAction),
EXECUTEACTION_RELEASE);
} else {
executeAction(&(currentProfile->scrollBraceAction),
EXECUTEACTION_RELEASE);
}
if(currentProfile->scrollEasing) {
int intv;
int dirX = lastScrollDirectionX;
int dirY = lastScrollDirectionY;

/* Start easing */
if(inDebugMode()) printf("Start easing\n");

/* Compensate for scrolling gestures getting a little bit slower at the end */
if(lastLastScrollXIntv < lastScrollXIntv && lastLastScrollXIntv != 0) lastScrollXIntv = lastLastScrollXIntv;
if(lastLastScrollYIntv < lastScrollYIntv && lastLastScrollYIntv != 0) lastScrollYIntv = lastLastScrollYIntv;

/* Check if scrolling intervals are not too long. Also check if last scrolling on an axis is longer
ago than twice its interval, which means the scrolling has been stopped or extremely slowed down since. */
if(lastScrollYIntv == 0 || timeDiff(lastScrollYTime, currentTime) > lastScrollYIntv * 2 || lastScrollYIntv > MAX_EASING_START_INTERVAL) dirY = 0;
if(lastScrollXIntv == 0 || timeDiff(lastScrollXTime, currentTime) > lastScrollXIntv * 2 || lastScrollXIntv > MAX_EASING_START_INTERVAL) dirX = 0;
if(dirX != 0 || dirY != 0) {
if(dirX != 0 && dirY != 0) {
/* As we only support one interval, only use larger axis. */
if(lastScrollXIntv < lastScrollYIntv) {
dirY = 0;
} else {
dirX = 0;
}
}

if(dirY == 0) {
intv = lastScrollXIntv;
} else if(dirX == 0) {
intv = lastScrollYIntv;
} else {
/* We will never reach this, but removes warning */
intv = 100000;
}
if(inDebugMode()) printf("Really start easing\n");
startEasing(currentProfile, dirX, dirY, intv);
}
}
}

/* If we haven't performed a gesture and haven't moved too far, perform tap action. */
if ((amPerformingGesture == GESTURE_NONE || amPerformingGesture
== GESTURE_UNDECIDED) && maxDist < 10) {
/* Move pointer to correct position */
if(clickMode == 2) {
/* Assume first finger is at ID 0 and second finger at ID 1, might have to be changed later */
movePointer(gestureStartCenterX, gestureStartCenterY, fingerInfos[0].rawZ);
} else {
/* Assume first finger is at ID 0 and second finger at ID 1, might have to be changed later */
movePointer(fingerInfos[clickMode].x, fingerInfos[clickMode].y, fingerInfos[clickMode].rawZ);
}

if (currentProfile->tapInherit) {
executeAction(&(defaultProfile.tapAction), EXECUTEACTION_BOTH);
} else {
executeAction(&(currentProfile->tapAction), EXECUTEACTION_BOTH);
}
}

amPerformingGesture = GESTURE_NONE;

} else if (fingersDown == 1 && fingersWereDown == 0) {
/* First finger touched */
fingerDownTime = currentTime;
/* Fake single-touch move event */
int i;
for(i = 0; i <= 1; i++) {
if(fingerInfos[i].slotUsed) {
movePointer(fingerInfos[i].x, fingerInfos[i].y, fingerInfos[i].rawZ);
}
}

} else if (fingersDown == 1) {
/* Moved with one finger */
if (hadTwoFingersOn == 0 && !isButtonDown()) {
if (timeDiff(fingerDownTime, currentTime) > CLICK_DELAY) {
/* Delay has passed, no gesture been performed, so perform single-touch press now */
pressButton();
}
}
if(isButtonDown()) {
/* Fake single-touch move event */
int i;
for(i = 0; i <= 1; i++) {
if(fingerInfos[i].slotUsed) {
movePointer(fingerInfos[i].x, fingerInfos[i].y, fingerInfos[i].rawZ);
}
}

}
} else if (fingersDown == 0 && fingersWereDown > 0) {
printf("Last finger released!\n");
/* Last finger released */
if (hadTwoFingersOn == 0 && !isButtonDown()) {
/* The button press time has not been reached yet, and we never had two
* fingers on (we could not have done this in this short time) so
* we simulate button down and up now. */
pressButton();
releaseButton();
printf("Handle release 1\n");
} else {
/* We release the button if it is down. */
releaseButton();
printf("Handle release 2\n");
}
}
if (fingersDown == 0) {
/* Reset fields after release */
hadTwoFingersOn = 0;
}
}


Profile* getDefaultProfile() {
return &defaultProfile;
}

/* Returns a pointer to the profile of the currently selected
* window, or defaultProfile if there is no specific profile for it or the window is invalid. */
Profile* getWindowProfile(Window w) {
if (w != None) {

char* class = getWindowClass(w);

if(class != NULL) {
if(inDebugMode()) {
printf("Current window: '%s'\n", class);
}

int i;
/* Look for the profile with this class */
for (i = 0; i < profileCount; i++) {
if (!strncmp(class, profiles[i].windowClass, 30)) {
free(class);
/* Return this profile */
return &profiles[i];
}
}

/* No profile found, return default. */
free(class);
return &defaultProfile;
} else {
return &defaultProfile;
}
} else {
return &defaultProfile;
}
}

int isWindowBlacklistedForGestures(Window w) {
int i;
char* class = getWindowClass(w);

if (class != NULL) {
if(inDebugMode()) printf("Found window with id %i and class '%s' \n", (int) w,
class);

for (i = 0; blacklist[i] != NULL; i++) {
if (strncmp(class, blacklist[i], 30) == 0) {
free(class);
return 1;
}
}

/* Not blacklisted. Check if is on wmBlacklist */
for(i = 0; wmBlacklist[i] != NULL; i++) {
if (strncmp(class, wmBlacklist[i], 30) == 0) {
free(class);
if(inDebugMode()) printf("Look for child\n");
return isWindowBlacklisted(getLastChildWindow(w));
}
}

free(class);
return 0;
} else {
if(inDebugMode()) printf("Found window with id %i and no class.\n", (int) w);
//if(inDebugMode()) printf("Look for another child\n");
return isWindowBlacklisted(getLastChildWindow(w));
return 0;
}
}
twofing-0.0.9/gestures.h000064400000000000000000000020021173133520700152010ustar00rootroot00000000000000/*
Copyright (C) 2010, Philipp Merkel <linux@philmerk.de>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef GESTURES_H_
#define GESTURES_H_

void initGestures(int);
void processFingerGesture(FingerInfo*, int, int);

Profile *getWindowProfile(Window);

int isWindowBlacklistedForGestures(Window);

Profile * getDefaultProfile();

#endif /* GESTURES_H_ */
twofing-0.0.9/profiles.h000064400000000000000000000224231173133520700151740ustar00rootroot00000000000000/*
Copyright (C) 2010, Philipp Merkel <linux@philmerk.de>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/

#include "twofingemu.h"
#include <X11/keysym.h>

#ifndef PROFILES_H_
#define PROFILES_H_

/* Blacklist */
char* blacklist[] = { "xournal", "gimp-2.6", "gimp", "mypaint", "inkscape", NULL };

/* WM-Blacklist */
char* wmBlacklist[] = { "kwin", "mutter", "metacity", "compiz", "unity", NULL };

/* Profiles */
int profileCount = 8;
Profile profiles[] = {
{ .windowClass = "evince",
.scrollInherit = 0,
.scrollMinDistance = 50,
.hscrollStep = 1000,
.vscrollStep = 1000,
.scrollBraceAction = { ACTIONTYPE_BUTTONPRESS, 2, 0 },
.scrollUpAction = { ACTIONTYPE_NONE,0,0 },
.scrollDownAction = { ACTIONTYPE_NONE,0,0 },
.scrollLeftAction = { ACTIONTYPE_NONE,0,0 },
.scrollRightAction = { ACTIONTYPE_NONE,0,0 },
.scrollEasing = 0,
.zoomInherit = 0,
.zoomMinDistance = 80,
.zoomInAction = { ACTIONTYPE_BUTTONPRESS, 4, MODIFIER_CONTROL },
.zoomOutAction = { ACTIONTYPE_BUTTONPRESS, 5, MODIFIER_CONTROL },
.zoomStep = 1.2,
.zoomMinFactor = 1.3,
.rotateInherit = 0,
.rotateMinDistance = 100,
.rotateMinAngle = 30,
.rotateStep = 70,
.rotateLeftAction = { ACTIONTYPE_KEYPRESS, XK_Left, MODIFIER_CONTROL },
.rotateRightAction = { ACTIONTYPE_KEYPRESS, XK_Right, MODIFIER_CONTROL },
.tapInherit = 1
},
{ .windowClass = "eog",
.scrollInherit = 0,
.scrollMinDistance = 50,
.hscrollStep = 1000,
.vscrollStep = 1000,
.scrollBraceAction = { ACTIONTYPE_BUTTONPRESS, 2, 0 },
.scrollUpAction = { ACTIONTYPE_NONE,0,0 },
.scrollDownAction = { ACTIONTYPE_NONE,0,0 },
.scrollLeftAction = { ACTIONTYPE_NONE,0,0 },
.scrollRightAction = { ACTIONTYPE_NONE,0,0 },
.scrollEasing = 0,
.zoomInherit = 0,
.zoomMinDistance = 80,
.zoomInAction = { ACTIONTYPE_BUTTONPRESS, 4, 0 },
.zoomOutAction = { ACTIONTYPE_BUTTONPRESS, 5, 0 },
.zoomStep = 1.05,
.zoomMinFactor = 1.1,
.rotateInherit = 0,
.rotateMinDistance = 100,
.rotateMinAngle = 30,
.rotateStep = 70,
.rotateLeftAction = { ACTIONTYPE_KEYPRESS, XK_R, MODIFIER_CONTROL | MODIFIER_SHIFT },
.rotateRightAction = { ACTIONTYPE_KEYPRESS, XK_R, MODIFIER_CONTROL },
.tapInherit = 1
//,
//.tapAction = {ACTIONTYPE_NONE,0,0 }
},
{ .windowClass = "f-spot",
.scrollInherit = 0,
.scrollMinDistance = 50,
.hscrollStep = 50,
.vscrollStep = 100,
.scrollBraceAction = { ACTIONTYPE_NONE, 0, 0 },
.scrollUpAction = { ACTIONTYPE_BUTTONPRESS, 5, MODIFIER_SHIFT },
.scrollDownAction = { ACTIONTYPE_BUTTONPRESS, 4, MODIFIER_SHIFT },
.scrollLeftAction = { ACTIONTYPE_BUTTONPRESS, 7, MODIFIER_SHIFT },
.scrollRightAction = { ACTIONTYPE_BUTTONPRESS, 6, MODIFIER_SHIFT },
.scrollEasing = 1,
.zoomInherit = 0,
.zoomMinDistance = 80,
.zoomInAction = { ACTIONTYPE_BUTTONPRESS, 4, 0 },
.zoomOutAction = { ACTIONTYPE_BUTTONPRESS, 5, 0 },
.zoomStep = 1.1,
.zoomMinFactor = 1.3,
.rotateInherit = 0,
.rotateMinDistance = 100,
.rotateMinAngle = 30,
.rotateStep = 70,
.rotateLeftAction = { ACTIONTYPE_KEYPRESS, XK_bracketleft, 0 },
.rotateRightAction = { ACTIONTYPE_KEYPRESS, XK_bracketright, 0 },
.tapInherit = 1
},
{ .windowClass = "netbook-launcher",
.scrollInherit = 0,
.scrollMinDistance = 50,
.hscrollStep = 300,
.vscrollStep = 125,
.scrollBraceAction = { ACTIONTYPE_NONE, 0, 0 },
.scrollUpAction = { ACTIONTYPE_BUTTONPRESS,5,0 },
.scrollDownAction = { ACTIONTYPE_BUTTONPRESS,4,0 },
.scrollLeftAction = { ACTIONTYPE_KEYPRESS,XK_Right, MODIFIER_CONTROL | MODIFIER_ALT },
.scrollRightAction = { ACTIONTYPE_KEYPRESS,XK_Left, MODIFIER_CONTROL | MODIFIER_ALT },
.scrollEasing = 0,
.zoomInherit = 1,
.rotateInherit = 1,
.tapInherit = 1
},
{ .windowClass = "desktop_window",
.scrollInherit = 0,
.scrollMinDistance = 50,
.hscrollStep = 300,
.vscrollStep = 125,
.scrollBraceAction = { ACTIONTYPE_NONE, 0, 0 },
.scrollUpAction = { ACTIONTYPE_KEYPRESS,XK_Down, MODIFIER_CONTROL | MODIFIER_ALT },
.scrollDownAction = { ACTIONTYPE_KEYPRESS,XK_Up, MODIFIER_CONTROL | MODIFIER_ALT },
.scrollLeftAction = { ACTIONTYPE_KEYPRESS,XK_Right, MODIFIER_CONTROL | MODIFIER_ALT },
.scrollRightAction = { ACTIONTYPE_KEYPRESS,XK_Left, MODIFIER_CONTROL | MODIFIER_ALT },
.scrollEasing = 0,
.zoomInherit = 1,
.rotateInherit = 1,
.tapInherit = 1
},
{ .windowClass = "acroread",
.scrollInherit = 0,
.scrollMinDistance = 50,
.hscrollStep = 20,
.vscrollStep = 20,
.scrollBraceAction = { ACTIONTYPE_NONE,0,0 },
.scrollUpAction = { ACTIONTYPE_BUTTONPRESS, 5, 0 },
.scrollDownAction = { ACTIONTYPE_BUTTONPRESS, 4, 0 },
.scrollLeftAction = { ACTIONTYPE_BUTTONPRESS, 5, MODIFIER_SHIFT },
.scrollRightAction = { ACTIONTYPE_BUTTONPRESS, 4, MODIFIER_SHIFT },
.scrollEasing = 1,
.zoomInherit = 0,
.zoomMinDistance = 80,
.zoomInAction = { ACTIONTYPE_KEYPRESS, XK_equal, MODIFIER_CONTROL },
.zoomOutAction = { ACTIONTYPE_KEYPRESS, XK_minus, MODIFIER_CONTROL },
.zoomStep = 1.5,
.zoomMinFactor = 1.5,
.rotateInherit = 0,
.rotateMinDistance = 100,
.rotateMinAngle = 30,
.rotateStep = 70,
.rotateLeftAction = { ACTIONTYPE_KEYPRESS, XK_Left, MODIFIER_CONTROL },
.rotateRightAction = { ACTIONTYPE_KEYPRESS, XK_Right, MODIFIER_CONTROL },
.tapInherit = 1
},
{ .windowClass = "SimCity 4.exe",
.scrollInherit = 0,
.scrollMinDistance = 20,
.hscrollStep = 1000,
.vscrollStep = 1000,
.scrollBraceAction = { ACTIONTYPE_BUTTONPRESS, 3, 0 },
.scrollUpAction = { ACTIONTYPE_NONE,0,0 },
.scrollDownAction = { ACTIONTYPE_NONE,0,0 },
.scrollLeftAction = { ACTIONTYPE_NONE,0,0 },
.scrollRightAction = { ACTIONTYPE_NONE,0,0 },
.scrollEasing = 0,
// .scrollMinDistance = 20,
// .hscrollStep = 10,
// .vscrollStep = 10,
// .scrollBraceAction = { ACTIONTYPE_NONE,0,0 },
// .scrollUpAction = { ACTIONTYPE_KEYPRESS, XK_Down, 0 },
// .scrollDownAction = { ACTIONTYPE_KEYPRESS, XK_Up, 0 },
// .scrollLeftAction = { ACTIONTYPE_KEYPRESS, XK_Right, 0 },
// .scrollRightAction = { ACTIONTYPE_KEYPRESS, XK_Left, 0 },
.zoomInherit = 0,
.zoomMinDistance = 80,
.zoomInAction = { ACTIONTYPE_BUTTONPRESS, 4, 0 },
.zoomOutAction = { ACTIONTYPE_BUTTONPRESS, 5, 0 },
.zoomStep = 1.5,
.zoomMinFactor = 1.5,
.rotateInherit = 1,
.tapInherit = 1
},
{ .windowClass = "googleearth-bin",
.scrollInherit = 0,
.scrollMinDistance = 100,
.hscrollStep = 30,
.vscrollStep = 30,
.scrollBraceAction = { ACTIONTYPE_KEYPRESS, XK_Shift_L, 0 },
.scrollUpAction = { ACTIONTYPE_BUTTONPRESS,4,0 },
.scrollDownAction = { ACTIONTYPE_BUTTONPRESS,5,0 },
.scrollLeftAction = { ACTIONTYPE_NONE,0,0 },
.scrollRightAction = { ACTIONTYPE_NONE,0,0 },
.scrollEasing = 0,
.zoomInherit = 0,
.zoomMinDistance = 80,
.zoomInAction = { ACTIONTYPE_BUTTONPRESS, 4, 0 },
.zoomOutAction = { ACTIONTYPE_BUTTONPRESS, 5, 0 },
.zoomStep = 1.2,
.zoomMinFactor = 1.3,
.rotateInherit = 0,
.rotateMinDistance = 100,
.rotateMinAngle = 10,
.rotateStep = 15,
.rotateLeftAction = { ACTIONTYPE_BUTTONPRESS, 5, MODIFIER_CONTROL },
.rotateRightAction = { ACTIONTYPE_BUTTONPRESS, 4, MODIFIER_CONTROL },
.tapInherit = 1
//,
//.tapAction = {ACTIONTYPE_NONE,0,0 }
}
};
/* DON'T FORGET TO CHANGE profileCount WHEN ADDING PROFILES! */


/* Default (fallback) profile */
Profile defaultProfile = { .windowClass = NULL,
.scrollInherit = 0,
.hscrollStep = 40,
.vscrollStep = 40,
.scrollMinDistance = 50,
.scrollBraceAction = { ACTIONTYPE_NONE,0,0 },
.scrollUpAction = { ACTIONTYPE_BUTTONPRESS, 5, 0 },
.scrollDownAction = { ACTIONTYPE_BUTTONPRESS, 4, 0 },
.scrollLeftAction = { ACTIONTYPE_BUTTONPRESS, 7, 0 },
.scrollRightAction = { ACTIONTYPE_BUTTONPRESS, 6, 0 },
.scrollEasing = 1,
.zoomInherit = 0,
.zoomMinDistance = 80,
.zoomInAction = { ACTIONTYPE_BUTTONPRESS, 4, MODIFIER_CONTROL },
.zoomOutAction = { ACTIONTYPE_BUTTONPRESS, 5, MODIFIER_CONTROL },
.zoomStep = 1.5,
.zoomMinFactor = 1.5,
.rotateInherit = 0,
.rotateMinDistance = 10000,
.rotateMinAngle = 370,
.rotateLeftAction = { ACTIONTYPE_NONE,0,0 },
.rotateRightAction = { ACTIONTYPE_NONE,0,0 },
.rotateStep = 90,
.tapInherit = 0,
.tapAction = { ACTIONTYPE_BUTTONPRESS, 3, 0 }
};


#endif /* PROFILES_H_ */
twofing-0.0.9/twofingemu.c000064400000000000000000000747631173133520700155460ustar00rootroot00000000000000/*
Copyright (C) 2010, 2011 Philipp Merkel <linux@philmerk.de>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/

#include <linux/input.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <X11/Xutil.h>
#include <X11/X.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <X11/extensions/XTest.h>
#include <X11/keysym.h>
#include <X11/extensions/XInput2.h>
#include <X11/extensions/Xrandr.h>
#include "twofingemu.h"
#include "gestures.h"
#include "easing.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <time.h>

#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1

int debugMode = 0;

int inDebugMode() {
return debugMode;
}


/* Daemonize. Source: http://www-theorie.physik.unizh.ch/~dpotter/howto/daemonize (public domain) */
static void daemonize(void) {
pid_t pid, sid;

/* already a daemon */
if (getppid() == 1)
return;

/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}

/* At this point we are executing as the child process */

/* Change the file mode mask */
umask(0);

/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
exit(EXIT_FAILURE);
}

/* Change the current working directory. This prevents the current
directory from being locked; hence not being able to remove it. */
if ((chdir("/")) < 0) {
exit(EXIT_FAILURE);
}

/* Redirect standard files to /dev/null */
void* r;
r = freopen("/dev/null", "r", stdin);
r = freopen("/dev/null", "w", stdout);
r = freopen("/dev/null", "w", stderr);
r = r;
}

/* Finger information */
FingerInfo fingerInfos[2] = { { .rawX=0, .rawY=0, .rawZ=0, .id = -1, .slotUsed = 0, .setThisTime = 0 }, { .rawX=0, .rawY=0, .rawZ=0,
.id = -1, .slotUsed = 0, .setThisTime = 0 } };

/* X stuff */
Display* display;
Window root;
int screenNum;
int deviceID;
Atom WM_CLASS;
pthread_t xLoopThread;

/* Calibration data */
int calibMinX, calibMaxX, calibMinY, calibMaxY;
unsigned char calibSwapX, calibSwapY, calibSwapAxes;
float calibMatrix[6];
int calibMatrixUse = 0;

/* The width and height of the screen in pixels */
unsigned int screenWidth, screenHeight;

/* Finger data */
int fingersDown = 0;
int fingersWereDown = 0;
/* Has button press of first button been called in XTest? */
int buttonDown = 0;

/* Does the device use the legacy MT protocol? */
int useLegacyProtocol = 0;

/* Handle errors by, well, throwing them away. */
int invalidWindowHandler(Display *dsp, XErrorEvent *err) {
return 0;
}

/* Grab the device so input is captured */
void grab(Display* display, int grabDeviceID) {
XIEventMask device_mask;
unsigned char mask_data[8] = { 0,0,0,0,0,0,0,0 };
device_mask.mask_len = sizeof(mask_data);
device_mask.mask = mask_data;
XISetMask(device_mask.mask, XI_Motion);
XISetMask(device_mask.mask, XI_ButtonPress);

/* Experiments with X MT support, not working yet */
// XISetMask(device_mask.mask, XI_TouchBegin);
// XISetMask(device_mask.mask, XI_TouchUpdate);
// XISetMask(device_mask.mask, XI_TouchEnd);
// XIGrabModifiers modifiers;
// modifiers.modifiers = XIAnyModifier;
// int r = XIGrabButton(display, grabDeviceID, XIAnyButton, root, None, GrabModeSync,
// GrabModeAsync, True, &device_mask, 1, &modifiers);
// int r = XIGrabTouchBegin(display, grabDeviceID, root, None, &device_mask, 0, modifiers);

int r = XIGrabDevice(display, grabDeviceID, root, CurrentTime, None, GrabModeAsync, GrabModeAsync, False, &device_mask);

if(debugMode) printf("Grab Result: %i\n", r);

}

/* Ungrab the device so input can be handled by application directly */
void ungrab(Display* display, int grabDeviceID) {
/* Experiments with X MT support, not working yet */
// XIGrabModifiers modifiers[1] = { { 0, 0 } };
// XIUngrabButton(display, grabDeviceID, 1, root, 1, modifiers);
XIUngrabDevice(display, grabDeviceID, CurrentTime);
}

TimeVal getCurrentTime() {

TimeVal time;
gettimeofday(&time, NULL);

return time;
}

int timeDiff(TimeVal start, TimeVal end)
{
long seconds = end.tv_sec - start.tv_sec;
long microSeconds = end.tv_usec - start.tv_usec;

return (seconds * 1000 + microSeconds/1000);
}



/* Send an XTest event to release the first button if it is currently pressed */
void releaseButton() {
if (buttonDown) {
buttonDown = 0;
XTestFakeButtonEvent(display, 1, False, CurrentTime);
XFlush(display);

/* Experiments with Pressure Sensitivity, not working yet */
/* XDevice * dev = XOpenDevice(display, deviceID);
int axes[3] = {fingerInfos[0].rawX, fingerInfos[0].rawY, fingerInfos[0].rawZ};
XTestFakeDeviceButtonEvent(display, dev, 1, False, axes, 3, 0);
XCloseDevice(display, dev);

XFlush(display);
grab(display, deviceID);*/
}
}
/* Send an XTest event to press the first button if it is not pressed yet */
void pressButton() {
if(!buttonDown) {

/* Experiments with Pressure Sensitivity, not working yet */
/* XDevice * dev = XOpenDevice(display, deviceID);
int axes[3] = {fingerInfos[0].rawX, fingerInfos[0].rawY, fingerInfos[0].rawZ};
XTestFakeDeviceButtonEvent(display, dev, 1, False, axes, 3, 0);
XCloseDevice(display, dev);
XFlush(display);

ungrab(display, deviceID);
XFlush(display);

printf("PRESS!\n");
dev = XOpenDevice(display, deviceID);
XTestFakeDeviceButtonEvent(display, dev, 1, True, axes, 3, 0);
XCloseDevice(display, dev);
*/
buttonDown = 1;
XTestFakeButtonEvent(display, 1, True, CurrentTime);
XFlush(display);
}
}
/* Is the first button currently pressed? */
int isButtonDown() {
return buttonDown;
}


/* Moves the pointer to the given position */
void movePointer(int x, int y, int z) {
/* Move pointer to center between touch points */

/* Experiments with XI events, not working yet */
// int axes[3] = {x, y, z};
// XDevice * dev = XOpenDevice(display, 17);
// XTestFakeDeviceMotionEvent(display, dev, False, 0, axes, 2, 0);
// XCloseDevice(display, dev);

XTestFakeMotionEvent(display, -1, x, y, CurrentTime);
XFlush(display);
}


/* Executes the given action -- synthesizes key/button press, release or both, depending
* on value of whatToDo (EXECUTEACTION_PRESS/_RELEASE/_BOTH). */
void executeAction(Action* action, int whatToDo) {
if (whatToDo & EXECUTEACTION_PRESS) {
if (action->actionType != ACTIONTYPE_NONE && action->modifier != 0) {
if (action->modifier & MODIFIER_SHIFT) {
XTestFakeKeyEvent(display,
XKeysymToKeycode(display, XK_Shift_L), True,
CurrentTime);
XFlush(display);
}
if (action->modifier & MODIFIER_CONTROL) {
XTestFakeKeyEvent(display, XKeysymToKeycode(display,
XK_Control_L), True, CurrentTime);
XFlush(display);
}
if (action->modifier & MODIFIER_ALT) {
XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Alt_L),
True, CurrentTime);
XFlush(display);
}
if (action->modifier & MODIFIER_SUPER) {
XTestFakeKeyEvent(display,
XKeysymToKeycode(display, XK_Super_L), True,
CurrentTime);
XFlush(display);
}
}

switch (action->actionType) {
case ACTIONTYPE_BUTTONPRESS:
XTestFakeButtonEvent(display, action->keyButton, True, CurrentTime);
XFlush(display);
break;
case ACTIONTYPE_KEYPRESS:
XTestFakeKeyEvent(display, XKeysymToKeycode(display,
action->keyButton), True, CurrentTime);
XFlush(display);
break;
}

}

if (whatToDo & EXECUTEACTION_RELEASE) {

switch (action->actionType) {
case ACTIONTYPE_BUTTONPRESS:
XTestFakeButtonEvent(display, action->keyButton, False, CurrentTime);
XFlush(display);
break;
case ACTIONTYPE_KEYPRESS:
XTestFakeKeyEvent(display, XKeysymToKeycode(display,
action->keyButton), False, CurrentTime);
XFlush(display);
break;
}

if (action->actionType != ACTIONTYPE_NONE && action->modifier != 0) {
if (action->modifier & MODIFIER_SHIFT) {
XTestFakeKeyEvent(display,
XKeysymToKeycode(display, XK_Shift_L), False,
CurrentTime);
XFlush(display);
}
if (action->modifier & MODIFIER_CONTROL) {
XTestFakeKeyEvent(display, XKeysymToKeycode(display,
XK_Control_L), False, CurrentTime);
XFlush(display);
}
if (action->modifier & MODIFIER_ALT) {
XTestFakeKeyEvent(display, XKeysymToKeycode(display, XK_Alt_L),
False, CurrentTime);
XFlush(display);
}
if (action->modifier & MODIFIER_SUPER) {
XTestFakeKeyEvent(display,
XKeysymToKeycode(display, XK_Super_L), False,
CurrentTime);
XFlush(display);
}
}
}

}

Window getParentWindow(Window w) {
Window root, parent;
Window* childWindows = NULL;
unsigned int childCount;
if(XQueryTree(display, w, &root, &parent, &childWindows, &childCount)) {

if (childWindows != NULL)
XFree(childWindows);
return parent;
} else {
return None;
}

}

Window getLastChildWindow(Window w) {
Window root, parent;
Window* childWindows = NULL;
unsigned int childCount;
if(XQueryTree(display, w, &root, &parent, &childWindows, &childCount)) {

if (childWindows != NULL) {
if(childCount > 0) {
Window child = childWindows[childCount - 1];
XFree(childWindows);
return child;
}
XFree(childWindows);
}
return None;
} else {
return None;
}

}

Window getActiveWindow() {

/* Window *root_return = None, *child_return = None;
int *root_x_return = 0, *root_y_return = 0;
int *win_x_return = 0, *win_y_return = 0;
unsigned int *mask_return = 0;

XQueryPointer(display, root, root_return, child_return, root_x_return, root_y_return,
win_x_return, win_y_return, mask_return);

return *child_return;*/
return getCurrentWindow();

}

/* Returns the active top-level window. A top-level window is one that has WM_CLASS set.
* May also return None. */
Window getCurrentWindow() {

/* First get the window that has the input focus */
Window currentWindow;
int revert;
XGetInputFocus(display, &currentWindow, &revert);

if (currentWindow == None) {
//if(debugMode) printf("Leave getCurrentWindow\n");
return currentWindow;
}

/* Now go through parent windows until we find one with WM_CLASS set. */


XClassHint* classHint = XAllocClassHint();
if(classHint == NULL) {
if(debugMode) printf("Couldn't allocate class hint!!\n");
return None;
}

int i = 0;
while (1) {
//if(debugMode) printf("in Loop\n");
i++;
if(i >= 5) {
if(debugMode) printf("Too many iterations in getCurrentWindow\n");
XFree(classHint);
//if(debugMode) printf("Leave getCurrentWindow\n");
return None;
}
if (currentWindow == root || currentWindow == None) {
//if(debugMode) printf("Reached root!\n");
/* No top-level window available. Should never happen. */
XFree(classHint);
//if(debugMode) printf("Leave getCurrentWindow\n");
return currentWindow;
}

//if(debugMode) printf("Call XGetClassHint!\n");
if (XGetClassHint(display, currentWindow, classHint) == 0) {
//if(debugMode) printf("Has no Class!\n");
/* Has no WM_CLASS, thus no top-level window */
Window parent = getParentWindow(currentWindow);

if(parent == None || currentWindow == parent) {
/* something wrong */
XFree(classHint);
return currentWindow;
}
/* Continue with parent until we find WM_CLASS */
currentWindow = parent;
} else {
//if(debugMode) printf("Clean up class name!\n");
if(classHint->res_class != NULL) XFree(classHint->res_class);
if(classHint->res_name != NULL) XFree(classHint->res_name);
XFree(classHint);
//if(debugMode) printf("Leave getCurrentWindow\n");
return currentWindow;
}
}
}


/* Sets the calibrated x, y coordinates from the raw coordinates in the given FingerInfo */
void calibrate(FingerInfo* fingerInfo) {
float xf; float yf;
if (calibSwapAxes) {

xf = ((float)(fingerInfo->rawY - calibMinX))/((float) (calibMaxX-calibMinX));
yf = ((float)(fingerInfo->rawX - calibMinY))/((float) (calibMaxY-calibMinY));

} else {

xf = ((float)(fingerInfo->rawX - calibMinX))/((float) (calibMaxX-calibMinX));
yf = ((float)(fingerInfo->rawY - calibMinY))/((float) (calibMaxY-calibMinY));

}
if (calibSwapX) xf = 1 - xf;
if (calibSwapY) yf = 1 - yf;

if(calibMatrixUse) {
float xfold = xf;
/* Apply matrix transformation */
xf = xf * calibMatrix[0] + yf * calibMatrix[1] + calibMatrix[2];
yf = xfold * calibMatrix[3] + yf * calibMatrix[4] + calibMatrix[5];
}

fingerInfo->x = xf * screenWidth;
fingerInfo->y = yf * screenHeight;

if (fingerInfo->x < 0)
fingerInfo->x = 0;
if (fingerInfo->y < 0)
fingerInfo->y = 0;
if (fingerInfo->x > screenWidth)
fingerInfo->x = screenWidth;
if (fingerInfo->y > screenHeight)
fingerInfo->y = screenHeight;


}

/* Process the finger data gathered from the last set of events */
void processFingers() {
int i;
fingersDown = 0;
for(i = 0; i < 2; i++) {
if(fingerInfos[i].slotUsed) {
calibrate(&(fingerInfos[i]));
fingersDown++;
}
}

processFingerGesture(fingerInfos, fingersDown, fingersWereDown);

/* Save number of fingers to compare next time */
fingersWereDown = fingersDown;
}

/* Returns a pointer to the profile of the currently selected
* window, or defaultProfile if there is no specific profile for it or the window is invalid. */
char* getWindowClass(Window w) {
char * result = NULL;
if (w != None) {

XClassHint* classHint = XAllocClassHint();

/* Read WM_CLASS member */
if (XGetClassHint(display, w, classHint)) {

if(classHint->res_class != NULL) {

result = malloc((strlen(classHint->res_name) + 1) * sizeof(char));
if(result != NULL) {
strcpy(result, classHint->res_name);
}
XFree(classHint->res_class);

}

if(classHint->res_name != NULL) XFree(classHint->res_name);
XFree(classHint);
}
}

return result;

}


/* Returns whether the given window is blacklisted */
int isWindowBlacklisted(Window w) {
if(w == None) return 0;

return isWindowBlacklistedForGestures(w);
}

void setScreenSize(XRRScreenChangeNotifyEvent * evt) {
screenWidth = evt->width;
screenHeight = evt->height;
if(debugMode) {
printf("New screen size: %i x %i\n", screenWidth, screenHeight);
}
}



/* X thread that processes X events in parallel to kernel device loop */
void handleXEvent() {
XEvent ev;
XNextEvent(display, &ev);
//if(debugMode) printf("Handle event\n");
if (XGetEventData(display, &(ev.xcookie))) {
XGenericEventCookie *cookie = &(ev.xcookie);

// Touch events don't work right now
//if (cookie->evtype == XI_TouchBegin) {
// if(debugMode) printf("Touch begin\n");
//} else if (cookie->evtype == XI_TouchUpdate) {
// if(debugMode) printf("Touch update\n");
//} else if (cookie->evtype == XI_TouchEnd) {
// if(debugMode) printf("Touch end\n");
if (cookie->evtype == XI_Motion) {
if(debugMode) printf("Motion event\n");
//int r = XIAllowEvents(display, deviceID, XIReplayDevice, CurrentTime);
//printf("XIAllowEvents result: %i\n", r);
} else if (cookie->evtype == XI_PropertyEvent) {
/* Device properties changed -> recalibrate. */
if(debugMode) printf("Device properties changed.\n");
readCalibrationData(0);
} else if(cookie->evtype == XI_ButtonPress) {
if(debugMode) printf("Button Press\n");
//int r = XIAllowEvents(display, deviceID, XIReplayDevice, CurrentTime);
//printf("XIAllowEvents result: %i\n", r);
}


// In an ideal world, the following would work. But unfortunately the touch events
// delivered by evdev are often crap on the eGalax screen, with missing events when there
// should be some. So we still have to read directly from the input device, as bad as that is.
//if (cookie->evtype == XI_TouchBegin || cookie->evtype == XI_TouchUpdate || cookie->evtype == XI_TouchEnd) {
// XIDeviceEvent * devEvt = (XIDeviceEvent*) cookie->data;
// printf("Detail: %i\n", devEvt->detail);
// printf("Mask[0]: %i\n", (int) devEvt->valuators.mask[0]);
//
// /* Look for slot to put the data into by looking at the tracking ids */
// int index = -1;
// int i;
// for(i = 0; i < 2; i++) {
// if(fingerInfos[i].id == devEvt->detail) {
// index = i;
// break;
// }
// }
//
// /* No slot for this id found, look for free one */
// if(index == -1) {
// for(i = 0; i < 2; i++) {
// if(!fingerInfos[i].slotUsed) {
// /* "Empty" slot, so we can add it. */
// index = i;
// fingerInfos[i].id = devEvt->detail;
// break;
// }
// }
// }
//
// /* We have found a slot */
// if(index != -1) {
// fingerInfos[index].slotUsed = (cookie->evtype != XI_TouchEnd ? 1 : 0);
//
// i = 0;
// if((devEvt->valuators.mask[0] & 1) == 1)
// {
// fingerInfos[index].rawX = (int) devEvt->valuators.values[i++];
// }
// if((devEvt->valuators.mask[0] & 2) == 2)
// {
// fingerInfos[index].rawY = (int) devEvt->valuators.values[i++];
// }
// if((devEvt->valuators.mask[0] & 4) == 4)
// {
// fingerInfos[index].rawZ = (int) devEvt->valuators.values[i++];
// }
// }
//
// processFingers();
//}


XFreeEventData(display, &(ev.xcookie));

} else {
if(ev.type == 101) {
/* Why isn't this magic constant explained anywhere?? */
setScreenSize((XRRScreenChangeNotifyEvent *) &ev);
}
}
}

/* Reads the calibration data from evdev, should be self-explanatory. */
void readCalibrationData(int exitOnFail) {
if(debugMode) {
printf("Start calibration\n");
}
Atom retType;
int retFormat;
unsigned long retItems, retBytesAfter;
unsigned int* data;
if(XIGetProperty(display, deviceID, XInternAtom(display,
"Evdev Axis Calibration", 0), 0, 4 * 32, False, XA_INTEGER,
&retType, &retFormat, &retItems, &retBytesAfter,
(unsigned char**) &data) != Success) {
data = NULL;
}

if (data == NULL || retItems != 4 || data[0] == data[1] || data[2] == data[3]) {
/* evdev might not be ready yet after resume. Let's wait a second and try again. */
sleep(1);

if(XIGetProperty(display, deviceID, XInternAtom(display,
"Evdev Axis Calibration", 0), 0, 4 * 32, False, XA_INTEGER,
&retType, &retFormat, &retItems, &retBytesAfter,
(unsigned char**) &data) != Success) {
return;
}

if (retItems != 4 || data[0] == data[1] || data[2] == data[3]) {

if(debugMode) {
printf("No calibration data found, use default values.\n");
}

/* Get minimum/maximum of axes */

int nDev;
XIDeviceInfo * deviceInfo = XIQueryDevice(display, deviceID, &nDev);

int c;
for(c = 0; c < deviceInfo->num_classes; c++) {
if(deviceInfo->classes[c]->type == XIValuatorClass) {
XIValuatorClassInfo* valuatorInfo = (XIValuatorClassInfo *) deviceInfo->classes[c];
if(valuatorInfo->mode == XIModeAbsolute) {
if(valuatorInfo->label == XInternAtom(display, "Abs X", 0)
|| valuatorInfo->label == XInternAtom(display, "Abs MT Position X", 0))
{
calibMinX = valuatorInfo->min;
calibMaxX = valuatorInfo->max;
}
else if(valuatorInfo->label == XInternAtom(display, "Abs Y", 0) || valuatorInfo->label == XInternAtom(display, "Abs MT Position Y", 0))
{
calibMinY = valuatorInfo->min;
calibMaxY = valuatorInfo->max;
}
}
}
}

XIFreeDeviceInfo(deviceInfo);

} else {
calibMinX = data[0];
calibMaxX = data[1];
calibMinY = data[2];
calibMaxY = data[3];
}
} else {
calibMinX = data[0];
calibMaxX = data[1];
calibMinY = data[2];
calibMaxY = data[3];
}

if(data != NULL) {
XFree(data);
}

float * data4 = NULL;
if(XIGetProperty(display, deviceID, XInternAtom(display,
"Coordinate Transformation Matrix", 0), 0, 9 * 32, False, XInternAtom(display,
"FLOAT", 0),
&retType, &retFormat, &retItems, &retBytesAfter,
(unsigned char **) &data4) != Success) {
data4 = NULL;
}
calibMatrixUse = 0;
if(data4 != NULL && retItems == 9) {
int i;
for(i = 0; i < 6; i++) {
/* We only take the first two rows of the matrix, the rest is unimportant anyway */
calibMatrix[i] = data4[i];
}
calibMatrixUse = 1;
}
if(data4 != NULL) {
XFree(data4);
}



unsigned char* data2;

if(XIGetProperty(display, deviceID, XInternAtom(display,
"Evdev Axis Inversion", 0), 0, 2 * 8, False, XA_INTEGER, &retType,
&retFormat, &retItems, &retBytesAfter, (unsigned char**) &data2) != Success) {
return;
}

if (retItems != 2) {
if (exitOnFail) {
printf("No valid axis inversion data found.\n");
exit(1);
} else {
return;
}
}

calibSwapX = data2[0];
calibSwapY = data2[1];

XFree(data2);

if(XIGetProperty(display, deviceID,
XInternAtom(display, "Evdev Axes Swap", 0), 0, 8, False,
XA_INTEGER, &retType, &retFormat, &retItems, &retBytesAfter,
(unsigned char**) &data2) != Success) {
return;
}

if (retItems != 1) {
if (exitOnFail) {
printf("No valid axes swap data found.\n");
exit(1);
} else {
return;
}
}
calibSwapAxes = data2[0];

XFree(data2);

if(debugMode)
{
printf("Calibration: MinX: %i; MaxX: %i; MinY: %i; MaxY: %i\n", calibMinX, calibMaxX, calibMinY, calibMaxY);
}

}


/* Main function, contains kernel driver event loop */
int main(int argc, char **argv) {

char* devname = 0;
int doDaemonize = 1;
int doWait = 0;
int clickMode = 2;

int i;
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "--debug") == 0) {
doDaemonize = 0;
debugMode = 1;
} else if (strcmp(argv[i], "--wait") == 0) {
doWait = 1;
} else if (strcmp(argv[i], "--click=first") == 0) {
clickMode = 0;
} else if (strcmp(argv[i], "--click=second") == 0) {
clickMode = 1;
} else if (strcmp(argv[i], "--click=center") == 0) {
clickMode = 2;
} else {
devname = argv[i];
}

}

initGestures(clickMode);



if (doDaemonize) {
daemonize();
}

if (doWait) {
/* Wait until all necessary things are loaded */
sleep(10);
}


/* Connect to X server */
if ((display = XOpenDisplay(NULL)) == NULL) {
fprintf(stderr, "Couldn't connect to X server\n");
exit(1);
}

/* Read X data */
screenNum = DefaultScreen(display);

root = RootWindow(display, screenNum);

// realDisplayWidth = DisplayWidth(display, screenNum);
// realDisplayHeight = DisplayHeight(display, screenNum);

WM_CLASS = XInternAtom(display, "WM_CLASS", 0);

/* Get notified about new windows */
XSelectInput(display, root, StructureNotifyMask | SubstructureNotifyMask);

//TODO load blacklist and profiles from file(s)

/* Device file name */
if (devname == 0) {
devname = "/dev/twofingtouch";
}

/* Try to read from device file */
int fileDesc;
if ((fileDesc = open(devname, O_RDONLY)) < 0) {
perror("twofing");
return 1;
}

fd_set fileDescSet;
FD_ZERO(&fileDescSet);

int eventQueueDesc = XConnectionNumber(display);

while (1) {
/* Perform initialization at beginning and after module has been re-loaded */
int rd, i;
struct input_event ev[64];

char name[256] = "Unknown";

/* Read device name */
ioctl(fileDesc, EVIOCGNAME(sizeof(name)), name);
printf("Input device name: \"%s\"\n", name);

//TODO activate again?
//XSetErrorHandler(invalidWindowHandler);


int opcode, event, error;
if (!XQueryExtension(display, "RANDR", &opcode, &event,
&error)) {
printf("X RANDR extension not available.\n");
XCloseDisplay(display);
exit(1);
}

/* Which version of XRandR? We support 1.3 */
int major = 1, minor = 3;
if (!XRRQueryVersion(display, &major, &minor)) {
printf("XRandR version not available.\n");
XCloseDisplay(display);
exit(1);
} else if(!(major>1 || (major == 1 && minor >= 3))) {
printf("XRandR 1.3 not available. Server supports %d.%d\n", major, minor);
XCloseDisplay(display);
exit(1);
}

/* XInput Extension available? */
if (!XQueryExtension(display, "XInputExtension", &opcode, &event,
&error)) {
printf("X Input extension not available.\n");
XCloseDisplay(display);
exit(1);
}

/* Which version of XI2? We support 2.1 */
major = 2; minor = 1;
if (XIQueryVersion(display, &major, &minor) == BadRequest) {
printf("XI 2.1 not available. Server supports %d.%d\n", major, minor);
XCloseDisplay(display);
exit(1);
}

screenWidth = XDisplayWidth(display, screenNum);
screenHeight = XDisplayHeight(display, screenNum);

int n;
XIDeviceInfo *info = XIQueryDevice(display, XIAllDevices, &n);
if (!info) {
printf("No XInput devices available\n");
exit(1);
}

/* Go through input devices and look for that with the same name as the given device */
int devindex;
for (devindex = 0; devindex < n; devindex++) {
if (info[devindex].use == XIMasterPointer || info[devindex].use
== XIMasterKeyboard)
continue;

if (strcmp(info[devindex].name, name) == 0) {
deviceID = info[devindex].deviceid;

break;
}

}
if (deviceID == -1) {
printf("Input device not found in XInput device list.\n");
exit(1);
}

XIFreeDeviceInfo(info);

if(debugMode) printf("XInput device id is %i.\n", deviceID);

/* Prepare by reading calibration */
readCalibrationData(1);

/* Receive device property change events */
XIEventMask device_mask2;
unsigned char mask_data2[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
device_mask2.deviceid = deviceID;
device_mask2.mask_len = sizeof(mask_data2);
device_mask2.mask = mask_data2;
XISetMask(device_mask2.mask, XI_PropertyEvent);
XISetMask(device_mask2.mask, XI_ButtonPress);
//XISetMask(device_mask2.mask, XI_TouchBegin);
//XISetMask(device_mask2.mask, XI_TouchUpdate);
//XISetMask(device_mask2.mask, XI_TouchEnd);
XISelectEvents(display, root, &device_mask2, 1);

/* Recieve events when screen size changes */
XRRSelectInput(display, root, RRScreenChangeNotifyMask);


/* Receive touch events */



/* Needed for XTest to work correctly */
XTestGrabControl(display, True);


/* Needed for some reason to receive events */
/* XGrabPointer(display, root, False, 0, GrabModeAsync, GrabModeAsync,
None, None, CurrentTime);
XUngrabPointer(display, CurrentTime);*/

grab(display, deviceID);

printf("Reading input from device ... (interrupt to exit)\n");

/* We perform raw event reading here as X touch events don't seem too reliable */
int currentSlot = 0;

/* If we use the legacy protocol, we collect all data of one finger into tempFingerInfo and set
it to the correct slot once MT_SYNC occurs. */
FingerInfo tempFingerInfo = { .rawX=0, .rawY=0, .rawZ=0, .id = -1, .slotUsed = 0, .setThisTime = 0 };

while (1) {


FD_SET(fileDesc, &fileDescSet);
FD_SET(eventQueueDesc, &fileDescSet);

select(MAX(fileDesc, eventQueueDesc) + 1, &fileDescSet, NULL, NULL, getEasingStepTimeVal());

checkEasingStep();

if(FD_ISSET(fileDesc, &fileDescSet))
{


rd = read(fileDesc, ev, sizeof(struct input_event) * 64);
if (rd < (int) sizeof(struct input_event)) {
printf("Data stream stopped\n");
break;
}
for (i = 0; i < rd / sizeof(struct input_event); i++) {

if (ev[i].type == EV_SYN) {
if (0 == ev[i].code) { // Ev_Sync event end
/* All finger data received, so process now. */

if(useLegacyProtocol) {
/* Clear slots not set this time */
int i;
for(i = 0; i < 2; i++) {
if(fingerInfos[i].setThisTime) {
fingerInfos[i].setThisTime = 0;
} else {
/* Clear slot */
fingerInfos[i].slotUsed = 0;
}
}
tempFingerInfo.slotUsed = 0;
}

processFingers();

} else if (2 == ev[i].code) { // MT_Sync : Multitouch event end

if(!useLegacyProtocol) {

/* This messsage indicates we use legacy protocol, so switch */
useLegacyProtocol = 1;
currentSlot = -1;
tempFingerInfo.slotUsed = 0;
if(debugMode) printf("Switch to legacy protocol.\n");
} else {
if(tempFingerInfo.slotUsed) {
/* Finger info for one finger collected in tempFingerInfo, so save it to fingerInfos. */

/* Look for slot to put the data into by looking at the tracking ids */
int index = -1;
int i;
for(i = 0; i < 2; i++) {
if(fingerInfos[i].slotUsed && fingerInfos[i].id == tempFingerInfo.id) {
index = i;
break;
}
}

if(index == -1) {
for(i = 0; i < 2; i++) {
if(!fingerInfos[i].slotUsed) {
/* "Empty" slot, so we can add it. */
index = i;
fingerInfos[i].id = tempFingerInfo.id;
fingerInfos[i].slotUsed = 1;
break;
}
}
}

if(index != -1) {
/* Copy temporary data to slot */
fingerInfos[index].setThisTime = 1;
fingerInfos[index].rawX = tempFingerInfo.rawX;
fingerInfos[index].rawY = tempFingerInfo.rawY;
fingerInfos[index].rawZ = tempFingerInfo.rawZ;
}
}
}
}

} else if (ev[i].type == EV_MSC && (ev[i].code == MSC_RAW
|| ev[i].code == MSC_SCAN)) {
} else if (ev[i].code == 47) {
currentSlot = ev[i].value;
if(currentSlot < 0 || currentSlot > 1) currentSlot = -1;
} else {
/* Set finger info values for current finger */
if (ev[i].code == 57) {
/* ABS_MT_TRACKING_ID */
if(currentSlot != -1) {
if(ev[i].value == -1)
{
fingerInfos[currentSlot].slotUsed = 0;
}
else
{
fingerInfos[currentSlot].id = ev[i].value;
fingerInfos[currentSlot].slotUsed = 1;
}
} else if(useLegacyProtocol) {
tempFingerInfo.id = ev[i].value;
tempFingerInfo.slotUsed = 1;
}
};
if (ev[i].code == 53) {
if(currentSlot != -1) {
fingerInfos[currentSlot].rawX = ev[i].value;
} else if(useLegacyProtocol) {
tempFingerInfo.rawX = ev[i].value;
}
};
if (ev[i].code == 54) {
if(currentSlot != -1) {
fingerInfos[currentSlot].rawY = ev[i].value;
} else if(useLegacyProtocol) {
tempFingerInfo.rawY = ev[i].value;
}
};
if (ev[i].code == 58) {
if(currentSlot != -1) {
fingerInfos[currentSlot].rawZ = ev[i].value;
} else if(useLegacyProtocol) {
tempFingerInfo.rawZ = ev[i].value;
}
};
}
}

}

if(FD_ISSET(eventQueueDesc, &fileDescSet)) {
handleXEvent();
}
}

/* Stream stopped, probably because module has been unloaded */
close(fileDesc);

/* Clean up */
releaseButton();
ungrab(display, deviceID);

/* Wait until device file is there again */
while ((fileDesc = open(devname, O_RDONLY)) < 0) {
sleep(1);
}

}

}

twofing-0.0.9/twofingemu.h000064400000000000000000000055661173133520700155460ustar00rootroot00000000000000/*
Copyright (C) 2010, 2011 Philipp Merkel <linux@philmerk.de>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/

#ifndef TWOFINGEMU_H_
#define TWOFINGEMU_H_

#define MAX(a, b) (a > b ? a : b)

typedef struct FingerInfo FingerInfo;
typedef struct Action Action;
typedef struct Profile Profile;

struct FingerInfo {
int x;
int y;
int rawX;
int rawY;
int rawZ;
int slotUsed;
int setThisTime; /* For legacy protocol */
int id; /* Tracking ID */
};

struct Action {
int actionType;
int keyButton;
int modifier;
};

void startEasingThread();
void stopEasingThread();

#define MODIFIER_SHIFT 1
#define MODIFIER_CONTROL 2
#define MODIFIER_ALT 4
#define MODIFIER_SUPER 8

struct Profile {
char* windowClass;

int scrollInherit;
Action scrollDownAction;
Action scrollUpAction;
Action scrollLeftAction;
Action scrollRightAction;
Action scrollBraceAction;
int hscrollStep;
int vscrollStep;
int scrollMinDistance;
int scrollEasing;

int zoomInherit;
Action zoomInAction;
Action zoomOutAction;
double zoomStep;
int zoomMinDistance;
double zoomMinFactor;

int rotateInherit;
Action rotateLeftAction;
Action rotateRightAction;
double rotateStep;
int rotateMinDistance;
double rotateMinAngle;

int tapInherit;
Action tapAction;
};

#define ACTIONTYPE_NONE 0
#define ACTIONTYPE_KEYPRESS 1
#define ACTIONTYPE_BUTTONPRESS 2

#define EXECUTEACTION_PRESS 1
#define EXECUTEACTION_RELEASE 2
#define EXECUTEACTION_BOTH 3

#define GESTURE_NONE 0
#define GESTURE_UNDECIDED 1
#define GESTURE_SCROLL 2
#define GESTURE_ZOOM 3
#define GESTURE_ROTATE 4

int inDebugMode();

void *xLoopThreadFunction(void *arg);
int isWindowBlacklisted(Window w);

Window getCurrentWindow();
char* getWindowClass(Window);
Window getLastChildWindow(Window);

Window getActiveWindow();

void processFingers();

void pressButton();
void releaseButton();
int isButtonDown();

void movePointer(int, int, int);
void executeAction(Action* action, int what);

void ungrab(Display *display,int deviceid);
void grab(Display *display,int deviceid);

int invalidWindowHandler(Display *dsp,XErrorEvent *err);

void readCalibrationData();
void startContinuation();

typedef struct timeval TimeVal;

TimeVal getCurrentTime();

int timeDiff(TimeVal start, TimeVal end);

#endif /* TWOFINGEMU_H_ */
 
дизайн и разработка: Vladimir Lettiev aka crux © 2004-2005, Andrew Avramenko aka liks © 2007-2008
текущий майнтейнер: Michael Shigorin