/* -*-  tab-width:4; c-basic-offset:4  -*- 
 * rotate.c -- determine Freerunner orientation.
 * Authors  -- © 2008 Chris Ball <cjb@laptop.org>
 *             © 2008 Rui Miguel Silva Seabra <rms@1407.org>
 *
 * This file is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 */

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>

#define EVENT_PATH "/dev/input/event3"
#define ORIENTATION_NORMAL    0
#define ORIENTATION_LEFT      1
#define ORIENTATION_RIGHT     2
#define ORIENTATION_INVERTED  3

static Display  *display;
static Window   rootWindow;

void set_rotation (int rotation) {
    Rotation r_to,r;
    int screen = -1;
    int do_rotate=1;
    XRRScreenConfiguration * config;
    int current_size=0;

    printf("In set_rotation, rotation = %d\n", rotation);
    switch (rotation)
    {
    case 0:
        r_to=RR_Rotate_0;
        break;
    case 1:
        r_to=RR_Rotate_90;
        break;
    case 2:
        r_to=RR_Rotate_270;
        break;
    case 3:
        r_to=RR_Rotate_180;
        break;
    default:
        do_rotate=0;
        break;
    }
    if(do_rotate) {
        display = XOpenDisplay(":0");
        if (display == NULL) {
            fprintf (stderr, "Can't open display %s\n", XDisplayName(":0"));
            exit (1);
        }

        screen = DefaultScreen(display);
        rootWindow = RootWindow(display, screen);
        XRRRotations(display, screen, &r);
        config = XRRGetScreenInfo(display, rootWindow);
        current_size = XRRConfigCurrentConfiguration (config, &r);
        XRRSetScreenConfig(display, config, rootWindow, current_size, r_to, CurrentTime);
 
        /* Sleep for two seconds to give randr a chance to catch up. */
        sleep(2);
    }
}

int process_packet (FILE *eventfp) {
    int state;
    int curline;
    unsigned short buffer[8 * 7];
    int i;
    unsigned short x;
    unsigned short y;
    unsigned short z;

    /* The raw data looks like:
     *  
     * portrait
     * 2ca3 48ab 43bf 000d 0000 0000 0000 0000 # start of packet
     * 2ca3 48ab 687b 000d 0002 0000 005a 0000 # X
     * 2ca3 48ab 68a8 000d 0002 0001 fc34 ffff # Y
     * 2ca3 48ab 68c1 000d 0002 0002 0048 0000 # Z
     * 2ca3 48ab 68c6 000d 0000 0000 0000 0000 # start of packet
     *
     * landscape
     * 2ca5 48ab 0f8f 0008 0002 0000 03cc 0000 # X
     * 2ca5 48ab 0fc4 0008 0002 0001 0168 0000 # Y
     * 2ca5 48ab 0fde 0008 0002 0002 005a 0000 # Z
     */

    /* We have to read enough to guarantee a full packet, since if
     * we try to read one line at a time we'll end up missing lines. */
    if (!read (fileno(eventfp), buffer, sizeof (buffer)))
    {
        fprintf(stderr, "fread failed\n");
        exit(1);
    }

    state = 0;
    /* We get 7 packets at once, to ensure that we have 3 good ones. 
     * Each of those has 8*2 bytes inside it. */
    for (i = 0; i <= 6; i++) {
        curline = i * 8;
        switch (state) {
            /* State machine:
             * 0: Find a new packet
             * 1: record X (jump to 0 on error)
             * 2: record Y (jump to 0 on error)
             * 3: record Z (jump to 0 on error), process packet, reset
             */
        case 0:
            if (buffer[curline + 4] == 0x0 && buffer[curline + 5] == 0x0) {
                x = y = z = 0;
                state = 1;
            }
            break;
        case 1:
            if (!(buffer[curline + 4] == 0x2 && buffer[curline + 5] == 0x0)) {
                printf("EXPECTED: 2 0: %x %x\n",
                       buffer[curline + 4], buffer[curline + 5]);
                state = 0;
                break;
            }
            x = buffer[curline + 7];
            state++;
            break;
        case 2:
            if (!(buffer[curline + 4] == 0x2 && buffer[curline + 5] == 0x1)) {
                printf("EXPECTED: 2 1: %x %x\n",
                       buffer[curline + 4], buffer[curline + 5]);
                state = 0;
                break;
            }
            y = buffer[curline + 7];
            state++;
            break;
        case 3:
            if (!(buffer[curline + 4] == 0x2 && buffer[curline + 5] == 0x2)) {
                printf("EXPECTED: 2 2: %x %x\n",
                       buffer[curline + 4], buffer[curline + 5]);
                state = 0;
                break;
            }
            z = buffer[curline + 7];
            /*
             * We finished a packet.  Process it. 
             * We test the final 4 bytes for:
             * ffff/ffff -- portrait
             * 0000/ffff -- portrait
             * 0000/0000 -- landscape
             * ffff/0000 -- landscape
             *
             * We might do better by using the previous eight bytes and
             * diagonal quadrants instead of the final four bytes, but
             * this seems to work out well for now.
             */
            if (y == 0xffff && (x == 0xffff || x == 0x0))
                return ORIENTATION_NORMAL;
            else if (x == 0x0 && y == 0x0)
                return ORIENTATION_LEFT;
            else if (x == 0xffff && y == 0x0)
                return ORIENTATION_RIGHT;
            else {
                fprintf(stderr, "Unhandled orientation\n");
                return -1;
            }
            break;
        default:
            /* Shouldn't get here. */
            fprintf(stderr, "We hit the default case; bailing out.\n");
            exit(1);
        }
    }

    /* If we get here, we didn't manage to get a complete packet. */
    fprintf(stderr, "Didn't process a full packet.\n");
    return -1;

}

int main (int argc, char *argv[]) {
    FILE *eventfp;
    int rotation = -1;
    int oldrotation = -1;

    /* Does this always hold on OpenMoko distros? */
    putenv("DISPLAY=:0");

    eventfp = fopen(EVENT_PATH, "r");
    if (eventfp == NULL) {
        fprintf(stderr, "Couldn't open file.\n");
        exit(1);
    }

    while (1) {
        sleep(1);
        if (rotation != -1)
            oldrotation = rotation;

        rotation = process_packet(eventfp);
        if (rotation != oldrotation && rotation != -1)
            set_rotation(rotation);
    }
}

