mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-13 09:35:23 -04:00
Proper fix of UnsafeString on windows platforms - should fix input text and block info string.
This commit is contained in:
parent
f33428a531
commit
c163ebcb59
@ -28,7 +28,7 @@ namespace ClassicalSharp {
|
|||||||
int rows;
|
int rows;
|
||||||
int startX, startY;
|
int startX, startY;
|
||||||
readonly Font font;
|
readonly Font font;
|
||||||
UnsafeString buffer = new UnsafeString( 96 );
|
StringBuffer buffer = new StringBuffer( 96 );
|
||||||
|
|
||||||
public override void Render( double delta ) {
|
public override void Render( double delta ) {
|
||||||
graphicsApi.Texturing = true;
|
graphicsApi.Texturing = true;
|
||||||
@ -130,9 +130,7 @@ namespace ClassicalSharp {
|
|||||||
|
|
||||||
Block block = blocksTable[selectedIndex].BlockId;
|
Block block = blocksTable[selectedIndex].BlockId;
|
||||||
UpdateBlockInfoString( block );
|
UpdateBlockInfoString( block );
|
||||||
string value = buffer.value;
|
string value = buffer.UpdateCachedString();
|
||||||
if( Utils2D.needWinXpFix )
|
|
||||||
value = value.TrimEnd( Utils2D.trimChars );
|
|
||||||
|
|
||||||
Size size = Utils2D.MeasureSize( value, font, true );
|
Size size = Utils2D.MeasureSize( value, font, true );
|
||||||
int x = startX + ( blockSize * blocksPerRow ) / 2 - size.Width / 2;
|
int x = startX + ( blockSize * blocksPerRow ) / 2 - size.Width / 2;
|
||||||
|
@ -6,10 +6,10 @@ namespace ClassicalSharp {
|
|||||||
public class FpsScreen : Screen {
|
public class FpsScreen : Screen {
|
||||||
|
|
||||||
readonly Font font;
|
readonly Font font;
|
||||||
UnsafeString text;
|
StringBuffer text;
|
||||||
public FpsScreen( Game window ) : base( window ) {
|
public FpsScreen( Game window ) : base( window ) {
|
||||||
font = new Font( "Arial", 13 );
|
font = new Font( "Arial", 13 );
|
||||||
text = new UnsafeString( 96 );
|
text = new StringBuffer( 96 );
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWidget fpsTextWidget;
|
TextWidget fpsTextWidget;
|
||||||
@ -37,10 +37,8 @@ namespace ClassicalSharp {
|
|||||||
.Append( ref ptr2, "), chunks/s: " ).AppendNum( ref ptr2, game.ChunkUpdates )
|
.Append( ref ptr2, "), chunks/s: " ).AppendNum( ref ptr2, game.ChunkUpdates )
|
||||||
.Append( ref ptr2, ", vertices: " ).AppendNum( ref ptr2, game.Vertices );
|
.Append( ref ptr2, ", vertices: " ).AppendNum( ref ptr2, game.Vertices );
|
||||||
}
|
}
|
||||||
string textString = text.value;
|
|
||||||
if( Utils2D.needWinXpFix )
|
|
||||||
textString = textString.TrimEnd( Utils2D.trimChars );
|
|
||||||
|
|
||||||
|
string textString = text.UpdateCachedString();
|
||||||
fpsTextWidget.SetText( textString );
|
fpsTextWidget.SetText( textString );
|
||||||
maxDelta = 0;
|
maxDelta = 0;
|
||||||
accumulator = 0;
|
accumulator = 0;
|
||||||
|
@ -12,8 +12,6 @@ namespace ClassicalSharp {
|
|||||||
static Bitmap measuringBmp;
|
static Bitmap measuringBmp;
|
||||||
static Graphics measuringGraphics;
|
static Graphics measuringGraphics;
|
||||||
static Dictionary<int, SolidBrush> brushCache = new Dictionary<int, SolidBrush>( 16 );
|
static Dictionary<int, SolidBrush> brushCache = new Dictionary<int, SolidBrush>( 16 );
|
||||||
internal static bool needWinXpFix;
|
|
||||||
internal static char[] trimChars = { '\0' };
|
|
||||||
|
|
||||||
static Utils2D() {
|
static Utils2D() {
|
||||||
format = StringFormat.GenericTypographic;
|
format = StringFormat.GenericTypographic;
|
||||||
@ -24,8 +22,6 @@ namespace ClassicalSharp {
|
|||||||
measuringBmp = new Bitmap( 1, 1 );
|
measuringBmp = new Bitmap( 1, 1 );
|
||||||
measuringGraphics = Graphics.FromImage( measuringBmp );
|
measuringGraphics = Graphics.FromImage( measuringBmp );
|
||||||
measuringGraphics.TextRenderingHint = TextRenderingHint.AntiAlias;
|
measuringGraphics.TextRenderingHint = TextRenderingHint.AntiAlias;
|
||||||
OperatingSystem os = Environment.OSVersion;
|
|
||||||
needWinXpFix = os.Platform == PlatformID.Win32NT && os.Version.Major < 6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static SolidBrush GetOrCreateBrush( Color color ) {
|
static SolidBrush GetOrCreateBrush( Color color ) {
|
||||||
|
@ -19,7 +19,7 @@ namespace ClassicalSharp {
|
|||||||
typingLogPos = game.ChatInputLog.Count; // Index of newest entry + 1.
|
typingLogPos = game.ChatInputLog.Count; // Index of newest entry + 1.
|
||||||
this.font = font;
|
this.font = font;
|
||||||
this.boldFont = boldFont;
|
this.boldFont = boldFont;
|
||||||
chatInputText = new UnsafeString( 64 );
|
chatInputText = new StringBuffer( 64 );
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture chatInputTexture, chatCaretTexture;
|
Texture chatInputTexture, chatCaretTexture;
|
||||||
@ -27,7 +27,7 @@ namespace ClassicalSharp {
|
|||||||
int caretPos = -1;
|
int caretPos = -1;
|
||||||
int typingLogPos = 0;
|
int typingLogPos = 0;
|
||||||
public int ChatInputYOffset;
|
public int ChatInputYOffset;
|
||||||
internal UnsafeString chatInputText;
|
internal StringBuffer chatInputText;
|
||||||
readonly Font font, boldFont;
|
readonly Font font, boldFont;
|
||||||
|
|
||||||
public override void Render( double delta ) {
|
public override void Render( double delta ) {
|
||||||
@ -40,9 +40,7 @@ namespace ClassicalSharp {
|
|||||||
X = 10;
|
X = 10;
|
||||||
DrawTextArgs caretArgs = new DrawTextArgs( graphicsApi, "_", Color.White, false );
|
DrawTextArgs caretArgs = new DrawTextArgs( graphicsApi, "_", Color.White, false );
|
||||||
chatCaretTexture = Utils2D.MakeTextTexture( boldFont, 0, 0, ref caretArgs );
|
chatCaretTexture = Utils2D.MakeTextTexture( boldFont, 0, 0, ref caretArgs );
|
||||||
string value = chatInputText.value;
|
string value = chatInputText.UpdateCachedString();
|
||||||
if( Utils2D.needWinXpFix )
|
|
||||||
value = value.TrimEnd( Utils2D.trimChars );
|
|
||||||
|
|
||||||
if( chatInputText.Empty ) {
|
if( chatInputText.Empty ) {
|
||||||
caretPos = -1;
|
caretPos = -1;
|
||||||
@ -53,9 +51,12 @@ namespace ClassicalSharp {
|
|||||||
chatCaretTexture.X1 = 10 + size.Width;
|
chatCaretTexture.X1 = 10 + size.Width;
|
||||||
size.Width += chatCaretTexture.Width;
|
size.Width += chatCaretTexture.Width;
|
||||||
} else {
|
} else {
|
||||||
Size trimmedSize = Utils2D.MeasureSize( value.Substring( 0, caretPos ), font, false );
|
string subString = chatInputText.GetSubstring( caretPos );
|
||||||
|
Size trimmedSize = Utils2D.MeasureSize( subString, font, false );
|
||||||
|
chatInputText.RestoreLength();
|
||||||
|
|
||||||
chatCaretTexture.X1 = 10 + trimmedSize.Width;
|
chatCaretTexture.X1 = 10 + trimmedSize.Width;
|
||||||
Size charSize = Utils2D.MeasureSize( value.Substring( caretPos, 1 ), font, false );
|
Size charSize = Utils2D.MeasureSize( new String( value[caretPos], 1 ), font, false );
|
||||||
chatCaretTexture.Width = charSize.Width;
|
chatCaretTexture.Width = charSize.Width;
|
||||||
}
|
}
|
||||||
size.Height = Math.Max( size.Height, chatCaretTexture.Height );
|
size.Height = Math.Max( size.Height, chatCaretTexture.Height );
|
||||||
|
@ -164,7 +164,7 @@
|
|||||||
<Compile Include="Utils\TerrainAtlas1D.cs" />
|
<Compile Include="Utils\TerrainAtlas1D.cs" />
|
||||||
<Compile Include="Utils\TerrainAtlas2D.cs" />
|
<Compile Include="Utils\TerrainAtlas2D.cs" />
|
||||||
<Compile Include="Utils\TextureRectangle.cs" />
|
<Compile Include="Utils\TextureRectangle.cs" />
|
||||||
<Compile Include="Utils\UnsafeString.cs" />
|
<Compile Include="Utils\StringBuffer.cs" />
|
||||||
<Compile Include="Utils\Utils.cs" />
|
<Compile Include="Utils\Utils.cs" />
|
||||||
<Compile Include="Utils\Vector3I.cs" />
|
<Compile Include="Utils\Vector3I.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
180
ClassicalSharp/Utils/StringBuffer.cs
Normal file
180
ClassicalSharp/Utils/StringBuffer.cs
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace ClassicalSharp {
|
||||||
|
|
||||||
|
// Class used to minimise memory allocations of strings.
|
||||||
|
// Really, only useful for FpsScreen and TextInputWidget as they allocate lots of very small strings.
|
||||||
|
// Seriously, you should *not* use this anywhere else, as Empty and Length are O(N).
|
||||||
|
internal sealed unsafe class StringBuffer {
|
||||||
|
|
||||||
|
internal string value;
|
||||||
|
internal int capacity;
|
||||||
|
|
||||||
|
static readonly FieldInfo arrayField, lengthField;
|
||||||
|
static bool supportsLengthSetting;
|
||||||
|
|
||||||
|
static StringBuffer() {
|
||||||
|
if( OpenTK.Configuration.RunningOnMono )
|
||||||
|
return;
|
||||||
|
|
||||||
|
arrayField = typeof( String ).GetField( "m_arrayLength", BindingFlags.NonPublic | BindingFlags.Instance );
|
||||||
|
lengthField = typeof( String ).GetField( "m_stringLength", BindingFlags.NonPublic | BindingFlags.Instance );
|
||||||
|
|
||||||
|
// Make sure we are running on a framework that has both methods -
|
||||||
|
// otherwise we play it safe and just use TrimEnd().
|
||||||
|
if( arrayField != null && lengthField != null ) {
|
||||||
|
supportsLengthSetting = true;
|
||||||
|
} else {
|
||||||
|
arrayField = null;
|
||||||
|
lengthField = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringBuffer( int capacity ) {
|
||||||
|
this.capacity = capacity;
|
||||||
|
value = new String( '\0', capacity );
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringBuffer Append( int index, char c ) {
|
||||||
|
fixed( char* ptr = value ) {
|
||||||
|
ptr[index] = c;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringBuffer Append( int index, string s ) {
|
||||||
|
fixed( char* ptr = value ) {
|
||||||
|
char* offsetPtr = ptr + index;
|
||||||
|
return Append( ref offsetPtr, s );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringBuffer Append( ref char* ptr, string s ) {
|
||||||
|
for( int i = 0; i < s.Length; i++ ) {
|
||||||
|
*ptr++ = s[i];
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringBuffer Append( ref char* ptr, char c) {
|
||||||
|
*ptr++ = c;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char[] numBuffer = new char[20];
|
||||||
|
public StringBuffer AppendNum( ref char* ptr, long num ) {
|
||||||
|
int index = 0;
|
||||||
|
numBuffer[index++] = (char)( '0' + ( num % 10 ) );
|
||||||
|
num /= 10;
|
||||||
|
|
||||||
|
while( num > 0 ) {
|
||||||
|
numBuffer[index++] = (char)( '0' + ( num % 10 ) );
|
||||||
|
num /= 10;
|
||||||
|
}
|
||||||
|
for( int i = index - 1; i >= 0; i-- ) {
|
||||||
|
*ptr++ = numBuffer[i];
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringBuffer Clear() {
|
||||||
|
fixed( char* ptr = value ) {
|
||||||
|
return Clear( ptr );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringBuffer Clear( char* ptr ) {
|
||||||
|
for( int i = 0; i < capacity; i++ ) {
|
||||||
|
*ptr++ = '\0';
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteAt( int index ) {
|
||||||
|
fixed( char* ptr = value ) {
|
||||||
|
char* offsetPtr = ptr + index;
|
||||||
|
for( int i = index; i < capacity - 1; i++ ) {
|
||||||
|
*offsetPtr = *( offsetPtr + 1 );
|
||||||
|
offsetPtr++;
|
||||||
|
}
|
||||||
|
*offsetPtr = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InsertAt( int index, char c ) {
|
||||||
|
fixed( char* ptr = value ) {
|
||||||
|
char* offsetPtr = ptr + capacity - 1;
|
||||||
|
for( int i = capacity - 1; i > index; i-- ) {
|
||||||
|
*offsetPtr = *( offsetPtr - 1 );
|
||||||
|
offsetPtr--;
|
||||||
|
}
|
||||||
|
*offsetPtr = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public bool Empty {
|
||||||
|
get {
|
||||||
|
fixed( char* ptr = value ) {
|
||||||
|
for( int i = 0; i < capacity; i++ ) {
|
||||||
|
if( ptr[i] != '\0' )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Length {
|
||||||
|
get {
|
||||||
|
int len = capacity;
|
||||||
|
fixed( char* ptr = value ) {
|
||||||
|
for( int i = capacity - 1; i >= 0; i-- ) {
|
||||||
|
if( ptr[i] != '\0' )
|
||||||
|
break;
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Hack that modifies the underlying string's length to avoid memory allocations. </summary>
|
||||||
|
/// <returns> The underlying string - ***do not*** store this because it is mutable!
|
||||||
|
/// You should only use this string for temporary measuring and drawing. </returns>
|
||||||
|
public string UpdateCachedString() {
|
||||||
|
return supportsLengthSetting ? value : value.TrimEnd( trimEnd );
|
||||||
|
}
|
||||||
|
static char[] trimEnd = { '\0' };
|
||||||
|
|
||||||
|
public override string ToString() {
|
||||||
|
return GetCopy( Length );
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetSubstring( int length ) {
|
||||||
|
return supportsLengthSetting ? SetLength( length ) : GetCopy( length );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RestoreLength() {
|
||||||
|
if( supportsLengthSetting )
|
||||||
|
SetLength( Length );
|
||||||
|
}
|
||||||
|
|
||||||
|
string SetLength( int len ) {
|
||||||
|
arrayField.SetValue( value, len + 1 );
|
||||||
|
lengthField.SetValue( value, len );
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
string GetCopy( int len ) {
|
||||||
|
string copiedString = new String( '\0', len );
|
||||||
|
fixed( char* src = value, dst = copiedString ) {
|
||||||
|
for( int i = 0; i < len; i++ ) {
|
||||||
|
dst[i] = src[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return copiedString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,122 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace ClassicalSharp {
|
|
||||||
|
|
||||||
// Class used to minimise memory allocations of strings.
|
|
||||||
// Really, only useful for FpsScreen and TextInputWidget as they allocate lots of very small strings.
|
|
||||||
// Seriously, you should *not* use this anywhere else, as Empty and Length are O(N).
|
|
||||||
internal sealed unsafe class UnsafeString {
|
|
||||||
|
|
||||||
internal string value;
|
|
||||||
internal int capacity;
|
|
||||||
|
|
||||||
public UnsafeString( int capacity ) {
|
|
||||||
this.capacity = capacity;
|
|
||||||
value = new String( '\0', capacity );
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnsafeString Append( int index, char c ) {
|
|
||||||
fixed( char* ptr = value ) {
|
|
||||||
ptr[index] = c;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnsafeString Append( int index, string s ) {
|
|
||||||
fixed( char* ptr = value ) {
|
|
||||||
char* offsetPtr = ptr + index;
|
|
||||||
return Append( ref offsetPtr, s );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnsafeString Append( ref char* ptr, string s ) {
|
|
||||||
for( int i = 0; i < s.Length; i++ ) {
|
|
||||||
*ptr++ = s[i];
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnsafeString Append( ref char* ptr, char c) {
|
|
||||||
*ptr++ = c;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char[] numBuffer = new char[20];
|
|
||||||
public UnsafeString AppendNum( ref char* ptr, long num ) {
|
|
||||||
int index = 0;
|
|
||||||
numBuffer[index++] = (char)( '0' + ( num % 10 ) );
|
|
||||||
num /= 10;
|
|
||||||
|
|
||||||
while( num > 0 ) {
|
|
||||||
numBuffer[index++] = (char)( '0' + ( num % 10 ) );
|
|
||||||
num /= 10;
|
|
||||||
}
|
|
||||||
for( int i = index - 1; i >= 0; i-- ) {
|
|
||||||
*ptr++ = numBuffer[i];
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnsafeString Clear() {
|
|
||||||
fixed( char* ptr = value ) {
|
|
||||||
return Clear( ptr );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnsafeString Clear( char* ptr ) {
|
|
||||||
for( int i = 0; i < capacity; i++ ) {
|
|
||||||
*ptr++ = '\0';
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DeleteAt( int index ) {
|
|
||||||
fixed( char* ptr = value ) {
|
|
||||||
char* offsetPtr = ptr + index;
|
|
||||||
for( int i = index; i < capacity - 1; i++ ) {
|
|
||||||
*offsetPtr = *( offsetPtr + 1 );
|
|
||||||
offsetPtr++;
|
|
||||||
}
|
|
||||||
*offsetPtr = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InsertAt( int index, char c ) {
|
|
||||||
fixed( char* ptr = value ) {
|
|
||||||
char* offsetPtr = ptr + capacity - 1;
|
|
||||||
for( int i = capacity - 1; i > index; i-- ) {
|
|
||||||
*offsetPtr = *( offsetPtr - 1 );
|
|
||||||
offsetPtr--;
|
|
||||||
}
|
|
||||||
*offsetPtr = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public bool Empty {
|
|
||||||
get {
|
|
||||||
for( int i = 0; i < capacity; i++ ) {
|
|
||||||
if( value[i] != '\0' )
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Length {
|
|
||||||
get {
|
|
||||||
int len = capacity;
|
|
||||||
for( int i = capacity - 1; i >= 0; i-- ) {
|
|
||||||
if( value[i] != '\0' )
|
|
||||||
break;
|
|
||||||
len--;
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString() {
|
|
||||||
return value.TrimEnd( '\0' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -113,7 +113,7 @@ namespace SharpDX.Direct3D9 {
|
|||||||
return lockedRect;
|
return lockedRect;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetData( int level, LockFlags flags, IntPtr data,int bytes ) {
|
public void SetData( int level, LockFlags flags, IntPtr data, int bytes ) {
|
||||||
LockedRectangle rect = LockRectangle( level, flags );
|
LockedRectangle rect = LockRectangle( level, flags );
|
||||||
MemUtils.memcpy( data, rect.DataPointer, bytes );
|
MemUtils.memcpy( data, rect.DataPointer, bytes );
|
||||||
UnlockRectangle( level );
|
UnlockRectangle( level );
|
||||||
|
Loading…
x
Reference in New Issue
Block a user