KlikiMouse3.ino (Source)

/*
	Klikimouse3 aneb objects forever!
	3 tlacitka a LED+330 Ohm odpor proti zemi
	Macros for Mouse and Keyboard (ArduinoMicro)
	
	Based on:
	
	ButtonMouseControl
	For Leonardo and Due boards only.
	http://www.arduino.cc/en/Tutorial/ButtonMouseControl
	https://www.arduino.cc/en/Tutorial/KeyboardAndMouseControl
*/


#include <Keyboard.h>
#include <Mouse.h>


// set pin numbers for the buttons and led:
const int button_B = 4;
const int button_C = 3;
const int button_A = 2;
const int led = 8;

#define UNUSED(x) (void)(x)
#define FAST_MOUSE 25

class Base{	// {{{
	public:
		virtual void start(){ stop_me=false;};                          // initialise class for next round of cycles
		virtual void stop(){ stop_me=true;};                            // prepare class for finishing as soon as possible
		virtual unsigned long long pause(unsigned long x){ UNUSED(x); return 1;};       // pause ms before next cycle, 0..not changed; x=current internal state (nonzero)
		virtual unsigned long next(unsigned long x){ UNUSED(x); return 0;};        // next internal state; x=current internal state (nonzero)
	protected:
		bool stop_me;   // true = finish as soon as possible
};	// }}}
class Blink : public Base {	// {{{
	public:
		virtual void start(){ stop_me=false; count=0;};
		virtual void stop(){ stop_me=true; count=0;};
		virtual unsigned long long pause(unsigned long x){ UNUSED(x); return 100;};
		virtual unsigned long next(unsigned long x){ 	// {{{
			if (x == 1) return 2;
			if (stop_me) x=0; else if (!count) x=0; else {--count; x=1;};
			return x;
		};	// }}}
		void set_count(byte x){ if (x) count=x-1; else count=0;};
	private:
		byte count;
};	// }}}
class Recast : public Base {	// {{{
	public:
		virtual void start(){ stop_me=false; count=10;};
		virtual void stop(){ stop_me=true; count=0;};
		virtual unsigned long long pause(unsigned long x){ UNUSED(x); return 70;};
		virtual unsigned long next(unsigned long x){ 	// {{{
			switch (x) {
				case 1: ++x;Keyboard.press('c');break;
				case 2: ++x;Keyboard.release('c');break;
				case 3: ++x;Mouse.press(MOUSE_LEFT);break;
				case 4: 
					if (stop_me) x=0; else if (!count) x=0; else {--count; x=1;}
					Mouse.release(MOUSE_LEFT);
					break;
				default: x=0;break;
				}
			return x;
		};	// }}}
	private:
		byte count;
};	// }}}
class ClickYes : public Base {	// {{{
	public:
		virtual void start(){ stop_me=false; count=40;};
		virtual void stop(){ stop_me=true; count=0;};
		virtual unsigned long long pause(unsigned long x){ UNUSED(x); return FAST_MOUSE;};
		virtual unsigned long next(unsigned long x){ 	// {{{
			switch (x) {
				case 1: ++x;Mouse.press(MOUSE_LEFT);break;
				case 2: ++x;Mouse.release(MOUSE_LEFT);break;
				case 3: ++x;Keyboard.press('y');break;
				case 4: 
					if (stop_me) x=0; else if (!count) x=0; else {--count; x=1;}
					Keyboard.release('y');
					break;
				default: x=0;break;
				}
			return x;
		};	// }}}
	private:
		byte count;
};	// }}}
class Relearn : public Base {	// {{{ Rest / Loiter / 3 hours / Enter } x 3 + Center / click / Training
	public:
		virtual unsigned long long pause(unsigned long x){ // {{{
			unsigned long long p;
			switch (x) {
				case 15: case 115: case 215: p=6000;break;	// 6sec = 3 hours countdown
				case  1: case 101: case 201: 
				case  5: case 105: case 205:
				case  9: case 109: case 209:
					 case 221: case 223: 
								p=60;break;	// time to actualise mouse
				case  7: case 107: case 207:
				case 18: case 118: case 218:
				case 219: p=200;break;	// time to actualise mouse - Trainer screen is slow
				default: p=FAST_MOUSE;break;
			};
			return p;
		};	// }}}
		virtual unsigned long next(unsigned long x){ 	// {{{
			switch (x) {
				case  1: case 101: case 201: 
				case  2: case 102: case 202: 
				case  3: case 103: case 203: 
				case  4: case 104: case 204: 
					++x;Mouse.move(127,127,0);break;
				case  5: case 105: case 205: 
					++x;Mouse.move(-60,-20,0);break;
				case  6: case 106: case 206: ++x;Mouse.press(MOUSE_LEFT);break;		//  Mouse.click() is too fast, need press/release (Rest)
				case  7: case 107: case 207: ++x;Mouse.release(MOUSE_LEFT);break;
				case  8: case 108: case 208: ++x;Mouse.move(-20,-120,0);break;		// Mouse is accelerated over long fast moves (To Loiter)
				case  9: case 109: case 209: ++x;Mouse.move(0,-60,0);break;
				case 10: case 110: case 210: ++x;Mouse.press(MOUSE_LEFT);break;
				case 11: case 111: case 211: ++x;Mouse.release(MOUSE_LEFT);break;
				case 12: case 112: case 212: ++x;Keyboard.press('3');break;		// 3 hours
				case 13: case 113: case 213: ++x;Keyboard.release('3');break;
				case 14: case 114: case 214: ++x;Keyboard.press(KEY_RETURN);break;
				case 15: case 115: case 215: ++x;Keyboard.release(KEY_RETURN);break;	// wait here
				// waiting for 3 hour countdown
				case 16: case 116: case 216: ++x;Keyboard.press(KEY_RETURN);break;
				case 17: case 117: case 217: ++x;Keyboard.release(KEY_RETURN);break;
				// x3
				case 18: case 118: case 218: ++x;break;
											// 218->219 to Trainer; else back and again
				case 19: case 119: ++x;Mouse.move(20,120,0);break;	// Mouse is accelerated over long fast moves (Back To Loiter)
				case 20: case 120: ++x;Mouse.move(0,120,0);break;
				case 21: case 121: x += (101-21); break;		// loop: 23->101, 123->201
				// To Trainer
				case 219: ++x;Mouse.move(-80,-30,0);break;
				// click Trainer
				case 220: ++x;Mouse.press(MOUSE_LEFT);break;
				case 221: ++x;Mouse.release(MOUSE_LEFT);break;
				// Train?
				case 222: ++x;Mouse.press(MOUSE_LEFT);break;
				case 223: ++x;Mouse.release(MOUSE_LEFT);break;
				// yes
				case 224: ++x;Keyboard.press('y');break;
				case 225: ++x;Keyboard.release('y');break;
				//
				default: x=0;break;
				}
			return x;
		};	// }}}
};	// }}}
class Skills : public Base {	// {{{ Open minor skills tab
	public:
		virtual unsigned long long pause(unsigned long x){ UNUSED(x); return FAST_MOUSE;};
		virtual unsigned long next(unsigned long x){ 	// {{{
			switch (x) {
				case 1: 
				case 2: 
				case 3: 
				case 4: 
					++x;Mouse.move(-127,127,0);break;
				case 5: 
					++x;Mouse.move(20,-20,0);break;
				case 6: ++x;break;
				case 7: ++x;Mouse.press(MOUSE_LEFT);break;	//  Mouse.click() is too fast, need press/release
				case 8: ++x;Mouse.release(MOUSE_LEFT);break;
				case 9: ++x;break;
				case 10: 
					++x;Mouse.move(40,-75,0);break;		// Mouse is accelerated over long fast moves
				case 11: ++x;Mouse.press(MOUSE_LEFT);break;
				case 12: ++x;Mouse.release(MOUSE_LEFT);break;
				default: x=0;break;
				};
			return x;
		};	// }}}

};	// }}}
class Swing : public Base {	// {{{
	public:
		virtual unsigned long long pause(unsigned long x){ UNUSED(x); return FAST_MOUSE;};
		virtual unsigned long next(unsigned long x){ 	// {{{
			switch (x) {
				case 1: 
				case 2: 
				case 3: 
				case 4: 
					++x;Mouse.move(-127,127,0);break;
				case 5: 
					++x;Mouse.move(127,-120,0);break;
				case 6: ++x;break;
				case 7: ++x;Mouse.press(MOUSE_RIGHT);break;	//  Mouse.click() is too fast, need press/release
				case 8: ++x;Mouse.move(0,-120,0);break;
				case 9: ++x;break;
				case 10: ++x;Mouse.release(MOUSE_RIGHT);break;
				case 11: ++x;break;
				case 12: if (stop_me) x=0; else x=3;break;	// infinite loop
				default: x=0;break;
				};
			return x;
		};	// }}}

};	// }}}

