/* HideMouse was written by and is Copyright (C) Richard A. Rost  March 1,2020.
 * 
 * This program allows you to hide the mouse cursor and to move the
 * cursor out of the way to one of the corners of the screen.
 * 
 * My Copyright terms are simple. You may copy, modify, and use this
 * program as you see fit provided you meet the following terms:
 * 
 * 1. You leave my name as the author and do not take credit for my work.
 * 2. While I hope this program will be useful it is offered WITHOUT ANY
 *    WARRANTY and you agree not to hold me liable for any damages.
 * 3. You leave this header in it's entirety at the top of all source
 *    files for this program and any forks. You may append notes, list
 *    changes, and add the authors of any changes to the end of this header.
 * 4. You do not collect any fee for this program as is or modified.
 * 5. You do not collect any fee for any changes you make to this program.
 * 6. You do not include or package it with any software for which any fee
 *    is collected.
 * 7. You do not include it on media (CD, DVD, etc.) which is sold or any
 *    fee is collected for the cost of the media, shipping, handling, etc.
 * 8. Items 4, 5, 6, and 7 apply to the source code as well as the executable.
 * 9. If you modify this program, you must make the full source code with a
 *    functioning compile script available in one of the following ways.
 *   A. Packaged with the executable when you distribute it.
 *   B. As a separate package available from where you distribute the
 *      executable. If distributed on a CD a web link to the source package
 *      is acceptable.
 * 
 * 
 * This program was written using the Geany 1.23 fast and lightweight IDE.
 * Tabs are set to 4.
 * -----------------------End of original header----------------------------
 */

#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>		// uint8_t and friends.
#include <unistd.h>		// sleep usleep
#include <errno.h>
#include <string.h>		// memset strerror
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>


#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/Xfixes.h>

#define MAX_BUF 64
#define COPYRIGHT "\nCopyright Richard A. Rost March 1, 2020\n"
#define VERSION " version 0.1  "

static void Bail(int linenumber);
static void GetScreenParams(char *DisplayName);


Window rootwindow;		// The desktop window ID
int rootwidth;			// The desktop width in pixels
int rootheight;			// The desktop height in pixels
int screen;
Display *display;		//


/******************************************************************/
/* Print an error message and the line number of the source file
 * that triggered it. */
static void Bail(int linenumber)
{
	printf("Error line: %d\n%s\n", linenumber, strerror(errno));
	exit(1);
}
/******************************************************************/

/******************************************************************/
/* Fetch some common parameters required by X11 functions */
static void GetScreenParams(char *DisplayName)
{
	if((display=XOpenDisplay(DisplayName)))
	{
		screen=DefaultScreen(display);
		rootwindow=RootWindow(display,screen);		// XID of the root window
		rootwidth=DisplayWidth(display,screen);		// Screen width in pixels
		rootheight=DisplayHeight(display,screen);	// Screen height in pixels
		return;
	}
	Bail(__LINE__);
}
/******************************************************************/





/******************************************************************/
int main(int argc, char *argv[])
{
	char	readbuffer[MAX_BUF];		// Data read from the FIFO gets stored here.
	char	inputfifo[MAX_BUF]="/tmp/";	// Path and filename of the FIFO.
	char	*program_name=argv[0];		// Pointer to the path/name of this program.
	char	*arg0;						// Pointer used to parse argv[0] for the name of this program.
	int		fifo_fd;					// FIFO file descriptor.
	int		bytes_read;					// Number of bytes read from the FIFO.
	int		quit=0;						// Flag used to break us out of the main loop.
	int		cursor_hidden=0;			// Flag to keep track if the cursor is currently visible or not.
	
	// Set program_name to point to the programs name with path stripped away.
	for(arg0=argv[0]; *arg0; arg0++)
	{
		if(*arg0 == '/')
		{
			program_name=arg0 + 1;
		}
	}

	strcat(inputfifo, program_name);


	if(argc > 1)
	{
		printf("\n%s%s%s %s%s"
				"%s is for hiding/moving the mouse cursor.\n\n"
				"First run:  %s &\n\n"
				"Then:  echo X > %s\n\n"
				"Where X is:\n"
				"\th=Hide cursor  s=Show cursor  q=Quit\n"
				"\t1=Top Left  2=Top Right  3=Bottom Left  4=Bottom Right\n\n"
				, program_name, VERSION, __DATE__, __TIME__, COPYRIGHT
				, program_name, program_name, inputfifo);
		return(0);
	}

	GetScreenParams(NULL);
	
	// Create the FIFO and open it for reading.
	if(mkfifo(inputfifo, 0666) == -1)
		Bail(__LINE__);
	if((fifo_fd=open(inputfifo, O_RDONLY)) == -1)
		Bail(__LINE__);
	
	while(! quit)
	{
		bytes_read=read(fifo_fd, readbuffer, MAX_BUF - 1);
		if(bytes_read <= 0) continue;
		readbuffer[bytes_read]='\0';
		bytes_read=0;
		while(readbuffer[bytes_read])
		{
			switch(readbuffer[bytes_read])
			{
				case 's':	// Show
				if(cursor_hidden)
				{
					XFixesShowCursor(display, rootwindow);
					cursor_hidden=0;
				}
				break;

				case 'h':	// Hide
				if(! cursor_hidden)
				{
					XFixesHideCursor(display, rootwindow);
					cursor_hidden=1;
				}
				break;

				case '1':	// Move mouse cursor to quadrant 1 (Top Left).
				XWarpPointer(display, None, rootwindow, 0, 0, 0, 0, 0, 0);
				break;

				case '2':	// Move mouse cursor to quadrant 2 (Top Right).
				XWarpPointer(display, None, rootwindow, 0, 0, 0, 0, rootwidth, 0);
				break;

				case '3':	// Move mouse cursor to quadrant 3 (Bottom Left).
				XWarpPointer(display, None, rootwindow, 0, 0, 0, 0, 0, rootheight);
				break;

				case '4':	// Move mouse cursor to quadrant 4 (Bottom Right).
				XWarpPointer(display, None, rootwindow, 0, 0, 0, 0, rootwidth, rootheight);
				break;

				case 'q':	// Quit program. This causes the cursor to become visible again.
				quit=1;
				break;

				default:
				break;
			}
			XFlush(display);
			bytes_read++;
		}
	}
	close(fifo_fd);
	XCloseDisplay(display);
    unlink(inputfifo);
	return(0);
}
/******************************************************************/
