changeset 36:57d000574d75

Enabled drawing on demand, and made the polling interval configurable. Renamed mde.global to mde.imde. Enabled drawing on demand. Allowed options to take double values. Made the main loop's polling interval (sleep duration) settable from config files. committer: Diggory Hardy <diggory.hardy@gmail.com>
author Diggory Hardy <diggory.hardy@gmail.com>
date Fri, 02 May 2008 17:38:43 +0100
parents 928db3c75ed3
children 052df9b2fe07
files codeDoc/jobs.txt codeDoc/policies.txt data/conf/gui.mtt data/conf/options.mtt mde/Options.d mde/events.d mde/global.d mde/gui/Gui.d mde/gui/IGui.d mde/gui/widget/Ifaces.d mde/gui/widget/Widget.d mde/gui/widget/Window.d mde/imde.d mde/mde.d mde/scheduler/init2.d mde/sdl.d
diffstat 16 files changed, 151 insertions(+), 91 deletions(-) [+]
line wrap: on
line diff
--- a/codeDoc/jobs.txt	Fri May 02 16:20:35 2008 +0100
+++ b/codeDoc/jobs.txt	Fri May 02 17:38:43 2008 +0100
@@ -3,7 +3,6 @@
 
 
 In progress:
-Making Window dragable.
 
 
 
@@ -49,3 +48,7 @@
 
 
 Done (for git log message):
+Renamed mde.global to mde.imde.
+Enabled drawing on demand.
+Allowed options to take double values.
+Made the main loop's polling interval (sleep duration) settable from config files.
\ No newline at end of file
--- a/codeDoc/policies.txt	Fri May 02 16:20:35 2008 +0100
+++ b/codeDoc/policies.txt	Fri May 02 17:38:43 2008 +0100
@@ -44,6 +44,9 @@
 
 8   Translating strings
 
+9 Floating point types 
+
+
 
 
 --- Coding conventions ---
@@ -145,3 +148,8 @@
 User output (internationalization support): i18n.I18nTranslation is designed to fetch a full string and optionally an associated descripion given an identifier. The identifier may also be a code symbol, and should be brief, in English, and give at least some idea of its meaning, since if no translation is available the identifier will be output. For example:  "example message" or "exampleMessage", not "An example of a message". For any new entry created, at least one full entry should be added to the i18n database for some form of English so that:
     (a) there is something available to translate to other locales
     (b) English developers can understand what it means
+
+
+
+--- Floating point types ---
+The issue is whether to use float, double or real as the basic floating point type. In some cases which types are appropriate for use is set by the prototypes of external methods being used. In other cases, which type to use is left entirely to the user's discretion. Since even modern 32-bit CPUs support native 80-bit floating point operations, there seems little point in using floats. Since doubles are more widely supported than reals and provide sufficient precision for most purposes, double seems to be the ideal type to use. Actually some basic tests with 32-bit dmd on my athlon-64 (under 64-bit linux) show floats to be slower to use than doubles but faster than reals, so this appears to be a good choice for now.
\ No newline at end of file
--- a/data/conf/gui.mtt	Fri May 02 16:20:35 2008 +0100
+++ b/data/conf/gui.mtt	Fri May 02 17:38:43 2008 +0100
@@ -7,4 +7,4 @@
 {W2}
 <int|x=150>
 <int|y=200>
-<int[][int]|widgetData=[0:[1002,1,3,2,3,5],2:[1001,150,150],3:[1003,150,150],5:[1002,2,1,6,7],6:[1003,73,73],7:[1003,73,73]]>
+<int[][int]|widgetData=[0:[1002,1,3,2,3,5],2:[1001,150,150],3:[1003,150,150],5:[1002,2,1,6,6],6:[1003,73,73]]>
--- a/data/conf/options.mtt	Fri May 02 16:20:35 2008 +0100
+++ b/data/conf/options.mtt	Fri May 02 17:38:43 2008 +0100
@@ -3,6 +3,7 @@
 <bool|useThreads=false>
 <char[]|L10n="en-GB">
 <int|logLevel=0>
+<double|pollInterval=0.01>
 
 {video}
 <bool|noFrame=false>
--- a/mde/Options.d	Fri May 02 16:20:35 2008 +0100
+++ b/mde/Options.d	Fri May 02 17:38:43 2008 +0100
@@ -59,8 +59,9 @@
     
     // The "pointer lists":
     protected bool*  [ID]   optsBool;
