Mercurial > projects > mde
view mde/input/Config.d @ 84:e0f1ec7fe73a
Merge plus a few tweaks.
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Sun, 31 Aug 2008 15:59:17 +0100 |
parents | ac1e3fd07275 |
children | 56c0ddd90193 |
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 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, see <http://www.gnu.org/licenses/>. */ /// This module contains a class for holding configs and handles saving, loading and editing. module mde.input.Config; import mde.input.exception; import MT = mde.file.mergetag.Reader; import mde.setup.paths; import mde.file.deserialize; debug import mde.file.serialize; 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; //BEGIN File loading/saving code static this () { logger = Log.getLogger ("mde.input.Config"); 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; }; // Restrict config sections if this tag exists: auto file_configs_p = cast(MT.ID)"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 e) { logger.fatal ("Unable to load configs from: " ~ filename ~ ":"); logger.fatal (e.msg); 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.error ("Ended up with DataSection of wrong type; this should never happen."); } debug (MDE_CONFIG_DUMP) { logger.trace ("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) ); } } } 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 == "B") button = cast(outQueue[][uint]) parseTo!(uint[][][uint]) (dt); else if (id == "A") axis = cast(outQueue[][uint]) parseTo!(uint[][][uint]) (dt); else if (id == "M") 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 }