Mercurial > projects > mde
changeset 9:1885a9080f2a
Joystick button input now works with config.
committer: Diggory Hardy <diggory.hardy@gmail.com>
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Wed, 30 Jan 2008 11:33:56 +0000 |
parents | f63f4f41a2dc |
children | 4c3575400769 |
files | conf/input.mtt doc/jobs mde/init.d mde/input/config.d mde/input/input.d mde/input/joystick.d mde/mde.d mde/mergetag/dataset.d mde/mergetag/defaultdata.d mde/mergetag/read.d |
diffstat | 10 files changed, 307 insertions(+), 75 deletions(-) [+] |
line wrap: on
line diff
--- a/conf/input.mtt Fri Jan 25 18:17:38 2008 +0000 +++ b/conf/input.mtt Wed Jan 30 11:33:56 2008 +0000 @@ -1,4 +1,4 @@ {MT01} -!<string[]|Configs=["Std"]> +<char[][]|Configs=["Std"]> {Default} -<uint[][uint]|B=[0x20000000 : [0x100, 3] ]> +<uint[][uint]|B=[0x20000000 : [0x100, 3], 0x20000001 : [0x100, 5] ]>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/jobs Wed Jan 30 11:33:56 2008 +0000 @@ -0,0 +1,9 @@ +In progress: +* Why doesn't input.config filtering via headers "Configs" work? + +To do: +* change init threads (should catch own exceptions) +* finish event callback support +* add remaining SDL event support +* add options support; in particular for whether or not to use threads (and adjust Init to use this). +* OutOfMemoryException is not currently checked for − it should be at least in critical places (use high-level catching of all errors?).
--- a/mde/init.d Fri Jan 25 18:17:38 2008 +0000 +++ b/mde/init.d Wed Jan 30 11:33:56 2008 +0000 @@ -87,8 +87,8 @@ return; } logger.info ("Derelict: loaded SDL"); - - if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_JOYSTICK)) { + + if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_JOYSTICK /+| SDL_INIT_EVENTTHREAD+/)) { logger.fatal ("SDL initialisation failed:"); char* msg = SDL_GetError (); logger.fatal (msg ? fromUtf8z(msg) : "no reason available"); @@ -97,7 +97,7 @@ return; } - SDL_SetVideoMode (800, 600, 32, 0); + SDL_SetVideoMode (800, 600, 0, 0); addCleanupFct (&cleanupSDL); logger.info ("SDL initialised");
--- a/mde/input/config.d Fri Jan 25 18:17:38 2008 +0000 +++ b/mde/input/config.d Wed Jan 30 11:33:56 2008 +0000 @@ -81,7 +81,6 @@ 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. @@ -98,7 +97,9 @@ static void load (char[] filename) { if (loadedFiles.contains (filename)) return; // forget it; already done that loadedFiles.add (filename); + MT.Reader file; + try { file = new MT.Reader(filename, null, true); // open and read header // TODO: also load user-config file @@ -108,33 +109,30 @@ // D2.0: enum MT.ID CONFIGS = "Configs"; const MT.ID CONFIGS = cast(MT.ID)"Configs"; - MT.ID[] configs; // active config sections (may not exist) - MT.ID[]* configs_p = cast(MT.ID[]*) (CONFIGS in file.dataset.header._stringA); + MT.ID[] file_configs; // active config sections (may not exist) + MT.ID[]* file_configs_p = cast(MT.ID[]*) (CONFIGS in file.dataset.header._charAA); - if (configs_p) file.read(*configs_p); // restrict to this set IF a restriction was given - else file.read(); // otherwise read all + if (file_configs_p) file.read(*file_configs_p); // 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; } - // NOTE: It is in some ways a bad idea assuming all DataSections are Configs, but they should be. - if (!configs) { // these are the first Configs loaded - configs = cast (Config[char[]]) file.dataset.sec; - } else { // add to existing Configs, replacing ones with same ID - 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."); - } + // 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 "~format!(uint)(id)~": " ~ format!(uint[][uint])(cfg.button) ~ " (" ~ format!(uint)(cfg.dnbc) ~ ")"); + logger.trace ("Section "~id~": " ~ format!(uint[][uint])(cfg.button)); } } } @@ -147,11 +145,7 @@ void addTag (char[] tp, MT.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; - } + if (id == QUEUE.BUTTON) button = cast(outQueue[uint]) parse!(uint[][uint]) (dt); 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 logger.warn ("Unexpected tag encountered with ID " ~ cast(char[])id); @@ -161,8 +155,5 @@ void writeAll (ItemDelg) { // FIXME } - debug void debugFunc () { - logger.trace ("After parse: " ~ format!(uint[][uint])(button) ~ " (" ~ format!(uint)(dnbc) ~ ")"); - } //END File loading/saving code }
--- a/mde/input/input.d Fri Jan 25 18:17:38 2008 +0000 +++ b/mde/input/input.d Wed Jan 30 11:33:56 2008 +0000 @@ -13,13 +13,9 @@ import tango.util.log.Log : Log, Logger; -Input input; /// Global instance of input. -private Logger logger; - +public Input input; /// Global instance of input. static this() { - input = new Input; - - logger = Log.getLogger ("mde.input.config"); + input = new Input(); } /// Class encapsulating all input functionality. @@ -35,7 +31,8 @@ * * Returns: value (true = down, false = up) or false if no value at this ID. */ bool getButton (inputID id) { - bool* retp = cast(inputID) id in button; + assert (this is input); + bool* retp = id in button; if (retp) return *retp; else return false; } @@ -43,7 +40,7 @@ * * Returns: value (range -1.0 .. 1.0) or 0.0 if no value at this ID. */ real getAxis (inputID id) { - real* retp = cast(inputID) id in axis; + real* retp = id in axis; if (retp) return *retp; else return 0.0; } @@ -65,7 +62,7 @@ * positions. */ void mouseRelativePos (inputID id, out real x = 0.0, out real y = 0.0) { - RelPair* rp = cast(inputID) id in axis_rel; + RelPair* rp = id in axis_rel; if (rp) { x = rp.x; y = rp.y; } @@ -104,17 +101,14 @@ * Other types of event functions may be added. Returns true if the event was used, false if not. */ bool opCall (ref SDL_Event event) { + assert (this is input); switch (event.type) { case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: - debug { - char tmp[128] = void; - logger.trace (logger.format (tmp, "Got a joystick button event: ({}, {}) - {}", event.jbutton.which, event.jbutton.button, event.jbutton.state)); - } outQueue* p = (Config.B.JOYBUTTON | (event.jbutton.which << 12) | event.jbutton.button) in config.button; if (p) bEventOut (event.jbutton.state == 0x1, readOutQueue(*p)); break; - + /+ case SDL_KEYDOWN: case SDL_KEYUP: @@ -154,7 +148,6 @@ if (c_p) { config = *c_p; return false; - debug logger.trace ("Succesfully loaded config."); } debug logger.warn ("Config \"Default\" not found."); return true; @@ -164,6 +157,8 @@ // Static constructor for event stream (fills es_*_fcts tables). static this () { es_b_fcts = [ ES_B_OUT : &es_b_out ]; + + logger = Log.getLogger ("mde.input.input.Input"); } struct RelPair { // for mouse/joystick ball motion @@ -175,6 +170,8 @@ } } + static Logger logger; + Config config; // Configuration bool[inputID] button; // Table of button states @@ -213,19 +210,24 @@ ret._q = q; return ret; } - uint next () { // Get the next element. Throws an exception if there isn't another. + uint pop () { // Get the next element and advance. Throws an exception if there isn't another. if (p >= _q.length) throw new InputClassException ("Input: Invalid configuration: incomplete config stack"); uint ret = _q[p]; ++p; return ret; } + debug uint next () { // Get the next element. No checks; for debug use only. + return _q[p]; + } } // These aliases are for pointers to the event functions. - alias void function (bool, readOutQueue) ES_B_Func; - alias void function (short, readOutQueue) ES_A_Func; - alias void function (short, short, readOutQueue) ES_M_Func; + // These need to use "this", but cannot be delegates because they musn't be bound to a + // particular instance of Input, hence this must be passed when called. + alias void function (Input, bool, readOutQueue) ES_B_Func; + alias void function (Input, short, readOutQueue) ES_A_Func; + alias void function (Input, short, short, readOutQueue) ES_M_Func; // These are the codes allowing the config to specify event functions: enum : uint { @@ -244,44 +246,49 @@ //BEGIN ES Functions // These 3 functions pass an event to the appropriate event function (adjuster or output func). // They are used to start and continue an event stream. + const EVCONF_ERR = "Input: Invalid configuration: bad event function code"; void bEventOut (bool b, readOutQueue s) { - ES_B_Func* func = (s.next() in es_b_fcts); - if (func != null) (*func)(b,s); - else throw new InputClassException ("Input: Invalid configuration: bad event function code"); + ES_B_Func* func = (s.pop() in es_b_fcts); + if (func != null) (*func)(this, b, s); + else throw new InputClassException (EVCONF_ERR); } void aEventOut (short x, readOutQueue s) { - ES_A_Func* func = (s.next() in es_a_fcts); - if (func != null) (*func)(x,s); - else throw new InputClassException ("Input: Invalid configuration: bad event function code"); + ES_A_Func* func = (s.pop() in es_a_fcts); + if (func != null) (*func)(this, x, s); + else throw new InputClassException (EVCONF_ERR); } void mEventOut (short x, short y, readOutQueue s) { - ES_M_Func* func = (s.next() in es_m_fcts); - if (func != null) (*func)(x,y,s); - else throw new InputClassException ("Input: Invalid configuration: bad event function code"); + ES_M_Func* func = (s.pop() in es_m_fcts); + if (func != null) (*func)(this, x, y, s); + else throw new InputClassException (EVCONF_ERR); } // The remaining functions are the stream functions, for adjusting and outputting an event. + // They need to work like non-static functions, but are called via a function pointer, hencne + // should be static with their first parameter being instead of this. // Simple output function - void es_b_out (bool b, readOutQueue s) { - inputID id = cast(inputID) s.next(); - button[id] = b; - ButtonCallback* cb_p = id in buttonCallbacks; + static void es_b_out (Input myThis, bool b, readOutQueue s) { + inputID id = cast(inputID) s.pop(); + + myThis.button[id] = b; + + ButtonCallback* cb_p = id in myThis.buttonCallbacks; if (cb_p) (*cb_p) (id, b); } // Adjuster to check modifier keys - void es_b_modifier (bool b, readOutQueue s); + void es_b_modifier (Input myThis, bool b, readOutQueue s); /* Simple output function Adds 1-2 items on the stack. */ - void es_a_out (short x, readOutQueue s) { + void es_a_out (Input myThis, short x, readOutQueue s) { real y = x; - uint conf = s.next(); + uint conf = s.pop(); enum : uint { HALF_RANGE = 0x8000_0000u, SENSITIVITY = 0x0080_0000u, @@ -290,16 +297,16 @@ if (conf & HALF_RANGE) y = (y + 32767.0) * 1.5259254737998596e-05; // range 0.0 - 1.0 else y *= 3.0518509475997192e-05; // range -1.0 - 1.0 real a; - if (conf & SENSITIVITY) a = s.next(); + if (conf & SENSITIVITY) a = s.pop(); /+ When a global sensitivity is available (possibly only use if it's enabled)... else a = axis.sensitivity; y = sign(y) * pow(abs(y), a); // sensitivity adjustment by a +/ - axis[cast(inputID) s.next()] = y; + myThis.axis[cast(inputID) s.pop()] = y; } // Simple output function - void es_m_out (short x, short y, readOutQueue s) { - axis_rel[cast(inputID) s.next()] = RelPair(x,y); + void es_m_out (Input myThis, short x, short y, readOutQueue s) { + myThis.axis_rel[cast(inputID) s.pop()] = RelPair(x,y); } //END ES Functions //END Event stream functionality
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mde/input/joystick.d Wed Jan 30 11:33:56 2008 +0000 @@ -0,0 +1,41 @@ +/** Opens SDL joysticks ready for use. +* May be extended later to include other input devices and remap devices as per config. +*/ +module mde.input.joystick; + +import tango.util.log.Log : Log, Logger; + +import derelict.sdl.joystick; + +private Logger logger; +static this() { + logger = Log.getLogger ("mde.input.config"); +} +private SDL_Joystick*[] joysticks; // pointers to all joystick structs, whether successfully opened or not + +/** Open joysticks ready for use. +* +* This is simply required for SDL to handle joystick events. It can fail, but won't affect anything +* else, except for the controller not working. +* +* closeJoysticks must be run to cleanup afterwards. +*/ +void openJoysticks () { + joysticks = new SDL_Joystick*[SDL_NumJoysticks ()]; + char tmp[128] = void; + + for (int i = 0; i < joysticks.length; ++i) { + if ((joysticks[i] = SDL_JoystickOpen (i)) is null) { // null on failure + logger.warn (logger.format (tmp, "Unable to open joystick {} via SDL", i)); + } + } + + logger.info (logger.format (tmp, "Opened {} joysticks via SDL, succesfully unless preceding warnings say otherwise.", joysticks.length)); +} + +/// Cleanup fct. +void closeJoysticks () { + foreach (js; joysticks) { + if(js) SDL_JoystickClose(js); // only close if successfully opened + } +}
--- a/mde/mde.d Fri Jan 25 18:17:38 2008 +0000 +++ b/mde/mde.d Wed Jan 30 11:33:56 2008 +0000 @@ -34,11 +34,11 @@ return 1; } - input.addButtonCallback (cast(Input.inputID) 3u, delegate void(Input.inputID i, bool b) { + input.addButtonCallback (cast(Input.inputID) 5u, delegate void(Input.inputID i, bool b) { Stdout ("Event: ")(i)(" changed to: ")(b).newline; } ); + bool oldb = false; - while (run) /+for (ulong t = 0; t < 100; ++t)+/ { Scheduler.run (Clock.now()); @@ -49,7 +49,7 @@ Stdout ("Button 3 changed to: ")(b).newline; } - Thread.sleep (0.010); // 10 ms + Thread.sleep (0.050); // sleep this many seconds } return 0; // cleanup handled by init's DTOR
--- a/mde/mergetag/dataset.d Fri Jan 25 18:17:38 2008 +0000 +++ b/mde/mergetag/dataset.d Wed Jan 30 11:33:56 2008 +0000 @@ -90,7 +90,6 @@ * calling addTag. */ void addTag (char[],ID,char[]); void writeAll (ItemDelg); /// TBD - debug void debugFunc (); /// Run in debug builds after parseSection. } unittest { // Only covers DataSet really.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mde/mergetag/defaultdata.d Wed Jan 30 11:33:56 2008 +0000 @@ -0,0 +1,189 @@ +/// This module contains the DefaultData class, and some notes possibly useful for implementing +/// other types of DataSection. +module mde.mergetag.defaultdata; + +public import mde.mergetag.dataset; + +import mde.text.util; +import mde.text.parse : parse; +import mde.text.format : format; + +/** +* Default DataSection class. +* +* Supports all the basic types currently supported and array versions of +* each (except no arrays of binary, but arrays of strings are supported). +* Doesn't support custom types, but inheriting classes may add support. +*/ +/* Note: I wrote this comment when the code looked rather worse. It's still partially applicable though. +* +* Due to a failure to use generic programming techniques for most of this (maybe because it's not +* possible or maybe just because I don't know how to use templates properly) a lot of this code is +* really horrible and has to refer to EVERY data member. +* Be really careful if you add any items to this class. +* +* I really don't like having to do things like this, but it provides a lot of benefits such as no +* need to store types and no need to check an argument's type for every access (this could be done +* by throwing errors, but then an incorrect (perhaps hand-edited) data file could cause a lot of +* errors to be thrown). +*/ +class DefaultData : DataSection +{ + //BEGIN DATA + /** Data Members + * + * These names are available for direct access. + * + * An alternative access method is to use the provided templates: + * -------------------- + * template Arg(T) { + * alias Name Arg; + * } + * -------------------- + * + * Use with a mixin or directly: + * -------------------- + * mixin Arg!(type); + * auto x = Arg; + * + * type y = Arg!(type).Arg; + * -------------------- + * Note: trying to use Arg!(type) to implicitly refer to Arg!(type).Arg causes compiler errors due to + * --- alias Name Arg; --- + * actually being a mixin. + */ + + bool [ID] _bool; + byte [ID] _byte; /// ditto + short [ID] _short; /// ditto + int [ID] _int; /// ditto + long [ID] _long; /// ditto + ubyte [ID] _ubyte; /// ditto + ushort [ID] _ushort; /// ditto + uint [ID] _uint; /// ditto + ulong [ID] _ulong; /// ditto + + char [ID] _char; /// ditto + + float [ID] _float; /// ditto + double [ID] _double; /// ditto + real [ID] _real; /// ditto + + bool[] [ID] _boolA; /// ditto + byte[] [ID] _byteA; /// ditto + short[] [ID] _shortA; /// ditto + int[] [ID] _intA; /// ditto + long[] [ID] _longA; /// ditto + ubyte[] [ID] _ubyteA; /// ditto + ushort[] [ID] _ushortA; /// ditto + uint[] [ID] _uintA; /// ditto + ulong[] [ID] _ulongA; /// ditto + + char[] [ID] _charA; /// ditto + + float[] [ID] _floatA; /// ditto + double[] [ID] _doubleA; /// ditto + real[] [ID] _realA; /// ditto + + char[][] [ID] _charAA; /// ditto + + /** Alias names */ + alias _ubyteA _binary; + alias _charA _string; /// ditto + alias _charAA _stringA; /// ditto + //END DATA + + void addTag (char[] tp, ID id, char[] dt) { /// Supports all standard types. + if (tp.length == 0) throw new MTUnknownTypeException; + // split list up a bit for performance: + if (tp[0] < 'l') { + if (tp[0] < 'd') { + mixin ( `if (tp == "binary") addTag_add!(ubyte[]) (id, dt);` + ~ addTag_elifIsType_add!(bool) + ~ addTag_elifIsType_add!(bool[]) + ~ addTag_elifIsType_add!(byte) + ~ addTag_elifIsType_add!(byte[]) + ~ addTag_elifIsType_add!(char) + ~ addTag_elifIsType_add!(char[]) + ~ addTag_elifIsType_add!(char[][]) + ~ `else throw new MTUnknownTypeException;` ); + } else { + mixin ( `if (tp == "double") addTag_add!(double) (id, dt);` + ~ addTag_elifIsType_add!(double[]) + ~ addTag_elifIsType_add!(float) + ~ addTag_elifIsType_add!(float[]) + ~ addTag_elifIsType_add!(int) + ~ addTag_elifIsType_add!(int[]) + ~ `else throw new MTUnknownTypeException;` ); + } + } else { + if (tp[0] < 'u') { + mixin ( `if (tp == "long") addTag_add!(long) (id, dt);` + ~ addTag_elifIsType_add!(long[]) + ~ addTag_elifIsType_add!(real) + ~ addTag_elifIsType_add!(real[]) + ~ addTag_elifIsType_add!(short) + ~ addTag_elifIsType_add!(short[]) + ~ `else if (tp == "string") addTag_add!(char[]) (id, dt);` + ~ `else throw new MTUnknownTypeException;` ); + } else { + mixin ( `if (tp == "ubyte") addTag_add!(ubyte) (id, dt);` + ~ addTag_elifIsType_add!(ubyte[]) + ~ addTag_elifIsType_add!(ushort) + ~ addTag_elifIsType_add!(ushort[]) + ~ addTag_elifIsType_add!(uint) + ~ addTag_elifIsType_add!(uint[]) + ~ addTag_elifIsType_add!(ulong) + ~ addTag_elifIsType_add!(ulong[]) + ~ `else throw new MTUnknownTypeException;` ); + } + } + // try-catch block removed (caught by read) + } + private template addTag_elifIsType_add(T) { + const addTag_elifIsType_add = + `else if (tp == "`~T.stringof~`")` + `addTag_add!(`~T.stringof~`) (id, dt);` ; + } + private void addTag_add(T) (ID id, char[] dt) { + Arg!(T).Arg[id] = parse!(T) (dt); + } + + void writeAll (ItemDelg itemdlg) { + foreach (id, dt; _charA) itemdlg ("char[]", id, format!(char[])(dt)); + } + + /* These make no attempt to check Arg is valid. + * But if the symbol doesn't exist the complier will throw an error anyway, e.g.: + * Error: identifier '_boolAA' is not defined + */ + template Arg(T : T[]) { + const ArgString = Arg!(T).ArgString ~ `A`; + mixin(`alias `~ArgString~` Arg;`); + } + template Arg(T) { + const ArgString = `_` ~ T.stringof; + mixin(`alias `~ArgString~` Arg;`); + } +} + +/+class DynamicData : DataSection +{ +void*[TypeInfo] data; + +}+/ + +/+ +class TemplateData : DataSection +{ +void addTag (char[] tp, ID id, char[] dt) { +// runtime deduction of tp and aliasing? +// CANNOT add data at runtime though. +} +// will this work? no idea. +// templates can't be used to add non-static elements, so use a static array at index: this +template Data(T,TemplateData* p) { +static T[ID][TemplateData*] Data; +} +} ++/
--- a/mde/mergetag/read.d Fri Jan 25 18:17:38 2008 +0000 +++ b/mde/mergetag/read.d Wed Jan 30 11:33:56 2008 +0000 @@ -226,7 +226,6 @@ if (psmd && !psmd.read) { // may not exist DataSection ds = getOrCreateSec (id); parseSection (psmd.pos, &ds); - debug ds.debugFunc (); psmd.read = true; } } @@ -237,7 +236,6 @@ if (secSet.contains(id)) { DataSection ds = getOrCreateSec (id); pos = parseSection (pos, &ds); - debug ds.debugFunc (); secTable[id].read = true; } } @@ -248,7 +246,6 @@ if (!smd.read) { DataSection ds = getOrCreateSec (id); parseSection (smd.pos, &ds); - debug ds.debugFunc (); smd.read = true; } } @@ -257,7 +254,6 @@ ID id = fbufReadSecMarker (pos); DataSection ds = getOrCreateSec (id); pos = parseSection (pos, &ds); - debug ds.debugFunc (); } } allRead = true;