/*
    xvidmode support for gvidm
    Copyright (C) 2001-2004 Matthew Mueller <donut AT dakotacom DOT net>
    based on gvid:
    Copyright (C) 1999-2001 Keith Vanderline <kvand@mit.edu>

    This program 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 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#include "gvid.h"
#include "vidmode.h"
#include "_types.h"

#ifdef HAVE_LIBXXF86VM

static inline bool is_same_mode(XF86VidModeModeInfo *mi, int dotclock, XF86VidModeModeLine *ml)
{
	return mi->dotclock == (unsigned int)dotclock &&
		mi->hdisplay == ml->hdisplay && mi->vdisplay == ml->vdisplay &&
		mi->htotal == ml->htotal     && mi->vtotal == ml->vtotal &&
		mi->flags == ml->flags;
}

static inline float calc_refresh(int dotclock, short htotal, short vtotal, unsigned int flags) {
	return (dotclock * 1000.0 / (htotal * vtotal) * 
			((flags & XF86CONF_DBLSCAN)?0.5:1) *
			((flags & XF86CONF_INTERLACE)?2.0:1)
			);
}

static inline float calc_refresh(const XF86VidModeModeInfo *m) {
	return calc_refresh(m->dotclock, m->htotal, m->vtotal, m->flags);
}

static inline float calc_refresh(int dotclock, const XF86VidModeModeLine *m) {	
	return calc_refresh(dotclock, m->htotal, m->vtotal, m->flags); 
}

static int check_VidMode_supported(void) {
	int major,minor;
	return !XF86VidModeQueryVersion(display, &major, &minor);
}

XVidModeHandler::XVidModeHandler() {
	if (check_VidMode_supported()) {
		fprintf(stderr, "gvidm: VidMode extension not supported\n");
		exit(1);
	}

	int num_screens=get_screen_count();
	for (int screen=0; screen<num_screens; ++screen) {
		available_modes.push_back(mode_list_t());
		mode_list_t &smodes = available_modes.back();
		int vm_count, dotclock;
		XF86VidModeModeLine modeline;
		XF86VidModeGetModeLine(display, screen, &dotclock, &modeline);
		XF86VidModeModeInfo **vm_modelines;
		XF86VidModeGetAllModeLines(display, screen, &vm_count, &vm_modelines);
		for (int mode=0; mode < vm_count; mode++) {
			XF86VidModeModeInfo *m = vm_modelines[mode];
			string info;
			if (m->flags & XF86CONF_DBLSCAN) info += 'd';
			if (m->flags & XF86CONF_INTERLACE) info += 'i';
			smodes.push_back(new VidMode(is_same_mode(m, dotclock, &modeline), m->hdisplay, m->vdisplay, round(calc_refresh(m)), info, (void*)(uintptr_t)((screen<<16) + mode)));
			
		}
		XFree(vm_modelines);
	}
}

XVidModeHandler::~XVidModeHandler() {
}

int XVidModeHandler::doevent(gpointer data) {
	int mode=((uintptr_t)data)&0xFFFF;
	int screen=((uintptr_t)data)>>16;
	XF86VidModeModeInfo **vm_modelines;
	int vm_count;

	assert(screen<get_screen_count());
	XF86VidModeGetAllModeLines(display, screen, &vm_count, &vm_modelines);
	assert(mode<vm_count);
	XF86VidModeSwitchToMode(display, screen, vm_modelines[mode]);
	XFree(vm_modelines);
	XFlush(display);
	return 0;
}

#endif
