/**************
FILE          : XProposal.ycp
***************
PROJECT       : YaST2
              :
AUTHOR        : Marcus Schäfer <ms@suse.de>
              :
BELONGS TO    : YaST2
              : (X11 integration part using SaX2/ISaX)
              :
DESCRIPTION   : Provide global workflow functions to create
              : the X11 configuration proposal. Providing validation
              : check functions additionally within this module
              : too
              :
STATUS        : Development
**************/
/**
 * File:        modules/XProposal.ycp
 * Package:     X11 Configuration
 * Summary:     Global function to generate/write X11 configuration
 * Authors:     Marcus Schaefer <ms@suse.de>
 *
 * $Id: XProposal.ycp 22717 2005-03-21 16:40:08Z ms $
 */
{
module "XProposal";

textdomain "x11";

import "Mode";
import "Stage";
import "Mouse";
import "Keyboard";
import "XMessages";
import "Bootloader";
import "Display";

//==========================================
// Module Globals...
//------------------------------------------
string  AutoInstallMapDisplayManager = "kdm";
string  AutoInstallMapWindowManager = "kde";
boolean configurationChanged = false;
boolean AutoInstallMap3D = true;
list    initResult = [];
global map		X11 = $[];

//==========================================
// Module Functions...
//------------------------------------------
//---[ isNVidiaCard ]----//
define boolean isNVidiaCard () ``{
	integer status = (integer) SCR::Execute ( .target.bash,
		"/usr/X11R6/" + Display::lib + "/sax/api/tools/getNVCard"
	);
	if (status == 1) {
		return (true);
	}
	return (false);
}

//---[ cleanSweep ]-----//
define void cleanSweep () ``{
	// ...
	// clean the system to be X11 config clean this
	// means no graphics system is available after
	// this function was called
	// ---
	// y2milestone ("cleanSweep()...");
	// ---
	SCR::Execute ( .target.bash,
		"/bin/rm -f /etc/X11/xorg.conf"
	);
	SCR::Execute ( .target.bash,
		"/bin/rm -f /var/cache/sax/files/*"
	);
	SCR::Execute ( .target.remove,
		"/var/X11R6/bin/X"
	);
}

//==========================================
// Global Variables...
//------------------------------------------
global boolean AutoInstallMapEnableX11 = true;
global boolean D3Active   = false;
global boolean HWFlag     = false;

//==========================================
// Global Functions...
//------------------------------------------
//---[ getSuggestion ]----//
global define list getSuggestion (boolean init) ``{
	// ...
	// The keyboard and mouse module hold the user input information
	// provided in an earlier step of the installation process.
	// Therefore we need to generate a special init.pl batch input
	// file containing the user selections about pointer and keyboard
	// information.
	// ---
	// After the batch input file is created we will call the SaX2
	// init module to create a YCP suggestion file and initialize all
	// other modules needed for reconfigure X11
	// ---
	y2milestone ("getSuggestion()...");

	string data = "";
	string suggestFile = "/var/cache/sax/files/suggest.ycp";
	string profileFile = "/var/cache/sax/files/userProfile";
	string userProfile = "";
	string userLine    = "";

	if (init) {
	if (Stage::cont ()) {
		//===========================================
		// get currently available mouse information
		//-------------------------------------------
		/*
		if (Mouse::mset != "") {
			userLine = "InputDevice->1->Option->Protocol = ";
			userLine = userLine + Mouse::mset;
			userProfile = userLine + "\n";
		}
		if (Mouse::device != "") {
			userLine = "InputDevice->1->Option->Device = ";
			userLine = userLine + Mouse::device;
			userProfile = userProfile + userLine + "\n";
		}
		if ( Mouse::wheels != 0 ) {
			integer x = Mouse::wheels + 3;
			integer y = x + 1;
			userLine = "InputDevice->1->Option->ZAxisMapping = ";
			userLine = userLine + x + " " + y;
			userProfile = userProfile + userLine + "\n";
			userLine = "InputDevice->1->Option->ButtonNumber = ";
			userLine = userLine + Mouse::buttons;
			userProfile = userProfile + userLine + "\n";
		}
		//===========================================
		// get currently available keyboard info
		//-------------------------------------------
		if (Keyboard::XkbLayout != "") {
			userLine = "InputDevice->0->Option->XkbLayout  = ";
			userLine = userLine + Keyboard::XkbLayout;
			userProfile = userProfile + userLine + "\n";
		}
		if (Keyboard::XkbModel != "") {
			userLine = "InputDevice->0->Option->XkbModel   = ";
			userLine = userLine + Keyboard::XkbModel;
			userProfile = userProfile + userLine + "\n";
		}
		if (Keyboard::Protocol != "") {
			userLine = "InputDevice->0->Option->Protocol   = ";
			userLine = userLine + Keyboard::Protocol;
			userProfile = userProfile + userLine + "\n";
		}
		if (Keyboard::XkbRules != "") {
			userLine = "InputDevice->0->Option->XkbRules   = ";
			userLine = userLine + Keyboard::XkbRules;
			userProfile = userProfile + userLine + "\n";
		}
		if (Keyboard::XkbOptions != "") {
			userLine = "InputDevice->0->Option->XkbOptions = ";
			userLine = userLine + Keyboard::XkbOptions;
			userProfile = userProfile + userLine + "\n";
		}
		if (Keyboard::XkbKeyCodes != "") {
			userLine = "InputDevice->0->Option->XkbKeyCodes= ";
			userLine = userLine + Keyboard::XkbKeyCodes;
			userProfile = userProfile + userLine + "\n";
		}
		if (Keyboard::LeftAlt != "") {
			userLine = "InputDevice->0->Option->LeftAlt    = ";
			userLine = userLine + Keyboard::LeftAlt;
			userProfile = userProfile + userLine + "\n";
		}
		if (Keyboard::RightAlt != "") {
			userLine = "InputDevice->0->Option->RightAlt   = ";
			userLine = userLine + Keyboard::RightAlt;
			userProfile = userProfile + userLine + "\n";
		}
		if (Keyboard::ScrollLock != "") {
			userLine = "InputDevice->0->Option->ScrollLock = ";
			userLine = userLine + Keyboard::ScrollLock;
			userProfile = userProfile + userLine + "\n";
		}
		if (Keyboard::RightCtl != "") {
			userLine = "InputDevice->0->Option->RightCtl   = ";
			userLine = userLine + Keyboard::RightCtl;
			userProfile = userProfile + userLine + "\n";
		}
		*/
		//===========================================
		// Include special information if the YaST2
		// auto install mode is active
		//-------------------------------------------
		if ( Mode::autoinst () ) {
			map data = X11;
			boolean configure_x11 = data["configure_x11"]:false;
			// ...
			// Check if X11 should be configured or not via
			// the auto install map data
			// ---
			if (! configure_x11) {
				AutoInstallMapEnableX11 = false;
			}
			// ...
			// handle the auto install map and write the
			// needed data to the user profile
			// ---
			AutoInstallMapDisplayManager = data["display_manager"]:"kdm";
			AutoInstallMapWindowManager = data["window_manager"]:"kde";
			if (! data["enable_3d"]:true) {
				AutoInstallMap3D = false;
			}
			map monitor = data["monitor"]:$[];
			map display = monitor["display"]:$[];
			// ...
			// set color depth from auto install map
			// ---
			userLine = "Screen->0->DefaultDepth = ";
			userLine = userLine + sformat("%1", data["color_depth"]:16 );
			userProfile = userProfile + userLine + "\n";
			// ...
			// set H/V-sync value from auto install map
			// ---
			userLine = "Monitor->0->HorizSync = ";
			userLine = userLine + sformat("%1", display["min_hsync"]:30 ) + "-";
			userLine = userLine + sformat("%1", display["max_hsync"]:60 );
			userProfile = userProfile + userLine + "\n";
			userLine = "Monitor->0->VertRefresh = ";
			userLine = userLine + sformat("%1", display["min_vsync"]:50 ) + "-";
			userLine = userLine + sformat("%1", display["max_vsync"]:65 );
			userProfile = userProfile + userLine + "\n";
			// ...
			// set monitor vendor/name string from
			// auto install map
			// ---
			userLine = "Monitor->0->VendorName = ";
			userLine = userLine + monitor["monitor_vendor"]:"Unknown";
			userProfile = userProfile + userLine + "\n";
			userLine = "Monitor->0->ModelName = ";
			userLine = userLine + monitor["monitor_device"]:"Unknown";
			userProfile = userProfile + userLine + "\n";
			// ...
			// set resolution from auto install map
			// ---
			userLine = "Screen->0->Depth->" + sformat("%1", data["color_depth"]:16 );
			userLine = userLine + "->Modes = ";
			userLine = userLine + data["resolution"]:"640x480";
			userProfile = userProfile + userLine + "\n";
		}
		//===========================================
		// write profile, call init.pl and read the
		// generated suggestion file
		//-------------------------------------------
		y2milestone ("UserProfile: <%1>",userProfile);
		SCR::Write(.target.string, profileFile, userProfile );
	}

	//===========================================
	// create the initString for init.pl call
	// ------------------------------------------
	boolean haveProfile = (
		SCR::Read(.target.size, profileFile)!=-1
	);
	SCR::Execute ( .target.bash,
		"/bin/rm -f " + suggestFile
	);
	string initCommand = "/usr/X11R6/" + Display::lib + "/sax/init.pl";
	string initString  = initCommand + " --suggest";
	if ( haveProfile ) {
		initString = initString + " -b " + profileFile;
	}

	//=================================================
	// We decided not to ask any 3D question
	//-------------------------------------------------
	initString = initString + " --quiet no";

	/*
	// ...
	// if YaST2 is running in [normal] textmode init.pl will eventually
	// print the 3D question on stdout. To prevent this we will implement
	// the 3D question in ycp and set the answer via the init.pl option
	// --quiet. To make sure the question won`t be printed.
	//
	// if init.pl don`t need to perform a hardware scan we are not
	// allowed to display the 3D question. To check this we will
	// call hwupdate --test and check for the existance of the
	// storage binary file "/var/cache/sax/files/config"
	// ---
	if ( (! Mode::autoinst ()) && (! Mode::live_eval) ) {
	map displayInfo = UI::GetDisplayInfo();
	if (displayInfo["TextMode"]:false) {
		string hwupdate = "/usr/X11R6/" + Display::lib +"/sax/tools/hwupdate";
		string config   = "/var/cache/sax/files/config";
		integer update  = SCR::Execute ( .target.bash,
			hwupdate + " --test"
		);
		boolean haveConfig = (SCR::Read(.target.size, config)!=-1);
		if ((update == 0) || (! haveConfig)) {
			// ...
			// ok init will call sysp and now we have to find
			// out if the 3D question will come up if sysp is
			// called
			// ---
			string D3File = "/var/cache/sax/files/3dname";
			string D3Message = XMessages::Warning3D;
			if ( isNVidiaCard() ) {
				D3Message = XMessages::WarningNVidia;
			}
			integer question = SCR::Execute ( .target.bash,
				"/usr/X11R6/" + Display::lib + "/sax/sysp.pl -s server -n > "
				+ D3File
			);
			if (question == 1) {
			string cardName = SCR::Read(.target.string, D3File );
			SCR::Execute ( .target.bash,
				"/bin/rm -f " + D3File
			);
			string topic = _("3D-Capable Card Detected: ");
			if ( UI::YesNoPopup ( sformat (
				_("%1 %2%3"),
				topic,cardName,D3Message
			))) {
				initString = initString + " --quiet yes";
			} else {
				initString = initString + " --quiet no";
			}
			}
		} else {
			initString = initString + " --quiet no";
		}
	}
	}
	*/
	// ...
	// I we are in auto install mode check for the enable_3d
	// flag and set the answer for the 3D question to init.pl
	// via the --quiet option
	// ---
	if ( Mode::autoinst () ) {
	if (AutoInstallMap3D) {
		initString = initString + " --quiet yes";
	} else {
		initString = initString + " --quiet no";
	}
	}
	// ...
	// call init.pl now and remove the profile
	// file after this call
	// ---
	integer initCode = (integer) SCR::Execute (
		.target.bash, initString
	);
	if ( haveProfile ) {
	SCR::Execute (
		.target.bash, "/bin/rm -f " + profileFile
	);
	}
	y2milestone (
		"initCode: <%1>",initCode
	);
	} // end of [if (init)]

	//===========================================
	// read suggest.ycp
	//-------------------------------------------
	initResult = (list)SCR::Read ( .target.ycp,
		suggestFile
	);
	//===========================================
	// call 3Ddiag-result to get 3D status
	//-------------------------------------------
	D3Active = false;
	integer D3Status = (integer) SCR::Execute (
		.target.bash, "/usr/bin/3Ddiag-result"
	);
	if (D3Status == 0) {
		D3Active = true;
	}
	return ( initResult );
}

//==========================================
// resetSuggestion...
//------------------------------------------
global define list resetSuggestion () ``{
	// ...
	// use ISaX to recreate the suggest.ycp file
	// from the currently installed configuration
	// file
	// ---
	string suggestFile   = "/var/cache/sax/files/suggest.ycp";
	boolean suggestExist = (SCR::Read(.target.size, suggestFile)!=-1);
	if ((! configurationChanged) && (suggestExist)) {
		return (getSuggestion (false));
	}
	list suggestion = [];
	list<string> moduleISaX = [
		"Card","Desktop","Mouse","Keyboard","Layout","Path"
	];
	string tmpdir = (string)SCR::Read( .target.tmpdir );
	foreach (string mod, moduleISaX,``{
		string modFile = tmpdir + "/" + mod;
		SCR::Execute ( .target.bash,
			"/usr/X11R6/" + Display::lib + "/sax/tools/isax -y -l "
			+ mod + ">" + modFile
		);
		map modResult = (map)SCR::Read ( .target.ycp,
			modFile
		);
		suggestion = add (
			suggestion,modResult
		);
	});
	if (suggestion != []) {
		SCR::Write(.target.ycp, suggestFile,
		suggestion
		);
		list suggest = getSuggestion (false);
		return ( suggest );
	}
}

//==========================================
// abortConfiguration...
//------------------------------------------
global define void abortConfiguration () ``{
	// ...
	// callback function if the user aborted the
	// dialog in this case we will switch back to the
	// YaST2 saved configuration file
	// ---
	// y2milestone ("abortConfiguration()...");
	// ---
	string configFile = "/etc/X11/xorg.conf";
	boolean configExist = (SCR::Read(.target.size, configFile)!=-1);
	if (configExist) {
		SCR::Execute ( .target.bash,
			"/bin/mv /etc/X11/xorg.conf.YaST2save /etc/X11/xorg.conf"
		);
	}
	// ...
	// if there was a hardware.chg file before startup
	// we should recreate the file because the user
	// aborted the configuration at this point
	// ---
	if (HWFlag) {
	SCR::Execute ( .target.bash,
		"/bin/touch /var/cache/sax/files/hardware.chg"
	);
	}
}

//==========================================
// setup System runlevel (inittab)...
//------------------------------------------
global define void setupRunlevel (integer level) ``{
	y2milestone ("setting default runlevel to %1",level);
	SCR::Write (.etc.inittab.id, sformat ("%1:initdefault:", level));
	SCR::Write (.etc.inittab, nil);
}

//==========================================
// concludeConfiguration...
//------------------------------------------
global define void concludeConfiguration (boolean X11Enabled) ``{
	// ...
	// callback function if the user selected the next button.
	// Ensure a valid configuration file has been written. If
	// the configuration suggestion was not modified we will save
	// the suggestion as final configuration. Otherwhise the X11
	// Manager has already done the job of saving the config file
	// ---
	// y2milestone ("concludeConfiguration()...");
	// ---
	// ... /
	// clean sweep if we don`t want to use X11
	// ---
	if (! X11Enabled) {
		setupRunlevel (3); cleanSweep (); return;
	}
	// ... /
	// save and link configuration if not already
	// done via xapi
	// ---
	string configFile = "/etc/X11/xorg.conf";
	boolean configExist = (SCR::Read(.target.size, configFile)!=-1);

	if (configurationChanged == false) {
	if ( (Stage::cont ()) || (! configExist) || (HWFlag) ) {
	SCR::Execute ( .target.bash,
		"/usr/X11R6/" + Display::lib + "/sax/xc.pl --auto"
	);
	}
	}
	// ... /
	// set displaymanager to use after reboot during the
	// installation process and if YaST2 is started in the
	// installed system
	// ---
	string displayManager = "kdm";
	string windowManager  = "kde";
	if ( Mode::autoinst () ) {
		displayManager = AutoInstallMapDisplayManager;
		windowManager  = AutoInstallMapWindowManager;
	}
	boolean needSysconfigUpdate = true;
	if ( ! Stage::cont () )
	{
		// ... /
		// we are within the installed SuSE Linux system
		// if no trusted displaymanager have been set
		// we need to update the displaymanager according
		// to the provided packages
		// ---
		string currentManager = (string) eval (SCR::Read(
			.sysconfig.displaymanager.DISPLAYMANAGER
		));
		if (
			((currentManager == "kdm")  && (Pkg::IsProvided ("kdebase3-kdm")))||
			((currentManager == "gdm2") && (Pkg::IsProvided ("gdm2"))) ||
			((currentManager == "xdm")  && (Pkg::IsProvided ("xorg-x11")))
		) {
			// Trusted displaymanager already set...
			needSysconfigUpdate = false;
		}
		if ( needSysconfigUpdate )
		if (Pkg::IsProvided ("kdebase3-kdm")) {
			// KDE is installed...
			displayManager = "kdm";
			windowManager = "kde";
		}
		else if (Pkg::IsProvided ("gdm2")) {
			// KDE is not installed but Gnome...
			displayManager = "gdm";
			windowManager = "gnome";
		} else {
			// KDE and Gnome are not installed...
			displayManager = "xdm";
			windowManager = "twm";
		}
	}
	// ... /
	// rewrite lilo.conf if lilocode.ycp exist
	// ---
	string liloCodeFile = "/var/cache/sax/files/lilocode.ycp";
	boolean updateNeeded = (SCR::Read (.target.size, liloCodeFile)!=-1);
	if (updateNeeded) {
		//=====================================
		// Change Bootloader configuration...
		// ------------------------------------
		list liloCode = (list)SCR::Read ( .target.ycp,
			liloCodeFile
		);
		string orig = Bootloader::getKernelParam ("LINUX_DEFAULT","vga");
		string code = liloCode[0]:orig;
		if (orig != code) {
			y2milestone ("Set fbdev console to mode: %1",code);
			Bootloader::setKernelParam ("LINUX_DEFAULT", "vga", code);
			Bootloader::Write();
		}
	}
	// ... /
	// Update the console Keyboard using the keyboard
	// module which could do this job
	// ---
	map KBDSection = initResult[3]:$[];
	map KBDCurrent = KBDSection[0]:$[];
	map KBDRecord  = KBDCurrent["Keyboard"]:$[];
	string KBDLayout = KBDRecord["XkbLayout"]:"us";
	Keyboard::X11SetLanguage (
		KBDLayout
	);

	// ... /
	// update sysconfig variables after reboot during
	// the installation process
	// ---
	if ( needSysconfigUpdate ) {
	if ( (! Stage::cont ()) || (Mode::autoinst ()) ) {
		SCR::Write(
			.sysconfig.displaymanager.DISPLAYMANAGER, displayManager
		);
		SCR::Write(
			.sysconfig.windowmanager.DEFAULT_WM, windowManager
		);
		SCR::Write(
			.sysconfig.displaymanager.DISPLAYMANAGER_STARTS_XSERVER, "yes"
		);
		SCR::Write(
			.sysconfig.displaymanager, nil
		);
		SCR::Write(
			.sysconfig.windowmanager, nil
		);
	}
	}
	Display::Save();

	// ... /
	// call hwupdate to be sure the config flags are set
	// the call of X11SetLanguage may change this flags
	// remind to have a look at the keyboard module to fix
	// the X11SetLanguage() call
	// ---
	string hwupdate = "/usr/X11R6/" + Display::lib +"/sax/tools/hwupdate";
	SCR::Execute ( .target.bash,
		hwupdate + " --all"
	);

	// ... /
	// remove the hardware.chg file if it still exist
	// because we have written a valid configuration now
	// and want to use this configuration for any reconfiguration
	// tasks in the future
	// ---
	SCR::Execute ( .target.bash,
		"/bin/rm -f /var/cache/sax/files/hardware.chg"
	);
}

//==========================================
// changeConfiguration...
//------------------------------------------
global define boolean changeConfiguration () ``{
	// ...
	// callback function if the user selected the change button.
	// run the SaX2 interface in this case
	// ---
	// y2milestone ("changeConfiguration()...");
	// ---
	configurationChanged = true;
	string xcopt = "--nointro";
	SCR::Execute ( .target.bash,
		"/bin/rm -f /var/cache/sax/files/reinit"
	);
	if (Stage::cont ()) {
		xcopt = xcopt + " -w 0";
	}
	integer code = 0;
	// ...
	// if there is no hardware.chg file we are able to read the
	// currently used system configuration with xc.pl. Therefore we
	// will pass the option --sysconfig to xc.pl.
	// Notes:
	// -----
	// 1) --sysconfig does not have any effect if we are running
	//    YaST2 from a graphics mode.
	//
	// 2) if no configuration file exist and we are running in
	//    in textmode xc.pl will ignore --sysconfig
	// ---
	if (! HWFlag) {
		xcopt = xcopt + " --sysconfig";
	}
	if (Stage::cont ()) {
		xcopt = xcopt + " --nocheckpacs";
	}
	if (Stage::cont ()) {
	code = (integer)SCR::Execute ( .target.bash,
	"/usr/X11R6/"+Display::lib+"/sax/xc.pl -y --fullscreen --noborder "+xcopt
	);
	} else {
	code = (integer)SCR::Execute ( .target.bash,
		"/usr/X11R6/"+ Display::lib + "/sax/xc.pl -y " + xcopt
	);
	}
	if (code == 0) {
		configurationChanged = false;
	}
	return (
	configurationChanged
	);
}

//==========================================
// setup YaST2 remote call behaviour...
//------------------------------------------
global define void setupRemoteControl () ``{
	// ...
	// if we are running a remote session in text mode
	// we had to disable the change button because this
	// will lead to a starting X-Server on the local machine
	// which is not usefull in this case
	// ---
	map displayInfo = UI::GetDisplayInfo();
	if (displayInfo["TextMode"]:false) {
	integer remote = (integer)SCR::Execute ( .target.bash,
		"/usr/X11R6/" + Display::lib + "/sax/tools/whois"
	);
	if (remote == 1) {
		UI::ChangeWidget (`id(`change), `Enabled, false );
		UI::ChangeWidget (`id(`reprobe),`Enabled, false );
	}
	}
}
}
