/*****************************************************************************

GRAOUMF TRACKER 2

Copyright (c) 1996 - 2002 Laurent de Soras

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.

Contact the author : laurent@ohmforce.com
More information about this license : http://www.gnu.org/licenses/gpl.html

*****************************************************************************/



/*
Trucs rajoutes dans le project pour Visual C++ 5.0:

Settings for All Configurations:
	o General:
      - Intermediate files: debug
		- Output files: plus rien.
	o C/C++:
		- General:
			. Generate browse info
		- Code Generation:
			. Alignement: 1 Byte
			. Run-time: (Debug) Multi-Threaded
	o Link:
		- Object/Library modules: dinput.lib dsound.lib mixer_a.obj splhan_a.obj os_a.obj
	o Browse:
      - Build browse info file

Settings en mode Release:
	o C/C++:
		- Optimizations:
			. Customize: Global, intrinsic, fast code, full opt
			. Inline: any suitable
*/


#define	OS_CURRENT_MODULE
#define	MODULE_OS



/*\\\ FICHIERS INCLUDE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include	<assert.h>
#include	<stdio.h>
#include	<time.h>
#include	<process.h>
//#include	<math.h>	// Uniquement pour le test du module ReconstructionFilter
//#include	<math.h>	// Uniquement pour le test du module filter.cpp

/* Le label suivant ne doit etre defini que dans un seul module pour
   DirectDraw (pour pouvoir utiliser QueryInterface), et que dans un
   seul module pour DirectInput. */
#define	INITGUID
#define	WIN32_LEAN_AND_MEAN
#if ! defined (DIRECTINPUT_VERSION)
	#define	DIRECTINPUT_VERSION	0x0800
#endif
#include <windows.h>
#include <windowsx.h>
#include	<shellapi.h>
#include	<dinput.h>

#include	"archi.h"
#include	"base.h"
#include	"base_ct.h"
//#include	"ConfigFile.h"	// Uniquement pour le test du module de lecture de fichier de config
#include	"file.h"
#include	"fnames.h"
#include	"gtracker.h"
#include	"intrface.h"
//#include	"List.h"					// Uniquement pour le test du module de lecture de fichier de config
#include	"log.h"
#include	"mixer.h"
#include	"Mutex.h"
#include	"os.h"
#include	"os_di.h"
#include	"player.h"
//#include	"ReconstructionFilter.h"	// Uniquement pour le test du module ReconstructionFilter
#include	"sdrv_dx.h"
#include	"SoundDriver.h"
#include	"Thread.h"
#include	"windows_resources/resource.h"

// Provisoire
#include	"memory.h"



/*\\\ CONSTANTES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#define	OS_WINDOW_NAME_MAXLEN		255
#define	OS_WINDOW_TITLE_MAXLEN		255
#define	OS_MAX_COMMAND_LINE_LEN		1023
#define	OS_MAX_COMMAND_LINE_ARGC	255


/*\\\ TYPES & STRUCTURES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ PROTOTYPES DES FONCTIONS PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

signed int OS_create_window (HINSTANCE hInstance, int nCmdShow);
LRESULT CALLBACK	OS_window_proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int	OS_get_real_mouse_button_state (void);
void	OS_main_thread_routine (void);

inline void	OS_get_cpu_clock_time (__int64 *time_64_ptr);



/*\\\ VARIABLES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

/* Parametres transmis a GTK_main () */
int	OS_application_argc;
char	*OS_application_argv [OS_MAX_COMMAND_LINE_ARGC+1];
char	OS_application_command_line_0 [OS_MAX_COMMAND_LINE_LEN+1];

char	OS_window_name_0 [OS_WINDOW_NAME_MAXLEN+1] = "GTracker";
char	OS_window_title_0 [OS_WINDOW_TITLE_MAXLEN+1] = "Graoumf Tracker";

int	OS_graph_mode_state = 0;	/* 0 = ancien mode, 1 = mode graphique, 2 = changement */

clock_t	OS_clock_ticks_per_sec_beg_time;	/* Horraire de depart du comptage de cycles d'horloge par seconde */

HMENU	OS_window_menu = NULL;



/*\\\ VARIABLES EXTERNES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

/* Handle de la fenetre, pour Windows. Reserve en fait
   uniquement aux sous-modules de OS. */
HINSTANCE	OS_application_instance;	/* Instance de l'application Graoumf Tracker */
HWND	OS_window_handle;
bool	OS_active_application_flag = true;	/* Indique que l'application est active */

IDirectInput8 *	OS_di_object_ptr = NULL;