#define DEFAULT_DELAY 25
#define DEFAULT_DEBOUNCE 100

unsigned long state = 0;		// state of state automat - 0 = inactive; 1 = start state
Base *automat = NULL;
Base *next_automat = NULL;
unsigned long c_state = 0;
Blink *blink=new Blink;
// {{{ Automats
#define AUTOMATS 3
struct T_automat {Base *A; Base *B;};
T_automat automats[AUTOMATS] ={
	{new Recast, new ClickYes},
	{new Relearn, new Skills},
	{new Recast, new Swing}
};
//}}}
unsigned long long responseDelay = DEFAULT_DELAY;	// repepeat delay between states
unsigned long long debounceDelay = DEFAULT_DEBOUNCE;	// debounce delay of the button, in ms
bool somePressed=true;	// pressed any button => digitalRead()==0
bool debouncing=false;
bool led_shines=false;

unsigned long milsDebounce;
unsigned long milsState;
unsigned long currentMillis;
void setup() {	// {{{
	// initialize the buttons' inputs:
	pinMode(button_B, INPUT_PULLUP);
	pinMode(button_C, INPUT_PULLUP);
	pinMode(button_A, INPUT_PULLUP);
	pinMode(led, OUTPUT);
	// initialize mouse control:
	Mouse.begin();
	Keyboard.begin();
	milsDebounce=millis();
	milsState=millis();
}	// }}}