+    protected int*   [ID]   optsInt;
+    protected double*[ID]   optsDouble;
     protected char[]*[ID]   optsCharA;
-    protected int*   [ID]   optsInt;
     
     //BEGIN Mergetag loading/saving code
     void addTag (char[] tp, ID id, char[] dt) {
@@ -70,14 +71,19 @@
         } else if (tp == "char[]") {
             char[]** p = id in optsCharA;
             if (p !is null) **p = parseTo!(char[]) (dt);
+        } else if (tp == "double") {
+            double** p = id in optsDouble;
+            if (p !is null) **p = parseTo!(double) (dt);
         } else if (tp == "int") {
             int** p = id in optsInt;
             if (p !is null) **p = parseTo!(int) (dt);
         }
     }
+
     void writeAll (ItemDelg dlg) {
         foreach (ID id, bool*   val; optsBool)  dlg ("bool"  , id, parseFrom!(bool  ) (*val));
         foreach (ID id, char[]* val; optsCharA) dlg ("char[]", id, parseFrom!(char[]) (*val));
+        foreach (ID id, double* val; optsDouble)dlg ("double", id, parseFrom!(double) (*val));
         foreach (ID id, int*    val; optsInt)   dlg ("int"   , id, parseFrom!(int   ) (*val));
     }
     //END Mergetag loading/saving code
@@ -123,6 +129,17 @@
             logger.error (ERR_MSG);
         }
     }
+    static void setDouble (char[] subClass, char[] symbol, double val) {
+        changed = true;     // something got set (don't bother checking this isn't what it already was)
+        
+        try {
+            *(subClasses[cast(ID) subClass].optsDouble[cast(ID) symbol]) = val;
+            subClassChanges[cast(ID) subClass].setDouble (cast(ID) symbol, val);
+        } catch (ArrayBoundsException) {
+            // log and ignore:
+            logger.error (ERR_MSG);
+        }
+    }
     static void setCharA (char[] subClass, char[] symbol, char[] val) {
         changed = true;     // something got set (don't bother checking this isn't what it already was)
         
@@ -217,12 +234,15 @@
     template decBool(A...) {
         const char[] decBool = "bool " ~ decRecurse!(A) ~ ";\n";
     }
+    template decInt(A...) {
+        const char[] decInt = "int " ~ decRecurse!(A) ~ ";\n";
+    }
+    template decDouble(A...) {
+        const char[] decDouble = "double " ~ decRecurse!(A) ~ ";\n";
+    }
     template decCharA(A...) {
         const char[] decCharA = "char[] " ~ decRecurse!(A) ~ ";\n";
     }
-    template decInt(A...) {
-        const char[] decInt = "int " ~ decRecurse!(A) ~ ";\n";
-    }
     template aaRecurse(char[] A, B...) {
         static if (B.length) const char[] aaRecurse = "\""~A~"\"[]:&"~A ~ ", " ~ aaRecurse!(B);
         else                 const char[] aaRecurse = "\""~A~"\"[]:&"~A;
@@ -233,6 +253,9 @@
     template aaInt(A...) {
         const char[] aaInt = "optsInt = [" ~ aaRecurse!(A) ~ "];\n";
     }
+    template aaDouble(A...) {
+        const char[] aaDouble = "optsDouble = [" ~ aaRecurse!(A) ~ "];\n";
+    }
     template aaCharA(A...) {
         const char[] aaCharA = "optsCharA = [" ~ aaRecurse!(A) ~ "];\n";
     }
@@ -245,6 +268,7 @@
     // optsX store pointers to each item added along with the ID and are used for access.
     bool[] bools;
     int[] ints;
+    double[] doubles;
     char[][] strings;
     
     this () {}
@@ -265,6 +289,14 @@
             optsInt[id] = &ints[$-1];
         }
     }