class OS_ThreadStub
:	public ThreadCbInterface
{
public:
	// ThreadCbInterface
	virtual void	do_run_thread ()
	{
		OS_main_thread_routine ();
	}
};

OS_ThreadStub	OS_main_thread_stub;
Thread	OS_main_thread (OS_main_thread_stub);

/* Donnees concernant la carte son */
SoundDriver	OS_sound_driver;	// Le driver du son
LWORD	OS_min_replay_freq;	/* Frequence minimum de replay */
LWORD	OS_max_replay_freq;	/* Frequence maximum de replay */
int	OS_nbr_play_config;	/* Nombre de configurations de replay */
LWORD	OS_play_config [OS_MAX_PLAY_CONFIG];	/* Configurations de replay */

QWORD	OS_clock_ticks_per_sec = 1;	/* Nombre de cycles d'horloge par seconde */
bool	OS_clock_ticks_per_sec_counted_flag = false;	/* Indique si on a mesure le nombre de cycles d'horloge par seconde */

int	OS_cpu_time = 0;	/* Estimation du temps CPU utilise, en % */



/*\\\ FONCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

/*==========================================================================*/
/*      Nom: WinMain                                                        */
/*      Description: C'est la fonction par laquelle on rentre dans le       */
/*                   programme.                                             */
/*==========================================================================*/

int CALLBACK	WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{

/*______________________________________________
 *
 * Version normale
 *______________________________________________
 */

#if 1

	DWORD		process_id;
	HANDLE	process_handle;
	MSG		msg;
	bool		exit_flag;

#if defined (OS_USE_CPU_FREQUENCY)
	/* On commence deja a regarder le nombre de cycles par seconde du processeur */
	OS_get_cpu_clock_time (&OS_clock_ticks_per_sec);
	OS_clock_ticks_per_sec_beg_time = clock ();
#endif

	OS_application_instance = hInstance;

	/* Recuperation de la ligne de commandes au format standard */
	strncpy (OS_application_command_line_0, GetCommandLine (), OS_MAX_COMMAND_LINE_LEN);
	OS_application_command_line_0 [OS_MAX_COMMAND_LINE_LEN] = '\0';
	OS_application_argc = BASE_parse_command_line (OS_application_command_line_0,
	                                               OS_application_argv,
	                                               OS_MAX_COMMAND_LINE_ARGC, NULL);

	/* Choisit le bon driver pour le son */
	OS_sound_driver._init          = SDRVDX_init;
	OS_sound_driver._restore       = SDRVDX_restore;
	OS_sound_driver._set_config    = SDRVDX_set_config;
	OS_sound_driver._get_config    = SDRVDX_get_config;
	OS_sound_driver._start_replay  = SDRVDX_start_replay;
	OS_sound_driver._stop_replay   = SDRVDX_stop_replay;
	OS_sound_driver._set_new_frame = SDRVDX_set_new_frame;
	OS_sound_driver._set_data      = SDRVDX_set_data;
	OS_sound_driver._get_data      = SDRVDX_get_data;
	OS_sound_driver._is_playing    = SDRVDX_is_playing;
	OS_sound_driver._disconnect    = SDRVDX_disconnect;
	OS_sound_driver._connect       = SDRVDX_connect;
	OS_sound_driver._is_connected  = SDRVDX_is_connected;
	OS_sound_driver._get_message   = SDRVDX_get_message;
	OS_sound_driver._get_latency   = SDRVDX_get_latency;
	OS_sound_driver._set_latency   = SDRVDX_set_latency;

	/* Recupere le handle du process */
	process_id = _getpid ();
	process_handle = OpenProcess (STANDARD_RIGHTS_REQUIRED, false, process_id);
	if (process_handle != NULL)
	{
		#if 0 && defined (NDEBUG)
		{
			/* Passe le processus en mode de priorite haute */
			if (SetPriorityClass (process_handle, HIGH_PRIORITY_CLASS) == 0)
			{
				LOG_printf ("Warning: Couldn't set High Priority for process.\n");
			}
		}
		#endif

		CloseHandle (process_handle);
	}

	else
	{
		LOG_printf ("Warning: Couldn't get process handle to set High Priority.\n");
	}

	/* Creation de la fenetre de travail */
	if (OS_create_window (hInstance, nCmdShow))
	{
		LOG_printf ("WinMain: Error: Couldn't create application window.\n");
		return (-1);
	}

	/* Genere un thread pour le main */
	if (OS_main_thread.spawn ())
	{
		LOG_printf ("WinMain: Error: Couldn't spawn main thread.\n");
		return (-1);
	}

	/* Boucle de la fenetre */
	exit_flag = false;
	while (! exit_flag)
	{
		/* Traitement d'un message */
		while (   PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE) != 0
		       && ! exit_flag)
		{
			if (GetMessage (&msg, NULL, 0, 0) == 0)
			{
				exit_flag = true;
			}

			else
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}

		/* Comme on n'a rien a faire pour l'instant, on attend seulement
		   qu'il y ait un message qui arrive. */
		if (! exit_flag)
		{
			WaitMessage ();
		}
	}

	return (int (msg.wParam));

