/*************************************************************************
 *
 * ttyinit.c - Terminal initialization module of FB test program.
 *
 * $Id$
 *
 * Copyright 2004-2015 Citrix Systems, Inc.  All Rights Reserved.
 *
 *************************************************************************/

#include "citrix.h"

#include <errno.h>
#include <linux/vt.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>

#include <linux/kd.h>
#include <termios.h>

#include "fb_plugin.h"
#define TERMINAL_DEVICE "/dev/tty0"

int new_tty_file_descriptor = -1;       /* The virtual terminal for graphics */

static int old_tty_number;              /* Number of initially active VT. */
static int tty_file_descriptor;         /* /dev/tty0, used for VT control */

static void fail(char *msg)
{
    perror(msg);
    exit(1);
}

/* Check is a file desriptor (stdout or stdin) refers to the same
 * virtual terminal as the device in the buffer *sbp.
 * If so close it and substitute the null device.
 * This will prevent text output to the terminal form corrupting
 * the graphical session image.
 */

static void check_fd(int fd, struct stat *sbp)
{
    struct stat     our_sb;
    int             new_fd;

    if (fstat(fd, &our_sb) < 0)
        return;
    if (S_ISCHR(our_sb.st_mode) && our_sb.st_rdev == sbp->st_rdev) {
        /* This file descriptor refers to the same VT as stdin. */

        close(fd);
        new_fd = open("/dev/null", O_RDWR);
        if (new_fd >= 0 && new_fd != fd) {
            dup2(new_fd, fd);
            close(new_fd);
        }
    }
}

void ttyinit(void)
{
	char            vtname[30];
        struct vt_stat  terminal_status;
        struct stat     stat_buf;
        int             new_tty_number;

        /* Are we already running on a console virtual terminal? */

        if (ioctl(0, VT_GETSTATE, &terminal_status) == 0) {
            new_tty_file_descriptor = 0;
            if (fstat(0, &stat_buf) == 0) {
                /* Close stdout and stderr if they are the same VT
                 * to prevent text mode spoiling our graphic output.
                 */

                check_fd(1, &stat_buf);
                check_fd(2, &stat_buf);
            }
        } else {
            /* Get a free VT and use that - requires root. */

            tty_file_descriptor = open(TERMINAL_DEVICE, O_RDWR);

            if (tty_file_descriptor == -1) 
                fail(TERMINAL_DEVICE);
	
            if (ioctl(tty_file_descriptor, VT_GETSTATE, &terminal_status) < 0) 
                fail("Ioctl VT_GETSTATE failed");
	
            /* Get old tty number. */

            old_tty_number = terminal_status.v_active;
	
            /* Get new tty number. */

            if (ioctl(tty_file_descriptor, VT_OPENQRY, &new_tty_number) < 0)
                fail("Ioctl VT_OPENQRY failed");

            /* Without this, keyboard input does not work. Not sure why. */ 

            ioctl(tty_file_descriptor, KDSETMODE, KD_TEXT );

            /* Open new tty device. */

            sprintf(vtname, "/dev/tty%d", new_tty_number);
            new_tty_file_descriptor = open(vtname, O_RDWR);
            if (new_tty_file_descriptor < 0)
                fail(vtname);

            /* Change to new tty. */

            if (ioctl(new_tty_file_descriptor,
                      VT_ACTIVATE, new_tty_number) < 0)
                fail("Ioctl VT_ACTIVATE failed");
        }
}	

void CloseGraphics(void) {
    /* Put the terminal back into text mode. */

    if (new_tty_file_descriptor >= 0) {
        ioctl(new_tty_file_descriptor, KDSETMODE, KD_TEXT);

	/* Put the keyboard back to its original state. */
	keybd_fini();
    }

    if (tty_file_descriptor > 0 &&
        ioctl(tty_file_descriptor, VT_ACTIVATE, old_tty_number) < 0) {
        fail("Failed to revert terminal using Ioctl VT_ACTIVATE");
    }
}
