/**
 * File:	modules/Mouse.ycp
 * Package:	Mouse configuration
 * Summary:	Main module
 * Authors:	Marcus Schäfer <ms@suse.de>
 *
 * $Id: Mouse.ycp 22561 2005-03-15 10:00:51Z ms $
 * This module does all mouse related stuff:
 * Detect the mouse and mouse device
 * (create /dev/mouse)
 */

{

module "Mouse";
textdomain "mouse";

import "Arch";
import "Misc";
import "Mode";
import "Stage";
import "Linuxrc";
import "ModuleLoading";

//==========================================
// Globals accessed via Mouse::<variable>
//------------------------------------------
global string  mouse    = "none";	// current mouse
global string  mset	= "";		// x11 config values
global string  gpm	= "";		// gpm config values
global string  device	= "";		// mouse device
global boolean emul3	= false;	// emulate 3 buttons ?
global integer wheels	= 0;		// number of wheels
global integer buttons	= 0;		// number of buttons
global string  name     = "";       // user readable name
global string unique_key = "";		    // unique key

//==========================================
// Module globals
//------------------------------------------
boolean already_probed = false;	    // memorize if already probed

list<map> plist = [];                    // list got from last probing
map<string, list> mice = $[];
boolean first_proposal = true;

global define void Set( string mouse_id );

//==========================================
// Restore...
//------------------------------------------
global define boolean Restore()
    ``{
    // ...
    // Restore the the data from sysconfig.
    // ---
    mouse  = Misc::SysconfigRead(.sysconfig.mouse.YAST_MOUSE, mouse );
    device = Misc::SysconfigRead(.sysconfig.mouse.MOUSEDEVICE, device );
    gpm    = Misc::SysconfigRead(.sysconfig.mouse.MOUSETYPE, "" );
    if( gpm=="" )
	{
	// Try to read old variable for compatibility on update of old system
	gpm = Misc::SysconfigRead(.sysconfig.mouse.GPM_PROTOCOL, "" );
	}
    name   = Misc::SysconfigRead(.sysconfig.mouse.FULLNAME, name );
    emul3  = (Misc::SysconfigRead(.sysconfig.mouse.XEMU3,
				  (emul3 ? "yes" : "no") ) == "yes" );
    mset   = Misc::SysconfigRead(.sysconfig.mouse.XMOUSETYPE, mset );
    buttons = tointeger( Misc::SysconfigRead(.sysconfig.mouse.BUTTONS,
			 sformat("%1", buttons) ) );
    wheels = tointeger( Misc::SysconfigRead(.sysconfig.mouse.WHEELS,
			sformat("%1", wheels) ) );
    y2milestone("Restored data (sysconfig) for mouse: <%1>", mouse );
    return true;
    }

//==========================================
// Functions...
//------------------------------------------
global define void Mouse()
    ``{
    // ...
    // The module constructor.
    // Sets the proprietary module data defined globally for public access.
    // This is done only once (and automatically) when the module is
    // loaded for the first time.
    // ---
    y2milestone( "Stage::initial %1 Mode::config %2 Mode::reprobe %3 Mode::cont %4",
		 Stage::initial(), Mode::config(), Stage::reprobe(),
		 Stage::cont() );
    if( Stage::initial() || Mode::config() )
	{
	return;
	}
    // ...
    // Running system: Restore formerly stored state
    // from sysconfig.
    // ---
    Restore();
    if( size(mouse)>0 && Stage::cont() )
        {
        Set( mouse );
	}
    }

//==========================================
// do_really_probe...
//------------------------------------------
define list<map> do_really_probe( boolean manual )
    ``{
    // ...
    // Do a hardware probing of the attached mouse. Depending on the
    // parameter "manual" this is done by really probing the mouse
    // hardware or by just reading the libhd database. Real WH-probing
    // should not be called from within an x server because
    // in this case the probing will yield erroneous values. Use manual
    // in this case if possible.
    // ---
    y2milestone("Really probing");
    list<map> mouseprobelist = [];
    if( manual )
	{
	// libhd data lookup
	mouseprobelist = (list<map>)SCR::Read(.probe.mouse.manual);
	y2milestone( "Probed manual (no HW interception): <%1>",
		     mouseprobelist );
	if( mouseprobelist == nil ) mouseprobelist = [];
	if( mouseprobelist == [] )
	    {
	    // ...
	    // Data lookup not successful ==> Trying a real
	    // hardware probing as fallback
	    // ---
	    y2warning("Manual probing failed ==> Now trying a real HW Probing");
	    return( do_really_probe( false ));
	    }
	}
    else
	{
	// real hardware interception
	mouseprobelist = (list<map>)SCR::Read(.probe.mouse);
	y2milestone("Really probed with HW interception: <%1>", mouseprobelist);
	if( mouseprobelist == nil ) mouseprobelist = [];
	if( mouseprobelist != [] )
	    {
	    // ...
	    // Probing was successful ==>  Now probing has taken place
	    // ---
	    already_probed = true;
	    }
	}
    // ...
    // found a mouse ?
    //
    if( size( mouseprobelist ) > 0 )
	{
	// ...
	// get value from bus, select first mouse
	//
	map firstmouse = $[];
	integer idx=0;
	while( size(firstmouse)==0 && idx<size(mouseprobelist) )
	    {
	    map conf = (map)SCR::Read( .probe.status,
				       mouseprobelist[idx,"unique_key"]:"");
	    y2milestone( "key %1 conf %2",
			 mouseprobelist[idx,"unique_key"]:"", conf );
	    if( conf["available"]:`no == `yes )
		{
		firstmouse = mouseprobelist[idx]:$[];
		}
	    idx = idx + 1;
	    }
	device  = firstmouse["dev_name"]:"";
	unique_key = firstmouse["unique_key"]:"";
	string bus = firstmouse["bus"]:"";
	map mprotocol = firstmouse["mouse",0]:$[];	// "gpm" and "xf86"

	y2milestone( "mprotocol: <%1>", mprotocol );

	buttons = mprotocol["buttons"]:0;
	wheels  = mprotocol["wheels"]:0;
	gpm	= mprotocol["gpm"]:"";
	mset    = mprotocol["xf86"]:"";
	emul3   = mprotocol["emul3"]:(buttons<3);
	// ...
	// find mouse in database
	// ---
	mice = (map<string,list>)eval(SCR::Read (.target.yast2, "mouse_raw.ycp"));
	list<list> tl = maplist( string mouse_id, list mouse_data, mice,
				 ``{
				 mouse_data[1,"id"] = mouse_id;
				 return(mouse_data);
				 });
	tl = filter( list mouse_data, tl,
		     ``(mouse_data[1,"gpm"]:"" == gpm &&
		        mouse_data[1,"device"]:"" == device));
	y2milestone( "gpm %1 device %2 bus %3", gpm, device, bus );
	y2milestone( "tl = %1", tl );
	if( size(tl)>1 )
	    {
	    if( find( list md, tl, ``(md[1,"bus"]:"" == bus)) != nil )
		{
		tl = filter( list md, tl, ``(md[1,"bus"]:"" == bus));
		y2milestone( "tl = %1", tl );
		}
	    }
	mouse = tl[0,1,"id"]:"none";
	y2milestone( "found mouse %1", mouse );
	} // mprotocol found

    if( mouse == "none" )
	{
	y2warning("No mouse found, probed '%1'", mouseprobelist);
	}
    y2milestone ("Mouse::Probe %1", mouse);
    y2milestone ("unique_key %1", unique_key );
    return mouseprobelist;
    }
//==========================================
// Probe...
//------------------------------------------
global define string Probe()
    ``{
    // ...
    // Probe for mouse, return mouse_id for use with Set.
    // This is a "real" probe only under certain circunstances...
    // ---
    mouse = "none";
    // ...
    // Don't expect a mouse with serial console.
    //
    if( Linuxrc::serial_console() || Arch::s390 () )
	{
	return mouse;
	}
    // ...
    // During installation actually do probe only if called the
    // first time. Afterwards only read the libhd data base.
    // Probing in the running system (under X11) currently doesn't
    // work.
    // ---
    if( Stage::initial() )
	{
	if( already_probed )
	    {
	    // already probed
	    y2milestone("Initial: manual probing");
	    plist = do_really_probe( true );
	    }
	else
	    {
	    // not yet probed
	    y2milestone("Initial: real HW-probing");
	    plist = do_really_probe( false );
	    }
	}
    else if( Stage::reprobe() )
	{
	// reprobe
	y2milestone("Reprobe: real HW-probing");
	plist = do_really_probe ( false );
	}
    else
	{
	// ...
	// When called from within the running system we can safely read
	// the libhd database to avoid erroneous HW-probing under Y11.
	// ---
	y2milestone("Running system: manual probing");
	plist = do_really_probe( true );
	}
    y2milestone( "plist %1", plist );
    return mouse;
    }

//==========================================
// SetXMouse...
//------------------------------------------
global define void SetXMouse()
    ``{
    // ...
    // SetXMouse() -  formerly set_x_mouse.ycp
    // ---
    string do_emul3 = (emul3)?"on":"off";
    y2milestone("SetXMouse: Mouse params: %1:%2:%3", device, mset, do_emul3);
    // ...
    // mset
    //	--apply	<realdevice>:			// #1
    //		<protocol>:			        // #2
    //		<baudrate>:			        // #3
    //		<samplerate>:			    // #4
    //		<resolution>:			    // #5
    //		<buttons>:			        // #6
    //		<emul3 {on|off} >:		    // #7
    //		<emul3timeout{num}>:		// #8
    //		<chordmiddle{on|off}>:		// #9
    //		<{reopen}>			        // #10
    //
    //			     1      2    3    4 5 6  7  8   9
    // Example:   mset --apply /dev/psaux:PS/2:1200:60:0:3:on:50:reopen
    // ---
	string mset_command = sformat ( "/usr/X11R6/bin/xmset --apply %1:%2:1200:60:0:3:%3:50:off:reopen", device, mset, do_emul3 );
    if ( Mode::test() )
	{
	y2milestone ("Test mode - NOT setting X mouse:\n %1", mset_command );
	}
    else
	{
	if( Linuxrc::text() )
	    {
	    y2milestone("Not setting X mouse due to text mode");
	    }
	else if( SCR::Read( .target.size, "/usr/X11R6/bin/xmset" )<=0 )
	    {
	    y2milestone("Not setting X mouse due to missing xmset binary");
	    }
	else
	    {
	    y2milestone( "Setting X mouse: %1", mset_command );
	    SCR::Execute( .target.bash, mset_command, $["DISPLAY" : ":0.0"] );
	    }
	}
    }
//==========================================
// Set...
//------------------------------------------
global define void Set( string mouse_id )
    ``{
    // ...
    // Set system to selected mouse.
    // Load modules, set global variables, call xmset.
    // ---
    y2milestone ("Mouse::Set (%1)", mouse_id);
    if( ((mouse_id == "19_usb") || (mouse_id == "23_exps2") ||
	 (mouse_id == "21_imps2"))
	 && ! Mode::config())
	{
	if (Mode::test())
	    {
	    y2milestone ("Testmode - not loading modules");
	    }
	else
	    {
	    y2milestone ("Loading USB modules");
	    /*
	    // the modules input and mousedev are now compiled into the kernel
	    ModuleLoading::Load( "input", "", "USB", "input", Linuxrc::manual(),
	                         true);
	    */
	    ModuleLoading::Load( "usbhid", "", "USB", "human interface device",
	                         Linuxrc::manual(), true );
	    /*
	    // the modules input and mousedev are now compiled into the kernel
	    ModuleLoading::Load( "mousedev", "", "USB", "mouse", 
	                         Linuxrc::manual(), true );
	    */
	    }
	}
    // ...
    // Get mouse data base for possible retranslation.
    // ---
    mice = (map<string, list>)eval(SCR::Read ( .target.yast2, ["mouse_raw.ycp", $[]] ));
    locale translate = mice[mouse_id, 0]:mouse_id;
    name = (string) eval (translate);
    y2milestone ("Mouse '%1', name '%2'", mouse_id, name);
    // ...
    // Overwrite perhaps probed data only if the
    // mouse could be found in the DB.
    //
    if( name != "" )
	{
	map mouse_data = mice[mouse_id, 1]:$[];
	device	= mouse_data["device"]:"";
	gpm	= mouse_data["gpm"]:"";
	mset	= mouse_data["mset"]:"";
	emul3	= mouse_data["emul3"]:false;
	wheels	= mouse_data["wheels"]:0;
	}

    mouse = mouse_id;

    if( !Mode::config() && device!="" )
	{
	SetXMouse();
	}
    return;
    }
//==========================================
// MakeProposal...
//------------------------------------------
global define string MakeProposal( boolean force_reset,
                                   boolean language_changed)
   ``{
    // ...
    // Return proposal string and set system mouse.
    // ---
    y2milestone("MakeProposal force_reset: %1 language_changed: %2", 
                force_reset, language_changed );
    string n = "";

    if( size(mice)==0 || first_proposal || language_changed )
	{
	mice = (map<string, list>)eval(SCR::Read( .target.yast2, ["mouse_raw.ycp", $[]] ));
	}
    first_proposal = false;

    if( force_reset )
	{
	string mouse_id = Probe();
	Set( mouse_id );
	}
    n = mice[mouse, 0]:mouse;
    y2milestone("MakeProposal ret: %1", n );
    return n;
    }



//==========================================
// Found...
//------------------------------------------
global define boolean Found ()
    ``{
    // ...
    // Report if a mouse was alread found.
    // ---
    return ( mouse != "none" );
    }

//==========================================
// Selection...
//------------------------------------------
global define map<string, string> Selection()
    ``{
    // ...
    // Return a map of ids and names to build up a selection list
    // for the user. The key is used later in the Set function
    // to select this mouse. The name is a translated string.
    // ---
    // try translated mouse.ycp first, if this doesnt exist
    // use the raw (untranslated) version
    // ---
    locale translate = "";
    string mouse_name = "";
    mice = (map<string, list>)eval(SCR::Read ( .target.yast2, ["mouse_raw.ycp", $[]] ));
    map<string, string> selection =  mapmap( string mouse_code, list mouse_value, mice,
	``{
	translate = mouse_value[0]:"";
	mouse_name = (string)eval(translate);
	return $[mouse_code: mouse_name];
	});
    if (Mode::config())
	{
	// label text
	selection["probe"] = _("Probe");
	}
    return selection;
    }

/**
 * create_info_map()
 *
 * Creates a YCP map containing all the data necessary for writing the mouse section
 * of the rc.sax file via the isax agent.
 *
 * @param	-
 *
 * @return  The YCP map containing the mouse information to be passed to the isax agent.
 *
 * @see	update()
 */

global define map create_info_map()
    ``{
    // mouse protocol
    //
    string mouse_prot = Mouse::mset;

    if ( mouse_prot == "" || Mouse::mouse == "non" )	// if no mouse attached
	mouse_prot = "AUTO";

    // mouse device
    //
    //string mouse_device = Mouse::device;
    //
    //if ( mouse_device == "" || Mouse::mouse == "non" )	// if no mouse attached
    //mouse_device = "/dev/mouse";
    //
    // According to ms@suse.de it is necessary to write the mouse device _link_ into
    // the XF86Config to allow the mouse reconfiguration from within the running system.
    // Applying the mouse via xmset does only work if this is the case. The X11 misc extension
    // used by xmset might be extended some day to allow apllying the mouse even if the
    // device in XF86Config is not a link bur until then...
    // Therefore we _always_ write "/dev/mouse" here for now.
    //
    string mouse_device = "/dev/mouse";

    // mouse wheels
    //
    integer mouse_wheels = Mouse::wheels;

    // raw buttons without wheel buttons. Only XFree 4.
    //
    integer mouse_buttons = Mouse::buttons;

    // including wheel buttons
    //
    string mouse_buttons_str = "" + ( mouse_buttons + ( 2 * mouse_wheels ) );

    // try to use a wheel if present
    //
    string mouse_opt = "";

    if ( mouse_wheels >= 1 )			// we do have a wheel mouse
	mouse_opt = "ZAxisMapping:4 5";		// can be used "as is" for XFree 3

    // "emulate 3 buttons" is rather clumsy
    //
    string mouse_emu3btn = "";

    if ( Mouse::emul3 && mouse_buttons < 3 )	// emulate 3 buttons
	{
	mouse_emu3btn = "yes";
	}
    else	// don't emulate 3 buttons
	{
	mouse_emu3btn = "no";
	}

    // Create the info map for the isax-agent.
    //
    map info = $[
		 "MOUSEDEVICE"    : [ mouse_device, "Mouse device"],
		 "MOUSEPROT"      : [ mouse_prot, "Mouse protocol"],
		 "EMU3BTN"        : [ mouse_emu3btn, "Mouse 3 button emulation"],
		 "MOUSEOPT"       : [ mouse_opt, "Mouse options"],
		 "MOUSEBUTTONS"   : [ mouse_buttons_str, "Number of btns incl. wheel btns"]
    ];

    return( info );
    }


/**
 * update()
 *
 * Updates the mouse settings in the XF86Config file.
 *
 * !!! only XFree 4 !!!
 *
 *
 *
 * @param
 *
 * @return  true	- success
 *		false	- failure
 *
 * @see	create_info_map()
 */

global define boolean xf86_update()
    ``{
    // Create the map for the isax agent.
    //
    map info = create_info_map();

    y2milestone( "mouse_info: <%1>", info );

    // Call the isax agent to update the mouse settings in the XF86Config file.
    //
    boolean ret = (boolean)SCR::Write( .xf86config.mouse, info );

    return( ret );
    }


//==========================================
// Save...
//------------------------------------------
global define void Save()
    ``{
    // ...
    // Save state to target.
    // ---
    if( Mode::update() )
	{
	return;
	}
    y2milestone( "device %1 mouse %2 reprobe:%3", device, mouse, Stage::reprobe() );
    if( (device != "") || mouse=="none" || mouse=="ignore" || Stage::reprobe() )
	{
	// if we have a mouse device, set gpm_param

	SCR::Write( .sysconfig.mouse.FULLNAME, name);
	SCR::Write( .sysconfig.mouse.FULLNAME.comment,
		    "\n# The full name of the attached mouse.\n#\n" );
	SCR::Write( .sysconfig.mouse.YAST_MOUSE, mouse);
	SCR::Write( .sysconfig.mouse.YAST_MOUSE.comment,
		    "\n# The YaST-internal identifier of the attached mouse.\n#\n");
	SCR::Write( .sysconfig.mouse.MOUSEDEVICE, device);
	// Comment written by third party
	SCR::Write( .sysconfig.mouse.XMOUSEDEVICE,device);
	SCR::Write( .sysconfig.mouse.XMOUSEDEVICE.comment,
		    "\n# Mouse device used for the X11 system.\n#\n" );
	SCR::Write( .sysconfig.mouse.BUTTONS, sformat( "%1", buttons ) );
	SCR::Write( .sysconfig.mouse.BUTTONS.comment,
		    "\n# The number of buttons of the attached mouse.\n#\n" );
	SCR::Write( .sysconfig.mouse.WHEELS, sformat( "%1", wheels ) );
	SCR::Write( .sysconfig.mouse.WHEELS.comment,
		    "\n# The number of wheels of the attached mouse.\n#\n" );
	if (mset != "")
	    {
	    SCR::Write( .sysconfig.mouse.XEMU3, (emul3?"yes":"no"));
	    SCR::Write( .sysconfig.mouse.XEMU3.comment,
			"\n# Shall the emulate-3-buttons-mode be turned on under X11?\n#\n" );
	    SCR::Write( .sysconfig.mouse.XMOUSETYPE, mset);
	    SCR::Write( .sysconfig.mouse.XMOUSETYPE.comment,
			"\n# The mouse type under X11, e.g. \"ps/2\"\n#\n" );
	    SCR::Write( .sysconfig.mouse.MOUSETYPE, gpm);
	    SCR::Write( .sysconfig.mouse.MOUSETYPE.comment,
			"\n# The GPM mouse type, e.g. \"ps2\"\n#\n" );
	    }
	SCR::Write(.sysconfig.mouse, nil); // flush
	y2milestone("Saved sysconfig data for mouse: <%1>", name );
	}
    // ...
    // Only if the mouse has been probed in this run the unique_key
    // is not empty. Only in this case mark the device as "configured".
    // In any other case the device should already be configured and
    // the marking can't be done because the unique_key is missing.
    // ==> Only mark after probing!
    //
    y2milestone( "configured mouse key %1", unique_key );
    if( unique_key != "" )
	{
	SCR::Write( .probe.status.configured, unique_key, `yes );
	y2milestone("Marked mouse <%1> as configured", unique_key );
	if( !Linuxrc::serial_console() )
	    {
	    SCR::Write( .probe.status.needed, unique_key, `yes );
	    y2milestone("Marked mouse <%1> as needed", unique_key );
	    }
	}
    if( Stage::initial() || Stage::reprobe() )
	{
	foreach( map e, plist, ``{
	    y2milestone( "unique_key %2 entry %1", e, unique_key );
	    if( e["unique_key"]:"" != unique_key )
		{
		y2milestone( "set needed to no for key %1", e["unique_key"]:"" );
		SCR::Write( .probe.status.needed, e["unique_key"]:"", `no );
		}
	    });
	}

    // ...
    // Change the respective XF86Config content to match the current settings.
    // Do this only in the running system _and_ if XFree4 is installed.
    // ---
    if( Mode::normal() || Stage::reprobe() )
	{
	y2milestone ( "Storing settings into XF86Config file (XFree4).");
	xf86_update();
	}
    y2milestone("Saved data for mouse: <%1>", name );
    return;
    }


/* EOF */
}
