view mde/input/config.d @ 7:b544c3a7c9ca

Some changes to exceptions and a few more debug commands. committer: Diggory Hardy <diggory.hardy@gmail.com>
author Diggory Hardy <diggory.hardy@gmail.com>
date Wed, 16 Jan 2008 12:48:07 +0000
parents 9a990644948c
children f63f4f41a2dc
line wrap: on
line source

/// This module contains a class for holding configs and handles saving, loading and editing.
module mde.input.config;

debug import mde.text.format;

import mde.input.exception;

import mde.mergetag.read;
import mde.text.parse;

import tango.util.log.Log : Log, Logger;
import tango.util.collection.TreeBag : TreeBag;

Logger logger;
static this() {
    logger = Log.getLogger ("mde.input.config");
}

/** Class to hold the configuration for the input system. Thus loading and switching between
 *  multiple configurations should be easy.
 *
 * Class extends DataSection so that it can be loaded by mergetag easily.
 */
class Config : DataSection
{
    alias uint[] outQueue;		// This is the type for the out queue config data.
    /** Button event type bit-codes
     *
     *  These bitcodes are OR'd to the identifier code for the input device, to indicate which type
     *  of input they are for. E.g. when a key event is recieved with code x, look up
     *  $(_B _B.SDLKEY) | x in button. Keyboard events are SDL-specific since the codes may differ for other
     *  libraries.
     *
     *  For joystick hat events, a motion should be converted into up and down events on separate
     *  U,L,D,R positions and up and down events sent to the appropriate outputs.
     */
    enum B : uint {
        KEY		= 0x8000_0000u,		/// 0x8000_0000u
        SDLKEY		= 0x8800_0000u,		/// 0x8800_0000u
        MOUSE		= 0x4000_0000u,		/// 0x4000_0000u
        JOYBUTTON	= 0x2000_0000u,		/// 0x2000_0000u
        JOYHAT		= 0x1000_0000u,		/// 0x1000_0000u
        JOYHAT_U	= 0x1800_0000u,		/// 0x1800_0000u
        JOYHAT_D	= 0x1400_0000u,		/// 0x1400_0000u
        JOYHAT_L	= 0x1200_0000u,		/// 0x1200_0000u
        JOYHAT_R	= 0x1100_0000u,		/// 0x1100_0000u
    }
    
    /** Axis event type bit-codes
     *
     *  Well, SDL only supports one type of axis now, but this could be extended in the future.
    */
    enum A : uint {
        JOYAXIS		= 0x8000_0000u,		/// 0x8000_0000u
    }
    
    /** Mouse & Joystick ball event type bit-codes
     *
     *  Currently, mouse input only comes from the window manager: the code is exactly M.WMMOUSE.
     */
    enum M : uint {
        MOUSE		= 0x8000_0000u,		/// 0x8000_0000u
        WMMOUSE		= 0x8800_0000u,		/// 0x8800_0000u
        JOYBALL		= 0x4000_0000u,		/// 0x4000_0000u
    }
    
    /** Output queues: the core of the input configuration.
    *
    *  button, axis and mouse each have their own index specifications. This is split into two parts:
    *  the first byte specifies the type of input (given by the above enums), and the last three
    *  bytes define where the input comes from.
    *
    *  For B.SDLKEY, the last three bytes are for the SDL keysym.
    *  For B.MOUSE, B.JOY*, A.JOY* & M.JOY*, the last three bytes are split into two sets of 12
    *  bits (with masks 0x00FF_F000 and 0x0000_0FFF), the higher of which specifies the device
    *  (which mouse or joystick), and the lower of which specifies the button/axis/ball.
    *
    *  The code for mouse motion is currently only M.WMMOUSE. If/when multiple mice are supported
    *  new codes will be defined.
    */
    outQueue[uint] button;
    outQueue[uint] axis;	/// ditto
    outQueue[uint] mouse;	/// ditto
    debug uint dnbc;		// debug num button configs
    
    char[] name;		/// Name for user to save this under.
    uint[] inheritants;		/// Other profiles to inherit.
    
    // FIXME: using uint IDs really isn't nice...
    static Config[uint] configs;	/// All configs loaded by load().
    private static TreeBag!(char[]) loadedFiles;	// all filenames load tried to read
    
//BEGIN File loading/saving code
    static this () {
        loadedFiles = new TreeBag!(char[]);
    }
    
    // Load all configs from a file.
    static void load (char[] filename) {
        if (loadedFiles.contains (filename)) return;	// forget it; already done that
        loadedFiles.add (filename);
        Reader file;
        try {
            file = new Reader(filename, null, true);	// open and read header
            // TODO: also load user-config file
            
            file.dataSecCreator =
            	function DataSection (ID) {	return new Config;	};
            
            enum : ID { CONFIGS }
            ID[] configs;	// active config sections (may not exist)
            uint[]* configs_p = CONFIGS in file.dataset.header._uintA;
            
            if (configs_p)	file.read(cast(ID[]) *configs_p);	// restrict to this set IF a restriction was given
            else		file.read();		// otherwise read all
        }
        catch (MTException) {
            logger.error ("Unable to load configs from: " ~ filename);
            throw new ConfigLoadException;
        }
        // FIXME: don't override configs if not empty
        configs = cast (Config[uint]) file.dataset.sec;
        // NOTE: this is in some ways dangerous (assuming all DataSections are Configs), but they should be.
        debug {
            char tmp[128] = void;
            logger.info (logger.format (tmp, "Loaded {} config sections.", configs.length));
            foreach (id, cfg; configs) {
                logger.trace ("Section "~format!(uint)(id)~": " ~ format!(uint[][uint])(cfg.button) ~ " (" ~ format!(uint)(cfg.dnbc) ~ ")");
            }
        }
    }
    
    private enum QUEUE : ID { BUTTON, AXIS, MOUSE }
    private this() {}	// Private since this class should only be created from here.
    
    void addTag (char[] tp, ID id, char[] dt) {
        if (tp == "uint[][uint]") {
            if (id == QUEUE.BUTTON) {
                button = cast(outQueue[uint]) parse!(uint[][uint]) (dt);
                debug logger.trace ("Added button config: " ~ format!(uint[][uint])(button));
                debug ++dnbc;
            }
            else if (id == QUEUE.AXIS) axis = cast(outQueue[uint]) parse!(uint[][uint]) (dt);
            else if (id == QUEUE.MOUSE) mouse = cast(outQueue[uint]) parse!(uint[][uint]) (dt);
            else {
                char[80] tmp;
                logger.info (logger.format(tmp, "Unexpected tag encountered with ID {}", id));
            }
        } // FIXME: add support for name and inheritants.
        else throw new MTUnknownTypeException ("Input Config: only uint[][uint] type supported");
    }
    void writeAll (ItemDelg) {
        // FIXME
    }
    debug void debugFunc () {
        logger.trace ("After parse: " ~ format!(uint[][uint])(button) ~ " (" ~ format!(uint)(dnbc) ~ ")");
    }
//END File loading/saving code
}