/*______________________________________________
 *
 * Version de test de la classe
 * ReconstructionFilter
 *______________________________________________
 */

#elif 0

	ReconstructionFilter	rf_test (128);
	SWORD		source [1000];
	SLWORD	dest [20000] [2];
	int		i;
	int		length;
	double	invr;
	int		len_1;
	int		len_2;
	double	s_pos;
	double	temp_dbl;
	int		tot_len;

	memset (source, 0, 1000 * sizeof (*source));
	memset (dest, 0, 20000 * 2 * sizeof (**dest));
	tot_len = 1000;	// <= 1000

	freopen ("sample1.txt", "w", stdout);
	for (i = 0; i < tot_len; i ++)
	{
		source [i] = (SWORD) (sin ((double)i * 2 * PI / 20) * 32767);
//		source [i] = (i >= 100 ? 20000: 0);
//		source [i] = 10000;
		printf ("%d\n", source [i]);
	}

	invr = 0.25;	// < 1: sur-echantillonne
	length = (int) ceil (tot_len / invr);
	len_1 = length / 2;
	rf_test.resample_and_mix
	(
		source,		// sptr
		dest [0],	// dptr
		len_1,		// dlen
		1,				// schn
		2,				// dchn
		0x8000,		// lvol
		0x8000,		// rvol
		false,		// Courbe non cassee
		0x00000000,	// posfrac
		invr,			// invresampfactor
		16				// sbits
	);

	s_pos = len_1 * invr;
	len_2 = length - len_1;
	rf_test.resample_and_mix
	(
		source + (int)s_pos,		// sptr
		dest [len_1],	// dptr
		len_2,		// dlen
		1,				// schn
		2,				// dchn
		0x8000,		// lvol
		0x8000,		// rvol
		false,		// Courbe non cassee
		(ULWORD) (modf (s_pos, &temp_dbl) * 4294967296.0),	// posfrac
		invr,			// invresampfactor
		16				// sbits
	);

	freopen ("sample2.txt", "w", stdout);
	for (i = 0; i < length; i ++)
	{
		printf ("%d\n", MAX (MIN (dest [i] [1] / 256, 32767), -32768));
	}

	return (0);

/*______________________________________________
 *
 * Version de test du module filter
 *______________________________________________
 */

#elif 0

	#include	"filter.h"

	double	l_num [10];
	double	l_den [10];
	double	z_num [10];
	double	z_den [10];

	double	buf_y [16];
	SLWORD	source [44100];
	SLWORD	dest [44100];
	SLWORD	buf_x [16];
	int		nbr_coef;
	int		buffer_pos;
	long		i;
	double	src_power;
	double	dest_power;
	int		test;
	clock_t	init_time;
	clock_t	final_time;
	double	seconds;

	l_num [0] = 1;
	l_den [0] = 1;
	nbr_coef = 1;
	FILT_add_biquad (l_num, l_den, nbr_coef, 44100, 1000, 1000, 0.5, FILT_BIQUAD_LOWP);
	FILT_laplace_to_z (l_num, l_den, nbr_coef, 44100, 1000, z_num, z_den);
	FILT_z_to_diff_eq (z_num, z_den, nbr_coef, z_num, z_den);

	for (i = 0; i < 44100; i ++)
	{
		source [i] = (SLWORD) (sin (i * 2 * PI * 100 / 44100) * 0x7FFFFF);
	}
	memset (buf_x, 0, 16 * sizeof (*buf_x));
	memset (buf_y, 0, 16 * sizeof (*buf_y));

	buffer_pos = 0;
	init_time = clock ();
	for (test = 0; test < 1000; test ++)
	{
		FILT_do_iir_filtering (source, dest, 1, 1, z_num, z_den, nbr_coef,
		                       buf_x, buf_y, buffer_pos, 16, 44100);
	}
	final_time = clock ();
	seconds = (double) (final_time - init_time) / CLOCKS_PER_SEC;	// 34.510 pour un ordre 4

	src_power = 0;
	dest_power = 0;
	for (i = 0; i < 44100; i ++)
	{
		src_power += (double)source [i] * source [i];
		dest_power += (double)dest [i] * dest [i];
	}
	src_power /= 44100;
	dest_power /= 44100;

	return (0);

