view mde/input/config.d @ 17:5f90774ea1ef

Applied the GNU GPL v2 to mde. committer: Diggory Hardy <diggory.hardy@gmail.com>
author Diggory Hardy <diggory.hardy@gmail.com>
date Sat, 15 Mar 2008 15:14:25 +0000
parents 4608be19ebe2
children 838577503598
line wrap: on
line source

/* LICENSE BLOCK
Part of mde: a Modular D game-oriented Engine
Copyright © 2007-2008 Diggory Hardy

This program is free software; you can redistribute it and/or modify it under the terms of
the GNU General Public License, version 2, as published by the Free Software Foundation.

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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */

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

debug import tango.scrapple.text.convert.parseFrom : parseFrom;

import mde.input.exception;

import MT = mde.mergetag.Reader;
import mde.resource.paths;
import tango.scrapple.text.convert.parseTo : parseTo;

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

/** 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 : MT.IDataSection
{
    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 is converted into up and down events on separate U,L,D,R
     *  positions and up and down events sent to the appropriate outputs (effectively making four
     *  buttons, some pairs of which can be pressed simultaneously). For output to axes, see
     *  A.JOYHAT_* .
     */
    enum B : uint {
        KEY		= 0x8000_0000u,		/// 0x8000_0000
        SDLKEY		= 0x8800_0000u,		/// 0x8800_0000
        MOUSE		= 0x4000_0000u,		/// 0x4000_0000
        JOYBUTTON	= 0x2000_0000u,		/// 0x2000_0000
        JOYHAT		= 0x1000_0000u,		/// 0x1000_0000
        JOYHAT_U	= 0x1800_0000u,		/// 0x1800_0000
        JOYHAT_D	= 0x1400_0000u,		/// 0x1400_0000
        JOYHAT_L	= 0x1200_0000u,		/// 0x1200_0000
        JOYHAT_R	= 0x1100_0000u,		/// 0x1100_0000
    }
    
    /** Axis event type bit-codes
     *
     *  SDL only supports one type of axis now, but this could be extended in the future.
     *
     *  This can also be used to make joystick hats output to two axes (and can be used in
     *  conjuction with B.JOYHAT_* to output as buttons as well).
    */
    enum A : uint {
        JOYAXIS		= 0x8000_0000u,		/// 0x8000_0000
        JOYHAT		= 0x1000_0000u,		/// 0x1000_0000
        JOYHAT_LR	= 0x1300_0000u,		/// 0x1300_0000
        JOYHAT_UD	= 0x1C00_0000u,		/// 0x1C00_0000
    }
    
    /** 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_0000
        WMMOUSE		= 0x8800_0000u,		/// 0x8800_0000
        JOYBALL		= 0x4000_0000u,		/// 0x4000_0000
    }
    
    /** Output queues: the core of the input configuration.
    *
    *  These are all mapped with a uint key; upon any event outQueues are looked for in the
    *  appropriate associative array with a key as follows.
    *
    *  The index 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 type 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.
    *
    *  For all three types of output, the outQueues are used as follows. The first value in the queue is
    *  read, and a function is called with the event details dependant on this; most of the time an
    *  output function is called directly. Other functions may be used, however, to allow further
    *  functionality such as modifier keys, timed keys, and key sequences.
    *
    *  The output functions all have code 0x100; these read a single item from the outQueue which
    *  is the inputID the event outputs to (i.e. any callbacks at that ID called and status set for
    *  that ID).
    *
    */
    outQueue[][uint] button;
    outQueue[][uint] axis;	/// ditto
    outQueue[][uint] relMotion;	/// ditto
    
    // FIXME:
    char[] name;		/// Name for user to save this under.
    uint[] inheritants;		/// Other profiles to inherit.
    
    static Config[char[]] configs;	/// All configs loaded by load().
    private static TreeBag!(char[]) loadedFiles;	// all filenames load tried to read
    
    private static Logger logger;
    static this() {
        logger = Log.getLogger ("mde.input.config.Config");
    }
    
//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);
        
        MT.IReader file;
        
        try {
            // open and read header:
            file = confDir.makeMTReader (filename, PRIORITY.LOW_HIGH, null, true);
            
            file.dataSecCreator =
            delegate MT.IDataSection (MT.ID) {	return new Config;	};
            
            // D2.0: enum MT.ID CONFIGS = "Configs";
            const MT.ID CONFIGS = cast(MT.ID)"Configs";
            // Restrict config sections if this tag exists:
            auto file_configs_p = CONFIGS in file.dataset.header._charAA;
            MT.ID[] file_configs = null;
            if (file_configs_p) {
                file_configs = cast(MT.ID[]) *file_configs_p;
            }
            
            if (file_configs)	file.read(file_configs);	// restrict to this set IF a restriction was given
            else		file.read();			// otherwise read all
        }
        catch (MT.MTException) {
            logger.error ("Unable to load configs from: " ~ filename);
            throw new ConfigLoadException;
        }
        
        // Trying to directly cast dataset.sec to configs resulted in the Configs losing their data.
        // Also this is safer since it checks types (and must be done if configs wasn't previously empty).
        foreach (i, sec; file.dataset.sec) {
            Config c = cast(Config) sec;
            if (c) configs[i] = c;		// Check, because we don't want null entries in configs
            else debug logger.warn ("Ended up with DataSection of wrong type; this should never happen.");
        }
        
        debug {
            char tmp[128] = void;
            logger.trace (logger.format (tmp, "Loaded {} config sections.", configs.length));
            foreach (id, cfg; configs) {
                logger.trace ("Section " ~ id ~
                ":\n\tbutton:\t\t" ~ parseFrom!(uint[][][uint])(cfg.button) ~
                "\n\taxis:\t\t" ~ parseFrom!(uint[][][uint])(cfg.axis) ~
                "\n\trelMotion:\t" ~ parseFrom!(uint[][][uint])(cfg.relMotion) );
            }
        }
    }
    
    // D2.0: private enum QUEUE : MT.ID { BUTTON = "B", AXIS = "A", MOUSE = "M" }
    private struct QUEUE {
        static const MT.ID BUTTON = cast(MT.ID)"B", AXIS = cast(MT.ID)"A", MOUSE = cast(MT.ID)"M";
    }
    private this() {}	// Private since this class should only be created from here.
    
    void addTag (char[] tp, MT.ID id, char[] dt) {
        if (tp == "uint[][uint]") {
            if (id == QUEUE.BUTTON) button = cast(outQueue[][uint]) parseTo!(uint[][][uint]) (dt);
            else if (id == QUEUE.AXIS) axis = cast(outQueue[][uint]) parseTo!(uint[][][uint]) (dt);
            else if (id == QUEUE.MOUSE) relMotion = cast(outQueue[][uint]) parseTo!(uint[][][uint]) (dt);
            else logger.warn ("Unexpected tag encountered with ID " ~ cast(char[])id);
        } // FIXME: add support for name and inheritants.
    }
    void writeAll (ItemDelg) {
        // FIXME
    }
//END File loading/saving code
}