diff --git a/.gitignore b/.gitignore index 9fd04c510..3772ef42b 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ build*/ *.qmlc CMakeLists.txt.user *.pro.user +nbproject diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 2bf7bfdc0..f8654d336 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -28,6 +28,8 @@ if (ENABLE_HID_INPUT) add_subdirectory(hidapi) endif() +add_subdirectory(fonts) + if (ENABLE_PLIB_JOYSTICK) add_subdirectory(joystick) endif() diff --git a/3rdparty/fonts/CMakeLists.txt b/3rdparty/fonts/CMakeLists.txt new file mode 100644 index 000000000..04982db1b --- /dev/null +++ b/3rdparty/fonts/CMakeLists.txt @@ -0,0 +1,14 @@ + +set (FNT_SOURCES + fnt.cxx + fntTXF.cxx + fntBitmap.cxx +) + +add_library(PLIBFont STATIC ${FNT_SOURCES}) + +target_link_libraries(PLIBFont SimGearCore) + +target_include_directories(PLIBFont PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + + diff --git a/3rdparty/fonts/README b/3rdparty/fonts/README new file mode 100644 index 000000000..6ae250a9e --- /dev/null +++ b/3rdparty/fonts/README @@ -0,0 +1,2 @@ +This is PLIB's 'fnt' library, extracted and modified +- added UTF8 path support, especially for Windows \ No newline at end of file diff --git a/3rdparty/fonts/fnt.cxx b/3rdparty/fonts/fnt.cxx new file mode 100644 index 000000000..5cee71608 --- /dev/null +++ b/3rdparty/fonts/fnt.cxx @@ -0,0 +1,257 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: fnt.cxx 1939 2004-08-05 00:41:53Z puggles $ +*/ + +#include "fntLocal.h" + +#include +#include + +fntFont:: fntFont () {} +fntFont::~fntFont () {} + +int fntTexFont::load ( const SGPath& path, GLenum mag, GLenum min ) +{ + if (path.extension() == "txf") { + return loadTXF ( path, mag, min ) ; + } else { + const auto ps = path.utf8Str(); + fntSetError ( SG_WARN, + "fnt::load: Error - Unrecognised file format for '%s'", ps.c_str() ) ; + return FNT_FALSE ; + } +} + +float fntTexFont::low_putch ( sgVec3 curpos, float pointsize, + float italic, char c ) +{ + unsigned int cc = (unsigned char) c ; + + /* Auto case-convert if character is absent from font. */ + + if ( ! exists [ cc ] ) + { + if ( cc >= 'A' && cc <= 'Z' ) + cc = cc - 'A' + 'a' ; + else + if ( cc >= 'a' && cc <= 'z' ) + cc = cc - 'a' + 'A' ; + + if ( cc == ' ' ) + { + curpos [ 0 ] += pointsize / 2.0f ; + return pointsize / 2.0f ; + } + } + + /* + We might want to consider making some absent characters from + others (if they exist): lowercase 'l' could be made into digit '1' + or letter 'O' into digit '0'...or vice versa. We could also + make 'b', 'd', 'p' and 'q' by mirror-imaging - this would + save a little more texture memory in some fonts. + */ + + if ( ! exists [ cc ] ) + return 0.0f ; + + glBegin ( GL_TRIANGLE_STRIP ) ; + glTexCoord2f ( t_left [cc], t_bot[cc] ) ; + glVertex3f ( curpos[0] + v_left [cc] * pointsize, + curpos[1] + v_bot [cc] * pointsize, + curpos[2] ) ; + + glTexCoord2f ( t_left [cc], t_top[cc] ) ; + glVertex3f ( curpos[0] + (italic + v_left [cc]) * pointsize, + curpos[1] + v_top [cc] * pointsize, + curpos[2] ) ; + + glTexCoord2f ( t_right[cc], t_bot[cc] ) ; + glVertex3f ( curpos[0] + v_right[cc] * pointsize, + curpos[1] + v_bot [cc] * pointsize, + curpos[2] ) ; + + glTexCoord2f ( t_right[cc], t_top[cc] ) ; + glVertex3f ( curpos[0] + (italic + v_right[cc]) * pointsize, + curpos[1] + v_top [cc] * pointsize, + curpos[2] ) ; + glEnd () ; + + float ww = ( gap + ( fixed_pitch ? width : widths[cc] ) ) * pointsize ; + curpos[0] += ww ; + return ww ; +} + + + +void fntTexFont::setGlyph ( char c, float wid, + float tex_left, float tex_right, + float tex_bot , float tex_top , + float vtx_left, float vtx_right, + float vtx_bot , float vtx_top ) +{ + unsigned int cc = (unsigned char) c ; + + exists[cc] = FNT_TRUE ; + + widths[cc] = wid; + + t_left[cc] = tex_left ; t_right[cc] = tex_right ; + t_bot [cc] = tex_bot ; t_top [cc] = tex_top ; + + v_left[cc] = vtx_left ; v_right[cc] = vtx_right ; + v_bot [cc] = vtx_bot ; v_top [cc] = vtx_top ; +} + + +int fntTexFont::getGlyph ( char c, float* wid, + float *tex_left, float *tex_right, + float *tex_bot , float *tex_top , + float *vtx_left, float *vtx_right, + float *vtx_bot , float *vtx_top ) +{ + unsigned int cc = (unsigned char) c ; + + if ( ! exists[cc] ) return FNT_FALSE ; + + if ( wid != NULL ) *wid = widths [cc] ; + + if ( tex_left != NULL ) *tex_left = t_left [cc] ; + if ( tex_right != NULL ) *tex_right = t_right[cc] ; + if ( tex_bot != NULL ) *tex_bot = t_bot [cc] ; + if ( tex_top != NULL ) *tex_top = t_top [cc] ; + + if ( vtx_left != NULL ) *vtx_left = v_left [cc] ; + if ( vtx_right != NULL ) *vtx_right = v_right[cc] ; + if ( vtx_bot != NULL ) *vtx_bot = v_bot [cc] ; + if ( vtx_top != NULL ) *vtx_top = v_top [cc] ; + + return FNT_TRUE ; +} + + +void fntTexFont::getBBox ( const char *s, + float pointsize, float italic, + float *left, float *right, + float *bot , float *top ) +{ + float h_pos = 0.0f ; + float v_pos = 0.0f ; + float l, r, b, t ; + + l = r = b = t = 0.0f ; + + while ( *s != '\0' ) + { + if ( *s == '\n' ) + { + h_pos = 0.0f ; + v_pos -= 1.333f ; + s++ ; + continue ; + } + + unsigned int cc = (unsigned char) *(s++) ; + + if ( ! exists [ cc ] ) + { + if ( cc >= 'A' && cc <= 'Z' ) + cc = cc - 'A' + 'a' ; + else + if ( cc >= 'a' && cc <= 'z' ) + cc = cc - 'a' + 'A' ; + + if ( cc == ' ' ) + { + r += 0.5f ; + h_pos += 0.5f ; + + continue ; + } + } + + if ( ! exists [ cc ] ) + continue ; + + if ( italic >= 0 ) + { + if ( l > h_pos + v_left [cc] ) l = h_pos + v_left [cc] ; + if ( r < gap + h_pos + v_right[cc]+italic ) r = gap + h_pos + v_right[cc] + italic ; + } + else + { + if ( l > h_pos + v_left [cc]+italic ) l = h_pos + v_left [cc] + italic ; + if ( r < gap + h_pos + v_right[cc] ) r = gap + h_pos + v_right[cc] ; + } + + + if ( b > v_pos + v_bot [cc] ) b = v_pos + v_bot [cc] ; + if ( t < v_pos + v_top [cc] ) t = v_pos + v_top [cc] ; + + h_pos += gap + ( fixed_pitch ? width : widths[cc] ) ; + } + + if ( left != NULL ) *left = l * pointsize ; + if ( right != NULL ) *right = r * pointsize ; + if ( top != NULL ) *top = t * pointsize ; + if ( bot != NULL ) *bot = b * pointsize ; +} + + +void fntTexFont::puts ( sgVec3 curpos, float pointsize, float italic, const char *s ) +{ + SGfloat origx = curpos[0] ; + + if ( ! bound ) + bind_texture () ; + + while ( *s != '\0' ) + { + if (*s == '\n') + { + curpos[0] = origx ; + curpos[1] -= pointsize ; + } + else + low_putch ( curpos, pointsize, italic, *s ) ; + + s++ ; + } +} + +void fntInit () +{ + /* Empty right now */ +} + + +void fntSetError(int level, const char* format, ...) +{ + const int bufferLength = 2048; + char buffer[2048]; + va_list args; + va_start (args, format); + vsnprintf(buffer,bufferLength,format, args); + va_end (args); + + SG_LOG(SG_GUI, static_cast(level), buffer); +} diff --git a/3rdparty/fonts/fnt.h b/3rdparty/fonts/fnt.h new file mode 100644 index 000000000..a80591ec5 --- /dev/null +++ b/3rdparty/fonts/fnt.h @@ -0,0 +1,357 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: fnt.h 1923 2004-04-06 12:53:17Z sjbaker $ +*/ + + +#ifndef _FNT_H_ +#define _FNT_H_ 1 + +#include + +#include +#include + +#define FNTMAX_CHAR 256 +#define FNT_TRUE 1 +#define FNT_FALSE 0 + +class SGPath; + +class fntFont +{ +public: + fntFont () ; + + virtual ~fntFont () ; + + virtual void getBBox ( const char *s, float pointsize, float italic, + float *left, float *right, + float *bot , float *top ) = 0 ; + virtual void putch ( sgVec3 curpos, float pointsize, float italic, char c ) = 0 ; + virtual void puts ( sgVec3 curpos, float pointsize, float italic, const char *s ) = 0 ; + virtual void begin () = 0 ; + virtual void end () = 0 ; + + virtual int load ( const SGPath& fname, GLenum mag = GL_NEAREST, + GLenum min = GL_LINEAR_MIPMAP_LINEAR ) = 0 ; + + virtual void setFixedPitch ( int fix ) = 0 ; + virtual int isFixedPitch () const = 0 ; + + virtual void setWidth ( float w ) = 0 ; + virtual void setGap ( float g ) = 0 ; + + virtual float getWidth () const = 0 ; + virtual float getGap () const = 0 ; + + virtual int hasGlyph ( char c ) const = 0 ; +} ; + + +class fntTexFont : public fntFont +{ +private: + GLuint texture ; + int bound ; + int fixed_pitch ; + + float width ; /* The width of a character in fixed-width mode */ + float gap ; /* Set the gap between characters */ + + int exists [ FNTMAX_CHAR ] ; /* TRUE if character exists in tex-map */ + + /* + The quadrilaterals that describe the characters + in the font are drawn with the following texture + and spatial coordinates. The texture coordinates + are in (S,T) space, with (0,0) at the bottom left + of the image and (1,1) at the top-right. + + The spatial coordinates are relative to the current + 'cursor' position. They should be scaled such that + 1.0 represent the height of a capital letter. Hence, + characters like 'y' which have a descender will be + given a negative v_bot. Most capitals will have + v_bot==0.0 and v_top==1.0. + */ + + /* Nominal baseline widths */ + + float widths [ FNTMAX_CHAR ] ; + + /* Texture coordinates */ + + float t_top [ FNTMAX_CHAR ] ; /* Top edge of each character [0..1] */ + float t_bot [ FNTMAX_CHAR ] ; /* Bottom edge of each character [0..1] */ + float t_left [ FNTMAX_CHAR ] ; /* Left edge of each character [0..1] */ + float t_right [ FNTMAX_CHAR ] ; /* Right edge of each character [0..1] */ + + /* Vertex coordinates. */ + + float v_top [ FNTMAX_CHAR ] ; + float v_bot [ FNTMAX_CHAR ] ; + float v_left [ FNTMAX_CHAR ] ; + float v_right [ FNTMAX_CHAR ] ; + + void bind_texture () + { + glEnable ( GL_TEXTURE_2D ) ; +#ifdef GL_VERSION_1_1 + glBindTexture ( GL_TEXTURE_2D, texture ) ; +#else + /* For ancient SGI machines */ + glBindTextureEXT ( GL_TEXTURE_2D, texture ) ; +#endif + } + + float low_putch ( sgVec3 curpos, float pointsize, float italic, char c ) ; + + int loadTXF ( const SGPath& path, GLenum mag = GL_NEAREST, + GLenum min = GL_LINEAR_MIPMAP_LINEAR ) ; +public: + + fntTexFont () + { + bound = FNT_FALSE ; + fixed_pitch = FNT_TRUE ; + texture = 0 ; + width = 1.0f ; + gap = 0.1f ; + + memset ( exists, FNT_FALSE, FNTMAX_CHAR * sizeof(int) ) ; + } + + fntTexFont ( const SGPath& path, GLenum mag = GL_NEAREST, + GLenum min = GL_LINEAR_MIPMAP_LINEAR ) : fntFont () + { + bound = FNT_FALSE ; + fixed_pitch = FNT_TRUE ; + texture = 0 ; + width = 1.0f ; + gap = 0.1f ; + + memset ( exists, FNT_FALSE, FNTMAX_CHAR * sizeof(int) ) ; + load ( path, mag, min ) ; + } + + ~fntTexFont () + { + if ( texture != 0 ) + { +#ifdef GL_VERSION_1_1 + glDeleteTextures ( 1, &texture ) ; +#else + /* For ancient SGI machines */ + glDeleteTexturesEXT ( 1, &texture ) ; +#endif + } + } + + int load ( const SGPath& path, GLenum mag = GL_NEAREST, + GLenum min = GL_LINEAR_MIPMAP_LINEAR ) override ; + + void setFixedPitch ( int fix ) { fixed_pitch = fix ; } + int isFixedPitch () const { return fixed_pitch ; } + + void setWidth ( float w ) { width = w ; } + void setGap ( float g ) { gap = g ; } + + float getWidth () const { return width ; } + float getGap () const { return gap ; } + + + void setGlyph ( char c, float wid, + float tex_left, float tex_right, + float tex_bot , float tex_top , + float vtx_left, float vtx_right, + float vtx_bot , float vtx_top ) ; + void setGlyph ( char c, + float tex_left, float tex_right, + float tex_bot , float tex_top , + float vtx_left, float vtx_right, + float vtx_bot , float vtx_top ) /* deprecated */ + { + setGlyph ( c, vtx_right, + tex_left, tex_right, tex_bot, tex_top, + vtx_left, vtx_right, vtx_bot, vtx_top ) ; + } + + int getGlyph ( char c, float* wid, + float *tex_left = NULL, float *tex_right = NULL, + float *tex_bot = NULL, float *tex_top = NULL, + float *vtx_left = NULL, float *vtx_right = NULL, + float *vtx_bot = NULL, float *vtx_top = NULL) ; + int getGlyph ( char c, + float *tex_left = NULL, float *tex_right = NULL, + float *tex_bot = NULL, float *tex_top = NULL, + float *vtx_left = NULL, float *vtx_right = NULL, + float *vtx_bot = NULL, float *vtx_top = NULL) /* deprecated */ + { + return getGlyph ( c, NULL, + tex_left, tex_right, tex_bot, tex_top, + vtx_left, vtx_right, vtx_bot, vtx_top ) ; + } + + int hasGlyph ( char c ) const { return exists[ (GLubyte) c ] ; } + + void getBBox ( const char *s, float pointsize, float italic, + float *left, float *right, + float *bot , float *top ) ; + + void begin () + { + bind_texture () ; + bound = FNT_TRUE ; + } + + void end () + { + bound = FNT_FALSE ; + } + + void puts ( sgVec3 curpos, float pointsize, float italic, const char *s ) ; + + void putch ( sgVec3 curpos, float pointsize, float italic, char c ) + { + if ( ! bound ) + bind_texture () ; + + low_putch ( curpos, pointsize, italic, c ) ; + } + +} ; + + + +class fntBitmapFont : public fntFont +{ + protected: + + const GLubyte **data; + int first; + int count; + int height; + float xorig, yorig; + int fix; + float wid, gap; + + public: + + // data is a null-terminated list of glyph images: + // data[i][0] - image width + // data[i][1..n] - packed bitmap + + fntBitmapFont ( const GLubyte **data, int first, int height, + float xorig, float yorig ) ; + + virtual ~fntBitmapFont () ; + + virtual void getBBox ( const char *s, float pointsize, float italic, + float *left, float *right, + float *bot , float *top ) ; + + virtual void putch ( sgVec3 curpos, float pointsize, float italic, char c ) ; + virtual void puts ( sgVec3 curpos, float pointsize, float italic, const char *s ) ; + + virtual void begin () ; + virtual void end () ; + + virtual int load ( const SGPath& fname, GLenum mag, GLenum min ) override { return -1; } + + virtual void setFixedPitch ( int f ) { fix = f; } + virtual int isFixedPitch () const { return fix; } + + virtual void setWidth ( float w ) { wid = w; } + virtual void setGap ( float g ) { gap = g; } + + virtual float getWidth () const { return wid; } + virtual float getGap () const { return gap; } + + virtual int hasGlyph ( char c ) const ; +}; + + +/* Builtin Bitmap Fonts */ + +#define FNT_BITMAP_8_BY_13 0 +#define FNT_BITMAP_9_BY_15 1 +#define FNT_BITMAP_HELVETICA_10 2 +#define FNT_BITMAP_HELVETICA_12 3 +#define FNT_BITMAP_HELVETICA_18 4 +#define FNT_BITMAP_TIMES_ROMAN_10 5 +#define FNT_BITMAP_TIMES_ROMAN_24 6 + +fntBitmapFont *fntGetBitmapFont(int id); + + + +class fntRenderer +{ + fntFont *font ; + + sgVec3 curpos ; + + float pointsize ; + float italic ; + +public: + fntRenderer () + { + start2f ( 0.0f, 0.0f ) ; + font = NULL ; + pointsize = 10 ; + italic = 0 ; + } + + void start3fv ( sgVec3 pos ) { sgCopyVec3 ( curpos, pos ) ; } + void start2fv ( sgVec2 pos ) { sgCopyVec2 ( curpos, pos ) ; curpos[2]=0.0f ; } + void start2f ( float x, float y ) { sgSetVec3 ( curpos, x, y, 0.0f ) ; } + void start3f ( float x, float y, float z ) { sgSetVec3 ( curpos, x, y, z ) ; } + + void getCursor ( float *x, float *y, float *z ) const + { + if ( x != NULL ) *x = curpos [ 0 ] ; + if ( y != NULL ) *y = curpos [ 1 ] ; + if ( z != NULL ) *z = curpos [ 2 ] ; + } + + void setFont ( fntFont *f ) { font = f ; } + fntFont *getFont () const { return font ; } + + void setSlant ( float i ) { italic = i ; } + void setPointSize ( float p ) { pointsize = p ; } + + float getSlant () const { return italic ; } + float getPointSize () const { return pointsize ; } + + void begin () { font->begin () ; } + void end () { font->end () ; } + + void putch ( char c ) { font->putch ( curpos, pointsize, italic, c ) ; } + void puts ( const char *s ) { font->puts ( curpos, pointsize, italic, s ) ; } +} ; + + +void fntInit () ; + +#endif + diff --git a/3rdparty/fonts/fntBitmap.cxx b/3rdparty/fonts/fntBitmap.cxx new file mode 100644 index 000000000..22cf2a501 --- /dev/null +++ b/3rdparty/fonts/fntBitmap.cxx @@ -0,0 +1,1017 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: fntBitmap.cxx 2036 2005-07-14 16:37:55Z fayjf $ +*/ + + +#include "fnt.h" + + +fntBitmapFont::fntBitmapFont( const GLubyte **_data, int _first, int _height, + float _xorig, float _yorig ) +{ + data = _data; + first = _first; + count = 0; + height = _height; + xorig = _xorig; + yorig = _yorig; + fix = 0; + wid = 0; + gap = 0; + + while (data[count] != NULL) + count++; +} + + +fntBitmapFont::~fntBitmapFont() +{ +} + + +void fntBitmapFont::begin () +{ + glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); + glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); + glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); +} + + +void fntBitmapFont::end () +{ + glPopClientAttrib(); +} + + +void fntBitmapFont::putch ( sgVec3 curpos, float pointsize, float italic, char c ) +{ + if (c == '\n') { + curpos[1] -= height; + } else { + glRasterPos2i(0,0); // beware - coordinate may be negative + glBitmap(0, 0, 0, 0, curpos[0], curpos[1], NULL); + int i = (GLubyte) c - first; + if (i >= 0 && i < count) { + glBitmap(data[i][0], height, xorig, yorig, (float) data[i][0], 0, data[i] + 1); + curpos[0] += data[i][0]; + } + } +} + + +void fntBitmapFont::puts ( sgVec3 curpos, float pointsize, float italic, const char *s ) +{ + float x0 = curpos[0]; + glRasterPos2i(0,0); // beware - coordinate may be negative + glBitmap(0, 0, 0, 0, curpos[0], curpos[1], NULL); + for (int i = 0; s[i] != '\0'; i++) { + if (s[i] == '\n') { + curpos[0] = x0; + curpos[1] -= height; + glRasterPos2i(0,0); // beware - coordinate may be negative + glBitmap(0, 0, 0, 0, curpos[0], curpos[1], NULL); + } else { + int j = (GLubyte) s[i] - first; + if (j >= 0 && j < count) { + glBitmap(data[j][0], height, xorig, yorig, (float) data[j][0], 0, data[j] + 1); + curpos[0] += data[j][0]; + } + } + } +} + + +int fntBitmapFont::hasGlyph ( char c ) const +{ + int i = (GLubyte) c - first; + return i >= 0 && i < count && data[i][0] > 0; +} + + +void fntBitmapFont::getBBox ( const char *s, float pointsize, float italic, + float *left, float *right, + float *bot , float *top ) +{ + float l, r, b, t; + + if (count > 0) { + int x1 = 0, y1 = 0, x = 0, y = 0; + for (int i = 0; s[i] != '\0'; i++) { + if (s[i] == '\n') { + if (x > x1) x1 = x; + y -= height; + x = 0; + } else { + int j = (GLubyte) s[i] - first; + if (j >= 0 && j < count && data[j][0] > 0) { + x += data[j][0]; + y1 = y; + } + } + } + if (x > x1) x1 = x; + l = - xorig; + r = (float) x1 - xorig; + b = (float) y1 - yorig; + t = (float) height - yorig; + } else { + l = r = b = t = 0.0f; + } + + if (left != NULL) *left = l; + if (right != NULL) *right = r; + if (bot != NULL) *bot = b; + if (top != NULL) *top = t; +} + + + +// This is to match the FreeGLUT definitions below + +struct SFG_Font { + const char* Name; /* The source font name */ + int Quantity; /* Number of chars in font */ + int Height; /* Height of the characters */ + const GLubyte** Characters; /* The characters mapping */ + + float xorig, yorig; /* Relative origin of the character */ +}; + + +/* + * Below is the font data taken from FreeGLUT. Nothing is changed except + * the structures are made "static" to avoid namespace pollution. + */ + + +/* + * freeglut_font_data.c + * + * This file has been automatically generated by the genfonts utility. + * + * Copyright (c) 1999-2000 by Pawel W. Olszta + * Written by Pawel W. Olszta, + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Sotware. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +/* + * Following fonts are defined in this file: + * + * 1. fgFontFixed8x13 <-misc-fixed-medium-r-normal--13-120-75-75-C-80-iso8859-1> + * 2. fgFontFixed9x15 <-misc-fixed-medium-r-normal--15-140-75-75-C-90-iso8859-1> + * 3. fgFontHelvetica10 <-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1> + * 4. fgFontHelvetica12 <-adobe-helvetica-medium-r-normal--12-120-75-75-p-67-iso8859-1> + * 5. fgFontHelvetica18 <-adobe-helvetica-medium-r-normal--18-180-75-75-p-98-iso8859-1> + * 6. fgFontTimesRoman10 <-adobe-times-medium-r-normal--10-100-75-75-p-54-iso8859-1> + * 7. fgFontTimesRoman24 <-adobe-times-medium-r-normal--24-240-75-75-p-124-iso8859-1> + */ + +static const GLubyte Fixed8x13_Character_032[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* blank */ +static const GLubyte Fixed8x13_Character_097[] = { 8, 0, 0,116,140,132,124, 4,120, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte Fixed8x13_Character_098[] = { 8, 0, 0,184,196,132,132,196,184,128,128,128, 0, 0}; +static const GLubyte Fixed8x13_Character_099[] = { 8, 0, 0,120,132,128,128,132,120, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_100[] = { 8, 0, 0,116,140,132,132,140,116, 4, 4, 4, 0, 0}; +static const GLubyte Fixed8x13_Character_101[] = { 8, 0, 0,120,132,128,252,132,120, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_102[] = { 8, 0, 0, 64, 64, 64, 64,248, 64, 64, 68, 56, 0, 0}; +static const GLubyte Fixed8x13_Character_103[] = { 8,120,132,120,128,112,136,136,116, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_104[] = { 8, 0, 0,132,132,132,132,196,184,128,128,128, 0, 0}; +static const GLubyte Fixed8x13_Character_105[] = { 8, 0, 0,248, 32, 32, 32, 32, 96, 0, 32, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_106[] = { 8,112,136,136, 8, 8, 8, 8, 24, 0, 8, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_107[] = { 8, 0, 0,132,136,144,224,144,136,128,128,128, 0, 0}; +static const GLubyte Fixed8x13_Character_108[] = { 8, 0, 0,248, 32, 32, 32, 32, 32, 32, 32, 96, 0, 0}; +static const GLubyte Fixed8x13_Character_109[] = { 8, 0, 0,130,146,146,146,146,236, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_110[] = { 8, 0, 0,132,132,132,132,196,184, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_111[] = { 8, 0, 0,120,132,132,132,132,120, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_112[] = { 8,128,128,128,184,196,132,196,184, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_113[] = { 8, 4, 4, 4,116,140,132,140,116, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_114[] = { 8, 0, 0, 64, 64, 64, 64, 68,184, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_115[] = { 8, 0, 0,120,132, 24, 96,132,120, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_116[] = { 8, 0, 0, 56, 68, 64, 64, 64,248, 64, 64, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_117[] = { 8, 0, 0,116,136,136,136,136,136, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_119[] = { 8, 0, 0, 68,170,146,146,130,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_118[] = { 8, 0, 0, 32, 80, 80,136,136,136, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_120[] = { 8, 0, 0,132, 72, 48, 48, 72,132, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_121[] = { 8,120,132, 4,116,140,132,132,132, 0, 0, 0, 0, 0}; +static const GLubyte Fixed8x13_Character_122[] = { 8, 0, 0,252, 64, 32, 16, 8,252, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte Fixed8x13_Character_065[] = { 8, 0, 0,132,132,132,252,132,132,132, 72, 48, 0, 0}; /* "A" */ +static const GLubyte Fixed8x13_Character_066[] = { 8, 0, 0,252, 66, 66, 66,124, 66, 66, 66,252, 0, 0}; +static const GLubyte Fixed8x13_Character_067[] = { 8, 0, 0,120,132,128,128,128,128,128,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_068[] = { 8, 0, 0,252, 66, 66, 66, 66, 66, 66, 66,252, 0, 0}; +static const GLubyte Fixed8x13_Character_069[] = { 8, 0, 0,252,128,128,128,240,128,128,128,252, 0, 0}; +static const GLubyte Fixed8x13_Character_070[] = { 8, 0, 0,128,128,128,128,240,128,128,128,252, 0, 0}; +static const GLubyte Fixed8x13_Character_071[] = { 8, 0, 0,116,140,132,156,128,128,128,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_072[] = { 8, 0, 0,132,132,132,132,252,132,132,132,132, 0, 0}; +static const GLubyte Fixed8x13_Character_073[] = { 8, 0, 0,248, 32, 32, 32, 32, 32, 32, 32,248, 0, 0}; +static const GLubyte Fixed8x13_Character_074[] = { 8, 0, 0,112,136, 8, 8, 8, 8, 8, 8, 60, 0, 0}; +static const GLubyte Fixed8x13_Character_075[] = { 8, 0, 0,132,136,144,160,192,160,144,136,132, 0, 0}; +static const GLubyte Fixed8x13_Character_076[] = { 8, 0, 0,252,128,128,128,128,128,128,128,128, 0, 0}; +static const GLubyte Fixed8x13_Character_077[] = { 8, 0, 0,130,130,130,146,146,170,198,130,130, 0, 0}; +static const GLubyte Fixed8x13_Character_078[] = { 8, 0, 0,132,132,132,140,148,164,196,132,132, 0, 0}; +static const GLubyte Fixed8x13_Character_079[] = { 8, 0, 0,120,132,132,132,132,132,132,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_080[] = { 8, 0, 0,128,128,128,128,248,132,132,132,248, 0, 0}; +static const GLubyte Fixed8x13_Character_081[] = { 8, 0, 4,120,148,164,132,132,132,132,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_082[] = { 8, 0, 0,132,136,144,160,248,132,132,132,248, 0, 0}; +static const GLubyte Fixed8x13_Character_083[] = { 8, 0, 0,120,132, 4, 4,120,128,128,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_084[] = { 8, 0, 0, 16, 16, 16, 16, 16, 16, 16, 16,254, 0, 0}; +static const GLubyte Fixed8x13_Character_085[] = { 8, 0, 0,120,132,132,132,132,132,132,132,132, 0, 0}; +static const GLubyte Fixed8x13_Character_087[] = { 8, 0, 0, 68,170,146,146,146,130,130,130,130, 0, 0}; +static const GLubyte Fixed8x13_Character_086[] = { 8, 0, 0, 16, 40, 40, 40, 68, 68, 68,130,130, 0, 0}; +static const GLubyte Fixed8x13_Character_088[] = { 8, 0, 0,130,130, 68, 40, 16, 40, 68,130,130, 0, 0}; +static const GLubyte Fixed8x13_Character_089[] = { 8, 0, 0, 16, 16, 16, 16, 16, 40, 68,130,130, 0, 0}; +static const GLubyte Fixed8x13_Character_090[] = { 8, 0, 0,252,128,128, 64, 32, 16, 8, 4,252, 0, 0}; /* "Z" */ +static const GLubyte Fixed8x13_Character_048[] = { 8, 0, 0, 48, 72,132,132,132,132,132, 72, 48, 0, 0}; /* "0" */ +static const GLubyte Fixed8x13_Character_049[] = { 8, 0, 0,248, 32, 32, 32, 32, 32,160, 96, 32, 0, 0}; +static const GLubyte Fixed8x13_Character_050[] = { 8, 0, 0,252,128, 64, 48, 8, 4,132,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_051[] = { 8, 0, 0,120,132, 4, 4, 56, 16, 8, 4,252, 0, 0}; +static const GLubyte Fixed8x13_Character_052[] = { 8, 0, 0, 8, 8,252,136,136, 72, 40, 24, 8, 0, 0}; +static const GLubyte Fixed8x13_Character_053[] = { 8, 0, 0,120,132, 4, 4,196,184,128,128,252, 0, 0}; +static const GLubyte Fixed8x13_Character_054[] = { 8, 0, 0,120,132,132,196,184,128,128, 64, 56, 0, 0}; +static const GLubyte Fixed8x13_Character_055[] = { 8, 0, 0, 64, 64, 32, 32, 16, 16, 8, 4,252, 0, 0}; +static const GLubyte Fixed8x13_Character_056[] = { 8, 0, 0,120,132,132,132,120,132,132,132,120, 0, 0}; +static const GLubyte Fixed8x13_Character_057[] = { 8, 0, 0,112, 8, 4, 4,116,140,132,132,120, 0, 0}; /* "9" */ +static const GLubyte Fixed8x13_Character_096[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0, 16, 96,224, 0, 0}; /* "`" */ +static const GLubyte Fixed8x13_Character_126[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0,144,168, 72, 0, 0}; /* "~" */ +static const GLubyte Fixed8x13_Character_033[] = { 8, 0, 0,128, 0,128,128,128,128,128,128,128, 0, 0}; /* "!" */ +static const GLubyte Fixed8x13_Character_064[] = { 8, 0, 0,120,128,148,172,164,156,132,132,120, 0, 0}; /* "@" */ +static const GLubyte Fixed8x13_Character_035[] = { 8, 0, 0, 0, 72, 72,252, 72,252, 72, 72, 0, 0, 0}; /* "#" */ +static const GLubyte Fixed8x13_Character_036[] = { 8, 0, 0, 0, 32,240, 40,112,160,120, 32, 0, 0, 0}; /* "$" */ +static const GLubyte Fixed8x13_Character_037[] = { 8, 0, 0,136, 84, 72, 32, 16, 16, 72,164, 68, 0, 0}; /* "%" */ +static const GLubyte Fixed8x13_Character_094[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0,136, 80, 32, 0, 0}; /* "^" */ +static const GLubyte Fixed8x13_Character_038[] = { 8, 0, 0,116,136,148, 96,144,144, 96, 0, 0, 0, 0}; /* "&" */ +static const GLubyte Fixed8x13_Character_042[] = { 8, 0, 0, 0, 0, 72, 48,252, 48, 72, 0, 0, 0, 0}; /* "*" */ +static const GLubyte Fixed8x13_Character_040[] = { 8, 0, 0, 32, 64, 64,128,128,128, 64, 64, 32, 0, 0}; /* "(" */ +static const GLubyte Fixed8x13_Character_041[] = { 8, 0, 0,128, 64, 64, 32, 32, 32, 64, 64,128, 0, 0}; /* ")" */ +static const GLubyte Fixed8x13_Character_045[] = { 8, 0, 0, 0, 0, 0, 0,252, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte Fixed8x13_Character_095[] = { 8, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte Fixed8x13_Character_061[] = { 8, 0, 0, 0, 0,252, 0, 0,252, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte Fixed8x13_Character_043[] = { 8, 0, 0, 0, 0, 32, 32,248, 32, 32, 0, 0, 0, 0}; /* "+" */ +static const GLubyte Fixed8x13_Character_091[] = { 8, 0, 0,240,128,128,128,128,128,128,128,240, 0, 0}; /* "[" */ +static const GLubyte Fixed8x13_Character_123[] = { 8, 0, 0, 56, 64, 64, 32,192, 32, 64, 64, 56, 0, 0}; /* "{" */ +static const GLubyte Fixed8x13_Character_125[] = { 8, 0, 0,224, 16, 16, 32, 24, 32, 16, 16,224, 0, 0}; /* "}" */ +static const GLubyte Fixed8x13_Character_093[] = { 8, 0, 0,240, 16, 16, 16, 16, 16, 16, 16,240, 0, 0}; /* "]" */ +static const GLubyte Fixed8x13_Character_059[] = { 8, 0,128, 96,112, 0, 0, 32,112, 32, 0, 0, 0, 0}; /* ";" */ +static const GLubyte Fixed8x13_Character_058[] = { 8, 0, 64,224, 64, 0, 0, 64,224, 64, 0, 0, 0, 0}; /* ":" */ +static const GLubyte Fixed8x13_Character_044[] = { 8, 0,128, 96,112, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte Fixed8x13_Character_046[] = { 8, 0, 64,224, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte Fixed8x13_Character_060[] = { 8, 0, 0, 8, 16, 32, 64,128, 64, 32, 16, 8, 0, 0}; /* "<" */ +static const GLubyte Fixed8x13_Character_062[] = { 8, 0, 0,128, 64, 32, 16, 8, 16, 32, 64,128, 0, 0}; /* ">" */ +static const GLubyte Fixed8x13_Character_047[] = { 8, 0, 0,128,128, 64, 32, 16, 8, 4, 2, 2, 0, 0}; /* "/" */ +static const GLubyte Fixed8x13_Character_063[] = { 8, 0, 0, 16, 0, 16, 16, 8, 4,132,132,120, 0, 0}; /* "?" */ +static const GLubyte Fixed8x13_Character_092[] = { 8, 0, 0, 2, 2, 4, 8, 16, 32, 64,128,128, 0, 0}; /* "\" */ +static const GLubyte Fixed8x13_Character_034[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0,144,144,144, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte Fixed8x13_Character_039[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0, 32, 32, 32, 0, 0}; /* """ */ +static const GLubyte Fixed8x13_Character_124[] = { 8, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0}; /* """ */ + + +/* The font characters mapping: */ +static const GLubyte* Fixed8x13_Character_Map[] = {Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_032,Fixed8x13_Character_033,Fixed8x13_Character_034,Fixed8x13_Character_035,Fixed8x13_Character_036,Fixed8x13_Character_037,Fixed8x13_Character_038,Fixed8x13_Character_039,Fixed8x13_Character_040, + Fixed8x13_Character_041,Fixed8x13_Character_042,Fixed8x13_Character_043,Fixed8x13_Character_044,Fixed8x13_Character_045,Fixed8x13_Character_046,Fixed8x13_Character_047,Fixed8x13_Character_048,Fixed8x13_Character_049,Fixed8x13_Character_050,Fixed8x13_Character_051,Fixed8x13_Character_052,Fixed8x13_Character_053,Fixed8x13_Character_054,Fixed8x13_Character_055,Fixed8x13_Character_056,Fixed8x13_Character_057,Fixed8x13_Character_058,Fixed8x13_Character_059,Fixed8x13_Character_060,Fixed8x13_Character_061,Fixed8x13_Character_062,Fixed8x13_Character_063,Fixed8x13_Character_064,Fixed8x13_Character_065,Fixed8x13_Character_066,Fixed8x13_Character_067,Fixed8x13_Character_068,Fixed8x13_Character_069,Fixed8x13_Character_070,Fixed8x13_Character_071,Fixed8x13_Character_072,Fixed8x13_Character_073,Fixed8x13_Character_074,Fixed8x13_Character_075,Fixed8x13_Character_076,Fixed8x13_Character_077,Fixed8x13_Character_078,Fixed8x13_Character_079,Fixed8x13_Character_080,Fixed8x13_Character_081,Fixed8x13_Character_082, + Fixed8x13_Character_083,Fixed8x13_Character_084,Fixed8x13_Character_085,Fixed8x13_Character_086,Fixed8x13_Character_087,Fixed8x13_Character_088,Fixed8x13_Character_089,Fixed8x13_Character_090,Fixed8x13_Character_091,Fixed8x13_Character_092,Fixed8x13_Character_093,Fixed8x13_Character_094,Fixed8x13_Character_095,Fixed8x13_Character_096,Fixed8x13_Character_097,Fixed8x13_Character_098,Fixed8x13_Character_099,Fixed8x13_Character_100,Fixed8x13_Character_101,Fixed8x13_Character_102,Fixed8x13_Character_103,Fixed8x13_Character_104,Fixed8x13_Character_105,Fixed8x13_Character_106,Fixed8x13_Character_107,Fixed8x13_Character_108,Fixed8x13_Character_109,Fixed8x13_Character_110,Fixed8x13_Character_111,Fixed8x13_Character_112,Fixed8x13_Character_113,Fixed8x13_Character_114,Fixed8x13_Character_115,Fixed8x13_Character_116,Fixed8x13_Character_117,Fixed8x13_Character_118,Fixed8x13_Character_119,Fixed8x13_Character_120,Fixed8x13_Character_121,Fixed8x13_Character_122,Fixed8x13_Character_123,Fixed8x13_Character_124, + Fixed8x13_Character_125,Fixed8x13_Character_126,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042, + Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042, + Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042, + Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,Fixed8x13_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontFixed8x13 = { "-misc-fixed-medium-r-normal--13-120-75-75-C-80-iso8859-1", 93, 13, Fixed8x13_Character_Map, -1.0f, 2.0f }; + +static const GLubyte Fixed9x15_Character_032[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* blank */ +static const GLubyte Fixed9x15_Character_097[] = { 9, 0, 0, 0, 0, 0, 0,122, 0,134, 0,130, 0,126, 0, 2, 0, 2, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte Fixed9x15_Character_098[] = { 9, 0, 0, 0, 0, 0, 0,188, 0,194, 0,130, 0,130, 0,130, 0,194, 0,188, 0,128, 0,128, 0,128, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_099[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,128, 0,128, 0,128, 0,130, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_100[] = { 9, 0, 0, 0, 0, 0, 0,122, 0,134, 0,130, 0,130, 0,130, 0,134, 0,122, 0, 2, 0, 2, 0, 2, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_101[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,128, 0,128, 0,254, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_102[] = { 9, 0, 0, 0, 0, 0, 0, 32, 0, 32, 0, 32, 0, 32, 0,248, 0, 32, 0, 32, 0, 34, 0, 34, 0, 28, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_103[] = { 9,124, 0,130, 0,130, 0,124, 0,128, 0,120, 0,132, 0,132, 0,132, 0,122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_104[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,130, 0,130, 0,194, 0,188, 0,128, 0,128, 0,128, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_105[] = { 9, 0, 0, 0, 0, 0, 0,248, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0,224, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_106[] = { 9,120, 0,132, 0,132, 0,132, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 28, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_107[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,140, 0,176, 0,192, 0,176, 0,140, 0,130, 0,128, 0,128, 0,128, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_108[] = { 9, 0, 0, 0, 0, 0, 0,248, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0,224, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_109[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,146, 0,146, 0,146, 0,146, 0,146, 0,236, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_110[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,130, 0,130, 0,194, 0,188, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_111[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,130, 0,130, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_112[] = { 9,128, 0,128, 0,128, 0,188, 0,194, 0,130, 0,130, 0,130, 0,194, 0,188, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_113[] = { 9, 2, 0, 2, 0, 2, 0,122, 0,134, 0,130, 0,130, 0,130, 0,134, 0,122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_114[] = { 9, 0, 0, 0, 0, 0, 0, 64, 0, 64, 0, 64, 0, 64, 0, 66, 0, 98, 0,156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_115[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0, 2, 0,124, 0,128, 0,130, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_116[] = { 9, 0, 0, 0, 0, 0, 0, 28, 0, 34, 0, 32, 0, 32, 0, 32, 0, 32, 0,252, 0, 32, 0, 32, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_117[] = { 9, 0, 0, 0, 0, 0, 0,122, 0,132, 0,132, 0,132, 0,132, 0,132, 0,132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_119[] = { 9, 0, 0, 0, 0, 0, 0, 68, 0,170, 0,146, 0,146, 0,146, 0,130, 0,130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_118[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 40, 0, 40, 0, 68, 0, 68, 0,130, 0,130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_120[] = { 9, 0, 0, 0, 0, 0, 0,130, 0, 68, 0, 40, 0, 16, 0, 40, 0, 68, 0,130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_121[] = { 9,120, 0,132, 0, 4, 0,116, 0,140, 0,132, 0,132, 0,132, 0,132, 0,132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_122[] = { 9, 0, 0, 0, 0, 0, 0,254, 0, 64, 0, 32, 0, 16, 0, 8, 0, 4, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte Fixed9x15_Character_065[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,254, 0,130, 0,130, 0,130, 0, 68, 0, 40, 0, 16, 0, 0, 0, 0, 0}; /* "A" */ +static const GLubyte Fixed9x15_Character_066[] = { 9, 0, 0, 0, 0, 0, 0,252, 0, 66, 0, 66, 0, 66, 0, 66, 0,124, 0, 66, 0, 66, 0, 66, 0,252, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_067[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,130, 0,124, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_068[] = { 9, 0, 0, 0, 0, 0, 0,252, 0, 66, 0, 66, 0, 66, 0, 66, 0, 66, 0, 66, 0, 66, 0, 66, 0,252, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_069[] = { 9, 0, 0, 0, 0, 0, 0,254, 0, 64, 0, 64, 0, 64, 0, 64, 0,120, 0, 64, 0, 64, 0, 64, 0,254, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_070[] = { 9, 0, 0, 0, 0, 0, 0, 64, 0, 64, 0, 64, 0, 64, 0, 64, 0,120, 0, 64, 0, 64, 0, 64, 0,254, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_071[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,130, 0,130, 0,142, 0,128, 0,128, 0,128, 0,130, 0,124, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_072[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,130, 0,130, 0,254, 0,130, 0,130, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_073[] = { 9, 0, 0, 0, 0, 0, 0,248, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0,248, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_074[] = { 9, 0, 0, 0, 0, 0, 0,120, 0,132, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 4, 0, 30, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_075[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,132, 0,136, 0,144, 0,160, 0,224, 0,144, 0,136, 0,132, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_076[] = { 9, 0, 0, 0, 0, 0, 0,254, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_077[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,146, 0,146, 0,170, 0,170, 0,198, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_078[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,134, 0,138, 0,146, 0,162, 0,194, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_079[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_080[] = { 9, 0, 0, 0, 0, 0, 0,128, 0,128, 0,128, 0,128, 0,128, 0,252, 0,130, 0,130, 0,130, 0,252, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_081[] = { 9, 0, 0, 6, 0, 8, 0,124, 0,146, 0,162, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_082[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,132, 0,136, 0,144, 0,252, 0,130, 0,130, 0,130, 0,252, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_083[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,130, 0, 2, 0, 12, 0,112, 0,128, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_084[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0,254, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_085[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_087[] = { 9, 0, 0, 0, 0, 0, 0, 68, 0,170, 0,146, 0,146, 0,146, 0,146, 0,130, 0,130, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_086[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 40, 0, 40, 0, 40, 0, 68, 0, 68, 0, 68, 0,130, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_088[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0, 68, 0, 40, 0, 16, 0, 16, 0, 40, 0, 68, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_089[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 40, 0, 68, 0,130, 0,130, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_090[] = { 9, 0, 0, 0, 0, 0, 0,254, 0,128, 0,128, 0, 64, 0, 32, 0, 16, 0, 8, 0, 4, 0, 2, 0,254, 0, 0, 0, 0, 0}; /* "Z" */ +static const GLubyte Fixed9x15_Character_048[] = { 9, 0, 0, 0, 0, 0, 0, 56, 0, 68, 0,130, 0,130, 0,130, 0,130, 0,130, 0,130, 0, 68, 0, 56, 0, 0, 0, 0, 0}; /* "0" */ +static const GLubyte Fixed9x15_Character_049[] = { 9, 0, 0, 0, 0, 0, 0,254, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0,144, 0, 80, 0, 48, 0, 16, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_050[] = { 9, 0, 0, 0, 0, 0, 0,254, 0,128, 0, 64, 0, 48, 0, 8, 0, 4, 0, 2, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_051[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0, 2, 0, 2, 0, 2, 0, 28, 0, 8, 0, 4, 0, 2, 0,254, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_052[] = { 9, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 4, 0,254, 0,132, 0, 68, 0, 36, 0, 20, 0, 12, 0, 4, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_053[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0, 2, 0, 2, 0, 2, 0,194, 0,188, 0,128, 0,128, 0,254, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_054[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,130, 0,130, 0,130, 0,194, 0,188, 0,128, 0,128, 0, 64, 0, 60, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_055[] = { 9, 0, 0, 0, 0, 0, 0, 64, 0, 64, 0, 32, 0, 32, 0, 16, 0, 8, 0, 4, 0, 2, 0, 2, 0,254, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_056[] = { 9, 0, 0, 0, 0, 0, 0, 56, 0, 68, 0,130, 0,130, 0, 68, 0, 56, 0, 68, 0,130, 0, 68, 0, 56, 0, 0, 0, 0, 0}; +static const GLubyte Fixed9x15_Character_057[] = { 9, 0, 0, 0, 0, 0, 0,120, 0, 4, 0, 2, 0, 2, 0,122, 0,134, 0,130, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; /* "9" */ +static const GLubyte Fixed9x15_Character_096[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 32, 0, 64, 0,192, 0, 0, 0, 0, 0}; /* "`" */ +static const GLubyte Fixed9x15_Character_126[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,140, 0,146, 0, 98, 0, 0, 0, 0, 0}; /* "~" */ +static const GLubyte Fixed9x15_Character_033[] = { 9, 0, 0, 0, 0, 0, 0,128, 0,128, 0, 0, 0, 0, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0, 0, 0}; /* "!" */ +static const GLubyte Fixed9x15_Character_064[] = { 9, 0, 0, 0, 0, 0, 0,124, 0,128, 0,128, 0,154, 0,166, 0,162, 0,158, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; /* "@" */ +static const GLubyte Fixed9x15_Character_035[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 72, 0, 72, 0,252, 0, 72, 0, 72, 0,252, 0, 72, 0, 72, 0, 0, 0, 0, 0, 0, 0}; /* "#" */ +static const GLubyte Fixed9x15_Character_036[] = { 9, 0, 0, 0, 0, 16, 0,124, 0,146, 0, 18, 0, 18, 0, 20, 0, 56, 0, 80, 0,144, 0,146, 0,124, 0, 16, 0, 0, 0}; /* "$" */ +static const GLubyte Fixed9x15_Character_037[] = { 9, 0, 0, 0, 0, 0, 0,132, 0, 74, 0, 74, 0, 36, 0, 16, 0, 16, 0, 72, 0,164, 0,164, 0, 66, 0, 0, 0, 0, 0}; /* "%" */ +static const GLubyte Fixed9x15_Character_094[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,130, 0, 68, 0, 40, 0, 16, 0, 0, 0, 0, 0}; /* "^" */ +static const GLubyte Fixed9x15_Character_038[] = { 9, 0, 0, 0, 0, 0, 0, 98, 0,148, 0,136, 0,148, 0, 98, 0, 96, 0,144, 0,144, 0,144, 0, 96, 0, 0, 0, 0, 0}; /* "&" */ +static const GLubyte Fixed9x15_Character_042[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0,146, 0, 84, 0, 56, 0, 84, 0,146, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "*" */ +static const GLubyte Fixed9x15_Character_040[] = { 9, 0, 0, 0, 0, 32, 0, 64, 0, 64, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0, 64, 0, 64, 0, 32, 0, 0, 0}; /* "(" */ +static const GLubyte Fixed9x15_Character_041[] = { 9, 0, 0, 0, 0,128, 0, 64, 0, 64, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 64, 0, 64, 0,128, 0, 0, 0}; /* ")" */ +static const GLubyte Fixed9x15_Character_045[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte Fixed9x15_Character_095[] = { 9, 0, 0, 0, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte Fixed9x15_Character_061[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,254, 0, 0, 0, 0, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte Fixed9x15_Character_043[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 16, 0, 16, 0,254, 0, 16, 0, 16, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "+" */ +static const GLubyte Fixed9x15_Character_091[] = { 9, 0, 0, 0, 0,240, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,128, 0,240, 0, 0, 0}; /* "[" */ +static const GLubyte Fixed9x15_Character_123[] = { 9, 0, 0, 0, 0, 56, 0, 64, 0, 64, 0, 64, 0, 32, 0,192, 0,192, 0, 32, 0, 64, 0, 64, 0, 64, 0, 56, 0, 0, 0}; /* "{" */ +static const GLubyte Fixed9x15_Character_125[] = { 9, 0, 0, 0, 0,224, 0, 16, 0, 16, 0, 16, 0, 32, 0, 24, 0, 24, 0, 32, 0, 16, 0, 16, 0, 16, 0,224, 0, 0, 0}; /* "}" */ +static const GLubyte Fixed9x15_Character_093[] = { 9, 0, 0, 0, 0,240, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0,240, 0, 0, 0}; /* "]" */ +static const GLubyte Fixed9x15_Character_059[] = { 9,128, 0, 64, 0, 64, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ";" */ +static const GLubyte Fixed9x15_Character_058[] = { 9, 0, 0, 0, 0, 0, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ":" */ +static const GLubyte Fixed9x15_Character_044[] = { 9,128, 0, 64, 0, 64, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte Fixed9x15_Character_046[] = { 9, 0, 0, 0, 0, 0, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte Fixed9x15_Character_060[] = { 9, 0, 0, 0, 0, 0, 0, 8, 0, 16, 0, 32, 0, 64, 0,128, 0,128, 0, 64, 0, 32, 0, 16, 0, 8, 0, 0, 0, 0, 0}; /* "<" */ +static const GLubyte Fixed9x15_Character_062[] = { 9, 0, 0, 0, 0, 0, 0,128, 0, 64, 0, 32, 0, 16, 0, 8, 0, 8, 0, 16, 0, 32, 0, 64, 0,128, 0, 0, 0, 0, 0}; /* ">" */ +static const GLubyte Fixed9x15_Character_047[] = { 9, 0, 0, 0, 0, 0, 0,128, 0, 64, 0, 64, 0, 32, 0, 16, 0, 16, 0, 8, 0, 4, 0, 4, 0, 2, 0, 0, 0, 0, 0}; /* "/" */ +static const GLubyte Fixed9x15_Character_063[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 16, 0, 16, 0, 8, 0, 4, 0, 2, 0,130, 0,130, 0,124, 0, 0, 0, 0, 0}; /* "?" */ +static const GLubyte Fixed9x15_Character_092[] = { 9, 0, 0, 0, 0, 0, 0, 2, 0, 4, 0, 4, 0, 8, 0, 16, 0, 16, 0, 32, 0, 64, 0, 64, 0,128, 0, 0, 0, 0, 0}; /* "\" */ +static const GLubyte Fixed9x15_Character_034[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,144, 0,144, 0,144, 0, 0, 0, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte Fixed9x15_Character_039[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 32, 0, 32, 0, 32, 0, 0, 0, 0, 0}; /* "'" */ +static const GLubyte Fixed9x15_Character_124[] = { 9, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 0, 0, 0, 0}; /* "|" */ + + +/* The font characters mapping: */ +static const GLubyte* Fixed9x15_Character_Map[] = {Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_032,Fixed9x15_Character_033,Fixed9x15_Character_034,Fixed9x15_Character_035,Fixed9x15_Character_036,Fixed9x15_Character_037,Fixed9x15_Character_038,Fixed9x15_Character_039,Fixed9x15_Character_040, + Fixed9x15_Character_041,Fixed9x15_Character_042,Fixed9x15_Character_043,Fixed9x15_Character_044,Fixed9x15_Character_045,Fixed9x15_Character_046,Fixed9x15_Character_047,Fixed9x15_Character_048,Fixed9x15_Character_049,Fixed9x15_Character_050,Fixed9x15_Character_051,Fixed9x15_Character_052,Fixed9x15_Character_053,Fixed9x15_Character_054,Fixed9x15_Character_055,Fixed9x15_Character_056,Fixed9x15_Character_057,Fixed9x15_Character_058,Fixed9x15_Character_059,Fixed9x15_Character_060,Fixed9x15_Character_061,Fixed9x15_Character_062,Fixed9x15_Character_063,Fixed9x15_Character_064,Fixed9x15_Character_065,Fixed9x15_Character_066,Fixed9x15_Character_067,Fixed9x15_Character_068,Fixed9x15_Character_069,Fixed9x15_Character_070,Fixed9x15_Character_071,Fixed9x15_Character_072,Fixed9x15_Character_073,Fixed9x15_Character_074,Fixed9x15_Character_075,Fixed9x15_Character_076,Fixed9x15_Character_077,Fixed9x15_Character_078,Fixed9x15_Character_079,Fixed9x15_Character_080,Fixed9x15_Character_081,Fixed9x15_Character_082, + Fixed9x15_Character_083,Fixed9x15_Character_084,Fixed9x15_Character_085,Fixed9x15_Character_086,Fixed9x15_Character_087,Fixed9x15_Character_088,Fixed9x15_Character_089,Fixed9x15_Character_090,Fixed9x15_Character_091,Fixed9x15_Character_092,Fixed9x15_Character_093,Fixed9x15_Character_094,Fixed9x15_Character_095,Fixed9x15_Character_096,Fixed9x15_Character_097,Fixed9x15_Character_098,Fixed9x15_Character_099,Fixed9x15_Character_100,Fixed9x15_Character_101,Fixed9x15_Character_102,Fixed9x15_Character_103,Fixed9x15_Character_104,Fixed9x15_Character_105,Fixed9x15_Character_106,Fixed9x15_Character_107,Fixed9x15_Character_108,Fixed9x15_Character_109,Fixed9x15_Character_110,Fixed9x15_Character_111,Fixed9x15_Character_112,Fixed9x15_Character_113,Fixed9x15_Character_114,Fixed9x15_Character_115,Fixed9x15_Character_116,Fixed9x15_Character_117,Fixed9x15_Character_118,Fixed9x15_Character_119,Fixed9x15_Character_120,Fixed9x15_Character_121,Fixed9x15_Character_122,Fixed9x15_Character_123,Fixed9x15_Character_124, + Fixed9x15_Character_125,Fixed9x15_Character_126,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042, + Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042, + Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042, + Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,Fixed9x15_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontFixed9x15 = { "-misc-fixed-medium-r-normal--15-140-75-75-C-90-iso8859-1", 93, 15, Fixed9x15_Character_Map, -1.0f, 3.0f }; + +static const GLubyte Helvetica10_Character_032[] = { 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* " " */ +static const GLubyte Helvetica10_Character_097[] = { 5, 0, 0,104,144,144,112, 16,224, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte Helvetica10_Character_098[] = { 6, 0, 0,176,200,136,136,200,176,128,128, 0, 0, 0}; /* "b" */ +static const GLubyte Helvetica10_Character_099[] = { 5, 0, 0, 96,144,128,128,144, 96, 0, 0, 0, 0, 0}; /* "c" */ +static const GLubyte Helvetica10_Character_100[] = { 6, 0, 0,104,152,136,136,152,104, 8, 8, 0, 0, 0}; /* "d" */ +static const GLubyte Helvetica10_Character_101[] = { 5, 0, 0, 96,144,128,240,144, 96, 0, 0, 0, 0, 0}; /* "e" */ +static const GLubyte Helvetica10_Character_102[] = { 4, 0, 0, 64, 64, 64, 64, 64,224, 64, 48, 0, 0, 0}; /* "f" */ +static const GLubyte Helvetica10_Character_103[] = { 6,112, 8,104,152,136,136,152,104, 0, 0, 0, 0, 0}; /* "g" */ +static const GLubyte Helvetica10_Character_104[] = { 6, 0, 0,136,136,136,136,200,176,128,128, 0, 0, 0}; /* "h" */ +static const GLubyte Helvetica10_Character_105[] = { 2, 0, 0,128,128,128,128,128,128, 0,128, 0, 0, 0}; /* "i" */ +static const GLubyte Helvetica10_Character_106[] = { 2, 0,128,128,128,128,128,128,128, 0,128, 0, 0, 0}; /* "j" */ +static const GLubyte Helvetica10_Character_107[] = { 5, 0, 0,144,144,160,192,160,144,128,128, 0, 0, 0}; /* "k" */ +static const GLubyte Helvetica10_Character_108[] = { 2, 0, 0,128,128,128,128,128,128,128,128, 0, 0, 0}; /* "l" */ +static const GLubyte Helvetica10_Character_109[] = { 8, 0, 0,146,146,146,146,146,236, 0, 0, 0, 0, 0}; /* "m" */ +static const GLubyte Helvetica10_Character_110[] = { 6, 0, 0,136,136,136,136,200,176, 0, 0, 0, 0, 0}; /* "n" */ +static const GLubyte Helvetica10_Character_111[] = { 6, 0, 0,112,136,136,136,136,112, 0, 0, 0, 0, 0}; /* "o" */ +static const GLubyte Helvetica10_Character_112[] = { 6,128,128,176,200,136,136,200,176, 0, 0, 0, 0, 0}; /* "p" */ +static const GLubyte Helvetica10_Character_113[] = { 6, 8, 8,104,152,136,136,152,104, 0, 0, 0, 0, 0}; /* "q" */ +static const GLubyte Helvetica10_Character_114[] = { 4, 0, 0,128,128,128,128,192,160, 0, 0, 0, 0, 0}; /* "r" */ +static const GLubyte Helvetica10_Character_115[] = { 5, 0, 0, 96,144, 16, 96,144, 96, 0, 0, 0, 0, 0}; /* "s" */ +static const GLubyte Helvetica10_Character_116[] = { 4, 0, 0, 96, 64, 64, 64, 64,224, 64, 64, 0, 0, 0}; /* "t" */ +static const GLubyte Helvetica10_Character_117[] = { 5, 0, 0,112,144,144,144,144,144, 0, 0, 0, 0, 0}; /* "u" */ +static const GLubyte Helvetica10_Character_118[] = { 6, 0, 0, 32, 32, 80, 80,136,136, 0, 0, 0, 0, 0}; /* "v" */ +static const GLubyte Helvetica10_Character_119[] = { 8, 0, 0, 40, 40, 84, 84,146,146, 0, 0, 0, 0, 0}; /* "w" */ +static const GLubyte Helvetica10_Character_120[] = { 6, 0, 0,136,136, 80, 32, 80,136, 0, 0, 0, 0, 0}; /* "x" */ +static const GLubyte Helvetica10_Character_121[] = { 5,128, 64, 64, 96,160,160,144,144, 0, 0, 0, 0, 0}; /* "y" */ +static const GLubyte Helvetica10_Character_122[] = { 5, 0, 0,240,128, 64, 32, 16,240, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte Helvetica10_Character_065[] = { 7, 0, 0,130,130,124, 68, 40, 40, 16, 16, 0, 0, 0}; /* "A" */ +static const GLubyte Helvetica10_Character_066[] = { 7, 0, 0,240,136,136,136,240,136,136,240, 0, 0, 0}; /* "B" */ +static const GLubyte Helvetica10_Character_067[] = { 8, 0, 0,120,132,128,128,128,128,132,120, 0, 0, 0}; /* "C" */ +static const GLubyte Helvetica10_Character_068[] = { 8, 0, 0,240,136,132,132,132,132,136,240, 0, 0, 0}; /* "D" */ +static const GLubyte Helvetica10_Character_069[] = { 7, 0, 0,248,128,128,128,248,128,128,248, 0, 0, 0}; /* "E" */ +static const GLubyte Helvetica10_Character_070[] = { 6, 0, 0,128,128,128,128,240,128,128,248, 0, 0, 0}; /* "F" */ +static const GLubyte Helvetica10_Character_071[] = { 8, 0, 0,116,140,132,140,128,128,132,120, 0, 0, 0}; /* "G" */ +static const GLubyte Helvetica10_Character_072[] = { 8, 0, 0,132,132,132,132,252,132,132,132, 0, 0, 0}; /* "H" */ +static const GLubyte Helvetica10_Character_073[] = { 3, 0, 0,128,128,128,128,128,128,128,128, 0, 0, 0}; /* "I" */ +static const GLubyte Helvetica10_Character_074[] = { 5, 0, 0, 96,144, 16, 16, 16, 16, 16, 16, 0, 0, 0}; /* "J" */ +static const GLubyte Helvetica10_Character_075[] = { 7, 0, 0,136,136,144,144,224,160,144,136, 0, 0, 0}; /* "K" */ +static const GLubyte Helvetica10_Character_076[] = { 6, 0, 0,240,128,128,128,128,128,128,128, 0, 0, 0}; /* "L" */ +static const GLubyte Helvetica10_Character_077[] = { 9, 0, 0, 0, 0,146, 0,146, 0,146, 0,170, 0,170, 0,198, 0,198, 0,130, 0, 0, 0, 0, 0, 0, 0}; /* "M" */ +static const GLubyte Helvetica10_Character_078[] = { 8, 0, 0,140,140,148,148,164,164,196,196, 0, 0, 0}; /* "N" */ +static const GLubyte Helvetica10_Character_079[] = { 8, 0, 0,120,132,132,132,132,132,132,120, 0, 0, 0}; /* "O" */ +static const GLubyte Helvetica10_Character_080[] = { 7, 0, 0,128,128,128,128,240,136,136,240, 0, 0, 0}; /* "P" */ +static const GLubyte Helvetica10_Character_081[] = { 8, 0, 2,124,140,148,132,132,132,132,120, 0, 0, 0}; /* "Q" */ +static const GLubyte Helvetica10_Character_082[] = { 7, 0, 0,136,136,136,136,240,136,136,240, 0, 0, 0}; /* "R" */ +static const GLubyte Helvetica10_Character_083[] = { 7, 0, 0,112,136,136, 8,112,128,136,112, 0, 0, 0}; /* "S" */ +static const GLubyte Helvetica10_Character_084[] = { 5, 0, 0, 32, 32, 32, 32, 32, 32, 32,248, 0, 0, 0}; /* "T" */ +static const GLubyte Helvetica10_Character_085[] = { 8, 0, 0,120,132,132,132,132,132,132,132, 0, 0, 0}; /* "U" */ +static const GLubyte Helvetica10_Character_086[] = { 7, 0, 0, 16, 40, 40, 68, 68, 68,130,130, 0, 0, 0}; /* "V" */ +static const GLubyte Helvetica10_Character_087[] = { 9, 0, 0, 0, 0, 34, 0, 34, 0, 34, 0, 85, 0, 73, 0, 73, 0,136,128,136,128, 0, 0, 0, 0, 0, 0}; /* "W" */ +static const GLubyte Helvetica10_Character_088[] = { 7, 0, 0,136,136, 80, 80, 32, 80,136,136, 0, 0, 0}; /* "X" */ +static const GLubyte Helvetica10_Character_089[] = { 7, 0, 0, 16, 16, 16, 40, 40, 68, 68,130, 0, 0, 0}; /* "Y" */ +static const GLubyte Helvetica10_Character_090[] = { 7, 0, 0,248,128, 64, 32, 32, 16, 8,248, 0, 0, 0}; /* "Z" */ +static const GLubyte Helvetica10_Character_048[] = { 6, 0, 0,112,136,136,136,136,136,136,112, 0, 0, 0}; /* "0" */ +static const GLubyte Helvetica10_Character_049[] = { 6, 0, 0, 64, 64, 64, 64, 64, 64,192, 64, 0, 0, 0}; /* "1" */ +static const GLubyte Helvetica10_Character_050[] = { 6, 0, 0,248,128, 64, 48, 8, 8,136,112, 0, 0, 0}; /* "2" */ +static const GLubyte Helvetica10_Character_051[] = { 6, 0, 0,112,136, 8, 8, 48, 8,136,112, 0, 0, 0}; /* "3" */ +static const GLubyte Helvetica10_Character_052[] = { 6, 0, 0, 16, 16,248,144, 80, 80, 48, 16, 0, 0, 0}; /* "4" */ +static const GLubyte Helvetica10_Character_053[] = { 6, 0, 0,112,136, 8, 8,240,128,128,248, 0, 0, 0}; /* "5" */ +static const GLubyte Helvetica10_Character_054[] = { 6, 0, 0,112,136,136,200,176,128,136,112, 0, 0, 0}; /* "6" */ +static const GLubyte Helvetica10_Character_055[] = { 6, 0, 0, 64, 64, 32, 32, 16, 16, 8,248, 0, 0, 0}; /* "7" */ +static const GLubyte Helvetica10_Character_056[] = { 6, 0, 0,112,136,136,136,112,136,136,112, 0, 0, 0}; /* "8" */ +static const GLubyte Helvetica10_Character_057[] = { 6, 0, 0,112,136, 8,104,152,136,136,112, 0, 0, 0}; /* "9" */ +static const GLubyte Helvetica10_Character_096[] = { 3, 0, 0, 0, 0, 0, 0, 0,128,128, 64, 0, 0, 0}; /* "`" */ +static const GLubyte Helvetica10_Character_126[] = { 7, 0, 0, 0, 0, 0,152,100, 0, 0, 0, 0, 0, 0}; /* "~" */ +static const GLubyte Helvetica10_Character_033[] = { 3, 0, 0,128, 0,128,128,128,128,128,128, 0, 0, 0}; /* "!" */ +static const GLubyte Helvetica10_Character_064[] = { 11, 62, 0, 64, 0,155, 0,164,128,164,128,162, 64,146, 64, 77, 64, 32,128, 31, 0, 0, 0, 0, 0, 0, 0}; /* "@" */ +static const GLubyte Helvetica10_Character_035[] = { 6, 0, 0, 80, 80,248, 40,124, 40, 40, 0, 0, 0, 0}; /* "#" */ +static const GLubyte Helvetica10_Character_036[] = { 6, 0, 32,112,168, 40,112,160,168,112, 32, 0, 0, 0}; /* "$" */ +static const GLubyte Helvetica10_Character_037[] = { 9, 0, 0, 0, 0, 38, 0, 41, 0, 22, 0, 16, 0, 8, 0,104, 0,148, 0,100, 0, 0, 0, 0, 0, 0, 0}; /* "%" */ +static const GLubyte Helvetica10_Character_094[] = { 6, 0, 0, 0, 0, 0,136, 80, 80, 32, 32, 0, 0, 0}; /* "^" */ +static const GLubyte Helvetica10_Character_038[] = { 8, 0, 0,100,152,152,164, 96, 80, 80, 32, 0, 0, 0}; /* "&" */ +static const GLubyte Helvetica10_Character_042[] = { 4, 0, 0, 0, 0, 0, 0, 0,160, 64,160, 0, 0, 0}; /* "*" */ +static const GLubyte Helvetica10_Character_040[] = { 4, 32, 64, 64,128,128,128,128, 64, 64, 32, 0, 0, 0}; /* "(" */ +static const GLubyte Helvetica10_Character_041[] = { 4,128, 64, 64, 32, 32, 32, 32, 64, 64,128, 0, 0, 0}; /* ")" */ +static const GLubyte Helvetica10_Character_045[] = { 7, 0, 0, 0, 0, 0,248, 0, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte Helvetica10_Character_095[] = { 6,252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte Helvetica10_Character_061[] = { 5, 0, 0, 0, 0,240, 0,240, 0, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte Helvetica10_Character_043[] = { 6, 0, 0, 0, 32, 32,248, 32, 32, 0, 0, 0, 0, 0}; /* "+" */ +static const GLubyte Helvetica10_Character_091[] = { 3,192,128,128,128,128,128,128,128,128,192, 0, 0, 0}; /* "[" */ +static const GLubyte Helvetica10_Character_123[] = { 3, 32, 64, 64, 64, 64,128, 64, 64, 64, 32, 0, 0, 0}; /* "{" */ +static const GLubyte Helvetica10_Character_125[] = { 3,128, 64, 64, 64, 64, 32, 64, 64, 64,128, 0, 0, 0}; /* "}" */ +static const GLubyte Helvetica10_Character_093[] = { 3,192, 64, 64, 64, 64, 64, 64, 64, 64,192, 0, 0, 0}; /* "]" */ +static const GLubyte Helvetica10_Character_059[] = { 3,128, 64, 64, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0}; /* ";" */ +static const GLubyte Helvetica10_Character_058[] = { 3, 0, 0,128, 0, 0, 0, 0,128, 0, 0, 0, 0, 0}; /* ":" */ +static const GLubyte Helvetica10_Character_044[] = { 3,128, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte Helvetica10_Character_046[] = { 3, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte Helvetica10_Character_060[] = { 6, 0, 0, 0, 32, 64,128, 64, 32, 0, 0, 0, 0, 0}; /* "<" */ +static const GLubyte Helvetica10_Character_062[] = { 6, 0, 0, 0,128, 64, 32, 64,128, 0, 0, 0, 0, 0}; /* ">" */ +static const GLubyte Helvetica10_Character_047[] = { 3, 0, 0,128,128, 64, 64, 64, 64, 32, 32, 0, 0, 0}; /* "/" */ +static const GLubyte Helvetica10_Character_063[] = { 6, 0, 0, 64, 0, 64, 64, 32, 16,144, 96, 0, 0, 0}; /* "?" */ +static const GLubyte Helvetica10_Character_092[] = { 3, 0, 0, 32, 32, 64, 64, 64, 64,128,128, 0, 0, 0}; /* "\" */ +static const GLubyte Helvetica10_Character_034[] = { 4, 0, 0, 0, 0, 0, 0, 0, 0,160,160, 0, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte Helvetica10_Character_039[] = { 3, 0, 0, 0, 0, 0, 0, 0,128, 64, 64, 0, 0, 0}; /* "'" */ +static const GLubyte Helvetica10_Character_124[] = { 3, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0}; /* "|" */ + + +/* The font characters mapping: */ +static const GLubyte* Helvetica10_Character_Map[] = {Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_032,Helvetica10_Character_033,Helvetica10_Character_034,Helvetica10_Character_035,Helvetica10_Character_036,Helvetica10_Character_037, + Helvetica10_Character_038,Helvetica10_Character_039,Helvetica10_Character_040,Helvetica10_Character_041,Helvetica10_Character_042,Helvetica10_Character_043,Helvetica10_Character_044,Helvetica10_Character_045,Helvetica10_Character_046,Helvetica10_Character_047,Helvetica10_Character_048,Helvetica10_Character_049,Helvetica10_Character_050,Helvetica10_Character_051,Helvetica10_Character_052,Helvetica10_Character_053,Helvetica10_Character_054,Helvetica10_Character_055,Helvetica10_Character_056,Helvetica10_Character_057,Helvetica10_Character_058,Helvetica10_Character_059,Helvetica10_Character_060,Helvetica10_Character_061,Helvetica10_Character_062,Helvetica10_Character_063,Helvetica10_Character_064,Helvetica10_Character_065,Helvetica10_Character_066,Helvetica10_Character_067,Helvetica10_Character_068,Helvetica10_Character_069,Helvetica10_Character_070,Helvetica10_Character_071,Helvetica10_Character_072,Helvetica10_Character_073,Helvetica10_Character_074,Helvetica10_Character_075,Helvetica10_Character_076, + Helvetica10_Character_077,Helvetica10_Character_078,Helvetica10_Character_079,Helvetica10_Character_080,Helvetica10_Character_081,Helvetica10_Character_082,Helvetica10_Character_083,Helvetica10_Character_084,Helvetica10_Character_085,Helvetica10_Character_086,Helvetica10_Character_087,Helvetica10_Character_088,Helvetica10_Character_089,Helvetica10_Character_090,Helvetica10_Character_091,Helvetica10_Character_092,Helvetica10_Character_093,Helvetica10_Character_094,Helvetica10_Character_095,Helvetica10_Character_096,Helvetica10_Character_097,Helvetica10_Character_098,Helvetica10_Character_099,Helvetica10_Character_100,Helvetica10_Character_101,Helvetica10_Character_102,Helvetica10_Character_103,Helvetica10_Character_104,Helvetica10_Character_105,Helvetica10_Character_106,Helvetica10_Character_107,Helvetica10_Character_108,Helvetica10_Character_109,Helvetica10_Character_110,Helvetica10_Character_111,Helvetica10_Character_112,Helvetica10_Character_113,Helvetica10_Character_114,Helvetica10_Character_115, + Helvetica10_Character_116,Helvetica10_Character_117,Helvetica10_Character_118,Helvetica10_Character_119,Helvetica10_Character_120,Helvetica10_Character_121,Helvetica10_Character_122,Helvetica10_Character_123,Helvetica10_Character_124,Helvetica10_Character_125,Helvetica10_Character_126,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042, + Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042, + Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042, + Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,Helvetica10_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontHelvetica10 = { "-adobe-helvetica-medium-r-normal--10-100-75-75-p-56-iso8859-1", 93, 13, Helvetica10_Character_Map, -1.0f, 2.0f }; + +static const GLubyte Helvetica12_Character_032[] = { 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* " " */ +static const GLubyte Helvetica12_Character_097[] = { 7, 0, 0, 0,116,136,136,120, 8,136,112, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte Helvetica12_Character_098[] = { 7, 0, 0, 0,176,200,136,136,136,200,176,128,128, 0, 0, 0}; /* "b" */ +static const GLubyte Helvetica12_Character_099[] = { 7, 0, 0, 0,112,136,128,128,128,136,112, 0, 0, 0, 0, 0}; /* "c" */ +static const GLubyte Helvetica12_Character_100[] = { 7, 0, 0, 0,104,152,136,136,136,152,104, 8, 8, 0, 0, 0}; /* "d" */ +static const GLubyte Helvetica12_Character_101[] = { 7, 0, 0, 0,112,136,128,248,136,136,112, 0, 0, 0, 0, 0}; /* "e" */ +static const GLubyte Helvetica12_Character_102[] = { 4, 0, 0, 0, 64, 64, 64, 64, 64, 64,224, 64, 48, 0, 0, 0}; /* "f" */ +static const GLubyte Helvetica12_Character_103[] = { 7,112,136, 8,104,152,136,136,136,152,104, 0, 0, 0, 0, 0}; /* "g" */ +static const GLubyte Helvetica12_Character_104[] = { 7, 0, 0, 0,136,136,136,136,136,200,176,128,128, 0, 0, 0}; /* "h" */ +static const GLubyte Helvetica12_Character_105[] = { 3, 0, 0, 0,128,128,128,128,128,128,128, 0,128, 0, 0, 0}; /* "i" */ +static const GLubyte Helvetica12_Character_106[] = { 4,128, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 64, 0, 0, 0}; /* "j" */ +static const GLubyte Helvetica12_Character_107[] = { 6, 0, 0, 0,136,144,160,192,192,160,144,128,128, 0, 0, 0}; /* "k" */ +static const GLubyte Helvetica12_Character_108[] = { 3, 0, 0, 0,128,128,128,128,128,128,128,128,128, 0, 0, 0}; /* "l" */ +static const GLubyte Helvetica12_Character_109[] = { 9, 0, 0, 0, 0, 0, 0,146, 0,146, 0,146, 0,146, 0,146, 0,218, 0,164, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "m" */ +static const GLubyte Helvetica12_Character_110[] = { 7, 0, 0, 0,136,136,136,136,136,200,176, 0, 0, 0, 0, 0}; /* "n" */ +static const GLubyte Helvetica12_Character_111[] = { 7, 0, 0, 0,112,136,136,136,136,136,112, 0, 0, 0, 0, 0}; /* "o" */ +static const GLubyte Helvetica12_Character_112[] = { 7,128,128,128,176,200,136,136,136,200,176, 0, 0, 0, 0, 0}; /* "p" */ +static const GLubyte Helvetica12_Character_113[] = { 7, 8, 8, 8,104,152,136,136,136,152,104, 0, 0, 0, 0, 0}; /* "q" */ +static const GLubyte Helvetica12_Character_114[] = { 4, 0, 0, 0,128,128,128,128,128,192,160, 0, 0, 0, 0, 0}; /* "r" */ +static const GLubyte Helvetica12_Character_115[] = { 6, 0, 0, 0, 96,144, 16, 96,128,144, 96, 0, 0, 0, 0, 0}; /* "s" */ +static const GLubyte Helvetica12_Character_116[] = { 4, 0, 0, 0, 96, 64, 64, 64, 64, 64,224, 64, 64, 0, 0, 0}; /* "t" */ +static const GLubyte Helvetica12_Character_117[] = { 7, 0, 0, 0,104,152,136,136,136,136,136, 0, 0, 0, 0, 0}; /* "u" */ +static const GLubyte Helvetica12_Character_118[] = { 7, 0, 0, 0, 32, 32, 80, 80,136,136,136, 0, 0, 0, 0, 0}; /* "v" */ +static const GLubyte Helvetica12_Character_119[] = { 10, 0, 0, 0, 0, 0, 0, 34, 0, 34, 0, 85, 0, 73, 0, 73, 0,136,128,136,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "w" */ +static const GLubyte Helvetica12_Character_120[] = { 6, 0, 0, 0,132,132, 72, 48, 48, 72,132, 0, 0, 0, 0, 0}; /* "x" */ +static const GLubyte Helvetica12_Character_121[] = { 7,128, 64, 32, 32, 80, 80,144,136,136,136, 0, 0, 0, 0, 0}; /* "y" */ +static const GLubyte Helvetica12_Character_122[] = { 6, 0, 0, 0,240,128, 64, 64, 32, 16,240, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte Helvetica12_Character_065[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,124, 0, 68, 0, 68, 0, 40, 0, 40, 0, 16, 0, 0, 0, 0, 0, 0, 0}; /* "A" */ +static const GLubyte Helvetica12_Character_066[] = { 8, 0, 0, 0,248,132,132,132,248,132,132,132,248, 0, 0, 0}; /* "B" */ +static const GLubyte Helvetica12_Character_067[] = { 9, 0, 0, 0, 0, 0, 0, 60, 0, 66, 0,128, 0,128, 0,128, 0,128, 0,128, 0, 66, 0, 60, 0, 0, 0, 0, 0, 0, 0}; /* "C" */ +static const GLubyte Helvetica12_Character_068[] = { 9, 0, 0, 0, 0, 0, 0,248, 0,132, 0,130, 0,130, 0,130, 0,130, 0,130, 0,132, 0,248, 0, 0, 0, 0, 0, 0, 0}; /* "D" */ +static const GLubyte Helvetica12_Character_069[] = { 8, 0, 0, 0,252,128,128,128,252,128,128,128,252, 0, 0, 0}; /* "E" */ +static const GLubyte Helvetica12_Character_070[] = { 8, 0, 0, 0,128,128,128,128,248,128,128,128,252, 0, 0, 0}; /* "F" */ +static const GLubyte Helvetica12_Character_071[] = { 9, 0, 0, 0, 0, 0, 0, 58, 0, 70, 0,130, 0,130, 0,142, 0,128, 0,128, 0, 66, 0, 60, 0, 0, 0, 0, 0, 0, 0}; /* "G" */ +static const GLubyte Helvetica12_Character_072[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,130, 0,130, 0,130, 0,254, 0,130, 0,130, 0,130, 0,130, 0, 0, 0, 0, 0, 0, 0}; /* "H" */ +static const GLubyte Helvetica12_Character_073[] = { 3, 0, 0, 0,128,128,128,128,128,128,128,128,128, 0, 0, 0}; /* "I" */ +static const GLubyte Helvetica12_Character_074[] = { 7, 0, 0, 0,112,136,136, 8, 8, 8, 8, 8, 8, 0, 0, 0}; /* "J" */ +static const GLubyte Helvetica12_Character_075[] = { 8, 0, 0, 0,130,132,136,144,224,160,144,136,132, 0, 0, 0}; /* "K" */ +static const GLubyte Helvetica12_Character_076[] = { 7, 0, 0, 0,248,128,128,128,128,128,128,128,128, 0, 0, 0}; /* "L" */ +static const GLubyte Helvetica12_Character_077[] = { 11, 0, 0, 0, 0, 0, 0,136,128,136,128,148,128,148,128,162,128,162,128,193,128,193,128,128,128, 0, 0, 0, 0, 0, 0}; /* "M" */ +static const GLubyte Helvetica12_Character_078[] = { 9, 0, 0, 0, 0, 0, 0,130, 0,134, 0,138, 0,138, 0,146, 0,162, 0,162, 0,194, 0,130, 0, 0, 0, 0, 0, 0, 0}; /* "N" */ +static const GLubyte Helvetica12_Character_079[] = { 10, 0, 0, 0, 0, 0, 0, 60, 0, 66, 0,129, 0,129, 0,129, 0,129, 0,129, 0, 66, 0, 60, 0, 0, 0, 0, 0, 0, 0}; /* "O" */ +static const GLubyte Helvetica12_Character_080[] = { 8, 0, 0, 0,128,128,128,128,248,132,132,132,248, 0, 0, 0}; /* "P" */ +static const GLubyte Helvetica12_Character_081[] = { 10, 0, 0, 0, 0, 0, 0, 61, 0, 66, 0,133, 0,137, 0,129, 0,129, 0,129, 0, 66, 0, 60, 0, 0, 0, 0, 0, 0, 0}; /* "Q" */ +static const GLubyte Helvetica12_Character_082[] = { 8, 0, 0, 0,132,132,132,136,248,132,132,132,248, 0, 0, 0}; /* "R" */ +static const GLubyte Helvetica12_Character_083[] = { 8, 0, 0, 0,120,132,132, 4, 24, 96,128,132,120, 0, 0, 0}; /* "S" */ +static const GLubyte Helvetica12_Character_084[] = { 7, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16, 16,254, 0, 0, 0}; /* "T" */ +static const GLubyte Helvetica12_Character_085[] = { 8, 0, 0, 0,120,132,132,132,132,132,132,132,132, 0, 0, 0}; /* "U" */ +static const GLubyte Helvetica12_Character_086[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 16, 0, 40, 0, 40, 0, 68, 0, 68, 0, 68, 0,130, 0,130, 0, 0, 0, 0, 0, 0, 0}; /* "V" */ +static const GLubyte Helvetica12_Character_087[] = { 11, 0, 0, 0, 0, 0, 0, 34, 0, 34, 0, 34, 0, 85, 0, 85, 0, 73, 0,136,128,136,128,136,128, 0, 0, 0, 0, 0, 0}; /* "W" */ +static const GLubyte Helvetica12_Character_088[] = { 9, 0, 0, 0, 0, 0, 0,130, 0, 68, 0, 68, 0, 40, 0, 16, 0, 40, 0, 68, 0, 68, 0,130, 0, 0, 0, 0, 0, 0, 0}; /* "X" */ +static const GLubyte Helvetica12_Character_089[] = { 9, 0, 0, 0, 0, 0, 0, 16, 0, 16, 0, 16, 0, 16, 0, 40, 0, 68, 0, 68, 0,130, 0,130, 0, 0, 0, 0, 0, 0, 0}; /* "Y" */ +static const GLubyte Helvetica12_Character_090[] = { 9, 0, 0, 0, 0, 0, 0,254, 0,128, 0, 64, 0, 32, 0, 16, 0, 8, 0, 4, 0, 2, 0,254, 0, 0, 0, 0, 0, 0, 0}; /* "Z" */ +static const GLubyte Helvetica12_Character_048[] = { 7, 0, 0, 0,112,136,136,136,136,136,136,136,112, 0, 0, 0}; /* "0" */ +static const GLubyte Helvetica12_Character_049[] = { 7, 0, 0, 0, 32, 32, 32, 32, 32, 32, 32,224, 32, 0, 0, 0}; /* "1" */ +static const GLubyte Helvetica12_Character_050[] = { 7, 0, 0, 0,248,128,128, 64, 32, 16, 8,136,112, 0, 0, 0}; /* "2" */ +static const GLubyte Helvetica12_Character_051[] = { 7, 0, 0, 0,112,136,136, 8, 8, 48, 8,136,112, 0, 0, 0}; /* "3" */ +static const GLubyte Helvetica12_Character_052[] = { 7, 0, 0, 0, 8, 8,252,136, 72, 40, 40, 24, 8, 0, 0, 0}; /* "4" */ +static const GLubyte Helvetica12_Character_053[] = { 7, 0, 0, 0,112,136,136, 8, 8,240,128,128,248, 0, 0, 0}; /* "5" */ +static const GLubyte Helvetica12_Character_054[] = { 7, 0, 0, 0,112,136,136,136,200,176,128,136,112, 0, 0, 0}; /* "6" */ +static const GLubyte Helvetica12_Character_055[] = { 7, 0, 0, 0, 64, 64, 32, 32, 32, 16, 16, 8,248, 0, 0, 0}; /* "7" */ +static const GLubyte Helvetica12_Character_056[] = { 7, 0, 0, 0,112,136,136,136,136,112,136,136,112, 0, 0, 0}; /* "8" */ +static const GLubyte Helvetica12_Character_057[] = { 7, 0, 0, 0,112,136, 8, 8,120,136,136,136,112, 0, 0, 0}; /* "9" */ +static const GLubyte Helvetica12_Character_096[] = { 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,192,128, 64, 0, 0, 0}; /* "`" */ +static const GLubyte Helvetica12_Character_126[] = { 8, 0, 0, 0, 0, 0, 0,152,100, 0, 0, 0, 0, 0, 0, 0}; /* "~" */ +static const GLubyte Helvetica12_Character_033[] = { 3, 0, 0, 0,128, 0,128,128,128,128,128,128,128, 0, 0, 0}; /* "!" */ +static const GLubyte Helvetica12_Character_064[] = { 12, 0, 0, 0, 0, 62, 0, 64, 0,155, 0,166,128,162, 64,162, 64,146, 64, 77, 64, 96,128, 31, 0, 0, 0, 0, 0, 0, 0}; /* "@" */ +static const GLubyte Helvetica12_Character_035[] = { 7, 0, 0, 0, 80, 80, 80,252, 40,252, 40, 40, 0, 0, 0, 0}; /* "#" */ +static const GLubyte Helvetica12_Character_036[] = { 7, 0, 0, 32,112,168,168, 40,112,160,168,112, 32, 0, 0, 0}; /* "$" */ +static const GLubyte Helvetica12_Character_037[] = { 11, 0, 0, 0, 0, 0, 0, 35, 0, 20,128, 20,128, 19, 0, 8, 0,104, 0,148, 0,148, 0, 98, 0, 0, 0, 0, 0, 0, 0}; /* "%" */ +static const GLubyte Helvetica12_Character_094[] = { 6, 0, 0, 0, 0, 0, 0, 0, 0,136, 80, 32, 0, 0, 0, 0}; /* "^" */ +static const GLubyte Helvetica12_Character_038[] = { 9, 0, 0, 0, 0, 0, 0,114, 0,140, 0,132, 0,138, 0, 80, 0, 48, 0, 72, 0, 72, 0, 48, 0, 0, 0, 0, 0, 0, 0}; /* "&" */ +static const GLubyte Helvetica12_Character_042[] = { 5, 0, 0, 0, 0, 0, 0, 0, 0, 0,160, 64,160, 0, 0, 0}; /* "*" */ +static const GLubyte Helvetica12_Character_040[] = { 4, 32, 64, 64,128,128,128,128,128,128, 64, 64, 32, 0, 0, 0}; /* "(" */ +static const GLubyte Helvetica12_Character_041[] = { 4,128, 64, 64, 32, 32, 32, 32, 32, 32, 64, 64,128, 0, 0, 0}; /* ")" */ +static const GLubyte Helvetica12_Character_045[] = { 8, 0, 0, 0, 0, 0, 0,248, 0, 0, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte Helvetica12_Character_095[] = { 7, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte Helvetica12_Character_061[] = { 7, 0, 0, 0, 0, 0,248, 0,248, 0, 0, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte Helvetica12_Character_043[] = { 7, 0, 0, 0, 0, 32, 32,248, 32, 32, 0, 0, 0, 0, 0, 0}; /* "+" */ +static const GLubyte Helvetica12_Character_091[] = { 3,192,128,128,128,128,128,128,128,128,128,128,192, 0, 0, 0}; /* "[" */ +static const GLubyte Helvetica12_Character_123[] = { 4, 48, 64, 64, 64, 64, 64,128, 64, 64, 64, 64, 48, 0, 0, 0}; /* "{" */ +static const GLubyte Helvetica12_Character_125[] = { 4,192, 32, 32, 32, 32, 32, 16, 32, 32, 32, 32,192, 0, 0, 0}; /* "}" */ +static const GLubyte Helvetica12_Character_093[] = { 3,192, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,192, 0, 0, 0}; /* "]" */ +static const GLubyte Helvetica12_Character_059[] = { 3, 0,128, 64, 64, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0}; /* ";" */ +static const GLubyte Helvetica12_Character_058[] = { 3, 0, 0, 0,128, 0, 0, 0, 0,128, 0, 0, 0, 0, 0, 0}; /* ":" */ +static const GLubyte Helvetica12_Character_044[] = { 4, 0,128, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte Helvetica12_Character_046[] = { 3, 0, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte Helvetica12_Character_060[] = { 7, 0, 0, 0, 0, 12, 48,192, 48, 12, 0, 0, 0, 0, 0, 0}; /* "<" */ +static const GLubyte Helvetica12_Character_062[] = { 7, 0, 0, 0, 0,192, 48, 12, 48,192, 0, 0, 0, 0, 0, 0}; /* ">" */ +static const GLubyte Helvetica12_Character_047[] = { 4, 0, 0, 0,128,128,128, 64, 64, 64, 32, 32, 32, 0, 0, 0}; /* "/" */ +static const GLubyte Helvetica12_Character_063[] = { 7, 0, 0, 0, 32, 0, 32, 32, 16, 16,136,136,112, 0, 0, 0}; /* "?" */ +static const GLubyte Helvetica12_Character_092[] = { 4, 0, 0, 0, 32, 32, 32, 64, 64, 64,128,128,128, 0, 0, 0}; /* "\" */ +static const GLubyte Helvetica12_Character_034[] = { 5, 0, 0, 0, 0, 0, 0, 0, 0, 0,160,160,160, 0, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte Helvetica12_Character_039[] = { 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 64, 64, 0, 0}; /* "'" */ +static const GLubyte Helvetica12_Character_124[] = { 3, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 0}; /* "|" */ + + +/* The font characters mapping: */ +static const GLubyte* Helvetica12_Character_Map[] = {Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_032,Helvetica12_Character_033,Helvetica12_Character_034,Helvetica12_Character_035,Helvetica12_Character_036,Helvetica12_Character_037, + Helvetica12_Character_038,Helvetica12_Character_039,Helvetica12_Character_040,Helvetica12_Character_041,Helvetica12_Character_042,Helvetica12_Character_043,Helvetica12_Character_044,Helvetica12_Character_045,Helvetica12_Character_046,Helvetica12_Character_047,Helvetica12_Character_048,Helvetica12_Character_049,Helvetica12_Character_050,Helvetica12_Character_051,Helvetica12_Character_052,Helvetica12_Character_053,Helvetica12_Character_054,Helvetica12_Character_055,Helvetica12_Character_056,Helvetica12_Character_057,Helvetica12_Character_058,Helvetica12_Character_059,Helvetica12_Character_060,Helvetica12_Character_061,Helvetica12_Character_062,Helvetica12_Character_063,Helvetica12_Character_064,Helvetica12_Character_065,Helvetica12_Character_066,Helvetica12_Character_067,Helvetica12_Character_068,Helvetica12_Character_069,Helvetica12_Character_070,Helvetica12_Character_071,Helvetica12_Character_072,Helvetica12_Character_073,Helvetica12_Character_074,Helvetica12_Character_075,Helvetica12_Character_076, + Helvetica12_Character_077,Helvetica12_Character_078,Helvetica12_Character_079,Helvetica12_Character_080,Helvetica12_Character_081,Helvetica12_Character_082,Helvetica12_Character_083,Helvetica12_Character_084,Helvetica12_Character_085,Helvetica12_Character_086,Helvetica12_Character_087,Helvetica12_Character_088,Helvetica12_Character_089,Helvetica12_Character_090,Helvetica12_Character_091,Helvetica12_Character_092,Helvetica12_Character_093,Helvetica12_Character_094,Helvetica12_Character_095,Helvetica12_Character_096,Helvetica12_Character_097,Helvetica12_Character_098,Helvetica12_Character_099,Helvetica12_Character_100,Helvetica12_Character_101,Helvetica12_Character_102,Helvetica12_Character_103,Helvetica12_Character_104,Helvetica12_Character_105,Helvetica12_Character_106,Helvetica12_Character_107,Helvetica12_Character_108,Helvetica12_Character_109,Helvetica12_Character_110,Helvetica12_Character_111,Helvetica12_Character_112,Helvetica12_Character_113,Helvetica12_Character_114,Helvetica12_Character_115, + Helvetica12_Character_116,Helvetica12_Character_117,Helvetica12_Character_118,Helvetica12_Character_119,Helvetica12_Character_120,Helvetica12_Character_121,Helvetica12_Character_122,Helvetica12_Character_123,Helvetica12_Character_124,Helvetica12_Character_125,Helvetica12_Character_126,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042, + Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042, + Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042, + Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,Helvetica12_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontHelvetica12 = { "-adobe-helvetica-medium-r-normal--12-120-75-75-p-67-iso8859-1", 93, 15, Helvetica12_Character_Map, -1.0f, 3.0f }; + +static const GLubyte Helvetica18_Character_032[] = { 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* " " */ +static const GLubyte Helvetica18_Character_097[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0,118, 0,238, 0,198, 0,198, 0,230, 0,126, 0, 14, 0,198, 0,238, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte Helvetica18_Character_098[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0,222, 0,255, 0,227, 0,193,128,193,128,193,128,193,128,227, 0,255, 0,222, 0,192, 0,192, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "b" */ +static const GLubyte Helvetica18_Character_099[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0,127, 0, 99, 0,192, 0,192, 0,192, 0,192, 0, 99, 0,127, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "c" */ +static const GLubyte Helvetica18_Character_100[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 61,128,127,128, 99,128,193,128,193,128,193,128,193,128, 99,128,127,128, 61,128, 1,128, 1,128, 1,128, 1,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "d" */ +static const GLubyte Helvetica18_Character_101[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,127, 0,227, 0,192, 0,192, 0,255, 0,195, 0,195, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "e" */ +static const GLubyte Helvetica18_Character_102[] = { 6, 0, 0, 0, 0, 48, 48, 48, 48, 48, 48, 48, 48,252,252, 48, 48, 60, 28, 0, 0, 0, 0}; /* "f" */ +static const GLubyte Helvetica18_Character_103[] = { 11, 28, 0,127, 0, 99, 0, 1,128, 61,128,127,128, 99,128,193,128,193,128,193,128,193,128, 97,128,127,128, 61,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "g" */ +static const GLubyte Helvetica18_Character_104[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,227, 0,223, 0,206, 0,192, 0,192, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "h" */ +static const GLubyte Helvetica18_Character_105[] = { 4, 0, 0, 0, 0,192,192,192,192,192,192,192,192,192,192, 0, 0,192,192, 0, 0, 0, 0}; /* "i" */ +static const GLubyte Helvetica18_Character_106[] = { 4,224,240, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 48, 48, 0, 0, 0, 0}; /* "j" */ +static const GLubyte Helvetica18_Character_107[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0,199, 0,198, 0,206, 0,204, 0,216, 0,248, 0,240, 0,216, 0,204, 0,198, 0,192, 0,192, 0,192, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "k" */ +static const GLubyte Helvetica18_Character_108[] = { 4, 0, 0, 0, 0,192,192,192,192,192,192,192,192,192,192,192,192,192,192, 0, 0, 0, 0}; /* "l" */ +static const GLubyte Helvetica18_Character_109[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0,198, 48,198, 48,198, 48,198, 48,198, 48,198, 48,198, 48,231, 48,222,240,204, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "m" */ +static const GLubyte Helvetica18_Character_110[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,227, 0,223, 0,206, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "n" */ +static const GLubyte Helvetica18_Character_111[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0,127, 0, 99, 0,193,128,193,128,193,128,193,128, 99, 0,127, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "o" */ +static const GLubyte Helvetica18_Character_112[] = { 11,192, 0,192, 0,192, 0,192, 0,222, 0,255, 0,227, 0,193,128,193,128,193,128,193,128,227, 0,255, 0,222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "p" */ +static const GLubyte Helvetica18_Character_113[] = { 11, 1,128, 1,128, 1,128, 1,128, 61,128,127,128, 99,128,193,128,193,128,193,128,193,128, 99,128,127,128, 61,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "q" */ +static const GLubyte Helvetica18_Character_114[] = { 6, 0, 0, 0, 0,192,192,192,192,192,192,192,224,216,216, 0, 0, 0, 0, 0, 0, 0, 0}; /* "r" */ +static const GLubyte Helvetica18_Character_115[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0,120, 0,252, 0,198, 0, 6, 0, 62, 0,252, 0,192, 0,198, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "s" */ +static const GLubyte Helvetica18_Character_116[] = { 6, 0, 0, 0, 0, 24, 56, 48, 48, 48, 48, 48, 48,252,252, 48, 48, 48, 0, 0, 0, 0, 0}; /* "t" */ +static const GLubyte Helvetica18_Character_117[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,115, 0,251, 0,199, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "u" */ +static const GLubyte Helvetica18_Character_118[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 24, 0, 60, 0, 36, 0,102, 0,102, 0,102, 0,195, 0,195, 0,195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "v" */ +static const GLubyte Helvetica18_Character_119[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 25,128, 25,128, 57,192, 41, 64,105, 96,102, 96,102, 96,198, 48,198, 48,198, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "w" */ +static const GLubyte Helvetica18_Character_120[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,195, 0,231, 0,102, 0, 60, 0, 24, 0, 24, 0, 60, 0,102, 0,231, 0,195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "x" */ +static const GLubyte Helvetica18_Character_121[] = { 10,112, 0,112, 0, 24, 0, 24, 0, 24, 0, 24, 0, 60, 0, 36, 0,102, 0,102, 0,102, 0,195, 0,195, 0,195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "y" */ +static const GLubyte Helvetica18_Character_122[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0,254, 0,254, 0,192, 0, 96, 0, 48, 0, 24, 0, 12, 0, 6, 0,254, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte Helvetica18_Character_065[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0,192, 48,192, 48, 96, 96, 96, 96,127,224, 63,192, 48,192, 48,192, 25,128, 25,128, 15, 0, 15, 0, 6, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "A" */ +static const GLubyte Helvetica18_Character_066[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0,255,128,255,192,192,224,192, 96,192, 96,192,224,255,192,255,128,193,128,192,192,192,192,193,192,255,128,255, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "B" */ +static const GLubyte Helvetica18_Character_067[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 15,128, 63,224,112,112, 96, 48,224, 0,192, 0,192, 0,192, 0,192, 0,224, 0, 96, 48,112,112, 63,224, 15,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "C" */ +static const GLubyte Helvetica18_Character_068[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0,255, 0,255,128,193,192,192,192,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192,192,193,192,255,128,255, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "D" */ +static const GLubyte Helvetica18_Character_069[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0,255,128,255,128,192, 0,192, 0,192, 0,192, 0,255, 0,255, 0,192, 0,192, 0,192, 0,192, 0,255,128,255,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "E" */ +static const GLubyte Helvetica18_Character_070[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,255, 0,255, 0,192, 0,192, 0,192, 0,192, 0,255,128,255,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "F" */ +static const GLubyte Helvetica18_Character_071[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 15,176, 63,240,112,112, 96, 48,224, 48,193,240,193,240,192, 0,192, 0,224, 48, 96, 48,112,112, 63,224, 15,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "G" */ +static const GLubyte Helvetica18_Character_072[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,255,224,255,224,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96, 0, 0, 0, 0, 0, 0, 0, 0}; /* "H" */ +static const GLubyte Helvetica18_Character_073[] = { 6, 0, 0, 0, 0,192,192,192,192,192,192,192,192,192,192,192,192,192,192, 0, 0, 0, 0}; /* "I" */ +static const GLubyte Helvetica18_Character_074[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,126, 0,231, 0,195, 0,195, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "J" */ +static const GLubyte Helvetica18_Character_075[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0,192,112,192,224,193,192,195,128,199, 0,206, 0,252, 0,248, 0,220, 0,206, 0,199, 0,195,128,193,192,192,224, 0, 0, 0, 0, 0, 0, 0, 0}; /* "K" */ +static const GLubyte Helvetica18_Character_076[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,255, 0,255, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "L" */ +static const GLubyte Helvetica18_Character_077[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0,195, 12,195, 12,199,140,196,140,204,204,204,204,216,108,216,108,240, 60,240, 60,224, 28,224, 28,192, 12,192, 12, 0, 0, 0, 0, 0, 0, 0, 0}; /* "M" */ +static const GLubyte Helvetica18_Character_078[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0,192, 96,192,224,193,224,193,224,195, 96,198, 96,198, 96,204, 96,204, 96,216, 96,240, 96,240, 96,224, 96,192, 96, 0, 0, 0, 0, 0, 0, 0, 0}; /* "N" */ +static const GLubyte Helvetica18_Character_079[] = { 15, 0, 0, 0, 0, 0, 0, 0, 0, 15,128, 63,224,112,112, 96, 48,224, 56,192, 24,192, 24,192, 24,192, 24,224, 56, 96, 48,112,112, 63,224, 15,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "O" */ +static const GLubyte Helvetica18_Character_080[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,255, 0,255,128,193,192,192,192,192,192,193,192,255,128,255, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "P" */ +static const GLubyte Helvetica18_Character_081[] = { 15, 0, 0, 0, 0, 0, 0, 0, 48, 15,176, 63,224,112,240, 97,176,225,184,192, 24,192, 24,192, 24,192, 24,224, 56, 96, 48,112,112, 63,224, 15,128, 0, 0, 0, 0, 0, 0, 0, 0}; /* "Q" */ +static const GLubyte Helvetica18_Character_082[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0,192,192,192,192,192,192,192,192,193,128,193,128,255, 0,255,128,193,192,192,192,192,192,193,192,255,128,255, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "R" */ +static const GLubyte Helvetica18_Character_083[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0,127,192,224,224,192, 96, 0, 96, 0,224, 3,192, 31, 0,124, 0,224, 0,192, 96,224,224,127,192, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "S" */ +static const GLubyte Helvetica18_Character_084[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0, 12, 0,255,192,255,192, 0, 0, 0, 0, 0, 0, 0, 0}; /* "T" */ +static const GLubyte Helvetica18_Character_085[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 31, 0,127,192, 96,192,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96, 0, 0, 0, 0, 0, 0, 0, 0}; /* "U" */ +static const GLubyte Helvetica18_Character_086[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 15, 0, 15, 0, 25,128, 25,128, 25,128, 48,192, 48,192, 48,192, 96, 96, 96, 96, 96, 96,192, 48,192, 48, 0, 0, 0, 0, 0, 0, 0, 0}; /* "V" */ +static const GLubyte Helvetica18_Character_087[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 0, 24, 24, 0, 28, 56, 0, 52, 44, 0, 54,108, 0, 54,108, 0,102,102, 0,102,102, 0, 98, 70, 0, 99,198, 0,195,195, 0,193,131, 0,193,131, 0,193,131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "W" */ +static const GLubyte Helvetica18_Character_088[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0,192, 96,224,224, 96,192,113,192, 49,128, 27, 0, 14, 0, 14, 0, 27, 0, 49,128,113,192, 96,192,224,224,192, 96, 0, 0, 0, 0, 0, 0, 0, 0}; /* "X" */ +static const GLubyte Helvetica18_Character_089[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 15, 0, 25,128, 48,192, 48,192, 96, 96, 96, 96,192, 48,192, 48, 0, 0, 0, 0, 0, 0, 0, 0}; /* "Y" */ +static const GLubyte Helvetica18_Character_090[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0,255,192,255,192,192, 0, 96, 0, 48, 0, 24, 0, 28, 0, 12, 0, 6, 0, 3, 0, 1,128, 0,192,255,192,255,192, 0, 0, 0, 0, 0, 0, 0, 0}; /* "Z" */ +static const GLubyte Helvetica18_Character_048[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,126, 0,102, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,195, 0,102, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "0" */ +static const GLubyte Helvetica18_Character_049[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0,248, 0,248, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "1" */ +static const GLubyte Helvetica18_Character_050[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,255, 0,255, 0,192, 0,224, 0,112, 0, 56, 0, 28, 0, 14, 0, 7, 0, 3, 0,195, 0,254, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "2" */ +static const GLubyte Helvetica18_Character_051[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,126, 0,199, 0,195, 0, 3, 0, 7, 0, 30, 0, 28, 0, 6, 0,195, 0,195, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "3" */ +static const GLubyte Helvetica18_Character_052[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 3, 0,255,128,255,128,195, 0, 99, 0, 51, 0, 51, 0, 27, 0, 15, 0, 7, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "4" */ +static const GLubyte Helvetica18_Character_053[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,124, 0,254, 0,199, 0,195, 0, 3, 0, 3, 0,199, 0,254, 0,252, 0,192, 0,192, 0,254, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "5" */ +static const GLubyte Helvetica18_Character_054[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,126, 0,227, 0,195, 0,195, 0,195, 0,254, 0,220, 0,192, 0,192, 0, 99, 0,127, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "6" */ +static const GLubyte Helvetica18_Character_055[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 96, 0, 48, 0, 48, 0, 48, 0, 24, 0, 24, 0, 12, 0, 12, 0, 6, 0, 3, 0,255, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "7" */ +static const GLubyte Helvetica18_Character_056[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,126, 0,231, 0,195, 0,195, 0,102, 0,126, 0,102, 0,195, 0,195, 0,231, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "8" */ +static const GLubyte Helvetica18_Character_057[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,124, 0,254, 0,198, 0, 3, 0, 3, 0, 59, 0,127, 0,195, 0,195, 0,195, 0,199, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "9" */ +static const GLubyte Helvetica18_Character_096[] = { 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,192,192,128,128, 64, 0, 0, 0, 0}; /* "`" */ +static const GLubyte Helvetica18_Character_126[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,204, 0,126, 0, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "~" */ +static const GLubyte Helvetica18_Character_033[] = { 6, 0, 0, 0, 0,192,192, 0, 0,128,128,192,192,192,192,192,192,192,192, 0, 0, 0, 0}; /* "!" */ +static const GLubyte Helvetica18_Character_064[] = { 18, 0, 0, 0, 7,224, 0, 31,240, 0, 56, 0, 0,112, 0, 0,103,112, 0,207,248, 0,204,204, 0,204,102, 0,204,102, 0,204, 99, 0,198, 51, 0,103,115, 0, 99,179, 0, 48, 6, 0, 28, 14, 0, 15,252, 0, 3,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "@" */ +static const GLubyte Helvetica18_Character_035[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 36, 0, 36, 0,255,128,255,128, 18, 0, 18, 0, 18, 0,127,192,127,192, 9, 0, 9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "#" */ +static const GLubyte Helvetica18_Character_036[] = { 10, 0, 0, 0, 0, 8, 0, 8, 0, 62, 0,127, 0,235,128,201,128, 9,128, 15, 0, 62, 0,120, 0,232, 0,200, 0,203, 0,127, 0, 62, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "$" */ +static const GLubyte Helvetica18_Character_037[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0, 24,120, 24,252, 12,204, 12,204, 6,252, 6,120, 3, 0,123, 0,253,128,205,128,204,192,252,192,120, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "%" */ +static const GLubyte Helvetica18_Character_094[] = { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,130, 0,198, 0,108, 0, 56, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "^" */ +static const GLubyte Helvetica18_Character_038[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 60,112,126,224,231,192,195,128,195,192,198,192,238,192,124, 0, 60, 0,102, 0,102, 0,126, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "&" */ +static const GLubyte Helvetica18_Character_042[] = { 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,136,112,112,248, 32, 32, 0, 0, 0, 0}; /* "*" */ +static const GLubyte Helvetica18_Character_040[] = { 6, 16, 48, 96, 96,192,192,192,192,192,192,192,192,192,192, 96, 96, 48, 16, 0, 0, 0, 0}; /* "(" */ +static const GLubyte Helvetica18_Character_041[] = { 6,128,192, 96, 96, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 96, 96,192,128, 0, 0, 0, 0}; /* ")" */ +static const GLubyte Helvetica18_Character_045[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte Helvetica18_Character_095[] = { 10,255,248,255,248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte Helvetica18_Character_061[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,254, 0,254, 0, 0, 0, 0, 0,254, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte Helvetica18_Character_043[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 24, 0, 24, 0, 24, 0,255, 0,255, 0, 24, 0, 24, 0, 24, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "+" */ +static const GLubyte Helvetica18_Character_091[] = { 5,240,240,192,192,192,192,192,192,192,192,192,192,192,192,192,192,240,240, 0, 0, 0, 0}; /* "[" */ +static const GLubyte Helvetica18_Character_123[] = { 7, 12, 24, 48, 48, 48, 48, 48, 48, 96,192, 96, 48, 48, 48, 48, 48, 24, 12, 0, 0, 0, 0}; /* "{" */ +static const GLubyte Helvetica18_Character_125[] = { 7,192, 96, 48, 48, 48, 48, 48, 48, 24, 12, 24, 48, 48, 48, 48, 48, 96,192, 0, 0, 0, 0}; /* "}" */ +static const GLubyte Helvetica18_Character_093[] = { 5,240,240, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,240,240, 0, 0, 0, 0}; /* "]" */ +static const GLubyte Helvetica18_Character_059[] = { 5, 0,128, 64, 64,192,192, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0}; /* ";" */ +static const GLubyte Helvetica18_Character_058[] = { 5, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0}; /* ":" */ +static const GLubyte Helvetica18_Character_044[] = { 5, 0,128, 64, 64,192,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte Helvetica18_Character_046[] = { 5, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte Helvetica18_Character_060[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 15, 0, 60, 0,112, 0,192, 0,112, 0, 60, 0, 15, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "<" */ +static const GLubyte Helvetica18_Character_062[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0,192, 0,240, 0, 60, 0, 14, 0, 3, 0, 14, 0, 60, 0,240, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ">" */ +static const GLubyte Helvetica18_Character_047[] = { 5, 0, 0, 0, 0,192,192, 64, 64, 96, 96, 32, 32, 48, 48, 16, 16, 24, 24, 0, 0, 0, 0}; /* "/" */ +static const GLubyte Helvetica18_Character_063[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 48, 0, 0, 0, 0, 0, 48, 0, 48, 0, 48, 0, 56, 0, 28, 0, 14, 0,198, 0,198, 0,254, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "?" */ +static const GLubyte Helvetica18_Character_092[] = { 5, 0, 0, 0, 0, 24, 24, 16, 16, 48, 48, 32, 32, 96, 96, 64, 64,192,192, 0, 0, 0, 0}; /* "\" */ +static const GLubyte Helvetica18_Character_034[] = { 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,144,144,216,216,216, 0, 0, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte Helvetica18_Character_039[] = { 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 64, 64,192,192, 0, 0, 0, 0}; /* "'" */ +static const GLubyte Helvetica18_Character_124[] = { 4, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96}; /* "|" */ + + +/* The font characters mapping: */ +static const GLubyte* Helvetica18_Character_Map[] = {Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_032,Helvetica18_Character_033,Helvetica18_Character_034,Helvetica18_Character_035,Helvetica18_Character_036,Helvetica18_Character_037, + Helvetica18_Character_038,Helvetica18_Character_039,Helvetica18_Character_040,Helvetica18_Character_041,Helvetica18_Character_042,Helvetica18_Character_043,Helvetica18_Character_044,Helvetica18_Character_045,Helvetica18_Character_046,Helvetica18_Character_047,Helvetica18_Character_048,Helvetica18_Character_049,Helvetica18_Character_050,Helvetica18_Character_051,Helvetica18_Character_052,Helvetica18_Character_053,Helvetica18_Character_054,Helvetica18_Character_055,Helvetica18_Character_056,Helvetica18_Character_057,Helvetica18_Character_058,Helvetica18_Character_059,Helvetica18_Character_060,Helvetica18_Character_061,Helvetica18_Character_062,Helvetica18_Character_063,Helvetica18_Character_064,Helvetica18_Character_065,Helvetica18_Character_066,Helvetica18_Character_067,Helvetica18_Character_068,Helvetica18_Character_069,Helvetica18_Character_070,Helvetica18_Character_071,Helvetica18_Character_072,Helvetica18_Character_073,Helvetica18_Character_074,Helvetica18_Character_075,Helvetica18_Character_076, + Helvetica18_Character_077,Helvetica18_Character_078,Helvetica18_Character_079,Helvetica18_Character_080,Helvetica18_Character_081,Helvetica18_Character_082,Helvetica18_Character_083,Helvetica18_Character_084,Helvetica18_Character_085,Helvetica18_Character_086,Helvetica18_Character_087,Helvetica18_Character_088,Helvetica18_Character_089,Helvetica18_Character_090,Helvetica18_Character_091,Helvetica18_Character_092,Helvetica18_Character_093,Helvetica18_Character_094,Helvetica18_Character_095,Helvetica18_Character_096,Helvetica18_Character_097,Helvetica18_Character_098,Helvetica18_Character_099,Helvetica18_Character_100,Helvetica18_Character_101,Helvetica18_Character_102,Helvetica18_Character_103,Helvetica18_Character_104,Helvetica18_Character_105,Helvetica18_Character_106,Helvetica18_Character_107,Helvetica18_Character_108,Helvetica18_Character_109,Helvetica18_Character_110,Helvetica18_Character_111,Helvetica18_Character_112,Helvetica18_Character_113,Helvetica18_Character_114,Helvetica18_Character_115, + Helvetica18_Character_116,Helvetica18_Character_117,Helvetica18_Character_118,Helvetica18_Character_119,Helvetica18_Character_120,Helvetica18_Character_121,Helvetica18_Character_122,Helvetica18_Character_123,Helvetica18_Character_124,Helvetica18_Character_125,Helvetica18_Character_126,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042, + Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042, + Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042, + Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,Helvetica18_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontHelvetica18 = { "-adobe-helvetica-medium-r-normal--18-180-75-75-p-98-iso8859-1", 93, 22, Helvetica18_Character_Map, -1.0f, 4.0f }; + +static const GLubyte TimesRoman10_Character_032[] = { 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* " " */ +static const GLubyte TimesRoman10_Character_097[] = { 4, 0, 0, 0,224,160, 96, 32,192, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte TimesRoman10_Character_098[] = { 5, 0, 0, 0,224,144,144,144,224,128,128, 0, 0, 0}; /* "b" */ +static const GLubyte TimesRoman10_Character_099[] = { 4, 0, 0, 0, 96,128,128,128, 96, 0, 0, 0, 0, 0}; /* "c" */ +static const GLubyte TimesRoman10_Character_100[] = { 5, 0, 0, 0,104,144,144,144,112, 16, 48, 0, 0, 0}; /* "d" */ +static const GLubyte TimesRoman10_Character_101[] = { 4, 0, 0, 0, 96,128,192,160, 96, 0, 0, 0, 0, 0}; /* "e" */ +static const GLubyte TimesRoman10_Character_102[] = { 4, 0, 0, 0,224, 64, 64, 64,224, 64, 48, 0, 0, 0}; /* "f" */ +static const GLubyte TimesRoman10_Character_103[] = { 5, 0,224,144, 96, 64,160,160,112, 0, 0, 0, 0, 0}; /* "g" */ +static const GLubyte TimesRoman10_Character_104[] = { 5, 0, 0, 0,216,144,144,144,224,128,128, 0, 0, 0}; /* "h" */ +static const GLubyte TimesRoman10_Character_105[] = { 3, 0, 0, 0, 64, 64, 64, 64,192, 0, 64, 0, 0, 0}; /* "i" */ +static const GLubyte TimesRoman10_Character_106[] = { 3, 0,128, 64, 64, 64, 64, 64,192, 0, 64, 0, 0, 0}; /* "j" */ +static const GLubyte TimesRoman10_Character_107[] = { 5, 0, 0, 0,152,144,224,160,144,128,128, 0, 0, 0}; /* "k" */ +static const GLubyte TimesRoman10_Character_108[] = { 4, 0, 0, 0,224, 64, 64, 64, 64, 64,192, 0, 0, 0}; /* "l" */ +static const GLubyte TimesRoman10_Character_109[] = { 8, 0, 0, 0,219,146,146,146,236, 0, 0, 0, 0, 0}; /* "m" */ +static const GLubyte TimesRoman10_Character_110[] = { 5, 0, 0, 0,216,144,144,144,224, 0, 0, 0, 0, 0}; /* "n" */ +static const GLubyte TimesRoman10_Character_111[] = { 5, 0, 0, 0, 96,144,144,144, 96, 0, 0, 0, 0, 0}; /* "o" */ +static const GLubyte TimesRoman10_Character_112[] = { 5, 0,192,128,224,144,144,144,224, 0, 0, 0, 0, 0}; /* "p" */ +static const GLubyte TimesRoman10_Character_113[] = { 5, 0, 56, 16,112,144,144,144,112, 0, 0, 0, 0, 0}; /* "q" */ +static const GLubyte TimesRoman10_Character_114[] = { 4, 0, 0, 0,224, 64, 64, 96,160, 0, 0, 0, 0, 0}; /* "r" */ +static const GLubyte TimesRoman10_Character_115[] = { 4, 0, 0, 0,224, 32, 96,128,224, 0, 0, 0, 0, 0}; /* "s" */ +static const GLubyte TimesRoman10_Character_116[] = { 4, 0, 0, 0, 48, 64, 64, 64,224, 64, 0, 0, 0, 0}; /* "t" */ +static const GLubyte TimesRoman10_Character_117[] = { 5, 0, 0, 0,104,144,144,144,144, 0, 0, 0, 0, 0}; /* "u" */ +static const GLubyte TimesRoman10_Character_118[] = { 5, 0, 0, 0, 32, 96, 80,144,216, 0, 0, 0, 0, 0}; /* "v" */ +static const GLubyte TimesRoman10_Character_119[] = { 8, 0, 0, 0, 40,108, 84,146,219, 0, 0, 0, 0, 0}; /* "w" */ +static const GLubyte TimesRoman10_Character_120[] = { 6, 0, 0, 0,216, 80, 32, 80,216, 0, 0, 0, 0, 0}; /* "x" */ +static const GLubyte TimesRoman10_Character_121[] = { 5, 0, 64, 64, 32, 48, 80, 72,220, 0, 0, 0, 0, 0}; /* "y" */ +static const GLubyte TimesRoman10_Character_122[] = { 5, 0, 0, 0,240,144, 64, 32,240, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte TimesRoman10_Character_065[] = { 8, 0, 0, 0,238, 68,124, 40, 40, 56, 16, 0, 0, 0}; /* "A" */ +static const GLubyte TimesRoman10_Character_066[] = { 6, 0, 0, 0,240, 72, 72,112, 72, 72,240, 0, 0, 0}; /* "B" */ +static const GLubyte TimesRoman10_Character_067[] = { 7, 0, 0, 0,120,196,128,128,128,196,124, 0, 0, 0}; /* "C" */ +static const GLubyte TimesRoman10_Character_068[] = { 7, 0, 0, 0,248, 76, 68, 68, 68, 76,248, 0, 0, 0}; /* "D" */ +static const GLubyte TimesRoman10_Character_069[] = { 6, 0, 0, 0,248, 72, 64,112, 64, 72,248, 0, 0, 0}; /* "E" */ +static const GLubyte TimesRoman10_Character_070[] = { 6, 0, 0, 0,224, 64, 64,112, 64, 72,248, 0, 0, 0}; /* "F" */ +static const GLubyte TimesRoman10_Character_071[] = { 7, 0, 0, 0,120,196,132,156,128,196,124, 0, 0, 0}; /* "G" */ +static const GLubyte TimesRoman10_Character_072[] = { 8, 0, 0, 0,238, 68, 68,124, 68, 68,238, 0, 0, 0}; /* "H" */ +static const GLubyte TimesRoman10_Character_073[] = { 4, 0, 0, 0,224, 64, 64, 64, 64, 64,224, 0, 0, 0}; /* "I" */ +static const GLubyte TimesRoman10_Character_074[] = { 4, 0, 0, 0,192,160, 32, 32, 32, 32,112, 0, 0, 0}; /* "J" */ +static const GLubyte TimesRoman10_Character_075[] = { 7, 0, 0, 0,236, 72, 80, 96, 80, 72,236, 0, 0, 0}; /* "K" */ +static const GLubyte TimesRoman10_Character_076[] = { 6, 0, 0, 0,248, 72, 64, 64, 64, 64,224, 0, 0, 0}; /* "L" */ +static const GLubyte TimesRoman10_Character_077[] = { 10, 0, 0, 0, 0, 0, 0,235,128, 73, 0, 85, 0, 85, 0, 99, 0, 99, 0,227,128, 0, 0, 0, 0, 0, 0}; /* "M" */ +static const GLubyte TimesRoman10_Character_078[] = { 8, 0, 0, 0,228, 76, 76, 84, 84,100,238, 0, 0, 0}; /* "N" */ +static const GLubyte TimesRoman10_Character_079[] = { 7, 0, 0, 0,120,204,132,132,132,204,120, 0, 0, 0}; /* "O" */ +static const GLubyte TimesRoman10_Character_080[] = { 6, 0, 0, 0,224, 64, 64,112, 72, 72,240, 0, 0, 0}; /* "P" */ +static const GLubyte TimesRoman10_Character_081[] = { 7, 0, 12, 24,112,204,132,132,132,204,120, 0, 0, 0}; /* "Q" */ +static const GLubyte TimesRoman10_Character_082[] = { 7, 0, 0, 0,236, 72, 80,112, 72, 72,240, 0, 0, 0}; /* "R" */ +static const GLubyte TimesRoman10_Character_083[] = { 5, 0, 0, 0,224,144, 16, 96,192,144,112, 0, 0, 0}; /* "S" */ +static const GLubyte TimesRoman10_Character_084[] = { 6, 0, 0, 0,112, 32, 32, 32, 32,168,248, 0, 0, 0}; /* "T" */ +static const GLubyte TimesRoman10_Character_085[] = { 8, 0, 0, 0, 56,108, 68, 68, 68, 68,238, 0, 0, 0}; /* "U" */ +static const GLubyte TimesRoman10_Character_086[] = { 8, 0, 0, 0, 16, 16, 40, 40,108, 68,238, 0, 0, 0}; /* "V" */ +static const GLubyte TimesRoman10_Character_087[] = { 10, 0, 0, 0, 0, 0, 0, 34, 0, 34, 0, 85, 0, 85, 0,201,128,136,128,221,192, 0, 0, 0, 0, 0, 0}; /* "W" */ +static const GLubyte TimesRoman10_Character_088[] = { 8, 0, 0, 0,238, 68, 40, 16, 40, 68,238, 0, 0, 0}; /* "X" */ +static const GLubyte TimesRoman10_Character_089[] = { 8, 0, 0, 0, 56, 16, 16, 40, 40, 68,238, 0, 0, 0}; /* "Y" */ +static const GLubyte TimesRoman10_Character_090[] = { 6, 0, 0, 0,248,136, 64, 32, 16,136,248, 0, 0, 0}; /* "Z" */ +static const GLubyte TimesRoman10_Character_048[] = { 5, 0, 0, 0, 96,144,144,144,144,144, 96, 0, 0, 0}; /* "0" */ +static const GLubyte TimesRoman10_Character_049[] = { 5, 0, 0, 0,224, 64, 64, 64, 64,192, 64, 0, 0, 0}; /* "1" */ +static const GLubyte TimesRoman10_Character_050[] = { 5, 0, 0, 0,240, 64, 32, 32, 16,144, 96, 0, 0, 0}; /* "2" */ +static const GLubyte TimesRoman10_Character_051[] = { 5, 0, 0, 0,224, 16, 16, 96, 16,144, 96, 0, 0, 0}; /* "3" */ +static const GLubyte TimesRoman10_Character_052[] = { 5, 0, 0, 0, 16, 16,248,144, 80, 48, 16, 0, 0, 0}; /* "4" */ +static const GLubyte TimesRoman10_Character_053[] = { 5, 0, 0, 0,224,144, 16, 16,224, 64,112, 0, 0, 0}; /* "5" */ +static const GLubyte TimesRoman10_Character_054[] = { 5, 0, 0, 0, 96,144,144,144,224, 64, 48, 0, 0, 0}; /* "6" */ +static const GLubyte TimesRoman10_Character_055[] = { 5, 0, 0, 0, 64, 64, 64, 32, 32,144,240, 0, 0, 0}; /* "7" */ +static const GLubyte TimesRoman10_Character_056[] = { 5, 0, 0, 0, 96,144,144, 96,144,144, 96, 0, 0, 0}; /* "8" */ +static const GLubyte TimesRoman10_Character_057[] = { 5, 0, 0, 0,192, 32,112,144,144,144, 96, 0, 0, 0}; /* "9" */ +static const GLubyte TimesRoman10_Character_096[] = { 3, 0, 0, 0, 0, 0, 0, 0, 0,192,128, 0, 0, 0}; /* "`" */ +static const GLubyte TimesRoman10_Character_126[] = { 7, 0, 0, 0, 0, 0,152,100, 0, 0, 0, 0, 0, 0}; /* "~" */ +static const GLubyte TimesRoman10_Character_033[] = { 3, 0, 0, 0,128, 0,128,128,128,128,128, 0, 0, 0}; /* "!" */ +static const GLubyte TimesRoman10_Character_064[] = { 9, 0, 0, 62, 0, 64, 0,146, 0,173, 0,165, 0,165, 0,157, 0, 66, 0, 60, 0, 0, 0, 0, 0, 0, 0}; /* "@" */ +static const GLubyte TimesRoman10_Character_035[] = { 5, 0, 0, 0, 80, 80,248, 80,248, 80, 80, 0, 0, 0}; /* "#" */ +static const GLubyte TimesRoman10_Character_036[] = { 5, 0, 0, 32,224,144, 16, 96,128,144,112, 32, 0, 0}; /* "$" */ +static const GLubyte TimesRoman10_Character_037[] = { 8, 0, 0, 0, 68, 42, 42, 86,168,164,126, 0, 0, 0}; /* "%" */ +static const GLubyte TimesRoman10_Character_094[] = { 5, 0, 0, 0, 0, 0, 0, 0,160,160, 64, 0, 0, 0}; /* "^" */ +static const GLubyte TimesRoman10_Character_038[] = { 8, 0, 0, 0,118,141,152,116,110, 80, 48, 0, 0, 0}; /* "&" */ +static const GLubyte TimesRoman10_Character_042[] = { 5, 0, 0, 0, 0, 0, 0, 0,160, 64,160, 0, 0, 0}; /* "*" */ +static const GLubyte TimesRoman10_Character_040[] = { 4, 0, 32, 64, 64,128,128,128, 64, 64, 32, 0, 0, 0}; /* "(" */ +static const GLubyte TimesRoman10_Character_041[] = { 4, 0,128, 64, 64, 32, 32, 32, 64, 64,128, 0, 0, 0}; /* ")" */ +static const GLubyte TimesRoman10_Character_045[] = { 7, 0, 0, 0, 0, 0,240, 0, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte TimesRoman10_Character_095[] = { 5,252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte TimesRoman10_Character_061[] = { 6, 0, 0, 0, 0,248, 0,248, 0, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte TimesRoman10_Character_043[] = { 6, 0, 0, 0, 32, 32,248, 32, 32, 0, 0, 0, 0, 0}; /* "+" */ +static const GLubyte TimesRoman10_Character_091[] = { 3, 0,192,128,128,128,128,128,128,128,192, 0, 0, 0}; /* "[" */ +static const GLubyte TimesRoman10_Character_123[] = { 4, 0, 32, 64, 64, 64,128, 64, 64, 64, 32, 0, 0, 0}; /* "{" */ +static const GLubyte TimesRoman10_Character_125[] = { 4, 0,128, 64, 64, 64, 32, 64, 64, 64,128, 0, 0, 0}; /* "}" */ +static const GLubyte TimesRoman10_Character_093[] = { 3, 0,192, 64, 64, 64, 64, 64, 64, 64,192, 0, 0, 0}; /* "]" */ +static const GLubyte TimesRoman10_Character_059[] = { 3, 0,128,128,128, 0, 0, 0,128, 0, 0, 0, 0, 0}; /* ";" */ +static const GLubyte TimesRoman10_Character_058[] = { 3, 0, 0, 0,128, 0, 0, 0,128, 0, 0, 0, 0, 0}; /* ":" */ +static const GLubyte TimesRoman10_Character_044[] = { 3, 0,128,128,128, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte TimesRoman10_Character_046[] = { 3, 0, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte TimesRoman10_Character_060[] = { 5, 0, 0, 0, 32, 64,128, 64, 32, 0, 0, 0, 0, 0}; /* "<" */ +static const GLubyte TimesRoman10_Character_062[] = { 5, 0, 0, 0,128, 64, 32, 64,128, 0, 0, 0, 0, 0}; /* ">" */ +static const GLubyte TimesRoman10_Character_047[] = { 3, 0, 0, 0,128,128, 64, 64, 64, 32, 32, 0, 0, 0}; /* "/" */ +static const GLubyte TimesRoman10_Character_063[] = { 4, 0, 0, 0, 64, 0, 64, 64, 32,160,224, 0, 0, 0}; /* "?" */ +static const GLubyte TimesRoman10_Character_092[] = { 3, 0, 0, 0, 32, 32, 64, 64, 64,128,128, 0, 0, 0}; /* "\" */ +static const GLubyte TimesRoman10_Character_034[] = { 4, 0, 0, 0, 0, 0, 0, 0, 0,160,160, 0, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte TimesRoman10_Character_039[] = { 3, 0, 0, 0, 0, 0, 0, 0, 0, 64, 64,192, 0, 0}; /* "'" */ +static const GLubyte TimesRoman10_Character_124[] = { 2,128,128,128,128,128,128,128,128,128,128,128,128,128}; /* "|" */ + + +/* The font characters mapping: */ +static const GLubyte* TimesRoman10_Character_Map[] = {TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_032,TimesRoman10_Character_033,TimesRoman10_Character_034,TimesRoman10_Character_035, +TimesRoman10_Character_036,TimesRoman10_Character_037,TimesRoman10_Character_038,TimesRoman10_Character_039,TimesRoman10_Character_040,TimesRoman10_Character_041,TimesRoman10_Character_042,TimesRoman10_Character_043,TimesRoman10_Character_044,TimesRoman10_Character_045,TimesRoman10_Character_046,TimesRoman10_Character_047,TimesRoman10_Character_048,TimesRoman10_Character_049,TimesRoman10_Character_050,TimesRoman10_Character_051,TimesRoman10_Character_052,TimesRoman10_Character_053,TimesRoman10_Character_054,TimesRoman10_Character_055,TimesRoman10_Character_056,TimesRoman10_Character_057,TimesRoman10_Character_058,TimesRoman10_Character_059,TimesRoman10_Character_060,TimesRoman10_Character_061,TimesRoman10_Character_062,TimesRoman10_Character_063,TimesRoman10_Character_064,TimesRoman10_Character_065,TimesRoman10_Character_066,TimesRoman10_Character_067,TimesRoman10_Character_068,TimesRoman10_Character_069,TimesRoman10_Character_070,TimesRoman10_Character_071,TimesRoman10_Character_072, +TimesRoman10_Character_073,TimesRoman10_Character_074,TimesRoman10_Character_075,TimesRoman10_Character_076,TimesRoman10_Character_077,TimesRoman10_Character_078,TimesRoman10_Character_079,TimesRoman10_Character_080,TimesRoman10_Character_081,TimesRoman10_Character_082,TimesRoman10_Character_083,TimesRoman10_Character_084,TimesRoman10_Character_085,TimesRoman10_Character_086,TimesRoman10_Character_087,TimesRoman10_Character_088,TimesRoman10_Character_089,TimesRoman10_Character_090,TimesRoman10_Character_091,TimesRoman10_Character_092,TimesRoman10_Character_093,TimesRoman10_Character_094,TimesRoman10_Character_095,TimesRoman10_Character_096,TimesRoman10_Character_097,TimesRoman10_Character_098,TimesRoman10_Character_099,TimesRoman10_Character_100,TimesRoman10_Character_101,TimesRoman10_Character_102,TimesRoman10_Character_103,TimesRoman10_Character_104,TimesRoman10_Character_105,TimesRoman10_Character_106,TimesRoman10_Character_107,TimesRoman10_Character_108,TimesRoman10_Character_109, +TimesRoman10_Character_110,TimesRoman10_Character_111,TimesRoman10_Character_112,TimesRoman10_Character_113,TimesRoman10_Character_114,TimesRoman10_Character_115,TimesRoman10_Character_116,TimesRoman10_Character_117,TimesRoman10_Character_118,TimesRoman10_Character_119,TimesRoman10_Character_120,TimesRoman10_Character_121,TimesRoman10_Character_122,TimesRoman10_Character_123,TimesRoman10_Character_124,TimesRoman10_Character_125,TimesRoman10_Character_126,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042, +TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042, +TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042, +TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,TimesRoman10_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontTimesRoman10 = { "-adobe-times-medium-r-normal--10-100-75-75-p-54-iso8859-1", 93, 13, TimesRoman10_Character_Map, 0.0f, 3.0f }; + +static const GLubyte TimesRoman24_Character_032[] = { 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* " " */ +static const GLubyte TimesRoman24_Character_097[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,113,128,251, 0,199, 0,195, 0,195, 0, 99, 0, 59, 0, 15, 0, 3, 0, 99, 0,103, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "a" */ +static const GLubyte TimesRoman24_Character_098[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 0,115,128, 97,128, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 97,128,115,128,110, 0, 96, 0, 96, 0, 96, 0, 96, 0,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "b" */ +static const GLubyte TimesRoman24_Character_099[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0,127, 0,112,128,224, 0,192, 0,192, 0,192, 0,192, 0,192, 0, 65,128, 99,128, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "c" */ +static const GLubyte TimesRoman24_Character_100[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30,192,115,128, 97,128,193,128,193,128,193,128,193,128,193,128,193,128, 97,128,115,128, 29,128, 1,128, 1,128, 1,128, 1,128, 3,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "d" */ +static const GLubyte TimesRoman24_Character_101[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0,127, 0,112,128,224, 0,192, 0,192, 0,192, 0,255,128,193,128, 65,128, 99, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "e" */ +static const GLubyte TimesRoman24_Character_102[] = { 7, 0, 0, 0, 0, 0, 0,120, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,254, 48, 48, 48, 22, 14, 0, 0, 0, 0, 0}; /* "f" */ +static const GLubyte TimesRoman24_Character_103[] = { 12, 0, 0, 63, 0,241,192,192, 96,192, 32, 96, 96, 63,192,127, 0, 96, 0, 48, 0, 62, 0, 51, 0, 97,128, 97,128, 97,128, 97,128, 51, 0, 31,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "g" */ +static const GLubyte TimesRoman24_Character_104[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,241,224, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192,113,192,111,128,103, 0, 96, 0, 96, 0, 96, 0, 96, 0,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "h" */ +static const GLubyte TimesRoman24_Character_105[] = { 6, 0, 0, 0, 0, 0, 0,240, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,224, 0, 0, 0, 96, 96, 0, 0, 0, 0, 0}; /* "i" */ +static const GLubyte TimesRoman24_Character_106[] = { 6, 0,192,224, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,112, 0, 0, 0, 48, 48, 0, 0, 0, 0, 0}; /* "j" */ +static const GLubyte TimesRoman24_Character_107[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,243,224, 97,192, 99,128,103, 0,110, 0,108, 0,120, 0,104, 0,100, 0,102, 0, 99, 0,103,192, 96, 0, 96, 0, 96, 0, 96, 0,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "k" */ +static const GLubyte TimesRoman24_Character_108[] = { 6, 0, 0, 0, 0, 0, 0,240, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,224, 0, 0, 0, 0, 0}; /* "l" */ +static const GLubyte TimesRoman24_Character_109[] = { 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,241,227,192, 96,193,128, 96,193,128, 96,193,128, 96,193,128, 96,193,128, 96,193,128, 96,193,128, 96,193,128,113,227,128,111,159, 0,231, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "m" */ +static const GLubyte TimesRoman24_Character_110[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,241,224, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192,113,192,111,128,231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "n" */ +static const GLubyte TimesRoman24_Character_111[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0,115,128, 97,128,192,192,192,192,192,192,192,192,192,192,192,192, 97,128,115,128, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "o" */ +static const GLubyte TimesRoman24_Character_112[] = { 12, 0, 0,240, 0, 96, 0, 96, 0, 96, 0, 96, 0,110, 0,115,128, 97,128, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 97,128,115,128,238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "p" */ +static const GLubyte TimesRoman24_Character_113[] = { 12, 0, 0, 3,192, 1,128, 1,128, 1,128, 1,128, 29,128,115,128, 97,128,193,128,193,128,193,128,193,128,193,128,193,128, 97,128,115,128, 29,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "q" */ +static const GLubyte TimesRoman24_Character_114[] = { 8, 0, 0, 0, 0, 0, 0,240, 96, 96, 96, 96, 96, 96, 96, 96,118,110,230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "r" */ +static const GLubyte TimesRoman24_Character_115[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,248, 0,198, 0,131, 0, 3, 0, 7, 0, 30, 0,124, 0,112, 0,224, 0,194, 0,102, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "s" */ +static const GLubyte TimesRoman24_Character_116[] = { 7, 0, 0, 0, 0, 0, 0, 28, 50, 48, 48, 48, 48, 48, 48, 48, 48, 48,254,112, 48, 16, 0, 0, 0, 0, 0, 0, 0}; /* "t" */ +static const GLubyte TimesRoman24_Character_117[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28,224, 62,192,113,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192, 96,192,225,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "u" */ +static const GLubyte TimesRoman24_Character_118[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 14, 0, 14, 0, 26, 0, 25, 0, 25, 0, 49, 0, 48,128, 48,128, 96,128, 96,192,241,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "v" */ +static const GLubyte TimesRoman24_Character_119[] = { 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 16, 0, 14, 56, 0, 14, 56, 0, 26, 40, 0, 26,100, 0, 25,100, 0, 49,100, 0, 48,194, 0, 48,194, 0, 96,194, 0, 96,195, 0,241,231,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "w" */ +static const GLubyte TimesRoman24_Character_120[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,241,224, 96,192, 33,128, 51,128, 27, 0, 14, 0, 12, 0, 26, 0, 57, 0, 49,128, 96,192,241,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "x" */ +static const GLubyte TimesRoman24_Character_121[] = { 11, 0, 0,224, 0,240, 0, 24, 0, 8, 0, 12, 0, 4, 0, 14, 0, 14, 0, 26, 0, 25, 0, 25, 0, 49, 0, 48,128, 48,128, 96,128, 96,192,241,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "y" */ +static const GLubyte TimesRoman24_Character_122[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255, 0,195, 0, 97, 0,112, 0, 48, 0, 56, 0, 24, 0, 28, 0, 14, 0,134, 0,195, 0,255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "z" */ +static const GLubyte TimesRoman24_Character_065[] = { 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 31,128, 48, 6, 0, 16, 6, 0, 16, 12, 0, 24, 12, 0, 8, 12, 0, 15,248, 0, 12, 24, 0, 4, 24, 0, 4, 48, 0, 6, 48, 0, 2, 48, 0, 2, 96, 0, 1, 96, 0, 1,192, 0, 1,192, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "A" */ +static const GLubyte TimesRoman24_Character_066[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,224, 48,120, 48, 24, 48, 12, 48, 12, 48, 12, 48, 24, 48, 56, 63,224, 48, 64, 48, 48, 48, 24, 48, 24, 48, 24, 48, 48, 48,112,255,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "B" */ +static const GLubyte TimesRoman24_Character_067[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,224, 30, 56, 56, 8, 96, 4, 96, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0,192, 0, 96, 4, 96, 4, 56, 12, 28, 60, 7,228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "C" */ +static const GLubyte TimesRoman24_Character_068[] = { 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,192, 0, 48,112, 0, 48, 56, 0, 48, 12, 0, 48, 12, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 12, 0, 48, 12, 0, 48, 56, 0, 48,112, 0,255,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "D" */ +static const GLubyte TimesRoman24_Character_069[] = { 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,248, 48, 24, 48, 8, 48, 8, 48, 0, 48, 0, 48, 64, 48, 64, 63,192, 48, 64, 48, 64, 48, 0, 48, 0, 48, 16, 48, 16, 48, 48,255,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "E" */ +static const GLubyte TimesRoman24_Character_070[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 32, 48, 32, 63,224, 48, 32, 48, 32, 48, 0, 48, 0, 48, 16, 48, 16, 48, 48,255,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "F" */ +static const GLubyte TimesRoman24_Character_071[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,224, 0, 30, 56, 0, 56, 28, 0, 96, 12, 0, 96, 12, 0,192, 12, 0,192, 12, 0,192, 63, 0,192, 0, 0,192, 0, 0,192, 0, 0,192, 0, 0, 96, 4, 0, 96, 4, 0, 56, 12, 0, 28, 60, 0, 7,228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "G" */ +static const GLubyte TimesRoman24_Character_072[] = { 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 31,128, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 63,254, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0, 48, 6, 0,252, 31,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "H" */ +static const GLubyte TimesRoman24_Character_073[] = { 8, 0, 0, 0, 0, 0, 0,252, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,252, 0, 0, 0, 0, 0}; /* "I" */ +static const GLubyte TimesRoman24_Character_074[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,120, 0,204, 0,198, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 31,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "J" */ +static const GLubyte TimesRoman24_Character_075[] = { 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 31, 0, 48, 14, 0, 48, 28, 0, 48, 56, 0, 48,112, 0, 48,224, 0, 49,192, 0, 51,128, 0, 63, 0, 0, 62, 0, 0, 51, 0, 0, 49,128, 0, 48,192, 0, 48, 96, 0, 48, 48, 0, 48, 24, 0,252,126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "K" */ +static const GLubyte TimesRoman24_Character_076[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,248, 48, 24, 48, 8, 48, 8, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0,252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "L" */ +static const GLubyte TimesRoman24_Character_077[] = { 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,248, 33,248, 32, 96, 96, 32, 96, 96, 32,208, 96, 32,208, 96, 33,136, 96, 33,136, 96, 35, 8, 96, 35, 4, 96, 38, 4, 96, 38, 2, 96, 44, 2, 96, 44, 2, 96, 56, 1, 96, 56, 1, 96, 48, 0,224,240, 0,248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "M" */ +static const GLubyte TimesRoman24_Character_078[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,248, 12, 0, 32, 28, 0, 32, 28, 0, 32, 52, 0, 32,100, 0, 32,100, 0, 32,196, 0, 33,132, 0, 33,132, 0, 35, 4, 0, 38, 4, 0, 38, 4, 0, 44, 4, 0, 56, 4, 0, 56, 4, 0, 48, 4, 0,240, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "N" */ +static const GLubyte TimesRoman24_Character_079[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,224, 0, 28, 56, 0, 56, 28, 0, 96, 6, 0, 96, 6, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0, 96, 6, 0, 96, 6, 0, 56, 28, 0, 28, 56, 0, 7,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "O" */ +static const GLubyte TimesRoman24_Character_080[] = { 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 48, 0, 63,192, 48,112, 48, 48, 48, 24, 48, 24, 48, 24, 48, 48, 48,112,255,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "P" */ +static const GLubyte TimesRoman24_Character_081[] = { 18, 0, 0, 0, 0, 15, 0, 0, 56, 0, 0,112, 0, 0,224, 0, 1,192, 0, 7,224, 0, 28, 56, 0, 56, 28, 0, 96, 6, 0, 96, 6, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0,192, 3, 0, 96, 6, 0, 96, 6, 0, 56, 28, 0, 28, 56, 0, 7,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "Q" */ +static const GLubyte TimesRoman24_Character_082[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 30, 48, 28, 48, 56, 48,112, 48, 96, 48,192, 49,192, 51,128, 63,192, 48,112, 48, 48, 48, 56, 48, 24, 48, 56, 48, 48, 48,112,255,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "R" */ +static const GLubyte TimesRoman24_Character_083[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,158, 0,241,128,192,192,128, 96,128, 96, 0, 96, 0,224, 3,192, 15,128, 30, 0,120, 0,224, 0,192, 64,192, 64,192,192, 99,192, 30, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "S" */ +static const GLubyte TimesRoman24_Character_084[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15,192, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0,131, 4,131, 4,195, 12,255,252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "T" */ +static const GLubyte TimesRoman24_Character_085[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,224, 0, 28, 48, 0, 24, 8, 0, 48, 8, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0, 48, 4, 0,252, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "U" */ +static const GLubyte TimesRoman24_Character_086[] = { 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,128, 0, 1,128, 0, 1,128, 0, 3,192, 0, 3, 64, 0, 3, 96, 0, 6, 32, 0, 6, 32, 0, 6, 48, 0, 12, 16, 0, 12, 24, 0, 24, 8, 0, 24, 8, 0, 24, 12, 0, 48, 4, 0, 48, 6, 0,252, 31,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "V" */ +static const GLubyte TimesRoman24_Character_087[] = { 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,131, 0, 1,131, 0, 1,131,128, 3,135,128, 3, 70,128, 3, 70,192, 6, 70, 64, 6, 76, 64, 6, 76, 96, 12, 44, 96, 12, 44, 32, 24, 44, 32, 24, 24, 48, 24, 24, 16, 48, 24, 16, 48, 24, 24,252,126,126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "W" */ +static const GLubyte TimesRoman24_Character_088[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,252, 15,192, 48, 3,128, 24, 7, 0, 8, 14, 0, 4, 12, 0, 6, 24, 0, 2, 56, 0, 1,112, 0, 0,224, 0, 0,192, 0, 1,192, 0, 3,160, 0, 3, 16, 0, 6, 8, 0, 14, 12, 0, 28, 6, 0,126, 15,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "X" */ +static const GLubyte TimesRoman24_Character_089[] = { 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,224, 1,128, 1,128, 1,128, 1,128, 1,128, 1,128, 3,192, 3, 64, 6, 96, 6, 32, 12, 48, 28, 16, 24, 24, 56, 8, 48, 12,252, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "Y" */ +static const GLubyte TimesRoman24_Character_090[] = { 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,248,224, 24,112, 8, 48, 8, 56, 0, 24, 0, 28, 0, 14, 0, 6, 0, 7, 0, 3, 0, 3,128, 1,192,128,192,128,224,192,112,255,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "Z" */ +static const GLubyte TimesRoman24_Character_048[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, 51, 0, 97,128, 97,128,225,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192, 97,128, 97,128, 51, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "0" */ +static const GLubyte TimesRoman24_Character_049[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0,120, 0, 24, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "1" */ +static const GLubyte TimesRoman24_Character_050[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,128,255,192, 96, 64, 48, 0, 24, 0, 12, 0, 4, 0, 6, 0, 3, 0, 3, 0, 1,128, 1,128,129,128,129,128, 67,128,127, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "2" */ +static const GLubyte TimesRoman24_Character_051[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,120, 0,230, 0,195, 0, 1, 0, 1,128, 1,128, 1,128, 3,128, 7, 0, 30, 0, 12, 0, 6, 0,131, 0,131, 0, 71, 0,126, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "3" */ +static const GLubyte TimesRoman24_Character_052[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 3, 0, 3, 0,255,192,255,192,195, 0, 67, 0, 99, 0, 35, 0, 51, 0, 19, 0, 27, 0, 11, 0, 7, 0, 7, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "4" */ +static const GLubyte TimesRoman24_Character_053[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,126, 0,227,128,193,128, 0,192, 0,192, 0,192, 0,192, 1,192, 3,128, 15,128,126, 0,120, 0, 96, 0, 32, 0, 32, 0, 31,128, 31,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "5" */ +static const GLubyte TimesRoman24_Character_054[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0,123,128, 97,128,224,192,192,192,192,192,192,192,192,192,193,128,243,128,238, 0, 96, 0,112, 0, 48, 0, 24, 0, 14, 0, 3,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "6" */ +static const GLubyte TimesRoman24_Character_055[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 24, 0, 12, 0, 12, 0, 12, 0, 4, 0, 6, 0, 6, 0, 2, 0, 3, 0, 3, 0, 1, 0, 1,128,129,128,192,192,255,192,127,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "7" */ +static const GLubyte TimesRoman24_Character_056[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0,115,128,225,128,192,192,192,192,192,192, 65,192, 97,128, 55, 0, 30, 0, 30, 0, 51, 0, 97,128, 97,128, 97,128, 51, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "8" */ +static const GLubyte TimesRoman24_Character_057[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,240, 0, 28, 0, 6, 0, 3, 0, 3,128, 1,128, 29,128,115,192, 97,192,192,192,192,192,192,192,192,192,193,192, 97,128,119,128, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "9" */ +static const GLubyte TimesRoman24_Character_096[] = { 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96,224,128,192, 96, 0, 0, 0, 0, 0}; /* "`" */ +static const GLubyte TimesRoman24_Character_126[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,131,128,199,192,124, 96, 56, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "~" */ +static const GLubyte TimesRoman24_Character_033[] = { 8, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0,192,192,192,192,192,192,192,192,192,192,192,192, 0, 0, 0, 0, 0}; /* "!" */ +static const GLubyte TimesRoman24_Character_064[] = { 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,240, 0, 14, 12, 0, 24, 0, 0, 48, 0, 0, 97,222, 0, 99,123, 0,198, 57,128,198, 24,128,198, 24,192,198, 24, 64,198, 12, 64,195, 12, 64,195,140, 64,225,252, 64, 96,236,192,112, 0,128, 56, 1,128, 28, 3, 0, 15, 14, 0, 3,248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "@" */ +static const GLubyte TimesRoman24_Character_035[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 0, 34, 0, 34, 0, 34, 0, 34, 0,255,192,255,192, 17, 0, 17, 0, 17, 0,127,224,127,224, 8,128, 8,128, 8,128, 8,128, 8,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "#" */ +static const GLubyte TimesRoman24_Character_036[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 63, 0,229,192,196,192,132, 96,132, 96, 4, 96, 4,224, 7,192, 7,128, 30, 0, 60, 0,116, 0,100, 0,100, 32,100, 96, 52,224, 31,128, 4, 0, 4, 0, 0, 0, 0, 0, 0, 0}; /* "$" */ +static const GLubyte TimesRoman24_Character_037[] = { 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 60, 0, 24,114, 0, 12, 97, 0, 4, 96,128, 6, 96,128, 3, 48,128, 1, 25,128, 1,143, 0,120,192, 0,228, 64, 0,194, 96, 0,193, 48, 0,193, 16, 0, 97, 24, 0, 51,252, 0, 30, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "%" */ +static const GLubyte TimesRoman24_Character_094[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128,128,193,128, 65, 0, 99, 0, 34, 0, 54, 0, 20, 0, 28, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "^" */ +static const GLubyte TimesRoman24_Character_038[] = { 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 60, 0,127,126, 0,225,225, 0,192,192, 0,193,192, 0,193,160, 0, 99, 32, 0, 55, 16, 0, 30, 24, 0, 14, 62, 0, 15, 0, 0, 29,128, 0, 24,192, 0, 24, 64, 0, 24, 64, 0, 12,192, 0, 7,128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "&" */ +static const GLubyte TimesRoman24_Character_042[] = { 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 28, 0,201,128,235,128, 28, 0,235,128,201,128, 28, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "*" */ +static const GLubyte TimesRoman24_Character_040[] = { 8, 0, 4, 8, 16, 48, 32, 96, 96,192,192,192,192,192,192,192,192, 96, 96, 32, 48, 16, 8, 4, 0, 0, 0, 0, 0}; /* "(" */ +static const GLubyte TimesRoman24_Character_041[] = { 8, 0,128, 64, 32, 48, 16, 24, 24, 12, 12, 12, 12, 12, 12, 12, 12, 24, 24, 16, 48, 32, 64,128, 0, 0, 0, 0, 0}; /* ")" */ +static const GLubyte TimesRoman24_Character_045[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,240,255,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "-" */ +static const GLubyte TimesRoman24_Character_095[] = { 13, 0, 0,255,248,255,248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "_" */ +static const GLubyte TimesRoman24_Character_061[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,255,240,255,240, 0, 0, 0, 0,255,240,255,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "=" */ +static const GLubyte TimesRoman24_Character_043[] = { 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0,255,240,255,240, 6, 0, 6, 0, 6, 0, 6, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "+" */ +static const GLubyte TimesRoman24_Character_091[] = { 8, 0, 0,248,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,248, 0, 0, 0, 0, 0}; /* "[" */ +static const GLubyte TimesRoman24_Character_123[] = { 10, 0, 0, 7, 0, 12, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 16, 0, 48, 0, 32, 0,192, 0, 32, 0, 48, 0, 16, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 12, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "{" */ +static const GLubyte TimesRoman24_Character_125[] = { 10, 0, 0,224, 0, 48, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 8, 0, 12, 0, 4, 0, 3, 0, 4, 0, 12, 0, 8, 0, 24, 0, 24, 0, 24, 0, 24, 0, 24, 0, 48, 0,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "}" */ +static const GLubyte TimesRoman24_Character_093[] = { 8, 0, 0,248, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,248, 0, 0, 0, 0, 0}; /* "]" */ +static const GLubyte TimesRoman24_Character_059[] = { 7, 0, 0, 0,192, 96, 32,224,192, 0, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ";" */ +static const GLubyte TimesRoman24_Character_058[] = { 6, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ":" */ +static const GLubyte TimesRoman24_Character_044[] = { 7, 0, 0, 0,192, 96, 32,224,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "," */ +static const GLubyte TimesRoman24_Character_046[] = { 6, 0, 0, 0, 0, 0, 0,192,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "." */ +static const GLubyte TimesRoman24_Character_060[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 1,192, 7, 0, 28, 0,112, 0,192, 0,112, 0, 28, 0, 7, 0, 1,192, 0, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "<" */ +static const GLubyte TimesRoman24_Character_062[] = { 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,192, 0,112, 0, 28, 0, 7, 0, 1,192, 0, 96, 1,192, 7, 0, 28, 0,112, 0,192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* ">" */ +static const GLubyte TimesRoman24_Character_047[] = { 7, 0, 0, 0,192,192,192, 96, 96, 32, 48, 48, 16, 24, 24, 8, 12, 12, 4, 6, 6, 3, 3, 3, 0, 0, 0, 0, 0}; /* "/" */ +static const GLubyte TimesRoman24_Character_063[] = { 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 48, 0, 0, 0, 0, 0, 16, 0, 16, 0, 16, 0, 24, 0, 24, 0, 12, 0, 14, 0, 7, 0,195, 0,195, 0,131, 0,198, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* "?" */ +static const GLubyte TimesRoman24_Character_092[] = { 7, 0, 0, 0, 0, 0, 0, 6, 6, 4, 12, 12, 8, 24, 24, 16, 48, 48, 32, 96, 96, 64,192,192, 0, 0, 0, 0, 0}; /* "\" */ +static const GLubyte TimesRoman24_Character_034[] = { 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,136, 0,204, 0,204, 0,204, 0,204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* """ */ + +/* Missing Characters filled in by John Fay by hand ... */ +static const GLubyte TimesRoman24_Character_039[] = { 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,192, 96, 32,224,192, 0, 0, 0, 0, 0}; /* "'" */ +static const GLubyte TimesRoman24_Character_124[] = { 6, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 0, 0}; /* "|" */ + + +/* The font characters mapping: */ +static const GLubyte* TimesRoman24_Character_Map[] = {TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_032,TimesRoman24_Character_033,TimesRoman24_Character_034,TimesRoman24_Character_035, +TimesRoman24_Character_036,TimesRoman24_Character_037,TimesRoman24_Character_038,TimesRoman24_Character_039,TimesRoman24_Character_040,TimesRoman24_Character_041,TimesRoman24_Character_042,TimesRoman24_Character_043,TimesRoman24_Character_044,TimesRoman24_Character_045,TimesRoman24_Character_046,TimesRoman24_Character_047,TimesRoman24_Character_048,TimesRoman24_Character_049,TimesRoman24_Character_050,TimesRoman24_Character_051,TimesRoman24_Character_052,TimesRoman24_Character_053,TimesRoman24_Character_054,TimesRoman24_Character_055,TimesRoman24_Character_056,TimesRoman24_Character_057,TimesRoman24_Character_058,TimesRoman24_Character_059,TimesRoman24_Character_060,TimesRoman24_Character_061,TimesRoman24_Character_062,TimesRoman24_Character_063,TimesRoman24_Character_064,TimesRoman24_Character_065,TimesRoman24_Character_066,TimesRoman24_Character_067,TimesRoman24_Character_068,TimesRoman24_Character_069,TimesRoman24_Character_070,TimesRoman24_Character_071,TimesRoman24_Character_072, +TimesRoman24_Character_073,TimesRoman24_Character_074,TimesRoman24_Character_075,TimesRoman24_Character_076,TimesRoman24_Character_077,TimesRoman24_Character_078,TimesRoman24_Character_079,TimesRoman24_Character_080,TimesRoman24_Character_081,TimesRoman24_Character_082,TimesRoman24_Character_083,TimesRoman24_Character_084,TimesRoman24_Character_085,TimesRoman24_Character_086,TimesRoman24_Character_087,TimesRoman24_Character_088,TimesRoman24_Character_089,TimesRoman24_Character_090,TimesRoman24_Character_091,TimesRoman24_Character_092,TimesRoman24_Character_093,TimesRoman24_Character_094,TimesRoman24_Character_095,TimesRoman24_Character_096,TimesRoman24_Character_097,TimesRoman24_Character_098,TimesRoman24_Character_099,TimesRoman24_Character_100,TimesRoman24_Character_101,TimesRoman24_Character_102,TimesRoman24_Character_103,TimesRoman24_Character_104,TimesRoman24_Character_105,TimesRoman24_Character_106,TimesRoman24_Character_107,TimesRoman24_Character_108,TimesRoman24_Character_109, +TimesRoman24_Character_110,TimesRoman24_Character_111,TimesRoman24_Character_112,TimesRoman24_Character_113,TimesRoman24_Character_114,TimesRoman24_Character_115,TimesRoman24_Character_116,TimesRoman24_Character_117,TimesRoman24_Character_118,TimesRoman24_Character_119,TimesRoman24_Character_120,TimesRoman24_Character_121,TimesRoman24_Character_122,TimesRoman24_Character_123,TimesRoman24_Character_124,TimesRoman24_Character_125,TimesRoman24_Character_126,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042, +TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042, +TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042, +TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,TimesRoman24_Character_042,NULL}; + +/* The font structure: */ +static const SFG_Font fgFontTimesRoman24 = { "-adobe-times-medium-r-normal--24-240-75-75-p-124-iso8859-1", 93, 28, TimesRoman24_Character_Map, -1.0f, 6.0f }; + + + +/* + * End of code from FreeGLUT. + */ + + +#define ARGS(X) (X.Characters, 1, X.Height, X.xorig, X.yorig) + +static fntBitmapFont fntFixed8x13 ARGS( fgFontFixed8x13 ); +static fntBitmapFont fntFixed9x15 ARGS( fgFontFixed9x15 ); +static fntBitmapFont fntHelvetica10 ARGS( fgFontHelvetica10 ); +static fntBitmapFont fntHelvetica12 ARGS( fgFontHelvetica12 ); +static fntBitmapFont fntHelvetica18 ARGS( fgFontHelvetica18 ); +static fntBitmapFont fntTimesRoman10 ARGS( fgFontTimesRoman10 ); +static fntBitmapFont fntTimesRoman24 ARGS( fgFontTimesRoman24 ); + + +fntBitmapFont *fntGetBitmapFont(int id) +{ + fntBitmapFont *fnt = NULL; + switch (id) { + case FNT_BITMAP_8_BY_13: fnt = &fntFixed8x13; break; + case FNT_BITMAP_9_BY_15: fnt = &fntFixed9x15; break; + case FNT_BITMAP_HELVETICA_10: fnt = &fntHelvetica10; break; + case FNT_BITMAP_HELVETICA_12: fnt = &fntHelvetica12; break; + case FNT_BITMAP_HELVETICA_18: fnt = &fntHelvetica18; break; + case FNT_BITMAP_TIMES_ROMAN_10: fnt = &fntTimesRoman10; break; + case FNT_BITMAP_TIMES_ROMAN_24: fnt = &fntTimesRoman24; break; + } + return fnt; +} diff --git a/3rdparty/fonts/fntLocal.h b/3rdparty/fonts/fntLocal.h new file mode 100644 index 000000000..be020e918 --- /dev/null +++ b/3rdparty/fonts/fntLocal.h @@ -0,0 +1,112 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: fntLocal.h 1727 2002-11-09 21:27:21Z ude $ +*/ + + +#include "fnt.h" + + +extern int _fntIsSwapped ; +extern FILE *_fntCurrImageFd ; + + +inline void _fnt_swab_short ( unsigned short *x ) +{ + if ( _fntIsSwapped ) + *x = (( *x >> 8 ) & 0x00FF ) | + (( *x << 8 ) & 0xFF00 ) ; +} + + +inline void _fnt_swab_int ( unsigned int *x ) +{ + if ( _fntIsSwapped ) + *x = (( *x >> 24 ) & 0x000000FF ) | + (( *x >> 8 ) & 0x0000FF00 ) | + (( *x << 8 ) & 0x00FF0000 ) | + (( *x << 24 ) & 0xFF000000 ) ; +} + + +inline void _fnt_swab_int_array ( int *x, int leng ) +{ + if ( ! _fntIsSwapped ) + return ; + + for ( int i = 0 ; i < leng ; i++ ) + { + *x = (( *x >> 24 ) & 0x000000FF ) | + (( *x >> 8 ) & 0x0000FF00 ) | + (( *x << 8 ) & 0x00FF0000 ) | + (( *x << 24 ) & 0xFF000000 ) ; + x++ ; + } +} + + +inline unsigned char _fnt_readByte () +{ + unsigned char x ; + fread ( & x, sizeof(unsigned char), 1, _fntCurrImageFd ) ; + return x ; +} + +inline unsigned short _fnt_readShort () +{ + unsigned short x ; + fread ( & x, sizeof(unsigned short), 1, _fntCurrImageFd ) ; + _fnt_swab_short ( & x ) ; + return x ; +} + +inline unsigned int _fnt_readInt () +{ + unsigned int x ; + fread ( & x, sizeof(unsigned int), 1, _fntCurrImageFd ) ; + _fnt_swab_int ( & x ) ; + return x ; +} + + +#define FNT_BYTE_FORMAT 0 +#define FNT_BITMAP_FORMAT 1 + +struct TXF_Glyph +{ + unsigned short ch ; + unsigned char w ; + unsigned char h ; + signed char x_off ; + signed char y_off ; + signed char step ; + signed char unknown ; + short x ; + short y ; + + sgVec2 tx0 ; sgVec2 vx0 ; + sgVec2 tx1 ; sgVec2 vx1 ; + sgVec2 tx2 ; sgVec2 vx2 ; + sgVec2 tx3 ; sgVec2 vx3 ; +} ; + + +void fntSetError(int, const char* format, ...); \ No newline at end of file diff --git a/3rdparty/fonts/fntTXF.cxx b/3rdparty/fonts/fntTXF.cxx new file mode 100755 index 000000000..36c5bd175 --- /dev/null +++ b/3rdparty/fonts/fntTXF.cxx @@ -0,0 +1,341 @@ +/* + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information visit http://plib.sourceforge.net + + $Id: fntTXF.cxx 1735 2002-12-01 18:21:48Z sjbaker $ +*/ + + +#include "fntLocal.h" + +#include +#include + +FILE *_fntCurrImageFd ; +int _fntIsSwapped = FALSE ; + +static void tex_make_mip_maps ( GLubyte *image, int xsize, + int ysize, int zsize ) +{ + GLubyte *texels [ 20 ] ; /* One element per level of MIPmap */ + + for ( int l = 0 ; l < 20 ; l++ ) + texels [ l ] = NULL ; + + texels [ 0 ] = image ; + + int lev ; + + for ( lev = 0 ; (( xsize >> (lev+1) ) != 0 || + ( ysize >> (lev+1) ) != 0 ) ; lev++ ) + { + /* Suffix '1' is the higher level map, suffix '2' is the lower level. */ + + int l1 = lev ; + int l2 = lev+1 ; + int w1 = xsize >> l1 ; + int h1 = ysize >> l1 ; + int w2 = xsize >> l2 ; + int h2 = ysize >> l2 ; + + if ( w1 <= 0 ) w1 = 1 ; + if ( h1 <= 0 ) h1 = 1 ; + if ( w2 <= 0 ) w2 = 1 ; + if ( h2 <= 0 ) h2 = 1 ; + + texels [ l2 ] = new GLubyte [ w2 * h2 * zsize ] ; + + for ( int x2 = 0 ; x2 < w2 ; x2++ ) + for ( int y2 = 0 ; y2 < h2 ; y2++ ) + for ( int c = 0 ; c < zsize ; c++ ) + { + int x1 = x2 + x2 ; + int x1_1 = ( x1 + 1 ) % w1 ; + int y1 = y2 + y2 ; + int y1_1 = ( y1 + 1 ) % h1 ; + + int t1 = texels [ l1 ] [ (y1 * w1 + x1 ) * zsize + c ] ; + int t2 = texels [ l1 ] [ (y1_1 * w1 + x1 ) * zsize + c ] ; + int t3 = texels [ l1 ] [ (y1 * w1 + x1_1) * zsize + c ] ; + int t4 = texels [ l1 ] [ (y1_1 * w1 + x1_1) * zsize + c ] ; + + texels [ l2 ] [ (y2 * w2 + x2) * zsize + c ] = + ( t1 + t2 + t3 + t4 ) / 4 ; + } + } + + texels [ lev+1 ] = NULL ; + + if ( ! ((xsize & (xsize-1))==0) || + ! ((ysize & (ysize-1))==0) ) + { + fntSetError ( SG_ALERT, + "TXFloader: TXF Map is not a power-of-two in size!" ) ; + } + + glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 ) ; + + int map_level = 0 ; + +#ifdef PROXY_TEXTURES_ARE_NOT_BROKEN + int ww ; + + do + { + glTexImage2D ( GL_PROXY_TEXTURE_2D, + map_level, zsize, xsize, ysize, FALSE /* Border */, + (zsize==1)?GL_LUMINANCE: + (zsize==2)?GL_LUMINANCE_ALPHA: + (zsize==3)?GL_RGB: + GL_RGBA, + GL_UNSIGNED_BYTE, NULL ) ; + + glGetTexLevelParameteriv ( GL_PROXY_TEXTURE_2D, 0,GL_TEXTURE_WIDTH, &ww ) ; + + if ( ww == 0 ) + { + delete [] texels [ 0 ] ; + xsize >>= 1 ; + ysize >>= 1 ; + + for ( int l = 0 ; texels [ l ] != NULL ; l++ ) + texels [ l ] = texels [ l+1 ] ; + + if ( xsize < 64 && ysize < 64 ) + { + fntSetError ( SG_ALERT, + "FNT: OpenGL will not accept a font texture?!?" ) ; + } + } + } while ( ww == 0 ) ; +#endif + + for ( int i = 0 ; texels [ i ] != NULL ; i++ ) + { + int w = xsize>>i ; + int h = ysize>>i ; + + if ( w <= 0 ) w = 1 ; + if ( h <= 0 ) h = 1 ; + + glTexImage2D ( GL_TEXTURE_2D, + map_level, zsize, w, h, FALSE /* Border */, + (zsize==1)?GL_LUMINANCE: + (zsize==2)?GL_LUMINANCE_ALPHA: + (zsize==3)?GL_RGB: + GL_RGBA, + GL_UNSIGNED_BYTE, (GLvoid *) texels[i] ) ; + map_level++ ; + delete [] texels [ i ] ; + } +} + + + +int fntTexFont::loadTXF ( const SGPath& path, GLenum mag, GLenum min ) +{ + FILE *fd ; + const auto ps = path.utf8Str(); +#if defined(SG_WINDOWS) + std::wstring fp = path.wstr(); + fd = _wfopen(fp.c_str(), L"rb"); +#else + fd = fopen(ps.c_str(), "rb"); +#endif + if ( fd == nullptr ) + { + fntSetError ( SG_WARN, + "fntLoadTXF: Failed to open '%s' for reading.", ps.c_str() ) ; + return FNT_FALSE ; + } + + _fntCurrImageFd = fd ; + + unsigned char magic [ 4 ] ; + + if ( (int)fread ( &magic, sizeof (unsigned int), 1, fd ) != 1 ) + { + fntSetError ( SG_WARN, + "fntLoadTXF: '%s' an empty file!", ps.c_str() ) ; + return FNT_FALSE ; + } + + if ( magic [ 0 ] != 0xFF || magic [ 1 ] != 't' || + magic [ 2 ] != 'x' || magic [ 3 ] != 'f' ) + { + fntSetError ( SG_WARN, + "fntLoadTXF: '%s' is not a 'txf' font file.", ps.c_str() ) ; + return FNT_FALSE ; + } + + _fntIsSwapped = FALSE ; + int endianness = _fnt_readInt () ; + + _fntIsSwapped = ( endianness != 0x12345678 ) ; + + int format = _fnt_readInt () ; + int tex_width = _fnt_readInt () ; + int tex_height = _fnt_readInt () ; + int max_height = _fnt_readInt () ; + _fnt_readInt () ; + int num_glyphs = _fnt_readInt () ; + + int w = tex_width ; + int h = tex_height ; + + float xstep = 0.5f / (float) w ; + float ystep = 0.5f / (float) h ; + + int i, j ; + + /* + Load the TXF_Glyph array + */ + + TXF_Glyph glyph ; + + for ( i = 0 ; i < num_glyphs ; i++ ) + { + glyph . ch = _fnt_readShort () ; + + glyph . w = _fnt_readByte () ; + glyph . h = _fnt_readByte () ; + glyph . x_off = _fnt_readByte () ; + glyph . y_off = _fnt_readByte () ; + glyph . step = _fnt_readByte () ; + glyph . unknown = _fnt_readByte () ; + glyph . x = _fnt_readShort () ; + glyph . y = _fnt_readShort () ; + + setGlyph ( (char) glyph.ch, + (float) glyph.step / (float) max_height, + (float) glyph.x / (float) w + xstep, + (float)( glyph.x + glyph.w ) / (float) w + xstep, + (float) glyph.y / (float) h + ystep, + (float)( glyph.y + glyph.h ) / (float) h + ystep, + (float) glyph.x_off / (float) max_height, + (float)( glyph.x_off + glyph.w ) / (float) max_height, + (float) glyph.y_off / (float) max_height, + (float)( glyph.y_off + glyph.h ) / (float) max_height ) ; + } + + exists [ static_cast(' ') ] = FALSE ; + + /* + Load the image part of the file + */ + + int ntexels = w * h ; + + unsigned char *teximage ; + unsigned char *texbitmap ; + + switch ( format ) + { + case FNT_BYTE_FORMAT: + { + unsigned char *orig = new unsigned char [ ntexels ] ; + + if ( (int)fread ( orig, 1, ntexels, fd ) != ntexels ) + { + fntSetError ( SG_WARN, + "fntLoadTXF: Premature EOF in '%s'.", ps.c_str() ) ; + return FNT_FALSE ; + } + + teximage = new unsigned char [ 2 * ntexels ] ; + + for ( i = 0 ; i < ntexels ; i++ ) + { + teximage [ i*2 ] = orig [ i ] ; + teximage [ i*2 + 1 ] = orig [ i ] ; + } + + delete [] orig ; + } + break ; + + case FNT_BITMAP_FORMAT: + { + int stride = (w + 7) >> 3; + + texbitmap = new unsigned char [ stride * h ] ; + + if ( (int)fread ( texbitmap, 1, stride * h, fd ) != stride * h ) + { + delete [] texbitmap ; + fntSetError ( SG_WARN, + "fntLoadTXF: Premature EOF in '%s'.", ps.c_str() ) ; + return FNT_FALSE ; + } + + teximage = new unsigned char [ 2 * ntexels ] ; + + for ( i = 0 ; i < 2 * ntexels ; i++ ) + teximage [ i ] = 0 ; + + for (i = 0; i < h; i++) + for (j = 0; j < w; j++) + if (texbitmap[i * stride + (j >> 3)] & (1 << (j & 7))) + { + teximage[(i * w + j) * 2 ] = 255; + teximage[(i * w + j) * 2 + 1] = 255; + } + + delete [] texbitmap ; + } + break ; + + default: + fntSetError ( SG_WARN, + "fntLoadTXF: Unrecognised format type in '%s'.", ps.c_str() ) ; + return FNT_FALSE ; + } + + fclose ( fd ) ; + + fixed_pitch = FALSE ; + +#ifdef GL_VERSION_1_1 + glGenTextures ( 1, & texture ) ; + glBindTexture ( GL_TEXTURE_2D, texture ) ; +#else + /* This is only useful on some ancient SGI hardware */ + glGenTexturesEXT ( 1, & texture ) ; + glBindTextureEXT ( GL_TEXTURE_2D, texture ) ; +#endif + + tex_make_mip_maps ( teximage, w, h, 2 ) ; + + glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ) ; + + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag ) ; + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min ) ; + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ) ; + glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ) ; +#ifdef GL_VERSION_1_1 + glBindTexture ( GL_TEXTURE_2D, 0 ) ; +#else + glBindTextureEXT ( GL_TEXTURE_2D, 0 ) ; +#endif + + return FNT_TRUE ; +} + + diff --git a/3rdparty/hts_engine_API/lib/HTS_misc.c b/3rdparty/hts_engine_API/lib/HTS_misc.c old mode 100644 new mode 100755 index 290ef16e1..6a350fce2 --- a/3rdparty/hts_engine_API/lib/HTS_misc.c +++ b/3rdparty/hts_engine_API/lib/HTS_misc.c @@ -75,14 +75,51 @@ typedef struct _HTS_Data { size_t index; } HTS_Data; +#if defined(_WIN32) + +#include + +#define MAX_PATH_SIZE 8192 +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) + +// Encode 'path' which is assumed UTF-8 string, into UNICODE string. +// wbuf and wbuf_len is a target buffer and its length. +static void to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) { + char buf[MAX_PATH_SIZE * 2], buf2[MAX_PATH_SIZE * 2], *p; + + strncpy(buf, path, sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + + // Trim trailing slashes. Leave backslash for paths like "X:\" + p = buf + strlen(buf) - 1; + while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0'; + + // Convert to Unicode and back. If doubly-converted string does not + // match the original, something is fishy, reject. + memset(wbuf, 0, wbuf_len * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len); + WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2), + NULL, NULL); + if (strcmp(buf, buf2) != 0) { + wbuf[0] = L'\0'; + } +} +#endif + /* HTS_fopen_from_fn: wrapper for fopen */ HTS_File *HTS_fopen_from_fn(const char *name, const char *opt) { HTS_File *fp = (HTS_File *) HTS_calloc(1, sizeof(HTS_File)); fp->type = HTS_FILE; +#if defined(_WIN32) + wchar_t wpath[MAX_PATH_SIZE], wmode[10]; + to_wchar(name, wpath, ARRAY_SIZE(wpath)); + to_wchar(opt, wmode, ARRAY_SIZE(wmode)); + fp->pointer = (void*) _wfopen(wpath, wmode); +#else fp->pointer = (void *) fopen(name, opt); - +#endif if (fp->pointer == NULL) { HTS_error(0, "HTS_fopen: Cannot open %s.\n", name); HTS_free(fp); diff --git a/CMakeLists.txt b/CMakeLists.txt index b54b97379..3818b1110 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -370,10 +370,7 @@ if (ENABLE_QT) find_package(Qt5 5.4 COMPONENTS Widgets Network Qml Quick Svg) if (Qt5Widgets_FOUND) message(STATUS "Will enable Qt launcher GUI") - message(STATUS " Qt5Widgets version: ${Qt5Widgets_VERSION_STRING}") - message(STATUS " Qt5Widgets include dir: ${Qt5Widgets_INCLUDE_DIRS}") set(HAVE_QT 1) - include (Translations) else() # don't try to build FGQCanvas if Qt wasn't found correctly @@ -385,7 +382,7 @@ else() endif (ENABLE_QT) ############################################################################## -find_package(PLIB REQUIRED puaux pu fnt) +find_package(PLIB REQUIRED puaux pu) # FlightGear and SimGear versions need to match major + minor # split version string into components, note CMAKE_MATCH_0 is the entire regexp match diff --git a/CMakeModules/FindPLIB.cmake b/CMakeModules/FindPLIB.cmake index 62ee344f3..297718f2b 100644 --- a/CMakeModules/FindPLIB.cmake +++ b/CMakeModules/FindPLIB.cmake @@ -109,9 +109,9 @@ if(${PLIB_LIBRARIES} STREQUAL "PLIB_LIBRARIES-NOTFOUND") # handle MSVC confusion over pu/pui naming, by removing # 'pu' and then adding it back list(REMOVE_ITEM outDeps "pu" "fnt" "sg") - list(APPEND outDeps ${PUNAME} "fnt" "sg") + list(APPEND outDeps ${PUNAME} "sg") elseif (${c} STREQUAL "puaux") - list(APPEND outDeps ${PUNAME} "fnt" "sg") + list(APPEND outDeps ${PUNAME} "sg") elseif (${c} STREQUAL "ssg") list(APPEND outDeps "sg") endif() diff --git a/CMakeModules/SetupFGFSLibraries.cmake b/CMakeModules/SetupFGFSLibraries.cmake index f73df77dd..3eba04915 100644 --- a/CMakeModules/SetupFGFSLibraries.cmake +++ b/CMakeModules/SetupFGFSLibraries.cmake @@ -66,6 +66,8 @@ function(setup_fgfs_libraries target) target_link_libraries(${target} PLIBJoystick) endif() + target_link_libraries(${target} PLIBFont) + if(SYSTEM_HTS_ENGINE) target_link_libraries(${target} flite_hts ${HTS_ENGINE_LIBRARIES}) else() diff --git a/CMakeModules/Translations.cmake b/CMakeModules/Translations.cmake index 49e4c2c2d..9ca6b1307 100644 --- a/CMakeModules/Translations.cmake +++ b/CMakeModules/Translations.cmake @@ -19,9 +19,10 @@ if (${do_translate} AND NOT TARGET Qt5::lrelease) "\n(on Linux You may need to install an additional package containing the Qt5 translation tools)") endif() +# FIXME - determine this based on subdirs of TRANSLATIONS_SRC_DIR +set(LANGUAGES en_US de es nl fr it pl pt ru zh_CN) + if (${do_translate}) - # FIXME - determine this based on subdirs of TRANSLATIONS_SRC_DIR - set(LANGUAGES en_US de es nl fr it pl pt ru zh_CN) set(translation_res "${PROJECT_BINARY_DIR}/translations.qrc") add_custom_target(fgfs_qm_files ALL) @@ -55,3 +56,14 @@ if (${do_translate}) # set this so config.h can detect it set(HAVE_QRC_TRANSLATIONS TRUE) endif() # of do translate + +add_custom_target(ts) +foreach(lang ${LANGUAGES}) + add_custom_target( + ts_${lang} + COMMAND Qt5::lupdate ${CMAKE_SOURCE_DIR}/src/GUI + -locations relative -no-ui-lines -ts ${TRANSLATIONS_SRC_DIR}/${lang}/FlightGear-Qt.xlf + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) + add_dependencies(ts ts_${lang}) +endforeach() \ No newline at end of file diff --git a/scripts/python/TerraSync/terrasync/main.py b/scripts/python/TerraSync/terrasync/main.py index 8e4bcc5fb..82ffb7635 100755 --- a/scripts/python/TerraSync/terrasync/main.py +++ b/scripts/python/TerraSync/terrasync/main.py @@ -184,6 +184,7 @@ class DirIndex: def __init__(self, dirIndexFile): self.d = [] self.f = [] + self.t = [] self.version = 0 self.path = None # will be a VirtualPath instance when set @@ -221,6 +222,9 @@ class DirIndex: elif tokens[0] == "f": self.f.append({ 'name': tokens[1], 'hash': tokens[2], 'size': tokens[3] }) + elif tokens[0] == "t": + self.t.append({ 'name': tokens[1], 'hash': tokens[2], 'size': tokens[3] }) + def _sanityCheck(self): if self.path is None: assert self._rawContents is not None @@ -239,6 +243,9 @@ class DirIndex: def getDirectories(self): return self.d + def getTarballs(self): + return self.t + def getFiles(self): return self.f @@ -639,6 +646,14 @@ class TerraSync: subdir['hash']) serverDirs.append(d) + for tarball in dirIndex.getTarballs(): + # Tarballs are handled the same as normal files. + f = tarball['name'] + self.processFileEntry(virtualBase / f, + join(relativeBase, f), + tarball['hash']) + serverFiles.append(f) + localFullPath = join(self.target, relativeBase) localFiles = [ f for f in listdir(localFullPath) if isfile(join(localFullPath, f)) ] diff --git a/src/AIModel/AIBase.cxx b/src/AIModel/AIBase.cxx index 9271f2fc9..140a872a8 100644 --- a/src/AIModel/AIBase.cxx +++ b/src/AIModel/AIBase.cxx @@ -516,7 +516,7 @@ void FGAIBase::Transform() { */ std::vector FGAIBase::resolveModelPath(ModelSearchOrder searchOrder) { - std::vector path_list; + string_list path_list; if (searchOrder == DATA_ONLY) { SG_LOG(SG_AI, SG_DEBUG, "Resolving model path: DATA only"); @@ -536,7 +536,8 @@ std::vector FGAIBase::resolveModelPath(ModelSearchOrder searchOrder } } else { // No model, so fall back to the default - path_list.push_back(fgGetString("/sim/multiplay/default-model", default_model)); + const SGPath defaultModelPath = SGPath::fromUtf8(fgGetString("/sim/multiplay/default-model", default_model)); + path_list.push_back(defaultModelPath.utf8Str()); } } else { SG_LOG(SG_AI, SG_DEBUG, "Resolving model path: PREFER_AI/PREFER_DATA"); @@ -544,8 +545,8 @@ std::vector FGAIBase::resolveModelPath(ModelSearchOrder searchOrder for (SGPath p : globals->get_data_paths("AI")) { p.append(model_path); if (p.exists()) { - SG_LOG(SG_AI, SG_DEBUG, "Found AI model: " << p.local8BitStr()); - path_list.push_back(p.local8BitStr()); + SG_LOG(SG_AI, SG_DEBUG, "Found AI model: " << p); + path_list.push_back(p.utf8Str()); break; } } @@ -566,8 +567,8 @@ std::vector FGAIBase::resolveModelPath(ModelSearchOrder searchOrder for (SGPath p : globals->get_data_paths()) { p.append(fallback_path); if (p.exists()) { - SG_LOG(SG_AI, SG_DEBUG, "Found fallback model path for index " << _fallback_model_index << ": " << p.local8BitStr()); - path_list.push_back(p.local8BitStr()); + SG_LOG(SG_AI, SG_DEBUG, "Found fallback model path for index " << _fallback_model_index << ": " << p); + path_list.push_back(p.utf8Str()); break; } } diff --git a/src/AIModel/AICarrier.cxx b/src/AIModel/AICarrier.cxx index 13422c61a..617b532f0 100644 --- a/src/AIModel/AICarrier.cxx +++ b/src/AIModel/AICarrier.cxx @@ -17,9 +17,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#ifdef HAVE_CONFIG_H -# include -#endif +#include #include #include @@ -35,11 +33,13 @@ #include "AICarrier.hxx" -FGAICarrier::FGAICarrier() : FGAIShip(otCarrier), deck_altitude(65.0065) { +FGAICarrier::FGAICarrier() : + FGAIShip(otCarrier), + deck_altitude(65.0065) +{ } -FGAICarrier::~FGAICarrier() { -} +FGAICarrier::~FGAICarrier() = default; void FGAICarrier::readFromScenario(SGPropertyNode* scFileNode) { if (!scFileNode) @@ -67,11 +67,14 @@ void FGAICarrier::readFromScenario(SGPropertyNode* scFileNode) { // Transform to the right coordinate frame, configuration is done in // the usual x-back, y-right, z-up coordinates, computations // in the simulation usual body x-forward, y-right, z-down coordinates - flols_off(0) = - flols->getDoubleValue("x-offset-m", 0); - flols_off(1) = flols->getDoubleValue("y-offset-m", 0); - flols_off(2) = - flols->getDoubleValue("z-offset-m", 0); + _flolsPosOffset(0) = - flols->getDoubleValue("x-offset-m", 0); + _flolsPosOffset(1) = flols->getDoubleValue("y-offset-m", 0); + _flolsPosOffset(2) = - flols->getDoubleValue("z-offset-m", 0); + + _flolsHeadingOffsetDeg = flols->getDoubleValue("heading-offset-deg", 0.0); + _flolsApproachAngle = flols->getDoubleValue("glidepath-angle-deg", 3.0); } else - flols_off = SGVec3d::zeros(); + _flolsPosOffset = SGVec3d::zeros(); std::vector props = scFileNode->getChildren("parking-pos"); std::vector::const_iterator it; @@ -180,13 +183,13 @@ void FGAICarrier::update(double dt) { // rotate the eyepoint wrt carrier vector into the carriers frame eyeWrtCarrier = ec2body.transform(eyeWrtCarrier); // the eyepoints vector wrt the flols position - SGVec3d eyeWrtFlols = eyeWrtCarrier - flols_off; + SGVec3d eyeWrtFlols = eyeWrtCarrier - _flolsPosOffset; // the distance from the eyepoint to the flols dist = norm(eyeWrtFlols); // now the angle, positive angles are upwards - if (fabs(dist) < SGLimits::min()) { + if (fabs(dist) < SGLimits::min()) { angle = 0; } else { double sAngle = -eyeWrtFlols(2)/dist; @@ -327,11 +330,9 @@ bool FGAICarrier::getParkPosition(const string& id, SGGeod& geodPos, { // FIXME: does not yet cover rotation speeds. - list::iterator it = ppositions.begin(); - while (it != ppositions.end()) { + for (const auto& ppos : ppositions) { // Take either the specified one or the first one ... - if ((*it).name == id || id.empty()) { - ParkPosition ppos = *it; + if (ppos.name == id || id.empty()) { SGVec3d cartPos = getCartPosAt(ppos.offset); geodPos = SGGeod::fromCart(cartPos); hdng = hdg + ppos.heading_deg; @@ -341,12 +342,28 @@ bool FGAICarrier::getParkPosition(const string& id, SGGeod& geodPos, uvw = SGVec3d(chdng*speed_fps, shdng*speed_fps, 0); return true; } - ++it; } return false; } +bool FGAICarrier::getFLOLSPositionHeading(SGGeod& geodPos, double& heading) const +{ + SGVec3d cartPos = getCartPosAt(_flolsPosOffset); + geodPos = SGGeod::fromCart(cartPos); + + // at present we don't support a heading offset for the FLOLS, so + // heading is just the carrier heading + heading = hdg + _flolsHeadingOffsetDeg; + + return true; +} + +double FGAICarrier::getFLOLFSGlidepathAngleDeg() const +{ + return _flolsApproachAngle; +} + // find relative wind void FGAICarrier::UpdateWind( double dt) { @@ -371,8 +388,7 @@ void FGAICarrier::UpdateWind( double dt) { + (rel_wind_speed_from_north_kts * rel_wind_speed_from_north_kts)); //calculate the relative wind direction - rel_wind_from_deg = atan2(rel_wind_speed_from_east_kts, rel_wind_speed_from_north_kts) - * SG_RADIANS_TO_DEGREES; + rel_wind_from_deg = SGMiscd::rad2deg(atan2(rel_wind_speed_from_east_kts, rel_wind_speed_from_north_kts)); //calculate rel wind rel_wind = rel_wind_from_deg - hdg; @@ -640,7 +656,7 @@ SGSharedPtr FGAICarrier::findCarrierByNameOrPennant(const std::stri return {}; } - for (const auto aiObject : aiManager->get_ai_list()) { + for (const auto& aiObject : aiManager->get_ai_list()) { if (aiObject->isa(FGAIBase::otCarrier)) { SGSharedPtr c = static_cast(aiObject.get()); if ((c->sign == namePennant) || (c->_getName() == namePennant)) { @@ -652,7 +668,7 @@ SGSharedPtr FGAICarrier::findCarrierByNameOrPennant(const std::stri return {}; } -void FGAICarrier::extractNamesPennantsFromScenario(SGPropertyNode_ptr xmlNode, SGPropertyNode_ptr scenario) +void FGAICarrier::extractCarriersFromScenario(SGPropertyNode_ptr xmlNode, SGPropertyNode_ptr scenario) { for (auto c : xmlNode->getChildren("entry")) { if (c->getStringValue("type") != std::string("carrier")) @@ -673,6 +689,14 @@ void FGAICarrier::extractNamesPennantsFromScenario(SGPropertyNode_ptr xmlNode, S // the find code above just looks for anything called a name (so alias // are possible, for example) if (!name.empty()) carrierNode->addChild("name")->setStringValue(name); - if (!pennant.empty()) carrierNode->addChild("name")->setStringValue(pennant); + if (!pennant.empty()) { + carrierNode->addChild("name")->setStringValue(pennant); + carrierNode->addChild("pennant-number")->setStringValue(pennant); + } + + // extact parkings + for (auto p : c->getChildren("parking-pos")) { + carrierNode->addChild("parking-pos")->setStringValue(p->getStringValue("name")); + } } } diff --git a/src/AIModel/AICarrier.hxx b/src/AIModel/AICarrier.hxx index ad5ce1fe8..e07d82480 100644 --- a/src/AIModel/AICarrier.hxx +++ b/src/AIModel/AICarrier.hxx @@ -86,8 +86,11 @@ public: * This is used to support 'start on a carrier', since we can quickly find * the corresponding scenario file to be loaded. */ - static void extractNamesPennantsFromScenario(SGPropertyNode_ptr xmlNode, SGPropertyNode_ptr scenario); + static void extractCarriersFromScenario(SGPropertyNode_ptr xmlNode, SGPropertyNode_ptr scenario); + bool getFLOLSPositionHeading(SGGeod &pos, double &heading) const; + + double getFLOLFSGlidepathAngleDeg() const; private: /// Is sufficient to be private, stores a possible position to place an /// aircraft on start @@ -115,8 +118,10 @@ private: list ppositions; // List of positions where an aircraft can start. string sign; // The sign of this carrier. - // these describe the flols - SGVec3d flols_off; + // these describe the flols + SGVec3d _flolsPosOffset; + double _flolsHeadingOffsetDeg = 0.0; ///< angle in degrees offset from the carrier centerline + double _flolsApproachAngle = 3.0; ///< glidepath angle for the FLOLS double dist; // the distance of the eyepoint from the flols double angle; diff --git a/src/AIModel/AIManager.cxx b/src/AIModel/AIManager.cxx index 6a292c7c9..4625572ff 100644 --- a/src/AIModel/AIManager.cxx +++ b/src/AIModel/AIManager.cxx @@ -71,11 +71,11 @@ public: _unloadScript = nasalScripts->getStringValue("unload"); std::string loadScript = nasalScripts->getStringValue("load"); if (!loadScript.empty()) { - FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal"); + FGNasalSys* nasalSys = globals->get_subsystem(); std::string moduleName = "scenario_" + _internalName; nasalSys->createModule(moduleName.c_str(), moduleName.c_str(), loadScript.c_str(), loadScript.size(), - 0); + nullptr); } } @@ -85,7 +85,7 @@ public: [](FGAIBasePtr ai) { ai->setDie(true); }); - FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal"); + FGNasalSys* nasalSys = globals->get_subsystem(); if (!nasalSys) return; @@ -93,7 +93,7 @@ public: if (!_unloadScript.empty()) { nasalSys->createModule(moduleName.c_str(), moduleName.c_str(), _unloadScript.c_str(), _unloadScript.size(), - 0); + nullptr); } nasalSys->deleteModule(moduleName.c_str()); @@ -165,16 +165,19 @@ FGAIManager::init() { registerScenarios(); } -void FGAIManager::registerScenarios() +void FGAIManager::registerScenarios(SGPropertyNode_ptr root) { - // depending on if we're using a carrier startup, this function may get - // called early or during normal FGAIManager init, so guard against double - // invocation. - // we clear this flag on shudtdown so reset works as expected - if (static_haveRegisteredScenarios) - return; - - static_haveRegisteredScenarios = true; + if (!root) { + // depending on if we're using a carrier startup, this function may get + // called early or during normal FGAIManager init, so guard against double + // invocation. + // we clear this flag on shudtdown so reset works as expected + if (static_haveRegisteredScenarios) + return; + + static_haveRegisteredScenarios = true; + root = globals->get_props(); + } // find all scenarios at standard locations (for driving the GUI) std::vector scenarioSearchPaths; @@ -184,27 +187,29 @@ void FGAIManager::registerScenarios() // add-on scenario directories const auto& addonsManager = flightgear::addons::AddonManager::instance(); - for (auto a : addonsManager->registeredAddons()) { - scenarioSearchPaths.push_back(a->getBasePath() / "Scenarios"); + if (addonsManager) { + for (auto a : addonsManager->registeredAddons()) { + scenarioSearchPaths.push_back(a->getBasePath() / "Scenarios"); + } } - - SGPropertyNode_ptr scenariosNode = fgGetNode("/sim/ai/scenarios", true); + + SGPropertyNode_ptr scenariosNode = root->getNode("/sim/ai/scenarios", true); for (auto p : scenarioSearchPaths) { if (!p.exists()) continue; simgear::Dir dir(p); for (auto xmlPath : dir.children(simgear::Dir::TYPE_FILE, ".xml")) { - registerScenarioFile(xmlPath); + registerScenarioFile(root, xmlPath); } // of xml files in the scenario dir iteration } // of scenario dirs iteration } -SGPropertyNode_ptr FGAIManager::registerScenarioFile(const SGPath& xmlPath) +SGPropertyNode_ptr FGAIManager::registerScenarioFile(SGPropertyNode_ptr root, const SGPath& xmlPath) { if (!xmlPath.exists()) return {}; - auto scenariosNode = fgGetNode("/sim/ai/scenarios", true); + auto scenariosNode = root->getNode("/sim/ai/scenarios", true); SGPropertyNode_ptr sNode; try { @@ -235,7 +240,7 @@ SGPropertyNode_ptr FGAIManager::registerScenarioFile(const SGPath& xmlPath) sNode->setStringValue("description", xs->getStringValue("description")); } - FGAICarrier::extractNamesPennantsFromScenario(xs, sNode); + FGAICarrier::extractCarriersFromScenario(xs, sNode); } // of scenarios in the XML file } catch (std::exception&) { SG_LOG(SG_AI, SG_WARN, "Skipping malformed scenario file:" << xmlPath); @@ -367,7 +372,7 @@ FGAIManager::update(double dt) for (FGAIBase* base : ai_list) { try { if (base->isa(FGAIBase::otThermal)) { - processThermal(dt, (FGAIThermal*)base); + processThermal(dt, static_cast(base)); } else { base->update(dt); } @@ -428,7 +433,7 @@ bool FGAIManager::isVisible(const SGGeod& pos) const int FGAIManager::getNumAiObjects() const { - return ai_list.size(); + return static_cast(ai_list.size()); } void @@ -489,12 +494,14 @@ bool FGAIManager::loadScenarioCommand(const SGPropertyNode* args, SGPropertyNode bool FGAIManager::unloadScenarioCommand(const SGPropertyNode * arg, SGPropertyNode * root) { + SG_UNUSED(root); std::string name = arg->getStringValue("name"); return unloadScenario(name); } bool FGAIManager::addObjectCommand(const SGPropertyNode* arg, const SGPropertyNode* root) { + SG_UNUSED(root); if (!arg){ return false; } @@ -506,7 +513,7 @@ FGAIBasePtr FGAIManager::addObject(const SGPropertyNode* definition) { const std::string& type = definition->getStringValue("type", "aircraft"); - FGAIBase* ai = NULL; + FGAIBase* ai = nullptr; if (type == "tanker") { // refueling scenarios ai = new FGAITanker; } else if (type == "wingman") { @@ -545,6 +552,7 @@ FGAIBasePtr FGAIManager::addObject(const SGPropertyNode* definition) bool FGAIManager::removeObjectCommand(const SGPropertyNode* arg, const SGPropertyNode* root) { + SG_UNUSED(root); if (!arg) { return false; } @@ -703,7 +711,7 @@ FGAIManager::calcCollision(double alt, double lat, double lon, double fuse_range } ++ai_list_itr; } - return 0; + return nullptr; } double diff --git a/src/AIModel/AIManager.hxx b/src/AIModel/AIManager.hxx index d386d862f..9ee51634d 100644 --- a/src/AIModel/AIManager.hxx +++ b/src/AIModel/AIManager.hxx @@ -73,8 +73,8 @@ public: * we need carrier scenarios to start the position-init process for a * carrier start. */ - static void registerScenarios(); - static SGPropertyNode_ptr registerScenarioFile(const SGPath& p); + static void registerScenarios(SGPropertyNode_ptr root = {}); + static SGPropertyNode_ptr registerScenarioFile(SGPropertyNode_ptr root, const SGPath& p); static SGPropertyNode_ptr loadScenarioFile(const std::string& id); FGAIBasePtr addObject(const SGPropertyNode* definition); diff --git a/src/AIModel/AIMultiplayer.cxx b/src/AIModel/AIMultiplayer.cxx index be6985fc4..7995b34c6 100644 --- a/src/AIModel/AIMultiplayer.cxx +++ b/src/AIModel/AIMultiplayer.cxx @@ -50,6 +50,12 @@ FGAIMultiplayer::FGAIMultiplayer() : lastUpdateTime = 0; playerLag = 0.03; compensateLag = 1; + realTime = false; + lastTime=0.0; + lagPpsAveraged = 1.0; + rawLag = 0.0; + rawLagMod = 0.0; + lagModAveraged = 0.0; _searchOrder = PREFER_DATA; } @@ -152,73 +158,96 @@ void FGAIMultiplayer::update(double dt) // requested time to the most recent available packet. This is the // target we want to reach in average. double lag = it->second.lag; + + rawLag = curentPkgTime - curtime; + realTime = false; //default behaviour + if (!mTimeOffsetSet) { mTimeOffsetSet = true; mTimeOffset = curentPkgTime - curtime - lag; + lastTime = curentPkgTime; + lagModAveraged = remainder((curentPkgTime - curtime), 3600.0); + props->setDoubleValue("lag/pps-averaged", lagPpsAveraged); + props->setDoubleValue("lag/lag-mod-averaged", lagModAveraged); } else { - double offset = 0.0; + if ((curentPkgTime - lastTime) != 0) { + lagPpsAveraged = 0.99 * lagPpsAveraged + 0.01 * fabs( 1 / (lastTime - curentPkgTime)); + lastTime = curentPkgTime; + rawLagMod = remainder(rawLag, 3600.0); + lagModAveraged = lagModAveraged *0.99 + 0.01 * rawLagMod; + props->setDoubleValue("lag/pps-averaged", lagPpsAveraged); + props->setDoubleValue("lag/lag-mod-averaged", lagModAveraged); + } - //spectator mode, more late to be in the interpolation zone + double offset = 0.0; + + //spectator mode, more late to be in the interpolation zone if (compensateLag == 3) { offset = curentPkgTime -curtime -lag + playerLag; - // old behaviour + // old behaviour } else if (compensateLag == 1) { offset = curentPkgTime - curtime - lag; // using the prediction mode to display the mpaircraft in the futur/past with given playerlag value //currently compensatelag = 2 + } else if (fabs(lagModAveraged) < 0.3) { + mTimeOffset = (round(rawLag/3600))*3600; //real time mode if close enough + realTime = true; + } else { offset = curentPkgTime - curtime + 0.48*lag + playerLag; } - if ((!mAllowExtrapolation && offset + lag < mTimeOffset) - || (offset - 10 > mTimeOffset)) { - mTimeOffset = offset; - SG_LOG(SG_AI, SG_DEBUG, "Resetting time offset adjust system to " - "avoid extrapolation: time offset = " << mTimeOffset); - } else { - // the error of the offset, respectively the negative error to avoid - // a minus later ... - double err = offset - mTimeOffset; - // limit errors leading to shorter lag values somehow, that is late - // arriving packets will pessimize the overall lag much more than - // early packets will shorten the overall lag - double sysSpeed; - //trying to slow the rudderlag phenomenon thus using more the prediction system - //if we are off by less than 1.5s, do a little correction, and bigger step above 1.5s - if (fabs(err) < 1.5) { - if (err < 0) { - sysSpeed = mLagAdjustSystemSpeed*err*0.01; - } else { - sysSpeed = SGMiscd::min(0.5*err*err, 0.05); - } - } else { - if (err < 0) { + if (!realTime) { - // Ok, we have some very late packets and nothing newer increase the - // lag by the given speedadjust - sysSpeed = mLagAdjustSystemSpeed*err; - } else { - // We have a too pessimistic display delay shorten that a small bit - sysSpeed = SGMiscd::min(0.1*err*err, 0.5); - } - } + if ((!mAllowExtrapolation && offset + lag < mTimeOffset) + || (offset - 10 > mTimeOffset)) { + mTimeOffset = offset; + SG_LOG(SG_AI, SG_DEBUG, "Resetting time offset adjust system to " + "avoid extrapolation: time offset = " << mTimeOffset); + } else { + // the error of the offset, respectively the negative error to avoid + // a minus later ... + double err = offset - mTimeOffset; + // limit errors leading to shorter lag values somehow, that is late + // arriving packets will pessimize the overall lag much more than + // early packets will shorten the overall lag + double sysSpeed; + //trying to slow the rudderlag phenomenon thus using more the prediction system + //if we are off by less than 1.5s, do a little correction, and bigger step above 1.5s + if (fabs(err) < 1.5) { + if (err < 0) { + sysSpeed = mLagAdjustSystemSpeed*err*0.01; + } else { + sysSpeed = SGMiscd::min(0.5*err*err, 0.05); + } + } else { + if (err < 0) { + + // Ok, we have some very late packets and nothing newer increase the + // lag by the given speedadjust + sysSpeed = mLagAdjustSystemSpeed*err; + } else { + // We have a too pessimistic display delay shorten that a small bit + sysSpeed = SGMiscd::min(0.1*err*err, 0.5); + } + } // simple euler integration for that first order system including some // overshooting guard to prevent to aggressive system speeds // (stiff systems) to explode the systems state - double systemIncrement = dt*sysSpeed; - if (fabs(err) < fabs(systemIncrement)) - systemIncrement = err; - mTimeOffset += systemIncrement; + double systemIncrement = dt*sysSpeed; + if (fabs(err) < fabs(systemIncrement)) + systemIncrement = err; + mTimeOffset += systemIncrement; - SG_LOG(SG_AI, SG_DEBUG, "Offset adjust system: time offset = " + SG_LOG(SG_AI, SG_DEBUG, "Offset adjust system: time offset = " << mTimeOffset << ", expected longitudinal position error due to " " current adjustment of the offset: " << fabs(norm(it->second.linearVel)*systemIncrement)); + } } } - // Compute the time in the feeders time scale which fits the current time // we need to double tInterp = curtime + mTimeOffset; @@ -227,9 +256,10 @@ void FGAIMultiplayer::update(double dt) SGQuatf ecOrient; SGVec3f ecLinearVel; - if (tInterp <= curentPkgTime) { + if (tInterp < curentPkgTime) { // Ok, we need a time prevous to the last available packet, // that is good ... + // the case tInterp = curentPkgTime need to be in the interpolation, to avoid a bug zeroing the position // Find the first packet before the target time MotionInfo::iterator nextIt = mMotionInfo.upper_bound(tInterp); diff --git a/src/AIModel/AIMultiplayer.hxx b/src/AIModel/AIMultiplayer.hxx index fdfaed525..0f44b4c56 100644 --- a/src/AIModel/AIMultiplayer.hxx +++ b/src/AIModel/AIMultiplayer.hxx @@ -86,11 +86,15 @@ private: typedef std::map > PropertyMap; PropertyMap mPropertyMap; - double mTimeOffset; bool mTimeOffsetSet; - double playerLag; + bool realTime; int compensateLag; + double playerLag; + double mTimeOffset; double lastUpdateTime; + double lastTime; + double lagPpsAveraged; + double rawLag, rawLagMod, lagModAveraged; /// Properties which are for now exposed for testing bool mAllowExtrapolation; diff --git a/src/AIModel/AIShip.cxx b/src/AIModel/AIShip.cxx index ab91ddc2b..0c3e76f25 100644 --- a/src/AIModel/AIShip.cxx +++ b/src/AIModel/AIShip.cxx @@ -287,6 +287,8 @@ void FGAIShip::Run(double dt) { if (speed_diff < 0.0) speed -= _speed_constant * dt; + } else { + speed = tgt_speed; } // do not allow unreasonable speeds diff --git a/src/ATC/atcdialog.cxx b/src/ATC/atcdialog.cxx index b98893f28..ef378295f 100644 --- a/src/ATC/atcdialog.cxx +++ b/src/ATC/atcdialog.cxx @@ -162,6 +162,8 @@ void FGATCDialogNew::frequencyDisplay(const std::string& ident) buf[7] = '\0'; entry->setStringValue("text[1]/label", buf); + entry->setStringValue("button[0]/binding/value", buf); + entry->setStringValue("button[1]/binding/value", buf); } _gui->showDialog(dialog_name); diff --git a/src/Aircraft/replay.cxx b/src/Aircraft/replay.cxx index 626001e6a..504929b9b 100644 --- a/src/Aircraft/replay.cxx +++ b/src/Aircraft/replay.cxx @@ -139,12 +139,13 @@ FGReplay::clear() void FGReplay::init() { - disable_replay = fgGetNode("/sim/replay/disable", true); - replay_master = fgGetNode("/sim/replay/replay-state", true); - replay_time = fgGetNode("/sim/replay/time", true); - replay_time_str = fgGetNode("/sim/replay/time-str", true); - replay_looped = fgGetNode("/sim/replay/looped", true); - speed_up = fgGetNode("/sim/speed-up", true); + disable_replay = fgGetNode("/sim/replay/disable", true); + replay_master = fgGetNode("/sim/replay/replay-state", true); + replay_time = fgGetNode("/sim/replay/time", true); + replay_time_str = fgGetNode("/sim/replay/time-str", true); + replay_looped = fgGetNode("/sim/replay/looped", true); + replay_duration_act = fgGetNode("/sim/replay/duration-act", true); + speed_up = fgGetNode("/sim/speed-up", true); // alias to keep backward compatibility fgGetNode("/sim/freeze/replay-state", true)->alias(replay_master); @@ -431,8 +432,8 @@ FGReplay::update( double dt ) fgSetDouble( "/sim/replay/start-time", startTime ); fgSetDouble( "/sim/replay/end-time", endTime ); double duration = 0; - if (replay_looped->getBoolValue()) - fgGetDouble("/sim/replay/duration"); + if (replay_duration_act->getBoolValue()) + duration = fgGetDouble("/sim/replay/duration"); if( duration && (duration < (endTime - startTime)) ) { current_time = endTime - duration; } else { @@ -822,7 +823,7 @@ FGReplay::saveTape(const SGPath& Filename, SGPropertyNode* MetaDataProps) bool ok = true; /* open output stream *******************************************/ - gzContainerWriter output(Filename.local8BitStr(), FlightRecorderFileMagic); + gzContainerWriter output(Filename, FlightRecorderFileMagic); if (!output.good()) { SG_LOG(SG_SYSTEMS, SG_ALERT, "Cannot open file" << Filename); @@ -940,7 +941,7 @@ FGReplay::loadTape(const SGPath& Filename, bool Preview, SGPropertyNode* UserDat bool ok = true; /* open input stream ********************************************/ - gzContainerReader input(Filename.local8BitStr(), FlightRecorderFileMagic); + gzContainerReader input(Filename, FlightRecorderFileMagic); if (input.eof() || !input.good()) { SG_LOG(SG_SYSTEMS, SG_ALERT, "Cannot open file " << Filename); diff --git a/src/Aircraft/replay.hxx b/src/Aircraft/replay.hxx index b0315cd65..7377ef7ab 100644 --- a/src/Aircraft/replay.hxx +++ b/src/Aircraft/replay.hxx @@ -118,6 +118,7 @@ private: SGPropertyNode_ptr replay_time; SGPropertyNode_ptr replay_time_str; SGPropertyNode_ptr replay_looped; + SGPropertyNode_ptr replay_duration_act; SGPropertyNode_ptr speed_up; double m_high_res_time; // default: 60 secs of high res data diff --git a/src/Airports/runwayprefs.cxx b/src/Airports/runwayprefs.cxx index 3353820a7..76fec534b 100644 --- a/src/Airports/runwayprefs.cxx +++ b/src/Airports/runwayprefs.cxx @@ -148,15 +148,10 @@ void RunwayList::set(const std::string & tp, const std::string & lst) // timeOffsetInDays = weekday - currTimeDate->getGmt()->tm_wday; // timeCopy = timeCopy.substr(2,timeCopy.length()); type = tp; - - - - BOOST_FOREACH(std::string s, strutils::split(lst, ",")) { - std::string ident = strutils::strip(s); - + for (const auto s : strutils::split(lst, ",")) { + auto ident = strutils::strip(s); // http://code.google.com/p/flightgear-bugs/issues/detail?id=1137 if ((ident.size() < 2) || !isdigit(ident[1])) { - SG_LOG(SG_GENERAL, SG_INFO, "RunwayList::set: padding runway ident '" << ident << "'"); ident = "0" + ident; } diff --git a/src/Canvas/FGCanvasSystemAdapter.cxx b/src/Canvas/FGCanvasSystemAdapter.cxx index 7a2fb7fe1..7743b020e 100644 --- a/src/Canvas/FGCanvasSystemAdapter.cxx +++ b/src/Canvas/FGCanvasSystemAdapter.cxx @@ -52,7 +52,7 @@ namespace canvas "canvas::Text: using font file " << path ); - simgear::canvas::FontPtr font = osgText::readFontFile(path.local8BitStr()); + simgear::canvas::FontPtr font = osgText::readFontFile(path.utf8Str()); if( !font ) SG_LOG ( @@ -86,9 +86,9 @@ namespace canvas SGPath valid_path = fgValidatePath(p, false); if( !valid_path.isNull() ) #if OSG_VERSION_LESS_THAN(3,4,0) - return osgDB::readRefImageFile(valid_path.local8BitStr()); + return osgDB::readRefImageFile(valid_path.utf8Str()); #else - return osgDB::readRefImageFile(valid_path.local8BitStr()); + return osgDB::readRefImageFile(valid_path.utf8Str()); #endif SG_LOG(SG_IO, SG_ALERT, "canvas::Image: reading '" << path << "' denied"); @@ -98,9 +98,9 @@ namespace canvas SGPath tpath = globals->resolve_resource_path(path); if( !tpath.isNull() ) #if OSG_VERSION_LESS_THAN(3,4,0) - return osgDB::readImageFile(tpath.local8BitStr()); + return osgDB::readImageFile(tpath.utf8Str()); #else - return osgDB::readRefImageFile(tpath.local8BitStr()); + return osgDB::readRefImageFile(tpath.utf8Str()); #endif SG_LOG(SG_IO, SG_ALERT, "canvas::Image: No such image: '" << path << "'"); @@ -127,7 +127,7 @@ namespace canvas SG_LOG( SG_IO, SG_ALERT, "FGCanvasSystemAdapter: Failed to get HTTP subsystem" ); - return 0; + return nullptr; } } diff --git a/src/Canvas/canvas_mgr.cxx b/src/Canvas/canvas_mgr.cxx index 17ff96cbc..f652d6b05 100644 --- a/src/Canvas/canvas_mgr.cxx +++ b/src/Canvas/canvas_mgr.cxx @@ -71,21 +71,23 @@ CanvasMgr::CanvasMgr(): void CanvasMgr::init() { _gui_camera = flightgear::getGUICamera(flightgear::CameraGroup::getDefault()); - assert(_gui_camera.valid()); - - sc::Canvas::addPlacementFactory - ( - "object", - boost::bind - ( - &FGODGauge::set_aircraft_texture, - _1, - boost::bind(&sc::Canvas::getTexture, _2), - boost::bind(&sc::Canvas::getCullCallback, _2), - _2 - ) - ); - sc::Canvas::addPlacementFactory("scenery-object", &addSceneObjectPlacement); + if (_gui_camera.valid()) { + // add our two placement factories + sc::Canvas::addPlacementFactory + ( + "object", + boost::bind + ( + &FGODGauge::set_aircraft_texture, + _1, + boost::bind(&sc::Canvas::getTexture, _2), + boost::bind(&sc::Canvas::getCullCallback, _2), + _2 + ) + ); + + sc::Canvas::addPlacementFactory("scenery-object", &addSceneObjectPlacement); + } simgear::canvas::CanvasMgr::init(); } @@ -98,7 +100,7 @@ void CanvasMgr::shutdown() sc::Canvas::removePlacementFactory("object"); sc::Canvas::removePlacementFactory("scenery-object"); - _gui_camera = 0; + _gui_camera = 0; } //------------------------------------------------------------------------------ diff --git a/src/Cockpit/NavDisplay.cxx b/src/Cockpit/NavDisplay.cxx index bfabb9065..9885d81ff 100644 --- a/src/Cockpit/NavDisplay.cxx +++ b/src/Cockpit/NavDisplay.cxx @@ -777,7 +777,7 @@ NavDisplay::updateFont() } osg::ref_ptr fontOptions = new osgDB::ReaderWriter::Options("monochrome"); - osg::ref_ptr font = osgText::readFontFile(tpath.local8BitStr(), fontOptions.get()); + osg::ref_ptr font = osgText::readFontFile(tpath.utf8Str(), fontOptions.get()); if (font != 0) { _font = font; diff --git a/src/Cockpit/panel.cxx b/src/Cockpit/panel.cxx index 18b8fe78b..9cad531a4 100644 --- a/src/Cockpit/panel.cxx +++ b/src/Cockpit/panel.cxx @@ -47,7 +47,7 @@ #include -#include +#include "fnt.h" #include #include diff --git a/src/Cockpit/wxradar.cxx b/src/Cockpit/wxradar.cxx index 1165d93de..61a1b49bd 100644 --- a/src/Cockpit/wxradar.cxx +++ b/src/Cockpit/wxradar.cxx @@ -1084,7 +1084,7 @@ wxRadarBg::updateFont() } osg::ref_ptr fontOptions = new osgDB::ReaderWriter::Options("monochrome"); - osg::ref_ptr font = osgText::readFontFile(tpath.local8BitStr(), fontOptions.get()); + osg::ref_ptr font = osgText::readFontFile(tpath.utf8Str(), fontOptions.get()); if (font != 0) { _font = font; diff --git a/src/EmbeddedResources/CMakeLists.txt b/src/EmbeddedResources/CMakeLists.txt index d72d9629b..d49df99bf 100644 --- a/src/EmbeddedResources/CMakeLists.txt +++ b/src/EmbeddedResources/CMakeLists.txt @@ -1,6 +1,17 @@ add_executable(fgrcc fgrcc.cxx fgrcc.hxx) target_link_libraries(fgrcc SimGearCore ${PLATFORM_LIBS}) +# On Windows, make sure fgrcc can be run (it needs third-party libraries) in add_custom_target +if(MSVC) + set_target_properties(fgrcc PROPERTIES DEBUG_POSTFIX d) + if(MSVC_3RDPARTY_ROOT AND MSVC_3RDPARTY_DIR) + set(CMAKE_MSVCIDE_RUN_PATH ${MSVC_3RDPARTY_ROOT}/${MSVC_3RDPARTY_DIR}/bin) + else() + message(FATAL_ERROR + "Either MSVC_3RDPARTY_ROOT or MSVC_3RDPARTY_DIR is empty or unset") + endif() +endif() + add_custom_target( embeddedresources COMMAND diff --git a/src/Environment/ephemeris.cxx b/src/Environment/ephemeris.cxx index db9d59b1e..046e50084 100644 --- a/src/Environment/ephemeris.cxx +++ b/src/Environment/ephemeris.cxx @@ -54,7 +54,7 @@ void Ephemeris::init() { SGPath ephem_data_path(globals->get_fg_root()); ephem_data_path.append("Astro"); - _impl.reset(new SGEphemeris(ephem_data_path.local8BitStr())); + _impl.reset(new SGEphemeris(ephem_data_path)); tieStar("/ephemeris/sun/xs", _impl->get_sun(), &Star::getxs); tieStar("/ephemeris/sun/ys", _impl->get_sun(), &Star::getys); diff --git a/src/FDM/JSBSim/JSBSim.cxx b/src/FDM/JSBSim/JSBSim.cxx index 156dd1e37..8e1558955 100644 --- a/src/FDM/JSBSim/JSBSim.cxx +++ b/src/FDM/JSBSim/JSBSim.cxx @@ -198,6 +198,7 @@ FGJSBsim::FGJSBsim( double dt ) PropertyManager = new FGPropertyManager( (FGPropertyNode*)globals->get_props() ); fdmex = new FGFDMExec( PropertyManager ); + fdmex->Hold(); // Register ground callback. fdmex->SetGroundCallback( new FGFSGroundCallback(this) ); @@ -383,6 +384,15 @@ FGJSBsim::FGJSBsim( double dt ) mesh = new AircraftMesh(fgGetDouble("/fdm/jsbsim/metrics/bw-ft"), fgGetDouble("/fdm/jsbsim/metrics/cbarw-ft")); + + // Trim once to initialize all output parameters + FGTrim *fgtrim = new FGTrim(fdmex,tFull); + fgtrim->DoTrim(); + delete fgtrim; + + string directive_file = fgGetString("/sim/jsbsim/output-directive-file"); + if (!directive_file.empty()) + fdmex->SetOutputDirectives(directive_file); } /******************************************************************************/ @@ -427,6 +437,7 @@ void FGJSBsim::init() common_init(); copy_to_JSBsim(); + fdmex->Resume(); fdmex->RunIC(); //loop JSBSim once w/o integrating if (fgGetBool("/sim/presets/running")) { Propulsion->InitRunning(-1); diff --git a/src/FDM/JSBSim/input_output/FGModelLoader.cpp b/src/FDM/JSBSim/input_output/FGModelLoader.cpp index 6170b1bc6..1dbebae82 100644 --- a/src/FDM/JSBSim/input_output/FGModelLoader.cpp +++ b/src/FDM/JSBSim/input_output/FGModelLoader.cpp @@ -57,7 +57,7 @@ Element_ptr FGModelLoader::Open(Element *el) if (!fname.empty()) { FGXMLFileRead XMLFileRead; - SGPath path(SGPath::fromLocal8Bit(fname.c_str())); + SGPath path(SGPath::fromUtf8(fname)); if (path.isRelative()) path = model->FindFullPathName(path); diff --git a/src/FDM/JSBSim/models/flight_control/FGFCSComponent.cpp b/src/FDM/JSBSim/models/flight_control/FGFCSComponent.cpp index 7c0503c0a..0098f3f56 100644 --- a/src/FDM/JSBSim/models/flight_control/FGFCSComponent.cpp +++ b/src/FDM/JSBSim/models/flight_control/FGFCSComponent.cpp @@ -236,10 +236,10 @@ void FGFCSComponent::Clip(void) double range = vmax - vmin; if (range < 0.0) { - cerr << "Trying to clip with a max value " << ClipMax->GetName() - << " lower than the min value " << ClipMin->GetName() - << endl; - throw("JSBSim aborts"); + cerr << "Trying to clip with a max value (" << vmax << ") from " + << ClipMax->GetName() << " lower than the min value (" << vmin + << ") from " << ClipMin->GetName() << "." << endl + << "Clipping is ignored." << endl; return; } diff --git a/src/FDM/UFO.cxx b/src/FDM/UFO.cxx index 14a462bba..9422c5988 100644 --- a/src/FDM/UFO.cxx +++ b/src/FDM/UFO.cxx @@ -189,6 +189,11 @@ void FGUFO::update( double dt ) { set_V_north(cos(heading) * velocity * SG_METER_TO_FEET); set_V_east(sin(heading) * velocity * SG_METER_TO_FEET); set_V_down(-real_climb_rate); + + //we assume the ufo only fly along the roll axis, providing a velocity to allow lag compensation + _set_Velocities_Body( velocity * SG_METER_TO_FEET, + 0 , + 0 ); } diff --git a/src/FDM/YASim/FGGround.hpp b/src/FDM/YASim/FGGround.hpp index 78325b955..df85489f7 100644 --- a/src/FDM/YASim/FGGround.hpp +++ b/src/FDM/YASim/FGGround.hpp @@ -18,26 +18,26 @@ public: FGGround(FGInterface *iface); virtual ~FGGround(); - virtual void getGroundPlane(const double pos[3], + void getGroundPlane(const double pos[3], double plane[4], float vel[3], - unsigned int &body); + unsigned int &body) override; - virtual void getGroundPlane(const double pos[3], + void getGroundPlane(const double pos[3], double plane[4], float vel[3], const simgear::BVHMaterial **material, - unsigned int &body); + unsigned int &body) override; - virtual bool getBody(double t, double bodyToWorld[16], double linearVel[3], - double angularVel[3], unsigned int &id); + bool getBody(double t, double bodyToWorld[16], double linearVel[3], + double angularVel[3], unsigned int &id) override; - virtual bool caughtWire(const double pos[4][3]); + bool caughtWire(const double pos[4][3]) override; - virtual bool getWire(double end[2][3], float vel[2][3]); + bool getWire(double end[2][3], float vel[2][3]) override; - virtual void releaseWire(void); + void releaseWire(void) override; - virtual float getCatapult(const double pos[3], - double end[2][3], float vel[2][3]); + float getCatapult(const double pos[3], + double end[2][3], float vel[2][3]) override; void setTimeOffset(double toff); diff --git a/src/FDM/YASim/Ground.cpp b/src/FDM/YASim/Ground.cpp index af01a065c..47274aaa7 100644 --- a/src/FDM/YASim/Ground.cpp +++ b/src/FDM/YASim/Ground.cpp @@ -8,14 +8,6 @@ #include "Ground.hpp" namespace yasim { -Ground::Ground() -{ -} - -Ground::~Ground() -{ -} - void Ground::getGroundPlane(const double pos[3], double plane[4], float vel[3], unsigned int &body) @@ -47,8 +39,7 @@ void Ground::getGroundPlane(const double pos[3], bool Ground::getBody(double t, double bodyToWorld[16], double linearVel[3], double angularVel[3], unsigned int &body) { - return getBody(t, bodyToWorld, linearVel, - angularVel, body); + return false; } bool Ground::caughtWire(const double pos[4][3]) diff --git a/src/FDM/YASim/Ground.hpp b/src/FDM/YASim/Ground.hpp index 433caca76..52d6284b0 100644 --- a/src/FDM/YASim/Ground.hpp +++ b/src/FDM/YASim/Ground.hpp @@ -8,8 +8,7 @@ namespace yasim { class Ground { public: - Ground(); - virtual ~Ground(); + virtual ~Ground() = default; virtual void getGroundPlane(const double pos[3], double plane[4], float vel[3], @@ -20,8 +19,8 @@ public: const simgear::BVHMaterial **material, unsigned int &body); - virtual bool getBody(double t, double bodyToWorld[16], double linearVel[3], - double angularVel[3], unsigned int &id); + virtual bool getBody(double t, double bodyToWorld[16], double linearVel[3], + double angularVel[3], unsigned int &id); virtual bool caughtWire(const double pos[4][3]); diff --git a/src/FDM/fdm_shell.cxx b/src/FDM/fdm_shell.cxx index 40693b5b0..f6ec66079 100644 --- a/src/FDM/fdm_shell.cxx +++ b/src/FDM/fdm_shell.cxx @@ -190,6 +190,12 @@ void FDMShell::update(double dt) fgSetBool("/sim/fdm-initialized", true); fgSetBool("/sim/signals/fdm-initialized", true); + + if (!copyProperties(_props->getNode("fdm", true), + _initialFdmProperties)) + { + SG_LOG(SG_FLIGHT, SG_ALERT, "Failed to save initial FDM property state"); + } } } diff --git a/src/GUI/AddOnsController.cxx b/src/GUI/AddOnsController.cxx index 43d9988f7..3428dfe4d 100644 --- a/src/GUI/AddOnsController.cxx +++ b/src/GUI/AddOnsController.cxx @@ -22,12 +22,15 @@ #include "AddonsModel.hxx" #include "InstallSceneryDialog.hxx" #include "QtLauncher.hxx" +#include "PathListModel.hxx" +#include "LaunchConfig.hxx" ////////////////////////////////////////////////////////////////////////// -AddOnsController::AddOnsController(LauncherMainWindow *parent) : +AddOnsController::AddOnsController(LauncherMainWindow *parent, LaunchConfig* config) : QObject(parent), - m_launcher(parent) + m_launcher(parent), + m_config(config) { m_catalogs = new CatalogListModel(this, simgear::pkg::RootRef(globals->packageRoot())); @@ -38,10 +41,32 @@ AddOnsController::AddOnsController(LauncherMainWindow *parent) : connect(m_addonsModuleModel, &AddonsModel::modulesChanged, this, &AddOnsController::onAddonsChanged); using namespace flightgear::addons; - QSettings settings; - m_sceneryPaths = settings.value("scenery-paths").toStringList(); - m_aircraftPaths = settings.value("aircraft-paths").toStringList(); + m_sceneryPaths = new PathListModel(this); + connect(m_sceneryPaths, &PathListModel::enabledPathsChanged, [this] () { + m_sceneryPaths->saveToSettings("scenery-paths-v2"); + flightgear::launcherSetSceneryPaths(); + }); + m_sceneryPaths->loadFromSettings("scenery-paths-v2"); + + m_aircraftPaths = new PathListModel(this); + m_aircraftPaths->loadFromSettings("aircraft-paths-v2"); + + // sync up the aircraft cache now + auto aircraftCache = LocalAircraftCache::instance(); + aircraftCache->setPaths(m_aircraftPaths->enabledPaths()); + aircraftCache->scanDirs(); + + // watch for future changes + connect(m_aircraftPaths, &PathListModel::enabledPathsChanged, [this] () { + m_aircraftPaths->saveToSettings("aircraft-paths-v2"); + + auto aircraftCache = LocalAircraftCache::instance(); + aircraftCache->setPaths(m_aircraftPaths->enabledPaths()); + aircraftCache->scanDirs(); + }); + + QSettings settings; int size = settings.beginReadArray("addon-modules"); for (int i = 0; i < size; ++i) { settings.setArrayIndex(i); @@ -66,14 +91,18 @@ AddOnsController::AddOnsController(LauncherMainWindow *parent) : qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "AddOnsControllers", "no"); qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "CatalogListModel", "no"); qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "AddonsModel", "no"); + qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "PathListMode", "no"); + + connect(m_config, &LaunchConfig::collect, + this, &AddOnsController::collectArgs); } -QStringList AddOnsController::aircraftPaths() const +PathListModel* AddOnsController::aircraftPaths() const { return m_aircraftPaths; } -QStringList AddOnsController::sceneryPaths() const +PathListModel* AddOnsController::sceneryPaths() const { return m_sceneryPaths; } @@ -119,6 +148,7 @@ QString AddOnsController::addAircraftPath() const } } + m_aircraftPaths->appendPath(path); return path; } @@ -206,6 +236,7 @@ QString AddOnsController::addSceneryPath() const } } + m_sceneryPaths->appendPath(path); return path; } @@ -227,37 +258,6 @@ void AddOnsController::openDirectory(QString path) QDesktopServices::openUrl(u); } -void AddOnsController::setAircraftPaths(QStringList aircraftPaths) -{ - if (m_aircraftPaths == aircraftPaths) - return; - - m_aircraftPaths = aircraftPaths; - emit aircraftPathsChanged(m_aircraftPaths); - - - QSettings settings; - settings.setValue("aircraft-paths", m_aircraftPaths); - auto aircraftCache = LocalAircraftCache::instance(); - aircraftCache->setPaths(m_aircraftPaths); - aircraftCache->scanDirs(); -} - -void AddOnsController::setSceneryPaths(QStringList sceneryPaths) -{ - if (m_sceneryPaths == sceneryPaths) - return; - - m_sceneryPaths = sceneryPaths; - - QSettings settings; - settings.setValue("scenery-paths", m_sceneryPaths); - - flightgear::launcherSetSceneryPaths(); - - emit sceneryPathsChanged(m_sceneryPaths); -} - void AddOnsController::setAddons(AddonsModel* addons) { @@ -342,3 +342,31 @@ void AddOnsController::onAddonsChanged() } settings.endArray(); } + +void AddOnsController::collectArgs() +{ + // scenery paths + Q_FOREACH(QString path, m_sceneryPaths->enabledPaths()) { + m_config->setArg("fg-scenery", path); + } + + // aircraft paths + Q_FOREACH(QString path, m_aircraftPaths->enabledPaths()) { + m_config->setArg("fg-aircraft", path); + } + + // add-on module paths + // we could query this directly from AddonsModel, but this is simpler right now + QSettings settings; + int size = settings.beginReadArray("addon-modules"); + for (int i = 0; i < size; ++i) { + settings.setArrayIndex(i); + + QString path = settings.value("path").toString(); + bool enable = settings.value("enable").toBool(); + if (enable) { + m_config->setArg("addon", path); + } + } + settings.endArray(); +} diff --git a/src/GUI/AddOnsController.hxx b/src/GUI/AddOnsController.hxx index fab9119a8..2a9dea74e 100644 --- a/src/GUI/AddOnsController.hxx +++ b/src/GUI/AddOnsController.hxx @@ -7,13 +7,15 @@ class CatalogListModel; class AddonsModel; class LauncherMainWindow; +class PathListModel; +class LaunchConfig; class AddOnsController : public QObject { Q_OBJECT - Q_PROPERTY(QStringList aircraftPaths READ aircraftPaths WRITE setAircraftPaths NOTIFY aircraftPathsChanged) - Q_PROPERTY(QStringList sceneryPaths READ sceneryPaths WRITE setSceneryPaths NOTIFY sceneryPathsChanged) + Q_PROPERTY(PathListModel* aircraftPaths READ aircraftPaths CONSTANT) + Q_PROPERTY(PathListModel* sceneryPaths READ sceneryPaths CONSTANT) Q_PROPERTY(QStringList modulePaths READ modulePaths WRITE setModulePaths NOTIFY modulePathsChanged) Q_PROPERTY(CatalogListModel* catalogs READ catalogs CONSTANT) @@ -22,10 +24,10 @@ class AddOnsController : public QObject Q_PROPERTY(bool showNoOfficialHangar READ showNoOfficialHangar NOTIFY showNoOfficialHangarChanged) public: - explicit AddOnsController(LauncherMainWindow *parent = nullptr); + explicit AddOnsController(LauncherMainWindow *parent, LaunchConfig* config); - QStringList aircraftPaths() const; - QStringList sceneryPaths() const; + PathListModel* aircraftPaths() const; + PathListModel* sceneryPaths() const; QStringList modulePaths() const; CatalogListModel* catalogs() const @@ -50,8 +52,6 @@ public: Q_INVOKABLE void officialCatalogAction(QString s); signals: - void aircraftPathsChanged(QStringList aircraftPaths); - void sceneryPathsChanged(QStringList sceneryPaths); void modulePathsChanged(QStringList modulePaths); void modulesChanged(); @@ -59,12 +59,12 @@ signals: void showNoOfficialHangarChanged(); public slots: - void setAircraftPaths(QStringList aircraftPaths); - void setSceneryPaths(QStringList sceneryPaths); void setModulePaths(QStringList modulePaths); void setAddons(AddonsModel* addons); void onAddonsChanged(void); + void collectArgs(); + private: bool shouldShowOfficialCatalogMessage() const; void onCatalogsChanged(); @@ -72,9 +72,10 @@ private: LauncherMainWindow* m_launcher; CatalogListModel* m_catalogs = nullptr; AddonsModel* m_addonsModuleModel = nullptr; + LaunchConfig* m_config = nullptr; - QStringList m_aircraftPaths; - QStringList m_sceneryPaths; + PathListModel* m_aircraftPaths = nullptr; + PathListModel* m_sceneryPaths = nullptr; QStringList m_addonModulePaths; }; diff --git a/src/GUI/AddonsModel.cxx b/src/GUI/AddonsModel.cxx index 1d605051d..ba4ee041f 100644 --- a/src/GUI/AddonsModel.cxx +++ b/src/GUI/AddonsModel.cxx @@ -37,7 +37,7 @@ AddonsModel::AddonsModel(QObject* pr) : int roleValue = IdRole; for (auto it = m_roles.begin(); it != m_roles.end(); ++it) { - QByteArray name = (*it).toLocal8Bit(); + QByteArray name = it->toUtf8(); m_roleToName[roleValue] = name; m_nameToRole[*it] = roleValue++; } diff --git a/src/GUI/AircraftModel.cxx b/src/GUI/AircraftModel.cxx index b94a0cc9c..22c8c301b 100644 --- a/src/GUI/AircraftModel.cxx +++ b/src/GUI/AircraftModel.cxx @@ -23,6 +23,7 @@ #include #include #include +#include // Simgear #include @@ -38,10 +39,21 @@ #include "QmlAircraftInfo.hxx" -const int STANDARD_THUMBNAIL_HEIGHT = 128; - using namespace simgear::pkg; +bool isPackageFailure(Delegate::StatusCode status) +{ + switch (status) { + case Delegate::STATUS_SUCCESS: + case Delegate::STATUS_REFRESHED: + case Delegate::STATUS_IN_PROGRESS: + return false; + + default: + return true; + } +} + class PackageDelegate : public simgear::pkg::Delegate { public: @@ -57,18 +69,7 @@ public: } protected: - void catalogRefreshed(CatalogRef aCatalog, StatusCode aReason) override - { - if (aReason == STATUS_IN_PROGRESS) { - // nothing to do - } else if ((aReason == STATUS_REFRESHED) || (aReason == STATUS_SUCCESS)) { - m_model->refreshPackages(); - } else { - qWarning() << "failed refresh of" - << QString::fromStdString(aCatalog->url()) << ":" << aReason << endl; - } - } - + void catalogRefreshed(CatalogRef aCatalog, StatusCode aReason) override; void startInstall(InstallRef aInstall) override { QModelIndex mi(indexForPackage(aInstall->package())); @@ -77,8 +78,8 @@ protected: void installProgress(InstallRef aInstall, unsigned int bytes, unsigned int total) override { - Q_UNUSED(bytes); - Q_UNUSED(total); + Q_UNUSED(bytes) + Q_UNUSED(total) QModelIndex mi(indexForPackage(aInstall->package())); m_model->dataChanged(mi, mi); } @@ -104,7 +105,7 @@ protected: void installStatusChanged(InstallRef aInstall, StatusCode aReason) override { - Q_UNUSED(aReason); + Q_UNUSED(aReason) QModelIndex mi(indexForPackage(aInstall->package())); m_model->dataChanged(mi, mi); } @@ -115,44 +116,6 @@ protected: m_model->dataChanged(mi, mi); } - - virtual void dataForThumbnail(const std::string& aThumbnailUrl, - size_t length, const uint8_t* bytes) override - { - QImage img = QImage::fromData(QByteArray::fromRawData(reinterpret_cast(bytes), length)); - if (img.isNull()) { - qWarning() << "failed to load image data for URL:" << - QString::fromStdString(aThumbnailUrl); - return; - } - - QPixmap pix = QPixmap::fromImage(img); - if (pix.height() > STANDARD_THUMBNAIL_HEIGHT) { - pix = pix.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT, Qt::SmoothTransformation); - } - - QString url = QString::fromStdString(aThumbnailUrl); - m_model->m_downloadedPixmapCache.insert(url, pix); - - // notify any affected items. Linear scan here avoids another map/dict structure. - for (auto pkg : m_model->m_packages) { - const size_t variantCount = pkg->variants().size(); - bool notifyChanged = false; - - for (size_t v=0; v < variantCount; ++v) { - const Package::Thumbnail& thumb(pkg->thumbnailForVariant(v)); - if (thumb.url == aThumbnailUrl) { - notifyChanged = true; - } - } - - if (notifyChanged) { - QModelIndex mi = indexForPackage(pkg); - m_model->dataChanged(mi, mi); - } - } // of packages iteration - } - private: QModelIndex indexForPackage(const PackageRef& ref) const { @@ -169,6 +132,22 @@ private: AircraftItemModel* m_model; }; +void PackageDelegate::catalogRefreshed(CatalogRef aCatalog, StatusCode aReason) +{ + if (aReason == STATUS_IN_PROGRESS) { + // nothing to do + } else if ((aReason == STATUS_REFRESHED) || (aReason == STATUS_SUCCESS)) { + m_model->refreshPackages(); + } else { + qWarning() << "failed refresh of" + << QString::fromStdString(aCatalog->url()) << ":" << aReason << endl; + } +} + +////////////////////////////////////////////////////////////////////////// +/// \brief AircraftItemModel::AircraftItemModel +/// \param pr +/// AircraftItemModel::AircraftItemModel(QObject* pr) : QAbstractListModel(pr) { @@ -179,6 +158,8 @@ AircraftItemModel::AircraftItemModel(QObject* pr) : this, &AircraftItemModel::onScanAddedItems); connect(cache, &LocalAircraftCache::cleared, this, &AircraftItemModel::onLocalCacheCleared); + + loadFavourites(); } AircraftItemModel::~AircraftItemModel() @@ -260,8 +241,14 @@ QVariant AircraftItemModel::data(const QModelIndex& index, int role) const return m_delegateStates.at(row).variant; } + if (role == AircraftIsFavouriteRole) { + // recursive call here, hope that's okay + const auto uri = data(index, AircraftURIRole).toUrl(); + return m_favourites.contains(uri); + } + if (row >= m_cachedLocalAircraftCount) { - quint32 packageIndex = row - m_cachedLocalAircraftCount; + quint32 packageIndex = static_cast(row - m_cachedLocalAircraftCount); const PackageRef& pkg(m_packages[packageIndex]); InstallRef ex = pkg->existingInstall(); @@ -309,16 +296,12 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, const DelegateSta } return item->description; - } else if (role == Qt::DecorationRole) { - return item->thumbnail(); } else if (role == AircraftPathRole) { return item->path; } else if (role == AircraftAuthorsRole) { return item->authors; } else if ((role >= AircraftRatingRole) && (role < AircraftVariantDescriptionRole)) { return item->ratings[role - AircraftRatingRole]; - } else if (role == AircraftThumbnailRole) { - return item->thumbnail(); } else if (role == AircraftPackageIdRole) { // can we fake an ID? otherwise fall through to a null variant } else if (role == AircraftPackageStatusRole) { @@ -354,10 +337,6 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, const DelegateSta QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, const DelegateState& state, int role) const { - if (role == Qt::DecorationRole) { - role = AircraftThumbnailRole; - } - if (role >= AircraftVariantDescriptionRole) { int variantIndex = role - AircraftVariantDescriptionRole; QString desc = QString::fromStdString(item->nameForVariant(variantIndex)); @@ -393,6 +372,10 @@ QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, const Delega return LocalAircraftCache::PackageUpdateAvailable; } + const auto status = i->status(); + if (isPackageFailure(status)) + return LocalAircraftCache::PackageInstallFailed; + return LocalAircraftCache::PackageInstalled; } else { return LocalAircraftCache::PackageNotInstalled; @@ -401,8 +384,6 @@ QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, const Delega // this value wants the number of aditional variants, i.e not // including the primary. Hence the -1 term. return static_cast(item->variants().size() - 1); - } else if (role == AircraftThumbnailRole) { - return packageThumbnail(item, state); } else if (role == AircraftAuthorsRole) { std::string authors = item->getLocalisedProp("author", state.variant); if (!authors.empty()) { @@ -436,42 +417,6 @@ QVariant AircraftItemModel::packageRating(const PackageRef& p, int ratingIndex) return LocalAircraftCache::ratingFromProperties(p->properties()->getChild("rating"), ratingIndex); } -QVariant AircraftItemModel::packageThumbnail(PackageRef p, const DelegateState& ds, bool download) const -{ - const Package::Thumbnail& thumb(p->thumbnailForVariant(ds.variant)); - if (thumb.url.empty()) { - return QVariant(); - } - - QString urlQString(QString::fromStdString(thumb.url)); - if (m_downloadedPixmapCache.contains(urlQString)) { - // cache hit, easy - return m_downloadedPixmapCache.value(urlQString); - } - -// check the on-disk store. - InstallRef ex = p->existingInstall(); - if (ex.valid()) { - SGPath thumbPath = ex->path() / thumb.path; - if (thumbPath.exists()) { - QPixmap pix; - pix.load(QString::fromStdString(thumbPath.utf8Str())); - // resize to the standard size - if (pix.height() > STANDARD_THUMBNAIL_HEIGHT) { - pix = pix.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT); - } - m_downloadedPixmapCache[urlQString] = pix; - return pix; - } - } // of have existing install - - if (download) { - m_packageRoot->requestThumbnailData(thumb.url); - } - - return QVariant(); -} - bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value, int role) { int row = index.row(); @@ -485,6 +430,18 @@ bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value, m_delegateStates[row].variant = newValue; emit dataChanged(index, index); return true; + } else if (role == AircraftIsFavouriteRole) { + bool f = value.toBool(); + const auto uri = data(index, AircraftURIRole).toUrl(); + const auto cur = m_favourites.contains(uri); + if (f && !cur) { + m_favourites.append(uri); + } else if (!f && cur) { + m_favourites.removeOne(uri); + } + + saveFavourites(); + emit dataChanged(index, index); } return false; @@ -500,12 +457,12 @@ QHash AircraftItemModel::roleNames() const result[AircraftAuthorsRole] = "authors"; result[AircraftVariantCountRole] = "variantCount"; result[AircraftLongDescriptionRole] = "description"; - result[AircraftThumbnailRole] = "thumbnail"; result[AircraftPackageSizeRole] = "packageSizeBytes"; result[AircraftPackageStatusRole] = "packageStatus"; result[AircraftInstallDownloadedSizeRole] = "downloadedBytes"; result[AircraftVariantRole] = "activeVariant"; + result[AircraftIsFavouriteRole] = "favourite"; result[AircraftStatusRole] = "aircraftStatus"; result[AircraftMinVersionRole] = "requiredFGVersion"; @@ -625,7 +582,7 @@ QString AircraftItemModel::nameForAircraftURI(QUrl uri) const QString ident = uri.path(); PackageRef pkg = m_packageRoot->getPackageById(ident.toStdString()); if (pkg) { - int variantIndex = pkg->indexOfVariant(ident.toStdString()); + const auto variantIndex = pkg->indexOfVariant(ident.toStdString()); return QString::fromStdString(pkg->nameForVariant(variantIndex)); } } else { @@ -637,7 +594,7 @@ QString AircraftItemModel::nameForAircraftURI(QUrl uri) const void AircraftItemModel::onScanAddedItems(int addedCount) { - Q_UNUSED(addedCount); + Q_UNUSED(addedCount) const auto items = LocalAircraftCache::instance()->allItems(); const int newItemCount = items.size() - m_cachedLocalAircraftCount; const int firstRow = m_cachedLocalAircraftCount; @@ -696,7 +653,7 @@ bool AircraftItemModel::isIndexRunnable(const QModelIndex& index) const return true; // local file, always runnable } - quint32 packageIndex = index.row() - m_cachedLocalAircraftCount; + quint32 packageIndex = static_cast(index.row() - m_cachedLocalAircraftCount); const PackageRef& pkg(m_packages[packageIndex]); InstallRef ex = pkg->existingInstall(); if (!ex.valid()) { @@ -706,4 +663,21 @@ bool AircraftItemModel::isIndexRunnable(const QModelIndex& index) const return !ex->isDownloading(); } +void AircraftItemModel::loadFavourites() +{ + m_favourites.clear(); + QSettings settings; + Q_FOREACH(auto v, settings.value("favourite-aircraft").toList()) { + m_favourites.append(v.toUrl()); + } +} +void AircraftItemModel::saveFavourites() +{ + QVariantList favs; + Q_FOREACH(auto u, m_favourites) { + favs.append(u); + } + QSettings settings; + settings.setValue("favourite-aircraft", favs); +} diff --git a/src/GUI/AircraftModel.hxx b/src/GUI/AircraftModel.hxx index a0f12cf17..5590f5cf7 100644 --- a/src/GUI/AircraftModel.hxx +++ b/src/GUI/AircraftModel.hxx @@ -50,7 +50,7 @@ const int AircraftURIRole = Qt::UserRole + 14; const int AircraftIsHelicopterRole = Qt::UserRole + 16; const int AircraftIsSeaplaneRole = Qt::UserRole + 17; const int AircraftPackageRefRole = Qt::UserRole + 19; -const int AircraftThumbnailRole = Qt::UserRole + 20; +const int AircraftIsFavouriteRole = Qt::UserRole + 20; const int AircraftStatusRole = Qt::UserRole + 22; const int AircraftMinVersionRole = Qt::UserRole + 23; @@ -146,15 +146,20 @@ private: void installSucceeded(QModelIndex index); void installFailed(QModelIndex index, simgear::pkg::Delegate::StatusCode reason); + void loadFavourites(); + void saveFavourites(); + +private: PackageDelegate* m_delegate = nullptr; QVector m_delegateStates; simgear::pkg::RootRef m_packageRoot; simgear::pkg::PackageList m_packages; - - mutable QHash m_downloadedPixmapCache; + + QVector m_favourites; int m_cachedLocalAircraftCount = 0; + }; #endif // of FG_GUI_AIRCRAFT_MODEL diff --git a/src/GUI/AircraftSearchFilterModel.cxx b/src/GUI/AircraftSearchFilterModel.cxx index a906f5cd0..f4b5f18bc 100644 --- a/src/GUI/AircraftSearchFilterModel.cxx +++ b/src/GUI/AircraftSearchFilterModel.cxx @@ -1,5 +1,8 @@ #include "AircraftSearchFilterModel.hxx" +#include +#include + #include "AircraftModel.hxx" #include @@ -11,6 +14,8 @@ AircraftProxyModel::AircraftProxyModel(QObject *pr, QAbstractItemModel * source) setSortCaseSensitivity(Qt::CaseInsensitive); setFilterCaseSensitivity(Qt::CaseInsensitive); + + // important we sort on the primary name role and not Qt::DisplayRole // otherwise the aircraft jump when switching variant setSortRole(AircraftVariantDescriptionRole); @@ -110,6 +115,15 @@ void AircraftProxyModel::setHaveUpdateFilterEnabled(bool e) invalidate(); } +void AircraftProxyModel::setShowFavourites(bool e) +{ + if (e == m_onlyShowFavourites) + return; + + m_onlyShowFavourites = e; + invalidate(); +} + bool AircraftProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); @@ -142,6 +156,11 @@ bool AircraftProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour } } + if (m_onlyShowFavourites) { + if (!index.data(AircraftIsFavouriteRole).toBool()) + return false; + } + // if there is no search active, i.e we are browsing, we might apply the // ratings filter. if (m_filterString.isEmpty() && !m_onlyShowInstalled && m_ratingsFilter) { @@ -187,3 +206,28 @@ bool AircraftProxyModel::filterAircraft(const QModelIndex &sourceIndex) const return false; } +void AircraftProxyModel::loadRatingsSettings() +{ + QSettings settings; + m_ratingsFilter = settings.value("enable-ratings-filter", true).toBool(); + QVariantList vRatings = settings.value("ratings-filter").toList(); + if (vRatings.size() == 4) { + for (int i=0; i < 4; ++i) { + m_ratings[i] = vRatings.at(i).toInt(); + } + } + + invalidate(); +} + +void AircraftProxyModel::saveRatingsSettings() +{ + QSettings settings; + settings.setValue("enable-ratings-filter", m_ratingsFilter); + QVariantList vRatings; + for (int i=0; i < 4; ++i) { + vRatings.append(m_ratings.at(i)); + } + settings.setValue("ratings-filter", vRatings); +} + diff --git a/src/GUI/AircraftSearchFilterModel.hxx b/src/GUI/AircraftSearchFilterModel.hxx index e33bf6b2b..d42e73c0a 100644 --- a/src/GUI/AircraftSearchFilterModel.hxx +++ b/src/GUI/AircraftSearchFilterModel.hxx @@ -29,6 +29,10 @@ public: Q_INVOKABLE void selectVariantForAircraftURI(QUrl uri); + Q_INVOKABLE void loadRatingsSettings(); + + Q_INVOKABLE void saveRatingsSettings(); + QList ratings() const { return m_ratings; @@ -56,6 +60,8 @@ public slots: void setInstalledFilterEnabled(bool e); void setHaveUpdateFilterEnabled(bool e); + + void setShowFavourites(bool e); protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; @@ -65,6 +71,7 @@ private: bool m_ratingsFilter = true; bool m_onlyShowInstalled = false; bool m_onlyShowWithUpdate = false; + bool m_onlyShowFavourites = false; QList m_ratings; QString m_filterString; diff --git a/src/GUI/CMakeLists.txt b/src/GUI/CMakeLists.txt index 9715df6b9..a3aed0468 100644 --- a/src/GUI/CMakeLists.txt +++ b/src/GUI/CMakeLists.txt @@ -124,6 +124,10 @@ if (HAVE_QT) AddonsModel.hxx PixmapImageItem.cxx PixmapImageItem.hxx + PathListModel.cxx + PathListModel.hxx + CarriersLocationModel.cxx + CarriersLocationModel.hxx ${uic_sources} ${qrc_sources} ${qml_sources}) @@ -168,6 +172,8 @@ if (HAVE_QT) RouteDiagram.hxx ModelDataExtractor.cxx ModelDataExtractor.hxx + HoverArea.cxx + HoverArea.hxx ) set_property(TARGET fgqmlui PROPERTY AUTOMOC ON) diff --git a/src/GUI/CarriersLocationModel.cxx b/src/GUI/CarriersLocationModel.cxx new file mode 100644 index 000000000..9cebe1246 --- /dev/null +++ b/src/GUI/CarriersLocationModel.cxx @@ -0,0 +1,132 @@ +#include "CarriersLocationModel.hxx" + +#include + +#include + +#include "AIModel/AIManager.hxx" + +CarriersLocationModel::CarriersLocationModel(QObject *parent) + : QAbstractListModel(parent) +{ + SGPropertyNode_ptr localRoot(new SGPropertyNode); + FGAIManager::registerScenarios(localRoot); + +// this code encodes some scenario structre, sorry + for (auto s : localRoot->getNode("sim/ai/scenarios")->getChildren("scenario")) { + const std::string scenarioId = s->getStringValue("id"); + for (auto c : s->getChildren("carrier")) { + processCarrier(scenarioId, c); + } + } +} + +void CarriersLocationModel::processCarrier(const string &scenario, SGPropertyNode_ptr carrierNode) +{ + const auto name = QString::fromStdString(carrierNode->getStringValue("name")); + const auto pennant = QString::fromStdString(carrierNode->getStringValue("pennant-number")); + const auto tacan = QString::fromStdString(carrierNode->getStringValue("TACAN-channel-ID")); + SGGeod geod = SGGeod::fromDeg(carrierNode->getDoubleValue("longitude"), + carrierNode->getDoubleValue("latitude")); + + QStringList parkings; + for (auto c : carrierNode->getChildren("parking-pos")) { + parkings.append(QString::fromStdString(c->getStringValue())); + } + + mCarriers.push_back(Carrier{ + QString::fromStdString(scenario), + pennant, + name, + geod, + tacan, + parkings + }); +} + +int CarriersLocationModel::rowCount(const QModelIndex &parent) const +{ + // For list models only the root node (an invalid parent) should return the list's size. For all + // other (valid) parents, rowCount() should return 0 so that it does not become a tree model. + if (parent.isValid()) + return 0; + + return static_cast(mCarriers.size()); +} + +QVariant CarriersLocationModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + const auto& c = mCarriers.at(static_cast(index.row())); + switch (role) { + case Qt::DisplayRole: + case NameRole: return c.mName; + // case GeodRole: return QVariant::fromValue(c.mInitialLocation); + case IdentRole: return c.mCallsign; + case IconRole: return QPixmap(":/svg/aircraft-carrier"); + default: + break; + } + + return {}; +} + +QHash CarriersLocationModel::roleNames() const +{ + QHash result = QAbstractListModel::roleNames(); + + result[GeodRole] = "geod"; + result[GuidRole] = "guid"; + result[IdentRole] = "ident"; + result[NameRole] = "name"; + result[IconRole] = "icon"; + result[TypeRole] = "type"; + result[NavFrequencyRole] = "frequency"; + return result; +} + +int CarriersLocationModel::indexOf(const QString name) const +{ + auto it = std::find_if(mCarriers.begin(), mCarriers.end(), [name] + (const Carrier& carrier) + { return name == carrier.mName || name == carrier.mCallsign; }); + if (it == mCarriers.end()) + return -1; + + return static_cast(std::distance(mCarriers.begin(), it)); +} + +SGGeod CarriersLocationModel::geodForIndex(int index) const +{ + const auto uIndex = static_cast(index); + if ((index < 0) || (uIndex >= mCarriers.size())) { + return {}; + } + + const auto& c = mCarriers.at(uIndex); + return c.mInitialLocation; +} + +QString CarriersLocationModel::pennantForIndex(int index) const +{ + const auto uIndex = static_cast(index); + if ((index < 0) || (uIndex >= mCarriers.size())) { + return {}; + } + + const auto& c = mCarriers.at(uIndex); + return c.mCallsign; +} + +QStringList CarriersLocationModel::parkingsForIndex(int index) const +{ + const auto uIndex = static_cast(index); + if ((index < 0) || (uIndex >= mCarriers.size())) { + return {}; + } + + const auto& c = mCarriers.at(uIndex); + return c.mParkings; +} diff --git a/src/GUI/CarriersLocationModel.hxx b/src/GUI/CarriersLocationModel.hxx new file mode 100644 index 000000000..2bc4fa9d8 --- /dev/null +++ b/src/GUI/CarriersLocationModel.hxx @@ -0,0 +1,61 @@ +#ifndef CARRIERSMODEL_H +#define CARRIERSMODEL_H + +#include + +#include +#include + +#include + +class CarriersLocationModel : public QAbstractListModel +{ + Q_OBJECT + +public: + explicit CarriersLocationModel(QObject *parent = nullptr); + + // Basic functionality: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + QHash roleNames() const override; + + // copied from NavaidSearchModel + enum Roles { + GeodRole = Qt::UserRole + 1, + GuidRole = Qt::UserRole + 2, + IdentRole = Qt::UserRole + 3, + NameRole = Qt::UserRole + 4, + IconRole = Qt::UserRole + 5, + TypeRole = Qt::UserRole + 6, + NavFrequencyRole = Qt::UserRole + 7 + }; + + int indexOf(const QString name) const; + + SGGeod geodForIndex(int index) const; + QString pennantForIndex(int index) const; + + QStringList parkingsForIndex(int index) const; +private: + + struct Carrier + { + QString mScenario; // scenario ID for loading + QString mCallsign; // pennant-number + QString mName; + SGGeod mInitialLocation; + // icon? + QString mTACAN; + QStringList mParkings; + }; + + using CarrierVec = std::vector; + CarrierVec mCarriers; + + void processCarrier(const std::string& scenario, SGPropertyNode_ptr carrierNode); +}; + +#endif // CARRIERSMODEL_H diff --git a/src/GUI/CatalogListModel.cxx b/src/GUI/CatalogListModel.cxx index 5ac82bf73..7704727e7 100644 --- a/src/GUI/CatalogListModel.cxx +++ b/src/GUI/CatalogListModel.cxx @@ -93,13 +93,13 @@ void CatalogListModel::resetData() int CatalogListModel::rowCount(const QModelIndex& parent) const { - return m_catalogs.size(); + Q_UNUSED(parent) + return static_cast(m_catalogs.size()); } QVariant CatalogListModel::data(const QModelIndex& index, int role) const { - simgear::pkg::CatalogRef cat = m_catalogs.at(index.row()); - + const auto cat = m_catalogs.at(static_cast(index.row())); if (role == Qt::DisplayRole) { QString name = QString::fromStdString(cat->name()); QString desc; @@ -139,6 +139,8 @@ QVariant CatalogListModel::data(const QModelIndex& index, int role) const return translateStatusForCatalog(cat); } else if (role == CatalogIsNewlyAdded) { return (cat == m_newlyAddedCatalog); + } else if (role == CatalogEnabled) { + return cat->isUserEnabled(); } return QVariant(); @@ -146,13 +148,18 @@ QVariant CatalogListModel::data(const QModelIndex& index, int role) const bool CatalogListModel::setData(const QModelIndex &index, const QVariant &value, int role) { + auto cat = m_catalogs.at(static_cast(index.row())); + if (role == CatalogEnabled) { + cat->setUserEnabled(value.toBool()); + return true; + } return false; } Qt::ItemFlags CatalogListModel::flags(const QModelIndex &index) const { Qt::ItemFlags r = Qt::ItemIsSelectable; - const auto cat = m_catalogs.at(index.row()); + const auto cat = m_catalogs.at(static_cast(index.row())); if (cat->isEnabled()) { r |= Qt::ItemIsEnabled; } @@ -168,28 +175,27 @@ QHash CatalogListModel::roleNames() const result[CatalogNameRole] = "name"; result[CatalogStatusRole] = "status"; result[CatalogIsNewlyAdded] = "isNewlyAdded"; - + result[CatalogEnabled] = "enabled"; return result; } void CatalogListModel::removeCatalog(int index) { - if ((index < 0) || (index >= m_catalogs.size())) { + if ((index < 0) || (index >= static_cast(m_catalogs.size()))) { return; } - const std::string removeId = m_catalogs.at(index)->id(); + const std::string removeId = m_catalogs.at(static_cast(index))->id(); m_packageRoot->removeCatalogById(removeId); resetData(); } void CatalogListModel::refreshCatalog(int index) { - if ((index < 0) || (index >= m_catalogs.size())) { + if ((index < 0) || (index >= static_cast(m_catalogs.size()))) { return; } - - m_catalogs.at(index)->refresh(); + m_catalogs.at(static_cast(index))->refresh(); } void CatalogListModel::installDefaultCatalog() @@ -221,7 +227,7 @@ int CatalogListModel::indexOf(QUrl url) if (it == m_catalogs.end()) return -1; - return std::distance(m_catalogs.begin(), it); + return static_cast(std::distance(m_catalogs.begin(), it)); } void CatalogListModel::finalizeAddCatalog() @@ -237,7 +243,7 @@ void CatalogListModel::finalizeAddCatalog() return; } - const int row = std::distance(m_catalogs.begin(), it); + const int row = static_cast(std::distance(m_catalogs.begin(), it)); m_newlyAddedCatalog.clear(); emit isAddingCatalogChanged(); emit statusOfAddingCatalogChanged(); diff --git a/src/GUI/CatalogListModel.hxx b/src/GUI/CatalogListModel.hxx index 435cc3bfa..c79e0d249 100644 --- a/src/GUI/CatalogListModel.hxx +++ b/src/GUI/CatalogListModel.hxx @@ -38,6 +38,7 @@ const int CatalogStatusRole = Qt::UserRole + 5; const int CatalogDescriptionRole = Qt::UserRole + 6; const int CatalogNameRole = Qt::UserRole + 7; const int CatalogIsNewlyAdded = Qt::UserRole + 8; +const int CatalogEnabled = Qt::UserRole + 9; class CatalogDelegate; diff --git a/src/GUI/FGFontCache.cxx b/src/GUI/FGFontCache.cxx old mode 100644 new mode 100755 index fda16272b..4ca96e4f6 --- a/src/GUI/FGFontCache.cxx +++ b/src/GUI/FGFontCache.cxx @@ -13,20 +13,17 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // -#ifdef HAVE_CONFIG_H -# include -#endif - -#ifdef HAVE_WINDOWS_H -#include -#endif +#include #include "FGFontCache.hxx" -#include +// this is our one in 3rdparty +#include "fnt.h" + #include #include +#include #include
@@ -211,26 +208,18 @@ FGFontCache::getfntpath(const std::string& name) bool FGFontCache::initializeFonts() { - static std::string fontext("txf"); + static std::string fontext(".txf"); init(); - std::string fontPath = _path.local8BitStr(); - ulDir* fontdir = ulOpenDir(fontPath.c_str()); - if (!fontdir) - return false; - const ulDirEnt *dirEntry; - while ((dirEntry = ulReadDir(fontdir)) != 0) { - SGPath path(_path); - path.append(dirEntry->d_name); - if (path.extension() == fontext) { - fntTexFont* f = new fntTexFont; - std::string ps = path.local8BitStr(); - if (f->load(ps.c_str())) - _texFonts[std::string(dirEntry->d_name)] = f; - else - delete f; - } + + auto dir = simgear::Dir(_path); + for (auto p : dir.children(simgear::Dir::TYPE_FILE, fontext)) { + fntTexFont* f = new fntTexFont; + if (f->load(p)) + _texFonts[p.file()] = f; + else + delete f; } - ulCloseDir(fontdir); + return true; } diff --git a/src/GUI/HoverArea.cxx b/src/GUI/HoverArea.cxx new file mode 100644 index 000000000..1fc262fe8 --- /dev/null +++ b/src/GUI/HoverArea.cxx @@ -0,0 +1,33 @@ +#include "HoverArea.hxx" + +#include +#include + +HoverArea::HoverArea() +{ + connect(this, &QQuickItem::windowChanged, [this](QQuickWindow* win) { + if (win) { + win->installEventFilter(this); + } + }); +} + + + +bool HoverArea::eventFilter(QObject *sender, QEvent *event) +{ + Q_UNUSED(sender) + if (event->type() == QEvent::MouseMove) { + QMouseEvent* me = static_cast(event); + const auto local = mapFromScene(me->pos()); + const bool con = contains(local); + if (con != m_containsMouse) { + m_containsMouse = con; + emit containsMouseChanged(con); + } + } + + return false; +} + + diff --git a/src/GUI/HoverArea.hxx b/src/GUI/HoverArea.hxx new file mode 100644 index 000000000..8bea2c7b4 --- /dev/null +++ b/src/GUI/HoverArea.hxx @@ -0,0 +1,31 @@ +#ifndef HOVERAREA_HXX +#define HOVERAREA_HXX + +#include + +class HoverArea : public QQuickItem +{ + Q_OBJECT + + Q_PROPERTY(bool containsMouse READ containsMouse NOTIFY containsMouseChanged); + +public: + HoverArea(); + + bool containsMouse() const + { + return m_containsMouse; + } + +signals: + + void containsMouseChanged(bool containsMouse); + +protected: + bool eventFilter(QObject* sender, QEvent* event) override; + +private: + bool m_containsMouse = false; +}; + +#endif // HOVERAREA_HXX diff --git a/src/GUI/LauncherController.cxx b/src/GUI/LauncherController.cxx index 44a61ac60..fb2fe59e7 100644 --- a/src/GUI/LauncherController.cxx +++ b/src/GUI/LauncherController.cxx @@ -52,6 +52,9 @@ #include "NavaidSearchModel.hxx" #include "FlightPlanController.hxx" #include "ModelDataExtractor.hxx" +#include "CarriersLocationModel.hxx" +#include "SetupRootDialog.hxx" +#include "HoverArea.hxx" using namespace simgear::pkg; @@ -90,6 +93,9 @@ LauncherController::LauncherController(QObject *parent, QWindow* window) : m_aircraftSearchModel = new AircraftProxyModel(this, m_aircraftModel); + m_favouriteAircraftModel = new AircraftProxyModel(this, m_aircraftModel); + m_favouriteAircraftModel->setShowFavourites(true); + m_aircraftHistory = new RecentAircraftModel(m_aircraftModel, this); connect(m_aircraftModel, &AircraftItemModel::aircraftInstallCompleted, @@ -102,14 +108,12 @@ LauncherController::LauncherController(QObject *parent, QWindow* window) : this, &LauncherController::updateSelectedAircraft); QSettings settings; - LocalAircraftCache::instance()->setPaths(settings.value("aircraft-paths").toStringList()); - LocalAircraftCache::instance()->scanDirs(); m_aircraftModel->setPackageRoot(globals->packageRoot()); m_aircraftGridMode = settings.value("aircraftGridMode").toBool(); m_subsystemIdleTimer = new QTimer(this); - m_subsystemIdleTimer->setInterval(10); + m_subsystemIdleTimer->setInterval(5); connect(m_subsystemIdleTimer, &QTimer::timeout, []() {globals->get_subsystem_mgr()->update(0.0);}); m_subsystemIdleTimer->start(); @@ -144,6 +148,7 @@ void LauncherController::initQML() qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "MPServers", "Singleton API"); qmlRegisterType("FlightGear", 1, 0, "NavaidSearch"); + qmlRegisterType("FlightGear", 1, 0, "CarriersModel"); qmlRegisterUncreatableType("FlightGear", 1, 0, "Units", "Only for enum"); qmlRegisterType("FlightGear", 1, 0, "UnitsModel"); @@ -166,6 +171,7 @@ void LauncherController::initQML() qmlRegisterType("FlightGear", 1, 0, "NavaidDiagram"); qmlRegisterType("FlightGear", 1, 0, "RouteDiagram"); qmlRegisterType("FlightGear", 1, 0, "RadioButtonGroup"); + qmlRegisterType("FlightGear", 1, 0, "HoverArea"); qmlRegisterType("FlightGear", 1, 0, "ModelDataExtractor"); @@ -185,6 +191,7 @@ void LauncherController::setInAppMode() m_inAppMode = true; m_keepRunningInAppMode = true; m_appModeResult = true; + emit inAppChanged(); } bool LauncherController::keepRunningInAppMode() const @@ -280,31 +287,6 @@ void LauncherController::collectAircraftArgs() m_config->setArg("state", state); } } - - // scenery paths - QSettings settings; - Q_FOREACH(QString path, settings.value("scenery-paths").toStringList()) { - m_config->setArg("fg-scenery", path); - } - - // aircraft paths - Q_FOREACH(QString path, settings.value("aircraft-paths").toStringList()) { - m_config->setArg("fg-aircraft", path); - } - - // add-on module paths - int size = settings.beginReadArray("addon-modules"); - for (int i = 0; i < size; ++i) { - settings.setArrayIndex(i); - - QString path = settings.value("path").toString(); - bool enable = settings.value("enable").toBool(); - if (enable) { - m_config->setArg("addon", path); - } - } - settings.endArray(); - } void LauncherController::saveAircraft() @@ -722,6 +704,20 @@ QVariantList LauncherController::defaultSplashUrls() const return urls; } +QVariant LauncherController::loadUISetting(QString name, QVariant defaultValue) const +{ + QSettings settings; + if (!settings.contains(name)) + return defaultValue; + return settings.value(name); +} + +void LauncherController::saveUISetting(QString name, QVariant value) const +{ + QSettings settings; + settings.setValue(name, value); +} + void LauncherController::onAircraftInstalledCompleted(QModelIndex index) { maybeUpdateSelectedAircraft(index); @@ -777,7 +773,7 @@ void LauncherController::requestChangeDataPath() { QString currentLocText; QSettings settings; - QString root = settings.value("fg-root").toString(); + QString root = settings.value(SetupRootDialog::rootPathKey()).toString(); if (root.isNull()) { currentLocText = tr("Currently the built-in data files are being used"); } @@ -801,12 +797,7 @@ void LauncherController::requestChangeDataPath() return; } - { - QSettings settings; - // set the option to the magic marker value - settings.setValue("fg-root", "!ask"); - } // scope the ensure settings are written nicely - + SetupRootDialog::askRootOnNextLaunch(); flightgear::restartTheApp(); } diff --git a/src/GUI/LauncherController.hxx b/src/GUI/LauncherController.hxx index 29264c006..8074df0a9 100644 --- a/src/GUI/LauncherController.hxx +++ b/src/GUI/LauncherController.hxx @@ -51,6 +51,7 @@ class LauncherController : public QObject Q_PROPERTY(AircraftProxyModel* aircraftWithUpdatesModel MEMBER m_aircraftWithUpdatesModel CONSTANT) Q_PROPERTY(AircraftProxyModel* browseAircraftModel MEMBER m_browseAircraftModel CONSTANT) Q_PROPERTY(AircraftProxyModel* searchAircraftModel MEMBER m_aircraftSearchModel CONSTANT) + Q_PROPERTY(AircraftProxyModel* favouriteAircraftModel MEMBER m_favouriteAircraftModel CONSTANT) Q_PROPERTY(AircraftItemModel* baseAircraftModel MEMBER m_aircraftModel CONSTANT) @@ -85,6 +86,8 @@ class LauncherController : public QObject Q_PROPERTY(QSize minimumWindowSize READ minWindowSize WRITE setMinWindowSize NOTIFY minWindowSizeChanged) Q_PROPERTY(QUrl flyIconUrl READ flyIconUrl NOTIFY selectedAircraftChanged) + + Q_PROPERTY(bool inAppMode READ inApp NOTIFY inAppChanged) Q_PROPERTY(bool aircraftGridMode READ aircraftGridMode WRITE setAircraftGridMode NOTIFY aircraftGridModeChanged) public: @@ -142,7 +145,8 @@ public: // used on the summary screen Q_INVOKABLE QVariantList defaultSplashUrls() const; - + Q_INVOKABLE QVariant loadUISetting(QString name, QVariant defaultValue) const; + Q_INVOKABLE void saveUISetting(QString name, QVariant value) const; LaunchConfig* config() const { return m_config; } @@ -193,6 +197,10 @@ public: return m_aircraftGridMode; } + bool inApp() const + { + return m_inAppMode; + } signals: void selectedAircraftChanged(QUrl selectedAircraft); @@ -207,6 +215,7 @@ signals: void aircraftGridModeChanged(bool aircraftGridMode); + void inAppChanged(); public slots: void setSelectedAircraft(QUrl selectedAircraft); @@ -261,6 +270,8 @@ private: AircraftProxyModel* m_aircraftSearchModel; AircraftProxyModel* m_browseAircraftModel; AircraftProxyModel* m_aircraftWithUpdatesModel; + AircraftProxyModel* m_favouriteAircraftModel; + MPServersModel* m_serversModel = nullptr; LocationController* m_location = nullptr; FlightPlanController* m_flightPlan = nullptr; diff --git a/src/GUI/LauncherMainWindow.cxx b/src/GUI/LauncherMainWindow.cxx index 0af6c0ed1..23acdbe66 100644 --- a/src/GUI/LauncherMainWindow.cxx +++ b/src/GUI/LauncherMainWindow.cxx @@ -68,9 +68,8 @@ LauncherMainWindow::LauncherMainWindow() : connect(qa, &QAction::triggered, m_controller, &LauncherController::quit); m_controller->initialRestoreSettings(); - flightgear::launcherSetSceneryPaths(); - auto addOnsCtl = new AddOnsController(this); + auto addOnsCtl = new AddOnsController(this, m_controller->config()); //////////// #if defined(Q_OS_WIN) diff --git a/src/GUI/LocalAircraftCache.cxx b/src/GUI/LocalAircraftCache.cxx index 3290c6194..a3d88ae20 100644 --- a/src/GUI/LocalAircraftCache.cxx +++ b/src/GUI/LocalAircraftCache.cxx @@ -177,23 +177,6 @@ void AircraftItem::toDataStream(QDataStream& ds) const ds << tags; } -QPixmap AircraftItem::thumbnail(bool loadIfRequired) const -{ - if (m_thumbnail.isNull() && loadIfRequired) { - QFileInfo info(path); - QDir dir = info.dir(); - if (dir.exists(thumbnailPath)) { - m_thumbnail.load(dir.filePath(thumbnailPath)); - // resize to the standard size - if (m_thumbnail.height() > STANDARD_THUMBNAIL_HEIGHT) { - m_thumbnail = m_thumbnail.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT, Qt::SmoothTransformation); - } - } - } - - return m_thumbnail; -} - int AircraftItem::indexOfVariant(QUrl uri) const { const QString path = uri.toLocalFile(); @@ -253,6 +236,7 @@ public: { m_done = true; } + Q_SIGNALS: void addedItems(); @@ -437,12 +421,11 @@ void LocalAircraftCache::scanDirs() QStringList dirs = m_paths; - Q_FOREACH(SGPath ap, globals->get_aircraft_paths()) { + for (SGPath ap : globals->get_aircraft_paths()) { dirs << QString::fromStdString(ap.utf8Str()); } - SGPath rootAircraft(globals->get_fg_root()); - rootAircraft.append("Aircraft"); + SGPath rootAircraft = globals->get_fg_root() / "Aircraft"; dirs << QString::fromStdString(rootAircraft.utf8Str()); m_scanThread.reset(new AircraftScanThread(dirs)); @@ -522,10 +505,15 @@ AircraftItemPtr LocalAircraftCache::findItemWithUri(QUrl aircraftUri) const void LocalAircraftCache::abandonCurrentScan() { if (m_scanThread) { + // don't fire onScanFinished when the thread ends + disconnect(m_scanThread.get(), &AircraftScanThread::finished, this, + &LocalAircraftCache::onScanFinished); + m_scanThread->setDone(); - m_scanThread->wait(1000); + if (!m_scanThread->wait(2000)) { + qWarning() << Q_FUNC_INFO << "scan thread failed to terminate"; + } m_scanThread.reset(); - qWarning() << Q_FUNC_INFO << "current scan abandonded"; } } diff --git a/src/GUI/LocalAircraftCache.hxx b/src/GUI/LocalAircraftCache.hxx index db229fd84..3efb1f978 100644 --- a/src/GUI/LocalAircraftCache.hxx +++ b/src/GUI/LocalAircraftCache.hxx @@ -49,8 +49,6 @@ struct AircraftItem void toDataStream(QDataStream& ds) const; - QPixmap thumbnail(bool loadIfRequired = true) const; - int indexOfVariant(QUrl uri) const; bool excluded = false; @@ -75,7 +73,6 @@ struct AircraftItem QUrl supportUrl; QVariant status(int variant); private: - mutable QPixmap m_thumbnail; }; class LocalAircraftCache : public QObject @@ -126,7 +123,8 @@ public: PackageUpdateAvailable, PackageQueued, PackageDownloading, - NotPackaged + NotPackaged, + PackageInstallFailed }; Q_ENUMS(PackageStatus) diff --git a/src/GUI/LocationController.cxx b/src/GUI/LocationController.cxx index a11c0e11a..2e1a05eff 100644 --- a/src/GUI/LocationController.cxx +++ b/src/GUI/LocationController.cxx @@ -35,6 +35,7 @@ #include "LaunchConfig.hxx" #include "DefaultAircraftLocator.hxx" #include "NavaidSearchModel.hxx" +#include "CarriersLocationModel.hxx" #include #include @@ -53,10 +54,8 @@ const unsigned int MAX_RECENT_LOCATIONS = 64; QVariant savePositionList(const FGPositionedList& posList) { QVariantList vl; - FGPositionedList::const_iterator it; - for (it = posList.begin(); it != posList.end(); ++it) { + for (const auto& pos : posList) { QVariantMap vm; - FGPositionedRef pos = *it; vm.insert("ident", QString::fromStdString(pos->ident())); vm.insert("type", pos->type()); vm.insert("lat", pos->geod().getLatitudeDeg()); @@ -70,7 +69,7 @@ FGPositionedList loadPositionedList(QVariant v) { QVariantList vl = v.toList(); FGPositionedList result; - result.reserve(vl.size()); + result.reserve(static_cast(vl.size())); NavDataCache* cache = NavDataCache::instance(); Q_FOREACH(QVariant v, vl) { @@ -93,9 +92,10 @@ FGPositionedList loadPositionedList(QVariant v) LocationController::LocationController(QObject *parent) : QObject(parent) { - m_searchModel = new NavaidSearchModel; + m_searchModel = new NavaidSearchModel(this); m_detailQml = new QmlPositioned(this); m_baseQml = new QmlPositioned(this); + m_carriersModel = new CarriersLocationModel(this); m_defaultAltitude = QuantityValue{Units::FeetMSL, 6000}; m_defaultAirspeed = QuantityValue{Units::Knots, 120}; @@ -112,9 +112,7 @@ LocationController::LocationController(QObject *parent) : this, &LocationController::descriptionChanged); } -LocationController::~LocationController() -{ -} +LocationController::~LocationController() = default; void LocationController::setLaunchConfig(LaunchConfig *config) { @@ -196,35 +194,62 @@ void LocationController::setBaseGeod(QmlGeod geod) if (m_locationIsLatLon && (m_geodLocation == geod.geod())) return; + clearLocation(); m_locationIsLatLon = true; m_geodLocation = geod.geod(); + emit baseLocationChanged(); +} + +QString LocationController::carrierName() const +{ + return m_carrierName; +} + +void LocationController::setCarrierLocation(QString name) +{ + const auto cIndex = m_carriersModel->indexOf(name); + clearLocation(); + if (cIndex < 0) { + qWarning() << "invalid carrier name:" << name; + return; + } + + m_locationIsCarrier = true; + m_carrierName = name; + m_geodLocation = m_carriersModel->geodForIndex(cIndex); + m_carrierParkings = m_carriersModel->parkingsForIndex(cIndex); + + emit baseLocationChanged(); +} + +void LocationController::clearLocation() +{ + m_locationIsLatLon = false; + m_locationIsCarrier = false; m_location.clear(); + m_carrierName.clear(); m_airportLocation.clear(); m_detailLocation.clear(); + m_detailQml->setGuid(0); + m_baseQml->setGuid(0); + m_carrierParkings.clear(); + m_carrierParking.clear(); emit baseLocationChanged(); } void LocationController::setBaseLocation(QmlPositioned* pos) { if (!pos) { - m_location.clear(); - m_detailLocation.clear(); - m_detailQml->setGuid(0); - m_baseQml->setGuid(0); - m_airportLocation.clear(); - m_locationIsLatLon = false; - emit baseLocationChanged(); + clearLocation(); return; } if (pos->inner() == m_location) return; - m_locationIsLatLon = false; + clearLocation(); m_location = pos->inner(); m_baseQml->setGuid(pos->guid()); - m_detailLocation.clear(); - m_detailQml->setGuid(0); if (FGPositioned::isAirportType(m_location.ptr())) { m_airportLocation = static_cast(m_location.ptr()); @@ -249,7 +274,6 @@ void LocationController::setDetailLocation(QmlPositioned* pos) m_detailLocation.clear(); m_detailQml->setInner({}); } else { - qInfo() << Q_FUNC_INFO << "pos:" << pos->ident(); m_detailLocation = pos->inner(); m_useActiveRunway = false; m_useAvailableParking = false; @@ -261,7 +285,7 @@ void LocationController::setDetailLocation(QmlPositioned* pos) QmlGeod LocationController::baseGeod() const { - if (m_locationIsLatLon) + if (m_locationIsLatLon || m_locationIsCarrier) return m_geodLocation; if (m_location) @@ -368,6 +392,33 @@ QmlGeod LocationController::parseStringAsGeod(QString string) const return QmlGeod(g); } +QString LocationController::carrierParking() const +{ + if (!m_locationIsCarrier) + return {}; + return m_carrierParking; +} + +void LocationController::setCarrierParking(QString name) +{ + if (!m_locationIsCarrier) { + qWarning() << "active location is not a carrier"; + return; + } + + if (m_carrierParking == name) + return; + + if (!m_carrierParkings.contains(name)) { + qWarning() << "parking '" << name << "' not found in carrier parking list"; + return; + } + + m_carrierParking = name; + m_useCarrierFLOLS = false; + emit configChanged(); +} + QmlPositioned *LocationController::detail() const { return m_detailQml; @@ -378,6 +429,11 @@ QmlPositioned *LocationController::baseLocation() const return m_baseQml; } +QStringList LocationController::carrierParkings() const +{ + return m_carrierParkings; +} + void LocationController::setOffsetRadial(QuantityValue offsetRadial) { if (m_offsetRadial == offsetRadial) @@ -436,27 +492,26 @@ void LocationController::setUseAvailableParking(bool useAvailableParking) emit configChanged(); } +void LocationController::setUseCarrierFLOLS(bool useCarrierFLOLS) +{ + if (!m_locationIsCarrier) { + qWarning() << "location is not a carrier"; + return; + } + + if (m_useCarrierFLOLS == useCarrierFLOLS) + return; + + m_useCarrierFLOLS = useCarrierFLOLS; + m_carrierParking.clear(); + emit configChanged(); +} + void LocationController::restoreLocation(QVariantMap l) { - try { - if (l.contains("location-lat")) { - m_locationIsLatLon = true; - m_geodLocation = SGGeod::fromDeg(l.value("location-lon").toDouble(), - l.value("location-lat").toDouble()); - m_location.clear(); - m_airportLocation.clear(); - m_baseQml->setInner(nullptr); - } else if (l.contains("location-id")) { - m_location = NavDataCache::instance()->loadById(l.value("location-id").toULongLong()); - m_locationIsLatLon = false; - if (FGPositioned::isAirportType(m_location.ptr())) { - m_airportLocation = static_cast(m_location.ptr()); - } else { - m_airportLocation.clear(); - } - m_baseQml->setInner(m_location); - } + clearLocation(); + try { m_altitudeEnabled = l.contains("altitude"); m_speedEnabled = l.contains("speed"); m_headingEnabled = l.contains("heading"); @@ -470,10 +525,33 @@ void LocationController::restoreLocation(QVariantMap l) m_offsetDistance = l.value("offset-distance", QVariant::fromValue(m_defaultOffsetDistance)).value(); m_tuneNAV1 = l.value("tune-nav1-radio").toBool(); + if (l.contains("location-lat")) { + m_locationIsLatLon = true; + m_geodLocation = SGGeod::fromDeg(l.value("location-lon").toDouble(), + l.value("location-lat").toDouble()); + } else if (l.contains("carrier")) { + setCarrierLocation(l.value("carrier").toString()); + if (l.contains("carrier-flols")) { + setUseCarrierFLOLS(l.value("carrier-flols").toBool()); + // overwrite value form above, intentionally + m_offsetDistance = l.value("location-carrier-flols-distance", QVariant::fromValue(m_defaultOffsetDistance)).value(); + } else if (l.contains("carrier-parking")) { + setCarrierParking(l.value("carrier-parking").toString()); + } + } else if (l.contains("location-id")) { + m_location = NavDataCache::instance()->loadById(l.value("location-id").toLongLong()); + m_locationIsLatLon = false; + if (FGPositioned::isAirportType(m_location.ptr())) { + m_airportLocation = static_cast(m_location.ptr()); + } else { + m_airportLocation.clear(); + } + m_baseQml->setInner(m_location); + } + if (m_airportLocation) { m_useActiveRunway = false; m_useAvailableParking = false; - m_detailLocation.clear(); if (l.contains("location-apt-runway")) { QString runway = l.value("location-apt-runway").toString().toUpper(); @@ -502,10 +580,7 @@ void LocationController::restoreLocation(QVariantMap l) } // of location is an airport } catch (const sg_exception&) { qWarning() << "Errors restoring saved location, clearing"; - m_location.clear(); - m_airportLocation.clear(); - m_baseQml->setInner(nullptr); - m_offsetEnabled = false; + clearLocation(); } baseLocationChanged(); @@ -515,6 +590,10 @@ void LocationController::restoreLocation(QVariantMap l) bool LocationController::shouldStartPaused() const { + if (m_useCarrierFLOLS) { + return true; + } + if (!m_location) { return false; // defaults to on-ground at the default airport } @@ -535,6 +614,14 @@ QVariantMap LocationController::saveLocation() const if (m_locationIsLatLon) { locationSet.insert("location-lat", m_geodLocation.getLatitudeDeg()); locationSet.insert("location-lon", m_geodLocation.getLongitudeDeg()); + } else if (m_locationIsCarrier) { + locationSet.insert("carrier", m_carrierName); + if (m_useCarrierFLOLS) { + locationSet.insert("carrier-flols", true); + locationSet.insert("location-carrier-flols-distance", QVariant::fromValue(m_offsetDistance)); + } else if (!m_carrierParking.isEmpty()) { + locationSet.insert("carrier-parking", m_carrierParking); + } } else if (m_location) { locationSet.insert("location-id", static_cast(m_location->guid())); @@ -588,7 +675,7 @@ void LocationController::setLocationProperties() "runway-requested" << "navaid-id" << "offset-azimuth-deg" << "offset-distance-nm" << "glideslope-deg" << "speed-set" << "on-ground" << "airspeed-kt" << - "airport-id" << "runway" << "parkpos"; + "airport-id" << "runway" << "parkpos" << "carrier"; Q_FOREACH(QString s, props) { SGPropertyNode* c = presets->getChild(s.toStdString()); @@ -604,6 +691,8 @@ void LocationController::setLocationProperties() fgSetDouble("/position/longitude-deg", m_geodLocation.getLongitudeDeg()); applyPositionOffset(); + applyAltitude(); + applyAirspeed(); return; } @@ -612,6 +701,27 @@ void LocationController::setLocationProperties() fgSetDouble("/sim/presets/altitude-ft", -9999.0); fgSetDouble("/sim/presets/heading-deg", 9999.0); + if (m_locationIsCarrier) { + fgSetString("/sim/presets/carrier", m_carrierName.toStdString()); + + if (m_useCarrierFLOLS) { + fgSetBool("/sim/presets/runway-requested", true ); + // treat the FLOLS as a runway, for the purposes of communication with position-init + fgSetString("/sim/presets/runway", "FLOLS"); + fgSetDouble("/sim/presets/offset-distance-nm", m_offsetDistance.convertToUnit(Units::NauticalMiles).value); + + applyAirspeed(); + } else if (!m_carrierParking.isEmpty()) { + fgSetString("/sim/presets/parkpos", m_carrierParking.toStdString()); + } + + if (m_tuneNAV1) { + // tune TACAN to the carrier + qInfo() << "Implement TACAN tuning"; + } + return; + } + if (!m_location) { return; } @@ -681,7 +791,7 @@ void LocationController::setLocationProperties() break; default: break; - }; + } // set disambiguation property globals->get_props()->setIntValue("/sim/presets/navaid-id", @@ -796,6 +906,21 @@ void LocationController::onCollectConfig() return; } + if (m_locationIsCarrier) { + m_config->setArg("carrier", m_carrierName); + + if (!m_carrierParking.isEmpty()) { + m_config->setArg("parkpos", m_carrierParking); + } else if (m_useCarrierFLOLS) { + m_config->setArg("runway", QStringLiteral("FLOLS")); + const double offsetNm = m_offsetDistance.convertToUnit(Units::NauticalMiles).value; + m_config->setArg("offset-distance", QString::number(offsetNm)); + applyAirspeed(); + } + + return; + } + if (!m_location) { return; } @@ -852,7 +977,7 @@ void LocationController::onCollectConfig() break; default: break; - }; + } // set disambiguation property m_config->setProperty("/sim/presets/navaid-id", QString::number(m_location->guid())); @@ -912,6 +1037,11 @@ QString LocationController::description() const return tr("at position %1").arg(QString::fromStdString(s)); } + if (m_locationIsCarrier) { + QString pennant = m_carriersModel->pennantForIndex(m_carriersModel->indexOf(m_carrierName)); + return tr("on carrier %1 (%2)").arg(m_carrierName).arg(pennant); + } + return tr("No location selected"); } @@ -952,7 +1082,7 @@ QString LocationController::description() const if (m_offsetEnabled) { offsetDesc = tr("%1nm %2 of"). arg(offsetNm, 0, 'f', 1). - arg(compassPointFromHeading(m_offsetRadial.value)); + arg(compassPointFromHeading(static_cast(m_offsetRadial.value))); } QString navaidType; diff --git a/src/GUI/LocationController.hxx b/src/GUI/LocationController.hxx index b54b88f84..1310b0ea3 100644 --- a/src/GUI/LocationController.hxx +++ b/src/GUI/LocationController.hxx @@ -32,6 +32,7 @@ #include "UnitsModel.hxx" class NavaidSearchModel; +class CarriersLocationModel; class LocationController : public QObject { @@ -40,6 +41,7 @@ class LocationController : public QObject Q_PROPERTY(QString description READ description NOTIFY descriptionChanged) Q_PROPERTY(NavaidSearchModel* searchModel MEMBER m_searchModel CONSTANT) + Q_PROPERTY(CarriersLocationModel* carriersModel MEMBER m_carriersModel CONSTANT) Q_PROPERTY(QList airportRunways READ airportRunways NOTIFY baseLocationChanged) Q_PROPERTY(QList airportParkings READ airportParkings NOTIFY baseLocationChanged) @@ -68,6 +70,13 @@ class LocationController : public QObject Q_PROPERTY(QmlPositioned* detail READ detail CONSTANT) Q_PROPERTY(bool isBaseLatLon READ isBaseLatLon NOTIFY baseLocationChanged) + Q_PROPERTY(bool isCarrier READ isCarrier NOTIFY baseLocationChanged) + Q_PROPERTY(QString carrier READ carrierName WRITE setCarrierLocation NOTIFY baseLocationChanged) + Q_PROPERTY(QStringList carrierParkings READ carrierParkings NOTIFY baseLocationChanged) + Q_PROPERTY(bool useCarrierFLOLS READ useCarrierFLOLS WRITE setUseCarrierFLOLS NOTIFY configChanged) + Q_PROPERTY(QString carrierParking READ carrierParking WRITE setCarrierParking NOTIFY configChanged) + + // allow collecting the location properties to be disabled, if the // user is setting conflicting ones Q_PROPERTY(bool skipFromArgs MEMBER m_skipFromArgs NOTIFY skipFromArgsChanged) @@ -110,6 +119,9 @@ public: QmlGeod baseGeod() const; void setBaseGeod(QmlGeod geod); + QString carrierName() const; + void setCarrierLocation(QString name); + bool isAirportLocation() const; bool offsetEnabled() const @@ -138,6 +150,9 @@ public: Q_INVOKABLE QmlGeod parseStringAsGeod(QString string) const; + QString carrierParking() const; + void setCarrierParking(QString name); + bool tuneNAV1() const { return m_tuneNAV1; @@ -161,6 +176,19 @@ public: { return m_altitude; } + + bool isCarrier() const + { + return m_locationIsCarrier; + } + + QStringList carrierParkings() const; + + bool useCarrierFLOLS() const + { + return m_useCarrierFLOLS; + } + public slots: void setOffsetRadial(QuantityValue offsetRadial); @@ -174,6 +202,8 @@ public slots: void setUseAvailableParking(bool useAvailableParking); + void setUseCarrierFLOLS(bool useCarrierFLOLS); + Q_SIGNALS: void descriptionChanged(); void offsetChanged(); @@ -186,6 +216,7 @@ private Q_SLOTS: void onRestoreCurrentLocation(); void onSaveCurrentLocation(); private: + void clearLocation(); void onSearchComplete(); void addToRecent(FGPositionedRef pos); @@ -197,12 +228,15 @@ private: void applyOnFinal(); NavaidSearchModel* m_searchModel = nullptr; + CarriersLocationModel* m_carriersModel = nullptr; FGPositionedRef m_location; FGAirportRef m_airportLocation; // valid if m_location is an FGAirport FGPositionedRef m_detailLocation; // parking stand or runway detail bool m_locationIsLatLon = false; SGGeod m_geodLocation; + bool m_locationIsCarrier = false; + QString m_carrierName; FGPositionedList m_recentLocations; LaunchConfig* m_config = nullptr; @@ -227,6 +261,10 @@ private: bool m_speedEnabled = false; bool m_altitudeEnabled = false; bool m_skipFromArgs = false; + + bool m_useCarrierFLOLS = false; + QString m_carrierParking; + QStringList m_carrierParkings; }; #endif // LOCATION_CONTROLLER_HXX diff --git a/src/GUI/MapWidget.cxx b/src/GUI/MapWidget.cxx index b73bee8a8..f0d4b2848 100644 --- a/src/GUI/MapWidget.cxx +++ b/src/GUI/MapWidget.cxx @@ -2002,7 +2002,7 @@ MapWidget::DrawAIObject::DrawAIObject(SGPropertyNode* m, const SGGeod& g) : heading = model->getDoubleValue("orientation/true-heading-deg"); if ((name == "aircraft") || (name == "multiplayer") || - (name == "wingman") || (name == "tanker")) + (name == "wingman") || (name == "tanker") || (name == "swift")) { speedKts = static_cast(model->getDoubleValue("velocities/true-airspeed-kt")); label = model->getStringValue("callsign", "<>"); diff --git a/src/GUI/ModelDataExtractor.cxx b/src/GUI/ModelDataExtractor.cxx index a05efb7c0..b34ba80ca 100644 --- a/src/GUI/ModelDataExtractor.cxx +++ b/src/GUI/ModelDataExtractor.cxx @@ -24,35 +24,49 @@ QVariant ModelDataExtractor::data() const return m_model->data(m, role); } - if (m_value.isArray()) { + if (!m_stringsModel.empty()) { + if ((m_index < 0) || (m_index >= m_stringsModel.size())) + return {}; + + return m_stringsModel.at(m_index); + } + + if (m_rawModel.isArray()) { quint32 uIndex = static_cast(m_index); - auto v = m_value.property(uIndex); + auto v = m_rawModel.property(uIndex); if (v.isQObject()) { // handle the QList case auto obj = v.toQObject(); return obj->property(m_role.toUtf8().constData()); } - return m_value.property(uIndex).toVariant(); + return m_rawModel.property(uIndex).toVariant(); } + qWarning() << "Unable to convert model data:" << m_rawModel.toString(); return {}; } -void ModelDataExtractor::setModel(QJSValue model) +void ModelDataExtractor::clear() { - if (m_value.equals(model)) - return; - if (m_model) { // disconnect from everything disconnect(m_model, nullptr, this, nullptr); m_model = nullptr; } - m_value = model; - if (m_value.isQObject()) { - m_model = qobject_cast(m_value.toQObject()); +} + +void ModelDataExtractor::setModel(QJSValue raw) +{ + if (m_rawModel.strictlyEquals(raw)) + return; + + clear(); + m_rawModel = raw; + + if (raw.isQObject()) { + m_model = qobject_cast(raw.toQObject()); if (m_model) { connect(m_model, &QAbstractItemModel::modelReset, this, &ModelDataExtractor::dataChanged); @@ -61,10 +75,24 @@ void ModelDataExtractor::setModel(QJSValue model) // ToDo: handle rows added / removed } else { - qWarning() << "object but not a QAIM" << m_value.toQObject(); + qWarning() << "object but not a QAIM" << raw.toQObject(); + } + } else if (raw.isArray()) { + + } else if (raw.isVariant() || raw.isObject()) { + // special case the QStringList case + + // for reasons I don't understand yet, QStringList returned as a + // property value to JS, does not show up as a variant-in-JS-Value above + // (so ::isVariant returns false), but conversion to a variant + // works. Hence the 'raw.isObject' above + + const auto var = raw.toVariant(); + if (var.type() == QVariant::StringList) { + m_stringsModel = var.toStringList(); + } else { + qWarning() << Q_FUNC_INFO << "variant but not a QStringList" << var; } - } else { - // might be null, or an array } emit modelChanged(); diff --git a/src/GUI/ModelDataExtractor.hxx b/src/GUI/ModelDataExtractor.hxx index 8c673b5b1..dca4afa1b 100644 --- a/src/GUI/ModelDataExtractor.hxx +++ b/src/GUI/ModelDataExtractor.hxx @@ -20,7 +20,7 @@ public: QJSValue model() const { - return m_value; + return m_rawModel; } int index() const @@ -52,8 +52,12 @@ private slots: void onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); private: + void clear(); + QAbstractItemModel* m_model = nullptr; - QJSValue m_value; + QJSValue m_rawModel; + QStringList m_stringsModel; + int m_index = 0; QString m_role; }; diff --git a/src/GUI/NavaidSearchModel.cxx b/src/GUI/NavaidSearchModel.cxx index 4a4a9232f..d67e0f38f 100644 --- a/src/GUI/NavaidSearchModel.cxx +++ b/src/GUI/NavaidSearchModel.cxx @@ -34,6 +34,9 @@ QString fixNavaidName(QString s) QStringList words = s.split(QChar(' ')); QStringList changedWords; Q_FOREACH(QString w, words) { + if (w.isEmpty()) + continue; + QString up = w.toUpper(); // expand common abbreviations @@ -150,10 +153,17 @@ void NavaidSearchModel::clear() qlonglong NavaidSearchModel::guidAtIndex(int index) const { - if ((index < 0) || (index >= m_ids.size())) + const size_t uIndex = static_cast(index); + if ((index < 0) || (uIndex >= m_ids.size())) return 0; - return m_ids.at(index); + return m_ids.at(uIndex); +} + +NavaidSearchModel::NavaidSearchModel(QObject *parent) : + QAbstractListModel(parent) +{ + } void NavaidSearchModel::setSearch(QString t, NavaidSearchModel::AircraftType aircraft) diff --git a/src/GUI/NavaidSearchModel.hxx b/src/GUI/NavaidSearchModel.hxx index fa58755a9..0ca9c7915 100644 --- a/src/GUI/NavaidSearchModel.hxx +++ b/src/GUI/NavaidSearchModel.hxx @@ -51,7 +51,7 @@ class NavaidSearchModel : public QAbstractListModel }; public: - NavaidSearchModel() { } + NavaidSearchModel(QObject* parent = nullptr); enum AircraftType { diff --git a/src/GUI/PathListModel.cxx b/src/GUI/PathListModel.cxx new file mode 100644 index 000000000..a52313538 --- /dev/null +++ b/src/GUI/PathListModel.cxx @@ -0,0 +1,188 @@ +#include "PathListModel.hxx" + +#include +#include + +PathListModel::PathListModel(QObject *pr) : + QAbstractListModel(pr) +{ + +} + +PathListModel::~PathListModel() +{ + +} + +void PathListModel::loadFromSettings(QString key) +{ + QSettings settings; + if (!settings.contains(key)) + return; + + QVariantList vl = settings.value(key).toList(); + mPaths.clear(); + mPaths.reserve(static_cast(vl.size())); + + beginResetModel(); + + Q_FOREACH(QVariant v, vl) { + QVariantMap m = v.toMap(); + PathEntry entry; + entry.path = m.value("path").toString(); + if (entry.path.isEmpty()) { + continue; + } + + entry.enabled = m.value("enabled", QVariant{true}).toBool(); + mPaths.push_back(entry); + } + + endResetModel(); + emit enabledPathsChanged(); + emit countChanged(); +} + +void PathListModel::saveToSettings(QString key) const +{ + QVariantList vl; + for (const auto &e : mPaths) { + QVariantMap v; + v["path"] = e.path; + v["enabled"] = e.enabled; + vl.append(v); + } + + QSettings settings; + settings.setValue(key, vl); +} + +QStringList PathListModel::readEnabledPaths(QString settingsKey) +{ + QSettings settings; + if (!settings.contains(settingsKey)) + return {}; + + QStringList result; + QVariantList vl = settings.value(settingsKey).toList(); + Q_FOREACH(QVariant v, vl) { + QVariantMap m = v.toMap(); + if (!m.value("enabled").toBool()) + continue; + + result.append(m.value("path").toString()); + } + + return result; +} + +QStringList PathListModel::enabledPaths() const +{ + QStringList result; + for (const auto& e : mPaths) { + if (e.enabled) { + result.append(e.path); + } + } + return result; +} + +int PathListModel::count() +{ + return static_cast(mPaths.size()); +} + +int PathListModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return static_cast(mPaths.size()); +} + +QVariant PathListModel::data(const QModelIndex &index, int role) const +{ + int row = index.row(); + const auto& entry = mPaths.at(static_cast(row)); + switch (role) { + case Qt::DisplayRole: + case PathRole: + return entry.path; + + case PathEnabledRole: + return entry.enabled; + + default: + break; + } + + return {}; +} + +bool PathListModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + int row = index.row(); + auto& entry = mPaths.at(static_cast(row)); + if (role == PathEnabledRole) { + entry.enabled = value.toBool(); + emit dataChanged(index, index, {PathEnabledRole}); + emit enabledPathsChanged(); + return true; + } + + return false; +} + +QHash PathListModel::roleNames() const +{ + QHash result = QAbstractListModel::roleNames(); + result[Qt::DisplayRole] = "path"; + result[PathEnabledRole] = "enabled"; + return result; +} + +void PathListModel::removePath(int index) +{ + if ((index < 0) || (index >= static_cast(mPaths.size()))) { + qWarning() << Q_FUNC_INFO << "index invalid:" << index; + return; + } + + beginRemoveRows({}, index, index); + auto it = mPaths.begin() + index; + mPaths.erase(it); + endRemoveRows(); + emit enabledPathsChanged(); + emit countChanged(); +} + +void PathListModel::appendPath(QString path) +{ + PathEntry entry; + entry.path = path; + entry.enabled = true; // enable by default + const int newRow = static_cast(mPaths.size()); + beginInsertRows({}, newRow, newRow); + mPaths.push_back(entry); + endInsertRows(); + emit enabledPathsChanged(); + emit countChanged(); +} + +void PathListModel::swapIndices(int indexA, int indexB) +{ + if ((indexA < 0) || (indexA >= static_cast(mPaths.size()))) { + qWarning() << Q_FUNC_INFO << "index invalid:" << indexA; + return; + } + + if ((indexB < 0) || (indexB >= static_cast(mPaths.size()))) { + qWarning() << Q_FUNC_INFO << "index invalid:" << indexB; + return; + } + + std::swap(mPaths[static_cast(indexA)], + mPaths[static_cast(indexB)]); + emit dataChanged(index(indexA), index(indexA)); + emit dataChanged(index(indexB), index(indexB)); + emit enabledPathsChanged(); +} + diff --git a/src/GUI/PathListModel.hxx b/src/GUI/PathListModel.hxx new file mode 100644 index 000000000..3ecd78407 --- /dev/null +++ b/src/GUI/PathListModel.hxx @@ -0,0 +1,56 @@ +#ifndef PATHLISTMODEL_HXX +#define PATHLISTMODEL_HXX + +#include + +#include + +const int PathRole = Qt::UserRole + 1; +const int PathEnabledRole = Qt::UserRole + 2; + +class PathListModel : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY(int count READ count NOTIFY countChanged) +public: + + PathListModel(QObject* pr); + ~PathListModel() override; + + void loadFromSettings(QString key); + void saveToSettings(QString key) const; + + int rowCount(const QModelIndex& parent) const override; + + QVariant data(const QModelIndex& index, int role) const override; + + bool setData(const QModelIndex &index, const QVariant &value, int role) override; + + QHash roleNames() const override; + + static QStringList readEnabledPaths(QString settingsKey); + + QStringList enabledPaths() const; + + int count(); +signals: + void enabledPathsChanged(); + void countChanged(); + +public slots: + void removePath(int index); + void appendPath(QString path); + + void swapIndices(int indexA, int indexB); + +private: + struct PathEntry { + QString path; + bool enabled = true; + }; + + std::vector mPaths; +}; + +#endif // PATHLISTMODEL_HXX diff --git a/src/GUI/QtFileDialog.cxx b/src/GUI/QtFileDialog.cxx index 71a54edc9..deb65a9a7 100644 --- a/src/GUI/QtFileDialog.cxx +++ b/src/GUI/QtFileDialog.cxx @@ -38,16 +38,6 @@ QtFileDialog::~QtFileDialog() {} void QtFileDialog::exec() { - int fakeargc = 1; - static char fakeargv0[] = "fgfs"; - static char * fakeargv[2] = {fakeargv0, 0}; - // This does nothing if it has already been run, so the fake argc/argv are - // only used if run without launcher. Don't attempt to initialize the - // QSettings, because this would require FGGlobals to be initialized (for - // globals->get_fg_home()), which would prevent using this function at - // early startup. - flightgear::initApp(fakeargc, fakeargv, false /* doInitQSettings */); - // concatenate filter patterns, as Qt uses a single string std::string filter=""; for( string_list::const_iterator it = _filterPatterns.begin(); it != _filterPatterns.end();++it ) { diff --git a/src/GUI/QtLauncher.cxx b/src/GUI/QtLauncher.cxx index a7f1133a3..c1e18e5ce 100644 --- a/src/GUI/QtLauncher.cxx +++ b/src/GUI/QtLauncher.cxx @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,7 @@ #include "LauncherMainWindow.hxx" #include "LaunchConfig.hxx" #include "UnitsModel.hxx" +#include "PathListModel.hxx" using namespace flightgear; using namespace simgear::pkg; @@ -354,19 +356,22 @@ void initQSettings() bool checkKeyboardModifiersForSettingFGRoot() { initQSettings(); - +#if defined(Q_OS_WIN) + const auto altState = GetAsyncKeyState(VK_MENU); + const auto shiftState = GetAsyncKeyState(VK_SHIFT); + if ((altState < 0) || (shiftState < 0)) +#else Qt::KeyboardModifiers mods = qApp->queryKeyboardModifiers(); - if (mods & (Qt::AltModifier | Qt::ShiftModifier)) { + if (mods & (Qt::AltModifier | Qt::ShiftModifier)) +#endif + { qWarning() << "Alt/shift pressed during launch"; - QSettings settings; - settings.setValue("fg-root", "!ask"); return true; } return false; } - void restartTheApp() { QStringList fgArgs; @@ -403,7 +408,7 @@ void launcherSetSceneryPaths() // positions QSettings settings; // append explicit scenery paths - Q_FOREACH(QString path, settings.value("scenery-paths").toStringList()) { + Q_FOREACH(QString path, PathListModel::readEnabledPaths("scenery-paths-v2")) { globals->append_fg_scenery(path.toStdString()); } @@ -451,6 +456,15 @@ bool runLauncherDialog() fgInitPackageRoot(); + // setup package language + auto lang = options->valueForOption("language"); + if (!lang.empty()) { + globals->packageRoot()->setLocale(lang); + } else { + const auto langName = QLocale::languageToString(QLocale{}.language()); + globals->packageRoot()->setLocale(langName.toStdString()); + } + // startup the HTTP system now since packages needs it FGHTTPClient* http = globals->add_new_subsystem(); diff --git a/src/GUI/SetupRootDialog.cxx b/src/GUI/SetupRootDialog.cxx old mode 100644 new mode 100755 index 756380878..685db1e61 --- a/src/GUI/SetupRootDialog.cxx +++ b/src/GUI/SetupRootDialog.cxx @@ -40,7 +40,9 @@ #include #include -static QString rootPathKey() +#include "QtLauncher.hxx" + +QString SetupRootDialog::rootPathKey() { // return a settings key like fg-root-2018-3-0 return QString("fg-root-") + QString(FLIGHTGEAR_VERSION).replace('.', '-'); @@ -99,7 +101,8 @@ SGPath SetupRootDialog::restoreUserSelectedRoot() { QSettings settings; QString path = settings.value(rootPathKey()).toString(); - if (path == "!ask") { + bool ask = flightgear::checkKeyboardModifiersForSettingFGRoot(); + if (ask || (path == QStringLiteral("!ask"))) { bool ok = runDialog(ManualChoiceRequested); Q_ASSERT(ok); // run dialog either exit()s or sets fg_root, so this @@ -108,7 +111,7 @@ SGPath SetupRootDialog::restoreUserSelectedRoot() } if (path.isEmpty()) { - return std::string(); // use the default path + return SGPath{}; // use the default path } if (validatePath(path) && validateVersion(path)) { @@ -118,7 +121,7 @@ SGPath SetupRootDialog::restoreUserSelectedRoot() // let's see if the default root is acceptable, in which case we will // switch to it. (This gives a more friendly upgrade experience). if (defaultRootAcceptable()) { - return std::string(); // use the default path + return SGPath{}; // use the default path } // okay, we don't have an acceptable FG_DATA anywhere we can find, we @@ -131,6 +134,13 @@ SGPath SetupRootDialog::restoreUserSelectedRoot() } } +void SetupRootDialog::askRootOnNextLaunch() +{ + QSettings settings; + // set the option to the magic marker value + settings.setValue(rootPathKey(), "!ask"); +} + bool SetupRootDialog::validatePath(QString path) { // check assorted files exist in the root location, to avoid any chance of @@ -216,7 +226,7 @@ void SetupRootDialog::onUseDefaults() m_browsedPath = QString::fromStdString(r.utf8Str()); globals->set_fg_root(r); QSettings settings; - settings.remove("fg-root"); // remove any setting + settings.remove(rootPathKey()); // remove any setting accept(); } diff --git a/src/GUI/SetupRootDialog.hxx b/src/GUI/SetupRootDialog.hxx index d294c1938..79f758e3f 100644 --- a/src/GUI/SetupRootDialog.hxx +++ b/src/GUI/SetupRootDialog.hxx @@ -41,6 +41,10 @@ public: static bool runDialog(bool usingDefaultRoot); static SGPath restoreUserSelectedRoot(); + + static void askRootOnNextLaunch(); + + static QString rootPathKey(); private slots: void onBrowse(); diff --git a/src/GUI/ThumbnailImageItem.cxx b/src/GUI/ThumbnailImageItem.cxx index b4a24be6a..a2444470e 100644 --- a/src/GUI/ThumbnailImageItem.cxx +++ b/src/GUI/ThumbnailImageItem.cxx @@ -27,30 +27,33 @@ public: void startInstall(pkg::InstallRef) override {} void installProgress(pkg::InstallRef, unsigned int, unsigned int) override {} void finishInstall(pkg::InstallRef, StatusCode ) override {} - void dataForThumbnail(const std::string& aThumbnailUrl, - size_t length, const uint8_t* bytes) override - { - if (aThumbnailUrl != owner->url().toString().toStdString()) { - return; - } - - QImage img = QImage::fromData(QByteArray::fromRawData(reinterpret_cast(bytes), length)); - if (img.isNull()) { - if (length > 0) { - // warn if we had valid bytes but couldn't load it, i.e corrupted data or similar - qWarning() << "failed to load image data for URL:" << QString::fromStdString(aThumbnailUrl); - owner->clearImage(); - } - return; - } - - owner->setImage(img); - } + size_t length, const uint8_t* bytes) override; ThumbnailImageItem* owner; }; +void ThumbnailImageItem::ThumbnailPackageDelegate::dataForThumbnail(const std::string& aThumbnailUrl, + size_t length, const uint8_t* bytes) +{ + if (aThumbnailUrl != owner->url().toString().toStdString()) { + return; + } + + const auto iLength = static_cast(length); + QImage img = QImage::fromData(QByteArray::fromRawData(reinterpret_cast(bytes), iLength)); + if (img.isNull()) { + if (length > 0) { + // warn if we had valid bytes but couldn't load it, i.e corrupted data or similar + qWarning() << "failed to load image data for URL:" << QString::fromStdString(aThumbnailUrl); + owner->clearImage(); + } + return; + } + + owner->setImage(img); +} + ThumbnailImageItem::ThumbnailImageItem(QQuickItem* parent) : QQuickItem(parent), m_delegate(new ThumbnailPackageDelegate(this)), @@ -81,7 +84,7 @@ QSGNode *ThumbnailImageItem::updatePaintNode(QSGNode* oldNode, QQuickItem::Updat textureNode->setOwnsTexture(true); } - QSGTexture* tex = window()->createTextureFromImage(m_image); + QSGTexture* tex = window()->createTextureFromImage(m_image, QQuickWindow::TextureIsOpaque); textureNode->setTexture(tex); textureNode->markDirty(QSGBasicGeometryNode::DirtyMaterial); m_imageDirty = false; @@ -123,7 +126,7 @@ void ThumbnailImageItem::setAircraftUri(QString uri) pkg::Root* root = globals->packageRoot(); pkg::PackageRef package = root->getPackageById(packageId); if (package) { - int variant = package->indexOfVariant(packageId); + auto variant = package->indexOfVariant(packageId); const auto thumbnail = package->thumbnailForVariant(variant); m_imageUrl = QUrl(QString::fromStdString(thumbnail.url)); if (m_imageUrl.isValid()) { diff --git a/src/GUI/WindowsFileDialog.cxx b/src/GUI/WindowsFileDialog.cxx index 8a5b2ff97..077b8f8f1 100644 --- a/src/GUI/WindowsFileDialog.cxx +++ b/src/GUI/WindowsFileDialog.cxx @@ -47,9 +47,8 @@ static int CALLBACK BrowseFolderCallback( if (uMsg == BFFM_INITIALIZED) { // set the initial directory now WindowsFileDialog* dlg = reinterpret_cast(lpData); - std::string s = dlg->getDirectory().local8BitStr(); - LPCTSTR path = s.c_str(); - ::SendMessage(hwnd, BFFM_SETSELECTION, true, (LPARAM) path); + const auto w = dlg->getDirectory().wstr(); + ::SendMessageW(hwnd, BFFM_SETSELECTIONW, true, (LPARAM) w.c_str()); } return 0; } diff --git a/src/GUI/assets/aircraft-carrier-icon.png b/src/GUI/assets/aircraft-carrier-icon.png new file mode 100644 index 000000000..1597e0a56 Binary files /dev/null and b/src/GUI/assets/aircraft-carrier-icon.png differ diff --git a/src/GUI/assets/icons8-cargo-ship-50.png b/src/GUI/assets/icons8-cargo-ship-50.png new file mode 100644 index 000000000..d1de899f3 Binary files /dev/null and b/src/GUI/assets/icons8-cargo-ship-50.png differ diff --git a/src/GUI/assets/icons8-christmas-star-filled.png b/src/GUI/assets/icons8-christmas-star-filled.png new file mode 100644 index 000000000..1c54e821a Binary files /dev/null and b/src/GUI/assets/icons8-christmas-star-filled.png differ diff --git a/src/GUI/assets/icons8-christmas-star-outline.png b/src/GUI/assets/icons8-christmas-star-outline.png new file mode 100644 index 000000000..9b75eb27b Binary files /dev/null and b/src/GUI/assets/icons8-christmas-star-outline.png differ diff --git a/src/GUI/assets/icons8-hide-50.png b/src/GUI/assets/icons8-hide-50.png new file mode 100644 index 000000000..d0c37a022 Binary files /dev/null and b/src/GUI/assets/icons8-hide-50.png differ diff --git a/src/GUI/fonts.cxx b/src/GUI/fonts.cxx index 6b7b858fd..ee9f0f591 100644 --- a/src/GUI/fonts.cxx +++ b/src/GUI/fonts.cxx @@ -3,7 +3,9 @@ #endif #include -#include + +#include "fnt.h" + /** * fonts.cxx generated by the genfonts utility by Pawel W. Olszta. diff --git a/src/GUI/qml/AddOns.qml b/src/GUI/qml/AddOns.qml index cf32c38a5..4c96acc3b 100644 --- a/src/GUI/qml/AddOns.qml +++ b/src/GUI/qml/AddOns.qml @@ -102,12 +102,7 @@ Item { description: qsTr("To use aircraft you download yourself, FlightGear needs to " + "know the folder(s) containing the aircraft data.") showAddButton: true - onAdd: { - var newPath =_addOns.addAircraftPath(); - if (newPath !== "") { - _addOns.aircraftPaths.push(newPath) - } - } + onAdd: _addOns.addAircraftPath(); } Rectangle { @@ -127,21 +122,10 @@ Item { model: _addOns.aircraftPaths delegate: PathListDelegate { width: aircraftPathsColumn.width - deletePromptText: qsTr("Remove the aircraft folder: '%1' from the list? (The folder contents will not be changed)").arg(modelData); - modelCount: _addOns.aircraftPaths.length - - onPerformDelete: { - var modifiedPaths = _addOns.aircraftPaths.slice() - modifiedPaths.splice(model.index, 1); - _addOns.aircraftPaths = modifiedPaths; - } - - onPerformMove: { - var modifiedPaths = _addOns.aircraftPaths.slice() - modifiedPaths.splice(model.index, 1); - modifiedPaths.splice(newIndex, 0, modelData) - _addOns.aircraftPaths = modifiedPaths; - } + deletePromptText: qsTr("Remove the aircraft folder: '%1' from the list? (The folder contents will not be changed)").arg(model.path); + modelCount: _addOns.aircraftPaths.count + onPerformDelete: _addOns.aircraftPaths.removePath(model.index) + onPerformMove: _addOns.aircraftPaths.swapIndices(model.index, newIndex); } } @@ -241,12 +225,7 @@ Item { "to know the folders containing the scenery data. " + "Adjust the order of the list to control which scenery is used in a region."); showAddButton: true - onAdd: { - var newPath =_addOns.addSceneryPath(); - if (newPath !== "") { - _addOns.sceneryPaths.push(newPath) - } - } + onAdd: _addOns.addSceneryPath(); } Rectangle { @@ -267,21 +246,10 @@ Item { delegate: PathListDelegate { width: sceneryPathsColumn.width - deletePromptText: qsTr("Remove the scenery folder: '%1' from the list? (The folder contents will not be changed)").arg(modelData); - modelCount: _addOns.sceneryPaths.length - - onPerformDelete: { - var modifiedPaths = _addOns.sceneryPaths.slice() - modifiedPaths.splice(model.index, 1); - _addOns.sceneryPaths = modifiedPaths; - } - - onPerformMove: { - var modifiedPaths = _addOns.sceneryPaths.slice() - modifiedPaths.splice(model.index, 1); - modifiedPaths.splice(newIndex, 0, modelData) - _addOns.sceneryPaths = modifiedPaths; - } + deletePromptText: qsTr("Remove the scenery folder: '%1' from the list? (The folder contents will not be changed)").arg(model.path); + modelCount: _addOns.sceneryPaths.count + onPerformDelete: _addOns.sceneryPaths.removePath(model.index) + onPerformMove: _addOns.sceneryPaths.swapIndices(model.index, newIndex); } } @@ -304,14 +272,10 @@ Item { var path = _addOns.installCustomScenery(); if (path !== "") { // insert into scenery paths if not already present - var sceneryPaths = _addOns.sceneryPaths - for (var i = 0; i < sceneryPaths.length; i++) { - if (sceneryPaths[i] === path) - return; // found, we are are done - } + // not found, add it - _addOns.sceneryPaths.push(path); + _addOns.sceneryPaths.appendPath(path); } } } diff --git a/src/GUI/qml/AddOnsDelegate.qml b/src/GUI/qml/AddOnsDelegate.qml index 2839b9d19..c74c66c2b 100644 --- a/src/GUI/qml/AddOnsDelegate.qml +++ b/src/GUI/qml/AddOnsDelegate.qml @@ -41,12 +41,9 @@ Item { width: delegateRoot.width Checkbox { - id: chkbox + id: enableCheckbox checked: model.enable - width: 30 anchors.left: parent.left - anchors.right: addonsDelegateHover.left - anchors.rightMargin: Style.margin height: contentRect.height onCheckedChanged: { _addOns.modules.enable(model.index, checked) @@ -59,7 +56,7 @@ Item { anchors.top: parent.top anchors.bottom: parent.bottom anchors.right: parent.right - anchors.left: chkbox.right + anchors.left: enableCheckbox.right hoverEnabled: true acceptedButtons: Qt.NoButton diff --git a/src/GUI/qml/AircraftCompactDelegate.qml b/src/GUI/qml/AircraftCompactDelegate.qml index 2d86400b4..b97daee98 100644 --- a/src/GUI/qml/AircraftCompactDelegate.qml +++ b/src/GUI/qml/AircraftCompactDelegate.qml @@ -1,5 +1,7 @@ import QtQuick 2.4 import FlightGear.Launcher 1.0 +import FlightGear 1.0 + import "." Item { @@ -77,19 +79,39 @@ Item { spacing: Style.margin - AircraftVariantChoice { - id: titleBox - + Item { + height: titleBox.height width: parent.width - aircraft: model.uri; - currentIndex: model.activeVariant - onSelected: { - model.activeVariant = index - root.select(model.uri) + FavouriteToggleButton { + id: favourite + checked: model.favourite + anchors.verticalCenter: parent.verticalCenter + onToggle: { + model.favourite = on; + } + } + + AircraftVariantChoice { + id: titleBox + + anchors { + left: favourite.right + leftMargin: Style.margin + right: parent.right + } + + aircraft: model.uri; + currentIndex: model.activeVariant + onSelected: { + model.activeVariant = index + root.select(model.uri) + } } } + + StyledText { id: description width: parent.width diff --git a/src/GUI/qml/AircraftGridDelegate.qml b/src/GUI/qml/AircraftGridDelegate.qml index 99ad7eb33..0d8f4b7f6 100644 --- a/src/GUI/qml/AircraftGridDelegate.qml +++ b/src/GUI/qml/AircraftGridDelegate.qml @@ -1,5 +1,6 @@ import QtQuick 2.4 import FlightGear.Launcher 1.0 +import FlightGear 1.0 import "." Item { @@ -87,10 +88,21 @@ Item { } } - MouseArea { + FavouriteToggleButton { + id: favourite + anchors { + left: parent.left + top: parent.top + margins: Style.margin + } + + visible: hover.containsMouse || model.favourite + checked: model.favourite + onToggle: { model.favourite = on; } + } + + HoverArea { id: hover anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.NoButton } } // of root item diff --git a/src/GUI/qml/AircraftGridView.qml b/src/GUI/qml/AircraftGridView.qml index 5af2cf11a..04dd63496 100644 --- a/src/GUI/qml/AircraftGridView.qml +++ b/src/GUI/qml/AircraftGridView.qml @@ -11,6 +11,9 @@ Item { function updateSelectionFromLauncher() { + if (!model) + return; + var row = model.indexForURI(_launcher.selectedAircraft); if (row >= 0) { view.currentIndex = row; diff --git a/src/GUI/qml/AircraftList.qml b/src/GUI/qml/AircraftList.qml index 1dbf327e2..516ab8fa1 100644 --- a/src/GUI/qml/AircraftList.qml +++ b/src/GUI/qml/AircraftList.qml @@ -17,6 +17,10 @@ FocusScope } } + Component.onCompleted: { + _launcher.browseAircraftModel.loadRatingsSettings(); + } + Rectangle { id: tabBar @@ -24,6 +28,7 @@ FocusScope width: parent.width GridToggleButton { + id: gridModeToggle anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: Style.margin @@ -45,6 +50,16 @@ FocusScope active: root.state == "installed" } + TabButton { + id: favouritesButton + text: qsTr("Favourites") + onClicked: { + root.state = "favourites" + root.updateSelectionFromLauncher(); + } + active: root.state == "favourites" + } + TabButton { id: browseButton text: qsTr("Browse") @@ -128,7 +143,8 @@ FocusScope Loader { id: aircraftContent - source: _launcher.aircraftGridMode ? "qrc:///qml/AircraftGridView.qml" + // we use gridModeToggle vis to mean enabled, effectively + source: (gridModeToggle.visible && _launcher.aircraftGridMode) ? "qrc:///qml/AircraftGridView.qml" : "qrc:///qml/AircraftListView.qml" anchors { @@ -182,6 +198,10 @@ FocusScope __model: _launcher.installedAircraftModel __header: emptyHeader } + + PropertyChanges { + target: gridModeToggle; visible: true + } }, State { @@ -191,6 +211,10 @@ FocusScope __model: _launcher.searchAircraftModel __header: emptyHeader } + + PropertyChanges { + target: gridModeToggle; visible: true + } }, State { @@ -200,6 +224,10 @@ FocusScope __model: _launcher.browseAircraftModel __header: _addOns.showNoOfficialHangar ? noDefaultCatalogHeader : ratingsHeader } + + PropertyChanges { + target: gridModeToggle; visible: true + } }, State { @@ -209,7 +237,26 @@ FocusScope __model: _launcher.aircraftWithUpdatesModel __header: (_launcher.aircraftWithUpdatesModel.count > 0) ? updateAllHeader : emptyHeader } + + PropertyChanges { + target: gridModeToggle; visible: false + } + }, + + State { + name: "favourites" + + PropertyChanges { + target: root + __model: _launcher.favouriteAircraftModel + __header: emptyHeader + } + + PropertyChanges { + target: gridModeToggle; visible: true + } } + ] function showDetails(uri) diff --git a/src/GUI/qml/AircraftListView.qml b/src/GUI/qml/AircraftListView.qml index ed0958084..6fe7bce66 100644 --- a/src/GUI/qml/AircraftListView.qml +++ b/src/GUI/qml/AircraftListView.qml @@ -12,6 +12,9 @@ Item { function updateSelectionFromLauncher() { + if (!model) + return; + model.selectVariantForAircraftURI(_launcher.selectedAircraft); var row = model.indexForURI(_launcher.selectedAircraft); if (row >= 0) { diff --git a/src/GUI/qml/AircraftRatingsPanel.qml b/src/GUI/qml/AircraftRatingsPanel.qml index 3e6f46463..9df0585be 100644 --- a/src/GUI/qml/AircraftRatingsPanel.qml +++ b/src/GUI/qml/AircraftRatingsPanel.qml @@ -15,6 +15,7 @@ ListHeaderBox onCheckedChanged: { _launcher.browseAircraftModel.ratingsFilterEnabled = checked + _launcher.saveUISetting("enable-ratings-filter", checked); } label: qsTr("Filter using ratings") diff --git a/src/GUI/qml/AircraftVariantChoice.qml b/src/GUI/qml/AircraftVariantChoice.qml index 72366387f..43d564d7f 100644 --- a/src/GUI/qml/AircraftVariantChoice.qml +++ b/src/GUI/qml/AircraftVariantChoice.qml @@ -92,10 +92,9 @@ Rectangle { screenPos = _launcher.mapToGlobal(title, Qt.point(0, 0)) } - popupFrame.x = screenPos.x; - popupFrame.y = screenPos.y; - popupFrame.show() - tracker.window = popupFrame + var pop = popup.createObject(root, {x:screenPos.x, y:screenPos.y }) + tracker.window = pop; + pop.show(); } } @@ -108,59 +107,63 @@ Rectangle { id: tracker } - Window { - id: popupFrame + Component { + id: popup - width: root.width - flags: Qt.Popup - height: choicesColumn.childrenRect.height - color: "white" + Window { + id: popupFrame - Rectangle { - border.width: 1 - border.color: Style.minorFrameColor - anchors.fill: parent - } + width: root.width + flags: Qt.Popup + height: choicesColumn.childrenRect.height + color: "white" - Column { - id: choicesColumn + Rectangle { + border.width: 1 + border.color: Style.minorFrameColor + anchors.fill: parent + } - Repeater { - // would prefer the model to be conditional on visiblity, - // but this trips up the Window sizing on Linux (Ubuntu at - // least) and we get a mis-aligned origin - model: aircraftInfo.variantNames + Column { + id: choicesColumn - delegate: Item { - width: popupFrame.width - height: choiceText.implicitHeight + Repeater { + // would prefer the model to be conditional on visiblity, + // but this trips up the Window sizing on Linux (Ubuntu at + // least) and we get a mis-aligned origin + model: aircraftInfo.variantNames - Text { - id: choiceText - text: modelData + delegate: Item { + width: popupFrame.width + height: choiceText.implicitHeight - // allow override the size in case the title size is enormous - font.pixelSize: (popupFontPixelSize > 0) ? popupFontPixelSize : title.font.pixelSize + Text { + id: choiceText + text: modelData - color: choiceArea.containsMouse ? Style.themeColor : Style.baseTextColor - anchors { - left: parent.left - right: parent.right - margins: 4 + // allow override the size in case the title size is enormous + font.pixelSize: (root.popupFontPixelSize > 0) ? root.popupFontPixelSize : title.font.pixelSize + + color: choiceArea.containsMouse ? Style.themeColor : Style.baseTextColor + anchors { + left: parent.left + right: parent.right + margins: 4 + } } - } - MouseArea { - id: choiceArea - hoverEnabled: true - anchors.fill: parent - onClicked: { - popupFrame.hide() - root.selected(model.index) + MouseArea { + id: choiceArea + hoverEnabled: true + anchors.fill: parent + onClicked: { + popupFrame.close() + root.selected(model.index) + } } - } - } // of delegate Item - } // of Repeater - } - } // of popup frame + } // of delegate Item + } // of Repeater + } + } // of popup frame + } // of popup component } diff --git a/src/GUI/qml/CatalogDelegate.qml b/src/GUI/qml/CatalogDelegate.qml index 376b678ad..3cffffc75 100644 --- a/src/GUI/qml/CatalogDelegate.qml +++ b/src/GUI/qml/CatalogDelegate.qml @@ -26,12 +26,19 @@ Item { } } + function isDisabled() + { + return (model.status !== CatalogListModel.Ok) || + (enableCheckbox.checked === false); + } + Item { anchors.top: divider.bottom height: catalogTextColumn.childrenRect.height + Style.margin * 2 width: parent.width + Column { id: catalogTextColumn @@ -42,11 +49,28 @@ Item { anchors.rightMargin: Style.margin spacing: Style.margin - StyledText { - font.pixelSize: Style.subHeadingFontPixelSize - font.bold: true - width: parent.width - text: model.name + Row { + spacing: Style.margin + height: headingText.height + + Checkbox { + id: enableCheckbox + checked: model.enabled + height: parent.height + onCheckedChanged: model.enable = checked; + // only allow the user to toggle enable/disable if + // the catalog is valid + visible: (model.status === CatalogListModel.Ok) + } + + StyledText { + id: headingText + font.pixelSize: Style.subHeadingFontPixelSize + font.bold: true + width: catalogTextColumn.width - enableCheckbox.width + text: model.name + font.strikeout: delegateRoot.isDisabled(); + } } StyledText { diff --git a/src/GUI/qml/Checkbox.qml b/src/GUI/qml/Checkbox.qml index fb17da7ee..f2c1ffc9d 100644 --- a/src/GUI/qml/Checkbox.qml +++ b/src/GUI/qml/Checkbox.qml @@ -1,4 +1,5 @@ import QtQuick 2.4 +import "." Item { property bool checked: false @@ -11,7 +12,7 @@ Item { id: checkBox width: 18 height: 18 - border.color: mouseArea.containsMouse ? "#68A6E1" : "#9f9f9f" + border.color: mouseArea.containsMouse ? Style.frameColor : Style.inactiveThemeColor border.width: 1 anchors.left: parent.left anchors.leftMargin: 8 @@ -22,7 +23,7 @@ Item { height: 12 anchors.centerIn: parent id: checkMark - color: "#9f9f9f" + color: Style.themeColor visible: checked } } @@ -41,5 +42,6 @@ Item { onClicked: { checked = !checked } + cursorShape: Qt.PointingHandCursor } } diff --git a/src/GUI/qml/EnableDisableButton.qml b/src/GUI/qml/EnableDisableButton.qml new file mode 100644 index 000000000..345e8d4a0 --- /dev/null +++ b/src/GUI/qml/EnableDisableButton.qml @@ -0,0 +1,36 @@ +import QtQuick 2.0 +import "." + +Item { + id: root + + width: height + height: icon.implicitHeight + 1 + + property bool enable: true + + signal clicked(); + + Image { + id: icon + source: "qrc:///svg/icon-hide" + } + + MouseArea { + id: mouse + // hoverEnabled: true + onClicked: root.clicked(); + anchors.fill: parent + } + +// Text { +// anchors.right: root.left +// anchors.rightMargin: Style.margin +// anchors.verticalCenter: root.verticalCenter +// visible: mouse.containsMouse +// color: Style.baseTextColor +// font.pixelSize: Style.baseFontPixelSize +// text: root.enable ? qsTr("Click_to_disable") +// : qsTr("Click_to_enable") +// } +} diff --git a/src/GUI/qml/FavouriteToggleButton.qml b/src/GUI/qml/FavouriteToggleButton.qml new file mode 100644 index 000000000..218457a5e --- /dev/null +++ b/src/GUI/qml/FavouriteToggleButton.qml @@ -0,0 +1,32 @@ +import QtQuick 2.4 +import "." + +Item { + id: root + property bool checked: false + + implicitWidth: icon.width + Style.margin + implicitHeight: icon.height + Style.margin + + signal toggle(var on); + + + Image { + id: icon + source: { + var b = mouse.containsMouse ? !root.checked : root.checked; + return b ? "qrc:///favourite-icon-filled" : "qrc:///favourite-icon-outline"; + } + + anchors.centerIn: parent + } + + MouseArea { + id: mouse + anchors.fill: parent + hoverEnabled: true + onClicked: root.toggle(!root.checked); + + cursorShape: Qt.PointingHandCursor + } +} diff --git a/src/GUI/qml/HistoryPopup.qml b/src/GUI/qml/HistoryPopup.qml index a731fff39..83aa58a50 100644 --- a/src/GUI/qml/HistoryPopup.qml +++ b/src/GUI/qml/HistoryPopup.qml @@ -37,11 +37,12 @@ Item { hoverEnabled: root.enabled enabled: root.enabled onClicked: { - var screenPos = _launcher.mapToGlobal(button, Qt.point(-popupFrame.width, 0)) - popupFrame.x = screenPos.x; - popupFrame.y = screenPos.y; - popupFrame.visible = true - tracker.window = popupFrame + var pop = popup.createObject(root) + var screenPos = _launcher.mapToGlobal(button, Qt.point(-pop.width, 0)) + pop.y = screenPos.y; + pop.x = screenPos.x; + tracker.window = pop; + pop.show(); } } } @@ -50,51 +51,54 @@ Item { id: tracker } - Window { - id: popupFrame + Component { + id: popup - flags: Qt.Popup - height: choicesColumn.childrenRect.height + Style.margin * 2 - width: choicesColumn.childrenRect.width + Style.margin * 2 - visible: false - color: "white" + Window { + id: popupFrame - Rectangle { - border.width: 1 - border.color: Style.minorFrameColor - anchors.fill: parent - } + flags: Qt.Popup + height: choicesColumn.childrenRect.height + Style.margin * 2 + width: choicesColumn.childrenRect.width + Style.margin * 2 + color: "white" - // text repeater - Column { - id: choicesColumn - spacing: Style.margin - x: Style.margin - y: Style.margin + Rectangle { + border.width: 1 + border.color: Style.minorFrameColor + anchors.fill: parent + } - Repeater { - id: choicesRepeater - model: root.model - delegate: - StyledText { - id: choiceText + // text repeater + Column { + id: choicesColumn + spacing: Style.margin + x: Style.margin + y: Style.margin - // Taken from TableViewItemDelegateLoader.qml to follow QML role conventions - text: model && model.hasOwnProperty(displayRole) ? model[displayRole] // Qml ListModel and QAbstractItemModel - : modelData && modelData.hasOwnProperty(displayRole) ? modelData[displayRole] // QObjectList / QObject - : modelData != undefined ? modelData : "" // Models without role - height: implicitHeight + Style.margin + Repeater { + id: choicesRepeater + model: root.model + delegate: + StyledText { + id: choiceText - MouseArea { - width: popupFrame.width // full width of the popup - height: parent.height - onClicked: { - popupFrame.visible = false - root.selected(model.index); + // Taken from TableViewItemDelegateLoader.qml to follow QML role conventions + text: model && model.hasOwnProperty(displayRole) ? model[displayRole] // Qml ListModel and QAbstractItemModel + : modelData && modelData.hasOwnProperty(displayRole) ? modelData[displayRole] // QObjectList / QObject + : modelData != undefined ? modelData : "" // Models without role + height: implicitHeight + Style.margin + + MouseArea { + width: popupFrame.width // full width of the popup + height: parent.height + onClicked: { + root.selected(model.index); + popupFrame.close() + } } - } - } // of Text delegate - } // text repeater - } // text column - } // of popup Window + } // of Text delegate + } // text repeater + } // text column + } // of popup Window + } } diff --git a/src/GUI/qml/IconButton.qml b/src/GUI/qml/IconButton.qml new file mode 100644 index 000000000..48854ad5d --- /dev/null +++ b/src/GUI/qml/IconButton.qml @@ -0,0 +1,30 @@ +import QtQuick 2.0 +import "." + +Rectangle { + id: root + radius: Style.roundRadius + border.width: 1 + border.color: Style.themeColor + width: height + height: Style.baseFontPixelSize + Style.margin * 2 + color: mouse.containsMouse ? Style.minorFrameColor : "white" + + property alias icon: icon.source + + signal clicked(); + + Image { + id: icon + width: parent.width - Style.margin + height: parent.height - Style.margin + anchors.centerIn: parent + } + + MouseArea { + id: mouse + hoverEnabled: true + onClicked: root.clicked(); + anchors.fill: parent + } +} diff --git a/src/GUI/qml/Launcher.qml b/src/GUI/qml/Launcher.qml index 40b8eb77c..d7b43ab29 100644 --- a/src/GUI/qml/Launcher.qml +++ b/src/GUI/qml/Launcher.qml @@ -6,7 +6,7 @@ Item { id: root // order of this model sets the order of buttons in the sidebar ListModel { - id: pagesModel + id: startupPagesModel ListElement { title: qsTr("Summary"); pageSource: "qrc:///qml/Summary.qml"; iconPath: "qrc:///svg/toolbox-summary"; state:"loader" } ListElement { title: qsTr("Aircraft"); pageSource: "qrc:///qml/AircraftList.qml"; iconPath: "qrc:///svg/toolbox-aircraft"; state:"loader" } @@ -28,6 +28,18 @@ Item { } + ListModel { + id: inAppPagesModel + ListElement { title: qsTr("Summary"); pageSource: "qrc:///qml/Summary.qml"; iconPath: "qrc:///svg/toolbox-summary"; state:"loader" } + ListElement { title: qsTr("Aircraft"); pageSource: "qrc:///qml/AircraftList.qml"; iconPath: "qrc:///svg/toolbox-aircraft"; state:"loader" } + + ListElement { + title: qsTr("Location"); pageSource: "qrc:///qml/Location.qml"; + iconPath: "qrc:///toolbox-location"; state:"loader" + } + } + + Component.onCompleted: { _launcher.minimumWindowSize = Qt.size(Style.strutSize * 12, sidebar.minimumHeight); @@ -35,7 +47,7 @@ Item { Connections { target: _location - onSkipFromArgsChanged: pagesModel.setProperty(2, "buttonDisabled", _location.skipFromArgs) + onSkipFromArgsChanged: startupPagesModel.setProperty(2, "buttonDisabled", _location.skipFromArgs) } state: "loader" @@ -82,7 +94,7 @@ Item { id: sidebar height: parent.height z: 1 - pagesModel: pagesModel + pagesModel: _launcher.inAppMode ? inAppPagesModel : startupPagesModel selectedPage: 0 // open on the summary page onShowMenu: menu.show(); @@ -127,7 +139,7 @@ Item { function selectPage(index) { sidebar.setSelectedPage(index); - var page = pagesModel.get(index); + var page = sidebar.pagesModel.get(index); pageLoader.source = page.pageSource root.state = page.state } diff --git a/src/GUI/qml/Location.qml b/src/GUI/qml/Location.qml index d85257176..9fb4ed5f9 100644 --- a/src/GUI/qml/Location.qml +++ b/src/GUI/qml/Location.qml @@ -9,6 +9,9 @@ Item { property bool __searchActive: false property string lastSearch + property bool showCarriers: false + readonly property var locationModel: showCarriers ? _location.carriersModel : _location.searchModel + function backToSearch() { detailLoader.sourceComponent = null @@ -33,6 +36,13 @@ Item { } } + function selectCarrier(name) + { + selectedLocation.guid = 0; + _location.carrier = name; + detailLoader.sourceComponent = carrierDetails + } + Component.onCompleted: { // important so we can leave the location page and return to it, // preserving the state @@ -46,6 +56,8 @@ Item { } } else if (_location.isBaseLatLon) { detailLoader.sourceComponent = navaidDetails + } else if (_location.isCarrier) { + detailLoader.sourceComponent = carrierDetails; } else { _location.showHistoryInSearchModel(); } @@ -69,6 +81,13 @@ Item { } } + Component { + id: carrierDetails + LocationCarrierView { + id: carrierView + } + } + Rectangle { anchors.fill: parent color: "white" @@ -129,7 +148,11 @@ Item { anchors.fill: parent hoverEnabled: true onClicked: { - root.selectLocation(model.guid, model.type); + if (root.showCarriers) { + root.selectCarrier(model.name); + } else { + root.selectLocation(model.guid, model.type); + } } } } @@ -160,10 +183,12 @@ Item { anchors.topMargin: Style.margin } + + SearchButton { id: searchButton - anchors.right: parent.right + anchors.right: carriersButton.left anchors.top: headerText.bottom anchors.left: parent.left anchors.margins: Style.margin @@ -172,7 +197,8 @@ Item { placeholder: qsTr("Search for an airport or navaid"); onSearch: { - // when th search term is cleared, show the history + root.showCarriers = false; + // when the search term is cleared, show the history if (term == "") { _location.showHistoryInSearchModel(); return; @@ -191,6 +217,18 @@ Item { } } + IconButton { + id: carriersButton + anchors.top: headerText.bottom + anchors.right: parent.right + anchors.margins: Style.margin + icon: "qrc:///svg/icon-carrier" + + onClicked: { + root.showCarriers = true; + } + } + StyledText { id: searchHelpText anchors.right: parent.right @@ -220,12 +258,12 @@ Item { anchors.topMargin: Style.margin width: parent.width anchors.bottom: parent.bottom - model: _location.searchModel + model: root.locationModel delegate: locationSearchDelegate clip: true header: Item { - visible: _location.searchModel.isSearchActive + visible: !root.showCarriers && _location.searchModel.isSearchActive width: parent.width height: visible ? 50 : 0 @@ -246,7 +284,7 @@ Item { footer: Item { width: parent.width height: noResultsText.height - visible: (parent.count === 0) && !_location.searchModel.isSearchActive + visible: !root.showCarriers && (parent.count === 0) && !_location.searchModel.isSearchActive Text { id: noResultsText width: parent.width diff --git a/src/GUI/qml/LocationCarrierView.qml b/src/GUI/qml/LocationCarrierView.qml new file mode 100644 index 000000000..ab4d21bca --- /dev/null +++ b/src/GUI/qml/LocationCarrierView.qml @@ -0,0 +1,214 @@ +import QtQuick 2.4 +import FlightGear 1.0 +import FlightGear.Launcher 1.0 +import "." + +Item { + property alias geod: diagram.geod + + NavaidDiagram { + id: diagram + anchors.fill: parent + + offsetEnabled: _location.offsetEnabled + offsetBearing: _location.offsetRadial + offsetDistance: _location.offsetDistance + heading: _location.heading + } + + Component.onCompleted: { + syncUIFromController(); + } + + function syncUIFromController() + { + if (_location.useCarrierFLOLS) { + flolsRadio.select() + } else { + parkingRadio.select(); + parkingChoice.syncCurrentIndex(); + } + } + + Rectangle { + id: panel + + color: "transparent" + border.width: 1 + border.color: Style.frameColor + + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + margins: Style.strutSize + } + + height: selectionGrid.height + Style.margin * 2 + + // set opacity here only, so we don't make the whole summary pannel translucent + Rectangle { + id: background + anchors.fill: parent + z: -1 + opacity: Style.panelOpacity + color: "white" + } + + RadioButtonGroup { + id: radioGroup + } + + Column { + id: selectionGrid + spacing: Style.margin + width: parent.width + + StyledText { // heading text + id: headingText + anchors { + left: parent.left + right: parent.right + margins: Style.margin + } + + text: qsTr("Carrier: %1").arg(_location.carrier); + font.pixelSize: Style.headingFontPixelSize + } + + // on FLOLS offset + Row { + anchors.left: parent.left + anchors.leftMargin: Style.margin + anchors.right: parent.right + anchors.rightMargin: Style.margin + spacing: Style.margin + + RadioButton { + id: flolsRadio + anchors.verticalCenter: parent.verticalCenter + group: radioGroup + onClicked: { + if (selected) _location.useCarrierFLOLS = selected + } + selected: _location.useCarrierFLOLS + } + + StyledText { + text: qsTr("On final approach") + anchors.verticalCenter: parent.verticalCenter + enabled: flolsRadio.selected + } + + + NumericalEdit { + id: offsetNmEdit + quantity: _location.offsetDistance + onCommit: _location.offsetDistance = newValue; + label: qsTr("at") + unitsMode: Units.Distance + live: true + anchors.verticalCenter: parent.verticalCenter + enabled: flolsRadio.selected + } + + StyledText { + text: qsTr(" from the FLOLS (aka the ball)") + anchors.verticalCenter: parent.verticalCenter + enabled: flolsRadio.selected + } + + Item { + height: 1; width: Style.strutSize + } + + ToggleSwitch { + id: airspeedToggle + enabled: flolsRadio.selected + checked: _location.speedEnabled + onCheckedChanged: _location.speedEnabled = checked; + anchors.verticalCenter: parent.verticalCenter + } + + NumericalEdit { + id: airspeedSpinbox + label: qsTr("Airspeed:") + unitsMode: Units.SpeedWithoutMach + enabled: _location.speedEnabled && flolsRadio.selected + quantity: _location.airspeed + onCommit: _location.airspeed = newValue + anchors.verticalCenter: parent.verticalCenter + } + } // of FLOLS row + + // parking row + Row { + anchors.left: parent.left + anchors.leftMargin: Style.margin + anchors.right: parent.right + anchors.rightMargin: Style.margin + spacing: Style.margin + + // hide if there's no parking locations defined for this carrier + visible: _location.carrierParkings.length > 0 + + RadioButton { + id: parkingRadio + anchors.verticalCenter: parent.verticalCenter + group: radioGroup + onClicked: { + if (selected) parkingChoice.setLocation(); + } + } + + StyledText { + text: qsTr("Parking") + anchors.verticalCenter: parent.verticalCenter + enabled: parkingRadio.selected + } + + PopupChoice { + id: parkingChoice + model: _location.carrierParkings + // displayRole: "modelData" + width: parent.width * 0.5 + anchors.verticalCenter: parent.verticalCenter + enabled: parkingRadio.selected + + onCurrentIndexChanged: { + setLocation(); + } + + function syncCurrentIndex() + { + for (var i=0; i < _location.carrierParkings.length; ++i) { + if (_location.carrierParkings[i] === _location.carrierParking) { + currentIndex = i; + return; + } + } + + // not found, default to available + currentIndex = 0; + } + + function setLocation() + { + _location.carrierParking = _location.carrierParkings[currentIndex] + //diagram.selection = _location.airportParkings[currentIndex] + } + } + } + + ToggleSwitch { + anchors.left: parent.left + anchors.leftMargin: Style.margin + label: qsTr("Tune navigation radio (TACAN) to carrier") + checked: _location.tuneNAV1 + onCheckedChanged: { + _location.tuneNAV1 = checked + } + } + } // main layout column + } // main panel rectangle +} diff --git a/src/GUI/qml/NumericalEdit.qml b/src/GUI/qml/NumericalEdit.qml index 6bc417b5a..8cb3d934f 100644 --- a/src/GUI/qml/NumericalEdit.qml +++ b/src/GUI/qml/NumericalEdit.qml @@ -101,6 +101,15 @@ FocusScope { } } + function showUnitsMenu() + { + + var screenPos = _launcher.mapToGlobal(editFrame, Qt.point(0, editFrame.height)) + var pop = popup.createObject(root, {x:screenPos.x, y:screenPos.y }) + tracker.window = pop; + pop.show(); + } + Component.onCompleted: { // ensure any initial value is accepted by our mode. // this stops people passing in completely wrong quantities @@ -196,7 +205,7 @@ FocusScope { anchors.left: parent.left anchors.margins: Style.margin text: visible ? units.shortText : "" - onClicked: unitSelectionPopup.show() + onClicked: root.showUnitsMenu() clickable: (units.numChoices > 1) } @@ -251,7 +260,7 @@ FocusScope { anchors.baseline: edit.baseline anchors.right: upDownArea.left text: visible ? units.shortText : "" - onClicked: unitSelectionPopup.show() + onClicked: root.showUnitsMenu(); clickable: (units.numChoices > 1) } @@ -324,76 +333,69 @@ FocusScope { id: tracker } - Window { - id: unitSelectionPopup - visible: false - flags: Qt.Popup - color: "white" - height: choicesColumn.childrenRect.height + Style.margin * 2 - width: choicesColumn.width + Style.margin * 2 + Component { + id: popup + Window { + id: unitSelectionPopup + flags: Qt.Popup + color: "white" + height: choicesColumn.childrenRect.height + Style.margin * 2 + width: choicesColumn.width + Style.margin * 2 - function show() - { - var screenPos = _launcher.mapToGlobal(editFrame, Qt.point(0, editFrame.height)) - unitSelectionPopup.x = screenPos.x; - unitSelectionPopup.y = screenPos.y; - unitSelectionPopup.visible = true - tracker.window = unitSelectionPopup - } - - Rectangle { - border.width: 1 - border.color: Style.minorFrameColor - anchors.fill: parent - } - - // choice layout column - Column { - id: choicesColumn - spacing: Style.margin - x: Style.margin - y: Style.margin - width: menuWidth - - - function calculateMenuWidth() - { - var minWidth = 0; - for (var i = 0; i < choicesRepeater.count; i++) { - minWidth = Math.max(minWidth, choicesRepeater.itemAt(i).implicitWidth); - } - return minWidth; + Rectangle { + border.width: 1 + border.color: Style.minorFrameColor + anchors.fill: parent } - readonly property int menuWidth: calculateMenuWidth() + // choice layout column + Column { + id: choicesColumn + spacing: Style.margin + x: Style.margin + y: Style.margin + width: menuWidth - // main item repeater - Repeater { - id: choicesRepeater - model: units - delegate: - Text { - id: choiceText - readonly property bool selected: units.selectedIndex === model.index - text: model.longName - height: implicitHeight + Style.margin - font.pixelSize: Style.baseFontPixelSize - color: choiceArea.containsMouse ? Style.themeColor : Style.baseTextColor + function calculateMenuWidth() + { + var minWidth = 0; + for (var i = 0; i < choicesRepeater.count; i++) { + minWidth = Math.max(minWidth, choicesRepeater.itemAt(i).implicitWidth); + } + return minWidth; + } - MouseArea { - id: choiceArea - width: unitSelectionPopup.width // full width of the popup - height: parent.height - hoverEnabled: true + readonly property int menuWidth: calculateMenuWidth() - onClicked: { - root.changeToUnits(model.index); - unitSelectionPopup.visible = false; + // main item repeater + Repeater { + id: choicesRepeater + model: units + delegate: + Text { + id: choiceText + readonly property bool selected: units.selectedIndex === model.index + + text: model.longName + height: implicitHeight + Style.margin + font.pixelSize: Style.baseFontPixelSize + color: choiceArea.containsMouse ? Style.themeColor : Style.baseTextColor + + MouseArea { + id: choiceArea + width: unitSelectionPopup.width // full width of the popup + height: parent.height + hoverEnabled: true + + onClicked: { + root.changeToUnits(model.index); + unitSelectionPopup.close(); + } } - } - } // of Text delegate - } // text repeater - } // text column - } + } // of Text delegate + } // text repeater + } // text column + } + } // of popup component } diff --git a/src/GUI/qml/PathListDelegate.qml b/src/GUI/qml/PathListDelegate.qml index 7e9c37a07..f8fd34a36 100644 --- a/src/GUI/qml/PathListDelegate.qml +++ b/src/GUI/qml/PathListDelegate.qml @@ -36,9 +36,25 @@ Item { height: Math.max(label.implicitHeight, pathDeleteButton.height) width: delegateRoot.width + Checkbox { + id: enableCheckbox + checked: model.enabled + anchors.left: parent.left + height: parent.height + onCheckedChanged: { + if (model.enabled !== checked) { + model.enabled = checked; + } + } + } + MouseArea { id: pathDelegateHover - anchors.fill: parent + anchors.left: enableCheckbox.right + anchors.leftMargin: Style.margin + anchors.right: parent.right + height: parent.height + hoverEnabled: true acceptedButtons: Qt.NoButton @@ -46,10 +62,10 @@ Item { // MouseArea, so nested containsMouse logic works ClickableText { id: label - text: modelData + text: model.path onClicked: { // open the location - _addOns.openDirectory(modelData) + _addOns.openDirectory(model.path) } anchors.left: parent.left anchors.right: reorderButton.left @@ -58,6 +74,8 @@ Item { verticalAlignment: Text.AlignVCenter wrapMode: Text.WordWrap + + font.strikeout: enableCheckbox.checked === false } DeleteButton { @@ -88,7 +106,7 @@ Item { delegateRoot.performMove(model.index + 1) } } - } + } // of MouseArea for hover YesNoPanel { id: confirmDeletePath diff --git a/src/GUI/qml/RatingSlider.qml b/src/GUI/qml/RatingSlider.qml index 2a2d3c90d..ea6964177 100644 --- a/src/GUI/qml/RatingSlider.qml +++ b/src/GUI/qml/RatingSlider.qml @@ -15,5 +15,6 @@ Slider { onValueChanged: { ratings[ratingIndex] = value + _launcher.browseAircraftModel.saveRatingsSettings(); } } diff --git a/src/GUI/qml/Settings.qml b/src/GUI/qml/Settings.qml index 15d25c748..c328835b1 100644 --- a/src/GUI/qml/Settings.qml +++ b/src/GUI/qml/Settings.qml @@ -450,6 +450,20 @@ Item { enabled: !renderSection.rembrandt property var data: [0, 2, 4]; defaultIndex: 0 + }, + + SettingCheckbox { + id: compressTextures + setting: "texture-compression" + keywords: ["texture", "compresseion", "memory", "dxt", "cache"] + // no option, since we need to set a property + advanced: true + + label: qsTr("Cache graphics for faster loading") + description: qsTr("By converting images used in rendering to an optimised format " + + "loading times and memory use can be improved. This will consume " + + "some disk space and take initial time while images are converted, " + + "but subsequent loads will be faster, and use less memory.") } ] @@ -465,6 +479,8 @@ Item { if (alsEnabled) { _config.setProperty("/sim/rendering/shaders/skydome", true); } + + _config.setProperty("/sim/rendering/texture-cache/cache-enabled", compressTextures.value); } } diff --git a/src/GUI/qml/Summary.qml b/src/GUI/qml/Summary.qml index 9f959e033..425636ac7 100644 --- a/src/GUI/qml/Summary.qml +++ b/src/GUI/qml/Summary.qml @@ -297,6 +297,7 @@ Item { text: qsTr("Settings:") horizontalAlignment: Text.AlignRight font.pixelSize: Style.headingFontPixelSize + visible: !_launcher.inAppMode } StyledText { @@ -306,6 +307,7 @@ Item { maximumLineCount: 2 elide: Text.ElideRight width: summaryGrid.middleColumnWidth + visible: !_launcher.inAppMode } Item { diff --git a/src/GUI/resources.qrc b/src/GUI/resources.qrc index 3a7f3f820..79ee6f85a 100644 --- a/src/GUI/resources.qrc +++ b/src/GUI/resources.qrc @@ -91,6 +91,7 @@ qml/LineEdit.qml qml/LocationAirportView.qml qml/LocationNavaidView.qml + qml/LocationCarrierView.qml qml/icons8-linear-spinner.gif qml/icons8-ellipsis-filled-50.png qml/RadioButton.qml @@ -129,6 +130,11 @@ qml/AircraftGridView.qml qml/AircraftListView.qml qml/GridToggleButton.qml + qml/EnableDisableButton.qml + qml/IconButton.qml + qml/FavouriteToggleButton.qml + assets/icons8-christmas-star-filled.png + assets/icons8-christmas-star-outline.png preview-close.png @@ -147,5 +153,8 @@ assets/icons8-helicopter.svg assets/icons8-grid-view.svg assets/icons8-menu.svg + assets/icons8-hide-50.png + assets/icons8-cargo-ship-50.png + assets/aircraft-carrier-icon.png diff --git a/src/Input/FGEventInput.cxx b/src/Input/FGEventInput.cxx index 2d62e7809..4b0a17aea 100644 --- a/src/Input/FGEventInput.cxx +++ b/src/Input/FGEventInput.cxx @@ -537,9 +537,6 @@ bool FGReportSetting::Test() return d; } -static const char* hexTable = "0123456789ABCDEF"; - - std::string FGReportSetting::reportBytes(const std::string& moduleName) const { FGNasalSys *nas = globals->get_subsystem(); @@ -576,7 +573,8 @@ std::string FGReportSetting::reportBytes(const std::string& moduleName) const // can't access FGInputDevice here to check debugEvents flag #if 0 std::ostringstream byteString; - + static const char* hexTable = "0123456789ABCDEF"; + for (int i=0; i(s[i]); byteString << hexTable[uc >> 4]; diff --git a/src/Input/FGJoystickInput.cxx b/src/Input/FGJoystickInput.cxx index c161d449c..979664c55 100644 --- a/src/Input/FGJoystickInput.cxx +++ b/src/Input/FGJoystickInput.cxx @@ -30,6 +30,8 @@ # include #endif +#include + #include #include "FGDeviceConfigurationMap.hxx" #include
@@ -118,6 +120,7 @@ void FGJoystickInput::init() for (int i = 0; i < MAX_JOYSTICKS; i++) { jsJoystick * js = new jsJoystick(i); joysticks[i].plibJS.reset(js); + joysticks[i].initializing = true; if (js->notWorking()) { SG_LOG(SG_INPUT, SG_DEBUG, "Joystick " << i << " not found"); @@ -334,20 +337,56 @@ void FGJoystickInput::updateJoystick(int index, FGJoystickInput::joystick* joy, int modifiers = fgGetKeyModifiers(); int buttons; bool pressed, last_state; + bool axes_initialized; float delay; jsJoystick * js = joy->plibJS.get(); - if (js == 0 || js->notWorking()) + if (js == 0 || js->notWorking()) { + joysticks[index].initializing = true; + if (js) { + joysticks[index].init_dt += dt; + if (joysticks[index].init_dt >= 1.0) { + joysticks[index].plibJS.reset( new jsJoystick(index) ); + joysticks[index].init_dt = 0.0; + } + } return; + } js->read(&buttons, axis_values); - if (js->notWorking()) // If js is disconnected + if (js->notWorking()) { // If js is disconnected + joysticks[index].initializing = true; return; + } + + // Joystick axes can get initialized to extreme values, at least on Linux. + // Wait until one of the axes get a different value before continuing. + // https://sourceforge.net/p/flightgear/codetickets/2185/ + axes_initialized = true; + if (joysticks[index].initializing) { + + if (!joysticks[index].initialized) { + js->read(NULL, joysticks[index].values); + joysticks[index].initialized = true; + } + + int j; + for (j = 0; j < joy->naxes; j++) { + if (axis_values[j] != joysticks[index].values[j]) break; + } + if (j < joy->naxes) { + joysticks[index].initializing = false; + } else { + axes_initialized = false; + } + } // Update device status SGPropertyNode_ptr status = status_node->getChild("joystick", index, true); - for (int j = 0; j < MAX_JOYSTICK_AXES; j++) { - status->getChild("axis", j, true)->setFloatValue(axis_values[j]); + if (axes_initialized) { + for (int j = 0; j < MAX_JOYSTICK_AXES; j++) { + status->getChild("axis", j, true)->setFloatValue(axis_values[j]); + } } for (int j = 0; j < MAX_JOYSTICK_BUTTONS; j++) { @@ -355,36 +394,38 @@ void FGJoystickInput::updateJoystick(int index, FGJoystickInput::joystick* joy, } // Fire bindings for the axes. - for (int j = 0; j < joy->naxes; j++) { - axis &a = joy->axes[j]; + if (axes_initialized) { + for (int j = 0; j < joy->naxes; j++) { + axis &a = joy->axes[j]; - // Do nothing if the axis position - // is unchanged; only a change in - // position fires the bindings. - // But only if there are bindings - if (fabs(axis_values[j] - a.last_value) > a.tolerance - && a.bindings[KEYMOD_NONE].size() > 0 ) { - a.last_value = axis_values[j]; - for (unsigned int k = 0; k < a.bindings[KEYMOD_NONE].size(); k++) - a.bindings[KEYMOD_NONE][k]->fire(axis_values[j]); - } + // Do nothing if the axis position + // is unchanged; only a change in + // position fires the bindings. + // But only if there are bindings + if (fabs(axis_values[j] - a.last_value) > a.tolerance + && a.bindings[KEYMOD_NONE].size() > 0 ) { + a.last_value = axis_values[j]; + for (unsigned int k = 0; k < a.bindings[KEYMOD_NONE].size(); k++) + a.bindings[KEYMOD_NONE][k]->fire(axis_values[j]); + } - // do we have to emulate axis buttons? - last_state = joy->axes[j].low.last_state || joy->axes[j].high.last_state; - pressed = axis_values[j] < a.low_threshold || axis_values[j] > a.high_threshold; - delay = (pressed ? last_state ? a.interval_sec : a.delay_sec : a.release_delay_sec ); - if(pressed || last_state) a.last_dt += dt; - else a.last_dt = 0; - if(a.last_dt >= delay) { - if (a.low.bindings[modifiers].size()) - joy->axes[j].low.update( modifiers, axis_values[j] < a.low_threshold ); + // do we have to emulate axis buttons? + last_state = joy->axes[j].low.last_state || joy->axes[j].high.last_state; + pressed = axis_values[j] < a.low_threshold || axis_values[j] > a.high_threshold; + delay = (pressed ? last_state ? a.interval_sec : a.delay_sec : a.release_delay_sec ); + if(pressed || last_state) a.last_dt += dt; + else a.last_dt = 0; + if(a.last_dt >= delay) { + if (a.low.bindings[modifiers].size()) + joy->axes[j].low.update( modifiers, axis_values[j] < a.low_threshold ); - if (a.high.bindings[modifiers].size()) - joy->axes[j].high.update( modifiers, axis_values[j] > a.high_threshold ); + if (a.high.bindings[modifiers].size()) + joy->axes[j].high.update( modifiers, axis_values[j] > a.high_threshold ); - a.last_dt -= delay; - } - } // of axes iteration + a.last_dt -= delay; + } + } // of axes iteration + } // axes_initialized // Fire bindings for the buttons. for (int j = 0; j < joy->nbuttons; j++) { diff --git a/src/Input/FGJoystickInput.hxx b/src/Input/FGJoystickInput.hxx index 642950d6f..c2d6239b5 100644 --- a/src/Input/FGJoystickInput.hxx +++ b/src/Input/FGJoystickInput.hxx @@ -101,6 +101,10 @@ private: axis * axes; FGButton * buttons; bool predefined; + bool initializing = true; + bool initialized = false; + float values[MAX_JOYSTICK_AXES]; + double init_dt = 0.0f; void clearAxesAndButtons(); }; diff --git a/src/Input/FGMacOSXEventInput.cxx b/src/Input/FGMacOSXEventInput.cxx index b8dc29eee..d7f6a33f4 100644 --- a/src/Input/FGMacOSXEventInput.cxx +++ b/src/Input/FGMacOSXEventInput.cxx @@ -252,8 +252,21 @@ void FGMacOSXEventInputPrivate::matchedDevice(IOHIDDeviceRef device) std::string manufacturer = getDeviceStringProperty(device, CFSTR(kIOHIDManufacturerKey)); std::string serial = getDeviceStringProperty(device, CFSTR(kIOHIDSerialNumberKey)); +// filter out keyboard and mouse devices : this is especially important for +// Catalina TCC hardening to avoid secuirty alerts + int usagePage, usage; + getDeviceIntProperty(device, CFSTR(kIOHIDPrimaryUsagePageKey), usagePage); + getDeviceIntProperty(device, CFSTR(kIOHIDPrimaryUsageKey), usage); + + if (usagePage == kHIDPage_GenericDesktop) { + if ((usage == kHIDUsage_GD_Keyboard) || (usage == kHIDUsage_GD_Mouse)) { + SG_LOG(SG_INPUT, SG_INFO, "MacOSX-EventInput: skipping device:" << productName << "( from " << manufacturer << ") becuase it is a keyboard or mouse"); + return; + } + } + SG_LOG(SG_INPUT, SG_DEBUG, "MacOSX-EventInput: matched device:" << productName << "( from " << manufacturer << ")"); - + // allocate a Mac input device, and add to the base class to see if we have // a config diff --git a/src/Instrumentation/HUD/HUD.cxx b/src/Instrumentation/HUD/HUD.cxx index 2258a0fa4..45df2d094 100644 --- a/src/Instrumentation/HUD/HUD.cxx +++ b/src/Instrumentation/HUD/HUD.cxx @@ -35,7 +35,7 @@ #include #include -#include +#include "fnt.h" #include
#include
diff --git a/src/Instrumentation/tcas.cxx b/src/Instrumentation/tcas.cxx index 7021fa999..8fffdfb9e 100644 --- a/src/Instrumentation/tcas.cxx +++ b/src/Instrumentation/tcas.cxx @@ -590,7 +590,7 @@ bool TCAS::ThreatDetector::checkTransponder(const SGPropertyNode* pModel, float velocityKt) { const string name = pModel->getName(); - if (name != "multiplayer" && name != "aircraft") + if (name != "multiplayer" && name != "aircraft" && name != "swift") { // assume non-MP/non-AI planes (e.g. ships) have no transponder return false; diff --git a/src/Main/bootstrap.cxx b/src/Main/bootstrap.cxx index 09f3c7618..b0799a967 100644 --- a/src/Main/bootstrap.cxx +++ b/src/Main/bootstrap.cxx @@ -142,7 +142,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR* wideArgs = CommandLineToArgvW(GetCommandLineW(), &numArgs); std::vector utf8Args; - utf8Args.reserve(numArgs); + utf8Args.resize(numArgs); for (int a = 0; a < numArgs; ++a) { const auto s = simgear::strutils::convertWStringToUtf8(wideArgs[a]); diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index cfdda7660..a1b3460bc 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -40,6 +40,7 @@ # include # include # include +# include #endif #include @@ -473,35 +474,61 @@ bool fgInitHome() } #else // write our PID, and check writeability - SGPath pidPath(dataPath, "fgfs.pid"); + SGPath pidPath(dataPath, "fgfs_lock.pid"); + std::string ps = pidPath.utf8Str(); + if (pidPath.exists()) { - SG_LOG(SG_GENERAL, SG_ALERT, "flightgear instance already running, switching to FG_HOME read-only."); - // set a marker property so terrasync/navcache don't try to write - // from secondary instances - fgSetBool("/sim/fghome-readonly", true); - return true; - } + int fd = ::open(ps.c_str(), O_RDONLY, 0644); + if (fd < 0) { + SG_LOG(SG_GENERAL, SG_ALERT, "failed to open local file:" << pidPath + << "\n\tdue to:" << simgear::strutils::error_string(errno)); + return false; + } + + int err = ::flock(fd, LOCK_EX | LOCK_NB); + if (err < 0) { + if ( errno == EWOULDBLOCK) { + SG_LOG(SG_GENERAL, SG_ALERT, "flightgear instance already running, switching to FG_HOME read-only."); + // set a marker property so terrasync/navcache don't try to write + // from secondary instances + fgSetBool("/sim/fghome-readonly", true); + return true; + } else { + SG_LOG(SG_GENERAL, SG_ALERT, "failed to lock file:" << pidPath + << "\n\tdue to:" << simgear::strutils::error_string(errno)); + return false; + } + } + + // we locked it! + result = true; + } else { + char buf[16]; + std::string ps = pidPath.utf8Str(); - char buf[16]; - std::string ps = pidPath.local8BitStr(); + ssize_t len = snprintf(buf, 16, "%d\n", getpid()); + int fd = ::open(ps.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { + SG_LOG(SG_GENERAL, SG_ALERT, "failed to open local file:" << pidPath + << "\n\tdue to:" << simgear::strutils::error_string(errno)); + return false; + } + + int err = write(fd, buf, len); + if (err < 0) { + SG_LOG(SG_GENERAL, SG_ALERT, "failed to write to lock file:" << pidPath + << "\n\tdue to:" << simgear::strutils::error_string(errno)); + return false; + } - // do open+unlink trick to the file is deleted on exit, even if we - // crash or exit(-1) - ssize_t len = snprintf(buf, 16, "%d", getpid()); - int fd = ::open(ps.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644); - if (fd >= 0) { - result = ::write(fd, buf, len) == len; - if( ::unlink(ps.c_str()) != 0 ) // delete file when app quits - result = false; - } - - if (!result) { - flightgear::fatalMessageBoxWithoutExit( - "File permissions problem", - "Can't write to user-data storage folder, check file permissions " - "and FG_HOME.", - "User-data at '" + dataPath.utf8Str() + "'."); - return false; + err = flock(fd, LOCK_EX); + if (err != 0) { + SG_LOG(SG_GENERAL, SG_ALERT, "failed to lock file:" << pidPath + << "\n\tdue to:" << simgear::strutils::error_string(errno)); + return false; + } + + result = true; } #endif fgSetBool("/sim/fghome-readonly", false); @@ -514,6 +541,11 @@ void fgShutdownHome() if (static_fgHomeWriteMutex) { CloseHandle(static_fgHomeWriteMutex); } +#else + if (fgGetBool("/sim/fghome-readonly") == false) { + SGPath pidPath = globals->get_fg_home() / "fgfs_lock.pid"; + pidPath.remove(); + } #endif } @@ -873,8 +905,7 @@ void fgCreateSubsystems(bool duringReset) { SGPath mpath( globals->get_fg_root() ); mpath.append( fgGetString("/sim/rendering/materials-file") ); - if ( ! globals->get_matlib()->load(globals->get_fg_root().local8BitStr(), mpath.local8BitStr(), - globals->get_props()) ) { + if ( ! globals->get_matlib()->load(globals->get_fg_root(), mpath, globals->get_props()) ) { throw sg_io_exception("Error loading materials file", mpath); } diff --git a/src/Main/fg_props.cxx b/src/Main/fg_props.cxx index 2058f0db1..eee6d0006 100644 --- a/src/Main/fg_props.cxx +++ b/src/Main/fg_props.cxx @@ -230,16 +230,6 @@ setFreeze (bool f) { frozen = f; - // Stop sound on a pause - SGSoundMgr *smgr = globals->get_subsystem(); - if ( smgr != NULL ) { - if ( f ) { - smgr->suspend(); - } else if (fgGetBool("/sim/sound/working")) { - smgr->resume(); - } - } - // Pause the particle system simgear::Particles::setFrozen(f); } diff --git a/src/Main/fg_scene_commands.cxx b/src/Main/fg_scene_commands.cxx index beb907584..4f942a245 100644 --- a/src/Main/fg_scene_commands.cxx +++ b/src/Main/fg_scene_commands.cxx @@ -247,8 +247,8 @@ do_materials_reload (const SGPropertyNode * arg, SGPropertyNode * root) SGMaterialLib* new_matlib = new SGMaterialLib; SGPath mpath( globals->get_fg_root() ); mpath.append( fgGetString("/sim/rendering/materials-file") ); - bool loaded = new_matlib->load(globals->get_fg_root().local8BitStr(), - mpath.local8BitStr(), + bool loaded = new_matlib->load(globals->get_fg_root(), + mpath, globals->get_props()); if ( ! loaded ) { diff --git a/src/Main/globals.cxx b/src/Main/globals.cxx index 9aeb37ac7..1432ade88 100644 --- a/src/Main/globals.cxx +++ b/src/Main/globals.cxx @@ -133,6 +133,17 @@ public: } }; +class CompositorEffectsProvider : public simgear::ResourceProvider { +public: + CompositorEffectsProvider() : + simgear::ResourceProvider(simgear::ResourceManager::PRIORITY_NORMAL) { + } + SGPath resolve(const std::string &aResource, SGPath&) const override { + const SGPath p = globals->get_fg_root() / "Compositor" / aResource; + return p.exists() ? p : SGPath(); + } +}; + //////////////////////////////////////////////////////////////////////// // Implementation of FGGlobals. //////////////////////////////////////////////////////////////////////// @@ -165,6 +176,9 @@ FGGlobals::FGGlobals() : resMgr->addProvider(new AircraftResourceProvider()); resMgr->addProvider(new CurrentAircraftDirProvider()); resMgr->addProvider(new flightgear::addons::ResourceProvider()); +#ifdef ENABLE_COMPOSITOR + resMgr->addProvider(new CompositorEffectsProvider()); +#endif initProperties(); } @@ -364,9 +378,7 @@ void FGGlobals::append_fg_scenery (const SGPath &path) // tell the ResouceManager about the scenery path // needed to load Models from this scenery path - simgear::ResourceManager::instance()->addBasePath(abspath.local8BitStr(), - simgear::ResourceManager::PRIORITY_DEFAULT); - simgear::Dir dir(abspath); + simgear::ResourceManager::instance()->addBasePath(abspath, simgear::ResourceManager::PRIORITY_DEFAULT); fg_scenery.push_back(abspath); extra_read_allowed_paths.push_back(abspath); @@ -808,6 +820,13 @@ FGGlobals::loadUserSettings(SGPath userDataPath) } else { tryAutosaveMigration(userDataPath, &autosave); } + /* Before 2020-03-10, we could save portions of the /ai/models/ tree, which + confuses things when loaded again. So delete any such items if they have + been loaded. */ + SGPropertyNode* ai = autosave.getNode("ai"); + if (ai) { + ai->removeChildren("models"); + } copyProperties(&autosave, globals->get_props()); } diff --git a/src/Main/main.cxx b/src/Main/main.cxx old mode 100644 new mode 100755 index c856b4c33..9727d9a29 --- a/src/Main/main.cxx +++ b/src/Main/main.cxx @@ -359,7 +359,7 @@ static void fgIdleFunction ( void ) { flightgear::initPosition(); flightgear::initTowerLocationListener(); - simgear::SGModelLib::init(globals->get_fg_root().local8BitStr(), globals->get_props()); + simgear::SGModelLib::init(globals->get_fg_root().utf8Str(), globals->get_props()); auto timeManager = globals->get_subsystem(); timeManager->init(); @@ -541,6 +541,10 @@ int fgMainInit( int argc, char **argv ) if (!fgInitHome()) { return EXIT_FAILURE; } + +#if defined(HAVE_QT) + flightgear::initApp(argc, argv); +#endif const bool readOnlyFGHome = fgGetBool("/sim/fghome-readonly"); if (!readOnlyFGHome) { @@ -624,10 +628,7 @@ int fgMainInit( int argc, char **argv ) } #if defined(HAVE_QT) - flightgear::initApp(argc, argv); if (showLauncher) { - flightgear::checkKeyboardModifiersForSettingFGRoot(); - if (!flightgear::runLauncherDialog()) { return EXIT_SUCCESS; } @@ -704,7 +705,8 @@ int fgMainInit( int argc, char **argv ) int result = fgOSMainLoop(); frame_signal.clear(); fgOSCloseWindow(); - + fgShutdownHome(); + simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(mln_stopped); simgear::clearEffectCache(); diff --git a/src/Main/options.cxx b/src/Main/options.cxx index 147659aca..ea292a55d 100644 --- a/src/Main/options.cxx +++ b/src/Main/options.cxx @@ -719,7 +719,7 @@ clearLocation () static int fgOptAddon(const char *arg) { - const SGPath addonPath = SGPath::fromLocal8Bit(arg); + const SGPath addonPath = SGPath::fromUtf8(arg); const auto& addonManager = addons::AddonManager::instance(); try { @@ -904,7 +904,7 @@ fgOptRoc( const char *arg ) static int fgOptFgScenery( const char *arg ) { - globals->append_fg_scenery(SGPath::pathsFromLocal8Bit(arg)); + globals->append_fg_scenery(SGPath::pathsFromUtf8(arg)); return FG_OPTIONS_OK; } @@ -921,7 +921,7 @@ fgOptEnhancedLighting( const char *arg ) static int fgOptAllowNasalRead( const char *arg ) { - PathList paths = SGPath::pathsFromLocal8Bit(arg); + PathList paths = SGPath::pathsFromUtf8(arg); if(paths.size() == 0) { SG_LOG(SG_GENERAL, SG_WARN, "--allow-nasal-read requires a list of directories to allow"); } @@ -1121,7 +1121,7 @@ fgOptLogDir(const char* arg) if (!strcmp(arg, "desktop")) { dirPath = SGPath::desktop(); } else { - dirPath = SGPath::fromLocal8Bit(arg); + dirPath = SGPath::fromUtf8(arg); } if (!dirPath.isDir()) { @@ -1423,7 +1423,7 @@ fgOptScenario( const char *arg ) } // create description node - auto n = FGAIManager::registerScenarioFile(path); + auto n = FGAIManager::registerScenarioFile(globals->get_props(), path); if (!n) { SG_LOG(SG_GENERAL, SG_WARN, "failed to read scenario file at:" << path); return FG_OPTIONS_ERROR; @@ -1556,7 +1556,7 @@ fgOptLoadTape(const char* arg) class DelayedTapeLoader : SGPropertyChangeListener { public: DelayedTapeLoader( const char * tape ) : - _tape(SGPath::fromLocal8Bit(tape)) + _tape(SGPath::fromUtf8(tape)) { SGPropertyNode_ptr n = fgGetNode("/sim/signals/fdm-initialized", true); n->addChangeListener( this ); @@ -1846,6 +1846,7 @@ struct OptionDesc { {"prop", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptSetProperty}, {"load-tape", true, OPTION_FUNC, "", false, "", fgOptLoadTape }, {"developer", true, OPTION_IGNORE | OPTION_BOOL, "", false, "", nullptr }, + {"jsbsim-output-directive-file", true, OPTION_STRING, "/sim/jsbsim/output-directive-file", false, "", nullptr }, {0} }; @@ -2103,7 +2104,7 @@ void Options::init(int argc, char **argv, const SGPath& appDataPath) processArgResult(result); } else { // XML properties file - SGPath f = SGPath::fromLocal8Bit(argv[i]); + SGPath f = SGPath::fromUtf8(argv[i]); if (!f.exists()) { SG_LOG(SG_GENERAL, SG_ALERT, "config file not found:" << f); } else { @@ -2190,7 +2191,7 @@ void Options::init(int argc, char **argv, const SGPath& appDataPath) void Options::initPaths() { for (const string& pathOpt : valuesForOption("fg-aircraft")) { - PathList paths = SGPath::pathsFromLocal8Bit(pathOpt); + PathList paths = SGPath::pathsFromUtf8(pathOpt); globals->append_aircraft_paths(paths); } @@ -2240,7 +2241,7 @@ void Options::initAircraft() } if (isOptionSet("aircraft-dir")) { - SGPath aircraftDirPath = SGPath::fromLocal8Bit(valueForOption("aircraft-dir").c_str()); + SGPath aircraftDirPath = SGPath::fromUtf8(valueForOption("aircraft-dir")); globals->append_read_allowed_paths(aircraftDirPath); // Set this now, so it's available in FindAndCacheAircraft. Use realpath() @@ -2564,8 +2565,7 @@ OptionResult Options::processOptions() } // Download dir fix-up - SGPath downloadDir = SGPath::fromLocal8Bit( - valueForOption("download-dir").c_str()); + SGPath downloadDir = SGPath::fromUtf8(valueForOption("download-dir")); if (downloadDir.isNull()) { downloadDir = defaultDownloadDir(); SG_LOG(SG_GENERAL, SG_INFO, @@ -2588,8 +2588,7 @@ OptionResult Options::processOptions() globals->set_download_dir(downloadDir); // Texture Cache directory handling - SGPath textureCacheDir = SGPath::fromLocal8Bit( - valueForOption("texture-cache-dir").c_str()); + SGPath textureCacheDir = SGPath::fromUtf8(valueForOption("texture-cache-dir")); if (textureCacheDir.isNull()) { textureCacheDir = defaultTextureCacheDir(); SG_LOG(SG_GENERAL, SG_INFO, @@ -2611,8 +2610,7 @@ OptionResult Options::processOptions() // TerraSync directory fixup - SGPath terrasyncDir = SGPath::fromLocal8Bit( - valueForOption("terrasync-dir").c_str()); + SGPath terrasyncDir = SGPath::fromUtf8(valueForOption("terrasync-dir")); if (terrasyncDir.isNull()) { terrasyncDir = downloadDir / "TerraSync"; // No “default” qualifier here, because 'downloadDir' may be non-default @@ -2945,17 +2943,16 @@ void Options::setupRoot(int argc, char **argv) } if (isOptionSet("fg-root")) { - root = SGPath::fromLocal8Bit(valueForOption("fg-root").c_str()); // easy! + root = SGPath::fromUtf8(valueForOption("fg-root")); // easy! SG_LOG(SG_GENERAL, SG_INFO, "set from command-line argument: fg_root = " << root ); } else { // Next check if fg-root is set as an env variable char *envp = ::getenv( "FG_ROOT" ); if ( envp != nullptr ) { - root = SGPath::fromLocal8Bit(envp); + root = SGPath::fromEnv("FG_ROOT"); SG_LOG(SG_GENERAL, SG_INFO, "set from FG_ROOT env var: fg_root = " << root ); } else { #if defined(HAVE_QT) - flightgear::initApp(argc, argv); root = SetupRootDialog::restoreUserSelectedRoot(); #endif if (root.isNull()) { @@ -2981,7 +2978,6 @@ void Options::setupRoot(int argc, char **argv) // a command-line, env-var or default root this check can fail and // we still want to use the GUI in that case if (versionComp != 0) { - flightgear::initApp(argc, argv); SetupRootDialog::runDialog(usingDefaultRoot); } #else diff --git a/src/Main/positioninit.cxx b/src/Main/positioninit.cxx index 6389858de..d53b4990a 100644 --- a/src/Main/positioninit.cxx +++ b/src/Main/positioninit.cxx @@ -49,6 +49,7 @@ using std::endl; using std::string; + namespace flightgear { @@ -68,8 +69,11 @@ static bool global_callbackRegistered = false; static void finalizePosition(); +namespace { // annonymous namepsace to avoid warnings about inline classes + // Set current tower position lon/lat given an airport id -static bool fgSetTowerPosFromAirportID( const string& id) { +bool fgSetTowerPosFromAirportID( const string& id) +{ const FGAirport *a = fgFindAirportID( id); if (a) { SGGeod tower = a->getTowerLocation(); @@ -80,12 +84,12 @@ static bool fgSetTowerPosFromAirportID( const string& id) { } else { return false; } - } class FGTowerLocationListener : public SGPropertyChangeListener { - void valueChanged(SGPropertyNode* node) { + void valueChanged(SGPropertyNode* node) override + { string id(node->getStringValue()); if (fgGetBool("/sim/tower/auto-position",true)) { @@ -116,6 +120,9 @@ class FGClosestTowerLocationListener : public SGPropertyChangeListener } }; +} // of anonymous namespace + + void initTowerLocationListener() { SGPropertyChangeListener* tll = new FGTowerLocationListener(); @@ -160,10 +167,7 @@ static void fgApplyStartOffset(const SGGeod& aStartPos, double aHeading, double aHeading = aTargetHeading; } - SGGeod offset; - double az2; // dummy - SGGeodesy::direct(startPos, offsetAzimuth + 180, offsetDistance, offset, az2); - startPos = offset; + startPos = SGGeodesy::direct(startPos, offsetAzimuth + 180, offsetDistance); } setInitialPosition(startPos, aHeading); @@ -184,7 +188,7 @@ std::tuple runwayStartPos(FGRunwayRef runway) // add a margin, try to keep the entire aeroplane comfortable off the // runway. double margin = startOffset + (runway->widthM() * 1.5); - FGTaxiNodeRef taxiNode = groundNet ? groundNet->findNearestNodeOffRunway(pos, runway, margin) : 0; + FGTaxiNodeRef taxiNode = groundNet ? groundNet->findNearestNodeOffRunway(pos, runway, margin) : FGTaxiNodeRef{}; if (taxiNode) { // set this so multiplayer.nas can inform the user fgSetBool("/sim/presets/avoided-mp-runway", true); @@ -313,11 +317,10 @@ static bool fgSetPosFromAirportIDandRwy( const string& id, const string& rwy, bo } -static void fgSetDistOrAltFromGlideSlope() { - // cout << "fgSetDistOrAltFromGlideSlope()" << endl; +static void fgSetDistOrAltFromGlideSlope() +{ string apt_id = fgGetString("/sim/presets/airport-id"); - double gs = fgGetDouble("/sim/presets/glideslope-deg") - * SG_DEGREES_TO_RADIANS ; + double gs = SGMiscd::deg2rad(fgGetDouble("/sim/presets/glideslope-deg")); double od = fgGetDouble("/sim/presets/offset-distance-nm"); double alt = fgGetDouble("/sim/presets/altitude-ft"); @@ -363,9 +366,7 @@ static bool fgSetPosFromNAV( const string& id, FGPositioned::Type type, PositionedID guid) { - FGNavRecord* nav = 0; - - + FGNavRecordRef nav; if (guid != 0) { nav = FGPositioned::loadById(guid); if (!nav) @@ -383,14 +384,14 @@ static bool fgSetPosFromNAV( const string& id, if( navlist.size() > 1 ) { std::ostringstream buf; buf << "Ambigous NAV-ID: '" << id << "'. Specify id and frequency. Available stations:" << endl; - for( nav_list_type::const_iterator it = navlist.begin(); it != navlist.end(); ++it ) { + for( const auto& nav : navlist ) { // NDB stored in kHz, VOR stored in MHz * 100 :-P - double factor = (*it)->type() == FGPositioned::NDB ? 1.0 : 1/100.0; - string unit = (*it)->type() == FGPositioned::NDB ? "kHz" : "MHz"; - buf << (*it)->ident() << " " - << std::setprecision(5) << (double)((*it)->get_freq() * factor) << " " - << (*it)->get_lat() << "/" << (*it)->get_lon() - << endl; + double factor = nav->type() == FGPositioned::NDB ? 1.0 : 1/100.0; + string unit = nav->type() == FGPositioned::NDB ? "kHz" : "MHz"; + buf << nav->ident() << " " + << std::setprecision(5) << static_cast(nav->get_freq() * factor) << " " + << nav->get_lat() << "/" << nav->get_lon() + << endl; } SG_LOG( SG_GENERAL, SG_ALERT, buf.str() ); @@ -413,7 +414,7 @@ static InitPosResult setInitialPosFromCarrier( const string& carrier ) // so our PagedLOD is loaded fgSetDouble("/sim/presets/longitude-deg", initialPos.second.getLongitudeDeg()); fgSetDouble("/sim/presets/latitude-deg", initialPos.second.getLatitudeDeg()); - SG_LOG( SG_GENERAL, SG_INFO, "Initial carrier pos = " << initialPos.second ); + SG_LOG( SG_GENERAL, SG_DEBUG, "Initial carrier pos = " << initialPos.second ); return VicinityPosition; } @@ -421,6 +422,31 @@ static InitPosResult setInitialPosFromCarrier( const string& carrier ) return Failure; } +static InitPosResult checkCarrierSceneryLoaded(const SGSharedPtr carrierRef) +{ + SGVec3d cartPos = carrierRef->getCartPos(); + auto framestamp = globals->get_renderer()->getViewer()->getFrameStamp(); + simgear::CheckSceneryVisitor csnv(globals->get_scenery()->getPager(), + toOsg(cartPos), + 100.0 /* range in metres */, + framestamp); + + // currently the PagedLODs will not be loaded by the DatabasePager + // while the splashscreen is there, so CheckSceneryVisitor force-loads + // missing objects in the main thread + carrierRef->getSceneBranch()->accept(csnv); + if (!csnv.isLoaded()) { + return ContinueWaiting; + } + + // and then wait for the load to actually be synced to the main thread + if (carrierRef->getSceneBranch()->getNumChildren() < 1) { + return ContinueWaiting; + } + + return VicinityPosition; +} + // Set current_options lon/lat given an aircraft carrier id static InitPosResult setFinalPosFromCarrier( const string& carrier, const string& posid ) { @@ -432,24 +458,9 @@ static InitPosResult setFinalPosFromCarrier( const string& carrier, const string return Failure; } - SGVec3d cartPos = carrierRef->getCartPos(); - auto framestamp = globals->get_renderer()->getViewer()->getFrameStamp(); - simgear::CheckSceneryVisitor csnv(globals->get_scenery()->getPager(), - toOsg(cartPos), - 100.0 /* range in metres */, - framestamp); - - // currently the PagedLODs will not be loaded by the DatabasePager - // while the splashscreen is there, so CheckSceneryVisitor force-loads - // missing objects in the main thread - carrierRef->getSceneBranch()->accept(csnv); - if (!csnv.isLoaded()) { - return ContinueWaiting; - } - - // and then wait for the load to actually be synced to the main thread - if (carrierRef->getSceneBranch()->getNumChildren() < 1) { - return ContinueWaiting; + auto res = checkCarrierSceneryLoaded(carrierRef); + if (res != VicinityPosition) { + return res; // either failrue or keep waiting for scenery load } SGGeod geodPos; @@ -492,10 +503,55 @@ static InitPosResult setFinalPosFromCarrier( const string& carrier, const string return Failure; } +static InitPosResult setFinalPosFromCarrierFLOLS(const string& carrier) +{ + SGSharedPtr carrierRef = FGAICarrier::findCarrierByNameOrPennant(carrier); + if (!carrierRef) { + SG_LOG( SG_GENERAL, SG_ALERT, "Failed to locate aircraft carrier = " + << carrier ); + return Failure; + } + + auto res = checkCarrierSceneryLoaded(carrierRef); + if (res != VicinityPosition) { + return res; // either failure or keep waiting for scenery load + } + + SGGeod flolsPosition; + double headingToFLOLS; + if (!carrierRef->getFLOLSPositionHeading(flolsPosition, headingToFLOLS)) { + SG_LOG( SG_GENERAL, SG_ALERT, "Unable to compiute FLOLS position for carrier = " + << carrier ); + return Failure; + } + + const auto flolsElevationFt = flolsPosition.getElevationFt(); + double gs = SGMiscd::deg2rad(carrierRef->getFLOLFSGlidepathAngleDeg()); + const double od = fgGetDouble("/sim/presets/offset-distance-nm"); + + // start position, but with altitude not set + SGGeod startPos = SGGeodesy::direct(flolsPosition, headingToFLOLS + 180, od * SG_NM_TO_METER); + + const double offsetFt = od * SG_NM_TO_METER * SG_METER_TO_FEET; + startPos.setElevationFt(fabs(offsetFt*tan(gs)) + flolsElevationFt); + + fgSetDouble("/sim/presets/longitude-deg", startPos.getLongitudeDeg()); + fgSetDouble("/sim/presets/latitude-deg", startPos.getLatitudeDeg()); + fgSetDouble("/sim/presets/altitude-ft", startPos.getElevationFt()); + fgSetDouble("/sim/presets/heading-deg", headingToFLOLS); + fgSetDouble("/position/longitude-deg", startPos.getLongitudeDeg()); + fgSetDouble("/position/latitude-deg", startPos.getLatitudeDeg()); + fgSetDouble("/position/altitude-ft", startPos.getElevationFt()); + fgSetDouble("/orientation/heading-deg", headingToFLOLS); + fgSetBool("/sim/presets/onground", false); + + return ExactPosition; +} + // Set current_options lon/lat given a fix ident and GUID static bool fgSetPosFromFix( const string& id, PositionedID guid ) { - FGPositioned* fix = NULL; + FGPositionedRef fix; if (guid != 0) { fix = FGPositioned::loadById(guid); } else { @@ -521,8 +577,7 @@ bool initPosition() global_callbackRegistered = true; } - double gs = fgGetDouble("/sim/presets/glideslope-deg") - * SG_DEGREES_TO_RADIANS ; + double gs = SGMiscd::deg2rad(fgGetDouble("/sim/presets/glideslope-deg")); double od = fgGetDouble("/sim/presets/offset-distance-nm"); double alt = fgGetDouble("/sim/presets/altitude-ft"); @@ -762,20 +817,29 @@ void finalizePosition() */ std::string carrier = fgGetString("/sim/presets/carrier"); std::string parkpos = fgGetString("/sim/presets/parkpos"); + std::string runway = fgGetString("/sim/presets/runway"); std::string apt = fgGetString("/sim/presets/airport-id"); + const bool rwy_req = fgGetBool("/sim/presets/runway-requested"); if (!carrier.empty()) { - const auto res = setFinalPosFromCarrier(carrier, parkpos); - if (res == ExactPosition) { + const bool atFLOLS = rwy_req && (runway == "FLOLS"); + InitPosResult carrierResult; + if (atFLOLS) { + carrierResult = setFinalPosFromCarrierFLOLS(carrier); + } else { + carrierResult = setFinalPosFromCarrier(carrier, parkpos); + } + if (carrierResult == ExactPosition) { done = true; - } else if (res == Failure) { + } else if (carrierResult == Failure) { SG_LOG(SG_GENERAL, SG_ALERT, "secondary carrier init failed"); done = true; } else { done = false; // 60 second timeout on waiting for the carrier to load if (global_finalizeTime.elapsedMSec() > 60000) { + SG_LOG(SG_GENERAL, SG_ALERT, "Timeout waiting for carrier scenery to load, will start on the water."); done = true; } } diff --git a/src/Model/acmodel.cxx b/src/Model/acmodel.cxx index 6b36dd362..bff3d8d0d 100644 --- a/src/Model/acmodel.cxx +++ b/src/Model/acmodel.cxx @@ -33,7 +33,7 @@ static osg::Node * fgLoad3DModelPanel(const SGPath &path, SGPropertyNode *prop_root) { bool loadPanels = true; - osg::Node* node = simgear::SGModelLib::loadModel(path.local8BitStr(), prop_root, NULL, loadPanels); + osg::Node* node = simgear::SGModelLib::loadModel(path.utf8Str(), prop_root, NULL, loadPanels); if (node) node->setNodeMask(~SG_NODEMASK_TERRAIN_BIT); return node; @@ -110,7 +110,7 @@ FGAircraftModel::init () // no models loaded, load the glider instead if (!_aircraft.get()) { SG_LOG(SG_AIRCRAFT, SG_ALERT, "(Falling back to glider.ac.)"); - osg::Node* model = fgLoad3DModelPanel( SGPath::fromLocal8Bit("Models/Geometry/glider.ac"), + osg::Node* model = fgLoad3DModelPanel( SGPath::fromUtf8("Models/Geometry/glider.ac"), globals->get_props()); _aircraft.reset(new SGModelPlacement); _aircraft->init(model); diff --git a/src/MultiPlayer/multiplaymgr.cxx b/src/MultiPlayer/multiplaymgr.cxx index 6418f7eb6..56cb1d67d 100644 --- a/src/MultiPlayer/multiplaymgr.cxx +++ b/src/MultiPlayer/multiplaymgr.cxx @@ -2381,7 +2381,7 @@ FGMultiplayMgr::addMultiplayer(const std::string& callsign, */ mp->_getProps()->removeChildren("set"); - SGPropertyNode* set = NULL; + SGPropertyNode_ptr set; if (simgear::strutils::ends_with(modelName, ".xml") && simgear::strutils::starts_with(modelName, "Aircraft/")) { @@ -2421,7 +2421,15 @@ FGMultiplayMgr::addMultiplayer(const std::string& callsign, actually it's just a suffix. */ for (auto path: dir_contents) { - set = mp->_getProps()->addChild("set"); + /* Load into a local SGPropertyNode. + + As of 2020-03-08 we don't load directly into the global property tree + because that appears to result in runtime-specific multiplayer values + being written to autosave*.xml and reloaded next time fgfs is run, + which results in lots of bogus properties within /ai/multiplayer. So + instead we load into a local SGPropertyNode, then copy selected values + into the global tree below. */ + set = new SGPropertyNode; bool ok = true; try { readProperties(path, set); @@ -2436,26 +2444,42 @@ FGMultiplayMgr::addMultiplayer(const std::string& callsign, break; } } - mp->_getProps()->removeChildren("set"); - set = NULL; + set.reset(); } } } - /* Copy [set]/sim/chase-distance-m (or -25 if not present) into - mp->set/sim/view[]/config/z-offset-m if not there. This attempts to mimic - what fgdata/defaults.xml does when it defines default views. Also copy - view[1]'s config into other views if not specified. */ + /* Copy values from our local /sim/view[]/config/* into global + /ai/models/multiplayer/set/sim/view[]/config/ so that we have view offsets + available for this multiplayer aircraft. */ + SGPropertyNode* global_set = mp->_getProps()->addChild("set"); + SGPropertyNode* global_sim = global_set->addChild("sim"); double sim_chase_distance_m = -25; if (set) { - sim_chase_distance_m = set->getDoubleValue("sim/chase-distance-m", sim_chase_distance_m); + SGPropertyNode* sim = set->getChild("sim"); + if (sim) { + /* Override if present. */ + sim_chase_distance_m = sim->getDoubleValue("chase-distance-m", sim_chase_distance_m); + global_sim->setDoubleValue("chase-distance-m", sim_chase_distance_m); + + simgear::PropertyList views = sim->getChildren("view"); + for (auto view: views) { + int view_index = view->getIndex(); + SGPropertyNode* global_view = global_sim->addChild("view", view_index, false /*append*/); + assert(global_view->getIndex() == view_index); + SGPropertyNode* config = view->getChild("config"); + SGPropertyNode* global_config = global_view->addChild("config"); + if (config) { + int config_children_n = config->nChildren(); + for (int i=0; igetChild(i); + global_config->setDoubleValue(node->getName(), node->getDoubleValue()); + } + } + } + } } - else { - set = mp->_getProps()->addChild("set"); - set->setDoubleValue("sim/chase-distance-m", sim_chase_distance_m); - } - SGPropertyNode* set_sim = set->getNode("sim"); - + /* For views that are similar to Helicopter View, copy across Helicopter View target offsets if not specified. E.g. this allows Tower View AGL to work on aircraft that don't know about it but need non-zero target-*-offset-m values @@ -2464,14 +2488,16 @@ FGMultiplayMgr::addMultiplayer(const std::string& callsign, This mimics what fgdata:Nasal/view.nas:manager does for the user aircraft's views. */ - SGPropertyNode* view_1 = set_sim->getNode("view", 1); + SGPropertyNode* view_1 = global_sim->getNode("view", 1); std::initializer_list views_with_default_z_offset_m = {1, 2, 3, 5, 7}; for (int j: views_with_default_z_offset_m) { - SGPropertyNode* v = set_sim->getChild("view", j); + SGPropertyNode* v = global_sim->getChild("view", j); if (!v) { - v = set_sim->addChild("view", j, false /*append*/); + v = global_sim->addChild("view", j, false /*append*/); } SGPropertyNode* z_offset_m = v->getChild("config/z-offset-m"); + /* Setting config/z-offset-m default to here mimics + what fgdata/defaults.xml does when it defines default views. */ if (!z_offset_m) { v->setDoubleValue("config/z-offset-m", sim_chase_distance_m); } diff --git a/src/Navaids/FlightPlan.cxx b/src/Navaids/FlightPlan.cxx index 61c94662a..2cd97607a 100644 --- a/src/Navaids/FlightPlan.cxx +++ b/src/Navaids/FlightPlan.cxx @@ -75,6 +75,9 @@ const string_list static_icaoFlightTypeCode = { namespace flightgear { +// implemented in route.cxx +const char* restrictionToString(RouteRestriction aRestrict); + typedef std::vector FPDelegateFactoryVec; static FPDelegateFactoryVec static_delegateFactories; @@ -109,11 +112,6 @@ FlightPlan::~FlightPlan() delete d; } } - -// delete legs - for (auto l : _legs) { - delete l; - } } FlightPlan* FlightPlan::clone(const string& newIdent) const @@ -160,7 +158,7 @@ string FlightPlan::ident() const return _ident; } -FlightPlan::Leg* FlightPlan::insertWayptAtIndex(Waypt* aWpt, int aIndex) +FlightPlan::LegRef FlightPlan::insertWayptAtIndex(Waypt* aWpt, int aIndex) { if (!aWpt) { return nullptr; @@ -189,9 +187,7 @@ void FlightPlan::insertWayptsAtIndex(const WayptVec& wps, int aIndex) index = _legs.size(); } - LegVec::iterator it = _legs.begin(); - it += index; - + auto it = _legs.begin() + index; int endIndex = index + wps.size() - 1; if (_currentIndex >= endIndex) { _currentIndex += wps.size(); @@ -199,7 +195,7 @@ void FlightPlan::insertWayptsAtIndex(const WayptVec& wps, int aIndex) LegVec newLegs; for (WayptRef wp : wps) { - newLegs.push_back(new Leg(this, wp)); + newLegs.push_back(LegRef{new Leg(this, wp)}); } lockDelegates(); @@ -223,12 +219,11 @@ void FlightPlan::deleteIndex(int aIndex) lockDelegates(); _waypointsChanged = true; - LegVec::iterator it = _legs.begin(); - it += index; - Leg* l = *it; + auto it = _legs.begin() + index; + LegRef l = *it; _legs.erase(it); - delete l; - + l->_parent = nullptr; // orphan the leg so it's clear from Nasal + if (_currentIndex == index) { // current waypoint was removed _currentWaypointChanged = true; @@ -255,43 +250,18 @@ void FlightPlan::clear() _cruiseDataChanged = true; _currentIndex = -1; - for (Leg* l : _legs) { - delete l; - } _legs.clear(); notifyCleared(); unlockDelegates(); } -class RemoveWithFlag -{ -public: - RemoveWithFlag(WayptFlag f) : flag(f), delCount(0) { } - - int numDeleted() const { return delCount; } - - bool operator()(FlightPlan::Leg* leg) const - { - if (leg->waypoint()->flag(flag)) { - delete leg; - ++delCount; - return true; - } - - return false; - } -private: - WayptFlag flag; - mutable int delCount; -}; - int FlightPlan::clearWayptsWithFlag(WayptFlag flag) { int count = 0; // first pass, fix up currentIndex for (int i=0; i<_currentIndex; ++i) { - Leg* l = _legs[i]; + const auto& l = _legs.at(i); if (l->waypoint()->flag(flag)) { ++count; } @@ -311,13 +281,21 @@ int FlightPlan::clearWayptsWithFlag(WayptFlag flag) // and let the use re-activate. // http://code.google.com/p/flightgear-bugs/issues/detail?id=1134 if (currentIsBeingCleared) { - SG_LOG(SG_GENERAL, SG_INFO, "currentIsBeingCleared:" << currentIsBeingCleared); + SG_LOG(SG_GENERAL, SG_INFO, "FlightPlan::clearWayptsWithFlag: currentIsBeingCleared:" << currentIsBeingCleared); _currentIndex = -1; } // now delete and remove - RemoveWithFlag rf(flag); - auto it = std::remove_if(_legs.begin(), _legs.end(), rf); + int numDeleted = 0; + auto it = std::remove_if(_legs.begin(), _legs.end(), + [flag, &numDeleted](const LegRef& leg) + { + if (leg->waypoint()->flag(flag)) { + ++numDeleted; + return true; + } + return false; + }); if (it == _legs.end()) { return 0; // nothing was cleared, don't fire the delegate } @@ -335,7 +313,7 @@ int FlightPlan::clearWayptsWithFlag(WayptFlag flag) } unlockDelegates(); - return rf.numDeleted(); + return numDeleted; } bool FlightPlan::isActive() const @@ -407,14 +385,14 @@ int FlightPlan::findWayptIndex(const FGPositionedRef aPos) const return -1; } -FlightPlan::Leg* FlightPlan::currentLeg() const +FlightPlan::LegRef FlightPlan::currentLeg() const { if ((_currentIndex < 0) || (_currentIndex >= numLegs())) return nullptr; return legAtIndex(_currentIndex); } -FlightPlan::Leg* FlightPlan::previousLeg() const +FlightPlan::LegRef FlightPlan::previousLeg() const { if (_currentIndex <= 0) { return nullptr; @@ -423,7 +401,7 @@ FlightPlan::Leg* FlightPlan::previousLeg() const return legAtIndex(_currentIndex - 1); } -FlightPlan::Leg* FlightPlan::nextLeg() const +FlightPlan::LegRef FlightPlan::nextLeg() const { if ((_currentIndex < 0) || ((_currentIndex + 1) >= numLegs())) { return nullptr; @@ -432,19 +410,19 @@ FlightPlan::Leg* FlightPlan::nextLeg() const return legAtIndex(_currentIndex + 1); } -FlightPlan::Leg* FlightPlan::legAtIndex(int index) const +FlightPlan::LegRef FlightPlan::legAtIndex(int index) const { if ((index < 0) || (index >= numLegs())) { throw sg_range_exception("index out of range", "FlightPlan::legAtIndex"); } - return _legs[index]; + return _legs.at(index); } -int FlightPlan::findLegIndex(const Leg *l) const +int FlightPlan::findLegIndex(const Leg* l) const { for (unsigned int i=0; i<_legs.size(); ++i) { - if (_legs[i] == l) { + if (_legs.at(i).get() == l) { return i; } } @@ -778,8 +756,11 @@ void FlightPlan::saveToProperties(SGPropertyNode* d) const // route nodes SGPropertyNode* routeNode = d->getChild("route", 0, true); for (unsigned int i=0; i<_legs.size(); ++i) { - Waypt* wpt = _legs[i]->waypoint(); - wpt->saveAsNode(routeNode->getChild("wp", i, true)); + auto leg = _legs.at(i); + Waypt* wpt = leg->waypoint(); + auto legNode = routeNode->getChild("wp", i, true); + wpt->saveAsNode(legNode); + leg->writeToProperties(legNode); } // of waypoint iteration } @@ -1119,7 +1100,20 @@ void FlightPlan::loadVersion2XMLRoute(SGPropertyNode_ptr routeData) SGPropertyNode_ptr routeNode = routeData->getChild("route", 0); if (routeNode.valid()) { for (auto wpNode : routeNode->getChildren("wp")) { - Leg* l = new Leg{this, Waypt::createFromProperties(this, wpNode)}; + auto wp = Waypt::createFromProperties(this, wpNode); + LegRef l = new Leg{this, wp}; + // sync leg restrictions with waypoint ones + if (wp->speedRestriction() != RESTRICT_NONE) { + l->setSpeed(wp->speedRestriction(), wp->speed()); + } + + if (wp->altitudeRestriction() != RESTRICT_NONE) { + l->setAltitude(wp->altitudeRestriction(), wp->altitudeFt()); + } + + if (wpNode->hasChild("hold-count")) { + l->setHoldCount(wpNode->getIntValue("hold-count")); + } _legs.push_back(l); } // of route iteration } @@ -1135,7 +1129,7 @@ void FlightPlan::loadVersion1XMLRoute(SGPropertyNode_ptr routeData) SGPropertyNode_ptr routeNode = routeData->getChild("route", 0); for (int i=0; inChildren(); ++i) { SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i); - Leg* l = new Leg(this, parseVersion1XMLWaypt(wpNode)); + LegRef l = new Leg(this, parseVersion1XMLWaypt(wpNode)); _legs.push_back(l); } // of route iteration _waypointsChanged = true; @@ -1218,7 +1212,7 @@ bool FlightPlan::loadPlainTextFormat(const SGPath& path) throw sg_io_exception("Failed to create waypoint from line '" + line + "'."); } - _legs.push_back(new Leg(this, w)); + _legs.push_back(LegRef{new Leg(this, w)}); } // of line iteration } catch (sg_exception& e) { SG_LOG(SG_NAVAID, SG_ALERT, "Failed to load route from: '" << path << "'. " << e.getMessage()); @@ -1425,26 +1419,23 @@ void FlightPlan::activate() _currentIndex = 0; _currentWaypointChanged = true; - for (unsigned int i=0; i < _legs.size(); ) { + for (unsigned int i=1; i < _legs.size(); ) { if (_legs[i]->waypoint()->type() == "via") { WayptRef preceeding = _legs[i - 1]->waypoint(); Via* via = static_cast(_legs[i]->waypoint()); WayptVec wps = via->expandToWaypoints(preceeding); // delete the VIA leg - LegVec::iterator it = _legs.begin(); - it += i; - Leg* l = *it; + auto it = _legs.begin() + i; + LegRef l = *it; _legs.erase(it); - delete l; // create new legs and insert - it = _legs.begin(); - it += i; + it = _legs.begin() + i; LegVec newLegs; for (WayptRef wp : wps) { - newLegs.push_back(new Leg(this, wp)); + newLegs.push_back(LegRef{new Leg(this, wp)}); } _waypointsChanged = true; @@ -1630,6 +1621,28 @@ int FlightPlan::Leg::holdCount() const return _holdCount; } +void FlightPlan::Leg::writeToProperties(SGPropertyNode* aProp) const +{ + if (_speedRestrict != RESTRICT_NONE) { + aProp->setStringValue("speed-restrict", restrictionToString(_speedRestrict)); + if (_speedRestrict == SPEED_RESTRICT_MACH) { + aProp->setDoubleValue("speed", speedMach()); + } else { + aProp->setDoubleValue("speed", _speed); + } + } + + if (_altRestrict != RESTRICT_NONE) { + aProp->setStringValue("alt-restrict", restrictionToString(_altRestrict)); + aProp->setDoubleValue("altitude-ft", _altitudeFt); + } + + if (_holdCount > 0) { + aProp->setDoubleValue("hold-count", _holdCount); + } +} + + void FlightPlan::rebuildLegData() { _totalDistance = 0.0; diff --git a/src/Navaids/FlightPlan.hxx b/src/Navaids/FlightPlan.hxx index aa5b145f5..ed2c32acc 100644 --- a/src/Navaids/FlightPlan.hxx +++ b/src/Navaids/FlightPlan.hxx @@ -80,7 +80,7 @@ public: /** * flight-plan leg encapsulation */ - class Leg + class Leg : public SGReferenced { public: FlightPlan* owner() const @@ -138,6 +138,8 @@ public: Leg* cloneFor(FlightPlan* owner) const; + void writeToProperties(SGPropertyNode* node) const; + const FlightPlan* _parent; RouteRestriction _speedRestrict = RESTRICT_NONE, _altRestrict = RESTRICT_NONE; @@ -157,6 +159,8 @@ public: /// total distance of this leg from departure point mutable double _distanceAlongPath = 11.0; }; + + using LegRef = SGSharedPtr; class Delegate { @@ -191,7 +195,7 @@ public: bool _deleteWithPlan = false; }; - Leg* insertWayptAtIndex(Waypt* aWpt, int aIndex); + LegRef insertWayptAtIndex(Waypt* aWpt, int aIndex); void insertWayptsAtIndex(const WayptVec& wps, int aIndex); void deleteIndex(int index); @@ -211,15 +215,14 @@ public: bool isActive() const; - Leg* currentLeg() const; - Leg* nextLeg() const; - Leg* previousLeg() const; + LegRef currentLeg() const; + LegRef nextLeg() const; + LegRef previousLeg() const; int numLegs() const { return static_cast(_legs.size()); } - Leg* legAtIndex(int index) const; - int findLegIndex(const Leg* l) const; + LegRef legAtIndex(int index) const; int findWayptIndex(const SGGeod& aPos) const; int findWayptIndex(const FGPositionedRef aPos) const; @@ -373,6 +376,8 @@ public: private: friend class Leg; + int findLegIndex(const Leg* l) const; + void lockDelegates(); void unlockDelegates(); @@ -426,7 +431,7 @@ private: double _totalDistance; void rebuildLegData(); - typedef std::vector LegVec; + using LegVec = std::vector; LegVec _legs; std::vector _delegates; diff --git a/src/Navaids/NavDataCache.cxx b/src/Navaids/NavDataCache.cxx index 22f5dd8e1..50d49fe25 100644 --- a/src/Navaids/NavDataCache.cxx +++ b/src/Navaids/NavDataCache.cxx @@ -30,6 +30,7 @@ #include #include // for int64_t #include // for std::ostringstream +#include #ifdef SYSTEM_SQLITE // the standard sqlite3.h doesn't give a way to set SQLITE_UINT64_TYPE, @@ -54,7 +55,6 @@ #include #include #include -#include #include
#include
@@ -172,7 +172,7 @@ public: bool isFinished() const { - SGGuard g(_lock); + std::lock_guard g(_lock); return _isFinished; } @@ -183,7 +183,7 @@ public: _cache->doRebuild(); SG_LOG(SG_NAVCACHE, SG_INFO, "cache rebuild took:" << st.elapsedMSec() << "msec"); - SGGuard g(_lock); + std::lock_guard g(_lock); _isFinished = true; _phase = NavDataCache::REBUILD_DONE; } @@ -191,7 +191,7 @@ public: NavDataCache::RebuildPhase currentPhase() const { NavDataCache::RebuildPhase ph; - SGGuard g(_lock); + std::lock_guard g(_lock); ph = _phase; return ph; } @@ -199,14 +199,14 @@ public: unsigned int completionPercent() const { unsigned int perc = 0; - SGGuard g(_lock); + std::lock_guard g(_lock); perc = _completionPercent; return perc; } void setProgress(NavDataCache::RebuildPhase ph, unsigned int percent) { - SGGuard g(_lock); + std::lock_guard g(_lock); _phase = ph; _completionPercent = percent; } @@ -215,7 +215,7 @@ private: NavDataCache* _cache; NavDataCache::RebuildPhase _phase; unsigned int _completionPercent; - mutable SGMutex _lock; + mutable std::mutex _lock; bool _isFinished; }; @@ -2529,7 +2529,7 @@ public: break; } else if (err == SQLITE_ROW) { PositionedID r = sqlite3_column_int64(query, 0); - SGGuard g(lock); + std::lock_guard g(lock); results.push_back(r); } else if (err == SQLITE_BUSY) { // sleep a tiny amount @@ -2540,11 +2540,11 @@ public: } } - SGGuard g(lock); + std::lock_guard g(lock); isComplete = true; } - SGMutex lock; + std::mutex lock; sqlite3* db; sqlite3_stmt_ptr query; PositionedIDVec results; @@ -2576,7 +2576,7 @@ NavDataCache::ThreadedGUISearch::ThreadedGUISearch(const std::string& term, bool NavDataCache::ThreadedGUISearch::~ThreadedGUISearch() { { - SGGuard g(d->lock); + std::lock_guard g(d->lock); d->quit = true; } @@ -2589,7 +2589,7 @@ PositionedIDVec NavDataCache::ThreadedGUISearch::results() const { PositionedIDVec r; { - SGGuard g(d->lock); + std::lock_guard g(d->lock); r = std::move(d->results); } return r; @@ -2597,7 +2597,7 @@ PositionedIDVec NavDataCache::ThreadedGUISearch::results() const bool NavDataCache::ThreadedGUISearch::isComplete() const { - SGGuard g(d->lock); + std::lock_guard g(d->lock); return d->isComplete; } diff --git a/src/Navaids/SHPParser.cxx b/src/Navaids/SHPParser.cxx index 2d31bf237..58f597760 100644 --- a/src/Navaids/SHPParser.cxx +++ b/src/Navaids/SHPParser.cxx @@ -91,8 +91,13 @@ namespace flightgear void SHPParser::parsePolyLines(const SGPath& aPath, PolyLine::Type aTy, PolyLineList& aResult, bool aClosed) { - std::string s = aPath.local8BitStr(); +#if defined(SG_WINDOWS) + const auto ws = aPath.wstr(); + gzFile file = gzopen_w(ws.c_str(), "rb"); +#else + const auto s = aPath.utf8Str(); gzFile file = gzopen(s.c_str(), "rb"); +#endif if (!file) { throw sg_io_exception("couldn't open file:", aPath); } diff --git a/src/Navaids/route.cxx b/src/Navaids/route.cxx index a61f9a7f2..d2973b52b 100644 --- a/src/Navaids/route.cxx +++ b/src/Navaids/route.cxx @@ -175,7 +175,7 @@ static RouteRestriction restrictionFromString(const char* aStr) "Route restrictFromString"); } -static const char* restrictionToString(RouteRestriction aRestrict) +const char* restrictionToString(RouteRestriction aRestrict) { switch (aRestrict) { case RESTRICT_AT: return "at"; diff --git a/src/Network/HTTPClient.cxx b/src/Network/HTTPClient.cxx index 1e3e72faa..641599103 100644 --- a/src/Network/HTTPClient.cxx +++ b/src/Network/HTTPClient.cxx @@ -47,10 +47,10 @@ typedef nasal::Ghost NasalPackage; typedef nasal::Ghost NasalCatalog; typedef nasal::Ghost NasalInstall; -const char* OFFICIAL_CATALOG_ID = "org.flightgear.fgaddon.trunk"; +static const char* OFFICIAL_CATALOG_ID = "org.flightgear.fgaddon.trunk"; // fallback URL is used when looking up a version-specific catalog fails -const char* FALLBACK_CATALOG_URL = "http://mirrors.ibiblio.org/flightgear/ftp/Aircraft-trunk/catalog.xml"; +static const char* FALLBACK_CATALOG_URL = "http://mirrors.ibiblio.org/flightgear/ftp/Aircraft-trunk/catalog.xml"; namespace { @@ -274,7 +274,7 @@ void FGHTTPClient::postinit() pkg::Root* packageRoot = globals->packageRoot(); if (packageRoot) { - FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal"); + FGNasalSys* nasalSys = globals->get_subsystem(); nasal::Hash nasalGlobals = nasalSys->getGlobals(); nasal::Hash nasalPkg = nasalGlobals.createHash("pkg"); // module nasalPkg.set("root", packageRoot); diff --git a/src/Network/Swift/SwiftAircraft.cpp b/src/Network/Swift/SwiftAircraft.cpp index f4bf52140..7b0d639f2 100644 --- a/src/Network/Swift/SwiftAircraft.cpp +++ b/src/Network/Swift/SwiftAircraft.cpp @@ -42,7 +42,7 @@ #include #include -FGSwiftAircraft::FGSwiftAircraft(std::string callsign, std::string modelpath) +FGSwiftAircraft::FGSwiftAircraft(const std::string& callsign, const std::string& modelpath, SGPropertyNode_ptr p) { using namespace simgear; _model = SGModelLib::loadModel(modelpath); @@ -52,10 +52,14 @@ FGSwiftAircraft::FGSwiftAircraft(std::string callsign, std::string modelpath) aip.setVisible(true); aip.update(); globals->get_scenery()->get_models_branch()->addChild(aip.getSceneGraph()); + + props = p; + props->setStringValue("callsign", callsign); + props->setBoolValue("valid",true); } } -bool FGSwiftAircraft::updatePosition(SGGeod newPosition, SGVec3d orientation) +bool FGSwiftAircraft::updatePosition(SGGeod newPosition, SGVec3d orientation, double groundspeed, bool initPos) { position = newPosition; @@ -64,39 +68,66 @@ bool FGSwiftAircraft::updatePosition(SGGeod newPosition, SGVec3d orientation) aip.setRollDeg(orientation.y()); aip.setHeadingDeg(orientation.z()); aip.update(); + + this->initPos = initPos; + + //Update props + props->setDoubleValue("orientation/pitch-deg", orientation.x()); + props->setDoubleValue("orientation/roll-deg", orientation.y()); + props->setDoubleValue("orientation/true-heading-deg", orientation.z()); + SGVec3d cartPos = SGVec3d::fromGeod(position); + + props->setDoubleValue("position/global-x", cartPos.x()); + props->setDoubleValue("position/global-y", cartPos.y()); + props->setDoubleValue("position/global-z", cartPos.z()); + + + props->setDoubleValue("position/latitude-deg", position.getLatitudeDeg()); + props->setDoubleValue("position/longitude-deg", position.getLongitudeDeg()); + props->setDoubleValue("position/altitude-ft", position.getElevationFt()); + + props->setDoubleValue("velocities/true-airspeed-kt", groundspeed); + return true; } FGSwiftAircraft::~FGSwiftAircraft() { + props->setBoolValue("valid",false); + props->setIntValue("id",-1); aip.setVisible(false); } -double FGSwiftAircraft::getLatDeg() +double FGSwiftAircraft::getLatDeg() const { return position.getLatitudeDeg(); } -double FGSwiftAircraft::getLongDeg() +double FGSwiftAircraft::getLongDeg() const { return position.getLongitudeDeg(); } -double FGSwiftAircraft::getFudgeFactor() +double FGSwiftAircraft::getFudgeFactor() const { return 0; } -inline bool FGSwiftAircraft::operator<(std::string extCallsign) +inline bool FGSwiftAircraft::operator<(const std::string& extCallsign) { return _model->getName().compare(extCallsign); } -double FGSwiftAircraft::getGroundElevation() +double FGSwiftAircraft::getGroundElevation(const SGGeod& pos) const { - double alt; - globals->get_scenery()->get_elevation_m(position,alt,0); - return alt; + if(!initPos) { return std::numeric_limits::quiet_NaN(); } + double alt = 0; + SGGeod posReq; + posReq.setElevationFt(30000); + posReq.setLatitudeDeg(pos.getLatitudeDeg()); + posReq.setLongitudeDeg(pos.getLongitudeDeg()); + globals->get_scenery()->get_elevation_m(posReq,alt,0,_model.get()); + return alt; } \ No newline at end of file diff --git a/src/Network/Swift/SwiftAircraft.h b/src/Network/Swift/SwiftAircraft.h index 62949236e..2bad224d5 100644 --- a/src/Network/Swift/SwiftAircraft.h +++ b/src/Network/Swift/SwiftAircraft.h @@ -41,19 +41,21 @@ class PagedLOD; class FGSwiftAircraft { public: - FGSwiftAircraft(std::string callsign, std::string modelpath); - bool updatePosition(SGGeod newPosition, SGVec3d orientation); + FGSwiftAircraft(const std::string& callsign, const std::string& modelpath, SGPropertyNode_ptr p); + bool updatePosition(SGGeod newPosition, SGVec3d orientation, double groundspeed, bool initPos); ~FGSwiftAircraft(); std::string getName() { return _model->getName(); }; - double getLatDeg(); - double getLongDeg(); - double getGroundElevation(); - double getFudgeFactor(); + double getLatDeg() const; + double getLongDeg() const; + double getGroundElevation(const SGGeod& pos) const; + double getFudgeFactor() const; private: + bool initPos = false; SGGeod position; + SGPropertyNode_ptr props; osg::ref_ptr _model; SGModelPlacement aip; - inline bool operator<(std::string extCallsign); + inline bool operator<(const std::string& extCallsign); }; #endif \ No newline at end of file diff --git a/src/Network/Swift/SwiftAircraftManager.cpp b/src/Network/Swift/SwiftAircraftManager.cpp index df8beac97..8b2e6adfb 100644 --- a/src/Network/Swift/SwiftAircraftManager.cpp +++ b/src/Network/Swift/SwiftAircraftManager.cpp @@ -19,31 +19,43 @@ #include "SwiftAircraftManager.h" #include "SwiftAircraft.h" +#include
+#include FGSwiftAircraftManager::FGSwiftAircraftManager() -{ -} += default; FGSwiftAircraftManager::~FGSwiftAircraftManager() -{ -} += default; -bool FGSwiftAircraftManager::addPlane(std::string callsign, std::string modelString) +bool FGSwiftAircraftManager::addPlane(const std::string& callsign, std::string modelString) { if (aircraftByCallsign.find(callsign) != aircraftByCallsign.end()) return false; - FGSwiftAircraft* curAircraft = new FGSwiftAircraft(callsign, modelString); + const char* typeString = "swift"; + SGPropertyNode_ptr root = globals->get_props()->getNode("ai/models",true); + SGPropertyNode_ptr p; + int i; + for(i = 0; i < 10000; i++){ + p = root->getNode(typeString,i,false); + + if(!p || !p->getBoolValue("valid",false)) + break; + } + p = root->getNode(typeString,i,true); + p->setIntValue("id",i); + auto curAircraft = new FGSwiftAircraft(callsign, std::move(modelString), p); aircraftByCallsign.insert(std::pair(callsign, curAircraft)); return true; } -void FGSwiftAircraftManager::updatePlanes(std::vector callsigns, std::vector positions, std::vector orientations, std::vector onGrounds) +void FGSwiftAircraftManager::updatePlanes(std::vector callsigns, std::vector positions, std::vector orientations, std::vector groundspeeds, std::vector onGrounds) { - for (int i = 0; i < callsigns.size(); i++) { + for (long unsigned int i = 0; i < callsigns.size(); i++) { auto it = aircraftByCallsign.find(callsigns.at(i)); if (it != aircraftByCallsign.end()) { - it->second->updatePosition(positions.at(i), orientations.at(i)); + it->second->updatePosition(positions.at(i), orientations.at(i), groundspeeds.at(i),true); } } @@ -51,6 +63,8 @@ void FGSwiftAircraftManager::updatePlanes(std::vector callsigns, st void FGSwiftAircraftManager::getRemoteAircraftData(std::vector& callsigns, std::vector& latitudesDeg, std::vector& longitudesDeg, std::vector& elevationsM, std::vector& verticalOffsets) const { + if (callsigns.empty() || aircraftByCallsign.empty()) { return; } + const auto requestedCallsigns = callsigns; callsigns.clear(); latitudesDeg.clear(); @@ -58,25 +72,32 @@ void FGSwiftAircraftManager::getRemoteAircraftData(std::vector& cal elevationsM.clear(); verticalOffsets.clear(); - for (int i = 0; i < requestedCallsigns.size(); i++) { - auto it = aircraftByCallsign.find(requestedCallsigns.at(i)); - if (it != aircraftByCallsign.end()) { - double latDeg = it->second->getLatDeg(); - double lonDeg = it->second->getLongDeg(); - double groundElevation = it->second->getGroundElevation(); - double fudgeFactor = it->second->getFudgeFactor(); + for (const auto & requestedCallsign : requestedCallsigns) { + const auto it = aircraftByCallsign.find(requestedCallsign); + if(it == aircraftByCallsign.end()) { continue; } - callsigns.push_back(requestedCallsigns.at(i)); - latitudesDeg.push_back(latDeg); - longitudesDeg.push_back(lonDeg); - elevationsM.push_back(groundElevation); - verticalOffsets.push_back(0); - } + const FGSwiftAircraft *aircraft = it->second; + assert(aircraft); + + + SGGeod pos; + pos.setLatitudeDeg(aircraft->getLatDeg()); + pos.setLongitudeDeg(aircraft->getLongDeg()); + const double latDeg = pos.getLatitudeDeg(); + const double lonDeg = pos.getLongitudeDeg(); + double groundElevation = aircraft->getGroundElevation(pos); + + double fudgeFactor = aircraft->getFudgeFactor(); + (void)fudgeFactor; + callsigns.push_back(requestedCallsign); + latitudesDeg.push_back(latDeg); + longitudesDeg.push_back(lonDeg); + elevationsM.push_back(groundElevation); + verticalOffsets.push_back(0); } - } -void FGSwiftAircraftManager::removePlane(std::string callsign) +void FGSwiftAircraftManager::removePlane(const std::string& callsign) { auto it = aircraftByCallsign.find(callsign); if (it != aircraftByCallsign.end()) { @@ -92,3 +113,12 @@ void FGSwiftAircraftManager::removeAllPlanes() aircraftByCallsign.erase(it); } } + +double FGSwiftAircraftManager::getElevationAtPosition(const std::string &callsign, const SGGeod& pos) const +{ + auto it = aircraftByCallsign.find(callsign); + if(it != aircraftByCallsign.end()){ + return it->second->getGroundElevation(pos); + } + return std::numeric_limits::quiet_NaN(); +} diff --git a/src/Network/Swift/SwiftAircraftManager.h b/src/Network/Swift/SwiftAircraftManager.h index fbdbb4239..0c1e731c2 100644 --- a/src/Network/Swift/SwiftAircraftManager.h +++ b/src/Network/Swift/SwiftAircraftManager.h @@ -18,6 +18,7 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "SwiftAircraft.h" +#include #include #ifndef FGSWIFTAIRCRAFTMANAGER_H @@ -29,11 +30,12 @@ public: FGSwiftAircraftManager(); ~FGSwiftAircraftManager(); std::map aircraftByCallsign; - bool addPlane(std::string callsign, std::string modelString); - void updatePlanes(std::vector callsigns, std::vector positions, std::vector orientations, std::vector onGrounds); + bool addPlane(const std::string& callsign, std::string modelString); + void updatePlanes(std::vector callsigns, std::vector positions, std::vector orientations, std::vector groundspeeds, std::vector onGrounds); void getRemoteAircraftData(std::vector& callsigns, std::vector& latitudesDeg, std::vector& longitudesDeg, std::vector& elevationsM, std::vector& verticalOffsets) const; - void removePlane(std::string callsign); + void removePlane(const std::string& callsign); void removeAllPlanes(); + double getElevationAtPosition(const std::string &callsign, const SGGeod& pos) const; }; #endif \ No newline at end of file diff --git a/src/Network/Swift/plugin.cpp b/src/Network/Swift/plugin.cpp index 9ad727020..15a8ec6be 100644 --- a/src/Network/Swift/plugin.cpp +++ b/src/Network/Swift/plugin.cpp @@ -44,7 +44,10 @@ CPlugin::CPlugin() CPlugin::~CPlugin() { - m_dbusConnection->close(); + if(m_dbusConnection) + { + m_dbusConnection->close(); + } m_shouldStop = true; if (m_dbusThread.joinable()) { m_dbusThread.join(); } } @@ -79,7 +82,7 @@ void CPlugin::startServer() float CPlugin::startServerDeferred(float, float, int, void* refcon) { - auto* plugin = static_cast(refcon); + auto plugin = static_cast(refcon); if (!plugin->m_isRunning) { plugin->startServer(); plugin->m_isRunning = true; diff --git a/src/Network/Swift/service.cpp b/src/Network/Swift/service.cpp index 1adb6b17c..27de5fee1 100644 --- a/src/Network/Swift/service.cpp +++ b/src/Network/Swift/service.cpp @@ -19,22 +19,50 @@ #include "service.h" #include
-#include #include -#include #include -#include -#include -#include #include -#include -#include -#include + +#define FGSWIFTBUS_API_VERSION 1; namespace FGSwiftBus { CService::CService() { + // Initialize node pointers + textMessageNode = fgGetNode("/sim/messages/copilot"); + aircraftModelPathNode = fgGetNode("/sim/aircraft-dir"); + aircraftDescriptionNode = fgGetNode("/sim/description"); + isPausedNode = fgGetNode("/sim/freeze/master"); + latitudeNode = fgGetNode("/position/latitude-deg"); + longitudeNode = fgGetNode("/position/longitude-deg"); + altitudeMSLNode = fgGetNode("/position/altitude-ft"); + heightAGLNode = fgGetNode("/position/altitude-agl-ft"); + groundSpeedNode = fgGetNode("/velocities/groundspeed-kt"); + pitchNode = fgGetNode("/orientation/pitch-deg"); + rollNode = fgGetNode("/orientation/roll-deg"); + trueHeadingNode = fgGetNode("/orientation/heading-deg"); + wheelsOnGroundNode = fgGetNode("/gear/gear/wow"); + com1ActiveNode = fgGetNode("/instrumentation/comm/frequencies/selected-mhz"); + com1StandbyNode = fgGetNode("/instrumentation/comm/frequencies/standby-mhz"); + com2ActiveNode = fgGetNode("/instrumentation/comm[1]/frequencies/selected-mhz"); + com2StandbyNode = fgGetNode("/instrumentation/comm[1]/frequencies/standby-mhz"); + transponderCodeNode = fgGetNode("/instrumentation/transponder/id-code"); + transponderModeNode = fgGetNode("/instrumentation/transponder/inputs/knob-mode"); + transponderIdentNode = fgGetNode("/instrumentation/transponder/ident"); + beaconLightsNode = fgGetNode("/controls/lighting/beacon"); + landingLightsNode = fgGetNode("/controls/lighting/landing-lights"); + navLightsNode = fgGetNode("/controls/lighting/nav-lights"); + strobeLightsNode = fgGetNode("/controls/lighting/strobe"); + taxiLightsNode = fgGetNode("/controls/lighting/taxi-light"); + altimeterServiceableNode = fgGetNode("/instrumentation/altimeter/serviceable"); + pressAltitudeFtNode = fgGetNode("/instrumentation/altimeter/pressure-alt-ft"); + flapsDeployRatioNode = fgGetNode("/surface-positions/flap-pos-norm"); + gearDeployRatioNode = fgGetNode("/gear/gear/position-norm"); + speedBrakeDeployRatioNode = fgGetNode("/surface-positions/speedbrake-pos-norm"); + aircraftNameNode = fgGetNode("/sim/aircraft"); + groundElevation = fgGetNode("/position/ground-elev-m"); + SG_LOG(SG_NETWORK, SG_INFO, "FGSwiftBus Service initialized"); } @@ -50,20 +78,20 @@ const std::string& CService::ObjectPath() } // Static method -std::string CService::getVersionNumber() +int CService::getVersionNumber() { - return fgGetString("/sim/version/flightgear"); + return FGSWIFTBUS_API_VERSION; } void CService::addTextMessage(const std::string& text) { if (text.empty()) { return; } - fgSetString("/sim/messages/copilot", text); + textMessageNode->setStringValue(text); } std::string CService::getAircraftModelPath() const { - return fgGetString("/sim/aircraft-dir"); + return aircraftModelPathNode->getStringValue(); } std::string CService::getAircraftLivery() const @@ -78,176 +106,178 @@ std::string CService::getAircraftIcaoCode() const std::string CService::getAircraftDescription() const { - return fgGetString("/sim/description"); + return aircraftDescriptionNode->getStringValue(); } bool CService::isPaused() const { - return fgGetBool("/sim/freeze/master"); + return isPausedNode->getBoolValue(); } double CService::getLatitude() const { - return fgGetDouble("/position/latitude-deg"); + return latitudeNode->getDoubleValue(); } double CService::getLongitude() const { - return fgGetDouble("/position/longitude-deg"); + return longitudeNode->getDoubleValue(); } double CService::getAltitudeMSL() const { - return fgGetDouble("/position/altitude-ft"); + return altitudeMSLNode->getDoubleValue(); } double CService::getHeightAGL() const { - return fgGetDouble("/position/altitude-agl-ft"); + return heightAGLNode->getDoubleValue(); } double CService::getGroundSpeed() const { - return fgGetDouble("/velocities/groundspeed-kt"); + return groundSpeedNode->getDoubleValue(); } double CService::getPitch() const { - return fgGetDouble("/orientation/pitch-deg"); + return pitchNode->getDoubleValue(); } double CService::getRoll() const { - return fgGetDouble("/orientation/roll-deg"); + return rollNode->getDoubleValue(); } double CService::getTrueHeading() const { - return fgGetDouble("/orientation/heading-deg"); + return trueHeadingNode->getDoubleValue(); } bool CService::getAllWheelsOnGround() const { - return fgGetBool("/gear/gear/wow"); + return wheelsOnGroundNode->getBoolValue(); } int CService::getCom1Active() const -{ - return fgGetDouble("/instrumentation/comm/frequencies/selected-mhz") * 1000; +{ + return (int)(com1ActiveNode->getDoubleValue() * 1000); } int CService::getCom1Standby() const { - return fgGetDouble("/instrumentation/comm/frequencies/standby-mhz") * 1000; + return (int)(com1StandbyNode->getDoubleValue() * 1000); } int CService::getCom2Active() const { - return fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz") * 1000; + return (int)(com2ActiveNode->getDoubleValue() * 1000); } int CService::getCom2Standby() const { - return fgGetDouble("/instrumentation/comm[1]/frequencies/standby-mhz") * 1000; + return (int)(com2StandbyNode->getDoubleValue() * 1000); } int CService::getTransponderCode() const { - return fgGetInt("/instrumentation/transponder/id-code"); + return transponderCodeNode->getIntValue(); } int CService::getTransponderMode() const { - return fgGetInt("/instrumentation/transponder/inputs/knob-mode"); + return transponderModeNode->getIntValue(); } bool CService::getTransponderIdent() const { - return fgGetBool("/instrumentation/transponder/ident"); + return transponderIdentNode->getBoolValue(); } bool CService::getBeaconLightsOn() const { - return fgGetBool("/controls/lighting/beacon"); + return beaconLightsNode->getBoolValue(); } bool CService::getLandingLightsOn() const { - return fgGetBool("/controls/lighting/landing-lights"); + return landingLightsNode->getBoolValue(); } bool CService::getNavLightsOn() const { - return fgGetBool("/controls/lighting/nav-lights"); + return navLightsNode->getBoolValue(); } bool CService::getStrobeLightsOn() const { - return fgGetBool("/controls/lighting/strobe"); + return strobeLightsNode->getBoolValue(); } bool CService::getTaxiLightsOn() const { - return fgGetBool("/controls/lighting/taxi-light"); + return taxiLightsNode->getBoolValue(); } double CService::getPressAlt() const { - if (fgGetBool("/instrumentation/altimeter/serviceable")){ - return fgGetDouble("/instrumentation/altimeter/pressure-alt-ft"); + if (altimeterServiceableNode->getBoolValue()){ + return pressAltitudeFtNode->getDoubleValue(); } else { - return fgGetDouble("/position/altitude-ft"); + return altitudeMSLNode->getDoubleValue(); } } void CService::setCom1Active(int freq) { - fgSetDouble("/instrumentation/comm/frequencies/selected-mhz", freq / (double)1000); + com1ActiveNode->setDoubleValue(freq /(double)1000); } void CService::setCom1Standby(int freq) -{ - fgSetDouble("/instrumentation/comm/frequencies/standby-mhz", freq / (double)1000); +{ + com1StandbyNode->setDoubleValue(freq /(double)1000); } void CService::setCom2Active(int freq) { - fgSetDouble("/instrumentation/comm[1]/frequencies/selected-mhz", freq / (double)1000); + com2ActiveNode->setDoubleValue(freq /(double)1000); } void CService::setCom2Standby(int freq) { - fgSetDouble("/instrumentation/comm[1]/frequencies/standby-mhz", freq / (double)1000); + com2StandbyNode->setDoubleValue(freq /(double)1000); } void CService::setTransponderCode(int code) { - fgSetInt("/instrumentation/transponder/id-code", code); + transponderCodeNode->setIntValue(code); } void CService::setTransponderMode(int mode) { - fgSetInt("/instrumentation/transponder/inputs/knob-mode", mode); + transponderModeNode->setIntValue(mode); } double CService::getFlapsDeployRatio() const { - return fgGetFloat("/surface-positions/flap-pos-norm"); + return flapsDeployRatioNode->getFloatValue(); } double CService::getGearDeployRatio() const { - return fgGetFloat("/gear/gear/position-norm"); + return gearDeployRatioNode->getFloatValue(); } int CService::getNumberOfEngines() const -{ +{ + // TODO Use correct property return 2; } std::vector CService::getEngineN1Percentage() const { + // TODO use correct engine numbers std::vector list; const auto number = static_cast(getNumberOfEngines()); list.reserve(number); @@ -259,26 +289,31 @@ std::vector CService::getEngineN1Percentage() const double CService::getSpeedBrakeRatio() const { - return fgGetFloat("/surface-positions/speedbrake-pos-norm"); + return speedBrakeDeployRatioNode->getFloatValue(); +} + +double CService::getGroundElevation() const +{ + return groundElevation->getDoubleValue(); } std::string CService::getAircraftModelFilename() const { - std::string modelFileName = fgGetString("/sim/aircraft"); + std::string modelFileName = getAircraftName(); modelFileName.append("-set.xml"); return modelFileName; } std::string CService::getAircraftModelString() const { - std::string modelName = fgGetString("/sim/aircraft"); + std::string modelName = getAircraftName(); std::string modelString = "FG " + modelName; return modelString; } std::string CService::getAircraftName() const { - return fgGetString("/sim/aircraft"); + return aircraftNameNode->getStringValue(); } static const char* introspection_service = DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE; @@ -446,6 +481,10 @@ DBusHandlerResult CService::dbusMessageHandler(const CDBusMessage& message_) queueDBusCall([=]() { sendDBusReply(sender, serial, getPressAlt()); }); + } else if (message.getMethodName() == "getGroundElevation") { + queueDBusCall([=]() { + sendDBusReply(sender, serial, getGroundElevation()); + }); } else if (message.getMethodName() == "setCom1ActiveKhz") { maybeSendEmptyDBusReply(wantsReply, sender, serial); int frequency = 0; diff --git a/src/Network/Swift/service.h b/src/Network/Swift/service.h index a8747e810..d4c264eea 100644 --- a/src/Network/Swift/service.h +++ b/src/Network/Swift/service.h @@ -65,7 +65,7 @@ public: static const std::string& ObjectPath(); //! Getting flightgear version - static std::string getVersionNumber(); + static int getVersionNumber(); ////! Add a text message to the on-screen display, with RGB components in the range [0,1] void addTextMessage(const std::string& text); @@ -193,12 +193,53 @@ public: //! Get the ratio how much the speedbrakes surfaces are extended (0.0 is fully retracted, and 1.0 is fully extended) double getSpeedBrakeRatio() const; + //! Get ground elevation at aircraft current position + double getGroundElevation() const; + //! Perform generic processing int process(); protected: DBusHandlerResult dbusMessageHandler(const CDBusMessage& message) override; +private: + SGPropertyNode_ptr textMessageNode; + SGPropertyNode_ptr aircraftModelPathNode; + //SGPropertyNode_ptr aircraftLiveryNode; + //SGPropertyNode_ptr aircraftIcaoCodeNode; + SGPropertyNode_ptr aircraftDescriptionNode; + SGPropertyNode_ptr isPausedNode; + SGPropertyNode_ptr latitudeNode; + SGPropertyNode_ptr longitudeNode; + SGPropertyNode_ptr altitudeMSLNode; + SGPropertyNode_ptr heightAGLNode; + SGPropertyNode_ptr groundSpeedNode; + SGPropertyNode_ptr pitchNode; + SGPropertyNode_ptr rollNode; + SGPropertyNode_ptr trueHeadingNode; + SGPropertyNode_ptr wheelsOnGroundNode; + SGPropertyNode_ptr com1ActiveNode; + SGPropertyNode_ptr com1StandbyNode; + SGPropertyNode_ptr com2ActiveNode; + SGPropertyNode_ptr com2StandbyNode; + SGPropertyNode_ptr transponderCodeNode; + SGPropertyNode_ptr transponderModeNode; + SGPropertyNode_ptr transponderIdentNode; + SGPropertyNode_ptr beaconLightsNode; + SGPropertyNode_ptr landingLightsNode; + SGPropertyNode_ptr navLightsNode; + SGPropertyNode_ptr strobeLightsNode; + SGPropertyNode_ptr taxiLightsNode; + SGPropertyNode_ptr altimeterServiceableNode; + SGPropertyNode_ptr pressAltitudeFtNode; + SGPropertyNode_ptr flapsDeployRatioNode; + SGPropertyNode_ptr gearDeployRatioNode; + SGPropertyNode_ptr speedBrakeDeployRatioNode; + SGPropertyNode_ptr groundElevation; + //SGPropertyNode_ptr numberEnginesNode; + //SGPropertyNode_ptr engineN1PercentageNode; + SGPropertyNode_ptr aircraftNameNode; + }; } // namespace FGSwiftBus diff --git a/src/Network/Swift/swift_connection.cxx b/src/Network/Swift/swift_connection.cxx index d8011222c..7ce55e4b4 100644 --- a/src/Network/Swift/swift_connection.cxx +++ b/src/Network/Swift/swift_connection.cxx @@ -51,7 +51,8 @@ bool SwiftConnection::startServer(const SGPropertyNode* arg, SGPropertyNode* roo bool SwiftConnection::stopServer(const SGPropertyNode* arg, SGPropertyNode* root) { - SwiftConnection::plug->~CPlugin(); + delete SwiftConnection::plug; + SwiftConnection::plug = nullptr; fgSetBool("/sim/swift/serverRunning", false); serverRunning = false; return true; @@ -68,9 +69,9 @@ SwiftConnection::~SwiftConnection() shutdown(); } -void SwiftConnection::init(void) +void SwiftConnection::init() { - if (initialized == false) { + if (!initialized) { globals->get_commands()->addCommand("swiftStart", this, &SwiftConnection::startServer); globals->get_commands()->addCommand("swiftStop", this, &SwiftConnection::stopServer); fgSetBool("/sim/swift/available", true); @@ -81,14 +82,14 @@ void SwiftConnection::init(void) void SwiftConnection::update(double delta_time_sec) { - if (serverRunning == true) { + if (serverRunning) { SwiftConnection::plug->fastLoop(); } } -void SwiftConnection::shutdown(void) +void SwiftConnection::shutdown() { - if (initialized == true) { + if (initialized) { globals->get_commands()->removeCommand("swiftStart"); globals->get_commands()->removeCommand("swiftStop"); fgSetBool("/sim/swift/available", false); diff --git a/src/Network/Swift/swift_connection.hxx b/src/Network/Swift/swift_connection.hxx index 088823729..d7732acc3 100644 --- a/src/Network/Swift/swift_connection.hxx +++ b/src/Network/Swift/swift_connection.hxx @@ -52,7 +52,7 @@ public: bool startServer(const SGPropertyNode* arg, SGPropertyNode* root); bool stopServer(const SGPropertyNode* arg, SGPropertyNode* root); - FGSwiftBus::CPlugin* plug; + FGSwiftBus::CPlugin* plug{}; private: bool serverRunning = false; diff --git a/src/Network/Swift/traffic.cpp b/src/Network/Swift/traffic.cpp index e5d493ed2..854d2e8bf 100644 --- a/src/Network/Swift/traffic.cpp +++ b/src/Network/Swift/traffic.cpp @@ -38,7 +38,7 @@ namespace FGSwiftBus { CTraffic::CTraffic() { - acm = new FGSwiftAircraftManager(); + acm.reset(new FGSwiftAircraftManager()); SG_LOG(SG_NETWORK, SG_INFO, "FGSwiftBus Traffic started"); } @@ -62,7 +62,7 @@ const std::string& CTraffic::ObjectPath() void CTraffic::planeLoaded(void* id, bool succeeded, void* self) { - auto* traffic = static_cast(self); + auto traffic = static_cast(self); auto planeIt = traffic->m_planesById.find(id); if (planeIt == traffic->m_planesById.end()) { return; } @@ -192,6 +192,7 @@ DBusHandlerResult CTraffic::dbusMessageHandler(const CDBusMessage& message_) std::vector pitches; std::vector rolls; std::vector headings; + std::vector groundspeeds; std::vector onGrounds; message.beginArgumentRead(); message.getArgument(callsigns); @@ -201,11 +202,12 @@ DBusHandlerResult CTraffic::dbusMessageHandler(const CDBusMessage& message_) message.getArgument(pitches); message.getArgument(rolls); message.getArgument(headings); + message.getArgument(groundspeeds); message.getArgument(onGrounds); queueDBusCall([=]() { std::vector positions; std::vector orientations; - for (int i = 0; i < latitudes.size(); i++) { + for (long unsigned int i = 0; i < latitudes.size(); i++) { SGGeod newPos; newPos.setLatitudeDeg(latitudes.at(i)); newPos.setLongitudeDeg(longitudes.at(i)); @@ -215,7 +217,7 @@ DBusHandlerResult CTraffic::dbusMessageHandler(const CDBusMessage& message_) positions.push_back(newPos); orientations.push_back(vec); } - acm->updatePlanes(callsigns, positions, orientations, onGrounds); + acm->updatePlanes(callsigns, positions, orientations, groundspeeds, onGrounds); }); } else if (message.getMethodName() == "getRemoteAircraftData") { std::vector requestedcallsigns; @@ -237,6 +239,31 @@ DBusHandlerResult CTraffic::dbusMessageHandler(const CDBusMessage& message_) reply.appendArgument(verticalOffsets); sendDBusMessage(reply); }); + } + else if (message.getMethodName() == "getElevationAtPosition") + { + std::string callsign; + double latitudeDeg; + double longitudeDeg; + double altitudeMeters; + message.beginArgumentRead(); + message.getArgument(callsign); + message.getArgument(latitudeDeg); + message.getArgument(longitudeDeg); + message.getArgument(altitudeMeters); + queueDBusCall([ = ]() + { + SGGeod pos; + pos.setLatitudeDeg(latitudeDeg); + pos.setLongitudeDeg(longitudeDeg); + pos.setElevationM(altitudeMeters); + double elevation = acm->getElevationAtPosition(callsign, pos); + CDBusMessage reply = CDBusMessage::createReply(sender, serial); + reply.beginArgumentWrite(); + reply.appendArgument(callsign); + reply.appendArgument(elevation); + sendDBusMessage(reply); + }); } else { // Unknown message. Tell DBus that we cannot handle it return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; diff --git a/src/Network/Swift/traffic.h b/src/Network/Swift/traffic.h index 711540a67..bbc040001 100644 --- a/src/Network/Swift/traffic.h +++ b/src/Network/Swift/traffic.h @@ -99,7 +99,7 @@ private: bool m_emitSimFrame = true; - FGSwiftAircraftManager* acm; + std::unique_ptr acm; static void planeLoaded(void* id, bool succeeded, void* self); }; diff --git a/src/Scenery/SceneryPager.cxx b/src/Scenery/SceneryPager.cxx index 892b7bc1a..99b2d84ba 100644 --- a/src/Scenery/SceneryPager.cxx +++ b/src/Scenery/SceneryPager.cxx @@ -110,8 +110,9 @@ void SceneryPager::signalEndFrame() } if (!_pagerRequests.empty()) { arePagerRequests = true; - for_each(_pagerRequests.begin(), _pagerRequests.end(), - bind2nd(mem_fun_ref(&PagerRequest::doRequest), this)); + for (auto req : _pagerRequests) { + req.doRequest(this); + } _pagerRequests.clear(); } if (areDeleteRequests && !arePagerRequests) { diff --git a/src/Scenery/terrain_pgt.cxx b/src/Scenery/terrain_pgt.cxx index 3d440bc8a..d39d8dcf1 100644 --- a/src/Scenery/terrain_pgt.cxx +++ b/src/Scenery/terrain_pgt.cxx @@ -82,7 +82,7 @@ void FGPgtTerrain::init( osg::Group* terrain ) { fp.push_back(it->local8BitStr()); } - options->setPluginStringData("SimGear::FG_ROOT", globals->get_fg_root().local8BitStr()); + options->setPluginStringData("SimGear::FG_ROOT", globals->get_fg_root().utf8Str()); options->setPluginStringData("SimGear::BARE_LOD_RANGE", fgGetString("/sim/rendering/static-lod/bare-delta", boost::lexical_cast(SG_OBJECT_RANGE_BARE))); options->setPluginStringData("SimGear::ROUGH_LOD_RANGE", fgGetString("/sim/rendering/static-lod/rough-delta", boost::lexical_cast(SG_OBJECT_RANGE_ROUGH))); diff --git a/src/Scenery/tilemgr.cxx b/src/Scenery/tilemgr.cxx index 9eb762044..a29a6aa4b 100644 --- a/src/Scenery/tilemgr.cxx +++ b/src/Scenery/tilemgr.cxx @@ -168,14 +168,13 @@ void FGTileMgr::reinit() osgDB::FilePathList &fp = _options->getDatabasePathList(); const PathList &sc = globals->get_fg_scenery(); fp.clear(); - PathList::const_iterator it; - for (it = sc.begin(); it != sc.end(); ++it) { - fp.push_back(it->local8BitStr()); + for (auto it = sc.begin(); it != sc.end(); ++it) { + fp.push_back(it->utf8Str()); } - _options->setPluginStringData("SimGear::FG_ROOT", globals->get_fg_root().local8BitStr()); + _options->setPluginStringData("SimGear::FG_ROOT", globals->get_fg_root().utf8Str()); if (_terra_sync) { - _options->setPluginStringData("SimGear::TERRASYNC_ROOT", globals->get_terrasync_dir().local8BitStr()); + _options->setPluginStringData("SimGear::TERRASYNC_ROOT", globals->get_terrasync_dir().utf8Str()); } if (!_disableNasalHooks->getBoolValue()) diff --git a/src/Scripting/NasalPositioned.cxx b/src/Scripting/NasalPositioned.cxx index e4b788df0..3219b9909 100644 --- a/src/Scripting/NasalPositioned.cxx +++ b/src/Scripting/NasalPositioned.cxx @@ -235,7 +235,9 @@ static void wayptGhostDestroy(void* g) static void legGhostDestroy(void* g) { - // nothing for now + FlightPlan::Leg* leg = (FlightPlan::Leg*) g; + if (!FlightPlan::Leg::put(leg)) // unref + delete leg; } @@ -346,7 +348,7 @@ naRef ghostForPositioned(naContext c, FGPositionedRef pos) if (!pos) { return naNil(); } - + switch (pos->type()) { case FGPositioned::VOR: case FGPositioned::NDB: @@ -382,6 +384,7 @@ naRef ghostForLeg(naContext c, const FlightPlan::Leg* leg) return naNil(); } + FlightPlan::Leg::get(leg); // take a ref return naNewGhost2(c, &FPLegGhostType, (void*) leg); } @@ -478,7 +481,7 @@ static naRef waypointNavaid(naContext c, Waypt* wpt) if (!pos || (!FGNavRecord::isNavaidType(pos) && !fgpositioned_cast(pos))) { return naNil(); } - + return ghostForPositioned(c, wpt->source()); } @@ -616,7 +619,7 @@ static bool waypointCommonSetMember(naContext c, Waypt* wpt, const char* fieldNa // nothing changed return false; } - + return true; } @@ -701,6 +704,12 @@ static const char* legGhostGetMember(naContext c, void* g, naRef field, naRef* o { const char* fieldName = naStr_data(field); FlightPlan::Leg* leg = (FlightPlan::Leg*) g; + if (!leg) { + *out = naNil(); + naRuntimeError(c, "leg ghost member fetched, but no associated leg object found"); + return ""; + } + Waypt* wpt = leg->waypoint(); if (!strcmp(fieldName, "parents")) { @@ -732,7 +741,11 @@ static const char* legGhostGetMember(naContext c, void* g, naRef field, naRef* o } else if (!strcmp(fieldName, "hold_count")) { *out = naNum(leg->holdCount()); } else { // check for fields defined on the underlying waypoint - return waypointCommonGetMember(c, wpt, fieldName, out); + if (wpt) { // FIXME null check shouldn't be required, check refcount + return waypointCommonGetMember(c, wpt, fieldName, out); + } else { + naRuntimeError(c, "leg ghost member fetched, but no underlying waypoint object found"); + } } return ""; // success @@ -749,7 +762,7 @@ static void legGhostSetMember(naContext c, void* g, naRef field, naRef value) { const char* fieldName = naStr_data(field); FlightPlan::Leg* leg = (FlightPlan::Leg*) g; - + bool didChange = false; if (!strcmp(fieldName, "hold_count")) { const int count = static_cast(value.num); @@ -759,13 +772,13 @@ static void legGhostSetMember(naContext c, void* g, naRef field, naRef value) } else if (!strcmp(fieldName, "hold_heading_radial_deg")) { if (!leg->convertWaypointToHold()) naRuntimeError(c, "couldn't convert leg waypoint into a hold"); - + // now we can call the base method didChange = waypointCommonSetMember(c, leg->waypoint(), fieldName, value); } else { didChange = waypointCommonSetMember(c, leg->waypoint(), fieldName, value); } - + if (didChange) { leg->markWaypointDirty(); } @@ -874,7 +887,7 @@ static void flightplanGhostSetMember(naContext c, void* g, naRef field, naRef va fp->setDestination(static_cast(nullptr)); return; } - + FGRunway* rwy = runwayGhost(value); if (rwy){ fp->setDestination(rwy); @@ -1256,7 +1269,7 @@ static int geodFromArgs(naRef* args, int offset, int argc, SGGeod& result) result = wayptGhost(args[offset])->position(); return 1; } - + if (gt == &FPLegGhostType) { result = fpLegGhost(args[offset])->waypoint()->position(); return 1; @@ -1366,30 +1379,30 @@ static naRef f_geodtocart(naContext c, naRef me, int argc, naRef* args) */ static naRef f_get_cart_ground_intersection(naContext c, naRef me, int argc, naRef* args) { - SGVec3d dir; - SGVec3d pos; + SGVec3d dir; + SGVec3d pos; - if (argc != 2) - naRuntimeError(c, "geod_hash get_cart_ground_intersection(position: hash{x,y,z}, direction:hash{x,y,z}) expects 2 arguments"); + if (argc != 2) + naRuntimeError(c, "geod_hash get_cart_ground_intersection(position: hash{x,y,z}, direction:hash{x,y,z}) expects 2 arguments"); - if (!vec3dFromHash(args[0], pos)) - naRuntimeError(c, "geod_hash get_cart_ground_intersection(position:hash{x,y,z}, direction:hash{x,y,z}) expects argument(0) to be hash of position containing x,y,z"); + if (!vec3dFromHash(args[0], pos)) + naRuntimeError(c, "geod_hash get_cart_ground_intersection(position:hash{x,y,z}, direction:hash{x,y,z}) expects argument(0) to be hash of position containing x,y,z"); - if (!vec3dFromHash(args[1], dir)) - naRuntimeError(c, "geod_hash get_cart_ground_intersection(position: hash{x,y,z}, direction:hash{x,y,z}) expects argument(1) to be hash of direction containing x,y,z"); + if (!vec3dFromHash(args[1], dir)) + naRuntimeError(c, "geod_hash get_cart_ground_intersection(position: hash{x,y,z}, direction:hash{x,y,z}) expects argument(1) to be hash of direction containing x,y,z"); - SGVec3d nearestHit; - if (!globals->get_scenery()->get_cart_ground_intersection(pos, dir, nearestHit)) - return naNil(); + SGVec3d nearestHit; + if (!globals->get_scenery()->get_cart_ground_intersection(pos, dir, nearestHit)) + return naNil(); - const SGGeod geodHit = SGGeod::fromCart(nearestHit); + const SGGeod geodHit = SGGeod::fromCart(nearestHit); - // build a hash for returned intersection - naRef intersection_h = naNewHash(c); - hashset(c, intersection_h, "lat", naNum(geodHit.getLatitudeDeg())); - hashset(c, intersection_h, "lon", naNum(geodHit.getLongitudeDeg())); - hashset(c, intersection_h, "elevation", naNum(geodHit.getElevationM())); - return intersection_h; + // build a hash for returned intersection + naRef intersection_h = naNewHash(c); + hashset(c, intersection_h, "lat", naNum(geodHit.getLatitudeDeg())); + hashset(c, intersection_h, "lon", naNum(geodHit.getLongitudeDeg())); + hashset(c, intersection_h, "elevation", naNum(geodHit.getElevationM())); + return intersection_h; } // convert from aircraft reference frame to global (ECEF) cartesian @@ -1439,13 +1452,15 @@ static naRef f_geodinfo(naContext c, naRef me, int argc, naRef* args) double elev = argc == 3 ? naNumValue(args[2]).num : 10000; const simgear::BVHMaterial *material; SGGeod geod = SGGeod::fromDegM(lon, lat, elev); - + const auto scenery = globals->get_scenery(); if (scenery == nullptr) return naNil(); - - if(!scenery->get_elevation_m(geod, elev, &material)) - return naNil(); + + if(!scenery->get_elevation_m(geod, elev, &material)) { + SG_LOG(SG_TERRAIN, SG_DEV_WARN, "Nasal geodinfo() querying location with no loaded tiles:" << geod); + return naNil(); + } naRef vec = naNewVector(c); naVec_append(vec, naNum(elev)); @@ -1783,7 +1798,7 @@ static naRef f_airport_approaches(naContext c, naRef me, int argc, naRef* args) appIds.insert(app->ident()); } } - + for (auto s : appIds) { naVec_append(approaches, stringToNasal(c, s)); } @@ -1792,13 +1807,13 @@ static naRef f_airport_approaches(naContext c, naRef me, int argc, naRef* args) RunwayVec runways; if (star) runways = star->runways(); - + for (unsigned int s=0; snumApproaches(); ++s) { Approach* app = apt->getApproachByIndex(s); if ((ty != PROCEDURE_INVALID) && (app->type() != ty)) { continue; } - + naRef procId = stringToNasal(c, app->ident()); naVec_append(approaches, procId); } @@ -2166,24 +2181,24 @@ static naRef f_findByIdent(naContext c, naRef me, int argc, naRef* args) if ((argc < 2) || !naIsString(args[0]) || !naIsString(args[1]) ) { naRuntimeError(c, "finxByIdent: expects ident and type as first two args"); } - + std::string ident(naStr_data(args[0])); std::string typeSpec(naStr_data(args[1])); - + // optional specify search pos as final argument SGGeod pos = globals->get_aircraft_position(); geodFromArgs(args, 2, argc, pos); FGPositioned::TypeFilter filter(FGPositioned::TypeFilter::fromString(typeSpec)); - + naRef r = naNewVector(c); FGPositionedList matches = FGPositioned::findAllWithIdent(ident, &filter); FGPositioned::sortByRange(matches, pos); - + for (auto f : matches) { naVec_append(r, ghostForPositioned(c, f)); } - + return r; } @@ -2235,7 +2250,7 @@ static naRef f_formatLatLon(naContext c, naRef me, int argc, naRef* args) if (argOffset == 0) { naRuntimeError(c, "invalid arguments to formatLatLon, expect a geod or lat,lon"); } - + simgear::strutils::LatLonFormat format = static_cast(fgGetInt("/sim/lon-lat-format")); if (argOffset < argc && naIsNum(args[argOffset])) { @@ -2244,7 +2259,7 @@ static naRef f_formatLatLon(naContext c, naRef me, int argc, naRef* args) naRuntimeError(c, "invalid lat-lon format requested"); } } - + const auto s = simgear::strutils::formatGeodAsString(p, format); return stringToNasal(c, s); } @@ -2254,13 +2269,13 @@ static naRef f_parseStringAsLatLonValue(naContext c, naRef me, int argc, naRef* if ((argc < 1) || !naIsString(args[0])) { naRuntimeError(c, "Missing / bad argument to parseStringAsLatLonValue"); } - + double value; bool ok = simgear::strutils::parseStringAsLatLonValue(naStr_data(args[0]), value); if (!ok) { return naNil(); } - + return naNum(value); } @@ -2400,7 +2415,7 @@ public: { callDelegateMethod("activated"); } - + void sequence() override { callDelegateMethod("sequence"); @@ -2555,7 +2570,7 @@ static naRef f_airwaySearch(naContext c, naRef me, int argc, naRef* args) static FGPositionedRef positionedFromArg(naRef ref) { - if (!naIsGhost(ref)) + if (!naIsGhost(ref)) return {}; naGhostType* gt = naGhost_type(ref); @@ -2592,7 +2607,7 @@ static naRef f_findAirway(naContext c, naRef me, int argc, naRef* args) if (argc >= 2) { pos = positionedFromArg(args[1]); if (naIsString(args[1])) { - // level spec, + // level spec, } } @@ -2675,7 +2690,7 @@ static naRef f_createViaTo(naContext c, naRef me, int argc, naRef* args) std::string airwayName = naStr_data(args[0]); AirwayRef airway = Airway::findByIdent(airwayName, Airway::Both); if (!airway) { - naRuntimeError(c, "createViaTo: couldn't find airway with provided name: %s", + naRuntimeError(c, "createViaTo: couldn't find airway with provided name: %s", naStr_data(args[0])); } @@ -2717,8 +2732,8 @@ static naRef f_createViaFromTo(naContext c, naRef me, int argc, naRef* args) std::string airwayName = naStr_data(args[1]); AirwayRef airway = Airway::findByIdentAndNavaid(airwayName, from); if (!airway) { - naRuntimeError(c, "createViaFromTo: couldn't find airway with provided name: %s from wp %s", - naStr_data(args[0]), + naRuntimeError(c, "createViaFromTo: couldn't find airway with provided name: %s from wp %s", + naStr_data(args[0]), from->ident().c_str()); } @@ -3133,7 +3148,7 @@ static naRef f_leg_setAltitude(naContext c, naRef me, int argc, naRef* args) static naRef f_leg_path(naContext c, naRef me, int argc, naRef* args) { FlightPlan::Leg* leg = fpLegGhost(me); - if (!leg) { + if (!leg || !leg->owner()) { naRuntimeError(c, "leg.setAltitude called on non-flightplan-leg object"); } @@ -3155,7 +3170,7 @@ static naRef f_leg_path(naContext c, naRef me, int argc, naRef* args) static naRef f_leg_courseAndDistanceFrom(naContext c, naRef me, int argc, naRef* args) { FlightPlan::Leg* leg = fpLegGhost(me); - if (!leg) { + if (!leg || !leg->owner()) { naRuntimeError(c, "leg.courseAndDistanceFrom called on non-flightplan-leg object"); } diff --git a/src/Scripting/NasalPositioned_cppbind.cxx b/src/Scripting/NasalPositioned_cppbind.cxx index 0cfd60f54..f18ff332f 100644 --- a/src/Scripting/NasalPositioned_cppbind.cxx +++ b/src/Scripting/NasalPositioned_cppbind.cxx @@ -108,7 +108,9 @@ std::vector extract( const std::vector& in, T (C2::*getter)() const ) { std::vector ret(in.size()); - std::transform(in.begin(), in.end(), ret.begin(), std::mem_fun(getter)); + std::transform(in.begin(), in.end(), ret.begin(), [getter](const C1& c) + { return (c->*getter)(); } + ); return ret; } diff --git a/src/Scripting/NasalSys.cxx b/src/Scripting/NasalSys.cxx index f7a6001e6..711a53c13 100644 --- a/src/Scripting/NasalSys.cxx +++ b/src/Scripting/NasalSys.cxx @@ -186,7 +186,7 @@ public: bool isSingleShot() const { return _singleShot; } - + const std::string& name() const { return _name; } private: @@ -215,19 +215,16 @@ static void f_timerObj_setSimTime(TimerObj& timer, naContext c, naRef value) //////////////////// /// Timestamp - used to provide millisecond based timing of operations. See SGTimeStamp //// -/// 0.373404us to perform elapsedUSec from Nasal : Tested i7-4790K, Win64 +/// 0.373404us to perform elapsedUSec from Nasal : Tested i7-4790K, Win64 class TimeStampObj : public SGReferenced { public: - TimeStampObj(Context *c, FGNasalSys* sys) : - _sys(sys) + TimeStampObj(Context *c) { timestamp.stamp(); } - virtual ~TimeStampObj() - { - } + virtual ~TimeStampObj() = default; void stamp() { @@ -243,7 +240,6 @@ public: } private: SGTimeStamp timestamp; - FGNasalSys* _sys; }; typedef SGSharedPtr TimeStampObjRef; @@ -251,34 +247,6 @@ typedef nasal::Ghost NasalTimeStampObj; /////////////////////////////////////////////////////////////////////////// -// Read and return file contents in a single buffer. Note use of -// stat() to get the file size. This is a win32 function, believe it -// or not. :) Note the REALLY IMPORTANT use of the "b" flag to fopen. -// Text mode brain damage will kill us if we're trying to do bytewise -// I/O. -static char* readfile(const char* file, int* lenOut) -{ - struct stat data; - if(stat(file, &data) != 0) return 0; - FILE* f = fopen(file, "rb"); - if(!f) return 0; - char* buf = new char[data.st_size]; - *lenOut = fread(buf, 1, data.st_size, f); - fclose(f); - if(*lenOut != data.st_size) { - // Shouldn't happen, but warn anyway since it represents a - // platform bug and not a typical runtime error (missing file, - // etc...) - SG_LOG(SG_NASAL, SG_ALERT, - "ERROR in Nasal initialization: " << - "short count returned from fread() of " << file << - ". Check your C library!"); - delete[] buf; - return 0; - } - return buf; -} - FGNasalSys::FGNasalSys() : _inited(false) { @@ -353,7 +321,7 @@ FGNasalSys::~FGNasalSys() SG_LOG(SG_GENERAL, SG_ALERT, "Nasal was not shutdown"); } sglog().removeCallback(_log.get()); - + nasalSys = nullptr; } @@ -367,7 +335,7 @@ bool FGNasalSys::parseAndRunWithOutput(const std::string& source, std::string& o return false; } naRef result = callWithContext(ctx, code, 0, 0, naNil()); - + // if there was a result value, try to convert it to a string // value. if (!naIsNil(result)) { @@ -376,7 +344,7 @@ bool FGNasalSys::parseAndRunWithOutput(const std::string& source, std::string& o output = naStr_data(s); } } - + naFreeContext(ctx); return true; } @@ -590,6 +558,8 @@ static naRef f_makeTimer(naContext c, naRef me, int argc, naRef* args) } else if ((argc == 3) && naIsFunc(args[2])) { self = args[1]; func = args[2]; + } else { + naRuntimeError(c, "bad function/self arguments to maketimer"); } TimerObj* timerObj = new TimerObj(c, nasalSys, func, self, args[0].num); @@ -598,7 +568,7 @@ static naRef f_makeTimer(naContext c, naRef me, int argc, naRef* args) static naRef f_maketimeStamp(naContext c, naRef me, int argc, naRef* args) { - TimeStampObj* timeStampObj = new TimeStampObj(c, nasalSys); + TimeStampObj* timeStampObj = new TimeStampObj(c); return nasal::to_nasal(c, timeStampObj); } @@ -804,13 +774,65 @@ static naRef f_open(naContext c, naRef me, int argc, naRef* args) std::wstring wmodestr = simgear::strutils::convertUtf8ToWString(modestr); f = _wfopen(fp.c_str(), wmodestr.c_str()); #else - std::string fp = filename.local8BitStr(); + std::string fp = filename.utf8Str(); f = fopen(fp.c_str(), modestr); #endif if(!f) naRuntimeError(c, strerror(errno)); return naIOGhost(c, f); } +static naRef ftype(naContext ctx, const SGPath& f) +{ + const char* t = "unk"; + if (f.isFile()) t = "reg"; + else if(f.isDir()) t = "dir"; + return naStr_fromdata(naNewString(ctx), t, strlen(t)); +} + +// io.stat with UTF-8 path support, replaces the default one in +// Nasal iolib.c which does not hsupport UTF-8 paths +static naRef f_custom_stat(naContext ctx, naRef me, int argc, naRef* args) +{ + naRef pathArg = argc > 0 ? naStringValue(ctx, args[0]) : naNil(); + if (!naIsString(pathArg)) + naRuntimeError(ctx, "bad argument to stat()"); + + const auto path = SGPath::fromUtf8(naStr_data(pathArg)); + if (!path.exists()) { + return naNil(); + } + + const SGPath filename = fgValidatePath(path, false ); + if (filename.isNull()) { + SG_LOG(SG_NASAL, SG_ALERT, "stat(): reading '" << + naStr_data(pathArg) << "' denied (unauthorized directory - authorization" + " no longer follows symlinks; to authorize reading additional " + "directories, pass them to --allow-nasal-read)"); + naRuntimeError(ctx, "stat(): access denied (unauthorized directory)"); + return naNil(); + } + + naRef result = naNewVector(ctx); + naVec_setsize(ctx, result, 12); + + // every use in fgdata/Nasal only uses stat() to test existence of + // files, not to check any of this information. + int n = 0; + naVec_set(result, n++, naNum(0)); // device + naVec_set(result, n++, naNum(0)); // inode + naVec_set(result, n++, naNum(0)); // mode + naVec_set(result, n++, naNum(0)); // nlink + naVec_set(result, n++, naNum(0)); // uid + naVec_set(result, n++, naNum(0)); // guid + naVec_set(result, n++, naNum(0)); // rdev + naVec_set(result, n++, naNum(filename.sizeInBytes())); // size + naVec_set(result, n++, naNum(0)); // atime + naVec_set(result, n++, naNum(0)); // mtime + naVec_set(result, n++, naNum(0)); // ctime + naVec_set(result, n++, ftype(ctx, filename)); + return result; +} + // Parse XML file. // parsexml( [, [, [, [, ]]]]); // @@ -967,6 +989,7 @@ void FGNasalSys::init() naNewFunc(_context, naNewCCode(_context, funcs[i].func))); nasal::Hash io_module = getGlobals().get("io"); io_module.set("open", f_open); + io_module.set("stat", f_custom_stat); // And our SGPropertyNode wrapper hashset(_globals, "props", genPropsModule()); @@ -1055,7 +1078,7 @@ void FGNasalSys::shutdown() delete t; } _nasalTimers.clear(); - + naClearSaved(); _string = naNil(); // will be freed by _context @@ -1068,19 +1091,19 @@ void FGNasalSys::shutdown() _globals = naNil(); naGC(); - + // Destroy all queued ghosts : important to ensure persistent timers are // destroyed now. nasal::ghostProcessDestroyList(); - + if (!_persistentTimers.empty()) { SG_LOG(SG_NASAL, SG_DEV_WARN, "Extant persistent timer count:" << _persistentTimers.size()); - + for (auto pt : _persistentTimers) { SG_LOG(SG_NASAL, SG_DEV_WARN, "Extant:" << pt << " : " << pt->name()); } } - + _inited = false; } @@ -1173,8 +1196,14 @@ void FGNasalSys::addModule(string moduleName, simgear::PathList scripts) if (!module_node->hasChild("enabled",0)) { SGPropertyNode* node = module_node->getChild("enabled",0,true); - node->setBoolValue(true); - node->setAttribute(SGPropertyNode::USERARCHIVE,true); + node->setBoolValue(false); + node->setAttribute(SGPropertyNode::USERARCHIVE,false); + SG_LOG(SG_NASAL, SG_ALERT, "Nasal module " << + moduleName << + " present in FGDATA/Nasal but not configured in defaults.xml. " << + " Please add an entry to defaults.xml, and set " + << node->getPath() << + "=true to load the module on-demand at runtime when required."); } } } @@ -1288,19 +1317,21 @@ void FGNasalSys::logNasalStack(naContext context) // name. bool FGNasalSys::loadModule(SGPath file, const char* module) { - int len = 0; - std::string pdata = file.local8BitStr(); - char* buf = readfile(pdata.c_str(), &len); - if(!buf) { - SG_LOG(SG_NASAL, SG_ALERT, - "Nasal error: could not read script file " << file - << " into module " << module); + if (!file.exists()) { + SG_LOG(SG_NASAL, SG_ALERT, "Cannot load module, missing file:" << file); return false; } - bool ok = createModule(module, pdata.c_str(), buf, len); - delete[] buf; - return ok; + sg_ifstream file_in(file); + string buf; + while (!file_in.eof()) { + char bytes[8192]; + file_in.read(bytes, 8192); + buf.append(bytes, file_in.gcount()); + } + file_in.close(); + auto pathStr = file.utf8Str(); + return createModule(module, pathStr.c_str(), buf.data(), buf.length()); } // Parse and run. Save the local variables namespace, as it will @@ -1377,7 +1408,7 @@ naRef FGNasalSys::parse(naContext ctx, const char* filename, " in "<< filename <<", line " << errLine; errors = errorMessageStream.str(); SG_LOG(SG_NASAL, SG_ALERT, errors); - + return naNil(); } @@ -1547,7 +1578,7 @@ naRef FGNasalSys::setListener(naContext c, int argc, naRef* args) node->getPath()); } } - + naRef code = argc > 1 ? args[1] : naNil(); if(!(naIsCode(code) || naIsCCode(code) || naIsFunc(code))) { naRuntimeError(c, "setlistener() with invalid function argument"); @@ -1555,7 +1586,7 @@ naRef FGNasalSys::setListener(naContext c, int argc, naRef* args) } int init = argc > 2 && naIsNum(args[2]) ? int(args[2].num) : 0; // do not trigger when created - int type = argc > 3 && naIsNum(args[3]) ? int(args[3].num) : 1; // trigger will always be triggered when the property is written + int type = argc > 3 && naIsNum(args[3]) ? int(args[3].num) : 1; // trigger will always be triggered when the property is written FGNasalListener *nl = new FGNasalListener(node, code, this, gcSave(code), _listenerId, init, type); diff --git a/src/Sound/VoiceSynthesizer.cxx b/src/Sound/VoiceSynthesizer.cxx index ef51c3b5b..7ae061214 100644 --- a/src/Sound/VoiceSynthesizer.cxx +++ b/src/Sound/VoiceSynthesizer.cxx @@ -53,7 +53,7 @@ void FLITEVoiceSynthesizer::WorkerThread::run() // marker value indicating termination requested if ((request.speed < 0.0) && (request.volume < 0.0)) { - SG_LOG(SG_SOUND, SG_INFO, "FLITE synthesis thread exiting"); + SG_LOG(SG_SOUND, SG_DEBUG, "FLITE synthesis thread exiting"); return; } @@ -76,7 +76,7 @@ string FLITEVoiceSynthesizer::getVoicePath( voice_t voice ) { if( voice < 0 || voice >= VOICE_UNKNOWN ) return string(""); SGPath voicePath = globals->get_fg_root() / "ATC" / VOICE_FILES[voice]; - return voicePath.local8BitStr(); + return voicePath.utf8Str(); } string FLITEVoiceSynthesizer::getVoicePath( const string & voice ) @@ -106,7 +106,6 @@ FLITEVoiceSynthesizer::~FLITEVoiceSynthesizer() // push the special marker value _requests.push(SynthesizeRequest::cancelThreadRequest()); _worker->join(); - SG_LOG(SG_SOUND, SG_INFO, "FLITE synthesis thread joined OK"); Flite_HTS_Engine_clear(_engine); } @@ -124,7 +123,11 @@ SGSoundSample * FLITEVoiceSynthesizer::synthesize(const std::string & text, doub ALsizei rate, count; if ( FALSE == Flite_HTS_Engine_synthesize_samples_mono16(_engine, text.c_str(), &data, &count, &rate)) return NULL; - return new SGSoundSample(&data, + auto buf = std::unique_ptr{ + reinterpret_cast( data ), + free + }; + return new SGSoundSample(buf, count * sizeof(short), rate, SG_SAMPLE_MONO16); diff --git a/src/Sound/beacon.cxx b/src/Sound/beacon.cxx index 46f0013a4..90dcf0074 100644 --- a/src/Sound/beacon.cxx +++ b/src/Sound/beacon.cxx @@ -43,24 +43,32 @@ bool FGBeacon::init() { unsigned char *ptr; size_t i, len; - const unsigned char* inner_buf = (const unsigned char*)malloc( INNER_SIZE ); - const unsigned char* middle_buf = (const unsigned char*)malloc(MIDDLE_SIZE); - const unsigned char* outer_buf = (const unsigned char*)malloc( OUTER_SIZE ); + auto inner_buf = std::unique_ptr{ + reinterpret_cast( malloc( INNER_SIZE ) ), + free + }; + auto middle_buf = std::unique_ptr{ + reinterpret_cast( malloc( MIDDLE_SIZE ) ), + free + }; + auto outer_buf = std::unique_ptr{ + reinterpret_cast( malloc( OUTER_SIZE ) ), + free + }; // Make inner marker beacon sound len= (int)(INNER_DIT_LEN / 2.0 ); unsigned char inner_dit[INNER_DIT_LEN]; - make_tone( inner_dit, INNER_FREQ, len, INNER_DIT_LEN, - TRANSITION_BYTES ); + make_tone( inner_dit, INNER_FREQ, len, INNER_DIT_LEN, TRANSITION_BYTES ); - ptr = (unsigned char*)inner_buf; + ptr = inner_buf.get(); for ( i = 0; i < 6; ++i ) { memcpy( ptr, inner_dit, INNER_DIT_LEN ); ptr += INNER_DIT_LEN; } try { - inner = new SGSoundSample( &inner_buf, INNER_SIZE, BYTES_PER_SECOND ); + inner = new SGSoundSample( inner_buf, INNER_SIZE, BYTES_PER_SECOND ); inner->set_reference_dist( 10.0 ); inner->set_max_dist( 20.0 ); @@ -75,12 +83,12 @@ bool FGBeacon::init() { make_tone( middle_dah, MIDDLE_FREQ, len, MIDDLE_DAH_LEN, TRANSITION_BYTES ); - ptr = (unsigned char*)middle_buf; + ptr = (unsigned char*)middle_buf.get(); memcpy( ptr, middle_dit, MIDDLE_DIT_LEN ); ptr += MIDDLE_DIT_LEN; memcpy( ptr, middle_dah, MIDDLE_DAH_LEN ); - middle = new SGSoundSample( &middle_buf, MIDDLE_SIZE, BYTES_PER_SECOND); + middle = new SGSoundSample( middle_buf, MIDDLE_SIZE, BYTES_PER_SECOND); middle->set_reference_dist( 10.0 ); middle->set_max_dist( 20.0 ); @@ -89,13 +97,13 @@ bool FGBeacon::init() { unsigned char outer_dah[OUTER_DAH_LEN]; make_tone( outer_dah, OUTER_FREQ, len, OUTER_DAH_LEN, TRANSITION_BYTES ); - - ptr = (unsigned char*)outer_buf; + + ptr = (unsigned char*)outer_buf.get(); memcpy( ptr, outer_dah, OUTER_DAH_LEN ); ptr += OUTER_DAH_LEN; memcpy( ptr, outer_dah, OUTER_DAH_LEN ); - outer = new SGSoundSample( &outer_buf, OUTER_SIZE, BYTES_PER_SECOND ); + outer = new SGSoundSample( outer_buf, OUTER_SIZE, BYTES_PER_SECOND ); outer->set_reference_dist( 10.0 ); outer->set_max_dist( 20.0 ); } catch ( sg_io_exception &e ) { diff --git a/src/Sound/flitevoice.cxx b/src/Sound/flitevoice.cxx index 9cf0e4687..ce39fa2f6 100644 --- a/src/Sound/flitevoice.cxx +++ b/src/Sound/flitevoice.cxx @@ -41,7 +41,7 @@ FGFLITEVoice::FGFLITEVoice(FGVoiceMgr * mgr, const SGPropertyNode_ptr node, cons SGPath voice = globals->get_fg_root() / "ATC" / node->getStringValue("htsvoice", "cmu_us_arctic_slt.htsvoice"); - _synthesizer = new FLITEVoiceSynthesizer(voice.local8BitStr()); + _synthesizer = new FLITEVoiceSynthesizer(voice.utf8Str()); SGSoundMgr *smgr = globals->get_subsystem(); _sgr = smgr->find(sampleGroupRefName, true); @@ -49,7 +49,7 @@ FGFLITEVoice::FGFLITEVoice(FGVoiceMgr * mgr, const SGPropertyNode_ptr node, cons node->getNode("text", true)->addChangeListener(this); - SG_LOG(SG_SOUND, SG_INFO, "FLITEVoice initialized for sample-group '" << sampleGroupRefName + SG_LOG(SG_SOUND, SG_DEBUG, "FLITEVoice initialized for sample-group '" << sampleGroupRefName << "'. Samples will be named '" << _sampleName << "' " << "voice is '" << voice << "'"); } diff --git a/src/Sound/morse.cxx b/src/Sound/morse.cxx index 9e4d5eced..ffef13c59 100644 --- a/src/Sound/morse.cxx +++ b/src/Sound/morse.cxx @@ -190,10 +190,13 @@ SGSoundSample *FGMorse::make_ident( const std::string& id, const int freq ) { length += 2 * SPACE_SIZE; // 2. Allocate space for the message - const unsigned char* buffer = (const unsigned char *)malloc(length); + auto buffer = std::unique_ptr{ + reinterpret_cast( malloc( length ) ), + free + }; // 3. Assemble the message; - unsigned char *buf_ptr = (unsigned char*)buffer; + unsigned char *buf_ptr = buffer.get(); for ( i = 0; i < (int)id.length(); ++i ) { if ( idptr[i] >= 'A' && idptr[i] <= 'Z' ) { @@ -232,7 +235,7 @@ SGSoundSample *FGMorse::make_ident( const std::string& id, const int freq ) { buf_ptr += SPACE_SIZE; // 4. create the simple sound and return - SGSoundSample *sample = new SGSoundSample( &buffer, length, + SGSoundSample *sample = new SGSoundSample( buffer, length, BYTES_PER_SECOND ); sample->set_reference_dist( 10.0 ); diff --git a/src/Sound/soundmanager.cxx b/src/Sound/soundmanager.cxx index bf6b72a23..a78a79514 100644 --- a/src/Sound/soundmanager.cxx +++ b/src/Sound/soundmanager.cxx @@ -76,6 +76,8 @@ void FGSoundManager::init() _velocityEastFPS = fgGetNode("velocities/speed-east-fps", true); _velocityDownFPS = fgGetNode("velocities/speed-down-fps", true); + _frozen = fgGetNode("sim/freeze/master"); + SGPropertyNode_ptr scenery_loaded = fgGetNode("sim/sceneryloaded", true); scenery_loaded->addChangeListener(_listener.get()); @@ -159,9 +161,9 @@ bool FGSoundManager::stationaryView() const // Actual sound update is triggered by the subsystem manager. void FGSoundManager::update(double dt) { - if (is_active() && _is_initialized && _sound_working->getBoolValue()) + if (is_working() && _is_initialized && _sound_working->getBoolValue()) { - bool enabled = _sound_enabled->getBoolValue(); + bool enabled = _sound_enabled->getBoolValue() && !_frozen->getBoolValue(); if (enabled != _enabled) { if (enabled) diff --git a/src/Sound/soundmanager.hxx b/src/Sound/soundmanager.hxx index 94810a6be..b4387a6b6 100644 --- a/src/Sound/soundmanager.hxx +++ b/src/Sound/soundmanager.hxx @@ -63,6 +63,7 @@ private: bool _is_initialized, _enabled; SGPropertyNode_ptr _sound_working, _sound_enabled, _volume, _device_name; SGPropertyNode_ptr _velocityNorthFPS, _velocityEastFPS, _velocityDownFPS; + SGPropertyNode_ptr _frozen; std::unique_ptr _listener; std::map _synthesizers; diff --git a/src/Time/TimeManager.cxx b/src/Time/TimeManager.cxx index c00bab291..1f6325cb0 100644 --- a/src/Time/TimeManager.cxx +++ b/src/Time/TimeManager.cxx @@ -386,9 +386,7 @@ void TimeManager::throttleUpdateRate() // periodic time updater wrapper void TimeManager::updateLocalTime() { - SGPath zone(globals->get_fg_root()); - zone.append("Timezone"); - _impl->updateLocal(globals->get_aircraft_position(), zone.local8BitStr()); + _impl->updateLocal(globals->get_aircraft_position(), globals->get_fg_root() / "Timezone"); } void TimeManager::initTimeOffset() diff --git a/src/Traffic/TrafficMgr.cxx b/src/Traffic/TrafficMgr.cxx index a021ef880..15b070f72 100644 --- a/src/Traffic/TrafficMgr.cxx +++ b/src/Traffic/TrafficMgr.cxx @@ -45,6 +45,7 @@ #include #include #include +#include #include @@ -59,11 +60,10 @@ #include #include #include +#include #include #include -#include -#include #include #include @@ -124,7 +124,7 @@ public: bool isFinished() const { - SGGuard g(_lock); + std::lock_guard g(_lock); return _isFinished; } @@ -137,7 +137,7 @@ public: } } - SGGuard g(_lock); + std::lock_guard g(_lock); _isFinished = true; } @@ -394,7 +394,7 @@ private: } FGTrafficManager* _trafficManager; - mutable SGMutex _lock; + mutable std::mutex _lock; bool _isFinished; bool _cancelThread; simgear::PathList _trafficDirPaths; diff --git a/src/Viewer/CameraGroup_compositor.cxx b/src/Viewer/CameraGroup_compositor.cxx index 5631e8950..98c190b39 100644 --- a/src/Viewer/CameraGroup_compositor.cxx +++ b/src/Viewer/CameraGroup_compositor.cxx @@ -690,7 +690,7 @@ void CameraGroup::buildCamera(SGPropertyNode* cameraNode) std::string compositor_path = cameraNode->getStringValue("compositor", default_compositor.c_str()); osg::ref_ptr options = - SGReaderWriterOptions::fromPath(globals->get_fg_root().local8BitStr()); + SGReaderWriterOptions::fromPath(globals->get_fg_root()); options->setPropertyNode(globals->get_props()); Compositor *compositor = Compositor::create(_viewer, window->gc, diff --git a/src/Viewer/CameraGroup_legacy.cxx b/src/Viewer/CameraGroup_legacy.cxx index c3d25ac3d..2e946a568 100644 --- a/src/Viewer/CameraGroup_legacy.cxx +++ b/src/Viewer/CameraGroup_legacy.cxx @@ -1205,9 +1205,12 @@ const CameraInfo* CameraGroup::getGUICamera() const Camera* getGUICamera(CameraGroup* cgroup) { + if (!cgroup) + return nullptr; + const CameraInfo* info = cgroup->getGUICamera(); if (!info) { - return NULL; + return nullptr; } return info->getCamera(MAIN_CAMERA); diff --git a/src/Viewer/ViewPropertyEvaluator.cxx b/src/Viewer/ViewPropertyEvaluator.cxx index 472d8f0b7..6f8a10ac0 100644 --- a/src/Viewer/ViewPropertyEvaluator.cxx +++ b/src/Viewer/ViewPropertyEvaluator.cxx @@ -485,10 +485,37 @@ namespace ViewPropertyEvaluator { cache the double value. But the underlying SGPropertyNode* will be cached. */ double getSequenceDoubleValue(Sequence& sequence, double default_) + { + SGPropertyNode* node = getSequenceNode(sequence); + if (!node) { + return default_; + } + if (!node->getParent()) { + /* Root node. */ + return default_; + } + if (node->getType() == simgear::props::BOOL) { + /* 2020-03-22: there is a problem with aircraft rah-66 setting type + of the root node to bool which gives string value "false". So we + force a return of default_. */ + return default_; + } + if (node->getStringValue()[0] == 0) { + /* If we reach here, the node exists but its value is an empty + string, so node->getDoubleValue() would return 0 which isn't + useful, so instead we return default_. */ + return default_; + } + return node->getDoubleValue(); + } + + bool getSequenceBoolValue(Sequence& sequence, bool default_) { SGPropertyNode* node = getSequenceNode(sequence); if (node) { - return node->getDoubleValue(); + if (node->getStringValue()[0] != 0) { + return node->getBoolValue(); + } } return default_; } @@ -510,6 +537,17 @@ namespace ViewPropertyEvaluator { return ret; } + bool getBoolValue(const char* spec, bool default_) + { + std::shared_ptr sequence = getSequence(spec); + if (sequence->_nodes.size() != 1 || sequence->_nodes.front()->_begin[0] != '(') { + SG_LOG(SG_VIEW, SG_DEBUG, "bad sequence for getBoolValue() - must have outermost '(...)': '" << spec); + abort(); + } + bool ret = getSequenceBoolValue(*sequence, default_); + return ret; + } + std::ostream& operator << (std::ostream& out, const Dump& dump) { out << "ViewPropertyEvaluator\n"; @@ -698,5 +736,9 @@ namespace ViewPropertyEvaluator { } return out; } - + + void dump() + { + std::cerr << Dump() << "\n"; + } } diff --git a/src/Viewer/ViewPropertyEvaluator.hxx b/src/Viewer/ViewPropertyEvaluator.hxx index 4779feba6..66be22d92 100644 --- a/src/Viewer/ViewPropertyEvaluator.hxx +++ b/src/Viewer/ViewPropertyEvaluator.hxx @@ -106,6 +106,9 @@ namespace ViewPropertyEvaluator { node, instead it always calls ->getDoubleValue(). */ double getDoubleValue(const char* spec, double default_=0); + + /* Similar to getDoubleValue(). */ + bool getBoolValue(const char* spec, bool default_=false); /* Outputs detailed information about all specs that have been seen. diff --git a/src/Viewer/fg_os_osgviewer.cxx b/src/Viewer/fg_os_osgviewer.cxx index dbe2db560..6083db188 100644 --- a/src/Viewer/fg_os_osgviewer.cxx +++ b/src/Viewer/fg_os_osgviewer.cxx @@ -361,7 +361,6 @@ void fgOSInit(int* argc, char** argv) #if defined(HAVE_QT) global_usingGraphicsWindowQt = fgGetBool("/sim/rendering/graphics-window-qt", false); if (global_usingGraphicsWindowQt) { - flightgear::initApp(*argc, argv); SG_LOG(SG_GL, SG_INFO, "Using Qt implementation of GraphicsWindow"); flightgear::initQtWindowingSystem(); } else { diff --git a/src/Viewer/renderer_compositor.cxx b/src/Viewer/renderer_compositor.cxx index 16d6cf594..b787e7e99 100644 --- a/src/Viewer/renderer_compositor.cxx +++ b/src/Viewer/renderer_compositor.cxx @@ -414,6 +414,7 @@ FGRenderer::init( void ) _splash_alpha = fgGetNode("/sim/startup/splash-alpha", true); _point_sprites = fgGetNode("/sim/rendering/point-sprites", true); + _triangle_directional_lights = fgGetNode("/sim/rendering/triangle-directional-lights", true); _distance_attenuation = fgGetNode("/sim/rendering/distance-attenuation", true); _horizon_effect = fgGetNode("/sim/rendering/horizon-effect", true); @@ -424,8 +425,9 @@ FGRenderer::init( void ) bool use_point_sprites = _point_sprites->getBoolValue(); bool distance_attenuation = _distance_attenuation->getBoolValue(); + bool triangles = _triangle_directional_lights->getBoolValue(); - SGConfigureDirectionalLights( use_point_sprites, distance_attenuation ); + SGConfigureDirectionalLights( use_point_sprites, distance_attenuation, triangles ); if (const char* tc = fgGetString("/sim/rendering/texture-compression", NULL)) { if (strcmp(tc, "false") == 0 || strcmp(tc, "off") == 0 || @@ -449,15 +451,13 @@ FGRenderer::init( void ) // on other subsystems to be inited, eg Ephemeris _sky = new SGSky; - SGPath texture_path(globals->get_fg_root()); - texture_path.append("Textures"); - texture_path.append("Sky"); + const SGPath texture_path = globals->get_fg_root() / "Textures" / "Sky"; for (int i = 0; i < FGEnvironmentMgr::MAX_CLOUD_LAYERS; i++) { - SGCloudLayer * layer = new SGCloudLayer(texture_path.local8BitStr()); + SGCloudLayer * layer = new SGCloudLayer(texture_path); _sky->add_cloud_layer(layer); } - _sky->texture_path( texture_path.local8BitStr() ); + _sky->set_texture_path( texture_path ); // XXX: Should always be true eventHandler->setChangeStatsCameraRenderOrder( true ); @@ -527,7 +527,7 @@ FGRenderer::setupView( void ) // Moon diameter: 3,476 kilometers // Sun diameter: 1,390,000 kilometers osg::ref_ptr opt; - opt = simgear::SGReaderWriterOptions::fromPath(globals->get_fg_root().local8BitStr()); + opt = simgear::SGReaderWriterOptions::fromPath(globals->get_fg_root()); opt->setPropertyNode(globals->get_props()); _sky->build( 80000.0, 80000.0, 463.3, 361.8, @@ -657,7 +657,7 @@ FGRenderer::update( ) { GLint max_texture_size; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); - // abritrary range change as sometimes during init the device reports somewhat dubious values, so + // abritrary range change as sometimes during init the device reports somewhat dubious values, so // we know that the size must be greater than size and that probably 9999999 is unreasonably large if (max_texture_size > 0 && max_texture_size < 9999999) SGSceneFeatures::instance()->setMaxTextureSize(max_texture_size); diff --git a/src/Viewer/renderer_compositor.hxx b/src/Viewer/renderer_compositor.hxx index dd228945e..e5fff12d1 100644 --- a/src/Viewer/renderer_compositor.hxx +++ b/src/Viewer/renderer_compositor.hxx @@ -95,7 +95,7 @@ protected: SGPropertyNode_ptr _scenery_loaded, _position_finalized; SGPropertyNode_ptr _splash_alpha; - SGPropertyNode_ptr _point_sprites, _enhanced_lighting, _distance_attenuation; + SGPropertyNode_ptr _point_sprites, _enhanced_lighting, _distance_attenuation, _triangle_directional_lights; SGPropertyNode_ptr _textures; SGPropertyNode_ptr _cloud_status, _visibility_m; SGPropertyNode_ptr _xsize, _ysize; diff --git a/src/Viewer/renderer_legacy.cxx b/src/Viewer/renderer_legacy.cxx old mode 100644 new mode 100755 index 508258ca9..0b35ed1f2 --- a/src/Viewer/renderer_legacy.cxx +++ b/src/Viewer/renderer_legacy.cxx @@ -179,26 +179,26 @@ public: glPushAttrib(GL_ALL_ATTRIB_BITS); glPushClientAttrib(~0u); - + // HUD can be NULL HUD *hud = static_cast(globals->get_subsystem("hud")); if (hud) { hud->draw(state); } - + glPopClientAttrib(); glPopAttrib(); } virtual osg::Object* cloneType() const { return new SGHUDDrawable; } virtual osg::Object* clone(const osg::CopyOp&) const { return new SGHUDDrawable; } - + private: }; class FGLightSourceUpdateCallback : public osg::NodeCallback { public: - + /** * @param isSun true if the light is the actual sun i.e., for * illuminating the moon. @@ -209,19 +209,19 @@ public: : NodeCallback(nc, op), _isSun(nc._isSun) {} META_Object(flightgear,FGLightSourceUpdateCallback); - + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { assert(dynamic_cast(node)); osg::LightSource* lightSource = static_cast(node); osg::Light* light = lightSource->getLight(); - + FGLight *l = static_cast(globals->get_subsystem("lighting")); if (!l) { // lighting is down during re-init return; } - + if (_isSun) { light->setAmbient(Vec4(0.0f, 0.0f, 0.0f, 0.0f)); light->setDiffuse(Vec4(1.0f, 1.0f, 1.0f, 1.0f)); @@ -364,12 +364,12 @@ FGRenderer::FGRenderer() : _root->setName("fakeRoot"); _updateVisitor = new SGUpdateVisitor; - + // when Rembrandt is enabled, we use this group to access the whole // scene. Since the only child is the _viewerSceneRoot, we could // simply copy the reference, we don't need the additional group. _deferredRealRoot = new osg::Group; - + _numCascades = 4; _cascadeFar[0] = 5.f; _cascadeFar[1] = 50.f; @@ -383,12 +383,12 @@ FGRenderer::~FGRenderer() for (; i != _listeners.end(); ++i) { delete *i; } - + // replace the viewer's scene completely if (getViewer()) { getViewer()->setSceneData(new osg::Group); } - + delete _sky; } @@ -421,7 +421,7 @@ FGRenderer::preinit( void ) camera->addChild(_splash); } } - + _frameStamp = new osg::FrameStamp; viewer->setFrameStamp(_frameStamp.get()); // Scene doesn't seem to pass the frame stamp to the update @@ -429,7 +429,7 @@ FGRenderer::preinit( void ) _updateVisitor->setFrameStamp(_frameStamp.get()); viewer->setUpdateVisitor(_updateVisitor.get()); fgSetDouble("/sim/startup/splash-alpha", 1.0); - + // hide the menubar if it overlaps the window, so the splash screen // is completely visible. We reset this value when the splash screen // is fading out. @@ -470,7 +470,7 @@ FGRenderer::addChangeListener(SGPropertyChangeListener* l, const char* path) _listeners.push_back(l); fgAddChangeListener(l, path); } - + void FGRenderer::init( void ) { @@ -507,7 +507,7 @@ FGRenderer::init( void ) _pipeline = makeRenderingPipeline(_renderer, 0); _scenery_loaded = fgGetNode("/sim/sceneryloaded", true); _position_finalized = fgGetNode("/sim/position-finalized", true); - + _panel_hotspots = fgGetNode("/sim/panel-hotspots", true); _virtual_cockpit = fgGetNode("/sim/virtual-cockpit", true); @@ -519,17 +519,19 @@ FGRenderer::init( void ) _point_sprites = fgGetNode("/sim/rendering/point-sprites", true); _distance_attenuation = fgGetNode("/sim/rendering/distance-attenuation", true); + _triangle_directional_lights = fgGetNode("/sim/rendering/triangle-directional-lights", true); _horizon_effect = fgGetNode("/sim/rendering/horizon-effect", true); _altitude_ft = fgGetNode("/position/altitude-ft", true); _cloud_status = fgGetNode("/environment/clouds/status", true); _visibility_m = fgGetNode("/environment/visibility-m", true); - + bool use_point_sprites = _point_sprites->getBoolValue(); bool distance_attenuation = _distance_attenuation->getBoolValue(); + bool triangles = _triangle_directional_lights->getBoolValue(); - SGConfigureDirectionalLights( use_point_sprites, distance_attenuation ); + SGConfigureDirectionalLights( use_point_sprites, distance_attenuation, triangles ); if (const char* tc = fgGetString("/sim/rendering/texture-compression", NULL)) { if (strcmp(tc, "false") == 0 || strcmp(tc, "off") == 0 || @@ -550,18 +552,16 @@ FGRenderer::init( void ) } SGSceneFeatures::instance()->setTextureCompressionPath(globals->get_texture_cache_dir()); // create sky, but can't build until setupView, since we depend -// on other subsystems to be inited, eg Ephemeris +// on other subsystems to be inited, eg Ephemeris _sky = new SGSky; - - SGPath texture_path(globals->get_fg_root()); - texture_path.append("Textures"); - texture_path.append("Sky"); + + const SGPath texture_path = globals->get_fg_root() / "Textures" / "Sky"; for (int i = 0; i < FGEnvironmentMgr::MAX_CLOUD_LAYERS; i++) { - SGCloudLayer * layer = new SGCloudLayer(texture_path.local8BitStr()); + SGCloudLayer * layer = new SGCloudLayer(texture_path); _sky->add_cloud_layer(layer); } - - _sky->texture_path( texture_path.local8BitStr() ); + + _sky->set_texture_path( texture_path ); if (!_classicalRenderer) { eventHandler->setChangeStatsCameraRenderOrder( true ); @@ -621,7 +621,7 @@ FGRenderer::buildClassicalPipeline(CameraGroup* cgroup, unsigned flags, osg::Cam // The camera group will always update the camera camera->setReferenceFrame(Transform::ABSOLUTE_RF); info->name = "classic"; - + Camera* farCamera = 0; if ((flags & (CameraGroup::GUI | CameraGroup::ORTHO)) == 0) { farCamera = new Camera; @@ -1094,7 +1094,7 @@ FGRenderer::buildDeferredPipeline(CameraGroup* cgroup, unsigned flags, osg::Came return buildCameraFromRenderingPipeline(_pipeline, cgroup, flags, camera, view, projection, gc); } -osg::Camera* +osg::Camera* FGRenderer::buildDeferredFullscreenCamera( flightgear::CameraInfo* info, const FGRenderingPipeline::Pass* pass ) { osg::Camera* camera = new osg::Camera; @@ -1134,7 +1134,7 @@ FGRenderer::buildDeferredFullscreenCamera( flightgear::CameraInfo* info, const F g->setUseDisplayList(false); simgear::EffectGeode* eg = new simgear::EffectGeode; osg::ref_ptr opt; - opt = SGReaderWriterOptions::fromPath(globals->get_fg_root().local8BitStr()); + opt = SGReaderWriterOptions::fromPath(globals->get_fg_root()); opt->setPropertyNode(globals->get_props()); simgear::Effect* effect = simgear::makeEffect(pass->effect, true, opt.get()); if (effect) { @@ -1149,7 +1149,7 @@ FGRenderer::buildDeferredFullscreenCamera( flightgear::CameraInfo* info, const F return camera; } -osg::Camera* +osg::Camera* FGRenderer::buildDeferredFullscreenCamera( flightgear::CameraInfo* info, osg::GraphicsContext* gc, const FGRenderingPipeline::Stage* stage ) { osg::Camera* camera = buildDeferredFullscreenCamera(info, static_cast(stage)); @@ -1187,7 +1187,7 @@ FGRenderer::buildDeferredDisplayCamera( osg::Camera* camera, flightgear::CameraI g->setUseDisplayList(false); //DEBUG simgear::EffectGeode* eg = new simgear::EffectGeode; osg::ref_ptr opt; - opt = SGReaderWriterOptions::fromPath(globals->get_fg_root().local8BitStr()); + opt = SGReaderWriterOptions::fromPath(globals->get_fg_root()); opt->setPropertyNode(globals->get_props()); simgear::Effect* effect = simgear::makeEffect(stage->effect, true, opt.get()); if (!effect) { @@ -1269,7 +1269,7 @@ FGRenderer::buildLightingSkyCloudsPass(FGRenderingPipeline::Pass* pass) Group* group = new Group; group->setName("skyCloudsGroup"); group->setNodeMask(simgear::BACKGROUND_BIT); - + StateSet* ss = group->getOrCreateStateSet(); ss->setAttributeAndModes( new osg::ColorMask( true, true, true, false ), osg::StateAttribute::ON ); group->addChild( _sky->getPreRoot() ); @@ -1345,7 +1345,7 @@ CameraInfo* FGRenderer::buildCameraFromRenderingPipeline(FGRenderingPipeline* rp { CameraInfo* info = new CameraInfo(flags); buildBuffers(rpipe, info); - + for (size_t i = 0; i < rpipe->stages.size(); ++i) { osg::ref_ptr stage = rpipe->stages[i]; buildStage(info, stage, cgroup, camera, view, projection, gc); @@ -1359,26 +1359,26 @@ CameraInfo* FGRenderer::buildCameraFromRenderingPipeline(FGRenderingPipeline* rp void FGRenderer::setupRoot() { osg::StateSet* stateSet = _root->getOrCreateStateSet(); - + stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); - + stateSet->setAttribute(new osg::Depth(osg::Depth::LESS)); stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); - + stateSet->setAttribute(new osg::BlendFunc); stateSet->setMode(GL_BLEND, osg::StateAttribute::OFF); - + stateSet->setMode(GL_FOG, osg::StateAttribute::OFF); - + // this will be set below stateSet->setMode(GL_NORMALIZE, osg::StateAttribute::OFF); - + osg::Material* material = new osg::Material; stateSet->setAttribute(material); - + stateSet->setTextureAttribute(0, new osg::TexEnv); stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF); - + osg::Hint* hint = new osg::Hint(GL_FOG_HINT, GL_DONT_CARE); hint->setUpdateCallback(new FGHintUpdateCallback("/sim/rendering/fog")); stateSet->setAttribute(hint); @@ -1395,7 +1395,7 @@ void FGRenderer::setupRoot() hint->setUpdateCallback(new FGHintUpdateCallback("/sim/rendering/perspective-correction")); stateSet->setAttribute(hint); } - + void FGRenderer::setupView( void ) { @@ -1409,7 +1409,7 @@ FGRenderer::setupView( void ) osg::PolygonOffset::setFactorMultiplier(1); setupRoot(); - + // build the sky Ephemeris* ephemerisSub = globals->get_subsystem(); @@ -1420,18 +1420,18 @@ FGRenderer::setupView( void ) // Moon diameter: 3,476 kilometers // Sun diameter: 1,390,000 kilometers osg::ref_ptr opt; - opt = SGReaderWriterOptions::fromPath(globals->get_fg_root().local8BitStr()); + opt = SGReaderWriterOptions::fromPath(globals->get_fg_root()); opt->setPropertyNode(globals->get_props()); _sky->build( 80000.0, 80000.0, 463.3, 361.8, *ephemerisSub->data(), fgGetNode("/environment", true), opt.get()); - + viewer->getCamera() ->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR); - - + + // need to update the light on every frame // OSG LightSource objects are rather confusing. OSG only supports // the 10 lights specified by OpenGL itself; if more than one @@ -1441,7 +1441,7 @@ FGRenderer::setupView( void ) // LightSource is just a shortcut for setting up a state set that // has the corresponding OpenGL light enabled: a LightSource will // affect geometry anywhere in the scene graph that has its light - // number enabled in a state set. + // number enabled in a state set. osg::ref_ptr lightSource = new LightSource; lightSource->setName("FGLightSource"); lightSource->getLight()->setDataVariance(Object::DYNAMIC); @@ -1450,7 +1450,7 @@ FGRenderer::setupView( void ) lightSource->setLocalStateSetModes(osg::StateAttribute::ON); lightSource->setUpdateCallback(new FGLightSourceUpdateCallback); _viewerSceneRoot->addChild(lightSource); - + // we need a white diffuse light for the phase of the moon osg::ref_ptr sunLight = new osg::LightSource; sunLight->setName("sunLightSource"); @@ -1459,23 +1459,23 @@ FGRenderer::setupView( void ) sunLight->setUpdateCallback(new FGLightSourceUpdateCallback(true)); sunLight->setReferenceFrame(osg::LightSource::RELATIVE_RF); sunLight->setLocalStateSetModes(osg::StateAttribute::ON); - + // Hang a StateSet above the sky subgraph in order to turn off // light 0 Group* skyGroup = _sky->getPreRoot(); StateSet* skySS = skyGroup->getOrCreateStateSet(); skySS->setMode(GL_LIGHT0, StateAttribute::OFF); sunLight->addChild(skyGroup); - + if ( _classicalRenderer ) { _root->addChild(sunLight); } - + osg::Group* sceneGroup = globals->get_scenery()->get_scene_graph(); sceneGroup->setName("rendererScene"); sceneGroup->setNodeMask(~simgear::BACKGROUND_BIT); _root->addChild(sceneGroup); - + // setup state-set for main scenery (including models and aircraft) osg::StateSet* stateSet = sceneGroup->getOrCreateStateSet(); stateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON); @@ -1525,7 +1525,7 @@ FGRenderer::setupView( void ) guiCamera->insertChild(0, geode); guiCamera->insertChild(0, FGPanelNode::create2DPanelNode()); } - + osg::Switch* sw = new osg::Switch; sw->setName("scenerySwitch"); sw->setUpdateCallback(new FGScenerySwitchCallback); @@ -1538,7 +1538,7 @@ FGRenderer::setupView( void ) _viewerSceneRoot->addChild(_sky->getCloudRoot()); _viewerSceneRoot->addChild(FGCreateRedoutNode()); } - + // Attach empty program to the scene root so that shader programs // don't leak into state sets (effects) that shouldn't have one. stateSet = _viewerSceneRoot->getOrCreateStateSet(); @@ -1612,7 +1612,7 @@ FGRenderer::update( ) { flightgear::View *current__view = globals->get_current_view(); // Force update of center dependent values ... current__view->set_dirty(); - + osg::Camera *camera = viewer->getCamera(); osg::Vec4 clear_color = _altitude_ft->getDoubleValue() < 250000 @@ -1625,7 +1625,7 @@ FGRenderer::update( ) { camera->setClearColor(clear_color); updateSky(); - + // need to call the update visitor once _frameStamp->setCalendarTime(*globals->get_time_params()->getGmt()); _updateVisitor->setViewData(current__view->getViewPosition(), @@ -1655,18 +1655,18 @@ FGRenderer::updateSky() // update fog params if visibility has changed double visibility_meters = _visibility_m->getDoubleValue(); _sky->set_visibility(visibility_meters); - + double altitude_m = _altitude_ft->getDoubleValue() * SG_FEET_TO_METER; _sky->modify_vis( altitude_m, 0.0 /* time factor, now unused */); FGLight *l = static_cast(globals->get_subsystem("lighting")); - + // The sun and moon distances are scaled down versions // of the actual distance to get both the moon and the sun // within the range of the far clip plane. // Moon distance: 384,467 kilometers // Sun distance: 150,000,000 kilometers - + double sun_horiz_eff, moon_horiz_eff; if (_horizon_effect->getBoolValue()) { sun_horiz_eff @@ -1682,7 +1682,7 @@ FGRenderer::updateSky() } - + SGSkyState sstate; sstate.pos = globals->get_current_view()->getViewPosition(); sstate.pos_geod = globals->get_current_view()->getPosition(); @@ -1692,7 +1692,7 @@ FGRenderer::updateSky() sstate.sun_dist = 50000.0 * sun_horiz_eff; sstate.moon_dist = 40000.0 * moon_horiz_eff; sstate.sun_angle = l->get_sun_angle(); - + SGSkyColor scolor; scolor.sky_color = SGVec3f(l->sky_color().data()); scolor.adj_sky_color = SGVec3f(l->adj_sky_color().data()); @@ -1706,7 +1706,7 @@ FGRenderer::updateSky() _sky->reposition( sstate, *ephemerisSub->data(), delta_time_sec ); _sky->repaint( scolor, *ephemerisSub->data() ); } - + void FGRenderer::resize( int width, int height ) { @@ -1799,12 +1799,12 @@ PickList FGRenderer::pick(const osg::Vec2& windowPos) ++hit) { const osg::NodePath& np = hit->nodePath; osg::NodePath::const_reverse_iterator npi; - + for (npi = np.rbegin(); npi != np.rend(); ++npi) { SGSceneUserData* ud = SGSceneUserData::getSceneUserData(*npi); if (!ud || (ud->getNumPickCallbacks() == 0)) continue; - + for (unsigned i = 0; i < ud->getNumPickCallbacks(); ++i) { SGPickCallback* pickCallback = ud->getPickCallback(i); if (!pickCallback) @@ -1821,7 +1821,7 @@ PickList FGRenderer::pick(const osg::Vec2& windowPos) } // of installed pick callbacks iteration } // of reverse node path walk } - + return result; } @@ -1848,7 +1848,7 @@ FGRenderer::removeCamera(osg::Camera* camera) { _viewerSceneRoot->removeChild(camera); } - + void FGRenderer::setPlanes( double zNear, double zFar ) { @@ -1939,7 +1939,7 @@ public: } cout << endl; } - + void doTraversal(Camera* camera, Node* root, Viewport* viewport) { ref_ptr projection @@ -2019,7 +2019,7 @@ public: modelview = createOrReuseMatrix(*getModelViewMatrix() * camera.getViewMatrix()); } - else { // pre multiply + else { // pre multiply projection = createOrReuseMatrix(camera.getProjectionMatrix() * (*getProjectionMatrix())); modelview = createOrReuseMatrix(camera.getViewMatrix() @@ -2034,10 +2034,10 @@ public: pushViewport(camera.getViewport()); pushProjectionMatrix(projection); - pushModelViewMatrix(modelview, camera.getReferenceFrame()); + pushModelViewMatrix(modelview, camera.getReferenceFrame()); traverse(camera); - + // restore the previous model view matrix. popModelViewMatrix(); @@ -2079,4 +2079,3 @@ bool printVisibleSceneInfo(FGRenderer* renderer) } // end of renderer.cxx - diff --git a/src/Viewer/renderer_legacy.hxx b/src/Viewer/renderer_legacy.hxx index a3dd3272a..174abc13a 100644 --- a/src/Viewer/renderer_legacy.hxx +++ b/src/Viewer/renderer_legacy.hxx @@ -64,7 +64,7 @@ public: void resize(int width, int height ); void update(); - + /** Just pick into the scene and return the pick callbacks on the way ... */ PickList pick(const osg::Vec2& windowPos); @@ -85,7 +85,7 @@ public: void addCamera(osg::Camera* camera, bool useSceneData); void removeCamera(osg::Camera* camera); - + /** Add a camera to the group. The camera is added to the viewer * as a slave. See osgViewer::Viewer::addSlave. * @param flags properties of the camera; see CameraGroup::Flags @@ -126,20 +126,20 @@ public: protected: osg::ref_ptr viewer; osg::ref_ptr eventHandler; - + osg::ref_ptr _frameStamp; osg::ref_ptr _updateVisitor; osg::ref_ptr _viewerSceneRoot; osg::ref_ptr _deferredRealRoot; osg::ref_ptr _root; - + SGPropertyNode_ptr _scenery_loaded, _position_finalized; - - + + SGPropertyNode_ptr _splash_alpha; - SGPropertyNode_ptr _point_sprites, _enhanced_lighting, _distance_attenuation; + SGPropertyNode_ptr _point_sprites, _enhanced_lighting, _distance_attenuation, _triangle_directional_lights; SGPropertyNode_ptr _textures; - SGPropertyNode_ptr _cloud_status, _visibility_m; + SGPropertyNode_ptr _cloud_status, _visibility_m; SGPropertyNode_ptr _xsize, _ysize; SGPropertyNode_ptr _panel_hotspots, _sim_delta_sec, _horizon_effect, _altitude_ft; SGPropertyNode_ptr _virtual_cockpit; @@ -155,7 +155,7 @@ protected: typedef std::vector SGPropertyChangeListenerVec; SGPropertyChangeListenerVec _listeners; - + flightgear::CameraInfo* buildCameraFromRenderingPipeline(FGRenderingPipeline* rpipe, flightgear::CameraGroup* cgroup, unsigned flags, osg::Camera* camera, const osg::Matrix& view, const osg::Matrix& projection, osg::GraphicsContext* gc); @@ -186,11 +186,11 @@ protected: osg::ref_ptr _depthInColor; osg::ref_ptr _pipeline; - + void addChangeListener(SGPropertyChangeListener* l, const char* path); - + void updateSky(); - + void setupRoot(); SplashScreen* _splash; diff --git a/src/Viewer/splash.cxx b/src/Viewer/splash.cxx index 7064ef539..8e69b53aa 100644 --- a/src/Viewer/splash.cxx +++ b/src/Viewer/splash.cxx @@ -45,6 +45,7 @@ #include #include #include +#include #include
#include
@@ -56,8 +57,11 @@ #include + static const char* LICENSE_URL_TEXT = "Licensed under the GNU GPL. See http://www.flightgear.org for more information"; +using namespace simgear; + class SplashScreenUpdateCallback : public osg::NodeCallback { public: virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) @@ -81,13 +85,20 @@ SplashScreen::~SplashScreen() void SplashScreen::createNodes() { + std::string splashImage = selectSplashImage(); #if OSG_VERSION_LESS_THAN(3,4,0) - _splashImage = osgDB::readImageFile(selectSplashImage()); + _splashImage = osgDB::readImageFile(splashImage); #else - _splashImage = osgDB::readRefImageFile(selectSplashImage()); + osg::ref_ptr staticOptions = SGReaderWriterOptions::copyOrCreate(osgDB::Registry::instance()->getOptions()); + staticOptions->setLoadOriginHint(SGReaderWriterOptions::LoadOriginHint::ORIGIN_SPLASH_SCREEN); + _splashImage = osgDB::readRefImageFile(splashImage, staticOptions); #endif - int width = _splashImage->s(); + if (!_splashImage){ + SG_LOG(SG_VIEW, SG_INFO, "Splash Image " << splashImage << " failed to load"); + return; + } + int width = _splashImage->s(); int height = _splashImage->t(); _splashImageAspectRatio = static_cast(width) / height; @@ -284,12 +295,16 @@ void SplashScreen::setupLogoImage() return; } -#if OSG_VERSION_LESS_THAN(3,4,0) - _logoImage = osgDB::readImageFile(logoPath.utf8Str()); + osg::ref_ptr staticOptions = simgear::SGReaderWriterOptions::copyOrCreate(osgDB::Registry::instance()->getOptions()); + staticOptions->setLoadOriginHint(simgear::SGReaderWriterOptions::LoadOriginHint::ORIGIN_SPLASH_SCREEN); + +#if OSG_VERSION_LESS_THAN(3, 4, 0) + _logoImage = osgDB::readImageFile(logoPath.utf8Str(), staticOptions); #else - _logoImage = osgDB::readRefImageFile(logoPath.utf8Str()); + _logoImage = osgDB::readRefImageFile(logoPath.utf8Str(), staticOptions); #endif if (!_logoImage) { + SG_LOG(SG_VIEW, SG_INFO, "Splash logo image " << logoPath << " failed to load"); return; } diff --git a/src/Viewer/view.cxx b/src/Viewer/view.cxx index f87a2549e..cb640ef75 100644 --- a/src/Viewer/view.cxx +++ b/src/Viewer/view.cxx @@ -1000,17 +1000,9 @@ View::recalcLookAt () SGGeodesy::SGCartToGeod(targetCart2, _target); _position = _target; - - const std::string& tail = ViewPropertyEvaluator::getStringValue("(/sim/view[(/sim/current-view/view-number-raw)]/config/eye-lon-deg-path)"); - - if (tail != "") { - /* will typically be /sim/tower/longitude-deg, so that this view's - eye position is from the tower rather than relative to the aircraft. - If we are viewing a multiplayer aircraft, the nearest tower - will be in /sim/view[]/config/root/sim/tower/longitude-deg (see - FGEnvironmentMgr::updateClosestAirport()), so we use a prefix to select - either /sim/tower/longitude-deg or multiplayer's tower. */ + bool eye_fixed = ViewPropertyEvaluator::getBoolValue("(/sim/view[(/sim/current-view/view-number-raw)]/config/eye-fixed)"); + if (eye_fixed) { _position.setLongitudeDeg( ViewPropertyEvaluator::getDoubleValue( "((/sim/view[(/sim/current-view/view-number-raw)]/config/root)(/sim/view[(/sim/current-view/view-number-raw)]/config/eye-lon-deg-path))", @@ -1031,6 +1023,25 @@ View::recalcLookAt () ); } + _target.setLongitudeDeg( + ViewPropertyEvaluator::getDoubleValue( + "((/sim/view[(/sim/current-view/view-number-raw)]/config/root)(/sim/view[(/sim/current-view/view-number-raw)]/config/target-lon-deg-path))", + _target.getLongitudeDeg() + ) + ); + _target.setLatitudeDeg( + ViewPropertyEvaluator::getDoubleValue( + "((/sim/view[(/sim/current-view/view-number-raw)]/config/root)(/sim/view[(/sim/current-view/view-number-raw)]/config/target-lat-deg-path))", + _target.getLatitudeDeg() + ) + ); + _target.setElevationFt( + ViewPropertyEvaluator::getDoubleValue( + "((/sim/view[(/sim/current-view/view-number-raw)]/config/root)(/sim/view[(/sim/current-view/view-number-raw)]/config/target-alt-ft-path))", + _target.getElevationFt() + ) + ); + if (_lookat_agl) { handleAGL(); } diff --git a/test_suite/CMakeLists.txt b/test_suite/CMakeLists.txt index 89ce2d831..bcd803974 100644 --- a/test_suite/CMakeLists.txt +++ b/test_suite/CMakeLists.txt @@ -135,6 +135,7 @@ set_target_properties(fgfs_test_suite RUNTIME_OUTPUT_DIRECTORY "${TESTSUITE_OUTPUT_DIR}" ) if(ENABLE_AUTOTESTING) + set(TEST_SUITE_COMMAND "fgfs_test_suite") set(TEST_SUITE_COMMENT "Running the full FlightGear test suite") else() set(TEST_SUITE_COMMENT "Building the FlightGear test suite.") @@ -162,7 +163,7 @@ if (MSVC) endif() add_custom_target(test_suite - fgfs_test_suite + ${TEST_SUITE_COMMAND} DEPENDS fgfs_test_suite COMMENT ${TEST_SUITE_COMMENT} ) diff --git a/test_suite/FGTestApi/testGlobals.cxx b/test_suite/FGTestApi/testGlobals.cxx index 2f402c887..4144ecaa0 100644 --- a/test_suite/FGTestApi/testGlobals.cxx +++ b/test_suite/FGTestApi/testGlobals.cxx @@ -104,7 +104,7 @@ bool logPositionToKML(const std::string& testName) return true; } -void initStandardNasal() +void initStandardNasal(bool withCanvas) { fgInitAllowedPaths(); @@ -123,7 +123,7 @@ void initStandardNasal() props->setBoolValue("sim/rendering/precipitation-aircraft-enable", false); // disable various larger modules - nasalNode->setBoolValue("canvas/enabled", false); + nasalNode->setBoolValue("canvas/enabled", withCanvas); nasalNode->setBoolValue("jetways/enabled", false); nasalNode->setBoolValue("jetways_edit/enabled", false); nasalNode->setBoolValue("local_weather/enabled", false); diff --git a/test_suite/FGTestApi/testGlobals.hxx b/test_suite/FGTestApi/testGlobals.hxx index a243f6fcf..051b4fdbf 100644 --- a/test_suite/FGTestApi/testGlobals.hxx +++ b/test_suite/FGTestApi/testGlobals.hxx @@ -24,7 +24,7 @@ void initTestGlobals(const std::string& testName); bool logPositionToKML(const std::string& testName); -void initStandardNasal(); +void initStandardNasal(bool withCanvas = false); void populateFPWithoutNasal(flightgear::FlightPlanRef f, const std::string& depICAO, const std::string& depRunway, diff --git a/test_suite/simgear_tests/CMakeLists.txt b/test_suite/simgear_tests/CMakeLists.txt index 59cb6bb4f..e1f7a98f0 100644 --- a/test_suite/simgear_tests/CMakeLists.txt +++ b/test_suite/simgear_tests/CMakeLists.txt @@ -2,6 +2,7 @@ foreach( simgear_test_category math props + canvas ) add_subdirectory(${simgear_test_category}) diff --git a/test_suite/simgear_tests/canvas/CMakeLists.txt b/test_suite/simgear_tests/canvas/CMakeLists.txt new file mode 100644 index 000000000..d437cec8d --- /dev/null +++ b/test_suite/simgear_tests/canvas/CMakeLists.txt @@ -0,0 +1,12 @@ +set(TESTSUITE_SOURCES + ${TESTSUITE_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/testCanvas.cxx + PARENT_SCOPE +) + +set(TESTSUITE_HEADERS + ${TESTSUITE_HEADERS} + ${CMAKE_CURRENT_SOURCE_DIR}/testCanvas.hxx + PARENT_SCOPE +) diff --git a/test_suite/simgear_tests/canvas/TestSuite.cxx b/test_suite/simgear_tests/canvas/TestSuite.cxx new file mode 100644 index 000000000..b5e695037 --- /dev/null +++ b/test_suite/simgear_tests/canvas/TestSuite.cxx @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2016 Edward d'Auvergne + * + * This file is part of the program FlightGear. + * + * 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 . + */ + +#include "testCanvas.hxx" + + +// Set up the tests. +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CanvasTests, "Simgear unit tests"); diff --git a/test_suite/simgear_tests/canvas/testCanvas.cxx b/test_suite/simgear_tests/canvas/testCanvas.cxx new file mode 100644 index 000000000..1b5652595 --- /dev/null +++ b/test_suite/simgear_tests/canvas/testCanvas.cxx @@ -0,0 +1,120 @@ +#include "testCanvas.hxx" + +#include +#include + +#include "test_suite/FGTestApi/testGlobals.hxx" + +#include
+#include
+ +#include +#include + +void verifyPixel(osg::Image* img, int x, int y, const uint8_t* pixel) +{ + auto rawPixel = img->data(x, y); + CPPUNIT_ASSERT(memcmp(rawPixel, pixel, 4) == 0); +} + +void CanvasTests::setUp() +{ + FGTestApi::setUp::initTestGlobals("canvas"); + + // Canvas needs loadxml command + fgInitCommands(); + + simgear::canvas::Canvas::setSystemAdapter( + simgear::canvas::SystemAdapterPtr(new canvas::FGCanvasSystemAdapter) + ); + + globals->add_subsystem("Canvas", new CanvasMgr, SGSubsystemMgr::DISPLAY); + + globals->get_subsystem_mgr()->bind(); + globals->get_subsystem_mgr()->init(); + + FGTestApi::setUp::initStandardNasal(true /* withCanvas */); + globals->get_subsystem_mgr()->postinit(); +} + +// Clean up after each test. +void CanvasTests::tearDown() +{ + FGTestApi::tearDown::shutdownTestGlobals(); +} + +void CanvasTests::testCanvasBasic() +{ + bool ok = FGTestApi::executeNasal(R"( + var my_canvas = canvas.new({ + "name": "PFD-Test", + "size": [512, 512], + "view": [768, 1024], + "mipmapping": 1 + }); + + globals.pfd = my_canvas; + )"); + CPPUNIT_ASSERT(ok); + + auto canvasPtr = globals->get_subsystem()->getCanvas("PFD-Test"); + CPPUNIT_ASSERT(canvasPtr.valid()); + + ok = FGTestApi::executeNasal(R"( + globals.pfd.createGroup("wibble"); + )"); + CPPUNIT_ASSERT(ok); + + auto wibbleGroupPtr = canvasPtr->getGroup("wibble"); + CPPUNIT_ASSERT(wibbleGroupPtr.valid()); +} + +void CanvasTests::testImagePixelOps() +{ + bool ok = FGTestApi::executeNasal(R"( + var my_canvas = canvas.new({ + "name": "ImagePixelOpsCanvas", + "size": [512, 512], + "view": [768, 1024], + "mipmapping": 1 + }); + + var g = my_canvas.createGroup("root"); + var img = g.createChild('image', 'ops-image'); + + # deliberate non-power-of-two sizes here + img.setSize(150, 170); + + globals.pixelOps = my_canvas; + )"); + CPPUNIT_ASSERT(ok); + + auto canvasPtr = globals->get_subsystem()->getCanvas("ImagePixelOpsCanvas"); + CPPUNIT_ASSERT(canvasPtr.valid()); + + auto imagePtr = canvasPtr->getRootGroup()->getElementById("ops-image"); + CPPUNIT_ASSERT(imagePtr.valid()); + + auto image = dynamic_cast(imagePtr.get()); + image->setFill(osg::Vec4(1.0, 0.0, 0.5, 1.0)); + + image->fillRect(SGRecti(20, 40, 70, 55), "#7f3f1f"); + + ok = FGTestApi::executeNasal(R"( + var canvas = globals.pixelOps; + var img = canvas.getGroup('root').getElementById('ops-image'); + + print('Image is:', img); + img.fillRect([5, 8, 180, 37], '#aabbcc'); + )"); + CPPUNIT_ASSERT(ok); + + auto osgImage = image->getImage(); + const uint8_t testColor1[] = {0xff, 0xcc, 0xbb, 0xaa}; // little endian + verifyPixel(osgImage.get(), 5, 8, testColor1); + verifyPixel(osgImage.get(), 149, 44, testColor1); + + // test pixles should not have been touched by the second fillRect + const uint8_t testColor2[] = {0xff, 0x1f, 0x3f, 0x7f}; // little endian + verifyPixel(osgImage.get(), 20, 45, testColor2); +} diff --git a/test_suite/simgear_tests/canvas/testCanvas.hxx b/test_suite/simgear_tests/canvas/testCanvas.hxx new file mode 100644 index 000000000..402cdf26e --- /dev/null +++ b/test_suite/simgear_tests/canvas/testCanvas.hxx @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 Edward d'Auvergne + * + * This file is part of the program FlightGear. + * + * 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 . + */ + + +#pragma once + + +#include +#include + + +// The unit tests. +class CanvasTests : public CppUnit::TestFixture +{ + // Set up the test suite. + CPPUNIT_TEST_SUITE(CanvasTests); + CPPUNIT_TEST(testCanvasBasic); + CPPUNIT_TEST(testImagePixelOps); + CPPUNIT_TEST_SUITE_END(); + +public: + // Set up function for each test. + void setUp(); + + // Clean up after each test. + void tearDown(); + + // The tests. + void testCanvasBasic(); + void testImagePixelOps(); +}; + + diff --git a/test_suite/unit_tests/Navaids/CMakeLists.txt b/test_suite/unit_tests/Navaids/CMakeLists.txt index bc0bfc66d..cc6ec4d0c 100644 --- a/test_suite/unit_tests/Navaids/CMakeLists.txt +++ b/test_suite/unit_tests/Navaids/CMakeLists.txt @@ -2,6 +2,7 @@ set(TESTSUITE_SOURCES ${TESTSUITE_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/TestSuite.cxx ${CMAKE_CURRENT_SOURCE_DIR}/test_flightplan.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/test_fpNasal.cxx ${CMAKE_CURRENT_SOURCE_DIR}/test_navaids2.cxx ${CMAKE_CURRENT_SOURCE_DIR}/test_aircraftPerformance.cxx ${CMAKE_CURRENT_SOURCE_DIR}/test_routeManager.cxx @@ -11,6 +12,7 @@ set(TESTSUITE_SOURCES set(TESTSUITE_HEADERS ${TESTSUITE_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/test_flightplan.hxx + ${CMAKE_CURRENT_SOURCE_DIR}/test_fpNasal.hxx ${CMAKE_CURRENT_SOURCE_DIR}/test_aircraftPerformance.hxx ${CMAKE_CURRENT_SOURCE_DIR}/test_routeManager.hxx PARENT_SCOPE diff --git a/test_suite/unit_tests/Navaids/TestSuite.cxx b/test_suite/unit_tests/Navaids/TestSuite.cxx index 9e2af8918..7ae0e8ed2 100644 --- a/test_suite/unit_tests/Navaids/TestSuite.cxx +++ b/test_suite/unit_tests/Navaids/TestSuite.cxx @@ -21,9 +21,11 @@ #include "test_navaids2.hxx" #include "test_aircraftPerformance.hxx" #include "test_routeManager.hxx" +#include "test_fpNasal.hxx" // Set up the unit tests. CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(FlightplanTests, "Unit tests"); +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(FPNasalTests, "Unit tests"); CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(NavaidsTests, "Unit tests"); CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(AircraftPerformanceTests, "Unit tests"); CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(RouteManagerTests, "Unit tests"); diff --git a/test_suite/unit_tests/Navaids/test_flightplan.cxx b/test_suite/unit_tests/Navaids/test_flightplan.cxx index 3a0f194e9..fe5fbd7be 100644 --- a/test_suite/unit_tests/Navaids/test_flightplan.cxx +++ b/test_suite/unit_tests/Navaids/test_flightplan.cxx @@ -24,6 +24,10 @@ void FlightplanTests::setUp() { FGTestApi::setUp::initTestGlobals("flightplan"); FGTestApi::setUp::initNavDataCache(); + + globals->get_subsystem_mgr()->bind(); + globals->get_subsystem_mgr()->init(); + globals->get_subsystem_mgr()->postinit(); } @@ -54,16 +58,16 @@ void FlightplanTests::testBasic() CPPUNIT_ASSERT(fp1->destinationAirport()->ident() == "EHAM"); CPPUNIT_ASSERT(fp1->destinationRunway()->ident() == "24"); - CPPUNIT_ASSERT_EQUAL(fp1->numLegs(), 5); + CPPUNIT_ASSERT_EQUAL(5, fp1->numLegs()); CPPUNIT_ASSERT(fp1->legAtIndex(0)->waypoint()->source()->ident() == "23L"); - + CPPUNIT_ASSERT(fp1->legAtIndex(1)->waypoint()->source()->ident() == "TNT"); CPPUNIT_ASSERT(fp1->legAtIndex(1)->waypoint()->source()->name() == "TRENT VOR-DME"); CPPUNIT_ASSERT(fp1->legAtIndex(2)->waypoint()->source()->ident() == "CLN"); CPPUNIT_ASSERT(fp1->legAtIndex(2)->waypoint()->source()->name() == "CLACTON VOR-DME"); - + CPPUNIT_ASSERT(fp1->legAtIndex(4)->waypoint()->source()->ident() == "24"); } @@ -147,24 +151,24 @@ void FlightplanTests::testRoutePathVec() FlightPlanRef fp1 = makeTestFP("KNUQ", "14L", "PHNL", "22R", "ROKME WOVAB"); RoutePath rtepath(fp1); - + SGGeodVec vec = rtepath.pathForIndex(0); - + FGAirportRef ksfo = FGAirport::findByIdent("KSFO"); FGFixRef rokme = fgpositioned_cast(FGPositioned::findClosestWithIdent("ROKME", ksfo->geod())); auto depRwy = fp1->departureRunway(); - + CPPUNIT_ASSERT_DOUBLES_EQUAL(depRwy->geod().getLongitudeDeg(), vec.front().getLongitudeDeg(), 0.01); CPPUNIT_ASSERT_DOUBLES_EQUAL(depRwy->geod().getLatitudeDeg(), vec.front().getLatitudeDeg(), 0.01); - + SGGeodVec vec1 = rtepath.pathForIndex(1); CPPUNIT_ASSERT_DOUBLES_EQUAL(rokme->geod().getLongitudeDeg(), vec1.back().getLongitudeDeg(), 0.01); CPPUNIT_ASSERT_DOUBLES_EQUAL(rokme->geod().getLatitudeDeg(), vec1.back().getLatitudeDeg(), 0.01); - + SGGeodVec vec2 = rtepath.pathForIndex(2); CPPUNIT_ASSERT_DOUBLES_EQUAL(rokme->geod().getLongitudeDeg(), vec2.front().getLongitudeDeg(), 0.01); CPPUNIT_ASSERT_DOUBLES_EQUAL(rokme->geod().getLatitudeDeg(), vec2.front().getLatitudeDeg(), 0.01); - + //CPPUNIT_ASSERT(vec.front() } @@ -172,22 +176,22 @@ void FlightplanTests::testRoutPathWpt0Midflight() { // test behaviour of RoutePath when WP0 is not a runway // happens for the Airbus ND which removes past wpts when sequencing - + FlightPlanRef fp1 = makeTestFP("KNUQ", "14L", "PHNL", "22R", "ROKME WOVAB"); // actually delete leg 0 so we start at ROKME fp1->deleteIndex(0); - + RoutePath rtepath(fp1); - + SGGeodVec vec = rtepath.pathForIndex(0); - + FGAirportRef ksfo = FGAirport::findByIdent("KSFO"); FGFixRef rokme = fgpositioned_cast(FGPositioned::findClosestWithIdent("ROKME", ksfo->geod())); - + CPPUNIT_ASSERT_DOUBLES_EQUAL(rokme->geod().getLongitudeDeg(), vec.front().getLongitudeDeg(), 0.01); CPPUNIT_ASSERT_DOUBLES_EQUAL(rokme->geod().getLatitudeDeg(), vec.front().getLatitudeDeg(), 0.01); - + SGGeodVec vec2 = rtepath.pathForIndex(1); CPPUNIT_ASSERT_DOUBLES_EQUAL(rokme->geod().getLongitudeDeg(), vec2.front().getLongitudeDeg(), 0.01); CPPUNIT_ASSERT_DOUBLES_EQUAL(rokme->geod().getLatitudeDeg(), vec2.front().getLatitudeDeg(), 0.01); @@ -199,11 +203,11 @@ void FlightplanTests::testBasicAirways() { Airway* awy = Airway::findByIdent("J547", Airway::HighLevel); CPPUNIT_ASSERT_EQUAL(awy->ident(), std::string("J547")); - + FGAirportRef kord = FGAirport::findByIdent("KORD"); FlightPlanRef f = new FlightPlan; f->setDeparture(kord); - + CPPUNIT_ASSERT(awy->findEnroute("KITOK")); CPPUNIT_ASSERT(awy->findEnroute("LESUB")); @@ -214,12 +218,12 @@ void FlightplanTests::testBasicAirways() auto wptKUBBS = f->waypointFromString("KUBBS"); auto wptFNT = f->waypointFromString("FNT"); - + CPPUNIT_ASSERT(awy->canVia(wptKUBBS, wptFNT)); - + WayptVec path = awy->via(wptKUBBS, wptFNT); CPPUNIT_ASSERT_EQUAL(4, static_cast(path.size())); - + CPPUNIT_ASSERT_EQUAL(std::string("PMM"), path.at(0)->ident()); CPPUNIT_ASSERT_EQUAL(std::string("HASTE"), path.at(1)->ident()); CPPUNIT_ASSERT_EQUAL(std::string("DEWIT"), path.at(2)->ident()); @@ -231,16 +235,16 @@ void FlightplanTests::testAirwayNetworkRoute() FGAirportRef egph = FGAirport::findByIdent("EGPH"); FlightPlanRef f = new FlightPlan; f->setDeparture(egph); - + auto highLevelNet = Airway::highLevel(); - + auto wptTLA = f->waypointFromString("TLA"); auto wptCNA = f->waypointFromString("CNA"); - + WayptVec route; bool ok = highLevelNet->route(wptTLA, wptCNA, route); CPPUNIT_ASSERT(ok); - + CPPUNIT_ASSERT_EQUAL(static_cast(route.size()), 18); } @@ -250,25 +254,25 @@ void FlightplanTests::testParseICAORoute() FlightPlanRef f = new FlightPlan; f->setDeparture(kord); f->setDestination(FGAirport::findByIdent("KSAN")); - + const char* route = "DCT JOT J26 IRK J96 SLN J18 GCK J96 CIM J134 GUP J96 KEYKE J134 DRK J78 LANCY J96 PKE"; // const char* route = "DCT KUBBS J547 FNT Q824 HOCKE Q905 SIKBO Q907 MIILS N400A TUDEP NATW GISTI DCT SLANY UL9 DIKAS UL18 GAVGO UL9 KONAN UL607 UBIDU Y863 RUDUS T109 HAREM T104 ANORA STAR"; bool ok = f->parseICAORouteString(route); CPPUNIT_ASSERT(ok); - - - + + + } void FlightplanTests::testParseICANLowLevelRoute() { const char* route = "DCT DPA V6 IOW V216 LAA V210 GOSIP V83 ACHES V210 BLOKE V83 ALS V210 RSK V95 INW V12 HOXOL V264 OATES V12 JUWSO V264 PKE"; - + FGAirportRef kord = FGAirport::findByIdent("KORD"); FlightPlanRef f = new FlightPlan; f->setDeparture(kord); f->setDestination(FGAirport::findByIdent("KSAN")); - + bool ok = f->parseICAORouteString(route); CPPUNIT_ASSERT(ok); } @@ -320,3 +324,73 @@ void FlightplanTests::testBug1814() CPPUNIT_ASSERT_DOUBLES_EQUAL(137, leg->distanceNm(), 0.5); CPPUNIT_ASSERT_DOUBLES_EQUAL(101, f->legAtIndex(2)->distanceNm(), 0.5); } + +void FlightplanTests::testLoadSaveMachRestriction() +{ + const std::string fpXML = R"( + + 2 + + SAWG + 25 + + + SUMU + + + + navaid + PUGLI + -60.552200 + -40.490000 + + + basic + at + 36000 + mach + 1.24 + SV002 + -115.50531 + 37.89523 + + + navaid + SIGUL + -60.552200 + -40.490000 + + + + )"; + + std::istringstream stream(fpXML); + FlightPlanRef f = new FlightPlan; + bool ok = f->load(stream); + CPPUNIT_ASSERT(ok); + + auto leg = f->legAtIndex(1); + CPPUNIT_ASSERT_EQUAL(SPEED_RESTRICT_MACH, leg->speedRestriction()); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.24, leg->speedMach(), 0.01); + + auto firstLeg = f->legAtIndex(0); + firstLeg->setSpeed(SPEED_RESTRICT_MACH, 1.56); + + // upgrade to a hold and set the count + f->legAtIndex(2)->setHoldCount(8); + + // round trip through XML to check :) + std::ostringstream ss; + f->save(ss); + + std::istringstream iss(ss.str()); + FlightPlanRef f2 = new FlightPlan; + ok = f2->load(iss); + CPPUNIT_ASSERT(ok); + + auto leg3 = f2->legAtIndex(0); + CPPUNIT_ASSERT_EQUAL(SPEED_RESTRICT_MACH, leg3->speedRestriction()); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.56, leg3->speedMach(), 0.01); + + CPPUNIT_ASSERT_EQUAL(8, f2->legAtIndex(2)->holdCount()); +} diff --git a/test_suite/unit_tests/Navaids/test_flightplan.hxx b/test_suite/unit_tests/Navaids/test_flightplan.hxx index a52df3fb8..1be0b5209 100644 --- a/test_suite/unit_tests/Navaids/test_flightplan.hxx +++ b/test_suite/unit_tests/Navaids/test_flightplan.hxx @@ -40,6 +40,7 @@ class FlightplanTests : public CppUnit::TestFixture CPPUNIT_TEST(testBug1814); CPPUNIT_TEST(testRoutPathWpt0Midflight); CPPUNIT_TEST(testRoutePathVec); + CPPUNIT_TEST(testLoadSaveMachRestriction); // CPPUNIT_TEST(testParseICAORoute); // CPPUNIT_TEST(testParseICANLowLevelRoute); @@ -64,6 +65,7 @@ public: void testBug1814(); void testRoutPathWpt0Midflight(); void testRoutePathVec(); + void testLoadSaveMachRestriction(); }; #endif // FG_FLIGHTPLAN_UNIT_TESTS_HXX diff --git a/test_suite/unit_tests/Navaids/test_fpNasal.cxx b/test_suite/unit_tests/Navaids/test_fpNasal.cxx new file mode 100644 index 000000000..bd702e7de --- /dev/null +++ b/test_suite/unit_tests/Navaids/test_fpNasal.cxx @@ -0,0 +1,105 @@ +#include "test_fpNasal.hxx" + +#include "test_suite/FGTestApi/testGlobals.hxx" +#include "test_suite/FGTestApi/NavDataCache.hxx" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace flightgear; + + +// Set up function for each test. +void FPNasalTests::setUp() +{ + FGTestApi::setUp::initTestGlobals("flightplan"); + FGTestApi::setUp::initNavDataCache(); + + // flightplan() acces needs the route manager + globals->add_new_subsystem(); + + globals->get_subsystem_mgr()->bind(); + globals->get_subsystem_mgr()->init(); + + FGTestApi::setUp::initStandardNasal(); + globals->get_subsystem_mgr()->postinit(); +} + + +// Clean up after each test. +void FPNasalTests::tearDown() +{ + FGTestApi::tearDown::shutdownTestGlobals(); +} + +static FlightPlanRef makeTestFP(const std::string& depICAO, const std::string& depRunway, + const std::string& destICAO, const std::string& destRunway, + const std::string& waypoints) +{ + FlightPlanRef f = new FlightPlan; + FGTestApi::setUp::populateFPWithNasal(f, depICAO, depRunway, destICAO, destRunway, waypoints); + return f; +} + +void FPNasalTests::testBasic() +{ + + FlightPlanRef fp1 = makeTestFP("EGCC", "23L", "EHAM", "24", + "TNT CLN"); + fp1->setIdent("testplan"); + +// setup the FP on the route-manager, so flightplan() call works + auto rm = globals->get_subsystem(); + rm->setFlightPlan(fp1); + rm->activate(); + + // modify leg data dfrom Nasal + bool ok = FGTestApi::executeNasal(R"( + var fp = flightplan(); # retrieve the global flightplan + var leg = fp.getWP(3); + leg.setAltitude(6000, 'AT'); + )"); + CPPUNIT_ASSERT(ok); + + // check the value updated in the leg + CPPUNIT_ASSERT_EQUAL(RESTRICT_AT, fp1->legAtIndex(3)->altitudeRestriction()); + CPPUNIT_ASSERT_EQUAL(6000, fp1->legAtIndex(3)->altitudeFt()); + +// insert some waypoints from Nasal + + ok = FGTestApi::executeNasal(R"( + var fp = flightplan(); + var leg = fp.getWP(2); + var newWP = createWPFrom(navinfo(leg.lat, leg.lon, 'COA')[0]); + fp.insertWPAfter(newWP, 2); + )"); + CPPUNIT_ASSERT(ok); + + CPPUNIT_ASSERT_EQUAL(string{"COSTA VOR-DME"}, fp1->legAtIndex(3)->waypoint()->source()->name()); +} + + +void FPNasalTests::testSegfaultWaypointGhost() +{ + // checking for a segfault here, no segfault indicates success. A runtime error in the log is acceptable here. + bool ok = FGTestApi::executeNasal(R"( + var fp = createFlightplan(); + fp.departure = airportinfo("BIKF"); + fp.destination = airportinfo("EGLL"); + var wp = fp.getWP(1); + fp.deleteWP(1); + print(wp.wp_name); + )"); + CPPUNIT_ASSERT(ok); +} diff --git a/test_suite/unit_tests/Navaids/test_fpNasal.hxx b/test_suite/unit_tests/Navaids/test_fpNasal.hxx new file mode 100644 index 000000000..5a8bddc6c --- /dev/null +++ b/test_suite/unit_tests/Navaids/test_fpNasal.hxx @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2018 Edward d'Auvergne + * + * This file is part of the program FlightGear. + * + * 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 . + */ + + +#pragma once + +#include +#include + + +// The flight plan unit tests. +class FPNasalTests : public CppUnit::TestFixture +{ + // Set up the test suite. + CPPUNIT_TEST_SUITE(FPNasalTests); + CPPUNIT_TEST(testBasic); + CPPUNIT_TEST(testSegfaultWaypointGhost); + CPPUNIT_TEST_SUITE_END(); + +public: + // Set up function for each test. + void setUp(); + + // Clean up after each test. + void tearDown(); + + // The tests. + void testBasic(); + void testSegfaultWaypointGhost(); +};