#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>

// sound stuff
#include <linux/soundcard.h>

// keys
#define  FN_F2  1       // cut sound
#define  FN_F3  2       // volume -
#define  FN_F4  4       // volume +
#define  FN_F5  8       // Brightness -
#define  FN_F6  16      // Brightness +
#define  FN_F7  32      // LCD/SCREEN
#define  FN_F10 128     // Zoom in
#define  FN_F12 64      // Suspend
#define  S1_BTN 4096    // S1 custom button
#define  S2_BTN 8192    // S2 custom button

// config hard coded :p
#define  MIXER_DEV      "/dev/mixer"

// bool stuff
#define FALSE	0
#define TRUE	!(FALSE)

// SOUND HANDLER
int get_volume(int *value)
{
    int mixer = open(MIXER_DEV, O_RDONLY);

    if (mixer) {
        ioctl(mixer, SOUND_MIXER_READ_VOLUME, value);
        close(mixer);
        return 0;
    }
    else
        return 1;
}

int set_volume(int *value)
{
    int mixer = open(MIXER_DEV, O_RDWR);

    if (mixer) {
        ioctl(mixer, SOUND_MIXER_WRITE_VOLUME, value);
        close(mixer);
        return 0;
    }
    else
        return 1;
}

int volume_up()
{
    int value = 0;

    get_volume(&value);

    if (value < 0x5a5a)
        value += 0x0a0a;
    else
        value = 0x6464;

    set_volume(&value);

    return 0;
}

int volume_down()
{
    int value = 0;

    get_volume(&value);

    if (value > 0x0a0a)
        value -= 0x0a0a;
    else
        value = 0;

    set_volume(&value);

    return 0;
}

int oldvalue;
int mute()
{
    int value;

    get_volume(&value);

    if (value) {
            oldvalue=value;
            value=0;
            set_volume(&value);
    }
    else {
            if (!oldvalue) {
                volume_up();
            }
            else {
                set_volume(&oldvalue);
            }
    }

    return 0;
}
// END OF SOUND

/* Return current brightness of the screen */
int getBrightness() {
        FILE* handle;
        char buffer[2];
        int ret;

        if ((handle=fopen("/proc/acpi/sony/brightness","rb"))==NULL) {
                perror("Error opening /proc/acpi/sony/brightness");
                exit(-1);
        }
        if (fscanf(handle,"%d",&ret)!=1) {
                perror("Error reading /proc/acpi/sony/brightness");
                exit(-1);
        }
        fclose(handle);
        return ret;

}

/* Set the current brightness of the screen */
void setBrightness(int b) {
        FILE* handle;
        char buffer[2];

        // validate values
        if (b>8) {
                b=8;
        }
        else if (b<1) {
                b=1;
        }

        if ((handle=fopen("/proc/acpi/sony/brightness","wb"))==NULL) {
                perror("Error opening /proc/acpi/sony/brightness");
                exit(-1);
        }
        if (fprintf(handle,"%d",b)!=1) {
                perror("Error writing /proc/acpi/sony/brightness");
                exit(-1);
        }
        fclose(handle);
}

// Pool the fnkey status
int getCodes() {
        FILE* handle;
        char buffer[10];
        int ret;

        if ((handle=fopen("/proc/acpi/sony/fnkey","rb"))==NULL) {
                perror("Error opening /proc/acpi/sony/fnkey");
                exit(-1);
        }
        if (fscanf(handle,"%d",&ret)!=1) {
                perror("Error reading /proc/acpi/sony/fnkey");
                exit(-1);
        }
        fclose(handle);
        return ret;
}

/*********************************************
 * Added by Bodo Akdeniz <cypher_ab@gmx.de>
 * http://cypher.xail.net/
 */
// Checks the status of vga-out using i810switch
int vgaOutIsOn() {
	FILE *handle;
	char buf[10], value[10];

	if ((handle=popen("i810switch", "r"))==NULL) {
		perror("Error opening pipe to i810switch");
		exit(-1);
	}
	if (fscanf(handle, "%s %s", buf, value)==EOF) {
		perror("Error reading from pipe to i810switch");
		exit(-1);
	}
	if (strcmp("on", value)==0) {
		return TRUE;
	}
	else {
		return FALSE;
	}
}

// Switch vga-out on/off
int i810switch() {
	if (vgaOutIsOn()) {
		printf("VGA-out is activated\n");
		printf("Deactivating VGA-out...\n");
		system("i810switch crt off");
	}
	else {
		printf("VGA-out is not activated\n");
		printf("Activating VGA-out...\n");
		system("i810switch crt on");
	}
}
/* End of Bodo's work                       */
/********************************************/
// main and loop
int main(void) {
        int key;
        nice(10); // be a nice dirty code

        printf("SonyFN loaded\n");
        while(1) { // loop
                if (key=getCodes()) { // get Fn status
                        if ((key & FN_F5)==FN_F5) { // lower brightness
                                setBrightness(getBrightness()-1);
                        }
                        if ((key & FN_F6)==FN_F6) { // higher brightness
                                setBrightness(getBrightness()+1);
                        }
                        if ((key & FN_F2)==FN_F2){
                                mute();
                        }
                        if ((key & FN_F3)==FN_F3) {
                                volume_down();
                        }
                        if ((key & FN_F4)==FN_F4) {
                                volume_up();
                        }
			
			/* CHANGE: by Bodo Akdeniz <cypher_ab@gmx.de> */
			if ((key & FN_F7)==FN_F7) {
				i810switch(); // uses i810switch
				// get the latest version here:
				// http://www16.plala.or.jp/mano-a-mano/i810switch.html
			}
			/* CHANGE END */
			
                        // rest i don't care
                }
                usleep(300000);
        }
        return 0;
}

