Mercurial > projects > mde
changeset 29:f985c28c0ec9
A new GUI widget plus changes to the init system.
GUI: Implemented a GridWidget to layout several sub-widgets.
Improved log messages about init functions.
Moved all dynamic-library loading into a separate init stage.
committer: Diggory Hardy <diggory.hardy@gmail.com>
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Sat, 12 Apr 2008 14:10:13 +0100 |
parents | b5fadd8d930b |
children | 467c74d4804d |
files | codeDoc/jobs.txt data/L10n/OptionsMisc.mtt data/L10n/mde.mtt data/conf/gui.mtt data/conf/options.mtt mde/SDL.d mde/events.d mde/exception.d mde/gui/Widget.d mde/gui/gui.d mde/input/joystick.d mde/scheduler/Init.d mde/scheduler/InitFunctions.d |
diffstat | 13 files changed, 151 insertions(+), 63 deletions(-) [+] |
line wrap: on
line diff
--- a/codeDoc/jobs.txt Tue Apr 08 15:52:21 2008 +0100 +++ b/codeDoc/jobs.txt Sat Apr 12 14:10:13 2008 +0100 @@ -3,7 +3,6 @@ In progress: -GUI work... To do: @@ -41,4 +40,6 @@ Done (for git log message): -Fixed a bug in paths.mdeDirectory.makeMTReader when called with readOrder == PRIORITY.HIGH_ONLY. +GUI: Implemented a GridWidget to layout several sub-widgets. +Improved log messages about init functions. +Moved all dynamic-library loading into a separate init stage. \ No newline at end of file
--- a/data/L10n/OptionsMisc.mtt Tue Apr 08 15:52:21 2008 +0100 +++ b/data/L10n/OptionsMisc.mtt Sat Apr 12 14:10:13 2008 +0100 @@ -2,4 +2,4 @@ {en-GB} <entry|useThreads=["Use threads","Global option for threading in mde."]> <entry|logLevel=["Logging level","Controls which messages are logged, from 0=trace to 6=none (default: 1=info)."]> -<entry|=["Localisation","Specifies the language to use."]> +<entry|L10n=["Localisation","Specifies the language to use."]>
--- a/data/L10n/mde.mtt Tue Apr 08 15:52:21 2008 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -{MT01} -{en-GB} -<entry|greeting=["Hello, and welcome to mde!"]>
--- a/data/conf/gui.mtt Tue Apr 08 15:52:21 2008 +0100 +++ b/data/conf/gui.mtt Sat Apr 12 14:10:13 2008 +0100 @@ -6,4 +6,4 @@ {W2} <int|x=150> <int|y=200> -<int[][int]|widgetData=[0:[1002,2],2:[1001,150,150]]> +<int[][int]|widgetData=[0:[1002,3,2,2,2,3,3,3,3],2:[1001,150,150],3:[1001,100,100]]>
--- a/data/conf/options.mtt Tue Apr 08 15:52:21 2008 +0100 +++ b/data/conf/options.mtt Sat Apr 12 14:10:13 2008 +0100 @@ -2,7 +2,7 @@ {misc} <bool|useThreads=false> <char[]|L10n="en-GB"> -<int|logLevel=1> +<int|logLevel=0> {video} <bool|noFrame=false>
--- a/mde/SDL.d Tue Apr 08 15:52:21 2008 +0100 +++ b/mde/SDL.d Sat Apr 12 14:10:13 2008 +0100 @@ -27,32 +27,17 @@ import tango.stdc.stringz; import derelict.sdl.sdl; -import derelict.opengl.gl; -import derelict.util.exception; private Logger logger; static this() { logger = Log.getLogger ("mde.SDL"); - init.addFunc (&initSdlAndGl); + init.addFunc (&initSdlAndGl, "initSdlAndGl"); } private uint flags = 0; void initSdlAndGl() { // init func - // Load SDL and GL dynamic libs - try { - DerelictSDL.load(); - DerelictGL.load(); - } catch (DerelictException de) { - logger.fatal ("Loading dynamic library failed:"); - logger.fatal (de.msg); - - setInitFailure (); - return; - } - debug logger.trace ("Derelict: loaded SDL and OpenGL"); - // Initialise SDL if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_JOYSTICK /+| SDL_INIT_EVENTTHREAD+/)) { logger.fatal ("SDL initialisation failed:"); @@ -67,7 +52,7 @@ // Must be called after SDL has been initialised, so cannot be a separate Init function. openJoysticks (); // after SDL init - cleanup.addFunc (&cleanupSDL); + cleanup.addFunc (&cleanupSDL, "cleanupSDL"); setupWindow(); }
--- a/mde/events.d Tue Apr 08 15:52:21 2008 +0100 +++ b/mde/events.d Sat Apr 12 14:10:13 2008 +0100 @@ -33,7 +33,7 @@ static this() { logger = Log.getLogger ("mde.events"); - init.addFunc (&initInput); + init.addFunc (&initInput, "initInput"); Scheduler.perFrame (&mde.events.pollEvents); }
--- a/mde/exception.d Tue Apr 08 15:52:21 2008 +0100 +++ b/mde/exception.d Sat Apr 12 14:10:13 2008 +0100 @@ -27,8 +27,8 @@ * "mde.pkg.file.Class" describing where the exception was thrown. (Since only methods overload * correctly, symbol is made static and an overloadable method is used to access the correct symbol.) */ - class mdeException : Exception { - /// Override in derived classes to name the module where the error occured. +class mdeException : Exception { + /// Override in derived classes to name the module where the error occured. char[] getSymbol () { return "mde"; }
--- a/mde/gui/Widget.d Tue Apr 08 15:52:21 2008 +0100 +++ b/mde/gui/Widget.d Sat Apr 12 14:10:13 2008 +0100 @@ -20,6 +20,7 @@ import mde.gui.exception; import gl = mde.gl; +import mde.scheduler.InitFunctions; /** Interface for widgets (may become a class). * @@ -70,27 +71,77 @@ } } -/// Encapsulates another widget -class SingleWidget : Widget +/// Encapsulates a grid of Widgets +class GridWidget : Widget { + const PADDING = 1; // padding between rows/cols + const BORDER = 1; // border width int w, h; // size - Widget subWidget; + int rows, cols; // number of cells in grid + int[] rowH; // row height (highest widget in the row) + int[] colW; // column width (widest widget) + int[] rowY; // cumulative rowH[i-1] + BORDER/PADDING + int[] colX; // cumulative colW[i-1] + BORDER/PADDING + Widget[] subWidgets;// all widgets in the grid (by row): + /* SubWidget order: [ 2 3 ] + * [ 0 1 ] */ this (IWindow window, int[] data) { - if (data.length != 1) throw new WidgetDataException; + // Get grid size + if (data.length < 2) throw new WidgetDataException; + rows = data[0]; + cols = data[1]; - subWidget = window.getWidget (data[0]); + // Get all sub-widgets + if (data.length != 2 + rows * cols) throw new WidgetDataException; + if (rows*cols == 0) { // special case + w = h = 2*BORDER; + return; + } + subWidgets.length = rows*cols; + for (uint i = 2; i < data.length; ++i) { + subWidgets[i-2] = window.getWidget (data[i]); + } + + // Find the sizes of all subWidgets + int[] widgetW = new int[subWidgets.length]; // dimensions + int[] widgetH = new int[subWidgets.length]; + foreach (i,widget; subWidgets) widget.getSize (widgetW[i],widgetH[i]); - subWidget.getSize (w,h); - w += 10; - h += 10; + // Find row heights and column widths (non cumulative) + rowH.length = rows; + colW.length = cols; + for (uint i = 0; i < subWidgets.length; ++i) { + uint x = i / cols; // row + if (rowH[x] < widgetH[i]) rowH[x] = widgetH[i]; + x = i % cols; // column + if (colW[x] < widgetW[i]) colW[x] = widgetW[i]; + } + + // rowY / colX + rowY.length = rows; + colX.length = cols; + int cum = BORDER; + foreach (i, x; rowH) { + rowY[i] = cum; + cum += x + PADDING; + } + h = cum + BORDER - PADDING; // total height + cum = BORDER; + foreach (i, x; colW) { + colX[i] = cum; + cum += x + PADDING; + } + w = cum + BORDER - PADDING; // total width } void draw (int x, int y) { gl.setColor (1.0f, 0.6f, 0.0f); gl.drawBox (x,x+w, y,y+h); - subWidget.draw (x+5, y+5); + foreach (i,widget; subWidgets) { + widget.draw (x + colX[i % cols], y + rowY[i / cols]); + } } void getSize (out int w, out int h) { @@ -101,7 +152,7 @@ // Widget types. Start high so they can be reordered easily later. enum WIDGET_TYPES : int { - BOX = 1001, SINGLE + BOX = 1001, GRID } Widget createWidget (IWindow window, int[] data) { @@ -110,6 +161,6 @@ data = data[1..$]; // the rest is passed to the Widget if (type == WIDGET_TYPES.BOX) return new BoxWidget (window, data); - else if (type == WIDGET_TYPES.SINGLE) return new SingleWidget (window, data); + else if (type == WIDGET_TYPES.GRID) return new GridWidget (window, data); else throw new WidgetDataException ("Bad widget type"); }
--- a/mde/gui/gui.d Tue Apr 08 15:52:21 2008 +0100 +++ b/mde/gui/gui.d Sat Apr 12 14:10:13 2008 +0100 @@ -34,7 +34,7 @@ static this () { logger = Log.getLogger ("mde.gui.gui"); - init.addFunc (&GUI.load); + init.addFunc (&GUI.load, "GUI.load"); } struct GUI {
--- a/mde/input/joystick.d Tue Apr 08 15:52:21 2008 +0100 +++ b/mde/input/joystick.d Sat Apr 12 14:10:13 2008 +0100 @@ -45,12 +45,14 @@ } } - logger.info (logger.format (tmp, "Opened {} joysticks via SDL, succesfully unless preceding warnings say otherwise.", joysticks.length)); + logger.info (logger.format (tmp, "Opened {} joysticks via SDL, succesfully unless preceding errors say otherwise.", joysticks.length)); } /// Cleanup fct. void closeJoysticks () { foreach (js; joysticks) { - if(js) SDL_JoystickClose(js); // only close if successfully opened + // FIXME: this is sometimes causing a SIGSEGV (Address boundary error) + // FIXME: when init fails + if(js !is null) SDL_JoystickClose(js); // only close if successfully opened } }
--- a/mde/scheduler/Init.d Tue Apr 08 15:52:21 2008 +0100 +++ b/mde/scheduler/Init.d Sat Apr 12 14:10:13 2008 +0100 @@ -43,6 +43,12 @@ import tango.util.log.RollingFileAppender : RollingFileAppender; } +// Derelict imports +import derelict.opengl.gl; +import derelict.sdl.sdl; +//import derelict.freetype.ft; +import derelict.util.exception; + /** * Static CTOR * @@ -101,11 +107,15 @@ logger = Log.getLogger ("mde.scheduler.Init.Init"); } - /** CTOR − initialisation + /** this() − initialisation * * Runs general initialisation code, in a threaded manner where this isn't difficult. + * If any init fails, cleanup is still handled by ~this(). * - * If any init fails, it must run necessary cleanup first since the DTOR cannot(?) be run. */ + * Init order: 1. Pre-init (loads components needed by most init functions). 2. Dynamic library + * loading (load any dynamic libraries first, so that if loading fails nothing else need be + * done). 3. Init functions (threaded functions handling the rest of initialisation). + */ /* In a single-threaded function this could be done with: * scope(failure) cleanup; * This won't work with a threaded init function since any threads completing succesfully will @@ -132,6 +142,25 @@ Log.getRootLogger.setLevel (cast(Log.Level) miscOpts.logLevel, true); // set the stored log level //END Pre-init + debug logger.trace ("Init: pre-init done"); + + //BEGIN Load dynamic libraries + /* Can be done by init functions but much neater to do here. + * Also means that init functions aren't run if a library fails to load. */ + try { + DerelictSDL.load(); + DerelictGL.load(); + //DerelictFT.load(); + } catch (DerelictException de) { + logger.fatal ("Loading dynamic library failed:"); + logger.fatal (de.msg); + + throw new InitException ("Loading dynamic libraries failed (see above)."); + return; + } + //END Load dynamic libraries + + debug logger.trace ("Init: dynamic libraries loaded"); //BEGIN Init (stages init2, init4) /* Call init functions. @@ -177,17 +206,23 @@ * catching any exceptions thrown by the functions (although this isn't guaranteed for threads), * and throw an InitStageException on initFailure. */ - const UFE = "Unhandled exception from Init function:"; + const LOG_IF_MSG = "Init function "; + const LOG_CF_MSG = "Cleanup function "; + const LOG_F_START = " - running"; + const LOG_F_END = " - completed"; + const LOG_F_FAIL = " - failed: "; /* Runs all functions consecutively, first-to-last. * If any function fails, halts immediately. */ void runStageForward (InitStage s) { foreach (func; s.funcs) { if (initFailure) break; try { - func(); + debug logger.trace (LOG_IF_MSG ~ func.name ~ LOG_F_START); + func.func(); + debug logger.trace (LOG_IF_MSG ~ func.name ~ LOG_F_END); } catch (Exception e) { - logger.fatal (UFE); - logger.fatal (e.msg); + logger.fatal (LOG_IF_MSG ~ func.name ~ LOG_F_FAIL ~ + ((e.msg is null || e.msg == "") ? "(no failure message)" : e.msg) ); setInitFailure(); } @@ -200,10 +235,12 @@ void runStageReverse (InitStage s) { foreach_reverse (func; s.funcs) { try { - func(); + debug logger.trace (LOG_CF_MSG ~ func.name ~ LOG_F_START); + func.func(); + debug logger.trace (LOG_CF_MSG ~ func.name ~ LOG_F_END); } catch (Exception e) { - logger.fatal (UFE); - logger.fatal (e.msg); + logger.fatal (LOG_CF_MSG ~ func.name ~ LOG_F_FAIL ~ + ((e.msg is null || e.msg == "") ? "(no failure message)" : e.msg) ); setInitFailure(); } @@ -218,11 +255,15 @@ ThreadGroup tg; try { // creating/starting threads could fail tg = new ThreadGroup; - foreach (func; s.funcs) tg.create(func); // Start all threads + foreach (func; s.funcs) { // Start all threads + debug logger.trace (LOG_IF_MSG ~ func.name ~ LOG_F_START); + tg.create(func.func); + debug logger.trace (LOG_IF_MSG ~ func.name ~ LOG_F_END); + } } catch (ThreadException e) { // Problem with threading; try without threads logger.error ("Caught ThreadException while trying to create threads:"); logger.error (e.msg); - logger.info ("Will disable threads and continue."); + logger.info ("Will disable threads and continue, assuming no threads were created."); Options.setBool("misc", "useThreads", false); // Disable threads entirely return true; // Try again without threads
--- a/mde/scheduler/InitFunctions.d Tue Apr 08 15:52:21 2008 +0100 +++ b/mde/scheduler/InitFunctions.d Sat Apr 12 14:10:13 2008 +0100 @@ -30,17 +30,17 @@ logger = Log.getLogger ("mde.scheduler.InitFunctions"); } -/** Should be called by an init function when a failure occurs. */ -void setInitFailure () { +void setInitFailure () { /// Call to indicate failure in an init function initFailure = true; } -package: - /** Represents all functions to be called for a particular init stage. */ struct InitStage { - alias void function() InitFunction; /// Alias + struct InitFunction { + void function() func; // the actual function + char[] name; // it's name; + } /** Add a function to be called during this init stage. * @@ -49,8 +49,11 @@ * Exceptions should never be thrown, since each function may run as a thread, and catching * thread exceptions is not guaranteed to work. Log a message, call setFailure() and return * instead. */ - void addFunc (InitFunction f) { - funcs ~= f; + void addFunc (void function() f, char[] name) { + InitFunction s; + s.func = f; + s.name = name; + funcs ~= s; } InitFunction[] funcs = []; @@ -59,17 +62,25 @@ InitStage init; // all functions called during init (all should be thread-safe) InitStage cleanup; // all functions called during cleanup (all should be thread-safe) +package: bool initFailure = false; // set on failure (throwing through threads isn't a good idea) private: Logger logger; -const FAIL_MSG = "Init function failed: "; +/+ I keep changing my mind about wrapping all init functions: +const LOG_MSG = "Init function "; +const TRACE_START = " - running"; +const TRACE_END = " - completed"; +const FAIL_MSG = " - failed: "; // Template to call function, catching exceptions: -void initInput(alias Func) () { +void initInput(alias Func, char[] name) () { try { + debug logger.trace (LOG_MSG ~ name ~ TRACE_START); Func(); + debug logger.trace (LOG_MSG ~ name ~ TRACE_END); } catch (Exception e) { - logger.fatal (FAIL_MSG ~ e.msg); + logger.fatal (LOG_MSG ~ name ~ FAIL_MSG ~ e.msg); initFailure = true; } } ++/