/*______________________________________________
 *
 * Autre
 *______________________________________________
 */

#else







	return (0);

#endif
}



signed int OS_create_window (HINSTANCE hInstance, int nCmdShow)
{
	WNDCLASSEX	wc;

	/* Fabrique le menu */
	OS_window_menu = CreateMenu ();
	if (! OS_window_menu)
	{
		LOG_printf ("OS_create_window: Error: menu creation failed.\n");
		return (-1);
	}

	/* Met en place une nouvelle classe de fenetre */
	wc.cbSize = sizeof (wc);
	wc.style = CS_BYTEALIGNCLIENT | CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = OS_window_proc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;
	wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
	wc.hCursor = NULL;	// LoadCursor (NULL, IDC_ARROW);
	wc.hbrBackground = NULL;
	wc.lpszMenuName = OS_window_name_0;
	wc.lpszClassName = OS_window_name_0;
	wc.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
	RegisterClassEx (&wc);

	/* Cree la fenetre */
	OS_window_handle = CreateWindowEx (WS_EX_APPWINDOW | WS_EX_OVERLAPPEDWINDOW | WS_EX_CONTEXTHELP,
	                                   OS_window_name_0, OS_window_title_0,
	                                   WS_OVERLAPPEDWINDOW | WS_VISIBLE,
	                                   CW_USEDEFAULT, 0,
	                                   0, 0,
	                                   NULL, NULL, hInstance, NULL );
	if (! OS_window_handle)
	{
		LOG_printf ("OS_create_window: Error: window creation failed.\n");
		return (-1);
	}

	/* Accepte les fichiers en Drag and Drop */
	DragAcceptFiles (OS_window_handle, TRUE);

	ShowWindow (OS_window_handle, nCmdShow);
	UpdateWindow (OS_window_handle);

	return (0);
}