void loop() {
	currentMillis=millis();		// {{{ prolog
	bool act_B= ! digitalRead(button_B);
	bool act_C= ! digitalRead(button_C);
	bool act_A= ! digitalRead(button_A);
	
	if (debouncing and ((currentMillis-milsDebounce) >= debounceDelay)) {
		debouncing = false;
	};
	// }}}
	if (! debouncing) {	// {{{ check for change
		if ( (act_B || act_C || act_A) != somePressed){	// {{{ somePressed changed !
			// {{{ start debouncing
			milsDebounce=currentMillis;
			somePressed=(act_B || act_C || act_A);
			debouncing=true;
			// }}}
			if (act_C) {	// act_C if possible
				next_automat = NULL;
				if (automat) automat->stop();
				else {
					if (++c_state >=AUTOMATS) c_state=0;
					automat=blink;
					automat->start();
					responseDelay = automat->pause(state);
					blink->set_count(c_state+1);
					state = 1;
					led_shines = false;
				};
			};
			if (act_B) {	// act_B if possible
				if (automat) automat->stop();
				next_automat = automats[c_state].B;
			};
			if (act_A) {	// act_A high priority
				if (automat) automat->stop();
				next_automat = automats[c_state].A;
			};
		};	// }}}
	};	// }}} ! debouncing
	
	if ((currentMillis-milsState) >= responseDelay){	// {{{ next state
		milsState = currentMillis;	// next step
		if (state == 0) {
			if (automat) automat->stop();
			automat = NULL;
			responseDelay = DEFAULT_DELAY;
			if (next_automat) {
				state = 1;
				automat = next_automat;
				next_automat = NULL;
				automat->start();
				responseDelay = automat->pause(state);
				led_shines = false;
			};
		};
		if (state){
			responseDelay = automat->pause(state);
			state = automat->next(state);
			led_shines = !led_shines;
		} else {
			led_shines = false;
		};
		digitalWrite(led,led_shines);
	};	// }}}
}