diff --git a/include/lgi/common/Variant.h b/include/lgi/common/Variant.h
--- a/include/lgi/common/Variant.h
+++ b/include/lgi/common/Variant.h
@@ -1,473 +1,474 @@
/**
\file
\author Matthew Allen
\brief Variant class.\n
Copyright (C), Matthew Allen
*/
#ifndef __LVariant_H__
#define __LVariant_H__
#undef Bool
#include "lgi/common/DateTime.h"
#include "lgi/common/Containers.h"
#include "lgi/common/HashTable.h"
#include "lgi/common/LgiString.h"
class LCompiledCode;
#if !defined(_MSC_VER) && !defined(LINUX) && (defined(LGI_64BIT) || defined(MAC)) && !defined(HAIKU)
#define LVARIANT_SIZET 1
#define LVARIANT_SSIZET 1
#endif
/// The different types the varient can be.
/// \sa LVariant::TypeToString to convert to string.
enum LVariantType
{
// Main types
/// Null type
GV_NULL,
/// 32-bit integer
GV_INT32,
/// 64-bit integer
GV_INT64,
/// true or false boolean.
GV_BOOL,
/// C++ double
GV_DOUBLE,
/// Null terminated string value
GV_STRING,
/// Block of binary data
GV_BINARY,
/// List of LVariant
GV_LIST,
/// Pointer to LDom object
GV_DOM,
/// DOM reference, ie. a variable in a DOM object
GV_DOMREF,
/// Untyped pointer
GV_VOID_PTR,
/// LDateTime class.
GV_DATETIME,
/// Hash table class, containing pointers to LVariants
GV_HASHTABLE,
// Scripting language operator
GV_OPERATOR,
// Custom scripting lang type
GV_CUSTOM,
// Wide string
GV_WSTRING,
// LSurface ptr
GV_LSURFACE,
/// Pointer to LView
GV_GVIEW,
/// Pointer to LMouse
GV_LMOUSE,
/// Pointer to LKey
GV_LKEY,
/// Pointer to LStream
GV_STREAM,
/// The maximum value for the variant type.
/// (This is used by the scripting engine to refer to a LVariant itself)
GV_MAX,
};
/// Language operators
enum LOperator
{
OpNull,
OpAssign,
OpPlus,
OpUnaryPlus,
OpMinus,
OpUnaryMinus,
OpMul,
OpDiv,
OpMod,
OpLessThan,
OpLessThanEqual,
OpGreaterThan,
OpGreaterThanEqual,
OpEquals,
OpNotEquals,
OpPlusEquals,
OpMinusEquals,
OpMulEquals,
OpDivEquals,
OpPostInc,
OpPostDec,
OpPreInc,
OpPreDec,
OpAnd,
OpOr,
OpNot,
};
class LgiClass LCustomType : public LDom
{
protected:
struct CustomField : public LDom
{
ssize_t Offset;
ssize_t Bytes;
ssize_t ArrayLen;
LVariantType Type;
LString Name;
LCustomType *Nested;
ssize_t Sizeof();
bool GetVariant(const char *Name, LVariant &Value, const char *Array = NULL);
};
public:
struct Method : public LDom
{
LString Name;
LArray Params;
size_t Address;
int FrameSize;
Method()
{
Address = -1;
FrameSize = -1;
}
};
protected:
// Global vars
int Pack;
size_t Size;
LString Name;
// Fields
LArray Flds;
LHashTbl, int> FldMap;
// Methods
LArray Methods;
LHashTbl, Method*> MethodMap;
// Private methods
ssize_t PadSize();
public:
LCustomType(const char *name, int pack = 1);
LCustomType(const char16 *name, int pack = 1);
~LCustomType();
size_t Sizeof();
const char *GetName() { return Name; }
ssize_t Members() { return Flds.Length(); }
int AddressOf(const char *Field);
int IndexOf(const char *Field);
bool DefineField(const char *Name, LVariantType Type, int Bytes, int ArrayLen = 1);
bool DefineField(const char *Name, LCustomType *Type, int ArrayLen = 1);
Method *DefineMethod(const char *Name, LArray &Params, size_t Address);
Method *GetMethod(const char *Name);
// Field access. You can't use the LDom interface to get/set member variables because
// there is no provision for the 'This' pointer.
bool Get(int Index, LVariant &Out, uint8_t *This, int ArrayIndex = 0);
bool Set(int Index, LVariant &In, uint8_t *This, int ArrayIndex = 0);
// Dom access. However the DOM can be used to access information about the type itself.
// Which doesn't need a 'This' pointer.
bool GetVariant(const char *Name, LVariant &Value, const char *Array = NULL);
bool SetVariant(const char *Name, LVariant &Value, const char *Array = NULL);
bool CallMethod(const char *MethodName, LScriptArguments &Args);
};
/// A class that can be different types
class LgiClass LVariant
{
public:
typedef LHashTbl,LVariant*> LHash;
/// The type of the variant
LVariantType Type;
/// The value of the variant
union
{
/// Valid when Type == #GV_INT32
int Int;
/// Valid when Type == #GV_BOOL
bool Bool;
/// Valid when Type == #GV_INT64
int64 Int64;
/// Valid when Type == #GV_DOUBLE
double Dbl;
/// Valid when Type == #GV_STRING
char *String;
/// Valid when Type == #GV_WSTRING
char16 *WString;
/// Valid when Type == #GV_DOM
LDom *Dom;
/// Valid when Type is #GV_VOID_PTR, #GV_GVIEW, #GV_LMOUSE or #GV_LKEY
void *Ptr;
/// Valid when Type == #GV_BINARY
struct _Binary
{
ssize_t Length;
void *Data;
} Binary;
/// Valid when Type == #GV_LIST
List *Lst;
/// Valid when Type == #GV_HASHTABLE
LHash *Hash;
/// Valid when Type == #GV_DATETIME
LDateTime *Date;
/// Valid when Type == #GV_CUSTOM
struct _Custom
{
LCustomType *Dom;
uint8_t *Data;
bool operator == (_Custom &c)
{
return Dom == c.Dom &&
Data == c.Data;
}
} Custom;
/// Valid when Type == #GV_DOMREF
struct _DomRef
{
/// The pointer to the dom object
LDom *Dom;
/// The name of the variable to set/get in the dom object
char *Name;
} DomRef;
/// Valid when Type == #GV_OPERATOR
LOperator Op;
/// Valid when Type == #GV_LSURFACE
struct
{
class LSurface *Ptr;
bool Own;
LSurface *Release()
{
auto p = Ptr;
Ptr = NULL;
Own = false;
return p;
}
} Surface;
/// Valid when Type == #GV_STREAM
struct
{
class LStreamI *Ptr;
bool Own;
LStreamI *Release()
{
auto p = Ptr;
Ptr = NULL;
Own = false;
return p;
}
} Stream;
/// Valid when Type == #GV_GVIEW
class LView *View;
/// Valid when Type == #GV_LMOUSE
class LMouse *Mouse;
/// Valid when Type == #GV_LKEY
class LKey *Key;
} Value;
/// Constructor to null
LVariant();
/// Constructor for integers
LVariant(int32_t i);
LVariant(uint32_t i);
LVariant(int64_t i);
LVariant(uint64_t i);
#if LVARIANT_SIZET
LVariant(size_t i);
#endif
#if LVARIANT_SSIZET
LVariant(ssize_t i);
#endif
/// Constructor for double
LVariant(double i);
/// Constructor for string
LVariant(const char *s);
/// Constructor for wide string
LVariant(const char16 *s);
/// Constructor for ptr
LVariant(void *p);
/// Constructor for DOM ptr
LVariant(LDom *p);
/// Constructor for DOM variable reference
LVariant(LDom *p, char *name);
/// Constructor for date
LVariant(const LDateTime *d);
/// Constructor for variant
LVariant(LVariant const &v);
/// Constructor for operator
LVariant(LOperator Op);
/// Destructor
~LVariant();
/// Assign bool value
LVariant &operator =(bool i);
/// Assign an integer value
LVariant &operator =(int32_t i);
LVariant &operator =(uint32_t i);
LVariant &operator =(int64_t i);
LVariant &operator =(uint64_t i);
#if LVARIANT_SIZET
LVariant &operator =(size_t i);
#endif
#if LVARIANT_SSIZET
LVariant &operator =(ssize_t i);
#endif
/// Assign double value
LVariant &operator =(double i);
/// Assign string value (makes a copy)
LVariant &operator =(const char *s);
/// Assign a wide string value (makes a copy)
LVariant &operator =(const char16 *s);
/// Assign another variant value
LVariant &operator =(LVariant const &i);
/// Assign value to a void ptr
LVariant &operator =(void *p);
/// Assign value to DOM ptr
LVariant &operator =(LDom *p);
/// Assign value to be a date/time
LVariant &operator =(const LDateTime *d);
LVariant &operator =(class LView *p);
LVariant &operator =(class LMouse *p);
LVariant &operator =(class LKey *k);
LVariant &operator =(class LStream *s);
bool operator ==(LVariant &v);
bool operator !=(LVariant &v) { return !(*this == v); }
/// Sets the value to a DOM variable reference
bool SetDomRef(LDom *obj, char *name);
/// Sets the value to a copy of block of binary data
bool SetBinary(ssize_t Len, void *Data, bool Own = false);
/// Sets the value to a copy of the list
List *SetList(List *Lst = NULL);
/// Sets the value to a hashtable
bool SetHashTable(LHash *Table = NULL, bool Copy = true);
/// Set the value to a surface
bool SetSurface(class LSurface *Ptr, bool Own);
/// Set the value to a stream
bool SetStream(class LStreamI *Ptr, bool Own);
/// Returns the string if valid (will convert a GV_WSTRING to utf)
char *Str();
/// Returns the value as an LString
LString LStr();
/// Returns a wide string if valid (will convert a GV_STRING to wide)
char16 *WStr();
/// Returns the string, releasing ownership of the memory to caller and
/// changing this LVariant to GV_NULL.
char *ReleaseStr();
/// Returns the wide string, releasing ownership of the memory to caller and
/// changing this LVariant to GV_NULL.
char16 *ReleaseWStr();
/// Sets the variant to a heap string and takes ownership of it
bool OwnStr(char *s);
/// Sets the variant to a wide heap string and takes ownership of it
bool OwnStr(char16 *s);
/// Sets the variant to NULL
void Empty();
/// Returns the byte length of the data
int64 Length();
/// True if currently a int
bool IsInt();
/// True if currently a bool
bool IsBool();
/// True if currently a double
bool IsDouble();
/// True if currently a string
bool IsString();
/// True if currently a binary block
bool IsBinary();
/// True if currently null
bool IsNull();
/// Changes the variant's type, maintaining the value where possible. If
/// no conversion is available then nothing happens.
LVariant &Cast(LVariantType NewType);
/// Casts the value to int, from whatever source type. The
/// LVariant type does not change after calling this.
int32 CastInt32() const;
/// Casts the value to a 64 bit int, from whatever source type. The
/// LVariant type does not change after calling this.
int64 CastInt64() const;
/// Casts the value to double, from whatever source type. The
/// LVariant type does not change after calling this.
double CastDouble() const;
/// Cast to a string from whatever source type, the LVariant will
/// take the type GV_STRING after calling this. This is because
/// returning a static string is not thread safe.
char *CastString();
/// Casts to a DOM ptr
LDom *CastDom() const;
/// Casts to a boolean. You probably DON'T want to use this function. The
/// behaviour for strings -> bool is such that if the string is value it
/// always evaluates to true, and false if it's not a valid string. Commonly
/// what you want is to evaluate whether the string is zero or non-zero in
/// which cast you should use "CastInt32() != 0" instead.
bool CastBool() const;
/// Returns the pointer if available.
void *CastVoidPtr() const;
/// Returns a LView
LView *CastView() const { return Type == GV_GVIEW ? Value.View : NULL; }
/// List insert
bool Add(LVariant *v, int Where = -1);
/// Converts the varient type to a string
static const char *TypeToString(LVariantType t);
/// Converts an operator to a string
static const char *OperatorToString(LOperator op);
/// Converts the value to a string description include type.
LString ToString();
};
// General collection of arguments and a return value
class LgiClass LScriptArguments : public LArray
{
friend class LScriptEngine;
friend class LVirtualMachine;
friend class LVirtualMachinePriv;
friend struct ExecuteFunctionState;
LVirtualMachineI *Vm = NULL;
class LStream *Console = NULL;
LVariant *LocalReturn = NULL; // Owned by this instance
LVariant *Return = NULL;
const char *File = NULL;
int Line = 0;
LString ExceptionMsg;
+ ssize_t Address;
public:
static LStream NullConsole;
- LScriptArguments(LVirtualMachineI *vm, LVariant *ret = NULL, LStream *console = NULL);
+ LScriptArguments(LVirtualMachineI *vm, LVariant *ret = NULL, LStream *console = NULL, ssize_t address = -1);
~LScriptArguments();
LVirtualMachineI *GetVm() { return Vm; }
void SetVm(LVirtualMachineI *vm) { Vm = vm; }
LVariant *GetReturn() { return Return; } // Must never be NULL.
LStream *GetConsole() { return Console; }
bool HasException() { return File != NULL || ExceptionMsg.Get() || Line != 0; }
bool Throw(const char *File, int Line, const char *Msg, ...);
// Accessor shortcuts
const char *StringAt(size_t i);
int32_t Int32At(size_t i, int32_t Default = 0);
int64_t Int64At(size_t i, int64_t Default = 0);
double DoubleAt(size_t i, double Default = 0);
};
#endif
diff --git a/src/common/Coding/Instructions.h b/src/common/Coding/Instructions.h
--- a/src/common/Coding/Instructions.h
+++ b/src/common/Coding/Instructions.h
@@ -1,2045 +1,2045 @@
/*
This file is included in both the LVirtualMachinePriv::Execute and LVirtualMachinePriv::Decompile
That way the "parsing" of instructions is the same.
During decompile the define VM_DECOMP is active.
During execution the define VM_EXECUTE is active.
*/
#ifdef VM_EXECUTE
#define Resolve() &Scope[c.r->Scope][c.r->Index]; c.r++
#define LResolveRef(nm) LVariant *nm =
#else
#define Resolve() c.r++
#define LResolveRef(nm)
// LVarRef *
#endif
default:
{
#if VM_DECOMP
if (Log)
Log->Print("\t%p Unknown instruction %i (0x%x)\n",
CurrentScriptAddress - 1,
c.u8[-1], c.u8[-1]);
#endif
OnException(_FL, CurrentScriptAddress, "Unknown instruction");
SetScriptError;
break;
}
case INop:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Nop\n", CurrentScriptAddress - 1);
#endif
break;
}
case ICast:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Cast %s",
CurrentScriptAddress - 1,
c.r[0].GetStr());
#endif
LResolveRef(Var) Resolve();
uint8_t Type = *c.u8++;
#if VM_DECOMP
if (Log)
Log->Print(" to %s\n", LVariant::TypeToString((LVariantType)Type));
#endif
#if VM_EXECUTE
switch (Type)
{
case GV_INT32:
{
*Var = Var->CastInt32();
break;
}
case GV_STRING:
{
*Var = Var->CastString();
break;
}
case GV_DOM:
{
*Var = Var->CastDom();
break;
}
case GV_DOUBLE:
{
*Var = Var->CastDouble();
break;
}
case GV_INT64:
{
*Var = Var->CastInt32();
break;
}
case GV_BOOL:
{
*Var = Var->CastBool();
break;
}
default:
{
LString s;
s.Printf("%s ICast warning: unknown type %i/%s\n",
Code->AddrToSourceRef(CurrentScriptAddress),
Var->Type,
LVariant::TypeToString(Var->Type));
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
break;
}
}
#endif
break;
}
case IAssign:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Assign %s <- %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
CheckParam(Dst != Src);
*Dst = *Src;
#endif
break;
}
case IJump:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Jump by %i (to 0x%x)\n",
CurrentScriptAddress - 1,
c.i32[0],
CurrentScriptAddress + 4 + c.i32[0]);
#endif
#ifdef VM_EXECUTE
int32 Jmp = *
#endif
c.i32++;
#ifdef VM_EXECUTE
CheckParam(Jmp != 0);
c.u8 += Jmp;
#endif
break;
}
case IJumpZero:
{
#if VM_DECOMP
if (Log)
Log->Print("%p JumpZ(%s) by 0x%x\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.i32[1]);
#endif
LResolveRef(Exp) Resolve();
#ifdef VM_EXECUTE
int32 Jmp = *
#endif
c.i32++;
#ifdef VM_EXECUTE
CheckParam(Jmp != 0);
if (!Exp->CastInt32())
c.u8 += Jmp;
#endif
break;
}
// case IUnaryPlus:
case IUnaryMinus:
{
#if VM_DECOMP
if (Log)
Log->Print("%p UnaryMinus %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr());
#endif
LResolveRef(Var) Resolve();
#ifdef VM_EXECUTE
switch (Var->Type)
{
case GV_DOUBLE:
*Var = -Var->CastDouble();
break;
case GV_INT64:
*Var = -Var->CastInt64();
break;
default:
*Var = -Var->CastInt32();
break;
}
#endif
break;
}
case IPlus:
case IPlusEquals:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Plus %s += %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
if (Dst->Str())
{
size_t dlen = strlen(Dst->Str());
char *ss;
LVariant SrcTmp;
switch (Src->Type)
{
case GV_NULL:
ss = (char*)"(null)";
break;
case GV_STRING:
ss = Src->Str();
break;
default:
SrcTmp = *Src;
ss = SrcTmp.CastString();
break;
}
if (ss)
{
size_t slen = strlen(ss);
char *s = new char[slen + dlen + 1];
if (s)
{
memcpy(s, Dst->Value.String, dlen);
memcpy(s + dlen, ss, slen);
s[dlen + slen] = 0;
DeleteArray(Dst->Value.String);
Dst->Value.String = s;
}
}
}
else switch (DecidePrecision(Dst->Type, Src->Type))
{
case GV_DOUBLE:
*Dst = Dst->CastDouble() + Src->CastDouble();
break;
case GV_INT64:
*Dst = Dst->CastInt64() + Src->CastInt64();
break;
default:
*Dst = Dst->CastInt32() + Src->CastInt32();
break;
}
#endif
break;
}
case IMinus:
case IMinusEquals:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Minus %s -= %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
switch (DecidePrecision(Dst->Type, Src->Type))
{
case GV_DOUBLE:
*Dst = Dst->CastDouble() - Src->CastDouble();
break;
case GV_INT64:
*Dst = Dst->CastInt64() - Src->CastInt64();
break;
default:
*Dst = Dst->CastInt32() - Src->CastInt32();
break;
}
#endif
break;
}
case IMul:
case IMulEquals:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Mul %s *= %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
switch (DecidePrecision(Dst->Type, Src->Type))
{
case GV_DOUBLE:
*Dst = Dst->CastDouble() * Src->CastDouble();
break;
case GV_INT64:
*Dst = Dst->CastInt64() * Src->CastInt64();
break;
default:
*Dst = Dst->CastInt32() * Src->CastInt32();
break;
}
#endif
break;
}
case IDiv:
case IDivEquals:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Div %s /= %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
switch (DecidePrecision(Dst->Type, Src->Type))
{
case GV_DOUBLE:
*Dst = Dst->CastDouble() / Src->CastDouble();
break;
case GV_INT64:
*Dst = Dst->CastInt64() / Src->CastInt64();
break;
default:
*Dst = Dst->CastInt32() / Src->CastInt32();
break;
}
#endif
break;
}
case IMod:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Mod %s %%= %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
switch (DecidePrecision(Dst->Type, Src->Type))
{
case GV_DOUBLE:
*Dst = fmod(Dst->CastDouble(), Src->CastDouble());
break;
case GV_INT64:
*Dst = Dst->CastInt64() % Src->CastInt64();
break;
default:
*Dst = Dst->CastInt32() % Src->CastInt32();
break;
}
#endif
break;
}
case IPostInc:
case IPreInc:
{
#if VM_DECOMP
if (Log)
Log->Print("%p PostInc %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr());
#endif
LResolveRef(v) Resolve();
#ifdef VM_EXECUTE
switch (v->Type)
{
case GV_DOUBLE:
*v = v->Value.Dbl + 1;
break;
case GV_INT64:
*v = v->Value.Int64 + 1;
break;
default:
*v = v->CastInt32() + 1;
break;
}
#endif
break;
}
case IPostDec:
case IPreDec:
{
#if VM_DECOMP
if (Log)
Log->Print("%p PostDec %sn",
CurrentScriptAddress - 1,
c.r[0].GetStr());
#endif
LResolveRef(v) Resolve();
#ifdef VM_EXECUTE
switch (v->Type)
{
case GV_DOUBLE:
*v = v->Value.Dbl - 1;
break;
case GV_INT64:
*v = v->Value.Int64 - 1;
break;
default:
*v = v->CastInt32() - 1;
break;
}
#endif
break;
}
case IEquals:
{
#if VM_DECOMP
if (Log)
Log->Print("%p %s == %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
*Dst = CompareVariants(Dst, Src) == 0;
#endif
break;
}
case INotEquals:
{
#if VM_DECOMP
if (Log)
Log->Print( "%p %s != %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
*Dst = CompareVariants(Dst, Src) != 0;
#endif
break;
}
case ILessThan:
{
#if VM_DECOMP
if (Log)
Log->Print("%p %s < %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
*Dst = CompareVariants(Dst, Src) < 0;
#endif
break;
}
case ILessThanEqual:
{
#if VM_DECOMP
if (Log)
Log->Print( "%p %s < %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
*Dst = CompareVariants(Dst, Src) <= 0;
#endif
break;
}
case IGreaterThan:
{
#if VM_DECOMP
if (Log)
Log->Print("%p %s < %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
*Dst = CompareVariants(Dst, Src) > 0;
#endif
break;
}
case IGreaterThanEqual:
{
#if VM_DECOMP
if (Log)
Log->Print("%p %s < %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
*Dst = CompareVariants(Dst, Src) >= 0;
#endif
break;
}
case ICallMethod:
{
LFunc *Meth = *c.fn++;
if (!Meth)
{
Log->Print( "%s ICallMethod error: No method struct.\n",
Code->AddrToSourceRef(CurrentScriptAddress - sizeof(Meth)));
SetScriptError;
break;
}
#ifdef VM_DECOMP
if (Log)
{
Log->Print("%p Call: %s = %s(",
CurrentScriptAddress - sizeof(Meth) - 1,
c.r[0].GetStr(),
Meth->Method.Get());
}
#endif
LResolveRef(Ret) Resolve();
uint16 Args = *c.u16++;
#ifdef VM_EXECUTE
- LScriptArguments Arg(Vm, Ret);
+ LScriptArguments Arg(Vm, Ret, NULL, CurrentScriptAddress);
#endif
for (int i=0; iPrint("%s%s", i?", ":"", c.r[0].GetStr());
#endif
#if VM_EXECUTE
Arg[i] = Resolve();
CheckParam(Arg[i] != NULL);
#else
c.r++;
#endif
}
#if VM_DECOMP
if (Log)
Log->Print(")\n");
#endif
#if VM_EXECUTE
LHostFunc *Hf = dynamic_cast(Meth);
if (Hf)
{
if (!(Hf->Context->*(Hf->Func))(Arg))
{
if (Log)
Log->Print( "%s ICallMethod error: Method '%s' failed.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
Meth->Method.Get());
SetScriptError;
}
}
else
{
// Fixme
if (!Meth->Call(NULL, Arg))
{
if (Log)
Log->Print( "%s ICallMethod error: Method '%s' failed.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
Meth->Method.Get());
SetScriptError;
}
}
- Arg.Length(0); // It doesn't own the variants, so don't delete them.
+ ON_EXCEPTION(Arg);
#endif
break;
}
case ICallScript:
{
int32 FuncAddr = *c.i32++;
if (FuncAddr < 0 || (uint32_t)FuncAddr >= Code->ByteCode.Length())
{
if (Log)
Log->Print( "%s ICallScript error: Script function call invalid addr '%p'.\n",
Code->AddrToSourceRef(CurrentScriptAddress - sizeof(FuncAddr)),
FuncAddr);
SetScriptError;
break;
}
uint16 Frame = *c.u16++;
#if VM_DECOMP
if (Log)
Log->Print("%p CallScript: %s = %p(frame=%i)(",
CurrentScriptAddress - 5,
c.r[0].GetStr(),
FuncAddr,
Frame);
#endif
#ifdef VM_EXECUTE
// Set up stack for function call
int CurFrameSize = Frames.Last().CurrentFrameSize;
StackFrame &Sf = Frames.New();
Sf.CurrentFrameSize = Frame;
Sf.PrevFrameStart = Locals.Length() ? Scope[1] - &Locals[0] : 0;
Sf.ReturnValue = *c.r++;
if (Sf.ReturnValue.Scope == SCOPE_LOCAL)
Sf.ReturnValue.Index -= CurFrameSize;
uint16 Args = *c.u16++;
// Increase the local stack size
size_t LocalsBase = Locals.Length();
size_t LocalsPos = Scope[SCOPE_LOCAL] - Locals.AddressOf();
Locals.SetFixedLength(false);
Locals.Length(LocalsBase + Frame);
Locals.SetFixedLength();
Scope[SCOPE_LOCAL] = Locals.AddressOf(LocalsPos);
// Put the arguments of the function call into the local array
LArray Arg;
#else
LResolveRef(Ret) Resolve();
int Args = *c.u16++;
#endif
for (int i=0; iPrint("%s%s", i?",":"", c.r[0].GetStr());
#endif
#if VM_EXECUTE
Locals[LocalsBase+i] = *Resolve();
#else
c.r++;
#endif
}
#if VM_EXECUTE
// Set IP to start of function
Sf.ReturnIp = CurrentScriptAddress;
c.u8 = Base + FuncAddr;
Scope[SCOPE_LOCAL] = Locals.AddressOf(LocalsBase); // This can evaluation to NULL when there is NO locals.
#endif
#if VM_DECOMP
if (Log)
Log->Print(")\n");
#endif
break;
}
case IRet:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Ret %s\n", CurrentScriptAddress - 1, c.r[0].GetStr());
#endif
LResolveRef(ReturnValue) Resolve();
#ifdef VM_EXECUTE
if (Frames.Length() > 0)
{
StackFrame Sf = Frames[Frames.Length()-1];
LVarRef &Ret = Sf.ReturnValue;
LVariant *RetVar = &Scope[Ret.Scope][Ret.Index];
// LgiTrace("IRet to %i:%i\n", Ret.Scope, Ret.Index);
if (Ret.Scope == SCOPE_LOCAL)
LAssert(Locals.PtrCheck(RetVar));
*RetVar = *ReturnValue;
CheckParam(RetVar->Type == ReturnValue->Type);
Frames.Length(Frames.Length()-1);
Locals.SetFixedLength(false);
if (Locals.Length() >= Sf.CurrentFrameSize)
{
ssize_t Base = Locals.Length() - Sf.CurrentFrameSize;
/*
if (ArgsOutput)
{
if (Frames.Length() == 0)
{
for (unsigned i=0; iLength(); i++)
{
*(*ArgsOutput)[i] = Locals[Base+i];
}
}
}
*/
// LgiTrace("%s:%i Locals %i -> %i\n", _FL, Locals.Length(), Base);
Locals.Length(Base);
Scope[SCOPE_LOCAL] = &Locals[Sf.PrevFrameStart];
}
else
{
// LgiTrace("%s:%i - Locals %i -> %i\n", _FL, Locals.Length(), 0);
Locals.Length(0);
Scope[SCOPE_LOCAL] = NULL;
}
Locals.SetFixedLength();
c.u8 = Base + Sf.ReturnIp;
}
else
{
ExitScriptExecution;
}
#endif
break;
}
case IArrayGet:
{
#if VM_DECOMP
if (Log)
Log->Print( "%p ArrayGet %s = %s[%s]\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr(),
c.r[2].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Var) Resolve();
LResolveRef(Idx) Resolve();
#ifdef VM_EXECUTE
switch (Var->Type)
{
case GV_LIST:
{
CheckParam(Var->Value.Lst);
LVariant *t = Var->Value.Lst->ItemAt(Idx->CastInt32());
if (t)
{
if (Var == Dst)
{
if (Var->Value.Lst->Delete(t))
{
*Var = *t;
DeleteObj(t);
}
else CheckParam(!"List delete failed.");
}
else *Dst = *t;
}
else Dst->Empty();
break;
}
case GV_HASHTABLE:
{
CheckParam(Var->Value.Hash);
LVariant *t = (LVariant*)Var->Value.Hash->Find(Idx->CastString());
if (t) *Dst = *t;
else Dst->Empty();
break;
}
case GV_CUSTOM:
{
LCustomType *T = Var->Value.Custom.Dom;
size_t Sz = T->Sizeof();
int Index = Idx->CastInt32();
Dst->Type = GV_CUSTOM;
Dst->Value.Custom.Dom = T;
Dst->Value.Custom.Data = Var->Value.Custom.Data + (Sz * Index);
break;
}
case GV_STRING:
{
auto c = Var->Str();
auto i = Idx->CastInt64();
if (!c || i < 0)
break;
LUtf8Ptr p(c);
uint32_t ch;
do
{
ch = p;
if (i-- == 0)
{
*Dst = ch;
break;
}
p++;
}
while (ch);
break;
}
default:
{
LString s;
s.Printf("%s IArrayGet warning: Can't array deref variant type %i\n",
Code->AddrToSourceRef(CurrentScriptAddress),
Var->Type);
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
break;
}
}
#endif
break;
}
case IArraySet:
{
#if VM_DECOMP
if (Log)
Log->Print( "%p ArraySet %s[%s] = %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr(),
c.r[2].GetStr());
#endif
LResolveRef(Var) Resolve();
LResolveRef(Idx) Resolve();
LResolveRef(Val) Resolve();
#ifdef VM_EXECUTE
switch (Var->Type)
{
case GV_LIST:
{
CheckParam(Var->Value.Lst);
(*Var->Value.Lst).Insert(new LVariant(*Val), Idx->CastInt32());
break;
}
case GV_HASHTABLE:
{
CheckParam(Var->Value.Hash);
LVariant *Old = (LVariant*)Var->Value.Hash->Find(Idx->CastString());
DeleteObj(Old);
Var->Value.Hash->Add(Idx->CastString(), new LVariant(*Val));
break;
}
default:
{
LString s;
s.Printf("%s IArraySet warning: Can't dereference type '%s'\n",
Code->AddrToSourceRef(CurrentScriptAddress),
LVariant::TypeToString(Var->Type));
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
break;
}
}
#endif
break;
}
case IAnd:
{
#if VM_DECOMP
if (Log)
Log->Print("%p %s && %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
*Dst = (Dst->CastInt32() != 0) && (Src->CastInt32() != 0);
#endif
break;
}
case IOr:
{
#if VM_DECOMP
if (Log)
Log->Print("%p %s || %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Src) Resolve();
#ifdef VM_EXECUTE
*Dst = (Dst->CastInt32() != 0) || (Src->CastInt32() != 0);
#endif
break;
}
case INot:
{
#if VM_DECOMP
if (Log)
Log->Print("%p %s = !%s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[0].GetStr());
#endif
LResolveRef(Dst) Resolve();
#ifdef VM_EXECUTE
*Dst = !Dst->CastBool();
#endif
break;
}
case IDomGet:
{
#if VM_DECOMP
if (Log)
Log->Print("%p %s = %s->DomGet(%s, %s)\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr(),
c.r[2].GetStr(),
c.r[3].GetStr());
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Dom) Resolve();
LResolveRef(Name) Resolve();
LResolveRef(Arr) Resolve();
#ifdef VM_EXECUTE
// Return "NULL" in Dst on error
if (Dst != Dom)
Dst->Empty();
switch (Dom->Type)
{
case GV_DOM:
case GV_STREAM:
case GV_LSURFACE:
{
auto *dom = Dom->CastDom();
CheckParam(dom != NULL);
char *sName = Name->Str();
CheckParam(sName);
bool Ret = dom->GetVariant(sName, *Dst, CastArrayIndex(Arr));
if (!Ret)
{
Dst->Empty();
LString s;
s.Printf("%s IDomGet warning: Unexpected %s member '%s'.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
LVariant::TypeToString(Dom->Type),
sName);
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
}
break;
}
case GV_DATETIME:
{
CheckParam(Dom->Value.Date != NULL);
char *sName = Name->Str();
CheckParam(sName);
bool Ret = Dom->Value.Date->GetVariant(sName, *Dst, CastArrayIndex(Arr));
if (!Ret)
{
Dst->Empty();
LString s;
s.Printf("%s IDomGet warning: Unexpected %s member '%s'.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
LVariant::TypeToString(Dom->Type),
sName);
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
}
break;
}
case GV_CUSTOM:
{
LCustomType *Type = Dom->Value.Custom.Dom;
if (Type)
{
int Fld;
if (Name->Type == GV_INT32)
Fld = Name->Value.Int;
else
Fld = Type->IndexOf(Name->Str());
int Index = Arr ? Arr->CastInt32() : 0;
Type->Get(Fld, *Dst, Dom->Value.Custom.Data, Index);
}
break;
}
case GV_LIST:
{
CheckParam(Dom->Value.Lst);
char *sName = Name->Str();
CheckParam(sName);
LDomProperty p = LStringToDomProp(sName);
if (p == ObjLength)
(*Dst) = (int)Dom->Value.Lst->Length();
break;
}
case GV_HASHTABLE:
{
CheckParam(Dom->Value.Hash);
char *sName = Name->Str();
CheckParam(sName);
LDomProperty p = LStringToDomProp(sName);
if (p == ObjLength)
(*Dst) = (int)Dom->Value.Hash->Length();
break;
}
case GV_BINARY:
{
char *sName = Name->Str();
CheckParam(sName);
LDomProperty p = LStringToDomProp(sName);
if (p == ObjLength)
(*Dst) = Dom->Value.Binary.Length;
break;
}
case GV_INT32:
{
char *sName = Name->Str();
CheckParam(sName);
LDomProperty p = LStringToDomProp(sName);
switch (p)
{
case TypeString:
{
char s[32];
sprintf_s(s, sizeof(s), "%i", Dom->Value.Int);
*Dst = s;
break;
}
case TypeDouble:
{
*Dst = (double)Dom->Value.Int;
break;
}
default:
{
Dst->Empty();
LString s;
s.Printf("%s IDomGet warning: Unexpected int32 member '%s'.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
sName);
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
break;
}
}
break;
}
case GV_INT64:
{
char *sName = Name->Str();
CheckParam(sName);
LDomProperty p = LStringToDomProp(sName);
switch (p)
{
case TypeString:
{
char s[32];
sprintf_s(s, sizeof(s), LPrintfInt64, Dom->Value.Int64);
*Dst = s;
break;
}
case TypeDouble:
{
*Dst = (double)Dom->Value.Int64;
break;
}
default:
{
Dst->Empty();
LString s;
s.Printf("%s IDomGet warning: Unexpected int64 member '%s'.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
sName);
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
break;
}
}
break;
}
case GV_DOUBLE:
{
char *sName = Name->Str();
CheckParam(sName);
LDomProperty p = LStringToDomProp(sName);
switch (p)
{
case TypeString:
{
char s[32];
sprintf_s(s, sizeof(s), "%g", Dom->Value.Dbl);
*Dst = s;
break;
}
case TypeInt:
{
*Dst = (int64)Dom->Value.Dbl;
break;
}
default:
{
Dst->Empty();
LString s;
s.Printf("%s IDomGet warning: Unexpected double member '%s'.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
sName);
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
break;
}
}
break;
}
case GV_STRING:
{
char *sName = Name->Str();
CheckParam(sName);
LDomProperty p = LStringToDomProp(sName);
switch (p)
{
case ObjLength:
{
(*Dst) = (int)strlen(Dom->Str());
break;
}
case TypeInt:
{
(*Dst) = Dom->CastInt32();
break;
}
case TypeDouble:
{
(*Dst) = Dom->CastDouble();
break;
}
default:
{
Dst->Empty();
LString s;
s.Printf("%s IDomGet warning: Unexpected string member '%s'.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
sName);
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
break;
}
}
break;
}
case GV_NULL:
{
LString s;
s.Printf("%s IDomGet warning: Can't deref NULL object.\n",
Code->AddrToSourceRef(CurrentScriptAddress));
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
break;
}
default:
{
LString s;
s.Printf("%s IDomGet warning: Unexpected type %s (Src=%s:%i IP=0x%x).\n",
Code->AddrToSourceRef(CurrentScriptAddress),
LVariant::TypeToString(Dom->Type),
_FL,
CurrentScriptAddress);
if (Log)
Log->Write(s, s.Length());
if (LVirtualMachine::BreakOnWarning)
OnException(NULL, -1, CurrentScriptAddress, s);
Status = ScriptWarning;
break;
}
}
#endif
break;
}
case IDomSet:
{
#if VM_DECOMP
if (Log)
Log->Print("%p %s->DomSet(%s, %s) = %s\n",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr(),
c.r[2].GetStr(),
c.r[3].GetStr());
#endif
LResolveRef(Dom) Resolve();
LResolveRef(Name) Resolve();
LResolveRef(Arr) Resolve();
LResolveRef(Value) Resolve();
#ifdef VM_EXECUTE
char *sName = Name->Str();
if (!sName)
{
if (Log)
Log->Print("%s IDomSet error: No name string.\n",
Code->AddrToSourceRef(CurrentScriptAddress));
SetScriptError;
break;
}
switch (Dom->Type)
{
case GV_DOM:
// case GV_GFILE:
case GV_STREAM:
case GV_LSURFACE:
{
auto *dom = Dom->CastDom();
CheckParam(dom != NULL);
bool Ret = dom->SetVariant(sName, *Value, CastArrayIndex(Arr));
if (!Ret)
{
if (Log)
Log->Print("%s IDomSet warning: Unexpected %s member '%s'.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
LVariant::TypeToString(Dom->Type),
sName);
Status = ScriptWarning;
}
break;
}
case GV_DATETIME:
{
CheckParam(Dom->Value.Date != NULL);
bool Ret = Dom->Value.Date->SetVariant(sName, *Value, CastArrayIndex(Arr));
if (!Ret)
{
if (Log)
Log->Print("%s IDomSet warning: Unexpected %s member '%s'.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
LVariant::TypeToString(Dom->Type),
sName);
Status = ScriptWarning;
}
break;
}
case GV_CUSTOM:
{
LCustomType *Type = Dom->Value.Custom.Dom;
if (Type)
{
int Fld;
if (IsDigit(*sName))
Fld = atoi(sName);
else
Fld = Type->IndexOf(sName);
int Index = Arr ? Arr->CastInt32() : 0;
if (!Type->Set(Fld, *Value, Dom->Value.Custom.Data, Index) &&
Log)
{
Log->Print("%s IDomSet warning: Couldn't set '%s' on custom type.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
sName);
}
}
break;
}
case GV_STRING:
{
LDomProperty p = LStringToDomProp(sName);
switch (p)
{
case ObjLength:
{
char *s;
int DLen = Value->CastInt32();
if (DLen && (s = new char[DLen+1]))
{
size_t SLen = Dom->Str() ? strlen(Dom->Str()) : 0;
if (SLen)
memcpy(s, Dom->Str(), SLen);
memset(s+SLen, ' ', DLen-SLen);
s[DLen] = 0;
DeleteArray(Dom->Value.String);
Dom->Value.String = s;
}
else Dom->Empty();
break;
}
case TypeInt:
{
*Dom = Value->CastInt32();
Dom->Str();
break;
}
case TypeDouble:
{
*Dom = Value->CastDouble();
Dom->Str();
break;
}
default:
{
if (Log)
Log->Print("%s IDomSet warning: Unexpected string member %s.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
sName);
Status = ScriptWarning;
break;
}
}
break;
}
default:
{
if (Log)
Log->Print("%s IDomSet warning: Unexpected type %s.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
LVariant::TypeToString(Dom->Type));
Status = ScriptWarning;
break;
}
}
#endif
break;
}
case IDomCall:
{
#if VM_DECOMP
if (Log)
Log->Print("%p %s = %s->DomCall(%s, ",
CurrentScriptAddress - 1,
c.r[0].GetStr(),
c.r[1].GetStr(),
c.r[2].GetStr());
#else
LVarRef DstRef = *c.r;
#endif
LResolveRef(Dst) Resolve();
LResolveRef(Dom) Resolve();
LResolveRef(Name) Resolve();
#ifdef VM_EXECUTE
LResolveRef(Args) Resolve();
int ArgCount = Args->CastInt32();
char *sName = Name->Str();
CheckParam(sName)
if (Dom->Type == GV_CUSTOM)
{
#define DEBUG_CUSTOM_METHOD_CALL 1
auto t = Dom->Value.Custom.Dom;
CheckParam(t);
auto m = t->GetMethod(sName);
CheckParam(m);
CheckParam(m->Params.Length() == ArgCount);
// Set up new stack frame...
StackFrame &Sf = Frames.New();
Sf.CurrentFrameSize = m->FrameSize;
Sf.PrevFrameStart = Locals.Length() ? Scope[1] - &Locals[0] : 0;
Sf.ReturnValue = DstRef;
// Increase the local stack size
AddLocalSize(m->FrameSize + 1);
#if DEBUG_CUSTOM_METHOD_CALL
LgiTrace("CustomType.Call(%s) Args=%i, Frame=%i, Addr=%i, LocalsBase=%i ",
sName, ArgCount, m->FrameSize, m->Address, LocalsBase);
#endif
size_t i = LocalsBase;
Locals[i++] = *Dom; // this pointer...
#if DEBUG_CUSTOM_METHOD_CALL
LString s = Locals[i-1].ToString();
LgiTrace("This=%s, ", s.Get());
#endif
size_t end = i + ArgCount;
while (i < end)
{
Locals[i++] = *Resolve();
#if DEBUG_CUSTOM_METHOD_CALL
s = Locals[i-1].ToString();
LgiTrace("[%i]=%s, ", i-1, s.Get());
#endif
}
// Now adjust the local stack to point to the locals for the function
Scope[1] = Locals.Length() ? &Locals[LocalsBase] : NULL;
// Set IP to start of function
Sf.ReturnIp = CurrentScriptAddress;
c.u8 = Base + m->Address;
#if DEBUG_CUSTOM_METHOD_CALL
LgiTrace("\n");
#endif
break;
}
- LScriptArguments Arg(Vm, Dst);
+ LScriptArguments Arg(Vm, Dst, NULL, CurrentScriptAddress);
Arg.Length(ArgCount);
for (int i=0; iType);
}
else switch (Dom->Type)
{
case GV_DOM:
case GV_STREAM:
case GV_LSURFACE:
{
auto *dom = Dom->CastDom();
CheckParam(dom);
bool Ret = dom->CallMethod(sName, Arg);
if (!Ret)
{
Dst->Empty();
if (Log)
Log->Print("%s IDomCall warning: %s(...) failed.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
sName);
Status = ScriptWarning;
}
break;
}
case GV_DATETIME:
{
CheckParam(Dom->Value.Date);
bool Ret = Dom->Value.Date->CallMethod(sName, Dst, Arg);
if (!Ret)
{
Dst->Empty();
if (Log)
Log->Print("%s IDomCall warning: %s(...) failed.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
sName);
Status = ScriptWarning;
}
break;
}
case GV_LIST:
{
CheckParam(Dom->Value.Lst);
switch (p)
{
case ObjLength:
{
*Dst = (int64)Dom->Value.Lst->Length();
break;
}
case ContainerAdd:
{
if (Arg.Length() > 0 &&
Arg[0])
{
int Index = Arg.Length() > 1 ? Arg[1]->CastInt32() : -1;
LVariant *v = new LVariant;
*v = *Arg[0];
Dom->Value.Lst->Insert(v, Index);
}
break;
}
case ContainerDelete:
{
for (unsigned i=0; iCastInt32();
LVariant *Elem = Dom->Value.Lst->ItemAt(n);
if (Elem)
{
Dom->Value.Lst->Delete(Elem);
DeleteObj(Elem);
}
}
}
break;
}
case ContainerHasKey:
{
if (Arg.Length() > 0 && Arg[0])
{
int Index = Arg[0]->CastInt32();
*Dst = (bool) (Index >= 0 && Index < (int)Dom->Value.Lst->Length());
}
else
{
*Dst = false;
}
break;
}
case ContainerSort:
{
LVariant *Param = Arg.Length() > 0 ? Arg[0] : NULL;
Dom->Value.Lst->Sort(LVariantCmp, (NativeInt)Param);
break;
}
default:
{
Dst->Empty();
if (Log)
Log->Print( "%s IDomCall warning: Unexpected list member '%s'.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
sName);
Status = ScriptWarning;
break;
}
}
break;
}
case GV_HASHTABLE:
{
CheckParam(Dom->Value.Hash);
switch (p)
{
case ObjLength:
{
*Dst = Dom->Value.Hash->Length();
break;
}
case ContainerAdd:
{
if (Arg.Length() == 2 &&
Arg[0] &&
Arg[1])
{
char *Key = Arg[1]->Str();
if (Key)
{
LVariant *v = new LVariant;
*v = *Arg[0];
Dom->Value.Hash->Add(Key, v);
}
}
break;
}
case ContainerDelete:
{
if (Arg.Length() == 1 &&
Arg[0])
{
char *Key = Arg[0]->Str();
if (Key)
{
LVariant *v = (LVariant*) Dom->Value.Hash->Find(Key);
if (v)
{
Dom->Value.Hash->Delete(Key);
delete v;
}
}
}
break;
}
case ContainerHasKey:
{
if (Arg.Length() > 0 && Arg[0])
{
char *Key = Arg[0]->Str();
*Dst = (bool) (Dom->Value.Hash->Find(Key) != NULL);
}
else
{
*Dst = false;
}
break;
}
default:
{
Dst->Empty();
if (Log)
Log->Print("%s IDomCall warning: Unexpected hashtable member '%s'.\n",
Code->AddrToSourceRef(CurrentScriptAddress),
sName);
Status = ScriptWarning;
break;
}
}
break;
}
case GV_BINARY:
{
switch (p)
{
default:
break;
case ObjLength:
*Dst = Dom->Value.Binary.Length;
break;
}
break;
}
case GV_STRING:
{
if (Arg.Length() > 0 && !Arg[0])
{
Dst->Empty();
break;
}
switch (p)
{
case ObjLength:
{
char *s = Dom->Str();
*Dst = (int) (s ? strlen(s) : 0);
break;
}
case StrJoin:
{
switch (Arg[0]->Type)
{
case GV_LIST:
{
LStringPipe p(256);
List *Lst = Arg[0]->Value.Lst;
const char *Sep = Dom->CastString();
auto It = Lst->begin();
LVariant *v = *It;
if (v)
{
LVariant Tmp = *v;
p.Print("%s", Tmp.CastString());
while ((v = *(++It)))
{
Tmp = *v;
p.Print("%s%s", Sep, Tmp.CastString());
}
}
Dst->OwnStr(p.NewStr());
break;
}
default:
{
*Dst = *Arg[0];
Dst->CastString();
break;
}
}
break;
}
case StrSplit:
{
const char *Sep = Arg[0]->Str();
if (!Sep)
{
Dst->Empty();
break;
}
LVariant Tmp;
if (Dst == Dom)
{
Tmp = *Dom;
Dom = &Tmp;
}
Dst->SetList();
size_t SepLen = strlen(Sep);
int MaxSplit = Arg.Length() > 1 ? Arg[1]->CastInt32() : -1;
const char *c = Dom->CastString();
while (c && *c)
{
if (MaxSplit > 0 && (int)Dst->Value.Lst->Length() >= MaxSplit)
break;
const char *next = strstr(c, Sep);
if (!next)
break;
LVariant *v = new LVariant;
v->OwnStr(NewStr(c, next - c));
Dst->Value.Lst->Insert(v);
c = next + SepLen;
}
if (c && *c)
{
LVariant *v = new LVariant;
v->OwnStr(NewStr(c));
Dst->Value.Lst->Insert(v);
}
break;
}
case StrSplitDelimit:
{
const char *Sep = Arg[0]->Str();
if (!Sep)
{
Dst->Empty();
break;
}
LVariant Tmp;
if (Dst == Dom)
{
Tmp = *Dom;
Dom = &Tmp;
}
Dst->SetList();
int MaxSplit = Arg.Length() > 1 ? Arg[1]->CastInt32() : -1;
const char *c = Dom->CastString();
while (c && *c)
{
if (MaxSplit > 0 && (int)Dst->Value.Lst->Length() >= MaxSplit)
break;
const char *next = c;
while (*next && !strchr(Sep, *next))
next++;
LVariant *v = new LVariant;
v->OwnStr(NewStr(c, next - c));
Dst->Add(v);
for (c = next; *c && strchr(Sep, *c); c++)
;
}
if (c && *c)
Dst->Add(new LVariant(c));
break;
}
case StrFind:
{
const char *s = Dom->Str();
if (!s)
{
*Dst = -1;
break;
}
ssize_t sLen = Strlen(s);
auto sub = Arg[0]->Str();
auto start = Arg.Length() > 1 ? Arg[1]->CastInt32() : 0;
auto end = Arg.Length() > 2 ? Arg[2]->CastInt32() : -1;
if (start >= sLen)
{
*Dst = -1;
break;
}
char *sStart = (char*)s + start;
char *pos;
if (end >= 0)
pos = Strnstr(sStart, sub, end);
else
pos = Strstr(sStart, sub);
if (pos)
*Dst = (int64) (pos - s);
else
*Dst = -1;
break;
}
case StrRfind:
{
const char *s = Dom->Str();
if (!s)
{
*Dst = -1;
break;
}
ssize_t sLen = strlen(s);
auto sub = Arg[0]->Str();
auto start_idx = Arg.Length() > 1 ? Arg[1]->CastInt32() : 0;
auto end_idx = Arg.Length() > 2 ? Arg[2]->CastInt32() : -1;
if (start_idx >= sLen)
{
*Dst = -1;
break;
}
auto sublen = Strlen(sub);
auto cur = s + start_idx;
auto end = end_idx >= 0 ? cur + end_idx : NULL;
const char *pos = NULL;
while (true)
{
cur = (end)
?
Strnstr(cur, sub, end - cur)
:
Strstr(cur, sub);
if (cur)
{
pos = cur;
cur += sublen;
}
else break;
}
if (pos)
*Dst = (int64) (pos - s);
else
*Dst = -1;
break;
}
case StrLower:
{
if (Dst != Dom)
*Dst = Dom->CastString();
StrLwr(Dst->Str());
break;
}
case StrUpper:
{
if (Dst != Dom)
*Dst = Dom->CastString();
StrUpr(Dst->Str());
break;
}
case StrStrip:
{
auto s = Dom->Str();
if (s)
{
const char *Delimit = Arg.Length() > 0 ? Arg[0]->Str() : NULL;
if (!Delimit)
Delimit = WhiteSpace;
auto start = s;
auto end = s + Strlen(s);
while (start < end && strchr(Delimit, *start))
start++;
while (end > start && strchr(Delimit, end[-1]))
end--;
Dst->OwnStr(NewStr(start, end - start));
}
else Dst->Empty();
break;
}
case StrSub:
{
auto s = Dom->Str();
if (s)
{
ssize_t Start = Arg.Length() > 0 ? Arg[0]->CastInt32() : 0;
ssize_t End = Arg.Length() > 1 ? Arg[1]->CastInt32() : -1;
ssize_t Len = strlen(s);
if (End < 0 || End > Len)
End = Len;
if (Start < 0)
Start = 0;
if (Start <= End)
Dst->OwnStr(NewStr(s + Start, End - Start));
else
Dst->Empty();
}
else Dst->Empty();
break;
}
default:
{
Dst->Empty();
if (Log)
Log->Print("%p IDomCall warning: Unexpected string member %s (%s:%i).\n",
CurrentScriptAddress,
sName,
_FL);
Status = ScriptWarning;
break;
}
}
break;
}
default:
{
const char *Type = LVariant::TypeToString(Dom->Type);
char t[32];
if (!Type)
{
sprintf_s(t, sizeof(t), "UnknownType(%i)", Dom->Type);
Type = t;
}
Dst->Empty();
if (Log)
{
Log->Print("%s IDomCall warning: Unexpected type %s (Src=%s:%i IP=0x%x).\n",
Code->AddrToSourceRef(CurrentScriptAddress),
Type,
_FL,
CurrentScriptAddress);
}
Status = ScriptWarning;
break;
}
}
- Arg.Length(0); // It doesn't own the variants, so don't delete them.
+ ON_EXCEPTION(Arg);
#else
LVariant *Count = NULL;
switch (c.r->Scope)
{
case SCOPE_GLOBAL:
Count = &Code->Globals[c.r->Index];
c.r++;
break;
default:
OnException(_FL, CurrentScriptAddress, "Unsupported scope.");
return ScriptError;
}
int Args = Count->CastInt32();
for (int i=0; iPrint("%s%s", i ? ", " : "", c.r->GetStr());
#endif
c.r++;
}
#if VM_DECOMP
if (Log)
Log->Print(")\n");
#endif
#endif
break;
}
case IBreakPoint:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Debugger\n", CurrentScriptAddress-1);
#elif VM_EXECUTE
OnException(_FL, CurrentScriptAddress-1, "ShowDebugger");
return ScriptWarning;
#endif
break;
}
case IDebug:
{
#if VM_DECOMP
if (Log)
Log->Print("%p Debugger\n", CurrentScriptAddress-1);
#elif VM_EXECUTE
#ifdef WINDOWS
__debugbreak();
#elif defined MAC
__builtin_trap();
#elif defined LINUX
Gtk::raise(SIGINT);
#else
#warning "Not impl."
#endif
#endif
break;
}
#undef Resolve
#undef LResolveRef
\ No newline at end of file
diff --git a/src/common/Coding/ScriptVM.cpp b/src/common/Coding/ScriptVM.cpp
--- a/src/common/Coding/ScriptVM.cpp
+++ b/src/common/Coding/ScriptVM.cpp
@@ -1,2168 +1,2160 @@
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/Scripting.h"
#include "lgi/common/Box.h"
#include "lgi/common/TabView.h"
#include "lgi/common/TextLog.h"
#include "lgi/common/List.h"
#include "lgi/common/ToolBar.h"
#include "lgi/common/TableLayout.h"
#include "lgi/common/TextLabel.h"
#include "lgi/common/ScrollBar.h"
#include "lgi/common/Matrix.h"
#include "lgi/common/Menu.h"
#include "ScriptingPriv.h"
#define TIME_INSTRUCTIONS 0
#define POST_EXECUTE_STATE 0
// #define BREAK_POINT 0x0000009F
#define ExitScriptExecution c.u8 = e
#define SetScriptError c.u8 = e; Status = ScriptError
#define CurrentScriptAddress (c.u8 - Base)
#define CheckParam(ptr) if (!(ptr)) \
{ \
OnException(_FL, CurrentScriptAddress-1, #ptr); \
c.u8 = e; \
Status = ScriptError; \
break; \
}
#define AddLocalSize(NewSize) \
size_t LocalsBase = Locals.Length(); \
Locals.SetFixedLength(false); \
/* LgiTrace("%s:%i - Locals %i -> %i\n", _FL, LocalsBase, LocalsBase + NewSize); */ \
Locals.Length(LocalsBase + NewSize); \
Scope[SCOPE_LOCAL] = &Locals[LocalsBase]; \
Locals.SetFixedLength();
#ifdef WIN32
extern "C" uint64 __cdecl CallExtern64(void *FuncAddr, NativeInt *Ret, uint32_t Args, void *Arg);
#elif defined(LINUX)
#include
#endif
int LVariantCmp(LVariant *a, LVariant *b, NativeInt Data)
{
LVariant *Param = (LVariant*) Data;
if (!a || !b)
return 0;
if (a->Type == GV_STRING &&
b->Type == GV_STRING)
{
const char *Empty = "";
const char *as = a->Str();
const char *bs = b->Str();
return _stricmp(as?as:Empty, bs?bs:Empty);
}
else if (a->Type == GV_DOM &&
b->Type == GV_DOM &&
Param)
{
const char *Fld = Param->Str();
int Dir = 1;
if (Fld && *Fld == '-')
{
Fld++;
Dir = -1;
}
LVariant av, bv;
if (a->Value.Dom->GetValue(Fld, av) &&
b->Value.Dom->GetValue(Fld, bv))
{
return LVariantCmp(&av, &bv, 0) * Dir;
}
}
else if (a->Type == GV_INT32 &&
b->Type == GV_INT32)
{
return a->CastInt32() - b->CastInt32();
}
else if (a->Type == GV_DATETIME &&
b->Type == GV_DATETIME)
{
return a->Value.Date->Compare(b->Value.Date);
}
else
{
LAssert(!"Impl a handler for this type.");
}
return 0;
}
inline LVariantType DecidePrecision(LVariantType a, LVariantType b)
{
if (a == GV_DOUBLE || b == GV_DOUBLE)
return GV_DOUBLE;
if (a == GV_INT64 || b == GV_INT64)
return GV_INT64;
return GV_INT32;
}
inline LVariantType ComparePrecision(LVariantType a, LVariantType b)
{
if (a == GV_NULL || b == GV_NULL)
return GV_NULL;
if (a == GV_DATETIME && b == GV_DATETIME)
return GV_DATETIME;
if (a == GV_DOUBLE || b == GV_DOUBLE)
return GV_DOUBLE;
if (a == GV_STRING || b == GV_STRING)
return GV_STRING;
if (a == GV_INT64 || b == GV_INT64)
return GV_INT64;
return GV_INT32;
}
inline char *CastString(LVariant *v, LVariant &cache)
{
if (v->Type == GV_STRING)
return v->Str();
cache = *v;
return cache.CastString();
}
inline int CompareVariants(LVariant *a, LVariant *b)
{
// Calculates "a - b"
switch (ComparePrecision(a->Type, b->Type))
{
case GV_DATETIME:
return a->Value.Date->Compare(b->Value.Date);
break;
case GV_DOUBLE:
{
double d = a->CastDouble() - b->CastDouble();
if (d < -MATRIX_DOUBLE_EPSILON)
return -1;
return d > MATRIX_DOUBLE_EPSILON;
}
case GV_STRING:
{
LVariant as, bs;
char *A = CastString(a, as);
char *B = CastString(b, bs);
if (!A || !B)
return -1;
else
return strcmp(A, B);
break;
}
case GV_INT64:
{
int64 d = a->CastInt64() - b->CastInt64();
if (d < 0)
return -1;
return d > 0;
}
case GV_NULL:
{
// One or more values is NULL
if (a->IsNull() && b->IsNull())
return 0; // The same..
LVariant *Val = a->IsNull() ? b : a;
if (Val->IsNull())
{
LAssert(0);
return 0;
}
switch (Val->Type)
{
case GV_INT32:
case GV_INT64:
return Val->CastInt64() != 0;
case GV_STRING:
return Val->Str() != NULL;
default:
return Val->CastVoidPtr() != NULL;
}
break;
}
default:
return a->CastInt32() - b->CastInt32();
break;
}
}
LExecutionStatus LExternFunc::Call(LScriptContext *Ctx, LScriptArguments &Args)
{
if (!Lib || !Method)
return ScriptError;
LStream *Log = Ctx ? Ctx->GetLog() : NULL;
if (Args.Length() != ArgType.Length())
{
if (Log)
Log->Print("Error: Extern '%s.%s' expecting %i arguments, not %i given.\n",
Lib.Get(), Method.Get(),
ArgType.Length(),
Args.Length());
return ScriptError;
}
LArray Val;
LArray Mem;
bool UnsupportedArg = false;
Val.Length(Args.Length() << 1);
LPointer Ptr;
Ptr.ni = &Val[0];
for (unsigned i=0; !UnsupportedArg && iCastVoidPtr();
*Ptr.vp++ = cp;
}
else
{
char *s = NewStr(v->CastString());
if (!s)
{
UnsupportedArg = true;
break;
}
Mem.Add(s);
*Ptr.vp++ = s;
}
break;
}
case GV_VOID_PTR:
{
*Ptr.vp++ = v->CastVoidPtr();
break;
}
default:
{
UnsupportedArg = true;
break;
}
}
}
else
{
// Plain type
switch (t.Base)
{
case GV_INT32:
{
#if defined(_WIN64)
*Ptr.s64++ = v->CastInt32();
#else
*Ptr.s32++ = v->CastInt32();
#endif
break;
}
case GV_INT64:
{
*Ptr.s64++ = v->CastInt64();
break;
}
default:
{
UnsupportedArg = true;
break;
}
}
}
}
LLibrary Library(Lib);
if (!Library.IsLoaded())
{
if (Log)
Log->Print("Error: Extern library '%s' missing.\n", Lib.Get());
return ScriptError;
}
void *c = Library.GetAddress(Method);
if (!c)
{
if (Log)
Log->Print("Error: Extern library '%s' has no method '%s'.\n", Lib.Get(), Method.Get());
return ScriptError;
}
#if defined(_MSC_VER) || (defined(MAC) && defined(LGI_32BIT) && !LGI_COCOA)
ssize_t a = Ptr.ni - &Val[0];
#endif
NativeInt r = 0;
#if defined(_MSC_VER)
#if defined(_WIN64)
// 64bit... boooo no inline asm!
void *b = &Val[0];
r = CallExtern64(c, &r, (uint32_t)a, b);
#else
// 32bit... yay inline asm!
void *b = Ptr.ni - 1;
_asm
{
mov ecx, a
mov ebx, b
}
label1:
_asm
{
push [ebx]
sub ebx, 4
loop label1
mov ebx, c
call ebx
mov r, eax
}
#endif
#elif defined(MAC)
#if LGI_COCOA
#warning FIXME
#elif LGI_32BIT
// 32bit only
void *b = Ptr.ni - 1;
asm ( "movl %2, %%ecx;"
"movl %3, %%ebx;"
"label1:"
"pushl (%%ebx);"
"subl %%ebx, 4;"
"loop label1;"
"call *%1;"
:"=a"(r) /* output */
:"r"(c), "r"(a), "r"(b) /* input */
:/*"%eax",*/ "%ecx", "%ebx" /* clobbered register */
);
#endif
#else
// Not implemented, gcc???
LAssert(0);
#endif
*Args.GetReturn() = (int) r;
for (unsigned i=0; iType == GV_BINARY &&
v->Value.Binary.Data != NULL &&
t.Base == GV_STRING)
{
// Cast the type back to t.Base
char *p = (char*)v->Value.Binary.Data;
v->Type = t.Base;
v->Value.String = p;
}
}
}
Mem.DeleteArrays();
return ScriptSuccess;
}
struct CodeBlock
{
unsigned SrcLine;
LArray AsmAddr;
unsigned ViewLine;
LAutoString Source;
int SrcLines;
LAutoString Asm;
int AsmLines;
};
class LVirtualMachinePriv : public LRefCount
{
LVariant ArrayTemp;
char *CastArrayIndex(LVariant *Idx)
{
if (Idx == NULL || Idx->Type == GV_NULL)
return NULL;
if (Idx->Type == GV_STRING)
return Idx->Str();
ArrayTemp = *Idx;
return ArrayTemp.CastString();
}
public:
struct StackFrame
{
uint32_t CurrentFrameSize;
ssize_t PrevFrameStart;
size_t ReturnIp;
LVarRef ReturnValue;
};
enum RunType
{
RunContinue,
RunStepInstruction,
RunStepLine,
RunStepOut
};
LVirtualMachine *Vm;
LStream *Log = NULL;
LCompiledCode *Code = NULL;
LExecutionStatus Status = ScriptNotStarted;
LScriptPtr c;
LVariant Reg[MAX_REGISTER];
LArray Locals;
LVariant *Scope[SCOPE_MAX];
LArray Frames;
RunType StepType;
LVmCallback *Callback = NULL;
LVmDebugger *Debugger = NULL;
LScriptArguments *ArgsOutput = NULL;
LArray BreakPts;
LString TempPath;
bool DebuggerEnabled = false;
bool BreakCpp = false;
LVirtualMachinePriv(LVirtualMachine *vm, LVmCallback *callback)
{
Vm = vm;
Callback = callback;
ZeroObj(Scope);
}
~LVirtualMachinePriv()
{
}
void DumpVariant(LStream *Log, LVariant &v)
{
if (!Log)
return;
switch (v.Type)
{
case GV_INT32:
Log->Print("(int) %i", v.Value.Int);
break;
case GV_INT64:
Log->Print("(int64) %I64i", v.Value.Int64);
break;
case GV_STRING:
{
char *nl = strchr(v.Value.String, '\n');
if (nl)
Log->Print("(string) '%.*s...' (%i bytes)", nl - v.Value.String, v.Value.String, strlen(v.Value.String));
else
Log->Print("(string) '%s'", v.Value.String);
break;
}
case GV_DOUBLE:
Log->Print("(double) %g", v.Value.Dbl);
break;
case GV_BOOL:
Log->Print("(bool) %s", v.Value.Bool ? "true" : "false");
break;
case GV_DOM:
Log->Print("(LDom*) %p", v.Value.Dom);
break;
case GV_HASHTABLE:
{
Log->Print("(GHashTable*) %p {", v.Value.Hash);
int n = 0;
// const char *k;
// for (LVariant *p = v.Value.Hash->First(&k); p; p = v.Value.Hash->Next(&k), n++)
for (auto it : *v.Value.Hash)
{
Log->Print("%s\"%s\"=", n?",":"", it.key);
DumpVariant(Log, *it.value);
}
Log->Print("}");
break;
}
case GV_LIST:
{
Log->Print("(LList*) %p {", v.Value.Lst);
int n=0;
for (auto i: *v.Value.Lst)
{
Log->Print("%s%i=", n?",":"", n);
DumpVariant(Log, *i);
n++;
}
Log->Print("}");
break;
}
case GV_NULL:
{
Log->Print("null");
break;
}
case GV_BINARY:
{
Log->Print("(Binary[%i])", v.Value.Binary.Length);
if (v.Value.Binary.Data)
{
int i;
for (i=0; i<16 && i < v.Value.Binary.Length; i++)
Log->Print(" %.2x", ((uint8_t*)v.Value.Binary.Data)[i]);
if (i < v.Value.Binary.Length)
Log->Print("...");
}
break;
}
default:
Log->Print("(Type-%i) ????", v.Type);
break;
}
}
void DumpVariables(LVariant *v, int len)
{
if (!Log)
return;
for (int i=0; iPrint("[%i] = ", i);
DumpVariant(Log, v[i]);
Log->Print("\n");
}
}
}
void OnException(const char *File, int Line, ssize_t Address, const char *Msg)
{
if (!Code)
{
LgiTrace("%s:%i - Exception without Code object: %s:%i, %s\n",
_FL,
File, Line,
Msg);
return;
}
if (Address < 0)
{
uint8_t *Base = &Code->ByteCode[0];
Address = c.u8 - Base;
}
if (!File || Line < 0)
{
// Extract the file / line from the current script location
File = Code->GetFileName();
Line = Code->ObjectToSourceAddress(Address);
}
if (Log)
{
char *Last = strrchr((char*)File, DIR_CHAR);
Log->Print("%s Exception: %s (%s:%i)\n", Code->AddrToSourceRef(Address), Msg, Last?Last+1:File, Line);
}
else
{
LgiTrace("%s:%i - Exception @ %i: %s\n", File, Line, Address, Msg);
}
LStringPipe AsmBuf;
Decompile(Code->UserContext, Code, &AsmBuf);
auto Asm = AsmBuf.NewLStr();
if (Vm && Vm->OpenDebugger(Code, Asm))
{
LAssert(Debugger->GetCode()); // It should have taken a copy of the Code we passed in
-
- /*
- if (!Debugger->GetCode())
- {
- LAutoPtr Cp(new LCompiledCode(*Code));
- Debugger->OwnCompiledCode(Cp);
-
- LStringPipe AsmBuf;
- Decompile(Code->UserContext, Code, &AsmBuf);
- LAutoString Asm(AsmBuf.NewStr());
- Debugger->SetSource(Asm);
- }
- */
-
Debugger->OnAddress(Address);
LString m;
m.Printf("%s (%s:%i)", Msg, LGetLeaf(File), Line);
Debugger->OnError(m);
Debugger->Run();
}
else
{
// Set the script return value to FALSE
if (Frames.Length())
{
StackFrame Sf = Frames[0];
LVarRef &Ret = Sf.ReturnValue;
LVariant *RetVar = &Scope[Ret.Scope][Ret.Index];
*RetVar = false;
}
// Exit the script
c.u8 = Code->ByteCode.AddressOf() + Code->ByteCode.Length();
// Set the script status...
Status = ScriptError;
}
}
LExecutionStatus Decompile(LScriptContext *Context, LCompiledCode *Code, LStream *log)
{
LExecutionStatus Status = ScriptSuccess;
LAssert(sizeof(LVarRef) == 4);
LScriptPtr c;
uint8_t *Base = &Code->ByteCode[0];
c.u8 = Base;
uint8_t *e = c.u8 + Code->ByteCode.Length();
LStream *OldLog = Log;
if (log)
Log = log;
for (unsigned k=0; kGlobals.Length(); k++)
{
Log->Print("G%i = ", k);
DumpVariant(Log, Code->Globals[k]);
Log->Print("\n");
}
Log->Print("\n");
LHashTbl, char*> Fn;
for (unsigned m=0; mMethods.Length(); m++)
{
LFunctionInfo *Info = Code->Methods[m];
if (Info->ValidStartAddr())
Fn.Add(Info->GetStartAddr(), Info->GetName());
else
LAssert(!"Method not defined.");
}
int OldLineNum = 0;
while (c.u8 < e)
{
char *Meth = Fn.Find(CurrentScriptAddress);
if (Meth)
{
Log->Print("%s:\n", Meth);
}
int LineNum = Code->ObjectToSourceAddress(CurrentScriptAddress);
if (LineNum >= 0 && LineNum != OldLineNum)
{
Log->Print(" %i:\n", OldLineNum = LineNum);
}
switch (*c.u8++)
{
#define VM_DECOMP 1
#include "Instructions.h"
#undef VM_DECOMP
}
}
if (log)
Log = OldLog;
return Status;
}
LExecutionStatus Setup(LCompiledCode *code, uint32_t StartOffset, LStream *log, LFunctionInfo *Func, LScriptArguments *Args)
{
Status = ScriptSuccess;
Code = code;
if (!Code)
return ScriptError;
if (log)
Log = log;
else if (Code->SysContext && Code->SysContext->GetLog())
Log = Code->SysContext->GetLog();
else if (Code->UserContext && Code->UserContext->GetLog())
Log = Code->UserContext->GetLog();
// else LgiTrace("%s:%i - Execution without a log?\n", _FL);
LAssert(sizeof(LVarRef) == 4);
uint8_t *Base = c.u8 = &Code->ByteCode[0];
uint8_t *e = c.u8 + Code->ByteCode.Length();
Scope[SCOPE_REGISTER] = Reg;
Scope[SCOPE_LOCAL] = NULL;
Scope[SCOPE_GLOBAL] = &Code->Globals[0];
Scope[SCOPE_OBJECT] = NULL;
Scope[SCOPE_RETURN] = Args->GetReturn();
#ifdef _DEBUG
const char *SourceFileName = Code->GetFileName();
char Obj[MAX_PATH_LEN];
if (SourceFileName)
{
if (strchr(SourceFileName, DIR_CHAR))
strcpy_s(Obj, sizeof(Obj), SourceFileName);
else if (TempPath != NULL)
LMakePath(Obj, sizeof(Obj), TempPath, SourceFileName);
else
{
LGetSystemPath(LSP_TEMP, Obj, sizeof(Obj));
LMakePath(Obj, sizeof(Obj), Obj, SourceFileName);
}
char *Ext = LGetExtension(Obj);
if (Ext)
strcpy_s(Ext, sizeof(Obj)-(Ext-Obj), "asm");
else
strcat_s(Obj, sizeof(Obj), ".asm");
}
else
{
LAutoString DataPath;
if (Code->UserContext)
DataPath = Code->UserContext->GetDataFolder();
if (!DataPath)
{
char p[256];
if (LGetSystemPath(LSP_APP_INSTALL, p, sizeof(p)))
DataPath.Reset(NewStr(p));
}
LMakePath(Obj, sizeof(Obj), DataPath, "Script.asm");
}
{
LDirectory SrcD, ObjD;
bool OutOfDate = true;
if (LFileExists(SourceFileName) &&
SrcD.First(SourceFileName, NULL) != 0 &&
ObjD.First(Obj, NULL) != 0)
{
OutOfDate = ObjD.GetLastWriteTime() < SrcD.GetLastWriteTime();
}
if (OutOfDate || Debugger)
{
LFile f;
LStringPipe p;
LStream *Out = NULL;
if (Debugger)
{
Out = &p;
}
else if (f.Open(Obj, O_WRITE))
{
f.SetSize(0);
Out = &f;
}
if (Out)
{
LExecutionStatus Decomp = Decompile(Code->UserContext, Code, Out);
f.Close();
if (Decomp != ScriptSuccess)
{
LAssert(!"Decompilation failed.");
return ScriptError;
}
if (Debugger)
{
auto a = p.NewLStr();
Debugger->OnAddress(CurrentScriptAddress);
Debugger->SetSource(a);
}
}
}
}
#endif
#if TIME_INSTRUCTIONS
LARGE_INTEGER freq = {0}, start, end;
QueryPerformanceFrequency(&freq);
LHashTbl, int64> Timings;
LHashTbl, int> TimingFreq;
#endif
// Calling a function only, not the whole script
StackFrame &Sf = Frames.New();
Sf.ReturnIp = e - c.u8;
Sf.PrevFrameStart = 0;
Sf.ReturnValue.Scope = SCOPE_RETURN;
Sf.ReturnValue.Index = 0; // array is only one item long anyway
if (Func)
{
// Set up stack for function call
if (!Func->ValidFrameSize())
{
Log->Print("%s:%i - Function '%s' has an invalid frame size. (Script: %s).\n",
_FL, Func->GetName(), Code->AddrToSourceRef(Func->GetStartAddr()));
return ScriptError;
}
Sf.CurrentFrameSize = Func->GetFrameSize();
AddLocalSize(Sf.CurrentFrameSize);
if (Args)
{
// Check the local frame size is at least big enough for the args...
if (Args->Length() > Sf.CurrentFrameSize)
{
Log->Print("%s:%i - Arg count mismatch, Supplied: %i, FrameSize: %i (Script: %s).\n",
_FL, (int)Args->Length(), (int)Sf.CurrentFrameSize,
Code->AddrToSourceRef(Func->GetStartAddr()));
return ScriptError;
}
// Put the arguments of the function call into the local array
for (unsigned i=0; iLength(); i++)
{
Locals[LocalsBase+i] = *(*Args)[i];
}
}
if (!Func->ValidStartAddr())
{
Log->Print("%s:%i - Function '%s' is not defined. (Script: %s).\n",
_FL, Func->GetName(), Code->AddrToSourceRef(Func->GetStartAddr()));
return ScriptError;
}
// Set IP to start of function
c.u8 = Base + Func->GetStartAddr();
}
else
{
// Executing body of script
Sf.CurrentFrameSize = 0;
if (StartOffset > 0)
c.u8 = Base + StartOffset;
}
return Status;
}
int NearestLine(size_t Addr)
{
int l = Code->Debug.Find(Addr);
if (l >= 0)
return l;
for (int Off = 1; Off < 20; Off++)
{
int l = Code->Debug.Find(Addr + Off);
if (l >= 0)
{
return l;
}
l = Code->Debug.Find(Addr - Off);
if (l >= 0)
{
return l;
}
}
return -1;
}
LExecutionStatus Run(RunType Type)
{
LAssert(Code != NULL);
uint8_t *Base = &Code->ByteCode[0];
uint8_t *e = Base + Code->ByteCode.Length();
+ #define ON_EXCEPTION(args) \
+ args.Length(0); \
+ args.Address = -1; \
+ if (args.HasException()) \
+ { \
+ Status = ScriptError; \
+ goto ExitExecutionLoop; \
+ }
+
+
if (Type == RunContinue && BreakPts.Length() == 0)
{
// Unconstrained execution
while (c.u8 < e)
{
#if TIME_INSTRUCTIONS
uint8 TimedOpCode = *c.u8;
QueryPerformanceCounter(&start);
#endif
-
- #ifdef BREAK_POINT
- if (c.u8 - Base == BREAK_POINT)
- {
- int asd=0;
- }
- #endif
switch (*c.u8++)
{
#define VM_EXECUTE 1
#include "Instructions.h"
#undef VM_EXECUTE
}
#if TIME_INSTRUCTIONS
QueryPerformanceCounter(&end);
int Ticks = end.QuadPart - start.QuadPart;
int64 i = Timings.Find(TimedOpCode);
Timings.Add(TimedOpCode, i + Ticks);
i = TimingFreq.Find(TimedOpCode);
TimingFreq.Add(TimedOpCode, i + 1);
#endif
}
if (Log)
{
#if TIME_INSTRUCTIONS
Log->Print("\nTimings:\n");
Log->Print("%-20s%-10s%-10s%-10s\n", "Instr", "Total", "Freq", "Ave");
int Op;
for (int64 t=Timings.First(&Op); t; t=Timings.Next(&Op))
{
int Frq = TimingFreq.Find(Op);
int MilliSec = t * 1000000 / freq.QuadPart;
Log->Print("%-20s%-10i%-10i%-10i\n", InstToString((GInstruction)Op), MilliSec, Frq, MilliSec / Frq);
}
Log->Print("\n");
#endif
#if POST_EXECUTE_STATE
Log->Print("Stack:\n");
char *v;
for (void *i=Code->Globals.Lut.First(&v); i; i=Code->Globals.Lut.Next(&v))
{
int Idx = (int)i - 1;
if (Idx >= 0 && Idx < Code->Globals.Length())
{
Log->Print("%s = ", v);
DumpVariant(Log, Code->Globals[Idx]);
Log->Print("\n");
}
}
Log->Print("\nRegisters:\n");
DumpVariables(Reg, MAX_REGISTER);
#endif
}
}
else
{
// Stepping through code
int Param = 0;
switch (Type)
{
case RunStepLine:
Param = NearestLine(CurrentScriptAddress);
break;
case RunStepOut:
Param = (int)Frames.Length();
break;
default:
break;
}
if (BreakCpp)
#if defined(WIN32) && !defined(_WIN64)
_asm int 3
#else
assert(!"BreakPoint");
#endif
while (c.u8 < e)
{
if (Type == RunContinue &&
BreakPts.HasItem(c.u8 - Base))
break;
switch (*c.u8++)
{
#define VM_EXECUTE 1
#include "Instructions.h"
#undef VM_EXECUTE
}
if (Type == RunContinue)
continue;
if (Type == RunStepLine)
{
int CurLine = NearestLine(CurrentScriptAddress);
if (CurLine && CurLine != Param)
break;
}
else if (Type == RunStepOut)
{
if ((int)Frames.Length() < Param)
break;
}
else if (Type == RunStepInstruction)
{
break;
}
else LAssert(!"Invalid Type.");
}
}
+ ExitExecutionLoop:
if (Debugger && Status != ScriptError)
Debugger->OnAddress(CurrentScriptAddress);
return Status;
}
};
bool LVirtualMachine::BreakOnWarning = false;
LVirtualMachine::LVirtualMachine(LVmCallback *callback)
{
d = new LVirtualMachinePriv(this, callback);
d->IncRef();
}
LVirtualMachine::LVirtualMachine(Context ctx)
{
d = new LVirtualMachinePriv(this, ctx.Callback);
d->IncRef();
if ((d->Code = ctx.Code))
{
if (d->Code->ByteCode.IdxCheck(ctx.Addr))
d->c.u8 = d->Code->ByteCode.AddressOf() + ctx.Addr;
}
else
d->c.u8 = NULL;
}
LVirtualMachine::LVirtualMachine(LVirtualMachine *vm)
{
d = vm->d;
d->IncRef();
}
LVirtualMachine::~LVirtualMachine()
{
if (d->Vm == this)
d->Vm = NULL;
d->DecRef();
}
void LVirtualMachine::OnException(const char *File, int Line, ssize_t Address, const char *Msg)
{
d->OnException(File, Line, Address, Msg);
}
LExecutionStatus LVirtualMachine::Execute(LCompiledCode *Code, uint32_t StartOffset, LStream *Log, bool StartImmediately, LVariant *Return)
{
if (!Code)
return ScriptError;
LScriptArguments Args(this, Return);
LExecutionStatus s = d->Setup(Code, StartOffset, Log, NULL, &Args);
if (s != ScriptSuccess || !StartImmediately)
return s;
return d->Run(LVirtualMachinePriv::RunContinue);
}
LExecutionStatus LVirtualMachine::ExecuteFunction(LCompiledCode *Code, LFunctionInfo *Func, LScriptArguments &Args, LStream *Log, LScriptArguments *ArgsOut)
{
LCompiledCode *Cc = dynamic_cast(Code);
if (!Cc || !Func)
return ScriptError;
LExecutionStatus s = d->Setup(Cc, 0, Log, Func, &Args);
if (s != ScriptSuccess)
return s;
d->ArgsOutput = ArgsOut;
auto Prev = Args.Vm;
Args.Vm = this;
LExecutionStatus r = d->Run(LVirtualMachinePriv::RunContinue);
Args.Vm = Prev;
return r;
}
void LVirtualMachine::SetDebuggerEnabled(bool b)
{
d->DebuggerEnabled = b;
}
LVmDebugger *LVirtualMachine::OpenDebugger(LCompiledCode *Code, const char *Assembly)
{
if (d->DebuggerEnabled)
{
if (!d->Callback)
return NULL;
d->Debugger = d->Callback->AttachVm(this, Code, Assembly);
}
return d->Debugger;
}
bool LVirtualMachine::StepInstruction()
{
LExecutionStatus s = d->Run(LVirtualMachinePriv::RunStepInstruction);
return s != ScriptError;
}
bool LVirtualMachine::StepLine()
{
LExecutionStatus s = d->Run(LVirtualMachinePriv::RunStepLine);
return s != ScriptError;
}
bool LVirtualMachine::StepOut()
{
LExecutionStatus s = d->Run(LVirtualMachinePriv::RunStepOut);
return s != ScriptError;
}
bool LVirtualMachine::BreakExecution()
{
return false;
}
bool LVirtualMachine::Continue()
{
LExecutionStatus s = d->Run(LVirtualMachinePriv::RunContinue);
return s != ScriptError;
}
bool LVirtualMachine::BreakPoint(const char *File, int Line, bool Add)
{
return false;
}
bool LVirtualMachine::BreakPoint(int Addr, bool Add)
{
if (Add)
d->BreakPts.Add(Addr);
else
d->BreakPts.Delete(Addr);
return true;
}
void LVirtualMachine::SetBreakCpp(bool Brk)
{
d->BreakCpp = Brk;
}
LVirtualMachine::Context LVirtualMachine::SaveContext()
{
LVirtualMachine::Context ctx;
ctx.Callback = d->Callback;
if ((ctx.Code = d->Code) &&
ctx.Code->ByteCode.PtrCheck(d->c.u8))
{
ctx.Addr = d->c.u8 - ctx.Code->ByteCode.AddressOf();
}
return ctx;
}
LVmCallback *LVirtualMachine::GetCallback()
{
return d->Callback;
}
void LVirtualMachine::SetTempPath(const char *Path)
{
d->TempPath = Path;
}
////////////////////////////////////////////////////////////////////
/*
bool GTypeDef::GetVariant(const char *Name, LVariant &Value, char *Arr)
{
GMember *m = Members.Find(Name);
if (!m || !Object)
{
LAssert(!"No member?");
return false;
}
GPtr p;
p.i8 = Object;
p.i8 += m->Offset;
switch (m->Type)
{
case GV_INT32:
{
Value = *p.i32;
break;
}
case GV_DOUBLE:
{
Value = *p.dbl;
break;
}
case GV_STRING:
{
Value = p.i8;
break;
}
case GV_CUSTOM:
{
Value.Empty();
Value.Type = GV_CUSTOM;
Value.Value.Custom.Dom = m->Nest;
Value.Value.Custom.Data = p.i8;
break;
}
default:
{
return false;
}
}
return true;
}
bool GTypeDef::SetVariant(const char *Name, LVariant &Value, char *Arr)
{
GMember *m = Members.Find(Name);
if (!m || !Object)
{
LAssert(!"No member?");
return false;
}
GPtr p;
p.i8 = Object;
p.i8 += m->Offset;
switch (m->Type)
{
case GV_INT32:
{
*p.i32 = Value.CastInt32();
break;
}
case GV_DOUBLE:
{
*p.dbl = Value.CastDouble();
break;
}
case GV_STRING:
{
char *s = Value.CastString();
if (!s)
return false;
int i;
for (i = 0; *s && i < m->Size - 1; i++)
{
*p.i8++ = *s++;
}
if (i < m->Size - 1)
*p.i8 = 0;
break;
}
case GV_CUSTOM:
{
GTypeDef *t = dynamic_cast(Value.Value.Custom.Dom);
if (m->Nest == t)
{
memcpy(p.i8, Value.Value.Custom.Data, t->Sizeof());
}
break;
}
default:
{
return false;
}
}
return true;
}
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
uint32_t IconsData[] =
{
0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x9D9CCEBE, 0x3B166419, 0x74594357, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x543CF81F, 0xCEDE647C, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F,
0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x7C998D1B, 0xF81FB61C, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xCEBFF81F, 0x43DB4C1C, 0xDF1E955B, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F,
0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x8C0CF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x8D5CF81F, 0x43595C1A, 0x3AF74338, 0x8CFA4B57, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xC69D6C39, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0x647BADFD, 0x543C53FB, 0x3B1553FB, 0x329132B2, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81F64CB, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F,
0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x8D9FF81F, 0x8D9F8D9F, 0x855E857E, 0x7CFD7D1D, 0x74BC74DC, 0xF81F74DC, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x8BEBF81F, 0xF81F83AB, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x7CBB8D5C, 0xF81FB63D, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81F5BD8, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x959D647C, 0xCEBDF81F, 0x32913AD3, 0xB61B5353, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x3BA564CB, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x8D9FF81F, 0x74DC8D9F,
0x8D9FF81F, 0x74DC8D9F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x8D9F8D9F, 0x855E857E, 0x7CFD7D1D, 0x74BC74DC, 0xF81F74DC, 0xF81FF81F, 0xF81FF81F, 0x8D9FF81F, 0xAE1EB65E, 0xD71FA5FE, 0x853D8D7D, 0x6CBD7CFD, 0xF81F2AB5, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x8BEBF81F, 0x7329FF98, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xCE9E5C1A, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81F5398, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x00000000, 0x00000000, 0xF81F0000, 0xF81F6C7C, 0x5BD6F81F, 0xD6DD5BB5, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xB6D45CAA, 0xF81F3BA5, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x8D9FF81F, 0x2AB5D71F, 0x8D9FF81F, 0x2AB5D71F, 0xF81FF81F,
0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xD71F8D9F, 0xC6DFD71F, 0xB65FBE7F, 0x9DDEAE3F, 0xF81F2AB5, 0xF81FF81F, 0xF81FF81F, 0x857EF81F, 0x9DDEAE1E, 0xFFFFDF3F, 0x74FD853D, 0x647C6CBC, 0xF81F2274, 0xF81FF81F, 0xF81FF81F, 0x9C6CF81F, 0x944D944C, 0x944D8C0C, 0xFF54FF76, 0xF81F62A8, 0xF81FF81F, 0xF81FF81F, 0xBE5D4B99, 0x543CF81F, 0xC6BE5C7C, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81F5398, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81F53DB, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x75ED5489, 0x3BA5B6D4, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x857EF81F, 0x2274DF3F, 0x857EF81F, 0x2274DF3F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F,
0xDF3F857E, 0xB65FCEDF, 0x9DBEAE1F, 0x851B959E, 0xF81F2274, 0xF81FF81F, 0xF81FF81F, 0x855EF81F, 0xE75F9DDE, 0xFFFFFFFF, 0x6CBC74DD, 0x543C647C, 0xF81F1A33, 0xF81FF81F, 0xF81FF81F, 0x944BF81F, 0xFFD9F756, 0xFF53FF97, 0xFEEEFF31, 0x5226F66A, 0xF81FF81F, 0xF81FF81F, 0x84DA6C3A, 0xBE7EADDC, 0x43DB4C1C, 0xF81F953B, 0xF81FF81F, 0x00000000, 0x00000000, 0xF81F0000, 0xF81F4B77, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x00000000, 0x00000000, 0xF81F0000, 0x9D7C5C3B, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x75ED4C48, 0x5D0A75ED, 0xF81F3BA5, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x855EF81F, 0x1A33D71F, 0x855EF81F, 0x1A33D71F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xD71F855E, 0xA5FFBE7F, 0x855E959E,
0x6C7A7CFD, 0xF81F1A33, 0xF81FF81F, 0xF81FF81F, 0x7D1DF81F, 0xFFFFDF1E, 0xFFFFFFFF, 0xFFFFFFFF, 0x4BFCC69D, 0xF81F11F2, 0xF81FF81F, 0xF81FF81F, 0x940AF81F, 0xFF75FF97, 0xFEEEFF31, 0xF6ABFECD, 0xBCA7E5E8, 0xF81F49C5, 0xF81FF81F, 0x7CBAB61C, 0x541C53FA, 0x3B1553FB, 0x329132B2, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81F4B57, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x6C9CB63D, 0x43584BBA, 0x3AD53B16, 0x743742F4, 0xF81FF81F, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x6D8B4407, 0x3C055D0A, 0x1B212B84, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x7D1DF81F, 0x11F2CEBF, 0x7D1DF81F, 0x11F2CEBF, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xCEBF7D1D, 0x9DBEAE3F, 0x7CFD8D5E, 0x53B76CBC, 0xF81F11F2, 0xF81FF81F,
0xF81FF81F, 0x7CFDF81F, 0xCEBD853D, 0xFFFFFFFF, 0x541C5C5C, 0x43BBFFFF, 0xF81F09B1, 0xF81FF81F, 0xF81FF81F, 0x83A9F81F, 0xFF31FF74, 0xF6ACF6CF, 0xDDEAF68B, 0x41A4B467, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xC69DF81F, 0x32913AD3, 0xB5FB4B53, 0xF81FF81F, 0x00000000, 0x00000000, 0xF81F0000, 0xF81F4336, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x5D0A33E5, 0x2B843C05, 0xF81F0B00, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x7CFDF81F, 0x09B1BE9F, 0x7CFDF81F, 0x09B1BE9F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xBE9F7CFD, 0x959EA5FF, 0x6C9C7CFD, 0x4B565C3B, 0xF81F09B1, 0xF81FF81F, 0xF81FF81F, 0x74DCF81F, 0x6CBC74FD,
0xFFFFBE3B, 0x43DC541C, 0x337BFFFF, 0xF81F0990, 0xF81FF81F, 0xF81FF81F, 0x8389F81F, 0x6AA472E7, 0x72C56264, 0xB487DDA9, 0xF81F41A4, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x5BD6F81F, 0xF81F5BB5, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81F4336, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x3C052BA4, 0x0B002B84, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x74DCF81F, 0x0990AE5F, 0x74DCF81F, 0x0990AE5F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xAE5F74DC, 0x7D1D95BE, 0x5C3B6CBC, 0x43354BD9, 0xF81F0990, 0xF81FF81F, 0xF81FF81F, 0x74BCF81F, 0x647C6CBC, 0x9D7A543C, 0x3B9B43DB, 0x2B5BFFFF,
0xF81F0170, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x5A45F81F, 0x41A4AC26, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x00000000, 0x00000000, 0xF81F0000, 0xF81F5377, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x2B842363, 0xF81F0B00, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x74BCF81F, 0x0170A5FE, 0x74BCF81F, 0x0170A5FE, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xA5FE74BC, 0x6C79851A, 0x4B555BF8, 0x32D34314, 0xF81F0170, 0xF81FF81F, 0xF81FF81F, 0x74DCF81F, 0x543C5C7C, 0x43BB4BFC, 0x335B3B9B, 0x231BFFFF, 0xF81F0170, 0xF81FF81F, 0xF81FF81F,
0xF81FF81F, 0xF81FF81F, 0x49E4F81F, 0xF81F41A4, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x9D7A53B7, 0x4BBAF81F, 0xB61C6459, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x0B001B42, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x74DCF81F, 0x01700170, 0x74DCF81F, 0x01700170, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x3B1674DC, 0x2A9432F6, 0x11F22253, 0x017009B1, 0xF81F0170, 0xF81FF81F, 0xF81FF81F, 0x74DCF81F, 0x3B163B16, 0x2A9432F6, 0x11F22253, 0x017009B1, 0xF81F0170, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x41A4F81F,
0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0x747863F7, 0x953BB61C, 0x4BB843B9, 0xB61B7C98, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81F1301, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F,
0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x6C18ADBA, 0x53D953B7, 0x3B144B98, 0x32503291, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x0000F81F, 0x00000000, 0x00000000, 0x00000000, 0xF81F0000, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F,
0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xB61BF81F, 0x32503291, 0xA5794B12, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F,
0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0x5B95F81F, 0xB61A5373, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F, 0xF81FF81F,
};
LInlineBmp DbgIcons = {128, 16, 16, IconsData};
enum DbgCtrls
{
IDC_STATIC = -1,
IDC_TABS = 300,
IDC_BOX,
IDC_BOX2,
IDC_TEXT,
IDC_LOCALS,
IDC_GLOBALS,
IDC_REGISTERS,
IDC_STACK,
IDC_LOG,
IDC_RUN,
IDC_PAUSE,
IDC_STOP,
IDC_RESTART,
IDC_GOTO,
IDC_STEP_INSTR,
IDC_STEP_LINE,
IDC_STEP_OUT,
IDC_SOURCE_LST,
IDC_BREAK_POINT,
IDC_BREAK_CPP,
IDC_VARS_TBL
};
struct LScriptVmDebuggerPriv;
class LDebugView : public LTextView3
{
LScriptVmDebuggerPriv *d;
int CurLine;
int ErrorLine;
LString Error;
LArray BreakPts;
public:
LDebugView(LScriptVmDebuggerPriv *priv);
~LDebugView();
void SetError(const char *Err);
int GetCurLine() { return CurLine; }
int GetAddr();
void ScrollToCurLine();
void PourText(size_t Start, ssize_t Length) override;
void OnPaintLeftMargin(LSurface *pDC, LRect &r, LColour &colour) override;
void OnPaint(LSurface *pDC) override;
bool Breakpoint(int Addr);
};
struct LScriptVmDebuggerPriv
{
// Current script
LAutoPtr Vm;
LAutoPtr Obj;
LVmCallback *Callback = NULL;
LString Script, Assembly;
LArray Blocks;
- size_t CurrentAddr = -1;
+ ssize_t CurrentAddr = -1;
LArray LineIsAsm;
LVariant Return;
bool AcceptNotify = false;
// Ui
LView *Parent = NULL;
LBox *Main = NULL;
LBox *Sub = NULL;
LList *SourceLst = NULL;
LTabView *Tabs = NULL;
LDebugView *Text = NULL;
LList *Locals = NULL, *Globals = NULL, *Registers = NULL, *Stack = NULL;
LTextLog *Log = NULL;
LToolBar *Tools = NULL;
LTableLayout *VarsTbl = NULL;
};
LDebugView::LDebugView(LScriptVmDebuggerPriv *priv) : LTextView3(IDC_TEXT, 0, 0, 100, 100)
{
d = priv;
ErrorLine = -1;
SetWrapType(TEXTED_WRAP_NONE);
GetCss(true)->PaddingLeft(LCss::Len(LCss::LenPx, 18));
}
LDebugView::~LDebugView()
{
}
void LDebugView::SetError(const char *Err)
{
ErrorLine = CurLine;
Error = Err;
}
#define IsHexChar(c) \
( \
IsDigit(c) \
|| \
((c) >= 'a' && (c) <= 'f') \
|| \
((c) >= 'A' && (c) <= 'F') \
)
int IsAddr(char16 *Ln)
{
int Addr = 0;
for (char16 *s = Ln; *s && *s != '\n' && s < Ln + 8; s++)
{
Addr += IsHexChar(*s);
}
if (Addr != 8)
return -1;
return HtoiW(Ln);
}
int LDebugView::GetAddr()
{
ssize_t Index;
LTextLine *t = GetTextLine(Cursor, &Index);
if (!t)
return -1;
int Addr = IsAddr(Text + t->Start);
return Addr;
}
void LDebugView::ScrollToCurLine()
{
SetLine(CurLine);
}
bool LDebugView::Breakpoint(int Addr)
{
if (BreakPts.HasItem(Addr))
{
BreakPts.Delete(Addr);
Invalidate();
return false;
}
else
{
BreakPts.Add(Addr);
Invalidate();
return true;
}
}
void LDebugView::OnPaintLeftMargin(LSurface *pDC, LRect &r, LColour &colour)
{
LTextView3::OnPaintLeftMargin(pDC, r, colour);
pDC->Colour(LColour(192, 0, 0));
LFont *f = GetFont();
f->Colour(L_LOW, L_WORKSPACE);
f->Transparent(true);
int Fy = f->GetHeight();
int Start = VScroll ? (int)VScroll->Value() : 0;
int Page = (r.Y() + Fy - 1) / Fy;
int Ln = Start;
int Rad = (Fy >> 1) - 1;
int PadY = GetCss(true)->PaddingTop().ToPx(Y(), f) + ((Fy - Rad) >> 1);
auto It = Line.begin(Start);
for (auto i = *It; i && Ln <= Start + Page; i = *(++It), Ln++)
{
int OffY = (Ln - Start) * f->GetHeight();
/*
LString Num;
Num.Printf("%i", Ln);
LDisplayString Ds(f, Num);
Ds.Draw(pDC, 0, r.y1+OffY);
*/
char16 *s = Text + i->Start;
int Addr = IsAddr(s);
if (BreakPts.HasItem(Addr))
{
pDC->FilledCircle(r.x1 + Rad + 2, OffY + PadY + Rad, Rad);
}
}
f->Transparent(false);
f->Colour(L_TEXT, L_WORKSPACE);
}
void LDebugView::OnPaint(LSurface *pDC)
{
LTextView3::OnPaint(pDC);
if (Error)
{
LTextLine *Ln = Line[ErrorLine];
LFont *f = GetFont();
LRect c = GetClient();
int Pad = 3;
LDisplayString Ds(f, Error);
LRect r(0, 0, Ds.X()-1, Ds.Y()-1);
r.Inset(-Pad, -Pad);
r.Offset(c.X()-r.X(), Ln ? Ln->r.y1 - ScrollYPixel(): 0);
f->Transparent(false);
f->Colour(LColour::White, LColour::Red);
Ds.Draw(pDC, r.x1 + Pad, r.y1 + Pad, &r);
}
}
void LDebugView::PourText(size_t Start, ssize_t Len)
{
LTextView3::PourText(Start, Len);
CurLine = -1;
for (unsigned i=0; iBlocks.Length(); i++)
{
CodeBlock &b = d->Blocks[i];
for (unsigned n=0; nCurrentAddr=%i b.AsmAddr[%i,%i]=%u\n", (int)d->CurrentAddr, i, n, b.AsmAddr[n]);
if (d->CurrentAddr >= b.AsmAddr[n])
{
+ LgiTrace("b.ViewLine=%i b.SrcLines=%i n=%i\n", b.ViewLine, b.SrcLines, n);
CurLine = b.ViewLine + b.SrcLines + n - 1;
}
}
}
unsigned Idx = 0;
for (auto l: Line)
{
// char16 *t = Text + l->Start;
// char16 *e = t + l->Len;
if (CurLine == Idx)
{
l->c.Rgb(0, 0, 0);
l->Back = LColour(L_DEBUG_CURRENT_LINE);
}
else
{
bool IsAsm = Idx < d->LineIsAsm.Length() ? d->LineIsAsm[Idx] : false;
if (IsAsm)
{
l->c.Rgb(0, 0, 255);
l->Back.Rgb(0xf0, 0xf0, 0xf0);
}
}
Idx++;
}
}
LVmDebuggerWnd::LVmDebuggerWnd(LView *Parent, LVmCallback *Callback, LAutoPtr Vm, LAutoPtr Code, const char *Assembly)
{
d = new LScriptVmDebuggerPriv;
d->Parent = Parent;
d->Vm = Vm;
d->Callback = Callback;
d->Obj = Code;
if (d->Obj)
d->Script = d->Obj->GetSource();
d->Assembly = Assembly;
LRect r(0, 0, 1000, 900);
SetPos(r);
if (Parent)
MoveSameScreen(Parent);
else
MoveToCenter();
Name("Script Debugger");
if (Attach(NULL))
{
if ((Menu = new LMenu))
{
Menu->Attach(this);
LSubMenu *s = Menu->AppendSub("Debug");
s->AppendItem("Run", IDC_RUN, true, -1, "F5");
s->AppendItem("Pause", IDC_PAUSE, true, -1, NULL);
s->AppendItem("Stop", IDC_STOP, true, -1, "Ctrl+Break");
s->AppendItem("Restart", IDC_RESTART, true, -1, NULL);
s->AppendItem("Goto", IDC_GOTO, true, -1, NULL);
s->AppendSeparator();
s->AppendItem("Step Instruction", IDC_STEP_INSTR, true, -1, "F11");
s->AppendItem("Step Line", IDC_STEP_LINE, true, -1, "F10");
s->AppendItem("Step Out", IDC_STEP_OUT, true, -1, "Shift+F11");
s->AppendSeparator();
s->AppendItem("Breakpoint", IDC_BREAK_POINT, true, -1, "F9");
s->AppendItem("Break Into C++", IDC_BREAK_CPP, true, -1, "Ctrl+F9");
}
AddView(d->Tools = new LToolBar);
uint16 *Px = (uint16*) DbgIcons.Data;
LImageList *il = new LImageList(16, 16, DbgIcons.Create(*Px));
if (il)
d->Tools->SetImageList(il, 16, 16, true);
d->Tools->AppendButton("Run", IDC_RUN);
d->Tools->AppendButton("Pause", IDC_PAUSE);
d->Tools->AppendButton("Stop", IDC_STOP);
d->Tools->AppendButton("Restart", IDC_RESTART);
d->Tools->AppendButton("Goto", IDC_GOTO);
d->Tools->AppendSeparator();
d->Tools->AppendButton("Step Instruction", IDC_STEP_INSTR);
d->Tools->AppendButton("Step Line", IDC_STEP_LINE);
d->Tools->AppendButton("Step Out", IDC_STEP_OUT);
AddView(d->Main = new LBox(IDC_BOX));
d->Main->SetVertical(true);
d->Main->AddView(d->Sub = new LBox(IDC_BOX2));
d->Sub->SetVertical(false);
d->Sub->AddView(d->SourceLst = new LList(IDC_SOURCE_LST, 0, 0, 100, 100));
d->SourceLst->GetCss(true)->Width(LCss::Len("200px"));
d->SourceLst->AddColumn("Source", 200);
d->Sub->AddView(d->Text = new LDebugView(d));
d->Main->AddView(d->Tabs = new LTabView(IDC_TABS));
d->Tabs->GetCss(true)->Height(LCss::Len("250px"));
LTabPage *p = d->Tabs->Append("Variables");
p->Append(d->VarsTbl = new LTableLayout(IDC_VARS_TBL));
int x = 0, y = 0;
auto *c = d->VarsTbl->GetCell(x++, y);
c->Add(new LTextLabel(IDC_STATIC, 0, 0, -1, -1, "Globals:"));
c = d->VarsTbl->GetCell(x++, y);
c->Add(new LTextLabel(IDC_STATIC, 0, 0, -1, -1, "Locals:"));
c = d->VarsTbl->GetCell(x++, y);
c->Add(new LTextLabel(IDC_STATIC, 0, 0, -1, -1, "Registers:"));
x = 0; y++;
c = d->VarsTbl->GetCell(x++, y);
c->Add(d->Globals = new LList(IDC_GLOBALS, 0, 0, 100, 100));
d->Globals->AddColumn("Name",100);
d->Globals->AddColumn("Value",400);
c = d->VarsTbl->GetCell(x++, y);
c->Add(d->Locals = new LList(IDC_LOCALS, 0, 0, 100, 100));
d->Locals->AddColumn("Name",100);
d->Locals->AddColumn("Value",400);
c = d->VarsTbl->GetCell(x++, y);
c->Add(d->Registers = new LList(IDC_REGISTERS, 0, 0, 100, 100));
d->Registers->AddColumn("Name",100);
d->Registers->AddColumn("Value",400);
p = d->Tabs->Append("Stack");
p->Append(d->Stack = new LList(IDC_STACK, 0, 0, 100, 100));
d->Stack->SetPourLargest(true);
d->Stack->AddColumn("Address", 100);
d->Stack->AddColumn("Function", 300);
p = d->Tabs->Append("Log");
p->Append(d->Log = new LTextLog(IDC_LOG));
AttachChildren();
Visible(true);
{
char p[MAX_PATH_LEN];
LMakePath(p, sizeof(p), LGetExePath(), "../Scripts");
LDirectory dir;
LListItem *Match = NULL;
d->SourceLst->MultiSelect(false);
for (int b = dir.First(p); b; b = dir.Next())
{
if (!dir.IsDir())
{
char *n = dir.GetName();
if (stristr(n, ".script") &&
dir.Path(p, sizeof(p)))
{
LListItem *it = new LListItem;
it->SetText(dir.GetName(), 0);
it->SetText(p, 1);
if (d->Obj && d->Obj->GetFileName())
{
if (_stricmp(p, d->Obj->GetFileName()) == 0)
Match = it;
}
d->SourceLst->Insert(it);
}
}
}
if (!Match && d->Obj)
{
LListItem *it = new LListItem;
if (it)
{
it->SetText(LGetLeaf(d->Obj->GetFileName()), 0);
it->SetText(d->Obj->GetFileName(), 1);
d->SourceLst->Insert(it);
it->Select(true);
}
}
}
}
d->AcceptNotify = true;
if (d->Assembly)
SetSource(d->Assembly);
}
LVmDebuggerWnd::~LVmDebuggerWnd()
{
}
bool LVmDebuggerWnd::OnRequestClose(bool OsShuttingDown)
{
return LWindow::OnRequestClose(OsShuttingDown);
}
void LVmDebuggerWnd::Run()
{
Visible(true);
}
LStream *LVmDebuggerWnd::GetLog()
{
return d->Log;
}
void LVmDebuggerWnd::SetCode(LAutoPtr Cc)
{
d->Obj = Cc;
}
LCompiledCode *LVmDebuggerWnd::GetCode()
{
return d->Obj;
}
void LVmDebuggerWnd::SetSource(const char *Mixed)
{
#if 1
LStringPipe Glob(256);
LStringPipe Tmp(256);
d->Blocks.Length(0);
CodeBlock *Cur = &d->Blocks.New();
// Parse the mixed source
auto t = LString(Mixed).SplitDelimit("\n", -1, false);
bool InGlobals = true;
int InAsm = -1;
for (unsigned i=0; i Code
Cur->Asm.Reset(Tmp.NewStr());
Cur = &d->Blocks.New();
}
else
{
// Code -> Asm
Tmp.Empty();
}
InAsm = IsAsm;
}
Tmp.Print("%s\n", l);
if (InAsm)
{
Cur->AsmLines++;
Cur->AsmAddr.Add(htoi(l));
}
else if (!Cur->SrcLine)
{
while (*l == ' ') l++;
if (IsDigit(*l))
Cur->SrcLine = atoi(l);
}
}
if (InAsm)
Cur->Asm.Reset(Tmp.NewStr());
Tmp.Empty();
LStringPipe Txt;
auto Src = d->Script.SplitDelimit("\n", -1, false);
unsigned SrcLine = 1;
unsigned ViewLine = 1;
for (unsigned i=0; iBlocks.Length(); i++)
{
CodeBlock &b = d->Blocks[i];
if (b.SrcLine > 0)
{
while (SrcLine <= b.SrcLine)
{
char *s = Src[SrcLine-1];
Tmp.Print("%i: %s\n", SrcLine, s ? s : "");
b.SrcLines++;
SrcLine++;
}
b.Source.Reset(Tmp.NewStr());
}
if (b.Source && b.Asm)
{
b.ViewLine = ViewLine;
ViewLine += b.SrcLines + b.AsmLines;
Txt.Print("%s%s", b.Source.Get(), b.Asm.Get());
}
else if (b.Source)
{
b.ViewLine = ViewLine;
ViewLine += b.SrcLines;
Txt.Print("%s", b.Source.Get());
}
else if (b.Asm)
{
b.ViewLine = ViewLine;
ViewLine += b.AsmLines;
Txt.Print("%s", b.Asm.Get());
}
}
while (SrcLine <= Src.Length())
{
Txt.Print("%i: %s\n", SrcLine, Src[SrcLine-1].Get());
SrcLine++;
}
for (unsigned i=0; iBlocks.Length(); i++)
{
CodeBlock &b = d->Blocks[i];
int Base = b.ViewLine + b.SrcLines;
for (int n = Base; nLineIsAsm[n-1] = true;
}
LAutoString a(Txt.NewStr());
d->Text->Name(a);
#else
d->Text->Name(Mixed);
#endif
}
void LVmDebuggerWnd::UpdateVariables(LList *Lst, LVariant *Arr, ssize_t Len, char Prefix)
{
if (!d->Vm || !Lst || !Arr)
return;
List all;
Lst->GetAll(all);
LListItem *it;
for (ssize_t i=0; iVm->d->DumpVariant(&p, *v);
LAutoString a(p.NewStr());
char nm[32];
sprintf_s(nm, sizeof(nm), "%c" LPrintfSSizeT, Prefix, i);
if (i >= (ssize_t)all.Length())
{
it = new LListItem;
all.Insert(it);
Lst->Insert(it);
}
it = i < (ssize_t)all.Length() ? all[i] : NULL;
if (it)
{
it->SetText(nm, 0);
it->SetText(a, 1);
}
}
Lst->ResizeColumnsToContent();
}
void LVmDebuggerWnd::OnAddress(size_t Addr)
{
d->CurrentAddr = Addr;
if (d->Text)
{
ssize_t Sz = d->Text->Length();
d->Text->PourText(0, Sz);
d->Text->ScrollToCurLine();
d->Text->Invalidate();
}
OnNotify(d->Tabs, LNotifyValueChanged);
}
void LVmDebuggerWnd::OnError(const char *Msg)
{
if (Msg)
d->Text->SetError(Msg);
}
void LVmDebuggerWnd::OnRun(bool Running)
{
}
void LVmDebuggerWnd::LoadFile(const char *File)
{
if (!d->Vm || !d->Callback)
{
LAssert(0);
return;
}
LFile f;
if (f.Open(File, O_READ))
d->Script = f.Read();
else
d->Script.Empty();
d->Obj.Reset();
if (d->Callback->CompileScript(d->Obj, File, d->Script))
{
LCompiledCode *Code = dynamic_cast(d->Obj.Get());
if (Code)
{
d->Return.Empty();
d->Vm->d->Frames.Length(0);
LScriptArguments Args(d->Vm, &d->Return);
d->Vm->d->Setup(Code, 0, d->Log, NULL, &Args);
}
}
}
int LVmDebuggerWnd::OnCommand(int Cmd, int Event, OsView Wnd)
{
if (d->Vm &&
d->Vm->d->Vm == NULL)
{
// This happens when the original VM decides to go away and leave
// our copy of the VM as the only one left. This means we have to
// update the pointer in the VM's private data to point to us.
d->Vm->d->Vm = d->Vm;
}
switch (Cmd)
{
case IDC_RUN:
{
if (d->Vm)
d->Vm->Continue();
break;
}
case IDC_PAUSE:
{
if (d->Vm)
d->Vm->BreakExecution();
break;
}
case IDC_STOP:
{
- LAssert(!"Impl me.");
+ Quit();
break;
}
case IDC_RESTART:
{
if (d->Vm && d->Obj)
{
LCompiledCode *Code = dynamic_cast(d->Obj.Get());
if (Code)
d->Vm->Execute(Code, 0, d->Log, false);
}
break;
}
case IDC_GOTO:
{
break;
}
case IDC_STEP_INSTR:
{
if (d->Vm)
d->Vm->StepInstruction();
break;
}
case IDC_STEP_LINE:
{
if (d->Vm)
d->Vm->StepLine();
break;
}
case IDC_STEP_OUT:
{
if (d->Vm)
d->Vm->StepOut();
break;
}
case IDC_BREAK_POINT:
{
int Addr = d->Text->GetAddr();
if (Addr >= 0)
d->Vm->BreakPoint(Addr, d->Text->Breakpoint(Addr));
break;
}
case IDC_BREAK_CPP:
{
if (!d->Vm)
{
LAssert(0);
break;
}
LMenuItem *i = Menu->FindItem(IDC_BREAK_CPP);
if (!i)
{
LAssert(0);
break;
}
bool b = !i->Checked();
i->Checked(b);
d->Vm->SetBreakCpp(b);
break;
}
}
return LWindow::OnCommand(Cmd, Event, Wnd);
}
int LVmDebuggerWnd::OnNotify(LViewI *Ctrl, LNotification n)
{
if (!d->AcceptNotify)
return 0;
switch (Ctrl->GetId())
{
case IDC_TABS:
{
switch (Ctrl->Value())
{
case 0: // Variables
{
if (d->Obj)
{
UpdateVariables(d->Globals,
d->Vm->d->Scope[SCOPE_GLOBAL],
d->Obj->Globals.Length(),
'G');
}
if (d->Vm->d->Frames.Length())
{
LVirtualMachinePriv::StackFrame &frm = d->Vm->d->Frames.Last();
UpdateVariables(d->Locals,
d->Vm->d->Scope[SCOPE_LOCAL],
frm.CurrentFrameSize,
'L');
}
else d->Locals->Empty();
UpdateVariables(d->Registers,
d->Vm->d->Scope[SCOPE_REGISTER],
MAX_REGISTER,
'R');
break;
}
case 1: // Call stack
{
d->Stack->Empty();
LArray &Frames = d->Vm->d->Frames;
for (int i=(int)Frames.Length()-1; i>=0; i--)
{
LVirtualMachinePriv::StackFrame &Sf = Frames[i];
LListItem *li = new LListItem;
LString s;
s.Printf("%p/%i", Sf.ReturnIp, Sf.ReturnIp);
li->SetText(s, 0);
const char *Src = d->Vm->d->Code->AddrToSourceRef(Sf.ReturnIp);
li->SetText(Src, 1);
d->Stack->Insert(li);
}
break;
}
case 2: // Log
{
break;
}
}
break;
}
case IDC_SOURCE_LST:
{
if (n.Type == LNotifyItemSelect)
{
LListItem *it = d->SourceLst->GetSelected();
if (!it)
break;
const char *full = it->GetText(1);
if (!LFileExists(full))
break;
LoadFile(full);
}
break;
}
}
return LWindow::OnNotify(Ctrl, n);
}
LMessage::Param LVmDebuggerWnd::OnEvent(LMessage *Msg)
{
return LWindow::OnEvent(Msg);
}
diff --git a/src/common/Lgi/GuiUtils.cpp b/src/common/Lgi/GuiUtils.cpp
--- a/src/common/Lgi/GuiUtils.cpp
+++ b/src/common/Lgi/GuiUtils.cpp
@@ -1,291 +1,294 @@
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/SkinEngine.h"
#if defined(LINUX) && !defined(LGI_SDL)
#include "LgiWinManGlue.h"
#endif
//////////////////////////////////////////////////////////////////////////////////
#if !defined(LK_CONTEXTKEY)
// LK_CONTEXTKEY is the key that brings up the context menu
#if defined(WINDOWS)
#define LK_CONTEXTKEY 0x5d
#elif defined(MAC)
#define LK_CONTEXTKEY 0x6e
#elif defined(__GTK_H__)
#define LK_CONTEXTKEY GDK_KEY_Menu
#else
#define LK_CONTEXTKEY 0x5d
#warning "Check local platform def for app menu key."
#endif
#endif
LKey::LKey(int Vkey, uint32_t flags)
{
c16 = vkey = Vkey;
- Flags = flags;
- Data = 0;
- IsChar = false;
+
+ #ifdef WINDOWS
+ Data = flags;
+ #else
+ Flags = flags;
+ #endif
}
bool LKey::IsContextMenu() const
{
return !IsChar && vkey == LK_CONTEXTKEY;
}
//////////////////////////////////////////////////////////////////////////////////
LString LMouse::ToString() const
{
LString s;
s.Printf("LMouse(pos=%i,%i view=%p/%s btns=%i/%i/%i/%i/%i dwn=%i dbl=%i "
"ctrl=%i alt=%i sh=%i sys=%i)",
x, y, // pos
Target, Target?Target->GetClass():NULL, // view
Left(), Middle(), Right(), Button1(), Button2(), // btns
Down(), Double(), // dwn
Ctrl(), Alt(), Shift(), System()); // mod keys
return s;
}
bool LMouse::IsContextMenu() const
{
if (Right())
return true;
#if defined(MAC)
if (Left() && Ctrl())
return true;
#endif
return false;
}
bool LMouse::ToScreen()
{
if (ViewCoords)
{
if (!Target)
{
printf("%s:%i - ToScreen Error: Target=%p ViewCoords=%i\n",
_FL,
Target,
ViewCoords);
return false;
}
LPoint p(x, y);
Target->PointToScreen(p);
x = p.x; y = p.y;
ViewCoords = false;
}
return true;
}
bool LMouse::ToView()
{
if (!ViewCoords)
{
if (!Target)
{
printf("%s:%i - ToView Error: Target=%p ViewCoords=%i\n",
_FL,
Target,
ViewCoords);
return false;
}
LPoint p(x, y);
Target->PointToView(p);
x = p.x; y = p.y;
ViewCoords = true;
}
return true;
}
#if WINNATIVE
#if !defined(DM_POSITION)
#define DM_POSITION 0x00000020L
#endif
typedef WINUSERAPI BOOL (WINAPI *pEnumDisplayDevicesA)(PVOID, DWORD, PDISPLAY_DEVICEA, DWORD);
typedef WINUSERAPI BOOL (WINAPI *pEnumDisplayDevicesW)(PVOID, DWORD, PDISPLAY_DEVICEW, DWORD);
typedef WINUSERAPI BOOL (WINAPI *pEnumDisplaySettingsA)(LPCSTR lpszDeviceName, DWORD iModeNum, LPDEVMODEA lpDevMode);
typedef WINUSERAPI BOOL (WINAPI *pEnumDisplaySettingsW)(LPCWSTR lpszDeviceName, DWORD iModeNum, LPDEVMODEW lpDevMode);
#endif
LPointF GDisplayInfo::Scale()
{
LPointF p((double)Dpi.x / 96.0, (double)Dpi.y / 96.0);
return p;
}
bool LGetDisplays(::LArray &Displays, LRect *AllDisplays)
{
#if WINNATIVE
if (AllDisplays)
AllDisplays->ZOff(-1, -1);
LLibrary User32("User32");
DISPLAY_DEVICEW disp;
ZeroObj(disp);
disp.cb = sizeof(disp);
pEnumDisplayDevicesW EnumDisplayDevicesW = (pEnumDisplayDevicesW) User32.GetAddress("EnumDisplayDevicesW");
pEnumDisplaySettingsW EnumDisplaySettingsW = (pEnumDisplaySettingsW) User32.GetAddress("EnumDisplaySettingsW");
for (int i=0;
EnumDisplayDevicesW(0, i, &disp, 0);
i++)
{
DEVMODEW mode;
ZeroObj(mode);
mode.dmSize = sizeof(mode);
mode.dmDriverExtra = sizeof(mode);
if (EnumDisplaySettingsW(disp.DeviceName, ENUM_CURRENT_SETTINGS, &mode))
{
GDisplayInfo *Dsp = new GDisplayInfo;
if (Dsp)
{
Dsp->r.ZOff(mode.dmPelsWidth-1, mode.dmPelsHeight-1);
if (mode.dmFields & DM_POSITION)
{
Dsp->r.Offset(mode.dmPosition.x, mode.dmPosition.y);
}
if (AllDisplays)
{
if (AllDisplays->Valid())
AllDisplays->Union(&Dsp->r);
else
*AllDisplays = Dsp->r;
}
disp.cb = sizeof(disp);
Dsp->BitDepth = mode.dmBitsPerPel;
Dsp->Refresh = mode.dmDisplayFrequency;
Dsp->Name = disp.DeviceString;
Dsp->Device = disp.DeviceName;
Dsp->Dpi.x = Dsp->Dpi.y = mode.dmLogPixels;
DISPLAY_DEVICEW temp = disp;
if (EnumDisplayDevicesW(temp.DeviceName, 0, &disp, 0))
Dsp->Monitor = disp.DeviceString;
Displays.Add(Dsp);
}
}
disp.cb = sizeof(disp);
}
#elif defined __GTK_H__
Gtk::GdkDisplay *Dsp = Gtk::gdk_display_get_default();
#if GtkVer(3, 22)
int monitors = Gtk::gdk_display_get_n_monitors(Dsp);
for (int i=0; ir = geometry;
di->Device = Gtk::gdk_monitor_get_manufacturer(m);
di->Name = Gtk::gdk_monitor_get_model(m);
di->Refresh = Gtk::gdk_monitor_get_refresh_rate(m);
Displays.Add(di);
}
#endif
#elif LGI_COCOA
for (NSScreen *s in [NSScreen screens])
{
GDisplayInfo *di = new GDisplayInfo;
di->r = s.frame;
Displays.Add(di);
}
#endif
return Displays.Length() > 0;
}
void GetChildrenList(LViewI *w, List &l)
{
if (!w)
return;
for (auto v: w->IterateViews())
{
#if WINNATIVE
int Style = GetWindowLong(v->Handle(), GWL_STYLE);
if (TestFlag(Style, WS_VISIBLE) &&
!TestFlag(Style, WS_DISABLED))
{
if (TestFlag(Style, WS_TABSTOP))
{
l.Insert(v);
}
GetChildrenList(v, l);
}
#else
if (v->Visible() && v->Enabled())
{
if (v->GetTabStop())
{
l.Insert(v);
}
GetChildrenList(v, l);
}
#endif
}
}
LViewI *GetNextTabStop(LViewI *v, bool Back)
{
if (v)
{
LWindow *Wnd = v->GetWindow();
if (Wnd)
{
List All;
GetChildrenList(Wnd, All);
ssize_t MyIndex = All.IndexOf(v);
if (MyIndex >= 0)
{
int Inc = Back ? -1 : 1;
size_t NewIndex = (MyIndex + All.Length() + Inc) % All.Length();
return All.ItemAt(NewIndex);
}
else
{
return All[0];
}
}
}
return 0;
}
diff --git a/src/common/Lgi/Variant.cpp b/src/common/Lgi/Variant.cpp
--- a/src/common/Lgi/Variant.cpp
+++ b/src/common/Lgi/Variant.cpp
@@ -1,2405 +1,2406 @@
#include
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/Variant.h"
const char *LVariant::TypeToString(LVariantType t)
{
switch (t)
{
case GV_NULL: return "NULL";
case GV_INT32: return "int32";
case GV_INT64: return "int64";
case GV_BOOL: return "bool";
case GV_DOUBLE: return "double";
case GV_STRING: return "String";
case GV_BINARY: return "Binary";
case GV_LIST: return "List";
case GV_DOM: return "Dom";
case GV_DOMREF: return "DomReference";
case GV_VOID_PTR: return "VoidPtr";
case GV_DATETIME: return "DateTime";
case GV_HASHTABLE: return "HashTable";
case GV_OPERATOR: return "Operator";
case GV_CUSTOM: return "Custom";
case GV_WSTRING: return "WString";
case GV_GVIEW: return "View";
case GV_STREAM: return "Stream";
case GV_LSURFACE: return "Surface";
case GV_LMOUSE: return "MouseEvent";
case GV_LKEY: return "KeyboardEvent";
default: return "Unknown";
}
return NULL;
}
const char *LVariant::OperatorToString(LOperator op)
{
switch (op)
{
case OpNull: return "OpNull";
case OpAssign: return "OpAssign";
case OpPlus: return "OpPlus";
case OpUnaryPlus: return "OpUnaryPlus";
case OpMinus: return "OpMinus";
case OpUnaryMinus: return "OpUnaryMinus";
case OpMul: return "OpMul";
case OpDiv: return "OpDiv";
case OpMod: return "OpMod";
case OpLessThan: return "OpLessThan";
case OpLessThanEqual: return "OpLessThanEqual";
case OpGreaterThan: return "OpGreaterThan";
case OpGreaterThanEqual: return "OpGreaterThanEqual";
case OpEquals: return "OpEquals";
case OpNotEquals: return "OpNotEquals";
case OpPlusEquals: return "OpPlusEquals";
case OpMinusEquals: return "OpMinusEquals";
case OpMulEquals: return "OpMulEquals";
case OpDivEquals: return "OpDivEquals";
case OpPostInc: return "OpPostInc";
case OpPostDec: return "OpPostDec";
case OpPreInc: return "OpPreInc";
case OpPreDec: return "OpPreDec";
case OpAnd: return "OpAnd";
case OpOr: return "OpOr";
case OpNot: return "OpNot";
}
return NULL;
}
LVariant::LVariant()
{
Type = GV_NULL;
ZeroObj(Value);
}
LVariant::LVariant(LVariant const &v)
{
Type = GV_NULL;
ZeroObj(Value);
*this = v;
}
#if LVARIANT_SIZET
LVariant::LVariant(size_t i)
{
Type = GV_NULL;
*this = i;
}
#endif
#if LVARIANT_SSIZET
LVariant::LVariant(ssize_t i)
{
Type = GV_NULL;
*this = i;
}
#endif
LVariant::LVariant(int32_t i)
{
Type = GV_INT32;
Value.Int = i;
}
LVariant::LVariant(uint32_t i)
{
Type = GV_INT32;
Value.Int = i;
}
LVariant::LVariant(int64_t i)
{
Type = GV_INT64;
Value.Int64 = i;
}
LVariant::LVariant(uint64_t i)
{
Type = GV_INT64;
Value.Int64 = i;
}
LVariant::LVariant(double i)
{
Type = GV_DOUBLE;
Value.Dbl = i;
}
LVariant::LVariant(const char *s)
{
Value.String = NewStr(s);
Type = Value.String ? GV_STRING : GV_NULL;
}
LVariant::LVariant(const char16 *s)
{
Value.WString = NewStrW(s);
Type = Value.WString ? GV_WSTRING : GV_NULL;
}
LVariant::LVariant(void *p)
{
Type = GV_NULL;
*this = p;
}
LVariant::LVariant(LDom *p)
{
Type = GV_NULL;
*this = p;
}
LVariant::LVariant(LDom *p, char *name)
{
Type = GV_NULL;
SetDomRef(p, name);
}
LVariant::LVariant(const LDateTime *d)
{
Type = GV_NULL;
*this = d;
}
LVariant::LVariant(LOperator Op)
{
Type = GV_OPERATOR;
Value.Op = Op;
}
LVariant::~LVariant()
{
Empty();
}
bool LVariant::operator ==(LVariant &v)
{
switch (Type)
{
default:
case GV_NULL:
return v.Type == Type;
case GV_INT32:
return Value.Int == v.CastInt32();
case GV_INT64:
return Value.Int64 == v.CastInt64();
case GV_BOOL:
return Value.Bool == v.CastBool();
case GV_DOUBLE:
return Value.Dbl == v.CastDouble();
case GV_STRING:
{
char *s = v.Str();
if (Value.String && s)
return !strcmp(Value.String, s);
break;
}
case GV_WSTRING:
{
char16 *w = v.WStr();
if (Value.WString && w)
return !StrcmpW(Value.WString, w);
break;
}
case GV_BINARY:
{
if (v.Type == Type &&
Value.Binary.Data == v.Value.Binary.Data &&
Value.Binary.Length == v.Value.Binary.Length)
{
return true;
}
break;
}
case GV_LIST:
{
if (!Value.Lst || !v.Value.Lst)
return false;
if (Value.Lst->Length() != v.Value.Lst->Length())
return false;
auto ValIt = Value.Lst->begin();
auto VIt = v.Value.Lst->begin();
LVariant *a, *b;
while ( (a = *ValIt) && (b = *VIt) )
{
if (!(*a == *b))
return false;
ValIt++;
VIt++;
}
return true;
}
case GV_DOMREF:
{
return Value.DomRef.Dom == v.Value.DomRef.Dom &&
Value.DomRef.Name != 0 &&
v.Value.DomRef.Name != 0 &&
!stricmp(Value.DomRef.Name, v.Value.DomRef.Name);
}
case GV_DATETIME:
{
if (Value.Date && v.Value.Date)
{
return Value.Date->Compare(v.Value.Date) == 0;
}
break;
}
case GV_DOM:
return Value.Dom == v.Value.Dom;
case GV_OPERATOR:
return Value.Op == v.Value.Op;
case GV_CUSTOM:
return Value.Custom == v.Value.Custom;
case GV_LSURFACE:
return Value.Surface.Ptr == v.Value.Surface.Ptr;
case GV_GVIEW:
return Value.View == v.Value.View;
/*
case GV_GFILE:
return Value.File.Ptr == v.Value.File.Ptr;
*/
case GV_STREAM:
return Value.Stream.Ptr == v.Value.Stream.Ptr;
case GV_LMOUSE:
return Value.Mouse == v.Value.Mouse;
case GV_LKEY:
return Value.Key == v.Value.Key;
case GV_VOID_PTR:
return Value.Ptr == v.Value.Ptr;
case GV_HASHTABLE:
{
LAssert(0);
break;
}
}
return false;
}
LVariant &LVariant::operator =(const LDateTime *d)
{
Empty();
if (d)
{
Type = GV_DATETIME;
Value.Date = new LDateTime;
if (Value.Date)
{
*Value.Date = *d;
// if (Dirty) *Dirty = true;
}
}
return *this;
}
LVariant &LVariant::operator =(bool i)
{
Empty();
Type = GV_BOOL;
Value.Bool = i;
// if (Dirty) *Dirty = true;
return *this;
}
#if LVARIANT_SIZET
LVariant &LVariant::operator =(size_t i)
{
Empty();
#if LGI_64BIT
Type = GV_INT64;
Value.Int64 = i;
#else
Type = GV_INT32;
Value.Int = i;
#endif
return *this;
}
#endif
#if LVARIANT_SSIZET
LVariant &LVariant::operator =(ssize_t i)
{
Empty();
#if LGI_64BIT
Type = GV_INT64;
Value.Int64 = i;
#else
Type = GV_INT32;
Value.Int = i;
#endif
return *this;
}
#endif
LVariant &LVariant::operator =(int32 i)
{
Empty();
Type = GV_INT32;
Value.Int = i;
return *this;
}
LVariant &LVariant::operator =(uint32_t i)
{
Empty();
Type = GV_INT32;
Value.Int = i;
return *this;
}
LVariant &LVariant::operator =(int64_t i)
{
Empty();
Type = GV_INT64;
Value.Int64 = i;
return *this;
}
LVariant &LVariant::operator =(uint64_t i)
{
Empty();
Type = GV_INT64;
Value.Int64 = i;
return *this;
}
LVariant &LVariant::operator =(double i)
{
Empty();
Type = GV_DOUBLE;
Value.Dbl = i;
// if (Dirty) *Dirty = true;
return *this;
}
LVariant &LVariant::operator =(const char *s)
{
Empty();
if (s)
{
Type = GV_STRING;
Value.String = NewStr(s);
}
return *this;
}
LVariant &LVariant::operator =(const char16 *s)
{
Empty();
if (s)
{
Type = GV_WSTRING;
Value.WString = NewStrW(s);
}
// if (Dirty) *Dirty = true;
return *this;
}
LVariant &LVariant::operator =(void *p)
{
Empty();
if (p)
{
Type = GV_VOID_PTR;
Value.Ptr = p;
// if (Dirty) *Dirty = true;
}
return *this;
}
LVariant &LVariant::operator =(LDom *p)
{
Empty();
if (p)
{
Type = GV_DOM;
Value.Dom = p;
// if (Dirty) *Dirty = true;
}
return *this;
}
LVariant &LVariant::operator =(LView *p)
{
Empty();
if (p)
{
Type = GV_GVIEW;
Value.View = p;
// if (Dirty) *Dirty = true;
}
return *this;
}
LVariant &LVariant::operator =(LMouse *p)
{
Empty();
if (p)
{
Type = GV_LMOUSE;
Value.Mouse = p;
// if (Dirty) *Dirty = true;
}
return *this;
}
LVariant &LVariant::operator =(LKey *p)
{
Empty();
if (p)
{
Type = GV_LKEY;
Value.Key = p;
}
return *this;
}
LVariant &LVariant::operator =(LStream *s)
{
Empty();
if (s)
{
Type = GV_STREAM;
Value.Stream.Ptr = s;
Value.Stream.Own = false;
}
return *this;
}
LVariant &LVariant::operator =(LVariant const &i)
{
if (&i == this)
return *this;
Empty();
Type = i.Type;
switch (Type)
{
case GV_NULL:
{
break;
}
case GV_INT32:
{
Value.Int = i.Value.Int;
break;
}
case GV_BOOL:
{
Value.Bool = i.Value.Bool;
break;
}
case GV_INT64:
{
Value.Int64 = i.Value.Int64;
break;
}
case GV_DOUBLE:
{
Value.Dbl = i.Value.Dbl;
break;
}
case GV_STRING:
{
Value.String = NewStr(((LVariant&)i).Str());
break;
}
case GV_WSTRING:
{
Value.WString = NewStrW(i.Value.WString);
break;
}
case GV_BINARY:
{
SetBinary(i.Value.Binary.Length, i.Value.Binary.Data);
break;
}
case GV_LIST:
{
SetList(i.Value.Lst);
break;
}
case GV_DOM:
{
Value.Dom = i.Value.Dom;
break;
}
case GV_VOID_PTR:
case GV_GVIEW:
case GV_LMOUSE:
case GV_LKEY:
{
Value.Ptr = i.Value.Ptr;
break;
}
case GV_DATETIME:
{
if (i.Value.Date)
{
Value.Date = new LDateTime;
if (Value.Date)
{
*Value.Date = *i.Value.Date;
}
}
break;
}
case GV_HASHTABLE:
{
if ((Value.Hash = new LHash))
{
if (i.Value.Hash)
{
// const char *k;
// for (LVariant *var = i.Value.Hash->First(&k); var; var = i.Value.Hash->Next(&k))
for (auto it : *i.Value.Hash)
{
Value.Hash->Add(it.key, new LVariant(*it.value));
}
}
}
break;
}
case GV_CUSTOM:
{
Value.Custom.Data = i.Value.Custom.Data;
Value.Custom.Dom = i.Value.Custom.Dom;
break;
}
case GV_LSURFACE:
{
Value.Surface = i.Value.Surface;
if (Value.Surface.Own &&
Value.Surface.Ptr)
Value.Surface.Ptr->IncRef();
break;
}
/*
case GV_GFILE:
{
Value.File = i.Value.File;
if (Value.File.Own &&
Value.File.Ptr)
Value.File.Ptr->AddRef();
break;
}
*/
case GV_STREAM:
{
Value.Stream.Ptr = i.Value.Stream.Ptr;
Value.Stream.Own = false;
break;
}
default:
{
printf("%s:%i - Unknown variant type '%i'\n", _FL, Type);
LAssert(0);
break;
}
}
// if (Dirty) *Dirty = true;
return *this;
}
bool LVariant::SetDomRef(LDom *obj, char *name)
{
Empty();
Type = GV_DOMREF;
Value.DomRef.Dom = obj;
Value.DomRef.Name = NewStr(name);
return Value.DomRef.Name != 0;
}
bool LVariant::SetBinary(ssize_t Len, void *Data, bool Own)
{
bool Status = false;
Empty();
Type = GV_BINARY;
Value.Binary.Length = Len;
if (Own)
{
Value.Binary.Data = Data;
Status = true;
}
else
{
if ((Value.Binary.Data = new uchar[Value.Binary.Length]))
{
if (Data)
memcpy(Value.Binary.Data, Data, Value.Binary.Length);
else
memset(Value.Binary.Data, 0, Value.Binary.Length);
Status = true;
}
}
return Status;
}
List *LVariant::SetList(List *Lst)
{
Empty();
Type = GV_LIST;
if ((Value.Lst = new List) && Lst)
{
for (auto s: *Lst)
{
LVariant *New = new LVariant;
if (New)
{
*New = *s;
Value.Lst->Insert(New);
}
}
}
return Value.Lst;
}
bool LVariant::SetHashTable(LHash *Table, bool Copy)
{
Empty();
Type = GV_HASHTABLE;
if (Copy && Table)
{
if ((Value.Hash = new LHash))
{
// const char *k;
// for (LVariant *p = Table->First(&k); p; p = Table->Next(&k))
for (auto i : *Table)
{
Value.Hash->Add(i.key, i.value);
}
}
}
else
{
Value.Hash = Table ? Table : new LHash;
}
return Value.Hash != 0;
}
bool LVariant::SetSurface(class LSurface *Ptr, bool Own)
{
Empty();
if (!Ptr)
return false;
Type = GV_LSURFACE;
Value.Surface.Ptr = Ptr;
if ((Value.Surface.Own = Own))
Value.Surface.Ptr->IncRef();
return true;
}
bool LVariant::SetStream(class LStreamI *Ptr, bool Own)
{
Empty();
if (!Ptr)
return false;
Type = GV_STREAM;
Value.Stream.Ptr = Ptr;
Value.Stream.Own = Own;
return true;
}
bool LVariant::OwnStr(char *s)
{
Empty();
if (!s)
return false;
Type = GV_STRING;
Value.String = s;
return true;
}
bool LVariant::OwnStr(char16 *w)
{
Empty();
if (!w)
return false;
Type = GV_WSTRING;
Value.WString = w;
return true;
}
char *LVariant::ReleaseStr()
{
char *Ret = Str();
if (Ret)
{
Value.String = 0;
Type = GV_NULL;
}
return Ret;
}
LString LVariant::LStr()
{
return Str();
}
char *LVariant::Str()
{
if (Type == GV_STRING)
return Value.String;
if (Type == GV_WSTRING)
{
char *u = WideToUtf8(Value.WString);
DeleteArray(Value.WString);
Type = GV_STRING;
return Value.String = u;
}
return 0;
}
char16 *LVariant::ReleaseWStr()
{
char16 *Ret = WStr();
if (Ret)
{
Value.WString = 0;
Type = GV_NULL;
}
return Ret;
}
char16 *LVariant::WStr()
{
if (Type == GV_WSTRING)
return Value.WString;
if (Type == GV_STRING)
{
char16 *w = Utf8ToWide(Value.String);
DeleteArray(Value.String);
Type = GV_WSTRING;
return Value.WString = w;
}
return 0;
}
void LVariant::Empty()
{
switch (Type)
{
default:
break;
case GV_CUSTOM:
{
Value.Custom.Data = 0;
Value.Custom.Dom = 0;
break;
}
case GV_DOMREF:
{
DeleteArray(Value.DomRef.Name);
Value.DomRef.Dom = 0;
break;
}
case GV_STRING:
{
DeleteArray(Value.String);
break;
}
case GV_WSTRING:
{
DeleteArray(Value.WString);
break;
}
case GV_BINARY:
{
char *d = (char*) Value.Binary.Data;
DeleteArray(d);
Value.Binary.Data = 0;
break;
}
case GV_DATETIME:
{
DeleteObj(Value.Date);
break;
}
case GV_LIST:
{
if (Value.Lst)
{
Value.Lst->DeleteObjects();
DeleteObj(Value.Lst);
}
break;
}
case GV_HASHTABLE:
{
if (Value.Hash)
{
// for (LVariant *v = (LVariant*) Value.Hash->First(); v; v = (LVariant*) Value.Hash->Next())
for (auto i : *Value.Hash)
{
DeleteObj(i.value);
}
DeleteObj(Value.Hash);
}
break;
}
case GV_LSURFACE:
{
if (Value.Surface.Own &&
Value.Surface.Ptr)
{
Value.Surface.Ptr->DecRef();
Value.Surface.Ptr = NULL;
}
break;
}
/*
case GV_GFILE:
{
if (Value.File.Ptr &&
Value.File.Own)
{
Value.File.Ptr->DecRef();
Value.File.Ptr = NULL;
}
break;
}
*/
case GV_STREAM:
{
if (Value.Stream.Ptr)
{
if (Value.Stream.Own)
delete Value.Stream.Ptr;
Value.Stream.Ptr = NULL;
}
break;
}
}
Type = GV_NULL;
ZeroObj(Value);
}
int64 LVariant::Length()
{
switch (Type)
{
case GV_INT32:
return sizeof(Value.Int);
case GV_INT64:
return sizeof(Value.Int64);
case GV_BOOL:
return sizeof(Value.Bool);
case GV_DOUBLE:
return sizeof(Value.Dbl);
case GV_STRING:
return Value.String ? strlen(Value.String) : 0;
case GV_BINARY:
return Value.Binary.Length;
case GV_LIST:
{
int64 Sz = 0;
if (Value.Lst)
{
for (auto v : *Value.Lst)
Sz += v->Length();
}
return Sz;
}
case GV_DOM:
{
LVariant v;
if (Value.Dom)
Value.Dom->GetValue("length", v);
return v.CastInt32();
}
case GV_DOMREF:
break;
case GV_VOID_PTR:
return sizeof(Value.Ptr);
case GV_DATETIME:
return sizeof(*Value.Date);
case GV_HASHTABLE:
{
int64 Sz = 0;
if (Value.Hash)
{
// for (LVariant *v=Value.Hash->First(); v; v=Value.Hash->Next())
for (auto i : *Value.Hash)
Sz += i.value->Length();
}
return Sz;
}
case GV_OPERATOR:
return sizeof(Value.Op);
case GV_CUSTOM:
break;
case GV_WSTRING:
return Value.WString ? StrlenW(Value.WString) * sizeof(char16) : 0;
case GV_LSURFACE:
{
int64 Sz = 0;
if (Value.Surface.Ptr)
{
LRect r = Value.Surface.Ptr->Bounds();
int Bytes = Value.Surface.Ptr->GetBits() >> 3;
Sz = r.X() * r.Y() * Bytes;
}
return Sz;
}
case GV_GVIEW:
return sizeof(LView);
case GV_LMOUSE:
return sizeof(LMouse);
case GV_LKEY:
return sizeof(LKey);
case GV_STREAM:
return Value.Stream.Ptr->GetSize();
default: break;
}
return 0;
}
bool LVariant::IsInt()
{
return Type == GV_INT32 || Type == GV_INT64;
}
bool LVariant::IsBool()
{
return Type == GV_BOOL;
}
bool LVariant::IsDouble()
{
return Type == GV_DOUBLE;
}
bool LVariant::IsString()
{
return Type == GV_STRING;
}
bool LVariant::IsBinary()
{
return Type == GV_BINARY;
}
bool LVariant::IsNull()
{
return Type == GV_NULL;
}
#define IsList() (Type == GV_LIST && Value.Lst)
LVariant &LVariant::Cast(LVariantType NewType)
{
if (NewType != Type)
{
switch (NewType)
{
default:
{
// No conversion possible
break;
}
case GV_INT32:
{
*this = (int)CastInt32();
break;
}
case GV_INT64:
{
*this = (int64_t) CastInt64();
break;
}
case GV_BOOL:
{
if (Type == GV_DOUBLE)
{
*this = Value.Dbl != 0.0;
}
else
{
*this = CastInt32() != 0;
}
break;
}
case GV_DOUBLE:
{
*this = CastDouble();
break;
}
case GV_STRING:
{
*this = CastString();
break;
}
case GV_DATETIME:
{
switch (Type)
{
case GV_STRING:
{
// String -> LDateTime
LDateTime *Dt = new LDateTime;
if (Dt)
{
Dt->Set(Value.String);
Empty();
Value.Date = Dt;
Type = NewType;
}
break;
}
case GV_INT64:
{
// Int64 (system date) -> LDateTime
LDateTime *Dt = new LDateTime;
if (Dt)
{
Dt->Set((uint64_t)Value.Int64);
Empty();
Value.Date = Dt;
Type = NewType;
}
break;
}
default:
{
// No conversion available
break;
}
}
break;
}
}
}
return *this;
}
void *LVariant::CastVoidPtr() const
{
switch (Type)
{
default:
break;
case GV_STRING:
return Value.String;
case GV_BINARY:
return Value.Binary.Data;
case GV_LIST:
return Value.Lst;
case GV_DOM:
return Value.Dom;
case GV_DOMREF:
return Value.DomRef.Dom;
case GV_VOID_PTR:
return Value.Ptr;
case GV_DATETIME:
return Value.Date;
case GV_HASHTABLE:
return Value.Hash;
case GV_CUSTOM:
return Value.Custom.Data;
case GV_WSTRING:
return Value.WString;
case GV_LSURFACE:
return Value.Surface.Ptr;
case GV_GVIEW:
return Value.View;
case GV_LMOUSE:
return Value.Mouse;
case GV_LKEY:
return Value.Key;
}
return 0;
}
LDom *LVariant::CastDom() const
{
switch (Type)
{
default:
break;
case GV_DOM:
return Value.Dom;
case GV_DOMREF:
return Value.DomRef.Dom;
case GV_STREAM:
return dynamic_cast(Value.Stream.Ptr);
case GV_LSURFACE:
return Value.Surface.Ptr;
case GV_CUSTOM:
return Value.Custom.Dom;
}
return NULL;
}
bool LVariant::CastBool() const
{
switch (Type)
{
default:
LAssert(0);
break;
case GV_NULL:
return false;
case GV_INT32:
return Value.Int != 0;
case GV_INT64:
return Value.Int64 != 0;
case GV_BOOL:
return Value.Bool;
case GV_DOUBLE:
return Value.Dbl != 0.0;
case GV_BINARY:
return Value.Binary.Data != NULL;
case GV_LIST:
return Value.Lst != NULL;
case GV_DOM:
return Value.Dom != NULL;
case GV_DOMREF:
return Value.DomRef.Dom != NULL;
case GV_VOID_PTR:
return Value.Ptr != NULL;
case GV_GVIEW:
return Value.View != NULL;
case GV_LMOUSE:
return Value.Mouse != NULL;
case GV_LKEY:
return Value.Key != NULL;
case GV_DATETIME:
return Value.Date != NULL;
case GV_HASHTABLE:
return Value.Hash != NULL;
case GV_OPERATOR:
return Value.Op != OpNull;
case GV_CUSTOM:
return Value.Custom.Dom != 0 && Value.Custom.Data != 0;
/*
case GV_GFILE:
return Value.File.Ptr != NULL;
*/
case GV_STREAM:
return Value.Stream.Ptr != NULL;
// As far as I understand this is the behavour in Python, which I'm using for
// a reference to what the "correct" thing to do here is. Basically it's treating
// the string like a pointer instead of a value. If the pointer is valid the
// conversion to bool return true, and false if it's not a valid pointer. This
// means things like if (!StrinLVariant) evaluate correctly in the scripting engine
// but it means that if you want to evaluate the value of the varient you should
// use CastInt32 instead.
case GV_STRING:
return ValidStr(Value.String);
case GV_WSTRING:
return ValidStrW(Value.WString);
}
return false;
}
double LVariant::CastDouble() const
{
switch (Type)
{
default:
break;
case GV_BOOL:
return Value.Bool ? 1.0 : 0.0;
case GV_DOUBLE:
return Value.Dbl;
case GV_INT32:
return (double)Value.Int;
case GV_INT64:
return (double)Value.Int64;
case GV_STRING:
return Value.String ? atof(Value.String) : 0;
case GV_DOMREF:
{
static LVariant v;
if (Value.DomRef.Dom)
{
if (Value.DomRef.Dom->GetValue(Value.DomRef.Name, v))
{
return v.CastDouble();
}
}
break;
}
}
return 0;
}
int32 LVariant::CastInt32() const
{
switch (Type)
{
default:
break;
case GV_BOOL:
return (int32)Value.Bool;
case GV_DOUBLE:
return (int32)Value.Dbl;
case GV_INT32:
return Value.Int;
case GV_INT64:
return (int32)Value.Int64;
case GV_STRING:
if (!Value.String)
return 0;
if (IsAlpha(Value.String[0]))
return !Stricmp(Value.String, "true");
else if (Value.String[0] == '0' && tolower(Value.String[1]) == 'x')
return static_cast(Atoi(Value.String, 16));
return atoi(Value.String);
case GV_DOM:
return Value.Dom != 0;
case GV_DOMREF:
{
static LVariant v;
if (Value.DomRef.Dom)
{
if (Value.DomRef.Dom->GetValue(Value.DomRef.Name, v))
{
return v.CastInt32();
}
}
break;
}
case GV_LIST:
return Value.Lst != NULL;
case GV_HASHTABLE:
return Value.Hash != NULL;
case GV_LSURFACE:
return Value.Surface.Ptr != NULL;
case GV_GVIEW:
return Value.View != NULL;
case GV_LMOUSE:
return Value.Mouse != NULL;
case GV_LKEY:
return Value.Key != NULL;
case GV_STREAM:
return Value.Stream.Ptr != NULL;
}
return 0;
}
int64 LVariant::CastInt64() const
{
switch (Type)
{
default:
break;
case GV_BOOL:
return (int64)Value.Bool;
case GV_DOUBLE:
return (int64)Value.Dbl;
case GV_INT32:
return Value.Int;
case GV_INT64:
return Value.Int64;
case GV_STRING:
{
if (!Value.String)
return 0;
if (IsAlpha(Value.String[0]))
return !Stricmp(Value.String, "true");
else if (Value.String[0] == '0' && tolower(Value.String[1]) == 'x')
return Atoi(Value.String, 16);
return Atoi(Value.String);
}
case GV_DOMREF:
{
static LVariant v;
if (Value.DomRef.Dom)
{
if (Value.DomRef.Dom->GetValue(Value.DomRef.Name, v))
{
return v.CastInt64();
}
}
break;
}
}
return 0;
}
char *LVariant::CastString()
{
char i[40];
switch (Type)
{
case GV_LIST:
{
LStringPipe p(256);
List::I it = Value.Lst->begin();
bool First = true;
p.Print("{");
for (LVariant *v = *it; v; v = *++it)
{
if (v->Type == GV_STRING ||
v->Type == GV_WSTRING)
p.Print("%s\"%s\"", First ? "" : ", ", v->CastString());
else
p.Print("%s%s", First ? "" : ", ", v->CastString());
First = false;
}
p.Print("}");
OwnStr(p.NewStr());
return Str();
break;
}
case GV_HASHTABLE:
{
LStringPipe p(256);
p.Print("{");
bool First = true;
// const char *k;
// for (LVariant *v = Value.Hash->First(&k); v; v = Value.Hash->Next(&k))
for (auto i : *Value.Hash)
{
p.Print("%s%s = %s", First ? "" : ", ", i.key, i.value->CastString());
First = false;
}
p.Print("}");
OwnStr(p.NewStr());
return Str();
break;
}
case GV_DOMREF:
{
static LVariant v;
if (Value.DomRef.Dom)
{
if (Value.DomRef.Dom->GetValue(Value.DomRef.Name, v))
{
return v.CastString();
}
}
break;
}
case GV_INT32:
{
sprintf_s(i, sizeof(i), "%i", Value.Int);
*this = i;
return Str();
}
case GV_DOUBLE:
{
sprintf_s(i, sizeof(i), "%f", Value.Dbl);
*this = i;
return Str();
}
case GV_BOOL:
{
sprintf_s(i, sizeof(i), "%i", Value.Bool);
*this = i;
return Str();
}
case GV_INT64:
{
sprintf_s(i, sizeof(i), LPrintfInt64, Value.Int64);
*this = i;
return Str();
}
case GV_STRING:
case GV_WSTRING:
{
return Str();
}
case GV_DATETIME:
{
if (Value.Date)
{
char s[64];
Value.Date->Get(s, sizeof(s));
*this = s;
return Str();
}
break;
}
case GV_DOM:
{
sprintf_s(i, sizeof(i), "dom:%p", Value.Dom);
*this = i;
break;
}
default:
{
break;
}
}
return 0;
}
/////////////////////////////////////////////////////////////////////////////////
LDom *LDom::ResolveObject(const char *Var, LString &Name, LString &Array)
{
LDom *Object = this;
try
{
// Tokenize the string
LString::Array t;
for (auto *s = Var; s && *s; )
{
const char *e = s;
while (*e && *e != '.')
{
if (*e == '[')
{
e++;
while (*e && *e != ']')
{
if (*e == '\"' || *e == '\'')
{
char d = *e++;
while (*e && *e != d)
e++;
if (*e == d) e++;
}
else e++;
}
if (*e == ']')
e++;
}
else e++;
}
LString part = LString(s, e - s).Strip();
if (part.Length() > 0)
t.New() = part; // Store non-empty part
s = *e ? e + 1 : e;
}
// Process elements
for (int i=0; iGetVariant(Obj, v, Index))
{
if (v.Type == GV_LIST)
{
int N = atoi(Index);
LVariant *Element = v.Value.Lst->ItemAt(N);
if (Element && Element->Type == GV_DOM)
{
Object = Element->Value.Dom;
}
else
{
return NULL;
}
}
else if (v.Type == GV_DOM)
{
Object = v.Value.Dom;
}
else
{
return NULL;
}
}
else
{
return NULL;
}
}
else
{
if (Object->GetVariant(Obj, v) &&
v.Type == GV_DOM)
{
Object = v.Value.Dom;
}
else
{
return NULL;
}
}
}
}
}
catch (...)
{
LgiTrace("LDom::ResolveObject crashed: '%s'\n", Var);
return NULL;
}
return Object;
}
struct LDomPropMap
{
LHashTbl, LDomProperty> ToProp;
LHashTbl, const char *> ToString;
LDomPropMap()
{
#undef _
#define _(symbol, txt) Define(txt, symbol);
#include "lgi/common/DomFields.h"
#undef _
}
void Define(const char *s, LDomProperty p)
{
if (!s)
return;
#if defined(_DEBUG) // Check for duplicates.
auto existing_prop = ToProp.Find(s);
LAssert(existing_prop == ObjNone);
auto existing_str = ToString.Find(p);
LAssert(existing_str == NULL);
#endif
ToProp.Add(s, p);
ToString.Add(p, s);
}
} DomPropMap;
LDomProperty LStringToDomProp(const char *Str)
{
return DomPropMap.ToProp.Find(Str);
}
const char *LDomPropToString(LDomProperty Prop)
{
return DomPropMap.ToString.Find(Prop);
}
bool LDom::GetValue(const char *Var, LVariant &Value)
{
if (!Var)
return false;
if (!_OnAccess(true))
{
LgiTrace("%s:%i - Locking error\n", _FL);
LAssert(0);
return false;
}
bool Status = false;
LString Name, Arr;
LDom *Object = ResolveObject(Var, Name, Arr);
if (Object)
{
if (Name.IsEmpty())
LgiTrace("%s:%i - Warning name parse failed for '%s'\n", _FL, Var);
else
Status = Object->GetVariant(Name, Value, Arr.IsEmpty() ? NULL : Arr.Get());
}
_OnAccess(false);
return Status;
}
bool LDom::SetValue(const char *Var, LVariant &Value)
{
bool Status = false;
if (Var)
{
// LMutex *Sem = dynamic_cast(this);
if (_OnAccess(true))
{
LString Name, Arr;
LDom *Object = ResolveObject(Var, Name, Arr);
if (Object)
{
if (Name.IsEmpty())
LgiTrace("%s:%i - Warning name parse failed for '%s'\n", _FL, Var);
else
Status = Object->SetVariant(Name, Value, Arr.IsEmpty() ? NULL : Arr.Get());
}
_OnAccess(false);
}
else
{
LgiTrace("%s:%i - Locking error\n", _FL);
LAssert(0);
}
}
return Status;
}
bool LVariant::Add(LVariant *v, int Where)
{
if (!v)
{
LAssert(!"No value to insert.");
return false;
}
if (Type == GV_NULL)
SetList();
if (Type != GV_LIST)
{
LAssert(!"Not a list variant");
return false;
}
return Value.Lst->Insert(v, Where);
}
LString LVariant::ToString()
{
LString s;
switch (Type)
{
case GV_NULL:
s = "NULL";
break;
case GV_INT32:
s.Printf("(int)%i", Value.Int);
break;
case GV_INT64:
s.Printf("(int64)" LPrintfInt64, Value.Int64);
break;
case GV_BOOL:
s.Printf("(bool)%s", Value.Bool ? "true" : "false");
break;
case GV_DOUBLE:
s.Printf("(double)%f", Value.Dbl);
break;
case GV_STRING:
s.Printf("(string)\"%s\"", Value.String);
break;
case GV_BINARY:
s.Printf("(binary[%i])%p", Value.Binary.Length, Value.Binary.Data);
break;
case GV_LIST:
s.Printf("(list[%i])%p", Value.Lst?Value.Lst->Length():0, Value.Lst);
break;
case GV_DOM:
s.Printf("(dom)%p", Value.Dom);
break;
case GV_DOMREF:
s.Printf("(dom)%p.%s", Value.DomRef.Dom, Value.DomRef.Name);
break;
case GV_VOID_PTR:
s.Printf("(void*)%p", Value.Ptr);
break;
case GV_DATETIME:
{
char dt[64];
Value.Date->Get(dt, sizeof(dt));
s.Printf("(datetime)%s", dt);
break;
}
case GV_HASHTABLE:
s.Printf("(hashtbl)%p", Value.Hash);
break;
case GV_OPERATOR:
s.Printf("(operator)%s", OperatorToString(Value.Op));
break;
case GV_CUSTOM:
s.Printf("(custom.%s)%p", Value.Custom.Dom->GetName(), Value.Custom.Data);
break;
case GV_WSTRING:
s.Printf("(wstring)\"%S\"", Value.WString);
break;
case GV_LSURFACE:
s.Printf("(gsurface)%p", Value.Surface.Ptr);
break;
case GV_GVIEW:
s.Printf("(gview)%p", Value.View);
break;
case GV_LMOUSE:
s.Printf("(gmouse)%p", Value.Mouse);
break;
case GV_LKEY:
s.Printf("(gkey)%p", Value.Key);
break;
case GV_STREAM:
s.Printf("(stream)%p", Value.Stream.Ptr);
break;
default:
s = "(unknown)NULL";
break;
}
return s;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
LCustomType::LCustomType(const char *name, int pack) : FldMap(0, -1)
{
Name = name;
Pack = 1;
Size = 0;
}
LCustomType::LCustomType(const char16 *name, int pack) : FldMap(0, -1)
{
Name = name;
Pack = 1;
Size = 0;
}
LCustomType::~LCustomType()
{
Flds.DeleteObjects();
Methods.DeleteObjects();
}
size_t LCustomType::Sizeof()
{
return (size_t)PadSize();
}
ssize_t LCustomType::PadSize()
{
if (Pack > 1)
{
// Bump size to the pack boundary...
int Remain = Size % Pack;
if (Remain)
return Size + Pack - Remain;
}
return Size;
}
int LCustomType::IndexOf(const char *Field)
{
return FldMap.Find(Field);
}
int LCustomType::AddressOf(const char *Field)
{
if (!Field)
return -1;
for (unsigned i=0; iName, Field))
return (int)i;
}
return -1;
}
bool LCustomType::DefineField(const char *Name, LCustomType *Type, int ArrayLen)
{
if (ArrayLen < 1)
{
LAssert(!"Can't have zero size field.");
return false;
}
if (Name == NULL || Type == NULL)
{
LAssert(!"Invalid parameter.");
return false;
}
if (FldMap.Find(Name) >= 0)
{
LAssert(!"Field already exists.");
return false;
}
FldMap.Add(Name, (int)Flds.Length());
CustomField *Def;
Flds.Add(Def = new CustomField);
Size = PadSize();
Def->Offset = Size;
Def->Name = Name;
Def->Type = GV_CUSTOM;
Def->Bytes = Type->Sizeof();
Def->ArrayLen = ArrayLen;
Size += Def->Bytes * ArrayLen;
return true;
}
bool LCustomType::DefineField(const char *Name, LVariantType Type, int Bytes, int ArrayLen)
{
if (ArrayLen < 1)
{
LAssert(!"Can't have zero size field.");
return false;
}
if (Name == NULL)
{
LAssert(!"No field name.");
return false;
}
if (FldMap.Find(Name) >= 0)
{
LAssert(!"Field already exists.");
return false;
}
FldMap.Add(Name, (int)Flds.Length());
CustomField *Def;
Flds.Add(Def = new CustomField);
Size = PadSize();
Def->Offset = Size;
Def->Name = Name;
Def->Type = Type;
Def->Bytes = Bytes;
Def->ArrayLen = ArrayLen;
Size += Bytes * ArrayLen;
return true;
}
LCustomType::Method *LCustomType::GetMethod(const char *Name)
{
return MethodMap.Find(Name);
}
LCustomType::Method *LCustomType::DefineMethod(const char *Name, LArray &Params, size_t Address)
{
Method *m = MethodMap.Find(Name);
if (m)
{
LAssert(!"Method already defined.");
return NULL;
}
Methods.Add(m = new Method);
m->Name = Name;
m->Params = Params;
m->Address = Address;
MethodMap.Add(Name, m);
return m;
}
bool LCustomType::CustomField::GetVariant(const char *Field, LVariant &Value, const char *Array)
{
LDomProperty p = LStringToDomProp(Field);
switch (p)
{
case ObjName: // Type: String
Value = Name;
break;
case ObjLength: // Type: Int32
Value = Bytes;
break;
default:
return false;
}
return true;
}
ssize_t LCustomType::CustomField::Sizeof()
{
switch (Type)
{
case GV_INT32:
return sizeof(int32);
case GV_INT64:
return sizeof(int64);
case GV_BOOL:
return sizeof(bool);
case GV_DOUBLE:
return sizeof(double);
case GV_STRING:
return sizeof(char);
case GV_DATETIME:
return sizeof(LDateTime);
case GV_HASHTABLE:
return sizeof(LVariant::LHash);
case GV_OPERATOR:
return sizeof(LOperator);
case GV_LMOUSE:
return sizeof(LMouse);
case GV_LKEY:
return sizeof(LKey);
case GV_CUSTOM:
return Nested->Sizeof();
default:
LAssert(!"Unknown type.");
break;
}
return 0;
}
bool LCustomType::Get(int Index, LVariant &Out, uint8_t *This, int ArrayIndex)
{
if (Index < 0 ||
Index >= Flds.Length() ||
!This)
{
LAssert(!"Invalid parameter error.");
return false;
}
CustomField *Def = Flds[Index];
if (ArrayIndex < 0 || ArrayIndex >= Def->ArrayLen)
{
LAssert(!"Array out of bounds.");
return false;
}
uint8_t *Ptr = This + Def->Offset;
Out.Empty();
switch (Def->Type)
{
case GV_STRING:
{
int Len;
for (Len = 0; Ptr[Len] && Len < Def->ArrayLen-1; Len++)
;
Out.OwnStr(NewStr((char*)Ptr, Len));
break;
}
case GV_WSTRING:
{
char16 *p = (char16*)Ptr;
int Len;
for (Len = 0; p[Len] && Len < Def->ArrayLen-1; Len++)
;
Out.OwnStr(NewStrW(p, Len));
break;
}
case GV_INT32:
case GV_INT64:
{
switch (Def->Bytes)
{
case 1:
{
Out.Value.Int = Ptr[ArrayIndex];
Out.Type = GV_INT32;
break;
}
case 2:
{
Out.Value.Int = ((uint16*)Ptr)[ArrayIndex];
Out.Type = GV_INT32;
break;
}
case 4:
{
Out.Value.Int = ((uint32_t*)Ptr)[ArrayIndex];
Out.Type = GV_INT32;
break;
}
case 8:
{
Out.Value.Int64 = ((uint64*)Ptr)[ArrayIndex];
Out.Type = GV_INT64;
break;
}
default:
{
LAssert(!"Unknown integer size.");
return false;
}
}
break;
}
case GV_MAX:
{
Out = *((LVariant*)Ptr);
break;
}
default:
{
LAssert(!"Impl this type.");
return false;
}
}
return true;
}
bool LCustomType::Set(int Index, LVariant &In, uint8_t *This, int ArrayIndex)
{
if (Index < 0 ||
Index >= Flds.Length() ||
!This)
{
LAssert(!"Invalid parameter error.");
return false;
}
CustomField *Def = Flds[Index];
uint8_t *Ptr = This + Def->Offset;
if (ArrayIndex < 0 || ArrayIndex >= Def->ArrayLen)
{
LAssert(!"Array out of bounds.");
return false;
}
switch (Def->Type)
{
case GV_STRING:
{
char *s = In.Str();
if (!s)
{
*Ptr = 0;
break;
}
if (Def->Bytes == 1)
{
// Straight up string copy...
if (s)
strcpy_s((char*)Ptr, Def->ArrayLen, s);
else
*Ptr = 0;
}
else if (Def->Bytes == sizeof(char16))
{
// utf8 -> wide conversion...
const void *In = Ptr;
ssize_t Len = strlen(s);
ssize_t Ch = LBufConvertCp(Ptr, LGI_WideCharset, Def->ArrayLen-1, In, "utf-8", Len);
if (Ch >= 0)
{
// Null terminate
Ptr[Ch] = 0;
}
else
{
LAssert(!"LBufConvertCp failed.");
return false;
}
}
break;
}
case GV_WSTRING:
{
char16 *p = (char16*)Ptr;
char16 *w = In.WStr();
if (!w)
{
*p = 0;
break;
}
if (Def->Bytes == sizeof(char16))
{
// Straight string copy...
Strcpy(p, Def->ArrayLen, w);
}
else
{
// Conversion to utf-8
const void *In = Ptr;
ssize_t Len = StrlenW(w) * sizeof(char16);
ssize_t Ch = LBufConvertCp(Ptr, "utf-8", Def->ArrayLen-sizeof(char16),
In, LGI_WideCharset, Len);
if (Ch >= 0)
{
// Null terminate
p[Ch/sizeof(char16)] = 0;
}
else
{
LAssert(!"LBufConvertCp failed.");
return false;
}
}
break;
}
case GV_INT32:
case GV_INT64:
{
switch (Def->Bytes)
{
case 1:
{
Ptr[ArrayIndex] = In.CastInt32();
break;
}
case 2:
{
((uint16*)Ptr)[ArrayIndex] = In.CastInt32();
break;
}
case 4:
{
((uint32_t*)Ptr)[ArrayIndex] = In.CastInt32();
break;
}
case 8:
{
((uint64*)Ptr)[ArrayIndex] = In.CastInt64();
break;
}
default:
{
LAssert(!"Unknown integer size.");
return false;
}
}
break;
}
case GV_MAX:
{
*((LVariant*)Ptr) = In;
break;
}
default:
LAssert(!"Impl this type.");
break;
}
return true;
}
bool LCustomType::GetVariant(const char *Field, LVariant &Value, const char *Array)
{
LDomProperty p = LStringToDomProp(Field);
switch (p)
{
case ObjName: // Type: String
{
Value = Name;
return true;
}
case ObjType: // Type: String
{
Value = "LCustomType";
return true;
}
case ObjLength: // Type: Int32
{
Value = (int)Sizeof();
return true;
}
case ObjField: // Type: CustomField[]
{
if (Array)
{
int Index = atoi(Array);
if (Index >= 0 && Index < Flds.Length())
{
Value = (LDom*)&Flds[Index];
return true;
}
}
else
{
Value = (int)Flds.Length();
break;
}
break;
}
default:
break;
}
LAssert(0);
return false;
}
bool LCustomType::SetVariant(const char *Name, LVariant &Value, const char *Array)
{
LAssert(0);
return false;
}
bool LCustomType::CallMethod(const char *MethodName, LScriptArguments &Args)
{
if (!MethodName)
return false;
if (!_stricmp(MethodName, "New"))
{
Args.GetReturn()->Empty();
Args.GetReturn()->Type = GV_CUSTOM;
Args.GetReturn()->Value.Custom.Dom = this;
Args.GetReturn()->Value.Custom.Data = new uint8_t[Sizeof()];
return true;
}
if (!_stricmp(MethodName, "Delete")) // Type: (Object)
{
for (unsigned i=0; iType == GV_CUSTOM)
{
DeleteArray(v->Value.Custom.Data);
v->Empty();
}
}
return true;
}
LAssert(0);
return false;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
LStream LScriptArguments::NullConsole;
-LScriptArguments::LScriptArguments(LVirtualMachineI *vm, LVariant *ret, LStream *console)
+LScriptArguments::LScriptArguments(LVirtualMachineI *vm, LVariant *ret, LStream *console, ssize_t address)
{
Vm = vm;
+ Address = address;
if (ret)
Return = ret;
else
Return = LocalReturn = new LVariant;
if (console)
Console = console;
else
Console = &NullConsole;
}
LScriptArguments::~LScriptArguments()
{
DeleteObjects();
DeleteObj(LocalReturn);
}
const char *LScriptArguments::StringAt(size_t i)
{
return IdxCheck(i) ? (*this)[i]->Str() : NULL;
}
int32_t LScriptArguments::Int32At(size_t i, int32_t Default)
{
return IdxCheck(i) ? (*this)[i]->CastInt32() : Default;
}
int64_t LScriptArguments::Int64At(size_t i, int64_t Default)
{
return IdxCheck(i) ? (*this)[i]->CastInt64() : Default;
}
double LScriptArguments::DoubleAt(size_t i, double Default)
{
return IdxCheck(i) ? (*this)[i]->CastDouble() : Default;
}
bool LScriptArguments::Throw(const char *file, int line, const char *Msg, ...)
{
if (!Vm)
return false;
File = file;
Line = line;
va_list Arg;
va_start(Arg, Msg);
ExceptionMsg.Printf(Arg, Msg);
va_end(Arg);
- Vm->OnException(File, Line, -1, ExceptionMsg);
+ Vm->OnException(File, Line, Address, ExceptionMsg);
return true;
}
diff --git a/src/win/Lgi/View.cpp b/src/win/Lgi/View.cpp
--- a/src/win/Lgi/View.cpp
+++ b/src/win/Lgi/View.cpp
@@ -1,2195 +1,2155 @@
/*hdr
** FILE: LView.cpp
** AUTHOR: Matthew Allen
** DATE: 23/4/98
** DESCRIPTION: Win32 LView Implementation
**
** Copyright (C) 1998-2003, Matthew Allen
** fret@memecode.com
*/
#include
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/Base64.h"
#include "lgi/common/Com.h"
#include "lgi/common/DragAndDrop.h"
#include "lgi/common/DropFiles.h"
#include "lgi/common/GdiLeak.h"
#include "lgi/common/Css.h"
#include "lgi/common/Edit.h"
#include "lgi/common/LgiRes.h"
#include "lgi/common/Menu.h"
#include "lgi/common/Thread.h"
#include "ViewPriv.h"
#define DEBUG_MOUSE_CLICKS 0
#define DEBUG_OVER 0
#define OLD_WM_CHAR_MODE 1
////////////////////////////////////////////////////////////////////////////////////////////////////
bool In_SetWindowPos = false;
HWND LViewPrivate::hPrevCapture = 0;
LViewPrivate::LViewPrivate(LView *view) : View(view)
{
WndStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN;
IsThemed = LResources::DefaultColours;
}
LViewPrivate::~LViewPrivate()
{
while (EventTargets.Length())
delete EventTargets[0];
if (hTheme)
{
CloseThemeData(hTheme);
hTheme = NULL;
}
if (FontOwnType == GV_FontOwned)
{
DeleteObj(Font);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Helper Stuff
#include "zmouse.h"
int MouseRollMsg = 0;
#ifdef __GNUC__
#define MSH_WHEELMODULE_CLASS "MouseZ"
#define MSH_WHEELMODULE_TITLE "Magellan MSWHEEL"
#define MSH_SCROLL_LINES "MSH_SCROLL_LINES_MSG"
#endif
int _lgi_mouse_wheel_lines()
{
UINT nScrollLines;
if (SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, (PVOID) &nScrollLines, 0))
return nScrollLines;
return 3;
}
#define SetKeyFlag(v, k, f) if (GetKeyState(k)&0xFF00) { v |= f; }
int _lgi_get_key_flags()
{
int Flags = 0;
if (LGetOs() == LGI_OS_WIN9X)
{
SetKeyFlag(Flags, VK_MENU, LGI_EF_ALT);
SetKeyFlag(Flags, VK_SHIFT, LGI_EF_SHIFT);
SetKeyFlag(Flags, VK_CONTROL, LGI_EF_CTRL);
}
else // is NT/2K/XP
{
SetKeyFlag(Flags, VK_LMENU, LGI_EF_LALT);
SetKeyFlag(Flags, VK_RMENU, LGI_EF_RALT);
SetKeyFlag(Flags, VK_LSHIFT, LGI_EF_LSHIFT);
SetKeyFlag(Flags, VK_RSHIFT, LGI_EF_RSHIFT);
SetKeyFlag(Flags, VK_LCONTROL, LGI_EF_LCTRL);
SetKeyFlag(Flags, VK_RCONTROL, LGI_EF_RCTRL);
}
if (GetKeyState(VK_CAPITAL))
SetFlag(Flags, LGI_EF_CAPS_LOCK);
return Flags;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
int GetInputACP()
{
char16 Str[16];
LCID Lcid = (NativeInt)GetKeyboardLayout(GetCurrentThreadId()) & 0xffff;
GetLocaleInfo(Lcid, LOCALE_IDEFAULTANSICODEPAGE, Str, sizeof(Str));
return _wtoi(Str);
}
-LKey::LKey(int v, uint32_t flags)
-{
- const char *Cp = 0;
-
- vkey = v;
- Data = flags;
- c16 = 0;
-
- #if OLD_WM_CHAR_MODE
-
- c16 = vkey;
-
- #else
-
- typedef int (WINAPI *p_ToUnicode)(UINT, UINT, PBYTE, LPWSTR, int, UINT);
-
- static bool First = true;
- static p_ToUnicode ToUnicode = 0;
-
- if (First)
- {
- ToUnicode = (p_ToUnicode) GetProcAddress(LoadLibrary("User32.dll"), "ToUnicode");
- First = false;
- }
-
- if (ToUnicode)
- {
- BYTE state[256];
- GetKeyboardState(state);
- char16 w[4];
- int r = ToUnicode(vkey, flags & 0x7f, state, w, CountOf(w), 0);
- if (r == 1)
- {
- c16 = w[0];
- }
- }
-
- #endif
-}
-
////////////////////////////////////////////////////////////////////////////////////////////////////
template
bool CastHwnd(T *&Ptr, HWND hWnd)
{
#if _MSC_VER >= _MSC_VER_VS2005
LONG_PTR user = GetWindowLongPtr(hWnd, GWLP_USERDATA);
#else
LONG user = GetWindowLong(hWnd, GWL_USERDATA);
#endif
LONG magic = GetWindowLong(hWnd, GWL_LGI_MAGIC);
if (magic != LGI_GViewMagic)
{
TCHAR ClsName[256] = {0};
int Ch = GetClassName(hWnd, ClsName, CountOf(ClsName));
LString Cls = ClsName;
// LgiTrace("%s:%i - Error: hWnd=%p/%s, GWL_LGI_MAGIC=%i\n", _FL, hWnd, Cls.Get(), magic);
return false;
}
Ptr = dynamic_cast((LViewI*)user);
return Ptr != NULL;
}
bool SetLgiMagic(HWND hWnd)
{
SetLastError(0);
LONG res = SetWindowLong(hWnd, GWL_LGI_MAGIC, LGI_GViewMagic);
bool Status = res != 0;
if (!Status)
{
DWORD err = GetLastError();
Status = err == 0;
}
LONG v = GetWindowLong(hWnd, GWL_LGI_MAGIC);
// LgiTrace("set LGI_GViewMagic for %p, %i, %i\n", hWnd, Status, v);
return Status;
}
LRESULT CALLBACK LWindowsClass::Redir(HWND hWnd, UINT m, WPARAM a, LPARAM b)
{
if (m == WM_NCCREATE)
{
LPCREATESTRUCT Info = (LPCREATESTRUCT) b;
LViewI *ViewI = (LViewI*) Info->lpCreateParams;
if (ViewI)
{
LView *View = ViewI->GetGView();
if (View) View->_View = hWnd;
#if _MSC_VER >= _MSC_VER_VS2005
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)ViewI);
#else
SetWindowLong(hWnd, GWL_USERDATA, (LONG)ViewI);
#endif
SetLgiMagic(hWnd);
}
}
LViewI *Wnd = (LViewI*)
#if _MSC_VER >= _MSC_VER_VS2005
GetWindowLongPtr(hWnd, GWLP_USERDATA);
#else
GetWindowLong(hWnd, GWL_USERDATA);
#endif
if (Wnd)
{
LMessage Msg(m, a, b);
Msg.hWnd = hWnd;
return Wnd->OnEvent(&Msg);
}
return DefWindowProcW(hWnd, m, a, b);
}
LRESULT CALLBACK LWindowsClass::SubClassRedir(HWND hWnd, UINT m, WPARAM a, LPARAM b)
{
if (m == WM_NCCREATE)
{
LPCREATESTRUCT Info = (LPCREATESTRUCT) b;
LViewI *ViewI = 0;
if (Info->lpCreateParams)
{
if (ViewI = (LViewI*) Info->lpCreateParams)
{
LView *View = ViewI->GetGView();
if (View)
View->_View = hWnd;
}
}
#if _MSC_VER >= _MSC_VER_VS2005
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) ViewI);
#else
SetWindowLong(hWnd, GWL_USERDATA, (LONG) ViewI);
#endif
SetLgiMagic(hWnd);
}
LViewI *Wnd = (LViewI*)
#if _MSC_VER >= _MSC_VER_VS2005
GetWindowLongPtr(hWnd, GWLP_USERDATA);
#else
GetWindowLong(hWnd, GWL_USERDATA);
#endif
if (Wnd)
{
LMessage Msg(m, a, b);
Msg.hWnd = hWnd;
LMessage::Result Status = Wnd->OnEvent(&Msg);
return Status;
}
return DefWindowProcW(hWnd, m, a, b);
}
LWindowsClass::LWindowsClass(const char *name)
{
Name(name);
ZeroObj(Class);
Class.lpfnWndProc = (WNDPROC) Redir;
Class.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
Class.cbWndExtra = GWL_EXTRA_BYTES;
Class.cbSize = sizeof(Class);
ParentProc = 0;
}
LWindowsClass::~LWindowsClass()
{
UnregisterClassW(NameW(), LProcessInst());
Class.lpszClassName = NULL;
}
LWindowsClass *LWindowsClass::Create(const char *ClassName)
{
if (!LAppInst)
return NULL;
LApp::ClassContainer *Classes = LAppInst->GetClasses();
if (!Classes)
return NULL;
LWindowsClass *c = Classes->Find(ClassName);
if (!c)
{
c = new LWindowsClass(ClassName);
if (c)
Classes->Add(ClassName, c);
}
return c;
}
bool LWindowsClass::IsSystem(const char *Cls)
{
if (!_stricmp(Cls, WC_BUTTONA) ||
!_stricmp(Cls, WC_COMBOBOXA) ||
!_stricmp(Cls, WC_STATICA)||
!_stricmp(Cls, WC_LISTBOXA)||
!_stricmp(Cls, WC_SCROLLBARA)||
!_stricmp(Cls, WC_HEADERA)||
!_stricmp(Cls, WC_LISTVIEWA)||
!_stricmp(Cls, WC_TREEVIEWA)||
!_stricmp(Cls, WC_COMBOBOXEXA)||
!_stricmp(Cls, WC_TABCONTROLA)||
!_stricmp(Cls, WC_IPADDRESSA)||
!_stricmp(Cls, WC_EDITA))
{
return true;
}
return false;
}
bool LWindowsClass::Register()
{
bool Status = false;
if (IsSystem(Name()))
{
ZeroObj(Class);
Class.cbSize = sizeof(Class);
Status = GetClassInfoExW(LProcessInst(), NameW(), &Class) != 0;
LAssert(Status);
}
else // if (!Class.lpszClassName)
{
Class.hInstance = LProcessInst();
if (!Class.lpszClassName)
Class.lpszClassName = NameW();
Status = RegisterClassExW(&Class) != 0;
if (!Status)
{
auto err = GetLastError();
if (err == 1410)
Status = true;
else
LAssert(Status);
}
}
return Status;
}
bool LWindowsClass::SubClass(char *Parent)
{
bool Status = false;
if (!Class.lpszClassName)
{
HBRUSH hBr = Class.hbrBackground;
LAutoWString p(Utf8ToWide(Parent));
if (p)
{
if (GetClassInfoExW(LProcessInst(), p, &Class))
{
ParentProc = Class.lpfnWndProc;
if (hBr)
{
Class.hbrBackground = hBr;
}
Class.cbWndExtra = max(Class.cbWndExtra, GWL_EXTRA_BYTES);
Class.hInstance = LProcessInst();
Class.lpfnWndProc = (WNDPROC) SubClassRedir;
Class.lpszClassName = NameW();
Status = RegisterClassExW(&Class) != 0;
LAssert(Status);
}
}
}
else Status = true;
return Status;
}
LRESULT CALLBACK LWindowsClass::CallParent(HWND hWnd, UINT m, WPARAM a, LPARAM b)
{
if (!ParentProc)
return 0;
if (IsWindowUnicode(hWnd))
{
return CallWindowProcW(ParentProc, hWnd, m, a, b);
}
else
{
return CallWindowProcA(ParentProc, hWnd, m, a, b);
}
}
//////////////////////////////////////////////////////////////////////////////
LViewI *LWindowFromHandle(HWND hWnd)
{
if (hWnd)
{
SetLastError(0);
int32 m = GetWindowLong(hWnd, GWL_LGI_MAGIC);
#if 0 //def _DEBUG
DWORD err = GetLastError();
if (err == 1413)
{
TCHAR name[256];
if (GetClassName(hWnd, name, sizeof(name)))
{
WNDCLASSEX cls;
ZeroObj(cls);
cls.cbSize = sizeof(WNDCLASSEX);
if (GetClassInfoEx(LAppInst->GetInstance(), name, &cls))
{
if (cls.cbWndExtra >= 8)
{
LAssert(!"Really?");
}
}
}
}
#endif
if (m == LGI_GViewMagic)
{
return (LViewI*)
#if _MSC_VER >= _MSC_VER_VS2005
GetWindowLongPtr(hWnd, GWLP_USERDATA);
#else
GetWindowLong(hWnd, GWL_USERDATA);
#endif
}
}
return 0;
}
//////////////////////////////////////////////////////////////////////////////
const char *LView::GetClass()
{
return "LView";
}
void LView::_Delete()
{
if (_View && d->DropTarget)
{
RevokeDragDrop(_View);
}
#ifdef _DEBUG
// Sanity check..
// LArray HasView;
for (auto c: Children)
{
auto par = c->GetParent();
bool ok = ((LViewI*)par) == this || par == NULL;
if (!ok)
LAssert(!"heirachy error");
}
#endif
// Delete myself out of my parent's list
if (d->Parent)
{
d->Parent->OnChildrenChanged(this, false);
d->Parent->DelView(this);
d->Parent = 0;
d->ParentI = 0;
}
// Delete all children
LViewI *c;
while (c = Children[0])
{
// If it has no parent, remove the pointer from the child list,
// Because the child isn't going to do it...
if (c->GetParent() == 0)
Children.Delete(c);
// Delete the child view
DeleteObj(c);
}
// Delete the OS representation of myself
if (_View && IsWindow(_View))
{
WndFlags |= GWF_DESTRUCTOR;
BOOL Status = DestroyWindow(_View);
LAssert(Status != 0);
}
// NULL my handles and flags
_View = 0;
WndFlags = 0;
// Remove static references to myself
if (_Over == this) _Over = 0;
if (_Capturing == this)
{
#if DEBUG_CAPTURE
LgiTrace("%s:%i - _Capturing %p/%s -> NULL\n",
_FL, this, GetClass());
#endif
_Capturing = 0;
}
LWindow *Wnd = GetWindow();
if (Wnd)
Wnd->SetFocus(this, LWindow::ViewDelete);
// this should only exist in an ex-LWindow, due to the way
// C++ deletes objects it needs to be here.
DeleteObj(_Lock);
}
void LView::Quit(bool DontDelete)
{
if (_View)
{
if (!DontDelete)
{
WndFlags |= GWF_QUIT_WND;
}
DestroyWindow(_View);
}
}
uint32_t LView::GetDlgCode()
{
return d->WndDlgCode;
}
void LView::SetDlgCode(uint32_t i)
{
d->WndDlgCode = i;
}
uint32_t LView::GetStyle()
{
return d->WndStyle;
}
void LView::SetStyle(uint32_t i)
{
d->WndStyle = i;
}
uint32_t LView::GetExStyle()
{
return d->WndExStyle;
}
void LView::SetExStyle(uint32_t i)
{
d->WndExStyle = i;
}
const char *LView::GetClassW32()
{
return d->WndClass;
}
void LView::SetClassW32(const char *c)
{
d->WndClass = c;
}
LWindowsClass *LView::CreateClassW32(const char *Class, HICON Icon, int AddStyles)
{
if (Class)
{
SetClassW32(Class);
}
if (GetClassW32())
{
LWindowsClass *c = LWindowsClass::Create(GetClassW32());
if (c)
{
if (Icon)
{
c->Class.hIcon = Icon;
}
if (AddStyles)
{
c->Class.style |= AddStyles;
}
c->Register();
return c;
}
}
return 0;
}
bool LView::IsAttached()
{
return _View && IsWindow(_View);
}
bool LView::Attach(LViewI *p)
{
bool Status = false;
SetParent(p);
LView *Parent = d->GetParent();
auto IsWnd = dynamic_cast(this);
if (Parent && !_Window)
_Window = Parent->_Window;
const char *ClsName = GetClassW32();
if (!ClsName)
ClsName = GetClass();
if (ClsName)
{
// Real window with HWND
bool Enab = Enabled();
// Check the class is created
bool IsSystemClass = LWindowsClass::IsSystem(ClsName);
LWindowsClass *Cls = LWindowsClass::Create(ClsName);
if (Cls)
{
auto r = Cls->Register();
if (!r)
{
LAssert(0);
}
}
else if (!IsSystemClass)
return false;
if (!IsWnd)
LAssert(!Parent || Parent->Handle() != 0);
DWORD Style = GetStyle();
DWORD ExStyle = GetExStyle() & ~WS_EX_CONTROLPARENT;
if (!TestFlag(WndFlags, GWF_SYS_BORDER))
ExStyle &= ~(WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE);
auto Text = LBase::NameW();
LAutoWString WCls(Utf8ToWide(ClsName));
bool hasCaption = (GetStyle() & WS_CAPTION) != 0;
int Shadow = WINDOWS_SHADOW_AMOUNT;
/*
LgiTrace("%p/%s::Attach %s\n", this, Name(), Pos.GetStr());
LRect r = Pos;
r.x2 += Shadow;
r.y2 += Shadow;
LgiTrace(" %s\n", r.GetStr());
*/
_View = CreateWindowExW(ExStyle,
WCls,
Text,
Style,
Pos.x1,
Pos.y1,
Pos.X(),
Pos.Y(),
Parent ? Parent->Handle() : 0,
NULL,
LProcessInst(),
(LViewI*) this);
#ifdef _DEBUG
if (!_View)
{
DWORD e = GetLastError();
LgiTrace("%s:%i - CreateWindowExW failed with 0x%x\n", _FL, e);
LAssert(!"CreateWindowEx failed");
}
#endif
if (_View)
{
Status = (_View != NULL);
if (d->Font)
SendMessage(_View, WM_SETFONT, (WPARAM) d->Font->Handle(), 0);
if (d->DropTarget)
RegisterDragDrop(_View, d->DropTarget);
if (TestFlag(WndFlags, GWF_FOCUS))
SetFocus(_View);
if (d->WantsPulse > 0)
{
SetPulse(d->WantsPulse);
d->WantsPulse = -1;
}
}
OnAttach();
}
else
{
// Virtual window (no HWND)
Status = true;
}
if (Status && d->Parent)
{
auto isWnd = dynamic_cast(this);
if (!isWnd)
{
if (!d->Parent->HasView(this))
d->Parent->AddView(this);
d->Parent->OnChildrenChanged(this, true);
}
}
return Status;
}
bool LView::Detach()
{
bool Status = false;
if (_Window)
{
LWindow *Wnd = dynamic_cast(_Window);
if (Wnd)
Wnd->SetFocus(this, LWindow::ViewDelete);
_Window = NULL;
}
if (d->Parent)
{
d->Parent->DelView(this);
d->Parent->OnChildrenChanged(this, false);
d->Parent = 0;
d->ParentI = 0;
Status = true;
WndFlags &= ~GWF_FOCUS;
if (_Capturing == this)
{
if (_View)
ReleaseCapture();
#if DEBUG_CAPTURE
LgiTrace("%s:%i - _Capturing %p/%s -> NULL\n",
_FL, this, GetClass());
#endif
_Capturing = 0;
}
if (_View)
{
WndFlags &= ~GWF_QUIT_WND;
BOOL Status = DestroyWindow(_View);
DWORD Err = GetLastError();
LAssert(Status != 0);
}
}
return Status;
}
LRect &LView::GetClient(bool InClientSpace)
{
static LRect Client;
if (_View)
{
RECT rc;
GetClientRect(_View, &rc);
Client = rc;
}
else
{
Client.Set(0, 0, Pos.X()-1, Pos.Y()-1);
if (dynamic_cast(this) ||
dynamic_cast(this))
{
Client.x1 += GetSystemMetrics(SM_CXFRAME);
Client.x2 -= GetSystemMetrics(SM_CXFRAME);
Client.y1 += GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
Client.y2 -= GetSystemMetrics(SM_CYFRAME);
}
else if (Sunken() || Raised())
{
Client.Inset(_BorderSize, _BorderSize);
}
}
if (InClientSpace)
Client.Offset(-Client.x1, -Client.y1);
return Client;
}
LCursor LView::GetCursor(int x, int y)
{
return LCUR_Normal;
}
#ifndef GCL_HCURSOR
#define GCL_HCURSOR -12
#endif
bool LgiToWindowsCursor(OsView Hnd, LCursor Cursor)
{
char16 *Set = 0;
switch (Cursor)
{
case LCUR_UpArrow:
Set = IDC_UPARROW;
break;
case LCUR_Cross:
Set = IDC_CROSS;
break;
case LCUR_Wait:
Set = IDC_WAIT;
break;
case LCUR_Ibeam:
Set = IDC_IBEAM;
break;
case LCUR_SizeVer:
Set = IDC_SIZENS;
break;
case LCUR_SizeHor:
Set = IDC_SIZEWE;
break;
case LCUR_SizeBDiag:
Set = IDC_SIZENESW;
break;
case LCUR_SizeFDiag:
Set = IDC_SIZENWSE;
break;
case LCUR_SizeAll:
Set = IDC_SIZEALL;
break;
case LCUR_PointingHand:
{
LArray Ver;
int Os = LGetOs(&Ver);
if
(
(
Os == LGI_OS_WIN32
||
Os == LGI_OS_WIN64
)
&&
Ver[0] >= 5)
{
#ifndef IDC_HAND
#define IDC_HAND MAKEINTRESOURCE(32649)
#endif
Set = IDC_HAND;
}
// else not supported
break;
}
case LCUR_Forbidden:
Set = IDC_NO;
break;
// Not impl
case LCUR_SplitV:
break;
case LCUR_SplitH:
break;
case LCUR_Blank:
break;
}
HCURSOR cur = LoadCursor(0, Set ? Set : IDC_ARROW);
SetCursor(cur);
if (Hnd)
SetWindowLongPtr(Hnd, GCL_HCURSOR, (LONG_PTR)cur);
return true;
}
bool LView::PointToScreen(LPoint &p)
{
POINT pt = {p.x, p.y};
LViewI *t = this;
while ( t &&
t->GetParent() &&
!t->Handle())
{
pt.x += t->GetPos().x1;
pt.y += t->GetPos().y1;
t = t->GetParent();
}
ClientToScreen(t->Handle(), &pt);
p.x = pt.x;
p.y = pt.y;
return true;
}
bool LView::PointToView(LPoint &p)
{
POINT pt = {p.x, p.y};
LViewI *t = this;
while ( t &&
t->GetParent() &&
!t->Handle())
{
pt.x -= t->GetPos().x1;
pt.y -= t->GetPos().y1;
t = t->GetParent();
}
ScreenToClient(t->Handle(), &pt);
p.x = pt.x;
p.y = pt.y;
return true;
}
bool LView::GetMouse(LMouse &m, bool ScreenCoords)
{
// position
POINT p;
GetCursorPos(&p);
if (!ScreenCoords)
{
ScreenToClient(_View, &p);
}
m.x = p.x;
m.y = p.y;
m.Target = this;
// buttons
m.Flags = ((GetAsyncKeyState(VK_LBUTTON)&0x8000) ? LGI_EF_LEFT : 0) |
((GetAsyncKeyState(VK_MBUTTON)&0x8000) ? LGI_EF_MIDDLE : 0) |
((GetAsyncKeyState(VK_RBUTTON)&0x8000) ? LGI_EF_RIGHT : 0) |
((GetAsyncKeyState(VK_CONTROL)&0x8000) ? LGI_EF_CTRL : 0) |
((GetAsyncKeyState(VK_MENU) &0x8000) ? LGI_EF_ALT : 0) |
((GetAsyncKeyState(VK_LWIN) &0x8000) ? LGI_EF_SYSTEM : 0) |
((GetAsyncKeyState(VK_RWIN) &0x8000) ? LGI_EF_SYSTEM : 0) |
((GetAsyncKeyState(VK_SHIFT) &0x8000) ? LGI_EF_SHIFT : 0);
if (m.Flags & (LGI_EF_LEFT | LGI_EF_MIDDLE | LGI_EF_RIGHT))
{
m.Flags |= LGI_EF_DOWN;
}
return true;
}
bool LView::SetPos(LRect &p, bool Repaint)
{
bool Status = true;
LRect OldPos = Pos;
if (Pos != p)
{
Pos = p;
if (_View)
{
HWND hOld = GetFocus();
bool WasVis = IsWindowVisible(_View) != 0;
int Shadow = WINDOWS_SHADOW_AMOUNT;
In_SetWindowPos = true;
Status = SetWindowPos( _View,
NULL,
Pos.x1,
Pos.y1,
Pos.X() + Shadow,
Pos.Y() + Shadow,
// ((Repaint) ? 0 : SWP_NOREDRAW) |
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER) != 0;
In_SetWindowPos = false;
}
else if (GetParent())
{
OnPosChange();
}
if (Repaint)
{
Invalidate();
}
}
return Status;
}
bool LView::Invalidate(LRect *r, bool Repaint, bool Frame)
{
if (_View)
{
bool Status = false;
if (Frame)
{
RedrawWindow( _View,
NULL,
NULL,
RDW_FRAME |
RDW_INVALIDATE |
RDW_ALLCHILDREN |
((Repaint) ? RDW_UPDATENOW : 0));
}
else
{
if (r)
{
Status = InvalidateRect(_View, &((RECT)*r), false) != 0;
}
else
{
RECT c = GetClient();
Status = InvalidateRect(_View, &c, false) != 0;
}
}
if (Repaint)
{
UpdateWindow(_View);
}
return Status;
}
else
{
LRect Up;
LViewI *p = this;
if (r)
{
Up = *r;
}
else
{
Up.Set(0, 0, Pos.X()-1, Pos.Y()-1);
}
if (dynamic_cast(this))
return true;
while (p && !p->Handle())
{
LViewI *Par = p->GetParent();
LView *VPar = Par?Par->GetGView():0;
LRect w = p->GetPos();
LRect c = p->GetClient(false);
if (Frame && p == this)
Up.Offset(w.x1, w.y1);
else
Up.Offset(w.x1 + c.x1, w.y1 + c.y1);
p = Par;
}
if (p && p->Handle())
{
return p->Invalidate(&Up, Repaint);
}
}
return false;
}
void
CALLBACK
LView::TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, uint32_t dwTime)
{
LView *View = (LView*) idEvent;
if (View)
{
View->OnPulse();
}
}
void LView::SetPulse(int Length)
{
if (_View)
{
if (Length > 0)
{
d->TimerId = SetTimer(_View, (UINT_PTR) this, Length, (TIMERPROC) TimerProc);
}
else
{
KillTimer(_View, d->TimerId);
d->TimerId = 0;
}
}
else
{
d->WantsPulse = Length;
}
}
static int ConsumeTabKey = 0;
bool SysOnKey(LView *w, LMessage *m)
{
if (m->a == VK_TAB &&
(m->m == WM_KEYDOWN ||
m->m == WM_SYSKEYDOWN) )
{
if (!TestFlag(w->d->WndDlgCode, DLGC_WANTTAB) &&
!TestFlag(w->d->WndDlgCode, DLGC_WANTALLKEYS))
{
// push the focus to the next control
bool Shifted = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
LViewI *Wnd = GetNextTabStop(w, Shifted);
if (Wnd)
{
if (In_SetWindowPos)
{
assert(0);
LgiTrace("%s:%i - SetFocus(%p)\\n", _FL, Wnd->Handle());
}
ConsumeTabKey = 2;
::SetFocus(Wnd->Handle());
return true;
}
}
}
return false;
}
#ifdef _MSC_VER
#include "vsstyle.h"
void LView::DrawThemeBorder(LSurface *pDC, LRect &r)
{
if (!d->hTheme)
d->hTheme = OpenThemeData(_View, VSCLASS_EDIT);
if (d->hTheme)
{
RECT rc = r;
int StateId;
if (!Enabled())
StateId = EPSN_DISABLED;
else if (GetFocus() == _View)
StateId = EPSN_FOCUSED;
else
StateId = EPSN_NORMAL;
// LgiTrace("ThemeDraw %s: %i\n", GetClass(), StateId);
RECT clip[4];
clip[0] = LRect(r.x1, r.y1, r.x1 + 1, r.y2); // left
clip[1] = LRect(r.x1 + 2, r.y1, r.x2 - 2, r.y1 + 1); // top
clip[2] = LRect(r.x2 - 1, r.y1, r.x2, r.y2); // right
clip[3] = LRect(r.x1 + 2, r.y2 - 1, r.x2 - 2, r.y2); // bottom
LColour cols[4] =
{
LColour(255, 0, 0),
LColour(0, 255, 0),
LColour(0, 0, 255),
LColour(255, 255, 0)
};
for (int i=0; iColour(cols[i]);
pDC->Rectangle(&tmp);
#else
DrawThemeBackground(d->hTheme,
pDC->Handle(),
EP_EDITBORDER_NOSCROLL,
StateId,
&rc,
&clip[i]);
#endif
}
pDC->Colour(L_MED);
pDC->Set(r.x1, r.y1);
pDC->Set(r.x2, r.y1);
pDC->Set(r.x1, r.y2);
pDC->Set(r.x2, r.y2);
r.Inset(2, 2);
}
else
{
LWideBorder(pDC, r, Sunken() ? DefaultSunkenEdge : DefaultRaisedEdge);
d->IsThemed = false;
}
}
#else
void LView::DrawThemeBorder(LSurface *pDC, LRect &r)
{
LWideBorder(pDC, r, DefaultSunkenEdge);
}
#endif
bool IsKeyChar(LKey &k, int vk)
{
if (k.Ctrl() || k.Alt() || k.System())
return false;
switch (vk)
{
case VK_BACK:
case VK_TAB:
case VK_RETURN:
case VK_SPACE:
case 0xba: // ;
case 0xbb: // =
case 0xbc: // ,
case 0xbd: // -
case 0xbe: // .
case 0xbf: // /
case 0xc0: // `
case 0xdb: // [
case 0xdc: // |
case 0xdd: // ]
case 0xde: // '
return true;
}
if (vk >= VK_NUMPAD0 && vk <= VK_DIVIDE)
return true;
if (vk >= '0' && vk <= '9')
return true;
if (vk >= 'A' && vk <= 'Z')
return true;
return false;
}
#define KEY_FLAGS (~(MK_LBUTTON | MK_MBUTTON | MK_RBUTTON))
LMessage::Result LView::OnEvent(LMessage *Msg)
{
int Status = 0;
if (Msg->Msg() == MouseRollMsg)
{
HWND hFocus = GetFocus();
if (_View)
{
int Flags = ((GetKeyState(VK_SHIFT)&0xF000) ? VK_SHIFT : 0) |
((GetKeyState(VK_CONTROL)&0xF000) ? VK_CONTROL : 0);
PostMessage(hFocus, WM_MOUSEWHEEL, MAKELONG(Flags, (short)Msg->a), Msg->b);
}
return 0;
}
for (auto target: d->EventTargets)
{
if (target->Msgs.Length() == 0 ||
target->Msgs.Find(Msg->Msg()))
target->OnEvent(Msg);
}
if (_View)
{
switch (Msg->m)
{
#if 1
case WM_CTLCOLORBTN:
case WM_CTLCOLOREDIT:
case WM_CTLCOLORSTATIC:
{
HDC hdc = (HDC)Msg->A();
HWND hwnd = (HWND)Msg->B();
LViewI *v = FindControl(hwnd);
LView *gv = v ? v->GetGView() : NULL;
if (gv)
{
int Depth = dynamic_cast(gv) ? 1 : 10;
LColour Fore = gv->StyleColour(LCss::PropColor, LColour(), Depth);
LColour Back = gv->StyleColour(LCss::PropBackgroundColor, LColour(), Depth);
if (Fore.IsValid())
{
COLORREF c = RGB(Fore.r(), Fore.g(), Fore.b());
SetTextColor(hdc, c);
}
if (Back.IsValid())
{
COLORREF c = RGB(Back.r(), Back.g(), Back.b());
SetBkColor(hdc, c);
SetDCBrushColor(hdc, c);
}
if (Fore.IsValid() || Back.IsValid())
{
#if !defined(DC_BRUSH)
#define DC_BRUSH 18
#endif
return (LRESULT) GetStockObject(DC_BRUSH);
}
}
goto ReturnDefaultProc;
return 0;
}
#endif
case 5700:
{
// I forget what this is for...
break;
}
case WM_ERASEBKGND:
{
return 1;
}
case WM_GETFONT:
{
LFont *f = GetFont();
if (!f || f == LSysFont)
return (LMessage::Result) LSysFont->Handle();
return (LMessage::Result) f->Handle();
break;
}
case WM_MENUCHAR:
case WM_MEASUREITEM:
{
return LMenu::_OnEvent(Msg);
break;
}
case WM_DRAWITEM:
{
DRAWITEMSTRUCT *di = (DRAWITEMSTRUCT*)Msg->B();
if (di)
{
if (di->CtlType == ODT_MENU)
{
return LMenu::_OnEvent(Msg);
}
/*
else if (di->CtlType == ODT_BUTTON)
{
LView *b;
if (CastHwnd(b, di->hwndItem) &&
b->GetCss())
{
LScreenDC dc(di->hDC, di->hwndItem);
switch (di->itemAction)
{
case ODA_DRAWENTIRE:
{
LRect c = di->rcItem;
LMemDC m(c.X(), c.Y(), GdcD->GetColourSpace());
HDC hdc = m.StartDC();
m.Colour(LColour(255, 0, 255));
m.Line(0, 0, m.X()-1, m.Y()-1);
LONG s = GetWindowLong(_View, GWL_STYLE);
SetWindowLong(_View, GWL_STYLE, (s & ~BS_TYPEMASK) | BS_PUSHBUTTON);
SendMessage(_View, WM_PRINT, (WPARAM)hdc, PRF_ERASEBKGND|PRF_CLIENT);
SetWindowLong(_View, GWL_STYLE, (s & ~BS_TYPEMASK) | BS_OWNERDRAW);
m.EndDC();
dc.Blt(0, 0, &m);
break;
}
case ODA_FOCUS:
{
break;
}
case ODA_SELECT:
{
break;
}
}
return true;
}
}
*/
}
if (!(WndFlags & GWF_DIALOG))
goto ReturnDefaultProc;
break;
}
case WM_ENABLE:
{
Invalidate(&Pos);
break;
}
case WM_HSCROLL:
case WM_VSCROLL:
{
LViewI *Wnd = FindControl((HWND) Msg->b);
if (Wnd)
{
Wnd->OnEvent(Msg);
}
break;
}
case WM_GETDLGCODE:
{
// we handle all tab control stuff
return DLGC_WANTALLKEYS; // d->WndDlgCode | DLGC_WANTTAB;
}
case WM_MOUSEWHEEL:
{
// short fwKeys = LOWORD(Msg->a); // key flags
short zDelta = (short) HIWORD(Msg->a); // wheel rotation
int nScrollLines = - _lgi_mouse_wheel_lines();
double Lines = ((double)zDelta * (double)nScrollLines) / WHEEL_DELTA;
if (ABS(Lines) < 1.0)
Lines *= 1.0 / ABS(Lines);
// LgiTrace("Lines = %g, zDelta = %i, nScrollLines = %i\n", Lines, zDelta, nScrollLines);
// Try giving the event to the current window...
if (!OnMouseWheel(Lines))
{
// Find the window under the cursor... and try giving it the mouse wheel event
short xPos = (short) LOWORD(Msg->b); // horizontal position of pointer
short yPos = (short) HIWORD(Msg->b); // vertical position of pointer
POINT Point = {xPos, yPos};
HWND hUnder = ::WindowFromPoint(Point);
HWND hParent = ::GetParent(hUnder);
if (hUnder &&
hUnder != _View && // Don't want to send ourselves a message...
hParent != _View) // WM_MOUSEWHEEL will propagate back up to us and cause an infinite loop
{
// Do a post event in case the window is deleting... at least it won't crash.
PostMessage(hUnder, Msg->m, Msg->a, Msg->b);
}
}
return 0;
}
case M_CHANGE:
{
LWindow *w = GetWindow();
LAutoPtr note((LNotification*)Msg->B());
LViewI *Ctrl = w ? w->FindControl((int)Msg->a) : 0;
if (Ctrl)
{
LAssert(note.Get() != NULL);
return OnNotify(Ctrl, note ? *note : LNotifyNull);
}
else
{
LgiTrace("Ctrl %i not found.\n", Msg->a);
}
break;
}
case M_COMMAND:
{
// LViewI *Ci = FindControl((HWND) Msg->b);
// LView *Ctrl = Ci ? Ci->GetGView() : 0;
LView *Ctrl;
if (Msg->b &&
CastHwnd(Ctrl, (HWND)Msg->b))
{
short Code = HIWORD(Msg->a);
switch (Code)
{
case CBN_CLOSEUP:
{
PostMessage(_View, WM_COMMAND, MAKELONG(Ctrl->GetId(), CBN_EDITCHANGE), Msg->b);
break;
}
case CBN_EDITCHANGE: // COMBO
{
Ctrl->SysOnNotify(Msg->Msg(), Code);
OnNotify(Ctrl, LNotifyValueChanged);
break;
}
/*
case BN_CLICKED: // BUTTON
case EN_CHANGE: // EDIT
*/
default:
{
Ctrl->SysOnNotify(Msg->Msg(), Code);
break;
}
}
}
break;
}
case WM_NCDESTROY:
{
#if _MSC_VER >= _MSC_VER_VS2005
SetWindowLongPtr(_View, GWLP_USERDATA, 0);
#else
SetWindowLong(_View, GWL_USERDATA, 0);
#endif
_View = NULL;
if (WndFlags & GWF_QUIT_WND)
{
delete this;
}
break;
}
case WM_CLOSE:
{
if (OnRequestClose(false))
{
Quit();
}
break;
}
case WM_DESTROY:
{
OnDestroy();
break;
}
case WM_CREATE:
{
SetId(d->CtrlId);
LWindow *w = GetWindow();
if (w && w->GetFocus() == this)
{
HWND hCur = GetFocus();
if (hCur != _View)
{
if (In_SetWindowPos)
{
assert(0);
LgiTrace("%s:%i - SetFocus(%p) (%s)\\n", __FILE__, __LINE__, Handle(), GetClass());
}
SetFocus(_View);
}
}
if (TestFlag(GViewFlags, GWF_DROP_TARGET))
{
DropTarget(true);
}
OnCreate();
break;
}
case WM_SETFOCUS:
{
LWindow *w = GetWindow();
if (w)
{
w->SetFocus(this, LWindow::GainFocus);
}
else
{
// This can happen in popup sub-trees of views. Where the focus
// is tracked separately from the main LWindow.
OnFocus(true);
Invalidate((LRect*)NULL, false, true);
}
break;
}
case WM_KILLFOCUS:
{
LWindow *w = GetWindow();
if (w)
{
w->SetFocus(this, LWindow::LoseFocus);
}
else
{
// This can happen when the LWindow is being destroyed
Invalidate((LRect*)NULL, false, true);
OnFocus(false);
}
break;
}
case WM_WINDOWPOSCHANGED:
{
if (!IsIconic(_View))
{
WINDOWPOS *Info = (LPWINDOWPOS) Msg->b;
if (Info)
{
if (Info->x == -32000 &&
Info->y == -32000)
{
#if 0
LgiTrace("WM_WINDOWPOSCHANGED %i,%i,%i,%i (icon=%i)\\n",
Info->x,
Info->y,
Info->cx,
Info->cy,
IsIconic(Handle()));
#endif
}
else
{
int Shadow = WINDOWS_SHADOW_AMOUNT;
LRect r;
r.ZOff(Info->cx-1, Info->cy-1);
r.Offset(Info->x, Info->y);
if (r.Valid() && r != Pos)
{
Pos = r;
/*
LgiTrace("%p/%s::PosChange %s\n", this, Name(), Pos.GetStr());
Pos.x2 -= Shadow;
Pos.y2 -= Shadow;
LgiTrace(" %s\n", Pos.GetStr());
*/
}
}
}
OnPosChange();
}
if (!(WndFlags & GWF_DIALOG))
{
goto ReturnDefaultProc;
}
break;
}
case WM_CAPTURECHANGED:
{
LViewI *Wnd;
if (Msg->B() &&
CastHwnd(Wnd, (HWND)Msg->B()))
{
if (Wnd != _Capturing)
{
#if DEBUG_CAPTURE
LgiTrace("%s:%i - _Capturing %p/%s -> %p/%s\n",
_FL,
_Capturing, _Capturing?_Capturing->GetClass():0,
Wnd, Wnd?Wnd->GetClass() : 0);
#endif
_Capturing = Wnd;
}
}
else if (_Capturing)
{
#if DEBUG_CAPTURE
LgiTrace("%s:%i - _Capturing %p/%s -> NULL\n",
_FL,
_Capturing, _Capturing?_Capturing->GetClass():0);
#endif
_Capturing = NULL;
}
break;
}
case M_MOUSEENTER:
{
LMouse Ms;
Ms.Target = this;
Ms.x = (short) (Msg->b&0xFFFF);
Ms.y = (short) (Msg->b>>16);
Ms.Flags = 0;
LViewI *MouseOver = WindowFromPoint(Ms.x, Ms.y);
if (MouseOver &&
_Over != MouseOver &&
!(MouseOver == this || MouseOver->Handle() == 0))
{
if (_Capturing)
{
if (MouseOver == _Capturing)
{
Ms = lgi_adjust_click(Ms, _Capturing);
_Capturing->OnMouseEnter(Ms);
}
}
else
{
if (_Over)
{
LMouse m = lgi_adjust_click(Ms, _Over);
_Over->OnMouseExit(m);
#if DEBUG_OVER
LgiTrace("Enter.LoseOver=%p/%s '%-20s'\n", _Over, _Over->GetClass(), _Over->Name());
#endif
}
_Over = MouseOver;
if (_Over)
{
#if DEBUG_OVER
LgiTrace("Enter.GetOver=%p/%s '%-20s'\n", _Over, _Over->GetClass(), _Over->Name());
#endif
LMouse m = lgi_adjust_click(Ms, _Over);
_Over->OnMouseEnter(m);
}
}
}
break;
}
case M_MOUSEEXIT:
{
if (_Over)
{
LMouse Ms;
Ms.Target = this;
Ms.x = (short) (Msg->b&0xFFFF);
Ms.y = (short) (Msg->b>>16);
Ms.Flags = 0;
bool Mine = false;
if (_Over->Handle())
{
Mine = _Over == this;
}
else
{
for (LViewI *o = _Capturing ? _Capturing : _Over; o; o = o->GetParent())
{
if (o == this)
{
Mine = true;
break;
}
}
}
if (Mine)
{
if (_Capturing)
{
LMouse m = lgi_adjust_click(Ms, _Capturing);
_Capturing->OnMouseExit(m);
}
else
{
#if DEBUG_OVER
LgiTrace("Exit.LoseOver=%p '%-20s'\n", _Over, _Over->Name());
#endif
_Over->OnMouseExit(Ms);
_Over = 0;
}
}
}
break;
}
case WM_MOUSEMOVE:
{
LMouse Ms;
Ms.Target = this;
Ms.x = (short) (Msg->b&0xFFFF);
Ms.y = (short) (Msg->b>>16);
Ms.Flags = _lgi_get_key_flags();
Ms.IsMove(true);
if (TestFlag(Msg->a, MK_LBUTTON)) SetFlag(Ms.Flags, LGI_EF_LEFT);
if (TestFlag(Msg->a, MK_RBUTTON)) SetFlag(Ms.Flags, LGI_EF_RIGHT);
if (TestFlag(Msg->a, MK_MBUTTON)) SetFlag(Ms.Flags, LGI_EF_MIDDLE);
SetKeyFlag(Ms.Flags, VK_MENU, MK_ALT);
Ms.Down((Msg->a & (MK_LBUTTON|MK_MBUTTON|MK_RBUTTON)) != 0);
LViewI *MouseOver = WindowFromPoint(Ms.x, Ms.y);
if (_Over != MouseOver)
{
if (_Over)
{
#if DEBUG_OVER
LgiTrace("Move.LoseOver=%p/%s '%-20s'\n", _Over, _Over->GetClass(), _Over->Name());
#endif
LMouse m = lgi_adjust_click(Ms, _Over);
_Over->OnMouseExit(m);
}
_Over = MouseOver;
if (_Over)
{
LMouse m = lgi_adjust_click(Ms, _Over);
_Over->OnMouseEnter(m);
#if DEBUG_OVER
LgiTrace("Move.GetOver=%p/%s '%-20s'\n", _Over, _Over->GetClass(), _Over->Name());
#endif
}
}
// int CurX = Ms.x, CurY = Ms.y;
LCursor Cursor = (_Over ? _Over : this)->GetCursor(Ms.x, Ms.y);
LgiToWindowsCursor(_View, Cursor);
#if 0
LgiTrace("WM_MOUSEMOVE %i,%i target=%p/%s, over=%p/%s, cap=%p/%s\n",
Ms.x, Ms.y,
Ms.Target, Ms.Target?Ms.Target->GetClass():0,
_Over, _Over?_Over->GetClass():0,
_Capturing, _Capturing?_Capturing->GetClass():0);
#endif
if (_Capturing)
Ms = lgi_adjust_click(Ms, _Capturing, true);
else if (_Over)
Ms = lgi_adjust_click(Ms, _Over);
else
return 0;
LWindow *Wnd = GetWindow();
if (!Wnd || Wnd->HandleViewMouse(dynamic_cast(Ms.Target), Ms))
{
Ms.Target->OnMouseMove(Ms);
}
break;
}
case WM_NCHITTEST:
{
POINT Pt = { LOWORD(Msg->b), HIWORD(Msg->b) };
ScreenToClient(_View, &Pt);
int Hit = OnHitTest(Pt.x, Pt.y);
if (Hit >= 0)
{
// LgiTrace("%I64i Hit=%i\n", LCurrentTime(), Hit);
return Hit;
}
if (!(WndFlags & GWF_DIALOG))
{
goto ReturnDefaultProc;
}
break;
}
case WM_LBUTTONDBLCLK:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
{
LMouse Ms;
Ms.x = (short) (Msg->b&0xFFFF);
Ms.y = (short) (Msg->b>>16);
Ms.Flags = _lgi_get_key_flags() | LGI_EF_LEFT;
Ms.Down(Msg->m != WM_LBUTTONUP);
Ms.Double(Msg->m == WM_LBUTTONDBLCLK);
if (_Capturing)
Ms = lgi_adjust_click(Ms, _Capturing, true);
else if (_Over)
Ms = lgi_adjust_click(Ms, _Over);
else
Ms.Target = this;
#if DEBUG_MOUSE_CLICKS
LString Msg;
Msg.Printf("%s.Click", Ms.Target->GetClass());
Ms.Trace(Msg);
#endif
LWindow *Wnd = GetWindow();
if (!Wnd || Wnd->HandleViewMouse(dynamic_cast(Ms.Target), Ms))
Ms.Target->OnMouseClick(Ms);
break;
}
case WM_RBUTTONDBLCLK:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
{
LMouse Ms;
Ms.x = (short) (Msg->b&0xFFFF);
Ms.y = (short) (Msg->b>>16);
Ms.Flags = _lgi_get_key_flags() | LGI_EF_RIGHT;
Ms.Down(Msg->m != WM_RBUTTONUP);
Ms.Double(Msg->m == WM_RBUTTONDBLCLK);
if (_Capturing)
Ms = lgi_adjust_click(Ms, _Capturing, true);
else if (_Over)
Ms = lgi_adjust_click(Ms, _Over);
else
Ms.Target = this;
#if DEBUG_MOUSE_CLICKS
LString Msg;
Msg.Printf("%s.Click", Ms.Target->GetClass());
Ms.Trace(Msg);
#endif
LWindow *Wnd = GetWindow();
if (!Wnd || Wnd->HandleViewMouse(dynamic_cast(Ms.Target), Ms))
Ms.Target->OnMouseClick(Ms);
break;
}
case WM_MBUTTONDBLCLK:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
{
LMouse Ms;
Ms.x = (short) (Msg->b&0xFFFF);
Ms.y = (short) (Msg->b>>16);
Ms.Flags = _lgi_get_key_flags() | LGI_EF_MIDDLE;
Ms.Down(Msg->m != WM_MBUTTONUP);
Ms.Double(Msg->m == WM_MBUTTONDBLCLK);
if (_Capturing)
Ms = lgi_adjust_click(Ms, _Capturing, true);
else if (_Over)
Ms = lgi_adjust_click(Ms, _Over);
else
Ms.Target = this;
LWindow *Wnd = GetWindow();
if (!Wnd || Wnd->HandleViewMouse(dynamic_cast(Ms.Target), Ms))
Ms.Target->OnMouseClick(Ms);
break;
}
case WM_XBUTTONDBLCLK:
case WM_XBUTTONDOWN:
case WM_XBUTTONUP:
{
LMouse Ms;
int Clicked = (Msg->a >> 16) & 0xffff;
Ms.x = (short) (Msg->b&0xFFFF);
Ms.y = (short) (Msg->b>>16);
Ms.Flags = _lgi_get_key_flags() | LGI_EF_MIDDLE;
Ms.Button1(TestFlag(Clicked, XBUTTON1));
Ms.Button2(TestFlag(Clicked, XBUTTON2));
Ms.Down(Msg->m != WM_XBUTTONUP);
Ms.Double(Msg->m == WM_XBUTTONDBLCLK);
if (_Capturing)
Ms = lgi_adjust_click(Ms, _Capturing, true);
else if (_Over)
Ms = lgi_adjust_click(Ms, _Over);
else
Ms.Target = this;
LWindow *Wnd = GetWindow();
if (!Wnd || Wnd->HandleViewMouse(dynamic_cast(Ms.Target), Ms))
Ms.Target->OnMouseClick(Ms);
break;
}
case WM_SYSKEYUP:
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
case WM_KEYUP:
{
static char AltCode[32];
bool IsDialog = TestFlag(WndFlags, GWF_DIALOG);
bool IsDown = Msg->m == WM_KEYDOWN || Msg->m == WM_SYSKEYDOWN;
int KeyFlags = _lgi_get_key_flags();
HWND hwnd = _View;
if (SysOnKey(this, Msg))
{
// LgiTrace("SysOnKey true, Msg=0x%x %x,%x\n", Msg->m, Msg->a, Msg->b);
return 0;
}
else
{
// Key
LKey Key((int)Msg->a, (int)Msg->b);
Key.Flags = KeyFlags;
Key.Down(IsDown);
Key.IsChar = false;
if (Key.Ctrl())
{
Key.c16 = (char16)Msg->a;
}
if (Key.c16 == VK_TAB && ConsumeTabKey)
{
ConsumeTabKey--;
}
else
{
LWindow *Wnd = GetWindow();
if (Wnd)
{
if (Key.Alt() ||
Key.Ctrl() ||
(Key.c16 < 'A' || Key.c16 > 'Z'))
{
Wnd->HandleViewKey(this, Key);
}
}
else
{
OnKey(Key);
}
}
if (Msg->m == WM_SYSKEYUP || Msg->m == WM_SYSKEYDOWN)
{
if (Key.vkey >= VK_F1 &&
Key.vkey <= VK_F12 &&
Key.Alt() == false)
{
// So in LgiIde if you press F10 (debug next) you get a hang
// sometimes in DefWindowProc. Until I figure out what's going
// on this code exits before calling DefWindowProc without
// breaking other WM_SYSKEY* functionality (esp Alt+F4).
return 0;
}
}
}
if (!IsDialog)
{
// required for Alt-Key function (eg Alt-F4 closes window)
goto ReturnDefaultProc;
}
break;
}
#if OLD_WM_CHAR_MODE
case WM_CHAR:
{
LKey Key((int)Msg->a, (int)Msg->b);
Key.Flags = _lgi_get_key_flags();
Key.Down(true);
Key.IsChar = true;
bool Shift = Key.Shift();
bool Caps = TestFlag(Key.Flags, LGI_EF_CAPS_LOCK);
if (!(Shift ^ Caps))
{
Key.c16 = ToLower(Key.c16);
}
else
{
Key.c16 = ToUpper(Key.c16);
}
if (Key.c16 == LK_TAB && ConsumeTabKey)
{
ConsumeTabKey--;
}
else
{
LWindow *Wnd = GetWindow();
if (Wnd)
{
Wnd->HandleViewKey(this, Key);
}
else
{
OnKey(Key);
}
}
break;
}
#endif
case M_SET_WND_STYLE:
{
SetWindowLong(Handle(), GWL_STYLE, (LONG)Msg->b);
SetWindowPos( Handle(),
0, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOSIZE | SWP_FRAMECHANGED);
break;
}
case WM_PAINT:
{
_Paint();
break;
}
case WM_NCPAINT:
{
if (GetWindow() != this &&
!TestFlag(WndFlags, GWF_SYS_BORDER))
{
HDC hDC = GetWindowDC(_View);
LScreenDC Dc(hDC, _View, true);
LRect p(0, 0, Dc.X()-1, Dc.Y()-1);
OnNcPaint(&Dc, p);
}
goto ReturnDefaultProc;
break;
}
case WM_NCCALCSIZE:
{
LMessage::Param Status = 0;
int Edge = (Sunken() || Raised()) ? _BorderSize : 0;
RECT *rc = NULL;
if (Msg->a)
{
NCCALCSIZE_PARAMS *p = (NCCALCSIZE_PARAMS*) Msg->b;
rc = p->rgrc;
}
else
{
rc = (RECT*)Msg->b;
}
if (!(WndFlags & GWF_DIALOG))
{
Status = DefWindowProcW(_View, Msg->m, Msg->a, Msg->b);
}
if (Edge && rc && !TestFlag(WndFlags, GWF_SYS_BORDER))
{
rc->left += Edge;
rc->top += Edge;
rc->right -= Edge;
rc->bottom -= Edge;
return 0;
}
return Status;
}
case WM_NOTIFY:
{
NMHDR *Hdr = (NMHDR*)Msg->B();
if (Hdr)
{
LView *Wnd;
if (CastHwnd(Wnd, Hdr->hwndFrom))
Wnd->SysOnNotify(Msg->Msg(), Hdr->code);
}
break;
}
case M_THREAD_COMPLETED:
{
auto Th = (LThread*)Msg->A();
if (!Th)
break;
Th->OnComplete();
if (Th->GetDeleteOnExit())
delete Th;
return true;
}
default:
{
if (!(WndFlags & GWF_DIALOG))
goto ReturnDefaultProc;
break;
}
}
}
return 0;
ReturnDefaultProc:
#ifdef _DEBUG
uint64 start = LCurrentTime();
#endif
LRESULT r = DefWindowProcW(_View, Msg->m, Msg->a, Msg->b);
#ifdef _DEBUG
uint64 now = LCurrentTime();
if (now - start > 1000)
{
LgiTrace("DefWindowProc(0x%.4x, %i, %i) took %ims\n",
Msg->m, Msg->a, Msg->b, (int)(now - start));
}
#endif
return r;
}
LViewI *LView::FindControl(OsView hCtrl)
{
if (_View == hCtrl)
{
return this;
}
for (List::I i = Children.begin(); i.In(); i++)
{
LViewI *Ctrl = (*i)->FindControl(hCtrl);
if (Ctrl)
return Ctrl;
}
return 0;
}