LRESULT CALLBACK	OS_window_proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	LRESULT			ret_val;
	PAINTSTRUCT		ps;
	HDC				hdc;
	Mouse_CURRENT_STATE	mouse;

	switch (message)
	{
	/* Activation ou desactivation de la fenetre */
	case	WM_ACTIVATEAPP:
		UpdateWindow (OS_window_handle);

		/* Desactivation */
		if (wParam == 0)
		{
			if (OS_active_application_flag)
			{
				OS_active_application_flag = false;

				/* On stoppe le thread qui s'occupe de gerer l'interface */
				DINP_keyboard_mutex.wait ();

				/* Si on n'est pas en mode play, on libere le peripherique son */
				Player &			player = Player::use_instance ();
				if (   (   player.get_play_mode () != Player::MODE_PATTERN
				        && player.get_play_mode () != Player::MODE_SONG)
					 || ! player.get_player_started_flag ())
				{
					OS_sound_driver.disconnect ();
				}
			}
		}
		
		/* Activation */
		else
		{
			ret_val = DefWindowProc (hWnd, message, wParam, lParam);

			if (! OS_active_application_flag)
			{
				/* Reactive le son si on l'avait desactive */
				OS_sound_driver.connect ();

				/* Restoration du thread de gestion de l'interface */
				DINP_keyboard_mutex.signal ();

				OS_active_application_flag = true;
			}

			return (ret_val);
		}
		return (0);

	/* Redraw du bitmap */
	case	WM_PAINT:
		hdc = BeginPaint (OS_window_handle, &ps);
		if (hdc == NULL)
		{
			LOG_printf ("OS_window_proc: Error: Couldn't get the display context.\n");
		}
		if (INTR_graph_ptr != NULL)
		{
			INTR_graph_ptr->paint (hdc);
		}
		EndPaint (OS_window_handle, &ps);
		return (0);

	/* Fermeture normale */
	case	WM_CLOSE:
		GTK_ask_for_exit_flag = true;
		return (0);

	/* Fermeture forcee */
	case	WM_DESTROY:
		GTK_exit_now_flag = true;
		return (0);

	/* Gestion de la souris */
	case	WM_MOUSEMOVE:
		if (INTR_graph_ptr != NULL)
		{
			INTR_graph_ptr->set_mouse (LOWORD (lParam), HIWORD (lParam),
			                             (((wParam & MK_LBUTTON) != 0) ? 1 : 0)
			                           + (((wParam & MK_RBUTTON) != 0) ? 2 : 0)
			                           + (((wParam & MK_MBUTTON) != 0) ? 4 : 0));
		}
		return (0);

	case	WM_LBUTTONUP:
		ReleaseCapture ();
		if (INTR_graph_ptr != NULL)
		{
			INTR_graph_ptr->get_mouse (&mouse);
			INTR_graph_ptr->set_mouse (LOWORD (lParam), HIWORD (lParam),
												mouse.k & 0x6);	// OS_get_real_mouse_button_state ());
		}
		return (0);

	case	WM_RBUTTONUP:
		ReleaseCapture ();
		if (INTR_graph_ptr != NULL)
		{
			INTR_graph_ptr->get_mouse (&mouse);
			INTR_graph_ptr->set_mouse (LOWORD (lParam), HIWORD (lParam),
			                           mouse.k & 0x5);	// OS_get_real_mouse_button_state ());
		}
		return (0);

	case	WM_MBUTTONUP:
		ReleaseCapture ();
		if (INTR_graph_ptr != NULL)
		{
			INTR_graph_ptr->get_mouse (&mouse);
			INTR_graph_ptr->set_mouse (LOWORD (lParam), HIWORD (lParam),
			                           mouse.k & 0x3);	// OS_get_real_mouse_button_state ());
		}
		return (0);

	case	WM_LBUTTONDOWN:
		SetCapture (OS_window_handle);
		if (INTR_graph_ptr != NULL)
		{
			INTR_graph_ptr->get_mouse (&mouse);
			INTR_graph_ptr->set_mouse (LOWORD (lParam), HIWORD (lParam),
			                           (mouse.k & 0x6) + 1);	// OS_get_real_mouse_button_state ());
		}
		return (0);

	case	WM_RBUTTONDOWN:
		SetCapture (OS_window_handle);
		if (INTR_graph_ptr != NULL)
		{
			INTR_graph_ptr->get_mouse (&mouse);
			INTR_graph_ptr->set_mouse (LOWORD (lParam), HIWORD (lParam),
			                           (mouse.k & 0x5) + 2);	// OS_get_real_mouse_button_state ());
		}
		return (0);

	case	WM_MBUTTONDOWN:
		SetCapture (OS_window_handle);
		if (INTR_graph_ptr != NULL)
		{
			INTR_graph_ptr->get_mouse (&mouse);
			INTR_graph_ptr->set_mouse (LOWORD (lParam), HIWORD (lParam),
			                           (mouse.k & 0x3) + 4);	// OS_get_real_mouse_button_state ());
		}
		return (0);

	/* La fenetre a change de taille */
	case	WM_SIZE:
		if (wParam != SIZE_MINIMIZED)
		{
			GTK_new_res_width = LOWORD (lParam);
			GTK_new_res_height = HIWORD (lParam);
			GTK_ask_for_resolution_change_flag = true;
		}
		return (0);

	/* Simple redraw */
	case	WM_MOVE:
		UpdateWindow (OS_window_handle);
		return (0);

	/* On doit redonner la forme du curseur */
	case	WM_SETCURSOR:
		if (INTR_graph_ptr != NULL)
		{
			SetCursor (INTR_graph_ptr->get_current_cursor_shape ());
		}
		else
		{
			SetCursor (LoadCursor (NULL, IDC_APPSTARTING));
		}
		break;

	/* Un fichier a ete traine sur la fenetre */
	case	WM_DROPFILES:
		if (! GTK_ask_for_loading_module_flag)
		{
			DragQueryFile ((HDROP) wParam, 0,
			               GTK_drag_and_drop_filename_0, FNAM_PATHNAME_MAXLEN);
			SetForegroundWindow (OS_window_handle);
			GTK_ask_for_loading_module_flag = true;
		}
		return (0);
	}

	return (DefWindowProc (hWnd, message, wParam, lParam));
}



int	OS_get_real_mouse_button_state (void)
{
	int		k;

	/* Recupere l'etat des boutons */
	if (GetSystemMetrics (SM_SWAPBUTTON))
	{
		k =   (GetAsyncKeyState (VK_LBUTTON) & 0x8000) ? 2 : 0
		    + (GetAsyncKeyState (VK_RBUTTON) & 0x8000) ? 1 : 0;
	}
	else
	{
		k =   (GetAsyncKeyState (VK_LBUTTON) & 0x8000) ? 1 : 0
		    + (GetAsyncKeyState (VK_RBUTTON) & 0x8000) ? 2 : 0;
	}
	k += (GetAsyncKeyState (VK_MBUTTON) & 0x8000) ? 4 : 0;

	return (k);
}