+    void setDouble (ID id, double x) {
+        double** p = id in optsDouble;
+        if (p !is null) **p = x;
+        else {
+            doubles ~= x;
+            optsDouble[id] = &doubles[$-1];
+        }
+    }
     void setCharA (ID id, char[] x) {
         char[]** p = id in optsCharA;
         if (p !is null) **p = x;
@@ -311,15 +343,18 @@
 class OptionsMisc : Options {
     alias store!("useThreads") BOOL;
     alias store!("logLevel") INT;
+    alias store!("pollInterval") DOUBLE;
     alias store!("L10n") CHARA;
     
     mixin (decBool!(BOOL.a));
     mixin (decInt!(INT.a));
+    mixin (decDouble!(DOUBLE.a));
     mixin (decCharA!(CHARA.a));
     
     this () {
         mixin (aaBool!(BOOL.a));
         mixin (aaInt!(INT.a));
+        mixin (aaDouble!(DOUBLE.a));
         mixin (aaCharA!(CHARA.a));
     }
     
--- a/mde/events.d	Fri May 02 16:20:35 2008 +0100
+++ b/mde/events.d	Fri May 02 17:38:43 2008 +0100
@@ -16,7 +16,7 @@
 /// Handles all events from SDL_PollEvent.
 module mde.events;
 
-import global = mde.global;
+import imde = mde.imde;
 import sdl = mde.sdl;           // resizeWindow
 
 import mde.input.Input;
@@ -37,19 +37,19 @@
         switch (event.type) {
             case SDL_QUIT:
                 logger.info ("Quit requested");
-                global.run = false;
+                imde.run = false;
                 break;
             case SDL_VIDEORESIZE:
                 sdl.resizeWindow (event.resize.w, event.resize.h);
-                //global.scheduler.request(global.SCHEDULE.DRAW);
+                imde.mainSchedule.request(imde.SCHEDULE.DRAW);
                 break;
-            /+case SDL_ACTIVEEVENT:
+            case SDL_ACTIVEEVENT:
             case SDL_VIDEOEXPOSE:
-                //global.scheduler.request(global.SCHEDULE.DRAW);
-                break;+/
+                imde.mainSchedule.request(imde.SCHEDULE.DRAW);
+                break;
             default:
                 try {
-                    global.input (event);
+                    imde.input (event);
                 } catch (Exception e) {
                     logger.error ("Caught input exception; event will be ignored. Exception was:");
                     logger.error (e.msg);
--- a/mde/global.d	Fri May 02 16:20:35 2008 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-/* 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 is for global objects.
-*
-* It's existance may only be temporary until objects are used in other ways; use of global objects
-* is generally disliked.
-*
-* I suggest importing via a rename or static import, so on use it's obvious where the objects used
-* are declared.
-*/
-module mde.global;
-
-import mde.input.Input;
-import mde.scheduler.Scheduler;
-
-/** Some enums used by per request functions. */
-enum SCHEDULE : Scheduler.ID {
-    DRAW
-};
-
-bool run = true;	// main loop continues if this is true
-
-Input input;		// Input instance. When multiple users are allowed instances will be per-user.
--- a/mde/gui/Gui.d	Fri May 02 16:20:35 2008 +0100
+++ b/mde/gui/Gui.d	Fri May 02 17:38:43 2008 +0100
@@ -27,6 +27,11 @@
 import mde.gui.renderer.createRenderer;
 import mde.gui.exception;
 
+// For adding the input event callbacks and requesting redraws:
+import imde = mde.imde;
+import mde.input.Input;
+import mde.scheduler.Scheduler;
+
 // For loading from file:
 import mt = mde.mergetag.DataSet;
 import mt = mde.mergetag.DefaultData;
@@ -95,6 +100,9 @@
                 logger.error ("Window failed to load: " ~ e.msg);
             }
         }
+        
+        imde.input.addMouseClickCallback(&clickEvent);
+        imde.input.addMouseMotionCallback(&motionEvent);
     }
     //END Loading code
     
@@ -145,6 +153,10 @@
         return rend;
     }
     
+    void requestRedraw () {
+        imde.mainSchedule.request(imde.SCHEDULE.DRAW);
+    }
+    
     void addClickCallback (void delegate(ushort, ushort, ubyte, bool) dg) {
         clickCallbacks[dg.ptr] = dg;
     }
--- a/mde/gui/IGui.d	Fri May 02 16:20:35 2008 +0100
+++ b/mde/gui/IGui.d	Fri May 02 17:38:43 2008 +0100
@@ -26,6 +26,12 @@
     /** Get the Gui's renderer. May be overriden by the window. */
     IRenderer renderer ();
     
+    /** Called by a sub-widget when a redraw is necessary (since drawing may sometimes be done on
+    * event.
+    *
+    * Currently forces the whole Gui to be redrawn. */
+    void requestRedraw ();
+    
     /** Add a mouse click callback: delegate will be called for all mouse click events recieved. */
     void addClickCallback (void delegate (ushort cx, ushort cy, ubyte b, bool state) dg);
     /** Add a mouse motion callback: delegate will be called for all motion events recieved. */
