/*************************************************************************
 *
 * mouse.c - Mouse module of framebuffer support.
 *
 * $Id$
 *
 * Copyright 2004-2013 Citrix Systems, Inc.  All Rights Reserved.
 *
 *************************************************************************/
#include "citrix.h"

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

#include "mainloop.h"
#include "simplified.h"
#include "fb_plugin.h"

/* Mouse button status and position variables */

static int mousefd;     /* Mouse device file descriptor. */
static int Xpos, Ypos;  /* Current pointer position. */

static struct button_info {
    int         input_flag, down_indicator, up_indicator;
    BOOLEAN     is_down;
} buttons[3] = {{1, MOU_STATUS_B1DOWN, MOU_STATUS_B1UP},
                {2, MOU_STATUS_B2DOWN, MOU_STATUS_B2UP},
                {4, MOU_STATUS_B3DOWN, MOU_STATUS_B3UP}};

/* X and Y values are 9-bit signed, with the top bit detached.
 * This value is for reassembly.
 */

#define NEGATIVE (-1 ^ 0xff)

static void get_mouse_movement(void *dummy1, void *dummy2)
{
    int                 i, flags, Xmovement, Ymovement;
    unsigned char       packet[4];

    /* Descriptor mousefd refers to /dev/input/mice which is a PS/2 device 
     * that yields three data byte data bytes to represent mouse movement.
     */ 

    if (read(mousefd, (char *)packet, 3) != 3)
        return;

    flags = 0;
    Xmovement = (packet[0] & 0x10) ? NEGATIVE : 0;
    Xmovement |= packet[1];
    if (Xmovement)
        flags |= MOU_STATUS_MOVED;
    Xpos += Xmovement;
    if (Xpos < 0)
        Xpos = 0;

    Ymovement = (packet[0] & 0x20) ? NEGATIVE : 0;
    Ymovement |= packet[2];
    if (Ymovement)
        flags |= MOU_STATUS_MOVED;
    Ypos -= Ymovement;                  /* Y axis is the other way! */
    if (Ypos < 0)
        Ypos = 0;
 
    /* Look for changes in buttons. */

    for (i = 0; i < ELEMENTS_IN_ARRAY(buttons); ++i) {
        struct button_info     *ip = buttons + i;

        if ((packet[0] & ip->input_flag) && !ip->is_down) {
            ip->is_down = TRUE;
            flags |= ip->down_indicator;
        } else if (!(packet[0] & ip->input_flag) && ip->is_down) {
            ip->is_down = FALSE;
            flags |= ip->up_indicator;
        }
    }

    if (!flags)          /* Nothing to report. */
        return;
    Mouse_changed(&Xpos, &Ypos, flags); /* Bounds position to session. */
}

/* Initialisation code. */

int mouse_init(void)
{
    static struct pal_event mouse_input = {{get_mouse_movement, NULL}, 0};

    mousefd = open("/dev/input/mice", O_RDONLY);
    if (mousefd >= 0) {
        /* Ask the event loop for callbacks when data available. */

        (*plug_ptr->select_fd)(&mouse_input, mousefd, FALSE);
    } else {
        perror("Open /dev/input/mice");
    }
    return mousefd;
}