/*==========================================================================*/
/*      Nom: OS_main_thread_routine                                         */
/*      Description: Thread principal. Il execute la partie principale du   */
/*                   programme (init, boucles, etc.)                        */
/*==========================================================================*/

void	OS_main_thread_routine (void)
{
	/* Fonction principale */
	GTK_main (OS_application_argc, OS_application_argv);

	/* On demande au thread pere de se suicider */
	if (PostMessage (OS_window_handle, WM_QUIT, 0, 0) == 0)
	{
		LOG_printf ("OS_main_thread: Error: couldn't send WM_QUIT message to main window.\n");
	}
}



void	OS_get_cpu_clock_time (__int64 *time_64_ptr)
{
	assert (time_64_ptr != 0);

	__asm
	{
		rdtsc
		mov				edi, time_64_ptr
		mov				[edi + 0], eax
		mov				[edi + 4], edx
	}
}



/****************************************************************************/
/*                                                                          */
/*      INITIALISATIONS                                                     */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: OS_first_init                                                  */
/*      Description: Premieres initialisations: recuperation du chemin      */
/*                   d'execution, initialisation du clavier...              */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	OS_first_init (void)
{
	/* Initialisation de Direct Input */
	if (DINP_init_direct_input (OS_application_instance))
	{
		LOG_printf ("OS_first_init: Error: DirectInput init failed.\n");
		return (-1);
	}

	/* Initialise le clavier */
	if (DINP_init_keyboard ())
	{
		LOG_printf ("OS_first_init: Error: keyboard init failed.\n");
		return (-1);
	}

	return (0);
}



void	OS_first_restore (void)
{
	DINP_restore_keyboard ();
	DINP_restore_direct_input ();
}



/*==========================================================================*/
/*      Nom: OS_init_sound_system                                           */
/*      Description: Initialise le systeme sonore.                          */
/*      Retour: 0 si tout va bien.                                          */
/*==========================================================================*/

signed int	OS_init_sound_system (void)
{
	SoundDriver_CONFIG	config;
	int		out_stereo [GTK_NBROUT_MAXI];
	int		in_stereo [GTK_NBRIN_MAXI];

	/* Initialise le driver */
	if (OS_sound_driver.init (OS_window_handle))
	{
		LOG_printf ("OS_init_sound_system: Error: couldn't init sound card.\n");
		return (-1);
	}

	/* C'est la configuration par defaut: 2 voies stereo en
		entree et en sortie. */
	config.sample_freq = 44100;
	config.nbr_buffers = 2;
	config.nbr_out = 2;
	config.nbr_in = 2;
	config.out_stereo_ptr = out_stereo;
	config.in_stereo_ptr = in_stereo;
	out_stereo [0] = 2;
	out_stereo [1] = 0;
	in_stereo [0] = 2;
	in_stereo [1] = 0;

	/* Fait evaluer la config */
	if (OS_sound_driver.set_config (&config))
	{
		LOG_printf ("OS_init_sound_system: Error: couldn't set configuration.\n");
		return (-1);
	}

	/* Verifie qu'il y a suffisamment de pistes de sortie et d'entree */
	if (config.nbr_out < 2)
	{
		LOG_printf ("OS_init_sound_system: Error: insufficient output tracks.\n");
		return (-1);
	}
	if (config.nbr_in < 2)
	{
		LOG_printf ("OS_init_sound_system: Error: insufficient input tracks.\n");
		return (-1);
	}

	/* La config est validee, on recupere les informations qui ont peut-etre
		ete corrigees. On ne s'occupe pas encore de la stereo de chaque piste
		car on la reglera plus tard (juste avant de demarrer le player) selon
		les preferences de l'utilisateur. */
	MIX_replay_freq = config.sample_freq;
	GTK_nbr_tracks [Pattern_TYPE_AIN] = config.nbr_in;
	GTK_nbr_tracks [GTK_TRACK_TYPE_AOU] = config.nbr_out;

	/* Maintenant, on reserve le peripherique */
	if (OS_sound_driver.connect ())
	{
		LOG_printf ("OS_init_sound_system: Warning: couldn't allocate sound device. Sound is disable for this session.\n");
		//return (-1);
	}

	return (0);
}



void	OS_restore_sound_system (void)
{
	OS_sound_driver.restore ();
}



