Mercurial > projects > mde
view mde/input/input.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 the interface to the input system; it should be the only module of the * input package imported from outside this package. */ module mde.input.input; // package imports import mde.input.config; import mde.input.exception; // sdl imports import derelict.sdl.events; class Input { /// Typedef for all indexes (type is uint). typedef uint inputID; alias void delegate(inputID, bool) ButtonCallback; alias void delegate(inputID, real) AxisCallback; alias void delegate(inputID, real,real) MouseCallback; /// Stores a static instance of Input for most usage. static Input instance () { static Input instance; if (instance is null) instance = new Input; return instance; } /** Get key status at this ID. * * 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; if (retp) return *retp; else return false; } /** Get axis status at this ID. * * 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; if (retp) return *retp; else return 0.0; } /** Get mouse pointer position in screen coordinates. * * Window managers only support one mouse, so there will only be one screen coordinate. * Unlike everything else, this is not configurable. */ void mouseScreenPos (out uint x, out uint y) { x = mouse_x; y = mouse_y; } /** Get relative mouse position (also for joystick balls). * * Future: Converts to a real via sensitivity settings (defaults may be set and overriden per item). * * To avoid confusion over the ID here, the idea is for the input-layer upward to support * multiple mice, in case future platforms do. * Also joystick balls (supported by SDL) can be used in the same way as a mouse for relative * positions. */ void mouseRelativePos (inputID id, out real x = 0.0, out real y = 0.0) { RelPair* rp = cast(inputID) id in axis_rel; if (rp) { x = rp.x; y = rp.y; } } // /// Is this modifier on? //bool modifierStatus (inputID id); /** Adds a callback delegate for key events (both DOWN and UP) with this ID. * * Delegate receives event status. */ void addButtonCallback (inputID id, ButtonCallback dg) { buttonCallbacks[id] = dg; } /** Adds a callback delegate for axis events with this ID. * * Delegate receives event status. */ void addAxisCallback (inputID id, AxisCallback dg) { axisCallbacks[id] = dg; } /** Adds a callback delegate for mouse motion/joystick ball events with this ID. * * Delegate receives event status. (A separate callback for mouse pointer position changes is not * necessary since this will be triggered by the same event - use mouseScreenPos from within the * function to get new screen coordinates.) */ void addMouseCallback (inputID id, MouseCallback dg) { mouseCallbacks[id] = dg; } /** Feed an SDL_Event struct (only uses if it's a key, mouse or joystick event). * * Other types of event functions may be added. Returns true if the event was used, false if not. */ bool opCall (ref SDL_Event event) { switch (event.type) { case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: 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: outQueue* p = (Config.B.SDLKEY | event.key.keysym.sym) in config.button; if (p) eventstream.bEventOut (event.key.state == SDL_PRESSED, readOutQueue(*p)); break; case SDL_MOUSEMOTION: mouse_x = event.motion.x; mouse_y = event.motion.y; outQueue* p = (Config.M.WMMOUSE) in config.mouse; if (p) eventstream.mEventOut (event.motion.xrel, event.motion.yrel, readOutQueue(*p)); +/ default: return false; } return true; } /** Resets relative movement of mice / joystick balls to zero. * * Should probably be called once-per-frame if these are used. */ void frameReset () { foreach (rp; axis_rel) { rp.x = rp.y = 0.0; } } /** Loads all configs, activating the requested id. * * Returns: true if the requested config id wasn't found. */ bool loadConfig (uint id) { Config.load("conf/input.mtt"); // FIXME: filename Config* c_p = id in Config.configs; if (c_p) { config = *c_p; return false; logger.info ("Succesfully loaded config."); } return true; } private: // Static constructor for event stream (fills es_*_fcts tables). static this () { es_b_fcts = [ ES_B_OUT : &es_b_out ]; } struct RelPair { // for mouse/joystick ball motion real x, y; static RelPair opCall (real a, real b) { RelPair ret; ret.x = a; ret.y = b; return ret; } } Config config; // Configuration bool[inputID] button; // Table of button states real[inputID] axis; // Table of axes states ushort mouse_x, mouse_y; // Current screen coords of the mouse // FIXME: might need a bit of work... at any rate defining a default ID. RelPair[inputID] axis_rel; // Table of relative mouse / joystick ball motions // FIXME: these need to be more like multimaps, supporting multiple dgs (also some means of removal?) ButtonCallback[inputID] buttonCallbacks; AxisCallback[inputID] axisCallbacks; MouseCallback[inputID] mouseCallbacks; //BEGIN Event stream functionality /* This section contains functions called on an event, which may modify the event (adjuster * functions), and finally output to one (or more) of the state tables (the event stream). * * Adjuster and other event functions should have a format to fit the ES_X_Func types, for X is B * (button event), A (axis event) or M (mouse relative motion event or joystick ball event). * Adjusters should call one of the xEventOut() functions with their output and the remainder of * the readOutQueue. * * To control which adjusters get called and pass parameters, a stack of sorts is used: outQueue. */ //BEGIN ES Definitions /* Note: We really want an array, not a stack. We cannot edit the lists, so we can either * copy to a stack or just iterate through it as an array. */ alias Config.outQueue outQueue; struct readOutQueue { // A convenient structure for reading an outQueue item by item. private Config.outQueue _q; // the queue, stored by reference to the original private uint p = 0; // current read position (start at beginning) static readOutQueue opCall (Config.outQueue q) { // Static constructor readOutQueue ret; ret._q = q; return ret; } uint next () { // Get the next element. 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; } } // 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 are the codes allowing the config to specify event functions: enum : uint { ES_B_OUT = 0x0000_0100u, ES_A_OUT = 0x0000_0200u, ES_M_OUT = 0x0000_0300u, } //END ES Definitions // ES Data: // These are the tables for looking up which event function to call. static ES_B_Func[uint] es_b_fcts; static ES_A_Func[uint] es_a_fcts; static ES_M_Func[uint] es_m_fcts; //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. 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"); } 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"); } 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"); } // The remaining functions are the stream functions, for adjusting and outputting an event. // 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; if (cb_p) (*cb_p) (id, b); } // Adjuster to check modifier keys void es_b_modifier (bool b, readOutQueue s); /* Simple output function Adds 1-2 items on the stack. */ void es_a_out (short x, readOutQueue s) { real y = x; uint conf = s.next(); enum : uint { HALF_RANGE = 0x8000_0000u, SENSITIVITY = 0x0080_0000u, } // Convert ranges into standard intervals (with or without reverse values) 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(); /+ 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; } // Simple output function void es_m_out (short x, short y, readOutQueue s) { axis_rel[cast(inputID) s.next()] = RelPair(x,y); } //END ES Functions //END Event stream functionality }