--- a/mde/gui/widget/Ifaces.d	Fri May 02 16:20:35 2008 +0100
+++ b/mde/gui/widget/Ifaces.d	Fri May 02 17:38:43 2008 +0100
@@ -43,11 +43,6 @@
     /** Get the managing Gui. */
     IGui gui ();
     
-    /+ Currently draw-on-event isn't used.
-    /** Called by a sub-widget when a redraw is necessary (since drawing may sometimes be done on
-    * event. */
-    void requestRedraw ();+/
-    
     /** Get the window's renderer.
     *
     * Normally specific to the GUI, but widgets have no direct contact with the GUI and this
--- a/mde/gui/widget/Widget.d	Fri May 02 16:20:35 2008 +0100
+++ b/mde/gui/widget/Widget.d	Fri May 02 17:38:43 2008 +0100
@@ -114,6 +114,7 @@
     void clickEvent (ushort, ushort, ubyte b, bool state) {
         if (b == 1 && state == true) {
             pushed = true;
+            window.gui.requestRedraw;
             window.gui.addClickCallback (&clickWhileHeld);
             window.gui.addMotionCallback (&motionWhileHeld);
         }
@@ -124,11 +125,15 @@
             Stdout ("Button clicked!").newline;
         
         pushed = false;
+        window.gui.requestRedraw;
         window.gui.removeCallbacks (cast(void*) this);
     }
     void motionWhileHeld (ushort cx, ushort cy) {
+        bool oldPushed = pushed;
         if (cx >= x && cx < x+w && cy >= y && cy < y+h) pushed = true;
         else pushed = false;
+        if (oldPushed != pushed)
+            window.gui.requestRedraw;
     }
 }
 //END Widgets
--- a/mde/gui/widget/Window.d	Fri May 02 16:20:35 2008 +0100
+++ b/mde/gui/widget/Window.d	Fri May 02 17:38:43 2008 +0100
@@ -107,22 +107,17 @@
         // widgetData is normally left to be garbage collected after widgets have been created:
         assert (widgetData !is null, "Window.makeWidget ("~name~"): widgetData is null");
     } body {
-        // See if it's already been created:
-        IWidget* p = i in widgets;
-        if (p !is null) {   // yes
-            char[128] tmp;
-            logger.warn (logger.format (tmp, "Window.makeWidget ("~name~"): widget {} has multiple uses!", i));
-            return *p;
-        }
-        else {              // no
-            int[]* d = i in widgetData;
-            if (d is null) throw new WindowLoadException ("Window.makeWidget ("~name~"): Widget not found");
-            
-            // Throws WidgetDataException (a WindowLoadException) if bad data:
-            IWidget widg = createWidget (this, parent, *d);
-            widgets[i] = widg;
-            return widg;
-        }
+        /* Each widget returned should be a unique object; if multiple widgets are requested with
+        * the same ID, a new widget is created each time. */
+        
+        int[]* d = i in widgetData;
+        if (d is null)
+            throw new WindowLoadException ("Window.makeWidget ("~name~"): Widget not found");
+        
+        // Throws WidgetDataException (a WindowLoadException) if bad data:
+        IWidget widg = createWidget (this, parent, *d);
+        widgets ~= widg;
+        return widg;
     }
     
     IGui gui () {
@@ -160,6 +155,8 @@
         widgetY = y + rend.windowBorder;
         
         widget.setPosition (widgetX, widgetY);
+        
+        gui_.requestRedraw ();  // obviously necessary whenever the window is moved
     }
     
     IWidget getWidget (int cx, int cy) {
@@ -209,7 +206,7 @@
     IGui gui_;                      // The gui managing this window
     
     int[][widgetID] widgetData;     // Data for all widgets under this window (deleted after loading)
-    IWidget[widgetID] widgets;      // List of all widgets under this window (created on demand).
+    IWidget[] widgets;              // List of all widgets under this window (created on demand). Use for saving?
     IWidget widget;                 // The primary widget in this window.
     
     IRenderer rend;                 // The window's renderer
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mde/imde.d	Fri May 02 17:38:43 2008 +0100
@@ -0,0 +1,31 @@
+/* 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 is for interfacing with the mde.mde module and some global items. */
+module mde.imde;
+
+import mde.input.Input;
+import mde.scheduler.Scheduler;
+
+Scheduler mainSchedule; /// The schedule used by the main loop.
+
+/** Some enums used by per request scheduled functions. */
+enum SCHEDULE : Scheduler.ID {
+    DRAW
+};
+
+bool run = true;	// main loop continues if this is true
+
+Input input;		// Input instance. When multiple users are allowed instances will be per-user.
--- a/mde/mde.d	Fri May 02 16:20:35 2008 +0100
+++ b/mde/mde.d	Fri May 02 17:38:43 2008 +0100
@@ -21,9 +21,12 @@
 
 // Comment to show use, where only used minimally:
 
-import global = mde.global;             // global.run
+import mde.imde;                        // this module's interface for external modules
+import mde.events;                      // pollEvents
+import mde.Options;                     // pollInterval option
+
 import gl = mde.gl.draw;                // gl.draw()
-import mde.events;                      // pollEvents
+import mde.input.Input;                 // new Input()
 
 import mde.scheduler.Init;
 import mde.scheduler.Scheduler;         // Scheduler.run()
@@ -39,6 +42,10 @@
     Logger logger = Log.getLogger ("mde.mde");
     logger.info ("Starting mde...");
     
+    // Create instances now, so they can be used during init (if necessary)
+    input = new Input();
+    mainSchedule = new Scheduler;
+    
     scope Init init;
     try {
         init = new Init();	// initialisation
@@ -46,19 +53,20 @@
         logger.fatal ("Initialisation failed: " ~ e.msg);
         return 1;
     }
+    
+    if (miscOpts.pollInterval !<= 0.1 || miscOpts.pollInterval !>= 0.0)
+        Options.setDouble ("misc", "pollInterval", 0.0);
     //END Initialisation
     
     //BEGIN Main loop setup
-    Scheduler scheduler = new Scheduler;    // main loop's scheduler
-    
-    scheduler.add (scheduler.getNewID, &mde.events.pollEvents).frame = true;
-    scheduler.add (global.SCHEDULE.DRAW, &gl.draw).frame = true;    // draw. for now, every frame.
+    mainSchedule.add (mainSchedule.getNewID, &mde.events.pollEvents).frame = true;
+    mainSchedule.add (SCHEDULE.DRAW, &gl.draw); // Draw, per event only.
     //END Main loop setup
     
-    while (global.run) {
-        scheduler.execute (Clock.now());
+    while (run) {
+        mainSchedule.execute (Clock.now());
         
-        Thread.sleep (0.050);	// sleep this many seconds
+        Thread.sleep (miscOpts.pollInterval);	// sleep this many seconds
     }
     
     return 0;		// cleanup handled by init's DTOR
--- a/mde/scheduler/init2.d	Fri May 02 16:20:35 2008 +0100
+++ b/mde/scheduler/init2.d	Fri May 02 17:38:43 2008 +0100
@@ -32,7 +32,7 @@
 import tango.util.log.Log : Log, Logger;
 
 // Modules requiring init code running:
-import global = mde.global;
+import imde = mde.imde;
 import mde.gui.Gui;
 import mde.input.Input;
 import ft = mde.ft.init;
@@ -59,20 +59,15 @@
 
 void initInput () { // init func
     try {
-        global.input = new Input();
-        global.input.loadConfig ();         // (may also create instance)
+        imde.input.loadConfig ();         // (may also create instance)
         
         // Quit on escape. NOTE: quit via SDL_QUIT event is handled completely independently!
-        global.input.addButtonCallback (cast(Input.inputID) 0x0u, delegate void(Input.inputID i, bool b) {
+        imde.input.addButtonCallback (cast(Input.inputID) 0x0u, delegate void(Input.inputID i, bool b) {
             if (b) {
                 logger.info ("Quiting...");
-                global.run = false;
+                imde.run = false;
             }
         } );
-        
-        // Aught to be added by the gui, but it doesn't know if input exists then.
-        global.input.addMouseClickCallback(&gui.clickEvent);
-        global.input.addMouseMotionCallback(&gui.motionEvent);
     } catch (Exception e) {
         logger.fatal ("initInput failed: " ~ e.msg);
         setInitFailure;
--- a/mde/sdl.d	Fri May 02 16:20:35 2008 +0100
+++ b/mde/sdl.d	Fri May 02 17:38:43 2008 +0100
@@ -21,7 +21,7 @@
 import mde.input.joystick;
 import mde.Options;
 import mde.gl.basic;
-import global = mde.global;
+import imde = mde.imde;
 
 import tango.util.log.Log : Log, Logger;
 import tango.stdc.stringz;
@@ -128,7 +128,7 @@
         char* msg = SDL_GetError ();
         logger.fatal (msg ? fromStringz(msg) : "no reason available");
         
-        global.run = false;
+        imde.run = false;
     }
     
     // Reset the projection and viewport