/****************************************************************************/
/*                                                                          */
/*      CLAVIER ET SOURIS                                                   */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: OS_key_pressed                                                 */
/*      Description: Interroge le calvier pour voir si une touche a ete     */
/*                   enfoncee (buffer clavier non vide).                    */
/*      Retour: true si une touche a ete pressee, false sinon.              */
/*==========================================================================*/

bool	OS_key_pressed (void)
{
	return (DINP_key_pressed ());
}



/*==========================================================================*/
/*      Nom: OS_get_key                                                     */
/*      Description: Renvoie le code d'une touche ayant ete pressee, avec   */
/*                   l'etat des touches "auxiliaires".                      */
/*      Retour: Mot long decoupe en bits:                                   */
/*              0-7  : Code ASCII                                           */
/*              8    : Au moins une des touche Shift appuyee                */
/*              12-15: Bit du groupe de touche auquel appartient la touche. */
/*                     Ce champ n'est pas rempli par cette routine.         */
/*              16-23: code SCAN                                            */
/*              24   : Shift droit enfonce                                  */
/*              25   : Shift gauche enfonce                                 */
/*              26   : Control enfonce                                      */
/*              27   : Alternate enfonce                                    */
/*              28   : Caps Lock active                                     */
/*              29   : Num Lock active                                      */
/*              30   : Scroll Lock active                                   */
/*==========================================================================*/

LWORD	OS_get_key (void)
{
	return (DINP_get_key ());
}



/****************************************************************************/
/*                                                                          */
/*      DIVERS                                                              */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: OS_get_time                                                    */
/*      Description: Renvoie le nombre de cycles du processeur effectues    */
/*                   depuis l'allumage. Cette fonction n'est disponible que */
/*                   quand le nombre de cycles par seconde a ete mesure.    */
/*                   Cette routine ne doit etre utilisee que pour les       */
/*                   benchs.                                                */
/*      Retour: Nombre de cycles                                            */
/*==========================================================================*/

QWORD	OS_get_time (void)
{
	QWORD		time_64;

	assert (OS_clock_ticks_per_sec_counted_flag);

#if defined (OS_USE_CPU_FREQUENCY)

	OS_get_cpu_clock_time (&time_64);

#else

	QueryPerformanceCounter ((LARGE_INTEGER *) &time_64);
	if (time_64 == 0)
	{
		time_64 = (QWORD) clock ();
	}

#endif

	return (time_64);
}



/*==========================================================================*/
/*      Nom: OS_processor_frequency_measurement                             */
/*      Description: Mesure du nombre de cycles d'horloge du processeur par */
/*                   seconde.                                               */
/*==========================================================================*/

/*** Changer tout ca et utiliser QueryPerformanceFrequency et
     QueryPerformanceCounter, qui sont plus propres. ***/

void	OS_processor_frequency_measurement (void)
{

#if defined (OS_USE_CPU_FREQUENCY)

	__int64	nbr_clock_ticks;
	clock_t	duration;

	if (! OS_clock_ticks_per_sec_counted_flag)
	{
		duration = 0;
		while (duration < CLOCKS_PER_SEC)
		{
			OS_get_cpu_clock_time (&nbr_clock_ticks);
			duration = clock () - OS_clock_ticks_per_sec_beg_time;
			if (duration < CLOCKS_PER_SEC)
			{
				Sleep (100);
			}
		}

		OS_clock_ticks_per_sec = (__int64) (  (  (double)(  nbr_clock_ticks
		                                                  - OS_clock_ticks_per_sec)
		                                       * CLOCKS_PER_SEC)
		                                    / duration);
		OS_clock_ticks_per_sec_counted_flag = true;
		LOG_printf ("OS_processor_frequency_measurement: Estimated processor frequency: %d MHz\n",
					   (int) (OS_clock_ticks_per_sec / 1000000));
	}

#else

	QueryPerformanceFrequency ((LARGE_INTEGER *) &OS_clock_ticks_per_sec);

	/* Si on ne supporte pas la mesure precise de frequence, tant pis
	   on utilise l'horloge standard. */
	if (OS_clock_ticks_per_sec == 0)
	{
		OS_clock_ticks_per_sec = CLOCKS_PER_SEC;
	}

	OS_clock_ticks_per_sec_counted_flag = true;

#endif

}



/*==========================================================================*/
/*      Nom: OS_change_app_title                                            */
/*      Description: Change le titre de l'application (pour faire figurer   */
/*                   le nom du module par exemple).                         */
/*      Parametres en entree:                                               */
/*        - title_0: pointeur sur une chaine qui sera recopiee dans le      */
/*                   titre.                                                 */
/*==========================================================================*/

void	OS_change_app_title (const char *title_0)
{
	char		temp_0 [31+1];

	BASE_double_to_simple (temp_0, GTK_VERSION_NUMBER);
	
	strcpy (OS_window_title_0, title_0);
	strcat (OS_window_title_0, " - Graoumf Tracker v");
	strcat (OS_window_title_0, temp_0);

	SetWindowText (OS_window_handle, OS_window_title_0);
}



/*==========================================================================*/
/*      Nom:                                                                */
/*      Description:                                                        */
/*      Parametres en entree:                                               */
/*      Parametres en sortie:                                               */
/*      Parametres en entree/sortie:                                        */
/*      Retour:                                                             */
/*==========================================================================*/

signed int	OS_launch_html_file (const char *filename_0)
{

#if 0

/*______________________________________________
 *
 * Methode bourrine mais interessante
 *______________________________________________
 */

	int		ret_val;
	int		argc;
	long		arg_pos;
	HKEY		key;
	char		key_string_0 [OS_MAX_COMMAND_LINE_LEN+1];
	long		key_length;
	String	key_address;
	String	command;
	char		*argv [OS_MAX_COMMAND_LINE_ARGC+1];

	/* Recupere le type associe a l'extension .html ou assimile */
	if (RegOpenKey (HKEY_CLASSES_ROOT, ".html", &key) != ERROR_SUCCESS)
	{
		if (RegOpenKey (HKEY_CLASSES_ROOT, ".htm", &key) != ERROR_SUCCESS)
		{
			if (RegOpenKey (HKEY_CLASSES_ROOT, ".shtml", &key) != ERROR_SUCCESS)
			{
				LOG_printf ("OS_launch_html_file: Error: couldn't find \".html\" key in registry.\n");
				return (-1);
			}
		}
	}
	key_length = OS_MAX_COMMAND_LINE_LEN;
	ret_val = RegQueryValue (key, NULL, key_string_0, &key_length);
	RegCloseKey (key);
	if (ret_val != ERROR_SUCCESS)
	{
		LOG_printf ("OS_launch_html_file: Error: couldn't get \".html\" key value in registry.\n");
		return (-1);
	}

	/* Recupere la ligne de commande qui permet d'ouvrir un .html */
	key_address = String (key_string_0) + "\\shell\\open\\command";
	if (RegOpenKeyEx (HKEY_CLASSES_ROOT, key_address, 0, KEY_READ, &key) != ERROR_SUCCESS)
	{
		LOG_printf ("OS_launch_html_file: Error: couldn't find \"%s\" key.\n", (const char *)key_address);
		return (-1);
	}
	key_length = OS_MAX_COMMAND_LINE_LEN;
	ret_val = RegQueryValue (key, NULL, key_string_0, &key_length);
	RegCloseKey (key);
	if (ret_val != ERROR_SUCCESS)
	{
		LOG_printf ("OS_launch_html_file: Error: couldn't get \"%s\" key data.\n", (const char *)key_address);
		return (-1);
	}

	/* Remplace l'argument (%1) par sa valeur */
	command = String (key_string_0);
	arg_pos = command.find ('%');
	if (arg_pos < 0)
	{
		command += String (" \"") + filename_0 + "\"";
	}
	else
	{
		command =   command.left (arg_pos)
		          + filename_0
		          + command.after (arg_pos + 1);
	}

	/* Convertit la ligne de commande en arguments separes */
	strncpy (key_string_0, command, OS_MAX_COMMAND_LINE_LEN);
	key_string_0 [OS_MAX_COMMAND_LINE_LEN] = '\0';
	argc = BASE_parse_command_line (key_string_0, argv,
	                                OS_MAX_COMMAND_LINE_ARGC, NULL);

	/* Lance le browser */
	if (_spawnv (_P_NOWAIT, argv [0], argv) == -1)
	{
		LOG_printf ("OS_launch_html_file: Error: couldn't open html browser.\n");
		return (-1);
	}

#else

/*______________________________________________
 *
 * Methode quand meme plus simple...
 *______________________________________________
 */

	if ((char *) ShellExecute (NULL, "open", filename_0, NULL, ".", 0) - (char *) 0 <= 32)
	{
		LOG_printf ("OS_launch_html_file: Error: couldn't open html browser.\n");
		return (-1);
	}

#endif

	return (0);
}



/*==========================================================================*/
/*      Nom:                                                                */
/*      Description:                                                        */
/*      Parametres en entree:                                               */
/*      Parametres en sortie:                                               */
/*      Parametres en entree/sortie:                                        */
/*      Retour:                                                             */
/*==========================================================================*/

